@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.
- package/dist/esm/Enumeration/Enumerate.js +7 -6
- package/dist/esm/Error/index.js +1 -1
- package/dist/esm/Fabricator/Constructor.js +11 -10
- package/dist/esm/Harnessing/Core.js +7 -16
- package/dist/esm/Instance/Core.js +51 -17
- 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 +2 -2
- package/dist/types/Enumeration/Enumerate.d.ts +15 -15
- package/dist/types/Enumeration/Types.d.ts +2 -2
- package/dist/types/Error/index.d.ts +8 -7
- package/dist/types/Fabricator/Constructor.d.ts +10 -7
- package/dist/types/Fabricator/Types.d.ts +15 -14
- package/dist/types/Harnessing/Core.d.ts +25 -25
- package/dist/types/Harnessing/Types.d.ts +56 -13
- package/dist/types/Instance/Core.d.ts +17 -6
- 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 +169 -19
- package/dist/types/Primitive/choice/Fabricator.d.ts +1 -1
- package/dist/types/Primitive/nullable/Fabricator.d.ts +1 -1
- package/dist/types/Primitive/nullish/Fabricator.d.ts +2 -2
- package/dist/types/Primitive/object/optional/Fabricator.d.ts +2 -2
- package/dist/types/Primitive/recursive/Fabricator.d.ts +7 -8
- package/dist/types/Primitive/recursive/Types.d.ts +1 -1
- package/dist/types/Primitive/recursive/self/Types.d.ts +1 -1
- package/dist/types/Primitive/undefinable/Fabricator.d.ts +1 -1
- package/dist/types/Random/Types.d.ts +23 -21
- package/dist/types/Random/index.d.ts +3 -3
- package/dist/types/harnessing.d.ts +18 -6
- package/dist/types/index.d.ts +11 -2
- package/dist/types/internal.d.ts +1 -1
- 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
|
|
11
|
-
return null != (_ref = null == (
|
|
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
|
|
94
|
+
const trace = forked.toConstructionTrace();
|
|
94
95
|
const stream = toStreamFromTrace(forked.algorithm, {
|
|
95
|
-
...
|
|
96
|
+
...trace,
|
|
96
97
|
path: [],
|
|
97
98
|
kind: "order"
|
|
98
99
|
});
|
package/dist/esm/Error/index.js
CHANGED
|
@@ -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
|
|
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:
|
|
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
|
-
...
|
|
298
|
+
...trace,
|
|
298
299
|
path,
|
|
299
300
|
kind
|
|
300
301
|
}),
|
|
301
|
-
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
|
-
|
|
307
|
-
const base = null != (_ref = null ==
|
|
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
|
-
|
|
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 ===
|
|
11
|
-
return
|
|
8
|
+
fabricator: ({ established })=>{
|
|
9
|
+
if (void 0 === established) throw new FabricatorError.HarnessingProviderError();
|
|
10
|
+
return established;
|
|
12
11
|
}
|
|
13
12
|
},
|
|
14
|
-
|
|
13
|
+
*frame ({ identity }) {
|
|
15
14
|
const salt = index_js_layer(saltFor(identity));
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
|
73
|
+
return derive(forkOverlay).instance;
|
|
54
74
|
}
|
|
55
75
|
function wrap(wrapOverlay, block) {
|
|
56
|
-
|
|
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:
|
|
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
|
|
75
|
-
return normalizeSalt((null != (_ref = null == (
|
|
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
|
|
80
|
-
return (null != (_ref1 = null == (
|
|
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
|
|
85
|
-
return resolveClock(null != (_ref2 = null == (
|
|
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
|
-
|
|
7
|
-
enter: (frame, block)=>store.run(
|
|
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
|
-
|
|
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.
|
|
13
|
+
const construction = privateSource.toConstructionTrace();
|
|
14
14
|
const context = {
|
|
15
15
|
toTrace: (path, kind)=>({
|
|
16
16
|
...construction,
|
package/dist/esm/Random/index.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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`
|
|
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
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
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
|
|
16
|
-
* instance's `source`; read fresh on every
|
|
17
|
-
* call, not once when `enumerables()` was
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* `construct()`) — a pin,
|
|
21
|
-
*
|
|
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
|
|
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
|
|
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
|
|
543
|
+
* own frame.
|
|
544
544
|
*
|
|
545
|
-
* The provider hands back the per-test scope
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
*
|
|
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
|
|
29
|
-
* (`Instance/Core.ts`
|
|
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
|
|
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
|
|
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
|
|
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
|
|
21
|
-
* `toTrace`: each lazy expansion
|
|
22
|
-
* forked `RandomSource` (see `recursive/Fabricator.ts`), so a
|
|
23
|
-
* expansion count can never perturb, or be perturbed by,
|
|
24
|
-
* from the same `initialize()` instance —
|
|
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
|
|
29
|
-
* `clock` is not a field of its own — it is
|
|
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
|
|
51
|
-
* `clock`: it is `trace.clock`, always. A
|
|
52
|
-
* `element`, an object's `fields`, a choice's
|
|
53
|
-
* its own trailing parameter: those vary per
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
* `
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* with no scope is a composer
|
|
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>>;
|