@ghostry/fabricator-extension-faker-v10 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/esm/Error.js CHANGED
@@ -2,14 +2,14 @@ import { FabricatorError } from "@ghostry/fabricator";
2
2
  class Error_FakerExtensionError extends FabricatorError {
3
3
  constructor(){
4
4
  super();
5
- this.name = "FakerExtensionError";
5
+ this.name = "@ghostry/fabricator-extension-faker-v10:FakerExtensionError";
6
6
  }
7
7
  }
8
8
  (function(FakerExtensionError) {
9
9
  class NoActiveScopeError extends FakerExtensionError {
10
10
  constructor(){
11
11
  super();
12
- this.name = "NoActiveScopeError";
12
+ this.name = "@ghostry/fabricator-extension-faker-v10:NoActiveScopeError";
13
13
  this.message = "A faker builder was invoked outside of fabricate(). Faker builders only draw randomness while the schema they belong to is being fabricated.";
14
14
  }
15
15
  }
@@ -1,7 +1,9 @@
1
+ import { toWords } from "./Words.js";
1
2
  function build(T, faker, draw) {
2
- const [f, d] = [
3
+ const [f, d, w] = [
3
4
  faker,
4
- draw
5
+ draw,
6
+ toWords(faker)
5
7
  ];
6
8
  const string = T.string.as(()=>"");
7
9
  return {
@@ -171,7 +173,7 @@ function build(T, faker, draw) {
171
173
  collation: (...args)=>T.string.as(d(()=>f.database.collation(...args))),
172
174
  column: (...args)=>T.string.as(d(()=>f.database.column(...args))),
173
175
  engine: (...args)=>T.string.as(d(()=>f.database.engine(...args))),
174
- mongodbObjectId: (...args)=>T.string.as(d(()=>f.database.mongodbObjectId(...args)), {
176
+ mongodbObjectId: (...args)=>T.string.as(d(()=>f.database.mongodbObjectId(...args))).admits({
175
177
  pattern: "^[0-9a-fA-F]{24}$"
176
178
  }),
177
179
  type: (...args)=>T.string.as(d(()=>f.database.type(...args)))
@@ -196,11 +198,11 @@ function build(T, faker, draw) {
196
198
  accountName: (...args)=>T.string.as(d(()=>f.finance.accountName(...args))),
197
199
  accountNumber: (...args)=>T.string.as(d(()=>f.finance.accountNumber(...args))),
198
200
  amount: (...args)=>T.string.as(d(()=>f.finance.amount(...args))),
199
- bic: (...args)=>T.string.as(d(()=>f.finance.bic(...args)), {
201
+ bic: (...args)=>T.string.as(d(()=>f.finance.bic(...args))).admits({
200
202
  pattern: "^[A-Z]{6}[A-Z0-9]{2}([A-Z0-9]{3})?$"
201
203
  }),
202
204
  bitcoinAddress: (...args)=>T.string.as(d(()=>f.finance.bitcoinAddress(...args))),
203
- creditCardCVV: (...args)=>T.string.as(d(()=>f.finance.creditCardCVV(...args)), {
205
+ creditCardCVV: (...args)=>T.string.as(d(()=>f.finance.creditCardCVV(...args))).admits({
204
206
  pattern: "^[0-9]{3,4}$"
205
207
  }),
206
208
  creditCardIssuer: (...args)=>T.string.as(d(()=>f.finance.creditCardIssuer(...args))),
@@ -250,38 +252,38 @@ function build(T, faker, draw) {
250
252
  verb: (...args)=>T.string.as(d(()=>f.hacker.verb(...args)))
251
253
  },
252
254
  image: {
253
- avatar: (...args)=>T.string.as(d(()=>f.image.avatar(...args)), {
255
+ avatar: (...args)=>T.string.as(d(()=>f.image.avatar(...args))).admits({
254
256
  format: "uri"
255
257
  }),
256
- avatarGitHub: (...args)=>T.string.as(d(()=>f.image.avatarGitHub(...args)), {
258
+ avatarGitHub: (...args)=>T.string.as(d(()=>f.image.avatarGitHub(...args))).admits({
257
259
  format: "uri"
258
260
  }),
259
- dataUri: (...args)=>T.string.as(d(()=>f.image.dataUri(...args)), {
261
+ dataUri: (...args)=>T.string.as(d(()=>f.image.dataUri(...args))).admits({
260
262
  format: "uri"
261
263
  }),
262
- personPortrait: (...args)=>T.string.as(d(()=>f.image.personPortrait(...args)), {
264
+ personPortrait: (...args)=>T.string.as(d(()=>f.image.personPortrait(...args))).admits({
263
265
  format: "uri"
264
266
  }),
265
- url: (...args)=>T.string.as(d(()=>f.image.url(...args)), {
267
+ url: (...args)=>T.string.as(d(()=>f.image.url(...args))).admits({
266
268
  format: "uri"
267
269
  }),
268
270
  urlLoremFlickr: (...args)=>T.string.as(d(()=>f.image.urlLoremFlickr(...args))),
269
- urlPicsumPhotos: (...args)=>T.string.as(d(()=>f.image.urlPicsumPhotos(...args)), {
271
+ urlPicsumPhotos: (...args)=>T.string.as(d(()=>f.image.urlPicsumPhotos(...args))).admits({
270
272
  format: "uri"
271
273
  })
272
274
  },
273
275
  internet: {
274
276
  displayName: (...args)=>T.string.as(d(()=>f.internet.displayName(...args))),
275
- domainName: (...args)=>T.string.as(d(()=>f.internet.domainName(...args)), {
277
+ domainName: (...args)=>T.string.as(d(()=>f.internet.domainName(...args))).admits({
276
278
  format: "hostname"
277
279
  }),
278
280
  domainSuffix: (...args)=>T.string.as(d(()=>f.internet.domainSuffix(...args))),
279
281
  domainWord: (...args)=>T.string.as(d(()=>f.internet.domainWord(...args))),
280
- email: (...args)=>T.string.as(d(()=>f.internet.email(...args)), {
282
+ email: (...args)=>T.string.as(d(()=>f.internet.email(...args))).admits({
281
283
  format: "email"
282
284
  }),
283
285
  emoji: (...args)=>T.string.as(d(()=>f.internet.emoji(...args))),
284
- exampleEmail: (...args)=>T.string.as(d(()=>f.internet.exampleEmail(...args)), {
286
+ exampleEmail: (...args)=>T.string.as(d(()=>f.internet.exampleEmail(...args))).admits({
285
287
  format: "email"
286
288
  }),
287
289
  httpMethod: (...args)=>T.enum.uniform([
@@ -293,10 +295,10 @@ function build(T, faker, draw) {
293
295
  ]).as(d(()=>f.internet.httpMethod(...args))),
294
296
  httpStatusCode: (...args)=>T.number.as(d(()=>f.internet.httpStatusCode(...args))),
295
297
  ip: (...args)=>T.string.as(d(()=>f.internet.ip(...args))),
296
- ipv4: (...args)=>T.string.as(d(()=>f.internet.ipv4(...args)), {
298
+ ipv4: (...args)=>T.string.as(d(()=>f.internet.ipv4(...args))).admits({
297
299
  format: "ipv4"
298
300
  }),
299
- ipv6: (...args)=>T.string.as(d(()=>f.internet.ipv6(...args)), {
301
+ ipv6: (...args)=>T.string.as(d(()=>f.internet.ipv6(...args))).admits({
300
302
  format: "ipv6"
301
303
  }),
302
304
  jwt: (...args)=>T.string.as(d(()=>f.internet.jwt(...args))),
@@ -308,7 +310,7 @@ function build(T, faker, draw) {
308
310
  "http",
309
311
  "https"
310
312
  ]).as(d(()=>f.internet.protocol(...args))),
311
- url: (...args)=>T.string.as(d(()=>f.internet.url(...args)), {
313
+ url: (...args)=>T.string.as(d(()=>f.internet.url(...args))).admits({
312
314
  format: "uri"
313
315
  }),
314
316
  userAgent: (...args)=>T.string.as(d(()=>f.internet.userAgent(...args))),
@@ -344,15 +346,15 @@ function build(T, faker, draw) {
344
346
  zipCode: (...args)=>T.string.as(d(()=>f.location.zipCode(...args)))
345
347
  },
346
348
  lorem: {
347
- lines: (...args)=>T.string.as(d(()=>f.lorem.lines(...args))),
348
- paragraph: (...args)=>T.string.as(d(()=>f.lorem.paragraph(...args))),
349
- paragraphs: (...args)=>T.string.as(d(()=>f.lorem.paragraphs(...args))),
350
- sentence: (...args)=>T.string.as(d(()=>f.lorem.sentence(...args))),
351
- sentences: (...args)=>T.string.as(d(()=>f.lorem.sentences(...args))),
352
- slug: (...args)=>T.string.as(d(()=>f.lorem.slug(...args))),
353
- text: (...args)=>T.string.as(d(()=>f.lorem.text(...args))),
354
- word: (...args)=>T.string.as(d(()=>f.lorem.word(...args))),
355
- words: (...args)=>T.string.as(d(()=>f.lorem.words(...args)))
349
+ lines: (...args)=>T.string.as(d(w.lorem.lines(...args))),
350
+ paragraph: (...args)=>T.string.as(d(w.lorem.paragraph(...args))),
351
+ paragraphs: (...args)=>T.string.as(d(w.lorem.paragraphs(...args))),
352
+ sentence: (...args)=>T.string.as(d(w.lorem.sentence(...args))),
353
+ sentences: (...args)=>T.string.as(d(w.lorem.sentences(...args))),
354
+ slug: (...args)=>T.string.as(d(w.lorem.slug(...args))),
355
+ text: (...args)=>T.string.as(d(w.lorem.text(...args))),
356
+ word: (...args)=>T.string.as(d(w.lorem.word(...args))),
357
+ words: (...args)=>T.string.as(d(w.lorem.words(...args)))
356
358
  },
357
359
  music: {
358
360
  album: (...args)=>T.string.as(d(()=>f.music.album(...args))),
@@ -417,7 +419,7 @@ function build(T, faker, draw) {
417
419
  sample: (...args)=>T.string.as(d(()=>f.string.sample(...args))),
418
420
  symbol: (...args)=>T.string.as(d(()=>f.string.symbol(...args))),
419
421
  ulid: (...args)=>T.string.as(d(()=>f.string.ulid(...args))),
420
- uuid: (...args)=>T.string.as(d(()=>f.string.uuid(...args)), {
422
+ uuid: (...args)=>T.string.as(d(()=>f.string.uuid(...args))).admits({
421
423
  format: "uuid"
422
424
  })
423
425
  },
@@ -429,7 +431,7 @@ function build(T, faker, draw) {
429
431
  directoryPath: (...args)=>T.string.as(d(()=>f.system.directoryPath(...args))),
430
432
  fileExt: (...args)=>T.string.as(d(()=>f.system.fileExt(...args))),
431
433
  fileName: (...args)=>T.string.as(d(()=>f.system.fileName(...args))),
432
- filePath: (...args)=>T.string.as(d(()=>f.system.filePath(...args)), {
434
+ filePath: (...args)=>T.string.as(d(()=>f.system.filePath(...args))).admits({
433
435
  format: "uri-reference"
434
436
  }),
435
437
  fileType: (...args)=>T.string.as(d(()=>f.system.fileType(...args))),
@@ -449,15 +451,15 @@ function build(T, faker, draw) {
449
451
  vrm: (...args)=>T.string.as(d(()=>f.vehicle.vrm(...args)))
450
452
  },
451
453
  word: {
452
- adjective: (...args)=>T.string.as(d(()=>f.word.adjective(...args))),
453
- adverb: (...args)=>T.string.as(d(()=>f.word.adverb(...args))),
454
- conjunction: (...args)=>T.string.as(d(()=>f.word.conjunction(...args))),
455
- interjection: (...args)=>T.string.as(d(()=>f.word.interjection(...args))),
456
- noun: (...args)=>T.string.as(d(()=>f.word.noun(...args))),
457
- preposition: (...args)=>T.string.as(d(()=>f.word.preposition(...args))),
458
- sample: (...args)=>T.string.as(d(()=>f.word.sample(...args))),
459
- verb: (...args)=>T.string.as(d(()=>f.word.verb(...args))),
460
- words: (...args)=>T.string.as(d(()=>f.word.words(...args)))
454
+ adjective: (...args)=>T.string.as(d(w.word.adjective(...args))),
455
+ adverb: (...args)=>T.string.as(d(w.word.adverb(...args))),
456
+ conjunction: (...args)=>T.string.as(d(w.word.conjunction(...args))),
457
+ interjection: (...args)=>T.string.as(d(w.word.interjection(...args))),
458
+ noun: (...args)=>T.string.as(d(w.word.noun(...args))),
459
+ preposition: (...args)=>T.string.as(d(w.word.preposition(...args))),
460
+ sample: (...args)=>T.string.as(d(w.word.sample(...args))),
461
+ verb: (...args)=>T.string.as(d(w.word.verb(...args))),
462
+ words: (...args)=>T.string.as(d(w.word.words(...args)))
461
463
  }
462
464
  };
463
465
  }
@@ -0,0 +1,151 @@
1
+ import { FakerError } from "@faker-js/faker";
2
+ const categories = [
3
+ "adjective",
4
+ "adverb",
5
+ "conjunction",
6
+ "interjection",
7
+ "noun",
8
+ "preposition",
9
+ "verb"
10
+ ];
11
+ function filterByLength(list, options = {}) {
12
+ const { length, strategy = "fail" } = "number" == typeof options ? {
13
+ length: options
14
+ } : options;
15
+ if (void 0 === length) {
16
+ if ("shortest" === strategy || "longest" === strategy) return extreme(list, strategy);
17
+ return list;
18
+ }
19
+ const range = "number" == typeof length ? {
20
+ min: length,
21
+ max: length
22
+ } : length;
23
+ const matching = list.filter((word)=>word.length >= range.min && word.length <= range.max);
24
+ if (matching.length > 0) return matching;
25
+ switch(strategy){
26
+ case "fail":
27
+ throw new FakerError("No words found that match the given length.");
28
+ case "closest":
29
+ return closest(list, range);
30
+ case "shortest":
31
+ case "longest":
32
+ return extreme(list, strategy);
33
+ case "any-length":
34
+ return list;
35
+ }
36
+ }
37
+ function extreme(list, strategy) {
38
+ const lengths = list.map((word)=>word.length);
39
+ const target = "shortest" === strategy ? Math.min(...lengths) : Math.max(...lengths);
40
+ return list.filter((word)=>word.length === target);
41
+ }
42
+ function closest(list, range) {
43
+ let below = -1 / 0;
44
+ let above = 1 / 0;
45
+ for (const { length } of list){
46
+ if (length < range.min && length > below) below = length;
47
+ if (length > range.max && length < above) above = length;
48
+ }
49
+ const offset = Math.min(range.min - below, above - range.max);
50
+ return list.filter(({ length })=>length === range.min - offset || length === range.max + offset);
51
+ }
52
+ function listOf(read, options) {
53
+ let resolved;
54
+ return ()=>{
55
+ if (void 0 === resolved) resolved = filterByLength(read(), options);
56
+ return resolved;
57
+ };
58
+ }
59
+ function pick(random, list) {
60
+ if (0 === list.length) throw new FakerError("Cannot get value from empty dataset.");
61
+ return list[list.length > 1 ? Math.floor(random.next() * list.length) : 0];
62
+ }
63
+ function toWords(faker) {
64
+ const repeat = (count, separator, next)=>faker.helpers.multiple(next, {
65
+ count
66
+ }).join(separator);
67
+ const words = (word, count = 3)=>repeat(count, " ", word);
68
+ const sentence = (word, count = {
69
+ min: 3,
70
+ max: 10
71
+ })=>{
72
+ const text = words(word, count);
73
+ return `${text.charAt(0).toUpperCase()}${text.substring(1)}.`;
74
+ };
75
+ const sentences = (word, count = {
76
+ min: 2,
77
+ max: 6
78
+ }, separator = " ")=>repeat(count, separator, ()=>sentence(word));
79
+ const paragraph = (word, count = 3)=>sentences(word, count);
80
+ const paragraphs = (word, count = 3, separator = "\n")=>repeat(count, separator, ()=>paragraph(word));
81
+ const lines = (word, count = {
82
+ min: 1,
83
+ max: 5
84
+ })=>sentences(word, count, "\n");
85
+ const texts = [
86
+ sentence,
87
+ sentences,
88
+ paragraph,
89
+ paragraphs,
90
+ lines
91
+ ];
92
+ const lorem = (compose)=>{
93
+ const list = listOf(()=>faker.definitions.lorem.word, void 0);
94
+ return ({ random })=>compose(()=>pick(random, list()), random);
95
+ };
96
+ const category = (name)=>(options)=>{
97
+ const list = listOf(()=>faker.definitions.word[name], options);
98
+ return ({ random })=>pick(random, list());
99
+ };
100
+ const sampler = (options)=>{
101
+ const lists = categories.map((name)=>listOf(()=>faker.definitions.word[name], options));
102
+ return (random)=>{
103
+ for (const list of faker.helpers.shuffle(lists))try {
104
+ return pick(random, list());
105
+ } catch {
106
+ continue;
107
+ }
108
+ throw new FakerError("No matching word data available for the current locale");
109
+ };
110
+ };
111
+ return {
112
+ lorem: {
113
+ lines: (count)=>lorem((word)=>lines(word, count)),
114
+ paragraph: (count)=>lorem((word)=>paragraph(word, count)),
115
+ paragraphs: (count, separator)=>lorem((word)=>paragraphs(word, count, separator)),
116
+ sentence: (count)=>lorem((word)=>sentence(word, count)),
117
+ sentences: (count, separator)=>lorem((word)=>sentences(word, count, separator)),
118
+ slug: (count)=>lorem((word)=>faker.helpers.slugify(words(word, count))),
119
+ text: ()=>lorem((word, random)=>pick(random, texts)(word)),
120
+ word: (options)=>{
121
+ const list = listOf(()=>faker.definitions.lorem.word, options);
122
+ return ({ random })=>pick(random, list());
123
+ },
124
+ words: (count)=>lorem((word)=>words(word, count))
125
+ },
126
+ word: {
127
+ adjective: category("adjective"),
128
+ adverb: category("adverb"),
129
+ conjunction: category("conjunction"),
130
+ interjection: category("interjection"),
131
+ noun: category("noun"),
132
+ preposition: category("preposition"),
133
+ sample: (options)=>{
134
+ const sample = sampler(options);
135
+ return ({ random })=>sample(random);
136
+ },
137
+ verb: category("verb"),
138
+ words: (options = {})=>{
139
+ const { count = {
140
+ min: 1,
141
+ max: 3
142
+ } } = "number" == typeof options ? {
143
+ count: options
144
+ } : options;
145
+ const sample = sampler(void 0);
146
+ return ({ random })=>repeat(count, " ", ()=>sample(random));
147
+ }
148
+ }
149
+ };
150
+ }
151
+ export { toWords };
package/dist/esm/index.js CHANGED
@@ -27,7 +27,7 @@ function draw(produce) {
27
27
  const previous = scope;
28
28
  scope = context;
29
29
  try {
30
- return produce();
30
+ return produce(context);
31
31
  } finally{
32
32
  scope = previous;
33
33
  }
@@ -22,4 +22,4 @@ import type { FakerExtension, Registry } from "./Types";
22
22
  * runtime floor on the builder count: the type-level guards read the surface
23
23
  * alone and cannot see a spread that drops a module.
24
24
  */
25
- export declare function construct(T: Registry, faker: Faker, draw: <$T>(produce: () => $T) => (context: ProduceContext) => $T): FakerExtension;
25
+ export declare function construct(T: Registry, faker: Faker, draw: <$T>(produce: (context: ProduceContext) => $T) => (context: ProduceContext) => $T): FakerExtension;
@@ -3,7 +3,8 @@
3
3
  * exports that name, and importing both into one scope would collide. Follows
4
4
  * core's `FabricatorError` shape (abstract base plus a merged namespace of
5
5
  * subclasses) so `e instanceof FabricatorError` still catches everything either
6
- * package throws.
6
+ * package throws. A `name` is prefixed with this package's name, as core's are
7
+ * with its own.
7
8
  */
8
9
  import { FabricatorError } from "@ghostry/fabricator";
9
10
  export declare abstract class FakerExtensionError extends FabricatorError {
@@ -9,7 +9,7 @@
9
9
  * the faker-bump workflow; an entry added or changed here needs the matching
10
10
  * edit there, and vice versa.
11
11
  *
12
- * Three shapes before editing:
12
+ * Shapes before editing:
13
13
  *
14
14
  * - Every builder spreads faker's `Parameters<...>` rather than taking a single
15
15
  * `options?`. Faker's zero-parameter methods (`animal.bear()`) and its
@@ -30,10 +30,16 @@
30
30
  * whose _first_ overload takes `format?: StringColorFormat` and returns
31
31
  * `string`. `'decimal'` is `NumberColorFormat`'s sole member, so naming it is
32
32
  * exact.
33
+ * - `lorem`'s and `word`'s builders never call faker's own method: faker's
34
+ * per-word path copies the word list and parses an options object for every
35
+ * word drawn, so they draw through `./Words.ts`, which reproduces faker's
36
+ * output exactly from the same stream.
33
37
  *
34
38
  * Every node is a plain object or a bare builder function, never a function
35
39
  * carrying properties — the invariant `deepMerge` needs to recurse through an
36
40
  * extended registry, guarded by `test/Mergeable.test.ts`.
41
+ *
42
+ * @module
37
43
  */
38
44
  import type { Faker } from "@faker-js/faker";
39
45
  import type { ProduceContext } from "@ghostry/fabricator";
@@ -41,9 +47,9 @@ import type { Registry } from "../Types";
41
47
  import type { FakerModules } from "./Types";
42
48
  /**
43
49
  * `src/index.ts`'s `draw`, by type — the save/restore wrapper that binds a
44
- * producer to the leaf's own `random`/`clock`. Named here rather than imported
45
- * so this file stays type-only in its dependencies.
50
+ * producer to the leaf's own `random`/`clock`. Named here rather than imported,
51
+ * since `src/index.ts` imports this file.
46
52
  */
47
- type Draw = <$T>(produce: () => $T) => (context: ProduceContext) => $T;
53
+ type Draw = <$T>(produce: (context: ProduceContext) => $T) => (context: ProduceContext) => $T;
48
54
  export declare function build(T: Registry, faker: Faker, draw: Draw): FakerModules;
49
55
  export {};
@@ -41,6 +41,9 @@
41
41
  * union rather than a bare `string`.
42
42
  * 4. A reordered overload set changes `Parameters<...>` here, which the matching
43
43
  * builder's `...args` forwarding then fails to accept.
44
+ * 5. A change to how `lorem` or `word` draws fails `test/Parity.test.ts`, at `bun
45
+ * test` rather than `check`: those builders reimplement faker's method
46
+ * bodies in `./Words.ts`, which must then be brought back in line.
44
47
  *
45
48
  * Fix the entry in **both** files; the annotation on `build` fails until they
46
49
  * agree.
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Faker 10's `lorem` and `word` method bodies, minus the word-list copy.
3
+ *
4
+ * Faker's per-word path has two costs. With no `length`, its shared
5
+ * `filterWordListByLength` returns `[...wordList]`, copying a whole locale list
6
+ * (up to 1,000 entries in `en`) to index one element. That copy is close to
7
+ * free where the engine shares a literal array's storage on spread (Node 22–26
8
+ * and Bun 1.4.4 with faker's bundled locales), but costs about a microsecond
9
+ * per word on Bun 1.2.14, and on any engine when a locale's lists are built at
10
+ * runtime. Each word then goes through `helpers.arrayElement` → `number.int`,
11
+ * which parses an options object per draw and is most of what remains where the
12
+ * copy is free. `lorem.sentence`/`paragraph`/`text` pay both once per word.
13
+ * Every builder in `./Build.ts`'s `lorem`/`word` modules draws through this
14
+ * file instead and never calls faker's own method.
15
+ *
16
+ * The copy is reported upstream as
17
+ * https://github.com/faker-js/faker/issues/4242. A faker release that drops it
18
+ * is the point to weigh calling faker's methods again, not an automatic
19
+ * removal: the per-draw options parsing and definitions lookup would remain.
20
+ *
21
+ * **The output is faker's exactly, not an approximation.** `pick` reads
22
+ * `random.next()` and indexes with `arrayElement`'s own arithmetic,
23
+ * `Math.floor(u * length)`, from the same stream faker's randomizer reads
24
+ * (`src/index.ts`'s `draw` installs one `context` for both). Every draw made
25
+ * once per group rather than once per word — a `count` resolved through
26
+ * `rangeToNumber`, `word.sample`'s shuffle, `slug`'s `slugify` — goes through
27
+ * faker's own helpers, which keeps their semantics (an inverted range's error,
28
+ * a fractional count's truncation) without restating them, at a cost that is
29
+ * off the hot path. So a seed's data is unchanged, the surface keeps faker's
30
+ * `Parameters<...>`, and nothing about this is a deviation in the sense of
31
+ * `AGENTS.md`'s deviation policy. `test/Parity.test.ts` is the guard: it
32
+ * compares every builder here against faker's own method on an identical
33
+ * stream, so a faker release that changes how these draw fails there.
34
+ *
35
+ * Each filtered list is resolved once per builder call, lazily on the first
36
+ * draw, and a throw is never cached: missing locale data (thrown by faker's
37
+ * definitions proxy) and `strategy: "fail"` still throw from `fabricate()` on
38
+ * every call, as faker's own methods do, rather than from the builder call.
39
+ *
40
+ * @module
41
+ */
42
+ import type { Faker } from "@faker-js/faker";
43
+ import type { ProduceContext } from "@ghostry/fabricator";
44
+ type Producer = (context: ProduceContext) => string;
45
+ type WordOptions = Parameters<Faker["word"]["noun"]>[0];
46
+ type Count = NonNullable<Parameters<Faker["lorem"]["words"]>[0]>;
47
+ export declare function toWords(faker: Faker): {
48
+ lorem: {
49
+ lines: (count?: Count) => Producer;
50
+ paragraph: (count?: Count) => Producer;
51
+ paragraphs: (count?: Count, separator?: string) => Producer;
52
+ sentence: (count?: Count) => Producer;
53
+ sentences: (count?: Count, separator?: string) => Producer;
54
+ slug: (count?: Count) => Producer;
55
+ text: () => Producer;
56
+ word: (options?: WordOptions) => Producer;
57
+ words: (count?: Count) => Producer;
58
+ };
59
+ word: {
60
+ adjective: (options?: WordOptions) => Producer;
61
+ adverb: (options?: WordOptions) => Producer;
62
+ conjunction: (options?: WordOptions) => Producer;
63
+ interjection: (options?: WordOptions) => Producer;
64
+ noun: (options?: WordOptions) => Producer;
65
+ preposition: (options?: WordOptions) => Producer;
66
+ sample: (options?: WordOptions) => Producer;
67
+ verb: (options?: WordOptions) => Producer;
68
+ words: (options?: Parameters<Faker["word"]["words"]>[0]) => Producer;
69
+ };
70
+ };
71
+ export {};
@@ -66,8 +66,8 @@ export type Options = {
66
66
  * declaration emission (`tsgo`, not `tsc --noEmit`; see `AGENTS.md`'s
67
67
  * declaration-emit trap) refuses to print an anonymous expansion of one. Even
68
68
  * where it printed it would be the wrong contract: inference would surface each
69
- * builder's incidental narrowness (`Schema<{ produce; hints }>`) where the
70
- * declared surface states `Primitive.string.Schema`.
69
+ * builder's incidental narrowness (`Schema<{ produce }>`) where the declared
70
+ * surface states `Primitive.string.Schema`.
71
71
  *
72
72
  * Every node is a plain object or a leaf builder function, never a function
73
73
  * carrying properties: `deepMerge` recurses only where `isPlainObject` holds on
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghostry/fabricator-extension-faker-v10",
3
- "version": "0.0.2",
3
+ "version": "0.0.3",
4
4
  "license": "MIT",
5
5
  "description": "@faker-js/faker integration for @ghostry/fabricator — faker's data generators, seeded and reproducible through the fabricator instance that calls them.",
6
6
  "keywords": [
@@ -21,7 +21,7 @@
21
21
  "url": "git+https://github.com/ghostry-dev/fabricator",
22
22
  "directory": "pkg/fabricator-extension-faker-v10"
23
23
  },
24
- "homepage": "https://docs.ghostry.dev/fabricator/",
24
+ "homepage": "https://docs.ghostry.dev/fabricator",
25
25
  "bugs": "https://github.com/ghostry-dev/fabricator/issues",
26
26
  "type": "module",
27
27
  "sideEffects": false,
@@ -53,20 +53,21 @@
53
53
  "check": "tsc --noEmit",
54
54
  "check:ts5": "bun --bun ../../node_modules/typescript-5/lib/tsc.js --noEmit",
55
55
  "check:jsr": "DENO_BIN_PATH=$PWD/../../node_modules/.bin/deno bunx --no-install jsr publish --dry-run --allow-dirty",
56
- "format": "bunx --no-install oxfmt .",
56
+ "format": "bun fmt",
57
57
  "test": "bun run check && bun run check:ts5 && bun run check:jsr && bun test"
58
58
  },
59
59
  "files": [
60
60
  "dist"
61
61
  ],
62
62
  "peerDependencies": {
63
- "@ghostry/fabricator": ">=0.0.1 <0.1.0",
63
+ "@ghostry/fabricator": ">=0.0.14 <0.1.0",
64
64
  "@faker-js/faker": "^10.5.0"
65
65
  },
66
66
  "devDependencies": {
67
- "@ghostry/fabricator": "0.0.5",
68
- "@ghostry/fabricator-adapter-typebox-v0": "0.0.2",
69
- "@faker-js/faker": "^10.5.0",
67
+ "@ghostry/fabricator": "0.0.14",
68
+ "@ghostry/fabricator-adapter-typebox-v0": "0.0.4",
69
+ "@faker-js/faker": "^10.6.0",
70
+ "@ghostry/harness": "^0.0.6",
70
71
  "@sinclair/typebox": "^0.34.52"
71
72
  }
72
73
  }