@ghostry/fabricator 0.0.7 → 0.0.9

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 (96) hide show
  1. package/dist/esm/Bound.js +6 -1
  2. package/dist/esm/Distribution/index.js +10 -1
  3. package/dist/esm/Enumeration/Plan.js +16 -0
  4. package/dist/esm/Error/index.js +8 -0
  5. package/dist/esm/Fabricator/Constructor.js +13 -0
  6. package/dist/esm/Instance/Core.js +8 -8
  7. package/dist/esm/Primitive/always/Schema.js +2 -0
  8. package/dist/esm/Primitive/array/Fabricator.js +4 -9
  9. package/dist/esm/Primitive/array/Schema.js +2 -0
  10. package/dist/esm/Primitive/bigint/Schema.js +2 -0
  11. package/dist/esm/Primitive/boolean/Schema.js +2 -0
  12. package/dist/esm/Primitive/choice/Schema.js +2 -0
  13. package/dist/esm/Primitive/date/Schema.js +2 -0
  14. package/dist/esm/Primitive/derive/Fabricator.js +31 -0
  15. package/dist/esm/Primitive/derive/Registry.js +17 -0
  16. package/dist/esm/Primitive/derive/Schema.js +12 -0
  17. package/dist/esm/Primitive/derive/Types.js +1 -0
  18. package/dist/esm/Primitive/derive/index.js +5 -0
  19. package/dist/esm/Primitive/enum/Schema.js +2 -0
  20. package/dist/esm/Primitive/index.js +1 -0
  21. package/dist/esm/Primitive/namespace.js +2 -1
  22. package/dist/esm/Primitive/null/Schema.js +2 -0
  23. package/dist/esm/Primitive/nullable/Schema.js +2 -0
  24. package/dist/esm/Primitive/nullish/Schema.js +2 -0
  25. package/dist/esm/Primitive/number/Fabricator.js +3 -7
  26. package/dist/esm/Primitive/number/Schema.js +2 -0
  27. package/dist/esm/Primitive/object/Registry.js +8 -8
  28. package/dist/esm/Primitive/object/Schema.js +3 -1
  29. package/dist/esm/Primitive/object/compute/Schema.js +2 -0
  30. package/dist/esm/Primitive/object/omittable/Schema.js +2 -0
  31. package/dist/esm/Primitive/object/optional/Schema.js +2 -0
  32. package/dist/esm/Primitive/opaque/Schema.js +2 -0
  33. package/dist/esm/Primitive/record/Fabricator.js +6 -1
  34. package/dist/esm/Primitive/record/Schema.js +2 -0
  35. package/dist/esm/Primitive/recursive/Schema.js +2 -0
  36. package/dist/esm/Primitive/recursive/Terminate.js +17 -0
  37. package/dist/esm/Primitive/recursive/self/Schema.js +2 -0
  38. package/dist/esm/Primitive/string/Fabricator.js +6 -2
  39. package/dist/esm/Primitive/string/Schema.js +2 -0
  40. package/dist/esm/Primitive/symbol/Schema.js +2 -0
  41. package/dist/esm/Primitive/tuple/Schema.js +2 -0
  42. package/dist/esm/Primitive/undefinable/Schema.js +2 -0
  43. package/dist/esm/Primitive/undefined/Schema.js +2 -0
  44. package/dist/esm/Schema/Core.js +13 -1
  45. package/dist/esm/Schema/Satisfies.js +5 -0
  46. package/dist/esm/index.js +2 -0
  47. package/dist/types/Bound.d.ts +20 -4
  48. package/dist/types/Distribution/index.d.ts +19 -0
  49. package/dist/types/Enumeration/Types.d.ts +4 -3
  50. package/dist/types/Error/index.d.ts +37 -7
  51. package/dist/types/Fabricator/Types.d.ts +1 -1
  52. package/dist/types/Harnessing/Core.d.ts +4 -4
  53. package/dist/types/Primitive/always/Schema.d.ts +8 -1
  54. package/dist/types/Primitive/array/Registry.d.ts +7 -5
  55. package/dist/types/Primitive/array/Schema.d.ts +7 -0
  56. package/dist/types/Primitive/array/Types.d.ts +3 -9
  57. package/dist/types/Primitive/bigint/Schema.d.ts +7 -0
  58. package/dist/types/Primitive/boolean/Schema.d.ts +7 -0
  59. package/dist/types/Primitive/choice/Registry.d.ts +16 -6
  60. package/dist/types/Primitive/choice/Schema.d.ts +7 -0
  61. package/dist/types/Primitive/date/Schema.d.ts +7 -0
  62. package/dist/types/Primitive/derive/Fabricator.d.ts +44 -0
  63. package/dist/types/Primitive/derive/Registry.d.ts +6 -0
  64. package/dist/types/Primitive/derive/Schema.d.ts +44 -0
  65. package/dist/types/Primitive/derive/Types.d.ts +38 -0
  66. package/dist/types/Primitive/derive/index.d.ts +5 -0
  67. package/dist/types/Primitive/enum/Registry.d.ts +19 -9
  68. package/dist/types/Primitive/enum/Schema.d.ts +7 -0
  69. package/dist/types/Primitive/index.d.ts +3 -2
  70. package/dist/types/Primitive/namespace.d.ts +1 -0
  71. package/dist/types/Primitive/null/Schema.d.ts +9 -2
  72. package/dist/types/Primitive/nullable/Schema.d.ts +7 -0
  73. package/dist/types/Primitive/nullish/Schema.d.ts +7 -0
  74. package/dist/types/Primitive/number/Schema.d.ts +7 -0
  75. package/dist/types/Primitive/object/Schema.d.ts +12 -2
  76. package/dist/types/Primitive/object/compute/Schema.d.ts +8 -0
  77. package/dist/types/Primitive/object/omittable/Schema.d.ts +10 -1
  78. package/dist/types/Primitive/object/optional/Schema.d.ts +9 -1
  79. package/dist/types/Primitive/opaque/Schema.d.ts +8 -1
  80. package/dist/types/Primitive/record/Schema.d.ts +7 -0
  81. package/dist/types/Primitive/record/Types.d.ts +2 -2
  82. package/dist/types/Primitive/recursive/Schema.d.ts +8 -1
  83. package/dist/types/Primitive/recursive/self/Schema.d.ts +11 -1
  84. package/dist/types/Primitive/string/Registry.d.ts +13 -9
  85. package/dist/types/Primitive/string/Schema.d.ts +7 -0
  86. package/dist/types/Primitive/string/Types.d.ts +7 -11
  87. package/dist/types/Primitive/symbol/Schema.d.ts +7 -0
  88. package/dist/types/Primitive/tuple/Schema.d.ts +7 -0
  89. package/dist/types/Primitive/undefinable/Schema.d.ts +7 -0
  90. package/dist/types/Primitive/undefined/Schema.d.ts +9 -2
  91. package/dist/types/Schema/Core.d.ts +13 -0
  92. package/dist/types/Schema/Satisfies.d.ts +84 -0
  93. package/dist/types/Schema/Types.d.ts +17 -1
  94. package/dist/types/Types.d.ts +4 -3
  95. package/dist/types/index.d.ts +18 -0
  96. package/package.json +1 -1
@@ -1,9 +1,11 @@
1
1
  import { withAdaptations } from "../../Adapter/Core.js";
2
+ import { schemaSatisfies } from "../../Schema/Satisfies.js";
2
3
  import { Kind } from "../../Types.js";
3
4
  function Schema(schema) {
4
5
  return {
5
6
  ...schema,
6
7
  [Kind]: "recursive",
8
+ satisfies: schemaSatisfies,
7
9
  adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
8
10
  };
9
11
  }
@@ -80,6 +80,18 @@ function terminateAt(schema, path) {
80
80
  ]))
81
81
  });
82
82
  }
83
+ case "derive":
84
+ {
85
+ const s = schema;
86
+ return withMeta(s, {
87
+ ...s[Meta],
88
+ from: s[Meta].from.map((item, i)=>terminateAt(item, [
89
+ ...path,
90
+ "from",
91
+ String(i)
92
+ ]))
93
+ });
94
+ }
83
95
  case "object.compute":
84
96
  {
85
97
  const s = schema;
@@ -164,6 +176,11 @@ function containsSelf(schema) {
164
176
  const s = schema;
165
177
  return s[Meta].items.some((item)=>containsSelf(item));
166
178
  }
179
+ case "derive":
180
+ {
181
+ const s = schema;
182
+ return s[Meta].from.some((item)=>containsSelf(item));
183
+ }
167
184
  case "object.compute":
168
185
  {
169
186
  const s = schema;
@@ -1,9 +1,11 @@
1
1
  import { withAdaptations } from "../../../Adapter/Core.js";
2
+ import { schemaSatisfies } from "../../../Schema/Satisfies.js";
2
3
  import { Kind } from "../../../Types.js";
3
4
  function Schema(schema) {
4
5
  return {
5
6
  ...schema,
6
7
  [Kind]: "recursive.self",
8
+ satisfies: schemaSatisfies,
7
9
  adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
8
10
  };
9
11
  }
@@ -1,5 +1,5 @@
1
1
  import { effectiveDiscrete } from "../../Bound.js";
2
- import { weighted } from "../../Distribution/index.js";
2
+ import { discreteSampler, weighted } from "../../Distribution/index.js";
3
3
  import { toStreamFromTrace } from "../../Random/index.js";
4
4
  import { Kind, Meta } from "../../Types.js";
5
5
  import { inline } from "../../Utility/Core.js";
@@ -26,6 +26,10 @@ function Fabricator(context) {
26
26
  const stream = toStreamFromTrace(algorithm, trace);
27
27
  const whereby = meta.whereby;
28
28
  const { min, max } = effectiveDiscrete(whereby.length.min, whereby.length.max);
29
+ const drawLength = discreteSampler(whereby.length.distribution, {
30
+ min,
31
+ max
32
+ }, stream);
29
33
  const sources = inline(()=>{
30
34
  const composition = whereby.composition;
31
35
  if (void 0 === composition) return [
@@ -46,7 +50,7 @@ function Fabricator(context) {
46
50
  ]), stream, "T.string.whereby.composition");
47
51
  const fill = compiler(bmpScalars, stream);
48
52
  const fabricate = ()=>{
49
- const length = min + Math.floor(stream.next() * (max - min + 1));
53
+ const length = drawLength();
50
54
  const pieces = [];
51
55
  let remaining = length;
52
56
  while(remaining > 0){
@@ -1,4 +1,5 @@
1
1
  import { withAdaptations } from "../../Adapter/Core.js";
2
+ import { schemaSatisfies } from "../../Schema/Satisfies.js";
2
3
  import { Kind, Meta } from "../../Types.js";
3
4
  function Schema(schema) {
4
5
  return {
@@ -12,6 +13,7 @@ function Schema(schema) {
12
13
  hints
13
14
  }
14
15
  }),
16
+ satisfies: schemaSatisfies,
15
17
  adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
16
18
  };
17
19
  }
@@ -1,4 +1,5 @@
1
1
  import { withAdaptations } from "../../Adapter/Core.js";
2
+ import { schemaSatisfies } from "../../Schema/Satisfies.js";
2
3
  import { Kind, Meta } from "../../Types.js";
3
4
  function Schema(schema) {
4
5
  return {
@@ -11,6 +12,7 @@ function Schema(schema) {
11
12
  produce
12
13
  }
13
14
  }),
15
+ satisfies: schemaSatisfies,
14
16
  adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
15
17
  };
16
18
  }
@@ -1,4 +1,5 @@
1
1
  import { withAdaptations } from "../../Adapter/Core.js";
2
+ import { schemaSatisfies } from "../../Schema/Satisfies.js";
2
3
  import { Kind, Meta } from "../../Types.js";
3
4
  function Schema(schema) {
4
5
  return {
@@ -11,6 +12,7 @@ function Schema(schema) {
11
12
  produce
12
13
  }
13
14
  }),
15
+ satisfies: schemaSatisfies,
14
16
  adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
15
17
  };
16
18
  }
@@ -1,5 +1,6 @@
1
1
  import { withAdaptations } from "../../Adapter/Core.js";
2
2
  import { assertDrawableKeyedWeights } from "../../Distribution/index.js";
3
+ import { schemaSatisfies } from "../../Schema/Satisfies.js";
3
4
  import { Kind, Meta } from "../../Types.js";
4
5
  import { outcomes } from "./Outcomes.js";
5
6
  function Schema(schema) {
@@ -13,6 +14,7 @@ function Schema(schema) {
13
14
  produce
14
15
  }
15
16
  }),
17
+ satisfies: schemaSatisfies,
16
18
  weighted: (weights)=>{
17
19
  const merged = {
18
20
  ...schema[Meta].weights,
@@ -1,9 +1,11 @@
1
1
  import { withAdaptations } from "../../Adapter/Core.js";
2
+ import { schemaSatisfies } from "../../Schema/Satisfies.js";
2
3
  import { Kind } from "../../Types.js";
3
4
  function Schema(schema) {
4
5
  return {
5
6
  ...schema,
6
7
  [Kind]: "undefined",
8
+ satisfies: schemaSatisfies,
7
9
  adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
8
10
  };
9
11
  }
@@ -11,6 +11,16 @@ function toSchema(value) {
11
11
  [Meta]: value[Meta]
12
12
  };
13
13
  }
14
+ function toValueKind(schema) {
15
+ switch(schema[Kind]){
16
+ case "object.compute":
17
+ return toValueKind(schema[Meta].source);
18
+ case "derive":
19
+ return toValueKind(schema[Meta].to);
20
+ default:
21
+ return schema[Kind];
22
+ }
23
+ }
14
24
  function violatesKind(kind, value) {
15
25
  switch(kind){
16
26
  case "object":
@@ -55,6 +65,8 @@ function violatesKind(kind, value) {
55
65
  return false;
56
66
  case "object.compute":
57
67
  return false;
68
+ case "derive":
69
+ return false;
58
70
  case "object.omittable":
59
71
  return false;
60
72
  case "object.optional":
@@ -65,4 +77,4 @@ function violatesKind(kind, value) {
65
77
  return never(kind);
66
78
  }
67
79
  }
68
- export { toSchema, violatesKind };
80
+ export { toSchema, toValueKind, violatesKind };
@@ -0,0 +1,5 @@
1
+ function schemaSatisfies() {
2
+ return this;
3
+ }
4
+ function satisfies(buildable) {}
5
+ export { satisfies, schemaSatisfies };
package/dist/esm/index.js CHANGED
@@ -9,4 +9,6 @@ export { layer } from "./Random/index.js";
9
9
  export { FabricatorError } from "./Error/index.js";
10
10
  export { registry } from "./Schema/Registry.js";
11
11
  export { effectiveDiscrete, toBound } from "./Bound.js";
12
+ export { sample, shuffle } from "./Distribution/index.js";
13
+ export { satisfies } from "./Schema/Satisfies.js";
12
14
  export { initialize };
@@ -1,3 +1,4 @@
1
+ import type { Distribution } from "./Distribution";
1
2
  /**
2
3
  * Canonical endpoint stored on `[Meta].whereby`. Call sites still accept a
3
4
  * scalar (inclusive) via {@link InputBound}; `.whereby()` runs {@link toBound} so
@@ -56,13 +57,28 @@ export declare function towardInterior(value: number, toward: number): number;
56
57
  export declare function constrainContinuous(next: () => number, min: Bound<number>, max: Bound<number>): () => number;
57
58
  export declare function epochBound(bound: Bound<Date>): Bound<number>;
58
59
  /**
59
- * Array/string `length`: a bare number is an exact count; an omitted `min` is
60
- * inclusive `0`. Always stored as a Bound pair so adapters have one path.
60
+ * Call-site `length` for `string` and `array`: a bare number is an exact count,
61
+ * and `{ max, min?, distribution? }` is a range. A scalar end is inclusive.
61
62
  */
62
- export declare function toLengthRange(length: number | {
63
+ export type InputLength = number | {
63
64
  max: InputBound<number>;
64
65
  min?: InputBound<number> | undefined;
65
- }, label: string): {
66
+ distribution?: Distribution | undefined;
67
+ };
68
+ /**
69
+ * Stored `length`: both ends are canonical {@link Bound}s. `distribution` is
70
+ * present only when the caller set one, so a bare count and an unspecified
71
+ * distribution stay off the stored object.
72
+ */
73
+ export type Length = {
66
74
  min: Bound<number>;
67
75
  max: Bound<number>;
76
+ distribution?: Distribution | undefined;
68
77
  };
78
+ /**
79
+ * Array/string `length`: a bare number is an exact count (no distribution); an
80
+ * omitted `min` is inclusive `0`. Always stored as a Bound pair so adapters
81
+ * have one path. `distribution` is copied only when the caller set one,
82
+ * matching `date/Registry.ts`'s `toWhereby`, so stored meta stays minimal.
83
+ */
84
+ export declare function toLengthRange(length: InputLength, label: string): Length;
@@ -88,6 +88,20 @@ export declare namespace Distribution {
88
88
  */
89
89
  const custom: (shape: (u: number) => number) => Distribution;
90
90
  }
91
+ /**
92
+ * Integer draw over the inclusive `[min, max]` of `range`. Samples `[min, max +
93
+ * 1)` and floors, so each integer owns a full-width bucket — avoiding the
94
+ * half-width endpoint bias that rounding a `[min, max]` draw would produce. An
95
+ * omitted `distribution` is uniform. `range` is already the effective inclusive
96
+ * interval; exclusive ends are the caller's to resolve (`effectiveDiscrete`).
97
+ *
98
+ * The `Math.min(max, …)` clamp is load-bearing, not defensive. `max + 1` is
99
+ * exact even at `Number.MAX_SAFE_INTEGER` (it is 2^53), but near there doubles
100
+ * are spaced 1 apart, so `sampler`'s `(1 - u) * min + u * (max + 1)` can round
101
+ * up to exactly `max + 1` — e.g. `[MAX_SAFE_INTEGER - 10, MAX_SAFE_INTEGER]` —
102
+ * which would floor to one past `max`.
103
+ */
104
+ export declare function discreteSampler(distribution: Distribution | undefined, range: Range, stream: Stream): () => number;
91
105
  /**
92
106
  * Build a sampler that draws values within `range` following `distribution`.
93
107
  * Each call consumes one fresh uniform draw and, by construction, returns a
@@ -96,6 +110,11 @@ export declare namespace Distribution {
96
110
  * clamped.
97
111
  */
98
112
  export declare function sampler(distribution: Distribution, range: Range, stream: Stream): () => number;
113
+ /**
114
+ * A uniform pick from `list`. One `stream.next()` per call; `list` is not
115
+ * mutated. An empty list throws {@link FabricatorError.EmptyItemsError} — there
116
+ * is no member to return.
117
+ */
99
118
  export declare function sample<$T>(list: ReadonlyArray<$T>, stream: Stream): $T;
100
119
  /**
101
120
  * Fisher–Yates (Durstenfeld) shuffle: a new array holding `items` in a
@@ -29,9 +29,10 @@ export type Orderer = (width: bigint) => ReadonlyArray<bigint>;
29
29
  * Recipe for one enumerated combination against a built Fabricator tree:
30
30
  * `undefined` for a drawn (not chosen) node — fabricate normally; `{ value }`
31
31
  * for a literal (enum member, boolean, `null`, `undefined`, `Omitted`); `{
32
- * slots }`/`{ fields }` for a tuple/object, recursing per position/key; `{
33
- * branch, inner }` for a choice's chosen option, or a presence wrapper's
34
- * "present" arm, recursing into whichever child was picked.
32
+ * slots }` for a tuple or a derive's `from`, recursing per position; `{ fields
33
+ * }` for an object, recursing per key; `{ branch, inner }` for a choice's
34
+ * chosen option, or a presence wrapper's "present" arm, recursing into
35
+ * whichever child was picked.
35
36
  */
36
37
  export type Pin = undefined | {
37
38
  value: unknown;
@@ -112,26 +112,33 @@ export declare namespace FabricatorError {
112
112
  during: Phase);
113
113
  }
114
114
  /**
115
- * `T.enum`/`T.choice` given no items. An empty draw table has nothing to
116
- * select, which would otherwise surface as an opaque `TypeError` inside
117
- * `weighted()` at fabricate time rather than here.
115
+ * A pick with nothing to pick from: `T.enum`/`T.choice` given no items, or
116
+ * `sample()` (`Distribution/index.ts`) given an empty list. Emptiness is
117
+ * otherwise only discovered mid-draw, and opaquely — as a `TypeError` inside
118
+ * `weighted()` for the registries, whose empty draw table makes `.find`
119
+ * return `undefined`, and as an out-of-range element read for `sample`.
120
+ *
121
+ * The registries throw at construction, where the mistake is; `sample` can
122
+ * only throw at fabricate time, since a producer is the only place it runs.
118
123
  */
119
124
  class EmptyItemsError extends FabricatorError {
120
125
  /**
121
- * The registry entry that was called, e.g. `"T.enum.uniform"`.
126
+ * The registry entry or helper that was called, e.g. `"T.enum"` or
127
+ * `"sample"`.
122
128
  */
123
129
  readonly label: string;
124
130
  /**
125
- * What the kind calls its items, e.g. `"member"`/`"option"`.
131
+ * What the caller calls its items, e.g. `"member"`/`"option"`/`"item"`.
126
132
  */
127
133
  readonly noun: string;
128
134
  constructor(
129
135
  /**
130
- * The registry entry that was called, e.g. `"T.enum.uniform"`.
136
+ * The registry entry or helper that was called, e.g. `"T.enum"` or
137
+ * `"sample"`.
131
138
  */
132
139
  label: string,
133
140
  /**
134
- * What the kind calls its items, e.g. `"member"`/`"option"`.
141
+ * What the caller calls its items, e.g. `"member"`/`"option"`/`"item"`.
135
142
  */
136
143
  noun: string);
137
144
  }
@@ -182,6 +189,29 @@ export declare namespace FabricatorError {
182
189
  */
183
190
  value: unknown);
184
191
  }
192
+ /**
193
+ * A `T.derive` resolver returned a value of the wrong shape for the `to`
194
+ * schema it was declared against.
195
+ */
196
+ class DeriveResultMismatchError extends FabricatorError {
197
+ /**
198
+ * The `[Kind]` of the derive's `to` schema.
199
+ */
200
+ readonly kind: string;
201
+ /**
202
+ * What the resolver actually returned.
203
+ */
204
+ readonly value: unknown;
205
+ constructor(
206
+ /**
207
+ * The `[Kind]` of the derive's `to` schema.
208
+ */
209
+ kind: string,
210
+ /**
211
+ * What the resolver actually returned.
212
+ */
213
+ value: unknown);
214
+ }
185
215
  /**
186
216
  * An override names a field the object schema does not define — most often a
187
217
  * typo, which is why the known fields are listed.
@@ -138,6 +138,6 @@ $Schema extends Primitive.object.Core ? Primitive.object.Fabricator<$Schema> : $
138
138
  * `Constructor.ts`'s `make` context, not through this boundary. `[]`
139
139
  * (unbound) is the same default `self.Core` itself falls back to.
140
140
  */
141
- $Schema extends Primitive.recursive.self.Core ? Primitive.recursive.self.Fabricator<[]> : $Schema extends Primitive.tuple.Core ? Primitive.tuple.Fabricator<$Schema> : $Schema extends Primitive.always.Core ? Primitive.always.Fabricator<$Schema> : $Schema extends Primitive.bigint.Core ? Primitive.bigint.Fabricator<$Schema> : $Schema extends Primitive.boolean.Core ? Primitive.boolean.Fabricator<$Schema> : $Schema extends Primitive.choice.Core ? Primitive.choice.Fabricator<$Schema> : $Schema extends Primitive.enum.Core ? Primitive.enum.Fabricator<$Schema> : $Schema extends Primitive.date.Core ? Primitive.date.Fabricator<$Schema> : $Schema extends Primitive.number.Core ? Primitive.number.Fabricator<$Schema> : $Schema extends Primitive.string.Core ? Primitive.string.Fabricator<$Schema> : $Schema extends Primitive.symbol.Core ? Primitive.symbol.Fabricator<$Schema> : $Schema extends Primitive.undefined.Core ? Primitive.undefined.Fabricator<$Schema> : $Schema extends Primitive.undefinable.Core ? Primitive.undefinable.Fabricator<$Schema> : $Schema extends Primitive.null.Core ? Primitive.null.Fabricator<$Schema> : $Schema extends Primitive.nullable.Core ? Primitive.nullable.Fabricator<$Schema> : $Schema extends Primitive.nullish.Core ? Primitive.nullish.Fabricator<$Schema> : $Schema extends {
141
+ $Schema extends Primitive.recursive.self.Core ? Primitive.recursive.self.Fabricator<[]> : $Schema extends Primitive.tuple.Core ? Primitive.tuple.Fabricator<$Schema> : $Schema extends Primitive.derive.Core ? Primitive.derive.Fabricator<$Schema> : $Schema extends Primitive.always.Core ? Primitive.always.Fabricator<$Schema> : $Schema extends Primitive.bigint.Core ? Primitive.bigint.Fabricator<$Schema> : $Schema extends Primitive.boolean.Core ? Primitive.boolean.Fabricator<$Schema> : $Schema extends Primitive.choice.Core ? Primitive.choice.Fabricator<$Schema> : $Schema extends Primitive.enum.Core ? Primitive.enum.Fabricator<$Schema> : $Schema extends Primitive.date.Core ? Primitive.date.Fabricator<$Schema> : $Schema extends Primitive.number.Core ? Primitive.number.Fabricator<$Schema> : $Schema extends Primitive.string.Core ? Primitive.string.Fabricator<$Schema> : $Schema extends Primitive.symbol.Core ? Primitive.symbol.Fabricator<$Schema> : $Schema extends Primitive.undefined.Core ? Primitive.undefined.Fabricator<$Schema> : $Schema extends Primitive.undefinable.Core ? Primitive.undefinable.Fabricator<$Schema> : $Schema extends Primitive.null.Core ? Primitive.null.Fabricator<$Schema> : $Schema extends Primitive.nullable.Core ? Primitive.nullable.Fabricator<$Schema> : $Schema extends Primitive.nullish.Core ? Primitive.nullish.Fabricator<$Schema> : $Schema extends {
142
142
  readonly [Produces]?: unknown;
143
143
  } ? Fabricator<ValueOf<$Schema>> : Fabricator<unknown>;
@@ -35,10 +35,10 @@ import type { FabricatorTestContext, Integration } from "./Types";
35
35
  * captured result would pin one frame.
36
36
  *
37
37
  * The per-test wrap is entered on `context.scope()`, so it governs that
38
- * instance and its ancestors: the integrated instance when nothing encloses
39
- * it, and that instance as an ancestor of the enclosing scope when something
40
- * does. It does not govern forks of the integrated instance. Per-test data
41
- * therefore comes from the instance handed to `integration(...)`, from
38
+ * instance and its ancestors: the integrated instance when nothing encloses it,
39
+ * and that instance as an ancestor of the enclosing scope when something does.
40
+ * It does not govern forks of the integrated instance. Per-test data therefore
41
+ * comes from the instance handed to `integration(...)`, from
42
42
  * `context.fabricator`, or from a fork of that scope — a module-level fork of
43
43
  * the integrated instance draws the same data in every test, and because its
44
44
  * construction counter runs across tests, which values a test gets depends on
@@ -1,6 +1,7 @@
1
1
  import { type AdaptationEntry } from "../../Adapter/Core";
2
2
  import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
- import type { Core, Value } from "./Types";
3
+ import { type SatisfiesThis } from "../../Schema/Satisfies";
4
+ import type { Core, Fabricated, Value } from "./Types";
4
5
  /**
5
6
  * A fixed value, so unlike every other kind there is no `.as()` — nothing left
6
7
  * to override about producing it. `adapt` still applies: what a value _means_
@@ -10,6 +11,12 @@ import type { Core, Value } from "./Types";
10
11
  * close the gap.
11
12
  */
12
13
  export interface Schema<$Value extends Value = Value, $Adaptations extends Adaptations = {}> extends Core<$Value, $Adaptations> {
14
+ /**
15
+ * Check that this schema's fabricated value type is assignable to `$Target`.
16
+ * Identity at runtime; the target is not carried into later chained calls —
17
+ * see `object/Schema.ts`'s `satisfies`.
18
+ */
19
+ satisfies<$Target>(this: this & SatisfiesThis<Fabricated<$Value>, $Target>): Schema<$Value, $Adaptations>;
13
20
  /**
14
21
  * Override what this schema maps to in one or more external schema libraries
15
22
  * — see `string/Schema.ts`'s `adapt` for the full contract.
@@ -3,11 +3,13 @@ import { Schema } from "./Schema";
3
3
  import type { Definition, Fabricated, InputWhereby } from "./Types";
4
4
  type ThisRegistry<$Definition extends Definition> = {
5
5
  /**
6
- * An array of `definition`, repeated `whereby.length` times — either a fixed
7
- * count, or uniformly across `[length.min, length.max]` with `length.min`
8
- * defaulting to inclusive 0, so an empty array is a legitimate outcome when
9
- * no `min` is given. Exclusive ends use a Bound object; a bare `length: N` is
10
- * stored as min = max = N inclusive.
6
+ * An array of `definition`, repeated `length` times. `length` is a bare `N`
7
+ * (exactly N) or `{ min?, max, distribution? }`. `min` defaults to inclusive
8
+ * 0, so an empty array is a legitimate outcome when no `min` is given.
9
+ * Exclusive ends use a Bound object. A bare `N` is stored as min = max = N
10
+ * inclusive and carries no distribution. Without `distribution`, the length
11
+ * is drawn uniformly across the inclusive interval; `distribution` shapes
12
+ * that draw the same way it does for `T.number.integer`.
11
13
  */
12
14
  whereby: (whereby: InputWhereby) => Schema<$Definition>;
13
15
  /**
@@ -1,6 +1,7 @@
1
1
  import { type AdaptationEntry } from "../../Adapter/Core";
2
2
  import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
3
  import type { Produce } from "../../Random/Types";
4
+ import { type SatisfiesThis } from "../../Schema/Satisfies";
4
5
  import type { Core, Definition, Fabricated } from "./Types";
5
6
  /**
6
7
  * The buildable recipe for an `array`: a single element Schema, repeated
@@ -13,6 +14,12 @@ export interface Schema<$Definition extends Definition = Definition, $Adaptation
13
14
  * (or future validation of `produce`) still has them to check against.
14
15
  */
15
16
  as: (produce: Produce<Fabricated<$Definition>>) => Schema<$Definition, $Adaptations>;
17
+ /**
18
+ * Check that this schema's fabricated value type is assignable to `$Target`.
19
+ * Identity at runtime; the target is not carried into later chained calls —
20
+ * see `object/Schema.ts`'s `satisfies`.
21
+ */
22
+ satisfies<$Target>(this: this & SatisfiesThis<Fabricated<$Definition>, $Target>): Schema<$Definition, $Adaptations>;
16
23
  /**
17
24
  * Override what this schema maps to in one or more external schema libraries
18
25
  * — see `string/Schema.ts`'s `adapt` for the full contract.
@@ -1,21 +1,15 @@
1
1
  import type { Adaptations } from "../../Adapter/Types";
2
- import type { Bound, InputBound } from "../../Bound";
2
+ import type { InputLength, Length } from "../../Bound";
3
3
  import type { Produce } from "../../Random/Types";
4
4
  import type { AnySchema, ValueOf } from "../../Schema/Types";
5
5
  import type { Adaptation, Kind, Meta, Produces } from "../../Types";
6
6
  export type Definition = AnySchema;
7
7
  export type Fabricated<$Definition extends Definition, $Bindings extends unknown[] = []> = Array<ValueOf<$Definition, $Bindings>>;
8
8
  export type InputWhereby = {
9
- length: number | {
10
- max: InputBound<number>;
11
- min?: InputBound<number> | undefined;
12
- };
9
+ length: InputLength;
13
10
  };
14
11
  export type Whereby = {
15
- length: {
16
- min: Bound<number>;
17
- max: Bound<number>;
18
- };
12
+ length: Length;
19
13
  };
20
14
  /**
21
15
  * `whereby` (a length spec — no natural bound to fuzz to, so unlike
@@ -1,6 +1,7 @@
1
1
  import { type AdaptationEntry } from "../../Adapter/Core";
2
2
  import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
3
  import type { Produce } from "../../Random/Types";
4
+ import { type SatisfiesThis } from "../../Schema/Satisfies";
4
5
  import type { Core, Fabricated, Meta as ThisMeta } from "./Types";
5
6
  /**
6
7
  * Buildable `bigint` recipe: a range (`whereby` — required, like `string`; no
@@ -18,6 +19,12 @@ export interface Schema<$Meta extends ThisMeta = ThisMeta, $Adaptations extends
18
19
  * validation of `produce`) still has it to check against.
19
20
  */
20
21
  as: (produce: Produce<Fabricated>) => Schema<$Meta, $Adaptations>;
22
+ /**
23
+ * Check that this schema's fabricated value type is assignable to `$Target`.
24
+ * Identity at runtime; the target is not carried into later chained calls —
25
+ * see `object/Schema.ts`'s `satisfies`.
26
+ */
27
+ satisfies<$Target>(this: this & SatisfiesThis<Fabricated, $Target>): Schema<$Meta, $Adaptations>;
21
28
  /**
22
29
  * Override what this schema maps to in one or more external schema libraries
23
30
  * — see `string/Schema.ts`'s `adapt` for the full contract.
@@ -1,6 +1,7 @@
1
1
  import { type AdaptationEntry } from "../../Adapter/Core";
2
2
  import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
3
  import type { Produce } from "../../Random/Types";
4
+ import { type SatisfiesThis } from "../../Schema/Satisfies";
4
5
  import type { Core, Fabricated, Meta as ThisMeta, Weights } from "./Types";
5
6
  /**
6
7
  * Buildable `boolean` recipe: an even coin flip, a weighted one (`weights`), or
@@ -18,6 +19,12 @@ export interface Schema<$Meta extends ThisMeta = ThisMeta, $Adaptations extends
18
19
  * validation of `produce`) still has it to check against.
19
20
  */
20
21
  as: (produce: Produce<Fabricated>) => Schema<$Meta, $Adaptations>;
22
+ /**
23
+ * Check that this schema's fabricated value type is assignable to `$Target`.
24
+ * Identity at runtime; the target is not carried into later chained calls —
25
+ * see `object/Schema.ts`'s `satisfies`.
26
+ */
27
+ satisfies<$Target>(this: this & SatisfiesThis<Fabricated, $Target>): Schema<$Meta, $Adaptations>;
21
28
  weighted: (weights: Weights) => Schema<$Meta, $Adaptations>;
22
29
  /**
23
30
  * Override what this schema maps to in one or more external schema libraries
@@ -15,11 +15,21 @@ type Weighted<$Items extends ReadonlyArray<Item>> = {
15
15
  * At least one element — a `choice` with no option has nothing to draw, which
16
16
  * would otherwise fail inside `weighted()` (`Distribution/index.ts`) with an
17
17
  * opaque `TypeError` at fabricate time (weight sum is `0`, so `.find` returns
18
- * `undefined` and `chosen![1]` throws). Rejecting emptiness here is a
19
- * construction-time compile error; the runtime check below is the backstop for
20
- * an `as any` call that bypasses it.
18
+ * `undefined` and `chosen![1]` throws).
19
+ *
20
+ * Only a _statically_ empty tuple (a literal `[]`) is a compile error. A
21
+ * dynamically built array — e.g. `.map()` over an `as const` array — is
22
+ * accepted: its element union survives but its arity doesn't, so it can't be
23
+ * proven non-empty and a tuple-shaped `readonly [$T, ...$T[]]` constraint would
24
+ * wrongly reject it. The runtime check below is the backstop for that case, and
25
+ * for an `as any` call.
26
+ *
27
+ * Intersected onto the parameter (`items: $Items & NonEmpty<$Items>`) rather
28
+ * than written as a conditional parameter type (`$Items extends readonly [] ?
29
+ * never : $Items`): the latter blocks `const` inference, widening `["a", "b"]`
30
+ * to `string[]` and losing the tuple `Weighted` depends on.
21
31
  */
22
- type NonEmpty<$T> = readonly [$T, ...$T[]];
32
+ type NonEmpty<$Items extends ReadonlyArray<unknown>> = $Items extends readonly [] ? never : unknown;
23
33
  declare const _default: {
24
34
  /**
25
35
  * Equal probability across every option. Delegates to `weighted` with a
@@ -27,12 +37,12 @@ declare const _default: {
27
37
  * uniform draw when every weight is equal, so there is no separate unweighted
28
38
  * code path to keep in sync.
29
39
  */
30
- uniform: <const $Items extends NonEmpty<Item>>(items: $Items) => Schema<Weighted<$Items>>;
40
+ uniform: <const $Items extends ReadonlyArray<Item>>(items: $Items & NonEmpty<$Items>) => Schema<Weighted<$Items>>;
31
41
  /**
32
42
  * Relative probability across options, given as `[weight, schema]` pairs —
33
43
  * the same tuple shape `weighted()` (`Distribution/index.ts`) itself accepts,
34
44
  * not an object keyed by option.
35
45
  */
36
- weighted: <const $Items extends NonEmpty<Items[number]>>(items: $Items) => Schema<$Items>;
46
+ weighted: <const $Items extends Items>(items: $Items & NonEmpty<$Items>) => Schema<$Items>;
37
47
  };
38
48
  export default _default;
@@ -1,6 +1,7 @@
1
1
  import { type AdaptationEntry } from "../../Adapter/Core";
2
2
  import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
3
  import type { Produce } from "../../Random/Types";
4
+ import { type SatisfiesThis } from "../../Schema/Satisfies";
4
5
  import type { Core, Fabricated, Items } from "./Types";
5
6
  /**
6
7
  * Buildable `choice` recipe: a weighted set of option Schemas, one of which is
@@ -15,6 +16,12 @@ export interface Schema<$Items extends Items = Items, $Adaptations extends Adapt
15
16
  * validation of `produce`) still has it to check against.
16
17
  */
17
18
  as: (produce: Produce<Fabricated<$Items>>) => Schema<$Items, $Adaptations>;
19
+ /**
20
+ * Check that this schema's fabricated value type is assignable to `$Target`.
21
+ * Identity at runtime; the target is not carried into later chained calls —
22
+ * see `object/Schema.ts`'s `satisfies`.
23
+ */
24
+ satisfies<$Target>(this: this & SatisfiesThis<Fabricated<$Items>, $Target>): Schema<$Items, $Adaptations>;
18
25
  /**
19
26
  * Override what this schema maps to in one or more external schema libraries
20
27
  * — see `string/Schema.ts`'s `adapt` for the full contract.
@@ -1,6 +1,7 @@
1
1
  import { type AdaptationEntry } from "../../Adapter/Core";
2
2
  import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
3
  import type { Produce } from "../../Random/Types";
4
+ import { type SatisfiesThis } from "../../Schema/Satisfies";
4
5
  import type { Core, Fabricated, Meta as ThisMeta } from "./Types";
5
6
  /**
6
7
  * Buildable `Date` recipe: a range/distribution (`whereby`, absent for the
@@ -18,6 +19,12 @@ export interface Schema<$Meta extends ThisMeta = ThisMeta, $Adaptations extends
18
19
  * future validation of `produce`) still has them to check against.
19
20
  */
20
21
  as: (produce: Produce<Fabricated>) => Schema<$Meta, $Adaptations>;
22
+ /**
23
+ * Check that this schema's fabricated value type is assignable to `$Target`.
24
+ * Identity at runtime; the target is not carried into later chained calls —
25
+ * see `object/Schema.ts`'s `satisfies`.
26
+ */
27
+ satisfies<$Target>(this: this & SatisfiesThis<Fabricated, $Target>): Schema<$Meta, $Adaptations>;
21
28
  /**
22
29
  * Override what this schema maps to in one or more external schema libraries
23
30
  * — see `string/Schema.ts`'s `adapt` for the full contract.