@ghostry/fabricator 0.0.6 → 0.0.8

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 (37) hide show
  1. package/dist/esm/Distribution/index.js +1 -0
  2. package/dist/esm/Enumeration/Plan.js +16 -0
  3. package/dist/esm/Error/index.js +8 -0
  4. package/dist/esm/Fabricator/Constructor.js +13 -0
  5. package/dist/esm/Instance/Stack/Visible.js +4 -4
  6. package/dist/esm/Primitive/derive/Fabricator.js +31 -0
  7. package/dist/esm/Primitive/derive/Registry.js +17 -0
  8. package/dist/esm/Primitive/derive/Schema.js +10 -0
  9. package/dist/esm/Primitive/derive/Types.js +1 -0
  10. package/dist/esm/Primitive/derive/index.js +5 -0
  11. package/dist/esm/Primitive/index.js +1 -0
  12. package/dist/esm/Primitive/namespace.js +2 -1
  13. package/dist/esm/Primitive/object/Registry.js +8 -8
  14. package/dist/esm/Primitive/recursive/Terminate.js +17 -0
  15. package/dist/esm/Schema/Core.js +13 -1
  16. package/dist/esm/index.js +1 -0
  17. package/dist/types/Distribution/index.d.ts +5 -0
  18. package/dist/types/Enumeration/Types.d.ts +4 -3
  19. package/dist/types/Error/index.d.ts +39 -7
  20. package/dist/types/Fabricator/Types.d.ts +1 -1
  21. package/dist/types/Harnessing/Core.d.ts +9 -6
  22. package/dist/types/Harnessing/Types.d.ts +2 -2
  23. package/dist/types/Instance/Stack/Visible.d.ts +8 -4
  24. package/dist/types/Instance/Types.d.ts +38 -37
  25. package/dist/types/Primitive/choice/Registry.d.ts +16 -6
  26. package/dist/types/Primitive/derive/Fabricator.d.ts +44 -0
  27. package/dist/types/Primitive/derive/Registry.d.ts +6 -0
  28. package/dist/types/Primitive/derive/Schema.d.ts +36 -0
  29. package/dist/types/Primitive/derive/Types.d.ts +39 -0
  30. package/dist/types/Primitive/derive/index.d.ts +5 -0
  31. package/dist/types/Primitive/enum/Registry.d.ts +19 -9
  32. package/dist/types/Primitive/index.d.ts +3 -2
  33. package/dist/types/Primitive/namespace.d.ts +1 -0
  34. package/dist/types/Schema/Core.d.ts +13 -0
  35. package/dist/types/Types.d.ts +4 -3
  36. package/dist/types/index.d.ts +9 -0
  37. package/package.json +1 -1
@@ -135,6 +135,7 @@ function normalInv(p) {
135
135
  return -(((((-0.007784894002430293 * q - 3.223964580411365e-1) * q - 2.400758277161838) * q - 2.549732539343734) * q + 4.374664141464968) * q + 2.938163982698783) / ((((7.784695709041462e-3 * q + 3.224671290700398e-1) * q + 2.445134137142996) * q + 3.754408661907416) * q + 1);
136
136
  }
137
137
  function sample(list, stream) {
138
+ if (0 === list.length) throw new FabricatorError.EmptyItemsError("sample", "item");
138
139
  const index = Math.floor(stream.next() * list.length);
139
140
  const item = list[index];
140
141
  return item;
@@ -186,6 +186,14 @@ function axisFor(node, planning) {
186
186
  slots
187
187
  }));
188
188
  }
189
+ case "derive":
190
+ {
191
+ const from = node[Children];
192
+ const axes = from.map((slot)=>plan(slot, planning));
193
+ return productAxis(planning.strategy, axes, (slots)=>({
194
+ slots
195
+ }));
196
+ }
189
197
  case "object":
190
198
  {
191
199
  const definition = node[Meta].definition;
@@ -271,6 +279,14 @@ function resolve(node, pin) {
271
279
  const elements = node[Children];
272
280
  return elements.map((element, i)=>resolve(element, p.slots[i]));
273
281
  }
282
+ case "derive":
283
+ {
284
+ const p = pin;
285
+ const from = node[Children];
286
+ return node.fabricate({
287
+ from: from.map((slot, i)=>resolve(slot, p.slots[i]))
288
+ });
289
+ }
274
290
  case "object":
275
291
  {
276
292
  const p = pin;
@@ -88,6 +88,14 @@ class Error_FabricatorError extends Error {
88
88
  }
89
89
  }
90
90
  FabricatorError.ComputeResultMismatchError = ComputeResultMismatchError;
91
+ class DeriveResultMismatchError extends FabricatorError {
92
+ constructor(kind, value){
93
+ super(), _define_property(this, "kind", void 0), _define_property(this, "value", void 0), this.kind = kind, this.value = value;
94
+ this.name = "DeriveResultMismatchError";
95
+ this.message = `A T.derive resolver returned a value of type ${typeof value} that does not match its \`to\` schema ("${kind}").`;
96
+ }
97
+ }
98
+ FabricatorError.DeriveResultMismatchError = DeriveResultMismatchError;
91
99
  class UnknownOverrideFieldError extends FabricatorError {
92
100
  constructor(field, available){
93
101
  super(), _define_property(this, "field", void 0), _define_property(this, "available", void 0), this.field = field, this.available = available;
@@ -209,6 +209,19 @@ function Constructor(source, stack, ancestry) {
209
209
  schema: s
210
210
  }, elements);
211
211
  }
212
+ case "derive":
213
+ {
214
+ const s = schema;
215
+ const from = s[Meta].from.map((item, i)=>make(item, [
216
+ ...path,
217
+ "from",
218
+ i.toString(10)
219
+ ], context));
220
+ return Primitive.derive.Fabricator({
221
+ ...common,
222
+ schema: s
223
+ }, from);
224
+ }
212
225
  case "object.compute":
213
226
  {
214
227
  const s = schema;
@@ -1,10 +1,10 @@
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;
1
+ function governs(receiver, reader) {
2
+ if (reader.length > receiver.length) return false;
3
+ for(let index = 0; index < reader.length; index++)if (reader[index] !== receiver[index]) return false;
4
4
  return true;
5
5
  }
6
6
  function toVisible(frames, ancestry) {
7
- return frames.filter((frame)=>onDirectLine(frame.ancestry, ancestry));
7
+ return frames.filter((frame)=>governs(frame.ancestry, ancestry));
8
8
  }
9
9
  function toInnermostFrame(stack, ancestry) {
10
10
  const frames = stack.visible(ancestry);
@@ -0,0 +1,31 @@
1
+ import { FabricatorError } from "../../Error/index.js";
2
+ import { toStreamFromTrace } from "../../Random/index.js";
3
+ import { toValueKind, violatesKind } from "../../Schema/Core.js";
4
+ import { Children, Kind, Meta } from "../../Types.js";
5
+ import { Schema } from "./Schema.js";
6
+ function Fabricator(context, from) {
7
+ const { schema, algorithm, trace } = context;
8
+ const meta = schema[Meta];
9
+ const rehydrated = Schema(schema);
10
+ const stream = toStreamFromTrace(algorithm, trace);
11
+ const resolve = meta.resolve;
12
+ const toKind = toValueKind(meta.to);
13
+ return {
14
+ [Kind]: "derive",
15
+ [Meta]: meta,
16
+ trace,
17
+ [Children]: from,
18
+ fabricate: (params)=>{
19
+ var _ref;
20
+ const values = null != (_ref = null == params ? void 0 : params.from) ? _ref : from.map((slot)=>slot.fabricate());
21
+ const result = resolve(values, {
22
+ random: stream,
23
+ clock: trace.clock
24
+ });
25
+ if (violatesKind(toKind, result)) throw new FabricatorError.DeriveResultMismatchError(toKind, result);
26
+ return result;
27
+ },
28
+ schema: rehydrated
29
+ };
30
+ }
31
+ export { Fabricator };
@@ -0,0 +1,17 @@
1
+ import { toSchema } from "../../Schema/Core.js";
2
+ import { Kind, Meta } from "../../Types.js";
3
+ import { Schema } from "./Schema.js";
4
+ function Registry({ to, from }) {
5
+ const normalized = from.map((item)=>toSchema(item));
6
+ return {
7
+ as: (resolve)=>Schema({
8
+ [Kind]: "derive",
9
+ [Meta]: {
10
+ from: normalized,
11
+ to,
12
+ resolve
13
+ }
14
+ })
15
+ };
16
+ }
17
+ export default Registry;
@@ -0,0 +1,10 @@
1
+ import { withAdaptations } from "../../Adapter/Core.js";
2
+ import { Kind } from "../../Types.js";
3
+ function Schema(schema) {
4
+ return {
5
+ ...schema,
6
+ [Kind]: "derive",
7
+ adapt: (adapter, produce)=>Schema(withAdaptations(schema, adapter, produce))
8
+ };
9
+ }
10
+ export { Schema };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,5 @@
1
+ export * from "./Types.js";
2
+ export * from "./Schema.js";
3
+ export * from "./Fabricator.js";
4
+ export * from "./Registry.js";
5
+ export { default } from "./Registry.js";
@@ -5,6 +5,7 @@ export { default as bigint } from "./bigint/index.js";
5
5
  export { default as boolean } from "./boolean/index.js";
6
6
  export { default as choice } from "./choice/index.js";
7
7
  export { default as date } from "./date/index.js";
8
+ export { default as derive } from "./derive/index.js";
8
9
  export { default as enum } from "./enum/index.js";
9
10
  export { default as null } from "./null/index.js";
10
11
  export { default as nullable } from "./nullable/index.js";
@@ -4,6 +4,7 @@ import * as __rspack_external__bigint_index_js_adedcded from "./bigint/index.js"
4
4
  import * as __rspack_external__boolean_index_js_0a69a391 from "./boolean/index.js";
5
5
  import * as __rspack_external__choice_index_js_ce126df9 from "./choice/index.js";
6
6
  import * as __rspack_external__date_index_js_99c830c9 from "./date/index.js";
7
+ import * as __rspack_external__derive_index_js_be41c87a from "./derive/index.js";
7
8
  import * as __rspack_external__enum_index_js_39677e4c from "./enum/index.js";
8
9
  import * as __rspack_external__null_index_js_b154bd62 from "./null/index.js";
9
10
  import * as __rspack_external__nullable_index_js_a31c5679 from "./nullable/index.js";
@@ -18,4 +19,4 @@ import * as __rspack_external__symbol_index_js_65f53bf7 from "./symbol/index.js"
18
19
  import * as __rspack_external__tuple_index_js_fe5f0f11 from "./tuple/index.js";
19
20
  import * as __rspack_external__undefinable_index_js_01dfdeb8 from "./undefinable/index.js";
20
21
  import * as __rspack_external__undefined_index_js_b61f9fc3 from "./undefined/index.js";
21
- export { __rspack_external__always_index_js_5b7da93f as always, __rspack_external__array_index_js_e390953b as array, __rspack_external__bigint_index_js_adedcded as bigint, __rspack_external__boolean_index_js_0a69a391 as boolean, __rspack_external__choice_index_js_ce126df9 as choice, __rspack_external__date_index_js_99c830c9 as date, __rspack_external__enum_index_js_39677e4c as enum, __rspack_external__null_index_js_b154bd62 as null, __rspack_external__nullable_index_js_a31c5679 as nullable, __rspack_external__nullish_index_js_03ff6564 as nullish, __rspack_external__number_index_js_b8c783ea as number, __rspack_external__object_index_js_53384658 as object, __rspack_external__opaque_index_js_0795d628 as opaque, __rspack_external__record_index_js_120e2811 as record, __rspack_external__recursive_index_js_3c8bba6b as recursive, __rspack_external__string_index_js_18f2393a as string, __rspack_external__symbol_index_js_65f53bf7 as symbol, __rspack_external__tuple_index_js_fe5f0f11 as tuple, __rspack_external__undefinable_index_js_01dfdeb8 as undefinable, __rspack_external__undefined_index_js_b61f9fc3 as undefined };
22
+ export { __rspack_external__always_index_js_5b7da93f as always, __rspack_external__array_index_js_e390953b as array, __rspack_external__bigint_index_js_adedcded as bigint, __rspack_external__boolean_index_js_0a69a391 as boolean, __rspack_external__choice_index_js_ce126df9 as choice, __rspack_external__date_index_js_99c830c9 as date, __rspack_external__derive_index_js_be41c87a as derive, __rspack_external__enum_index_js_39677e4c as enum, __rspack_external__null_index_js_b154bd62 as null, __rspack_external__nullable_index_js_a31c5679 as nullable, __rspack_external__nullish_index_js_03ff6564 as nullish, __rspack_external__number_index_js_b8c783ea as number, __rspack_external__object_index_js_53384658 as object, __rspack_external__opaque_index_js_0795d628 as opaque, __rspack_external__record_index_js_120e2811 as record, __rspack_external__recursive_index_js_3c8bba6b as recursive, __rspack_external__string_index_js_18f2393a as string, __rspack_external__symbol_index_js_65f53bf7 as symbol, __rspack_external__tuple_index_js_fe5f0f11 as tuple, __rspack_external__undefinable_index_js_01dfdeb8 as undefinable, __rspack_external__undefined_index_js_b61f9fc3 as undefined };
@@ -1,13 +1,13 @@
1
1
  import { mergeAdaptations } from "../../Adapter/Core.js";
2
2
  import { FabricatorError } from "../../Error/index.js";
3
- import { toSchema, violatesKind } from "../../Schema/Core.js";
3
+ import { toSchema, toValueKind, violatesKind } from "../../Schema/Core.js";
4
4
  import { Adaptation, Fixed, Kind, Meta, Omitted } from "../../Types.js";
5
5
  import { isPlainObject } from "../../Utility/Core.js";
6
6
  import { shallowMerge } from "../../Utility/ShallowMerge.js";
7
7
  import { isNullableSchema } from "../nullable/Schema.js";
8
8
  import { isNullishSchema } from "../nullish/Schema.js";
9
9
  import { isUndefinableSchema } from "../undefinable/Schema.js";
10
- import compute_0, { isObjectComputeSchema } from "./compute/index.js";
10
+ import compute_0 from "./compute/index.js";
11
11
  import { isObjectOmittableSchema } from "./omittable/Schema.js";
12
12
  import { isObjectOptionalSchema } from "./optional/Schema.js";
13
13
  import { Schema, isObjectSchema } from "./Schema.js";
@@ -67,7 +67,7 @@ function make(definition, refinements, adaptations) {
67
67
  };
68
68
  continue;
69
69
  }
70
- const innerKind = fieldSchema[Meta].definition[Kind];
70
+ const innerKind = toValueKind(fieldSchema[Meta].definition);
71
71
  if (violatesKind(innerKind, value)) throw new FabricatorError.InvalidOverrideValueError(key, innerKind, value);
72
72
  overridden[key] = {
73
73
  ...toSchema(fieldSchema),
@@ -83,7 +83,7 @@ function make(definition, refinements, adaptations) {
83
83
  };
84
84
  continue;
85
85
  }
86
- const innerKind = fieldSchema[Meta].definition[Kind];
86
+ const innerKind = toValueKind(fieldSchema[Meta].definition);
87
87
  if (violatesKind(innerKind, value)) throw new FabricatorError.InvalidOverrideValueError(key, innerKind, value);
88
88
  overridden[key] = {
89
89
  ...toSchema(fieldSchema),
@@ -93,7 +93,7 @@ function make(definition, refinements, adaptations) {
93
93
  }
94
94
  if (isUndefinableSchema(fieldSchema)) {
95
95
  if (void 0 !== value) {
96
- const innerKind = fieldSchema[Meta].definition[Kind];
96
+ const innerKind = toValueKind(fieldSchema[Meta].definition);
97
97
  if (violatesKind(innerKind, value)) throw new FabricatorError.InvalidOverrideValueError(key, innerKind, value);
98
98
  }
99
99
  overridden[key] = {
@@ -104,7 +104,7 @@ function make(definition, refinements, adaptations) {
104
104
  }
105
105
  if (isNullableSchema(fieldSchema)) {
106
106
  if (null !== value) {
107
- const innerKind = fieldSchema[Meta].definition[Kind];
107
+ const innerKind = toValueKind(fieldSchema[Meta].definition);
108
108
  if (violatesKind(innerKind, value)) throw new FabricatorError.InvalidOverrideValueError(key, innerKind, value);
109
109
  }
110
110
  overridden[key] = {
@@ -115,7 +115,7 @@ function make(definition, refinements, adaptations) {
115
115
  }
116
116
  if (isNullishSchema(fieldSchema)) {
117
117
  if (null != value) {
118
- const innerKind = fieldSchema[Meta].definition[Kind];
118
+ const innerKind = toValueKind(fieldSchema[Meta].definition);
119
119
  if (violatesKind(innerKind, value)) throw new FabricatorError.InvalidOverrideValueError(key, innerKind, value);
120
120
  }
121
121
  overridden[key] = {
@@ -125,7 +125,7 @@ function make(definition, refinements, adaptations) {
125
125
  continue;
126
126
  }
127
127
  if (value === Omitted) throw new FabricatorError.IllegalOmittedOverrideError(key, fieldSchema[Kind]);
128
- const kind = isObjectComputeSchema(fieldSchema) ? fieldSchema[Meta].source[Kind] : fieldSchema[Kind];
128
+ const kind = toValueKind(fieldSchema);
129
129
  if (violatesKind(kind, value)) throw new FabricatorError.InvalidOverrideValueError(key, kind, value);
130
130
  overridden[key] = {
131
131
  ...toSchema(fieldSchema),
@@ -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;
@@ -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 };
package/dist/esm/index.js CHANGED
@@ -9,4 +9,5 @@ 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";
12
13
  export { initialize };
@@ -96,6 +96,11 @@ export declare namespace Distribution {
96
96
  * clamped.
97
97
  */
98
98
  export declare function sampler(distribution: Distribution, range: Range, stream: Stream): () => number;
99
+ /**
100
+ * A uniform pick from `list`. One `stream.next()` per call; `list` is not
101
+ * mutated. An empty list throws {@link FabricatorError.EmptyItemsError} — there
102
+ * is no member to return.
103
+ */
99
104
  export declare function sample<$T>(list: ReadonlyArray<$T>, stream: Stream): $T;
100
105
  /**
101
106
  * 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,35 @@ 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.
132
+ * `"member"`/`"option"`/`"item"`.
126
133
  */
127
134
  readonly noun: string;
128
135
  constructor(
129
136
  /**
130
- * The registry entry that was called, e.g. `"T.enum.uniform"`.
137
+ * The registry entry or helper that was called, e.g. `"T.enum"` or
138
+ * `"sample"`.
131
139
  */
132
140
  label: string,
133
141
  /**
134
- * What the kind calls its items, e.g. `"member"`/`"option"`.
142
+ * What the caller calls its items, e.g.
143
+ * `"member"`/`"option"`/`"item"`.
135
144
  */
136
145
  noun: string);
137
146
  }
@@ -182,6 +191,29 @@ export declare namespace FabricatorError {
182
191
  */
183
192
  value: unknown);
184
193
  }
194
+ /**
195
+ * A `T.derive` resolver returned a value of the wrong shape for the `to`
196
+ * schema it was declared against.
197
+ */
198
+ class DeriveResultMismatchError extends FabricatorError {
199
+ /**
200
+ * The `[Kind]` of the derive's `to` schema.
201
+ */
202
+ readonly kind: string;
203
+ /**
204
+ * What the resolver actually returned.
205
+ */
206
+ readonly value: unknown;
207
+ constructor(
208
+ /**
209
+ * The `[Kind]` of the derive's `to` schema.
210
+ */
211
+ kind: string,
212
+ /**
213
+ * What the resolver actually returned.
214
+ */
215
+ value: unknown);
216
+ }
185
217
  /**
186
218
  * An override names a field the object schema does not define — most often a
187
219
  * 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>;
@@ -34,12 +34,15 @@ import type { FabricatorTestContext, Integration } from "./Types";
34
34
  * `scope` is a function so that capturing it captures the lookup, where a
35
35
  * captured result would pin one frame.
36
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.
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
42
+ * `context.fabricator`, or from a fork of that scope — a module-level fork of
43
+ * the integrated instance draws the same data in every test, and because its
44
+ * construction counter runs across tests, which values a test gets depends on
45
+ * which tests ran before it.
43
46
  *
44
47
  * `provides.fabricator` is then that scope — the instance `wrap` gave its
45
48
  * block, not the base instance — so `context.fabricator.salt` is the per-test
@@ -3,8 +3,8 @@ import type { PlainObject } from "../Utility/Types";
3
3
  /**
4
4
  * What identifies one registered test or suite — the material a salt is derived
5
5
  * from. Named to match how this codebase already talks about the concept:
6
- * fabricator's own reproducibility guide reaches for `layer(...)` for "a tenant
7
- * id, a test's own name" — this is that identity, structured.
6
+ * fabricator's own reproducibility guide reaches for `layer(...)` for "a test's
7
+ * own name" — this is that identity, structured.
8
8
  *
9
9
  * Declared here rather than imported from `@ghostry/harness`, so neither
10
10
  * package depends on the other — the same arrangement `Adapter`/`walk` already
@@ -5,12 +5,16 @@ import type { Ancestry, Frame, Stack } from "../Types";
5
5
  * delegate to so neither can drift from the other. A carrier's own job is
6
6
  * reduced to holding the chain in whatever way its runtime allows.
7
7
  *
8
+ * A wrap governs the instance it was called on and that instance's ancestors,
9
+ * never its descendants: `governs` is a prefix test in that one direction.
8
10
  * Order is preserved rather than reduced to the innermost match, because the
9
11
  * count is `context.depth` and the innermost is just the last element.
10
- * Skipping, rather than stopping at, the first invisible frame is the outward
11
- * walk: with a parent's `wrap` open and a child's nested inside it, that
12
- * child's sibling must pass over the inner frame and still resolve against the
13
- * outer one.
12
+ *
13
+ * It filters the whole chain rather than walking in from the innermost frame
14
+ * and stopping at the first that does not govern the reader, because a
15
+ * governing frame can sit beneath one that does not: with `B`'s wrap open and
16
+ * `C`'s nested inside it, `B` must pass over `C`'s frame and still resolve
17
+ * against its own.
14
18
  */
15
19
  export declare function toVisible(frames: ReadonlyArray<Frame>, ancestry: Ancestry): ReadonlyArray<Frame>;
16
20
  /**
@@ -69,19 +69,19 @@ export type Token = symbol & {
69
69
  * over that instance's `config`. One rule, so an instance's position and its
70
70
  * configuration always agree about who its parent is.
71
71
  *
72
- * Two chains describe instances on the same ancestral line when either is a
73
- * prefix of the other, which is what {@link Stack.visible} tests. That relation
74
- * decides whose calls resolve against whose frames: a parent's calls resolve
75
- * against a child's frame and a child's against a parent's, while two siblings
76
- * resolve against neither's. Note this never crosses lineages, and not for want
77
- * of identity — two roots hold two separate carriers, so a `wrap` on one pushes
78
- * where the other's reads never look.
72
+ * {@link Stack.visible} tests whether the reader is the wrap's receiver or one
73
+ * of that receiver's ancestors — the reader's chain a prefix of the frame's. A
74
+ * parent's constructions resolve against a wrap entered on a child; a child's
75
+ * constructions do not resolve against a wrap entered on a parent. Two siblings
76
+ * resolve against neither's. The relation never crosses lineages, and not for
77
+ * want of identity — two roots hold two separate carriers, so a `wrap` on one
78
+ * pushes where the other's reads never look.
79
79
  */
80
80
  export type Ancestry = readonly [Token, ...ReadonlyArray<Token>];
81
81
  /**
82
82
  * One active `wrap` — its resolved config plus the single `RandomSource` every
83
- * build reached inside that `wrap` shares, whether reached implicitly (any
84
- * instance on the origin's ancestral line consulting the frame) or explicitly
83
+ * build reached inside that `wrap` shares, whether reached implicitly (the
84
+ * receiver and its ancestors consulting the frame) or explicitly
85
85
  * (`scope.Fabricator`, the `Instance` passed to the block). Storing the scope's
86
86
  * own already-built `source` here, rather than each consumer re-deriving one
87
87
  * from `config`, keeps the two routes resolving against the _same_ source —
@@ -93,11 +93,12 @@ export type Ancestry = readonly [Token, ...ReadonlyArray<Token>];
93
93
  * separate `salt`/`algorithm`/`clock` fields.
94
94
  *
95
95
  * `ancestry` is the **origin's** — the instance `wrap` was called on — not the
96
- * scope's. The scope is a fresh child of the origin, so keying on it would make
97
- * every `fork` taken off that origin a _sibling_ of the scope, and calls on
98
- * those forks would stop resolving against the frame. Keying on the origin
99
- * keeps everything on the origin's own line resolving against it, which is the
100
- * whole point of entering one.
96
+ * scope's, and {@link Stack.visible} governs that origin and its ancestors. Not
97
+ * load-bearing for who is governed: keyed on the scope, the frame would govern
98
+ * the same instances plus the scope itself, which resolves against this very
99
+ * `source` either way. What it decides is the scope's own `context.depth` 0,
100
+ * since the scope is a descendant of the origin and not among those the frame
101
+ * governs.
101
102
  */
102
103
  export type Frame = {
103
104
  readonly config: Config<PlainObject>;
@@ -132,23 +133,21 @@ export type Stack = {
132
133
  readonly asynchronous: boolean;
133
134
  /**
134
135
  * Every open frame `ancestry` may resolve against, outermost first: those
135
- * whose own `ancestry` is a prefix of this one or has this one as a prefix.
136
- * The innermost visible frame — what a build or a `context` read actually
137
- * resolves against — is the last element, and the count is `context.depth`.
136
+ * whose receiver this instance is, or is an ancestor of this chain a prefix
137
+ * of the frame's. The innermost visible frame — what a build or a `context`
138
+ * read actually resolves against — is the last element, and the count is
139
+ * `context.depth`.
138
140
  *
139
- * Filtering, rather than simply reporting the innermost frame, is what makes
140
- * the outward walk possible: with a parent's `wrap` open and a child's nested
141
- * inside it, the child's _sibling_ must skip the inner frame and still find
142
- * the outer one. Callers never filter themselves — `toVisible`
143
- * (`Instance/Stack/Visible.ts`) is the single definition both carriers
144
- * delegate to, so the rule cannot drift between them.
141
+ * Callers never filter themselves `toVisible` (`Instance/Stack/Visible.ts`)
142
+ * is the single definition both carriers delegate to, so the rule cannot
143
+ * drift between them.
145
144
  */
146
145
  visible(ancestry: Ancestry): ReadonlyArray<Frame>;
147
146
  /**
148
147
  * Append `frame`, run `block`, remove it in a `finally`, so a frame unwinds
149
- * correctly even if `block` throws. Appends rather than replaces: the chain
150
- * has to stay intact for {@link visible} to walk outward past a frame this
151
- * reader cannot see.
148
+ * correctly even if `block` throws. Appends rather than replaces: nested
149
+ * wraps are a chain, and {@link visible} reports every frame this reader is
150
+ * governed by, not only the innermost.
152
151
  */
153
152
  enter<$Return>(frame: Frame, block: () => $Return): $Return;
154
153
  };
@@ -188,11 +187,13 @@ export type Context = {
188
187
  */
189
188
  scope(): Instance<PlainObject>;
190
189
  /**
191
- * How many frames are currently visible to this instance — 0 outside any.
192
- * Genuine dynamic nesting depth, counted off the carrier rather than inferred
193
- * from {@link Ancestry}: a frame a sibling cannot see is not counted for that
194
- * sibling, and entering two `wrap`s on one instance reads as 2 even though
195
- * neither deepened anyone's ancestry.
190
+ * How many frames currently govern this instance — 0 outside any. Genuine
191
+ * dynamic nesting depth, counted off the carrier rather than inferred from
192
+ * {@link Ancestry}: a wrap entered on a descendant is counted (this instance
193
+ * is an ancestor of that receiver), a wrap entered on a sibling or an
194
+ * ancestor is not, and entering two `wrap`s on one instance reads as 2 even
195
+ * though neither deepened anyone's ancestry. A wrap's own `scope` is a
196
+ * descendant of the receiver, so its depth inside that wrap is 0.
196
197
  */
197
198
  readonly depth: number;
198
199
  };
@@ -280,7 +281,7 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
280
281
  /**
281
282
  * `fork(overlay)`, made ambient for the synchronous extent of `block`: every
282
283
  * `new Fabricator(...)`, `combinatorial(...)`, and `coverage(...)` reached
283
- * inside — on this instance or any other in the same lineage — resolves
284
+ * inside — on this instance, and on this instance's ancestors — resolves
284
285
  * against the fork instead, with nothing threaded through. The fork is also
285
286
  * passed to `block`: use it explicitly where that reads better, and
286
287
  * _necessarily_ for any async work, which the ambient frame does not survive
@@ -294,11 +295,11 @@ export interface Instance<$Registry extends PlainObject> extends Pick<RandomSour
294
295
  * bound outside, as a destructured `wrap` is. To compose onto whatever is
295
296
  * active regardless of receiver, go through `context.scope().wrap(...)`.
296
297
  *
297
- * While the block runs, calls made on this instance's ancestral line resolve
298
- * against the scope its forks, their forks, and its own ancestors up to the
299
- * root. Calls on a _sibling_ do not: a frame entered on one `fork` is not one
300
- * that another `fork` of the same parent can resolve against, which keeps two
301
- * unrelated derivations from drawing each other's data. See
298
+ * While the block runs, calls on this instance and its ancestors resolve
299
+ * against the scope. Calls on a descendant a fork of this instance, or the
300
+ * wrap's own scope draw that instance's own configuration; so does a
301
+ * sibling. The scope still shares one `RandomSource` with this instance
302
+ * inside the wrap, so ambient and explicit construction agree. See
302
303
  * {@link Ancestry}.
303
304
  */
304
305
  wrap<$Return, const $WrapRegistry extends PlainObject = $Registry>(overlay: Overlay<$WrapRegistry>, block: (scope: Instance<$WrapRegistry>) => $Return): $Return;
@@ -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;
@@ -0,0 +1,44 @@
1
+ import type { AdaptationsOf } from "../../Adapter/Types";
2
+ import type { FabricatorContext, NaiveFabricator } from "../../Fabricator/Types";
3
+ import type { Trace } from "../../Random/Types";
4
+ import { Children, Kind, Meta, type Adaptation } from "../../Types";
5
+ import type { Fabricated as FromValues } from "../tuple/Types";
6
+ import { Schema } from "./Schema";
7
+ import type { From, Meta as ThisMeta, Resolved, Source } from "./Types";
8
+ export type Fabrication<$Fabricator extends Fabricator> = $Fabricator extends Fabricator<infer $Schema extends {
9
+ [Meta]: ThisMeta;
10
+ }> ? Resolved<$Schema[typeof Meta]["to"]> : never;
11
+ export type Fabricator<$Schema extends {
12
+ [Meta]: ThisMeta;
13
+ } = {
14
+ [Meta]: ThisMeta;
15
+ }> = NaiveFabricator<Resolved<$Schema[typeof Meta]["to"]>> & {
16
+ [Kind]: "derive";
17
+ [Meta]: $Schema[typeof Meta];
18
+ readonly trace: Trace;
19
+ [Children]: ReadonlyArray<NaiveFabricator<any>>;
20
+ readonly [Adaptation]?: AdaptationsOf<$Schema>;
21
+ /**
22
+ * Given `from`, resolves those values instead of fabricating each slot.
23
+ * Enumeration uses this to pin `from` slots and still run `resolve` on them
24
+ * (see `Enumeration/Plan.ts`).
25
+ */
26
+ fabricate: (params?: {
27
+ from: FromValues<$Schema[typeof Meta]["from"]>;
28
+ }) => Resolved<$Schema[typeof Meta]["to"]>;
29
+ schema: Schema<$Schema[typeof Meta]["from"], $Schema[typeof Meta]["to"], AdaptationsOf<$Schema>>;
30
+ };
31
+ /**
32
+ * `from` are each input slot's _already-dispatched_ Fabricator, one per
33
+ * `[Meta].from` entry, built by `Constructor.ts`'s `make` before this call —
34
+ * the same shape `tuple.Fabricator`'s `elements` take. Each slot gets its own
35
+ * private stream, so two same-kind inputs produce independent sequences rather
36
+ * than a correlated shared one.
37
+ *
38
+ * `toStreamFromTrace` is always consulted: `resolve` is required (there is no
39
+ * bare path) and is handed this node's `ProduceContext`, so any extra
40
+ * randomness it draws still replays under a salt. Don't collapse that into a
41
+ * conditional — `resolve` is the equivalent of every other kind's `produce`,
42
+ * and here it is always present.
43
+ */
44
+ export declare function Fabricator<$From extends From, $To extends Source>(context: FabricatorContext<Schema<$From, $To>>, from: ReadonlyArray<NaiveFabricator<any>>): Fabricator<Schema<$From, $To>>;
@@ -0,0 +1,6 @@
1
+ import { type Deriver } from "./Schema";
2
+ import type { From, Source } from "./Types";
3
+ export default function <const $From extends From, const $To extends Source>({ to, from, }: {
4
+ to: $To;
5
+ from: $From;
6
+ }): Deriver<$From, $To>;
@@ -0,0 +1,36 @@
1
+ import { type AdaptationEntry } from "../../Adapter/Core";
2
+ import type { Adaptations, Adapter, Adapting, WithAdaptations } from "../../Adapter/Types";
3
+ import type { ProduceContext } from "../../Random/Types";
4
+ import type * as tuple from "../tuple/Types";
5
+ import type { Core, From, Resolved, Source } from "./Types";
6
+ /**
7
+ * What `T.derive({ to, from })` returns: not yet a Schema — `.as(resolve)` is
8
+ * what produces one, matching `compute(source).as(resolve)`. There is no
9
+ * re-`.as()` on the Schema; the resolver fully determines production.
10
+ */
11
+ export type Deriver<$From extends From, $To extends Source> = {
12
+ /**
13
+ * Supply the function that turns `from`'s fabricated values into the result
14
+ * `to` names. `values` is the positional tuple of those values; `context` is
15
+ * this node's own `{ random, clock }`, so any extra draw still replays under
16
+ * a salt.
17
+ */
18
+ as: (resolve: (values: tuple.Fabricated<$From>, context: ProduceContext) => NoInfer<Resolved<$To>>) => Schema<$From, $To>;
19
+ };
20
+ /**
21
+ * Buildable `derive` recipe: a value computed from other schemas' fabricated
22
+ * output. `from` is fabricated independently (one Fabricator per slot, like
23
+ * `tuple`); `to` declares only the result's type and shape. See `Fabricator.ts`
24
+ * for the mismatch check against `to`.
25
+ */
26
+ export interface Schema<$From extends From = From, $To extends Source = Source, $Adaptations extends Adaptations = {}> extends Core<$From, $To, $Adaptations> {
27
+ /**
28
+ * Override what this schema maps to in one or more external schema libraries
29
+ * — see `string/Schema.ts`'s `adapt` for the full contract. An unadapted
30
+ * derive maps to whatever `to` maps to, honoring any adaptation already on
31
+ * `to`. Adapting the derive itself overrides that, which is how to say the
32
+ * derive's mapping differs from `to`'s.
33
+ */
34
+ adapt: <const $Adapter extends Adapter, $Returnable extends ReturnType<$Adapter["convert"]>>(adapter: $Adapter, produce: (adapting: Adapting<Schema<$From, $To, $Adaptations>>) => $Returnable) => Schema<$From, $To, WithAdaptations<$Adaptations, AdaptationEntry<$Adapter, $Returnable>>>;
35
+ }
36
+ export declare function Schema<$From extends From, $To extends Source, $Adaptations extends Adaptations = {}>(schema: Core<$From, $To, $Adaptations>): Schema<$From, $To, $Adaptations>;
@@ -0,0 +1,39 @@
1
+ import type { Adaptations } from "../../Adapter/Types";
2
+ import type { ProduceContext } from "../../Random/Types";
3
+ import type { Adaptation, Kind, Meta, Produces } from "../../Types";
4
+ import type { Resolved, Source } from "../object/compute/Types";
5
+ import type * as tuple from "../tuple/Types";
6
+ /**
7
+ * `from` is a tuple of schemas, one per input slot — the same shape
8
+ * `tuple.Items` already is, so `tuple.Fabricated` can type `resolve`'s `values`
9
+ * as a positional `[A, B]` rather than `(A | B)[]`.
10
+ */
11
+ export type From = tuple.Items;
12
+ /**
13
+ * Re-exported so this kind's other files (and `Primitive.derive.Source`) name
14
+ * the same `to` contract `object.compute` uses, rather than restating it.
15
+ */
16
+ export type { Denoted, Resolved, Source } from "../object/compute/Types";
17
+ /**
18
+ * `from` is stored after `toSchema` (inert per-slot schemas, same as `tuple`).
19
+ * `to` is stored exactly as given, whether a Schema or a builder, so its
20
+ * `[Meta]` and `[Adaptation]` survive: adapters convert it as a nested schema
21
+ * (honoring its own adaptation unless the derive is adapted), and
22
+ * `toValueKind` follows it to the kind a result or override is checked
23
+ * against. `resolve` is plain data from the moment `.as(resolve)` is called.
24
+ * `to` is only used for its shape/type — the value comes entirely from
25
+ * `resolve`, which is why `to` may not even have a buildable recipe (a bare
26
+ * `T.string`/`T.bigint`).
27
+ */
28
+ export type Meta<$From extends From = From, $To extends Source = Source> = {
29
+ from: $From;
30
+ to: $To;
31
+ resolve: (values: tuple.Fabricated<$From>, context: ProduceContext) => Resolved<$To>;
32
+ };
33
+ export interface Core<$From extends From = From, $To extends Source = Source, $Adaptations extends Adaptations = {}> {
34
+ [Kind]: "derive";
35
+ [Meta]: Meta<$From, $To>;
36
+ bindings?: unknown[];
37
+ readonly [Produces]?: Resolved<$To, NonNullable<this["bindings"]>>;
38
+ readonly [Adaptation]?: $Adaptations;
39
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./Types";
2
+ export * from "./Schema";
3
+ export * from "./Fabricator";
4
+ export * from "./Registry";
5
+ export { default } from "./Registry";
@@ -13,14 +13,24 @@ type Weighted<$Items extends ReadonlyArray<Item>> = {
13
13
  [$K in keyof $Items]: readonly [number, $Items[$K]];
14
14
  };
15
15
  /**
16
- * At least one element — an empty `enum` has no member to draw, which would
17
- * otherwise fail inside `weighted()` (`Distribution/index.ts`) with an opaque
18
- * `TypeError` at fabricate time (weight sum is `0`, so `.find` returns
19
- * `undefined` and `chosen![1]` throws). Rejecting emptiness here is a
20
- * construction-time compile error; the runtime check below is the backstop for
21
- * an `as any` call that bypasses it.
16
+ * At least one element — an `enum` with no member has nothing to draw, which
17
+ * would otherwise fail inside `weighted()` (`Distribution/index.ts`) with an
18
+ * opaque `TypeError` at fabricate time (weight sum is `0`, so `.find` returns
19
+ * `undefined` and `chosen![1]` throws).
20
+ *
21
+ * Only a _statically_ empty tuple (a literal `[]`) is a compile error. A
22
+ * dynamically built array — e.g. `.map()` over an `as const` array — is
23
+ * accepted: its element union survives but its arity doesn't, so it can't be
24
+ * proven non-empty and a tuple-shaped `readonly [$T, ...$T[]]` constraint would
25
+ * wrongly reject it. The runtime check below is the backstop for that case, and
26
+ * for an `as any` call.
27
+ *
28
+ * Intersected onto the parameter (`items: $Items & NonEmpty<$Items>`) rather
29
+ * than written as a conditional parameter type (`$Items extends readonly [] ?
30
+ * never : $Items`): the latter blocks `const` inference, widening `["a", "b"]`
31
+ * to `string[]` and losing the tuple `Weighted` depends on.
22
32
  */
23
- type NonEmpty<$T> = readonly [$T, ...$T[]];
33
+ type NonEmpty<$Items extends ReadonlyArray<unknown>> = $Items extends readonly [] ? never : unknown;
24
34
  declare const _default: {
25
35
  /**
26
36
  * Equal probability across every member. Delegates to `weighted` with a
@@ -28,12 +38,12 @@ declare const _default: {
28
38
  * uniform draw when every weight is equal, so there is no separate unweighted
29
39
  * code path to keep in sync.
30
40
  */
31
- uniform: <const $Items extends NonEmpty<Item>>(items: $Items) => Schema<Weighted<$Items>>;
41
+ uniform: <const $Items extends ReadonlyArray<Item>>(items: $Items & NonEmpty<$Items>) => Schema<Weighted<$Items>>;
32
42
  /**
33
43
  * Relative probability across members, given as `[weight, item]` pairs — the
34
44
  * same tuple shape `weighted()` (`Distribution/index.ts`) itself accepts, not
35
45
  * an object keyed by member.
36
46
  */
37
- weighted: <const $Items extends NonEmpty<Items[number]>>(items: $Items) => Schema<$Items>;
47
+ weighted: <const $Items extends Items>(items: $Items & NonEmpty<$Items>) => Schema<$Items>;
38
48
  };
39
49
  export default _default;
@@ -6,6 +6,7 @@ export { default as bigint } from "./bigint";
6
6
  export { default as boolean } from "./boolean";
7
7
  export { default as choice } from "./choice";
8
8
  export { default as date } from "./date";
9
+ export { default as derive } from "./derive";
9
10
  export { default as enum } from "./enum";
10
11
  export { default as null } from "./null";
11
12
  export { default as nullable } from "./nullable";
@@ -23,6 +24,6 @@ export { default as tuple } from "./tuple";
23
24
  export { default as undefinable } from "./undefinable";
24
25
  export { default as undefined } from "./undefined";
25
26
  export { Primitive };
26
- export type Fabricator = Primitive.always.Fabricator<any> | Primitive.array.Fabricator<any> | Primitive.bigint.Fabricator | Primitive.boolean.Fabricator | Primitive.choice.Fabricator<any> | Primitive.date.Fabricator | Primitive.enum.Fabricator<any> | Primitive.null.Fabricator | Primitive.nullable.Fabricator<any> | Primitive.nullish.Fabricator<any> | Primitive.number.Fabricator | Primitive.object.Fabricator<any> | Primitive.object.compute.Fabricator<any, any> | Primitive.object.omittable.Fabricator<any> | Primitive.object.optional.Fabricator<any> | Primitive.opaque.Fabricator<any> | Primitive.record.Fabricator<any> | Primitive.recursive.Fabricator<any> | Primitive.recursive.self.Fabricator<any> | Primitive.string.Fabricator | Primitive.symbol.Fabricator | Primitive.tuple.Fabricator<any> | Primitive.undefined.Fabricator | Primitive.undefinable.Fabricator<any>;
27
- export type Schema = Primitive.always.Schema<any> | Primitive.array.Schema<any> | Primitive.bigint.Schema | Primitive.boolean.Schema | Primitive.choice.Schema<any> | Primitive.date.Schema | Primitive.enum.Schema<any> | Primitive.null.Schema | Primitive.nullable.Schema<any> | Primitive.nullish.Schema<any> | Primitive.number.Schema | Primitive.object.Schema<any> | Primitive.object.compute.Schema<any, any> | Primitive.object.omittable.Schema<any> | Primitive.object.optional.Schema<any> | Primitive.opaque.Schema<any> | Primitive.record.Schema<any, any> | Primitive.recursive.Schema<any> | Primitive.recursive.self.Schema<any> | Primitive.string.Schema | Primitive.symbol.Schema | Primitive.tuple.Schema<any> | Primitive.undefined.Schema | Primitive.undefinable.Schema<any>;
27
+ export type Fabricator = Primitive.always.Fabricator<any> | Primitive.array.Fabricator<any> | Primitive.bigint.Fabricator | Primitive.boolean.Fabricator | Primitive.choice.Fabricator<any> | Primitive.date.Fabricator | Primitive.derive.Fabricator<any> | Primitive.enum.Fabricator<any> | Primitive.null.Fabricator | Primitive.nullable.Fabricator<any> | Primitive.nullish.Fabricator<any> | Primitive.number.Fabricator | Primitive.object.Fabricator<any> | Primitive.object.compute.Fabricator<any, any> | Primitive.object.omittable.Fabricator<any> | Primitive.object.optional.Fabricator<any> | Primitive.opaque.Fabricator<any> | Primitive.record.Fabricator<any> | Primitive.recursive.Fabricator<any> | Primitive.recursive.self.Fabricator<any> | Primitive.string.Fabricator | Primitive.symbol.Fabricator | Primitive.tuple.Fabricator<any> | Primitive.undefined.Fabricator | Primitive.undefinable.Fabricator<any>;
28
+ export type Schema = Primitive.always.Schema<any> | Primitive.array.Schema<any> | Primitive.bigint.Schema | Primitive.boolean.Schema | Primitive.choice.Schema<any> | Primitive.date.Schema | Primitive.derive.Schema<any, any> | Primitive.enum.Schema<any> | Primitive.null.Schema | Primitive.nullable.Schema<any> | Primitive.nullish.Schema<any> | Primitive.number.Schema | Primitive.object.Schema<any> | Primitive.object.compute.Schema<any, any> | Primitive.object.omittable.Schema<any> | Primitive.object.optional.Schema<any> | Primitive.opaque.Schema<any> | Primitive.record.Schema<any, any> | Primitive.recursive.Schema<any> | Primitive.recursive.self.Schema<any> | Primitive.string.Schema | Primitive.symbol.Schema | Primitive.tuple.Schema<any> | Primitive.undefined.Schema | Primitive.undefinable.Schema<any>;
28
29
  export type Kind = Schema[typeof Kind];
@@ -22,6 +22,7 @@ export * as bigint from "./bigint";
22
22
  export * as boolean from "./boolean";
23
23
  export * as choice from "./choice";
24
24
  export * as date from "./date";
25
+ export * as derive from "./derive";
25
26
  export * as enum from "./enum";
26
27
  export * as null from "./null";
27
28
  export * as nullable from "./nullable";
@@ -32,6 +32,19 @@ export declare function toSchema<$Kind extends string, $Meta extends PlainObject
32
32
  readonly [Produces]?: $Produces;
33
33
  readonly [Adaptation]?: $Adaptations;
34
34
  };
35
+ /**
36
+ * The `[Kind]` a schema's fabricated value should be checked against with
37
+ * {@link violatesKind}. For most kinds that is its own `[Kind]`, but
38
+ * `object.compute` and `derive` only declare their value's shape, through
39
+ * `source` and `to`. Those resolve to that schema's kind, recursively, so a
40
+ * `to` that is itself a `derive` still reaches a checkable kind. Without this,
41
+ * an override of a derive-valued field would be checked against `"derive"`,
42
+ * which accepts anything.
43
+ */
44
+ export declare function toValueKind(schema: {
45
+ [Kind]: SchemaKind;
46
+ [Meta]?: any;
47
+ }): SchemaKind;
35
48
  /**
36
49
  * Whether `value`'s basic JS shape is compatible with `kind` (one of this
37
50
  * library's `[Kind]` literals, e.g. `"string"`, `"object"`, `"date"`). Stops at
@@ -19,9 +19,10 @@ export declare const Layer: unique symbol;
19
19
  * already constructed for this node's nested schemas before handing them to the
20
20
  * kind's own `Fabricator()` factory. Every composite kind whose construction
21
21
  * receives already-dispatched Fabricators carries this: `object` (its `Fields`
22
- * map), `tuple` (`elements`, in slot order), `choice` (the dispatched options,
23
- * in `[Meta].items` order — weights stay in `[Meta]`), `array` (the single
24
- * shared `element`), `record` (`{ key, value }`), and each of
22
+ * map), `tuple` (`elements`, in slot order), `derive` (the dispatched `from`
23
+ * slots, in `[Meta].from` order), `choice` (the dispatched options, in
24
+ * `[Meta].items` order weights stay in `[Meta]`), `array` (the single shared
25
+ * `element`), `record` (`{ key, value }`), and each of
25
26
  * `nullable`/`nullish`/`undefinable`/`object.omittable`/`object.optional` (the
26
27
  * single wrapped `source`).
27
28
  *
@@ -103,6 +103,15 @@ export type { Bound, InputBound } from "./Bound";
103
103
  * handed — so a caller writing either as a named function can name it.
104
104
  */
105
105
  export type { Stream } from "./Random/Types";
106
+ /**
107
+ * Uniform pick and Fisher–Yates shuffle over a caller-held list, each taking
108
+ * the `Stream` a producer is already handed. Exported so a caller writing
109
+ * `.as(produce)`, `T.opaque`, or a `T.derive` resolver can draw from a list
110
+ * they already have — the same rationale as the `Stream` export. Prefer
111
+ * `T.enum` when the pick can be a schema: it is enumerable and path-keyed;
112
+ * these helpers are for the cases that cannot.
113
+ */
114
+ export { sample, shuffle } from "./Distribution";
106
115
  /**
107
116
  * What every kind's `.as(produce)` producer is called with — a single curated
108
117
  * object rather than a positional argument list, so a caller writing the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostry/fabricator",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "license": "MIT",
5
5
  "description": "Fabricate typed data from composable schemas.",
6
6
  "keywords": [