@ghostry/fabricator 0.0.4 → 0.0.6
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/Error/index.js +1 -1
- package/dist/esm/Harnessing/Core.js +7 -16
- package/dist/types/Enumeration/Types.d.ts +2 -2
- package/dist/types/Error/index.d.ts +8 -7
- package/dist/types/Harnessing/Core.d.ts +50 -33
- package/dist/types/Harnessing/Types.d.ts +56 -13
- 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/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/harnessing.d.ts +18 -6
- package/package.json +4 -1
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;
|
|
@@ -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: instance })=>{
|
|
9
|
+
if (void 0 === instance) throw new FabricatorError.HarnessingProviderError();
|
|
10
|
+
return instance;
|
|
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.context.scope().wrap({
|
|
16
|
+
salt
|
|
17
|
+
}, body);
|
|
27
18
|
}
|
|
28
19
|
};
|
|
29
20
|
}
|
|
@@ -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();
|
|
@@ -2,42 +2,59 @@ 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 `context.scope().wrap({ salt: layer(saltFor(identity)) })` —
|
|
7
|
+
* one line of real work. Construction ordinals therefore restart per test (each
|
|
8
|
+
* `wrap` 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
|
-
*
|
|
21
|
+
* `body` is handed to `wrap` directly rather than called inside a closure.
|
|
22
|
+
* `wrap(overlay, block)` already takes `(scope) => $Return`, which is exactly
|
|
23
|
+
* the wrapper's own `(established) => $Return`, so the scope `wrap` opens _is_
|
|
24
|
+
* the established value with nothing in between to adapt it.
|
|
25
|
+
*
|
|
26
|
+
* `context.scope()`, not `instance`, is the receiver. A plain `instance.wrap`
|
|
27
|
+
* lays its overlay over the instance it was called on, and that receiver is
|
|
28
|
+
* bound before any test runs — so an integration composed _inside_ another
|
|
29
|
+
* library's fabricator scope would restate from the configured instance and
|
|
30
|
+
* drop that enclosing scope entirely, silently, while still landing innermost
|
|
31
|
+
* and therefore governing every draw. `context.scope()` is the frame in effect,
|
|
32
|
+
* or the instance itself when there is none, which is this integration's
|
|
33
|
+
* contract in both cases with no branch. Called fresh here rather than hoisted:
|
|
34
|
+
* `scope` is a function so that capturing it captures the lookup, where a
|
|
35
|
+
* captured result would pin one frame.
|
|
36
|
+
*
|
|
37
|
+
* The frame that `wrap` pushes is keyed on the receiver's ancestry, so under
|
|
38
|
+
* composition the per-test frame belongs to the enclosing scope's line: a
|
|
39
|
+
* collateral `fork()` taken inside a test resolves against the outermost frame
|
|
40
|
+
* on its own line instead. That is the ordinary ancestry rule — siblings never
|
|
41
|
+
* see each other's frames, and the outward walk supplies the outer ones — not a
|
|
42
|
+
* special case here.
|
|
43
|
+
*
|
|
44
|
+
* `provides.fabricator` is then that scope — the instance `wrap` gave its
|
|
45
|
+
* block, not the base instance — so `context.fabricator.salt` is the per-test
|
|
46
|
+
* salt and `.fork()` forks from the test's configuration. Nothing mediates
|
|
47
|
+
* between the two: the wrapper hands the scope forward and the provider
|
|
48
|
+
* receives it. A provider reached with no established scope is a composer
|
|
49
|
+
* running providers outside the frame it opened, and raises
|
|
32
50
|
* `HarnessingProviderError` rather than quietly providing the base instance.
|
|
33
51
|
*
|
|
34
|
-
* Nothing here
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* are the request honored, not a bug to override.
|
|
52
|
+
* Nothing here sets `clock`. A pinned `Date` (the recommended setup) and the
|
|
53
|
+
* wall-clock default are already concrete numbers by the time `overlay()` sees
|
|
54
|
+
* them, so they inherit through every `wrap` — the frame in effect's included:
|
|
55
|
+
* salt varies per test, "now" does not. `clock: "derived"` is left alone on
|
|
56
|
+
* purpose — that policy's documented meaning is that the salt _is_ the
|
|
57
|
+
* reproducibility unit, so per-test clocks are the request honored, not a bug
|
|
58
|
+
* to override.
|
|
42
59
|
*/
|
|
43
|
-
export declare function integration<$Registry extends PlainObject>(instance: Instance<$Registry>): Integration<FabricatorTestContext<$Registry>>;
|
|
60
|
+
export declare function integration<$Registry extends PlainObject>(instance: Instance<$Registry>): Integration<FabricatorTestContext<$Registry>, Instance<$Registry>>;
|
|
@@ -38,34 +38,77 @@ export type Identity = {
|
|
|
38
38
|
/**
|
|
39
39
|
* One context key's value, as a function of the test's `Identity` rather than a
|
|
40
40
|
* fixed value.
|
|
41
|
+
*
|
|
42
|
+
* `established` is whatever this integration's own wrapper handed forward. For
|
|
43
|
+
* this integration that is the scoped `Instance` `wrap` opened, which is the
|
|
44
|
+
* whole of what `provides.fabricator` returns — so the value travels from the
|
|
45
|
+
* wrapper to the provider directly, with no mutable slot written on the way in
|
|
46
|
+
* and read on the way out.
|
|
47
|
+
*/
|
|
48
|
+
export type Provider<$Value, $Established = void> = (args: ProviderArgs<$Established>) => $Value;
|
|
49
|
+
/**
|
|
50
|
+
* What `@ghostry/harness` hands {@link Integration.frame}: one object, never
|
|
51
|
+
* positional arguments, so a field added to the contract later is a key an
|
|
52
|
+
* existing hook ignores rather than a parameter it has to thread past.
|
|
41
53
|
*/
|
|
42
|
-
export type
|
|
54
|
+
export type FrameArgs = {
|
|
55
|
+
readonly identity: Identity;
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* What it hands each provider: everything {@link FrameArgs} carries, plus what
|
|
59
|
+
* this integration's own wrapper established — for this integration, the scoped
|
|
60
|
+
* `Instance` that `wrap` opened.
|
|
61
|
+
*/
|
|
62
|
+
export type ProviderArgs<$Established = void> = FrameArgs & {
|
|
63
|
+
readonly established: $Established;
|
|
64
|
+
};
|
|
43
65
|
/**
|
|
44
66
|
* The keys an integration contributes, and how each is produced. Homomorphic
|
|
45
67
|
* over `$Context`, so the context an integration contributes is read back out
|
|
46
68
|
* of this object's shape with no separate key declaration to keep in sync.
|
|
47
69
|
*/
|
|
48
|
-
export type Provides<$Context extends object> = {
|
|
49
|
-
readonly [$Key in keyof $Context]: Provider<$Context[$Key]>;
|
|
70
|
+
export type Provides<$Context extends object, $Established = void> = {
|
|
71
|
+
readonly [$Key in keyof $Context]: Provider<$Context[$Key], $Established>;
|
|
50
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* How an integration runs the body when the body must run _inside_ something.
|
|
75
|
+
* Here that is fabricator's own `wrap`, whose block parameter is already this
|
|
76
|
+
* shape — the scoped `Instance` it opens is what reaches `body`, and therefore
|
|
77
|
+
* what reaches the providers.
|
|
78
|
+
*
|
|
79
|
+
* Generic in its return and must hand the body's value back unchanged: that is
|
|
80
|
+
* what keeps a synchronous test synchronous and what lets frames nest.
|
|
81
|
+
*/
|
|
82
|
+
export type Wrapper<$Established = void> = <$Return>(body: (established: $Established) => $Return) => $Return;
|
|
83
|
+
/**
|
|
84
|
+
* What {@link Integration.frame} returns: a generator with **one** suspension
|
|
85
|
+
* point. Everything before the `yield` is setup, the body runs at the `yield`,
|
|
86
|
+
* and everything after it is teardown, resumed when the body _settles_.
|
|
87
|
+
*
|
|
88
|
+
* `@ghostry/harness` accepts an `AsyncGenerator` here too. This declares only
|
|
89
|
+
* the synchronous half, because that is the half this integration uses and the
|
|
90
|
+
* narrower type still satisfies the wider one — the same reason `frame` is
|
|
91
|
+
* required below though it is optional there. Opening a fabricator scope is
|
|
92
|
+
* synchronous, and declaring the async arm would invite an integration that
|
|
93
|
+
* promotes every test in the suite to a promise for no reason.
|
|
94
|
+
*/
|
|
95
|
+
export type Frame<$Established = void> = Generator<Wrapper<$Established> | void, void, unknown>;
|
|
51
96
|
/**
|
|
52
97
|
* What `integration(instance)` is, as `@ghostry/harness`'s `initialize` sees
|
|
53
98
|
* it. The subset of that package's contract this integration actually uses, not
|
|
54
|
-
* a copy of all of it:
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
* though optional there, because this integration always declares it.
|
|
99
|
+
* a copy of all of it: an object lacking an optional member still satisfies the
|
|
100
|
+
* contract structurally, so `frame` is required here though optional there,
|
|
101
|
+
* because this integration always declares it.
|
|
58
102
|
*
|
|
59
103
|
* `provides` is the _only_ source of context keys; `initialize` rejects a
|
|
60
104
|
* collision across integrations by reading `Object.keys(provides)`. Each
|
|
61
|
-
* provider runs
|
|
62
|
-
*
|
|
63
|
-
* work and what lets frames nest.
|
|
105
|
+
* provider runs _inside_ that frame and receives the same `$Established` the
|
|
106
|
+
* wrapper handed to the body.
|
|
64
107
|
*/
|
|
65
|
-
export type Integration<$Context extends object> = {
|
|
108
|
+
export type Integration<$Context extends object, $Established = void> = {
|
|
66
109
|
readonly name: string;
|
|
67
|
-
readonly provides: Provides<$Context>;
|
|
68
|
-
|
|
110
|
+
readonly provides: Provides<$Context, $Established>;
|
|
111
|
+
frame(args: FrameArgs): Frame<$Established>;
|
|
69
112
|
};
|
|
70
113
|
/**
|
|
71
114
|
* The slice of the test context `integration(instance)` contributes — one key,
|
|
@@ -26,6 +26,6 @@ export type Fabricator<$Schema extends {
|
|
|
26
26
|
* shared across every element. The weighted pick happens on this field's
|
|
27
27
|
* private stream; only the chosen option's `fabricate()` is called, so an
|
|
28
28
|
* unpicked option never advances its stream (safe for the same reason skipping
|
|
29
|
-
* `object.omittable`'s inner draw is — see
|
|
29
|
+
* `object.omittable`'s inner draw is — see AGENTS.md's "Randomness").
|
|
30
30
|
*/
|
|
31
31
|
export declare function Fabricator<$Items extends Items>(context: FabricatorContext<Schema<$Items>>, weightings: ReadonlyArray<readonly [number, NaiveFabricator<any>]>): Fabricator<Schema<$Items>>;
|
|
@@ -24,7 +24,7 @@ export type Fabricator<$Schema extends {
|
|
|
24
24
|
* `source` is already dispatched into its own independent stream by
|
|
25
25
|
* `Constructor.ts` regardless of this roll, skipping its draw here can never
|
|
26
26
|
* perturb any other field's reproducibility (see
|
|
27
|
-
* `object/omittable/Fabricator.ts`, and
|
|
27
|
+
* `object/omittable/Fabricator.ts`, and AGENTS.md's "Randomness"). 50/50 by
|
|
28
28
|
* default; `.weighted(...)` (`Schema.ts`) reweights either outcome relative to
|
|
29
29
|
* that same default of `1`.
|
|
30
30
|
*/
|
|
@@ -24,12 +24,12 @@ export type Fabricator<$Schema extends {
|
|
|
24
24
|
* by default; not achievable by composing `nullable(T.undefinable(inner))`'s
|
|
25
25
|
* two independent 50/50 rolls (that is 50/25/25, not 33/33/33), hence the
|
|
26
26
|
* dedicated three-way `weighted()` here — see `object/optional/Fabricator.ts`
|
|
27
|
-
* and
|
|
27
|
+
* and AGENTS.md's "Why `T.optional` isn't `omittable(undefinable(inner))`".
|
|
28
28
|
* `.weighted(...)` (`Schema.ts`) reweights individual outcomes relative to that
|
|
29
29
|
* same default of `1`; an unspecified outcome keeps it. Skipping `source`'s
|
|
30
30
|
* draw on the two non-"value" outcomes is safe for the same reason as
|
|
31
31
|
* `object/omittable/Fabricator.ts`: `source` already has its own independent
|
|
32
|
-
* stream, minted at build time regardless of this roll (see
|
|
32
|
+
* stream, minted at build time regardless of this roll (see AGENTS.md's
|
|
33
33
|
* "Randomness").
|
|
34
34
|
*/
|
|
35
35
|
export declare function Fabricator<$Definition extends Definition>(context: FabricatorContext<Schema<$Definition>>, source: BaseFabricator<any>): Fabricator<Schema<$Definition>>;
|
|
@@ -32,12 +32,12 @@ export declare function isObjectOptionalFabricator(candidate: BaseFabricator<unk
|
|
|
32
32
|
* Uniform (1/3 each) by default; not achievable by composing
|
|
33
33
|
* `object.omittable(T.undefinable(inner))`'s two independent 50/50 rolls (that
|
|
34
34
|
* is 50/25/25, not 33/33/33), hence the dedicated three-way `weighted()` here —
|
|
35
|
-
* see
|
|
35
|
+
* see AGENTS.md's "Why `T.optional` isn't `omittable(undefinable(inner))`"
|
|
36
36
|
* under "Compound / field-only kinds". `.weighted(...)` (`Schema.ts`) reweights
|
|
37
37
|
* individual outcomes relative to that same default of `1`; an unspecified
|
|
38
38
|
* outcome keeps it. Skipping `source`'s draw on the two non-"value" outcomes is
|
|
39
39
|
* safe for the same reason as `object/omittable/Fabricator.ts`: `source`
|
|
40
40
|
* already has its own independent stream, minted at build time regardless of
|
|
41
|
-
* this roll (see
|
|
41
|
+
* this roll (see AGENTS.md's "Randomness").
|
|
42
42
|
*/
|
|
43
43
|
export declare function Fabricator<$Definition extends Definition>(context: FabricatorContext<Schema<$Definition>>, source: BaseFabricator<any>): Fabricator<Schema<$Definition>>;
|
|
@@ -6,7 +6,7 @@ import { Produces, type Adaptation, type Kind, type Meta } from "../../Types";
|
|
|
6
6
|
* `TS2456` ("`RecursiveValue` circularly references itself") — legal _only_
|
|
7
7
|
* because `ValueOf`'s second argument is read through an interface member
|
|
8
8
|
* (`this["bindings"]` on every composite `Core`), which TypeScript defers. Do
|
|
9
|
-
* not rewrite as a conditional; that reintroduces the error. See `
|
|
9
|
+
* not rewrite as a conditional; that reintroduces the error. See `AGENTS.md`'s
|
|
10
10
|
* "`ValueOf`'s `$Bindings`".
|
|
11
11
|
*
|
|
12
12
|
* Wherever `self` sits in `$Body` (nested through `array`/`object`/`tuple`/
|
|
@@ -15,7 +15,7 @@ export type Fabricated<$Bindings extends unknown[] = []> = $Bindings[0];
|
|
|
15
15
|
export type Meta = Record<string, never>;
|
|
16
16
|
/**
|
|
17
17
|
* An `interface`, not a `type` alias, so `this["bindings"]` resolves — the
|
|
18
|
-
* entire mechanism this kind exists for. See `
|
|
18
|
+
* entire mechanism this kind exists for. See `AGENTS.md`'s "`ValueOf`'s
|
|
19
19
|
* `$Bindings`" section for why threading requires this.
|
|
20
20
|
*/
|
|
21
21
|
export interface Core<$Adaptations extends Adaptations = {}> {
|
|
@@ -24,7 +24,7 @@ export type Fabricator<$Schema extends {
|
|
|
24
24
|
* since `source` is already dispatched into its own independent stream by
|
|
25
25
|
* `Constructor.ts` regardless of this roll, skipping its draw here can never
|
|
26
26
|
* perturb any other field's reproducibility (see
|
|
27
|
-
* `object/omittable/Fabricator.ts`, and
|
|
27
|
+
* `object/omittable/Fabricator.ts`, and AGENTS.md's "Randomness"). 50/50 by
|
|
28
28
|
* default; `.weighted(...)` (`Schema.ts`) reweights either outcome relative to
|
|
29
29
|
* that same default of `1`.
|
|
30
30
|
*/
|
|
@@ -11,20 +11,32 @@
|
|
|
11
11
|
* package's contract this integration uses, satisfied structurally, so neither
|
|
12
12
|
* depends on the other.
|
|
13
13
|
*
|
|
14
|
+
* **Mirrors the integration contract of `@ghostry/harness` 0.0.4.** Structural
|
|
15
|
+
* satisfaction is what keeps the two packages independent, and it is also what
|
|
16
|
+
* leaves nothing to check the pairing at install time: neither manifest names
|
|
17
|
+
* the other, so a mismatched pair is caught by `tsc` at the point a consumer
|
|
18
|
+
* passes `integration(instance)` to `initialize`, with an error about shapes
|
|
19
|
+
* rather than about versions. This line is the only record of which version the
|
|
20
|
+
* copy tracks, so move it whenever that contract does — the types below are
|
|
21
|
+
* where the drift actually lives.
|
|
22
|
+
*
|
|
14
23
|
* @module
|
|
15
24
|
*/
|
|
16
25
|
/**
|
|
17
26
|
* Decorate an existing `Instance` as a `@ghostry/harness` integration — `{
|
|
18
|
-
* name, provides,
|
|
27
|
+
* name, provides, frame }`. Takes an instance rather than minting one: the
|
|
19
28
|
* caller owns `initialize(...)` (and in particular the suite-wide `clock`), and
|
|
20
29
|
* this only wraps each test body in that instance's per-identity `wrap`.
|
|
21
30
|
*/
|
|
22
31
|
export { integration } from "./Harnessing/Core";
|
|
23
32
|
/**
|
|
24
33
|
* `Identity` is what identifies one registered test or suite; `Integration` is
|
|
25
|
-
* the `{ name, provides,
|
|
26
|
-
* `provides` is a `Provides<$Context>`, one `Provider` per
|
|
27
|
-
*
|
|
28
|
-
*
|
|
34
|
+
* the `{ name, provides, frame }` shape `integration(instance)` returns —
|
|
35
|
+
* `provides` is a `Provides<$Context, $Established>`, one `Provider` per
|
|
36
|
+
* context key, each handed what the frame's wrapper established; `Frame` is the
|
|
37
|
+
* generator `frame` returns and `Wrapper` the optional value it yields; both
|
|
38
|
+
* hooks take a single object — `FrameArgs` for `frame`, and `ProviderArgs`, the
|
|
39
|
+
* same thing plus `established`, for a provider; `FabricatorTestContext` is the
|
|
40
|
+
* `{ fabricator }` slice of the test context this integration contributes.
|
|
29
41
|
*/
|
|
30
|
-
export type { FabricatorTestContext, Identity, Integration, Provider, Provides, } from "./Harnessing/Types";
|
|
42
|
+
export type { FabricatorTestContext, Frame, FrameArgs, Identity, Integration, Provider, ProviderArgs, Provides, Wrapper, } from "./Harnessing/Types";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ghostry/fabricator",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.6",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Fabricate typed data from composable schemas.",
|
|
6
6
|
"keywords": [
|
|
@@ -64,6 +64,9 @@
|
|
|
64
64
|
"access": "public",
|
|
65
65
|
"provenance": true
|
|
66
66
|
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@ghostry/harness": "^0.0.4"
|
|
69
|
+
},
|
|
67
70
|
"scripts": {
|
|
68
71
|
"build:reset": "rm -rf ./dist",
|
|
69
72
|
"build:compile": "bunx --no-install --bun rslib build --config ./rslib.config.ts",
|