forgery 0.2.0__tar.gz → 0.3.0__tar.gz

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 (160) hide show
  1. {forgery-0.2.0 → forgery-0.3.0}/CHANGELOG.md +20 -1
  2. {forgery-0.2.0 → forgery-0.3.0}/Cargo.lock +1 -1
  3. {forgery-0.2.0 → forgery-0.3.0}/Cargo.toml +1 -1
  4. {forgery-0.2.0 → forgery-0.3.0}/PKG-INFO +40 -2
  5. {forgery-0.2.0 → forgery-0.3.0}/README.md +39 -1
  6. {forgery-0.2.0 → forgery-0.3.0}/benches/generators.rs +74 -1
  7. {forgery-0.2.0 → forgery-0.3.0}/pyproject.toml +1 -1
  8. {forgery-0.2.0 → forgery-0.3.0}/python/forgery/__init__.py +418 -1
  9. {forgery-0.2.0 → forgery-0.3.0}/python/forgery/__init__.pyi +148 -0
  10. {forgery-0.2.0 → forgery-0.3.0}/python/forgery/_forgery.pyi +158 -0
  11. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/mod.rs +7 -0
  12. forgery-0.3.0/src/data/en_us/web.rs +299 -0
  13. {forgery-0.2.0 → forgery-0.3.0}/src/lib.rs +486 -0
  14. {forgery-0.2.0 → forgery-0.3.0}/src/providers/custom.rs +16 -0
  15. forgery-0.3.0/src/providers/html.rs +1588 -0
  16. {forgery-0.2.0 → forgery-0.3.0}/src/providers/mod.rs +2 -0
  17. {forgery-0.2.0 → forgery-0.3.0}/src/providers/records.rs +21 -1
  18. forgery-0.3.0/src/providers/web.rs +838 -0
  19. forgery-0.3.0/tests/test_html.py +306 -0
  20. forgery-0.3.0/tests/test_html_pages.py +232 -0
  21. forgery-0.3.0/tests/test_http_robots.py +219 -0
  22. forgery-0.3.0/tests/test_web.py +317 -0
  23. forgery-0.3.0/tests/test_website.py +157 -0
  24. {forgery-0.2.0 → forgery-0.3.0}/.github/workflows/ci.yml +0 -0
  25. {forgery-0.2.0 → forgery-0.3.0}/.github/workflows/dependency-review.yml +0 -0
  26. {forgery-0.2.0 → forgery-0.3.0}/.github/workflows/release.yml +0 -0
  27. {forgery-0.2.0 → forgery-0.3.0}/.gitignore +0 -0
  28. {forgery-0.2.0 → forgery-0.3.0}/.pre-commit-config.yaml +0 -0
  29. {forgery-0.2.0 → forgery-0.3.0}/ARCHITECTURE.md +0 -0
  30. {forgery-0.2.0 → forgery-0.3.0}/CLAUDE.md +0 -0
  31. {forgery-0.2.0 → forgery-0.3.0}/LICENSE +0 -0
  32. {forgery-0.2.0 → forgery-0.3.0}/codecov.yml +0 -0
  33. {forgery-0.2.0 → forgery-0.3.0}/python/forgery/py.typed +0 -0
  34. {forgery-0.2.0 → forgery-0.3.0}/sonar-project.properties +0 -0
  35. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/banks.rs +0 -0
  36. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/bundeslaender.rs +0 -0
  37. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/cities.rs +0 -0
  38. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/color_names.rs +0 -0
  39. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/companies.rs +0 -0
  40. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/first_names.rs +0 -0
  41. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/last_names.rs +0 -0
  42. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/mod.rs +0 -0
  43. {forgery-0.2.0 → forgery-0.3.0}/src/data/de_de/streets.rs +0 -0
  44. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/banks.rs +0 -0
  45. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/cities.rs +0 -0
  46. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/color_names.rs +0 -0
  47. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/companies.rs +0 -0
  48. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/counties.rs +0 -0
  49. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/first_names.rs +0 -0
  50. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/last_names.rs +0 -0
  51. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/mod.rs +0 -0
  52. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_gb/streets.rs +0 -0
  53. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/banks.rs +0 -0
  54. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/cities.rs +0 -0
  55. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/color_names.rs +0 -0
  56. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/companies.rs +0 -0
  57. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/countries.rs +0 -0
  58. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/first_names.rs +0 -0
  59. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/last_names.rs +0 -0
  60. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/lorem.rs +0 -0
  61. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/states.rs +0 -0
  62. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/streets.rs +0 -0
  63. {forgery-0.2.0 → forgery-0.3.0}/src/data/en_us/tlds.rs +0 -0
  64. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/banks.rs +0 -0
  65. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/cities.rs +0 -0
  66. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/color_names.rs +0 -0
  67. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/companies.rs +0 -0
  68. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/first_names.rs +0 -0
  69. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/last_names.rs +0 -0
  70. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/mod.rs +0 -0
  71. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/provinces.rs +0 -0
  72. {forgery-0.2.0 → forgery-0.3.0}/src/data/es_es/streets.rs +0 -0
  73. {forgery-0.2.0 → forgery-0.3.0}/src/data/formats.rs +0 -0
  74. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/banks.rs +0 -0
  75. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/cities.rs +0 -0
  76. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/color_names.rs +0 -0
  77. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/companies.rs +0 -0
  78. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/first_names.rs +0 -0
  79. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/last_names.rs +0 -0
  80. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/mod.rs +0 -0
  81. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/regions.rs +0 -0
  82. {forgery-0.2.0 → forgery-0.3.0}/src/data/fr_fr/streets.rs +0 -0
  83. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/banks.rs +0 -0
  84. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/cities.rs +0 -0
  85. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/color_names.rs +0 -0
  86. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/companies.rs +0 -0
  87. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/first_names.rs +0 -0
  88. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/last_names.rs +0 -0
  89. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/mod.rs +0 -0
  90. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/regions.rs +0 -0
  91. {forgery-0.2.0 → forgery-0.3.0}/src/data/it_it/streets.rs +0 -0
  92. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/banks.rs +0 -0
  93. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/cities.rs +0 -0
  94. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/color_names.rs +0 -0
  95. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/companies.rs +0 -0
  96. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/first_names.rs +0 -0
  97. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/last_names.rs +0 -0
  98. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/mod.rs +0 -0
  99. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/prefectures.rs +0 -0
  100. {forgery-0.2.0 → forgery-0.3.0}/src/data/ja_jp/streets.rs +0 -0
  101. {forgery-0.2.0 → forgery-0.3.0}/src/data/macros.rs +0 -0
  102. {forgery-0.2.0 → forgery-0.3.0}/src/data/mod.rs +0 -0
  103. {forgery-0.2.0 → forgery-0.3.0}/src/data/traits.rs +0 -0
  104. {forgery-0.2.0 → forgery-0.3.0}/src/error.rs +0 -0
  105. {forgery-0.2.0 → forgery-0.3.0}/src/locale.rs +0 -0
  106. {forgery-0.2.0 → forgery-0.3.0}/src/providers/address.rs +0 -0
  107. {forgery-0.2.0 → forgery-0.3.0}/src/providers/async_records.rs +0 -0
  108. {forgery-0.2.0 → forgery-0.3.0}/src/providers/barcode.rs +0 -0
  109. {forgery-0.2.0 → forgery-0.3.0}/src/providers/boolean.rs +0 -0
  110. {forgery-0.2.0 → forgery-0.3.0}/src/providers/colors.rs +0 -0
  111. {forgery-0.2.0 → forgery-0.3.0}/src/providers/commerce.rs +0 -0
  112. {forgery-0.2.0 → forgery-0.3.0}/src/providers/company.rs +0 -0
  113. {forgery-0.2.0 → forgery-0.3.0}/src/providers/currency.rs +0 -0
  114. {forgery-0.2.0 → forgery-0.3.0}/src/providers/datetime.rs +0 -0
  115. {forgery-0.2.0 → forgery-0.3.0}/src/providers/file.rs +0 -0
  116. {forgery-0.2.0 → forgery-0.3.0}/src/providers/finance.rs +0 -0
  117. {forgery-0.2.0 → forgery-0.3.0}/src/providers/geo.rs +0 -0
  118. {forgery-0.2.0 → forgery-0.3.0}/src/providers/identifiers.rs +0 -0
  119. {forgery-0.2.0 → forgery-0.3.0}/src/providers/internet.rs +0 -0
  120. {forgery-0.2.0 → forgery-0.3.0}/src/providers/isbn.rs +0 -0
  121. {forgery-0.2.0 → forgery-0.3.0}/src/providers/names.rs +0 -0
  122. {forgery-0.2.0 → forgery-0.3.0}/src/providers/network.rs +0 -0
  123. {forgery-0.2.0 → forgery-0.3.0}/src/providers/numbers.rs +0 -0
  124. {forgery-0.2.0 → forgery-0.3.0}/src/providers/password.rs +0 -0
  125. {forgery-0.2.0 → forgery-0.3.0}/src/providers/pattern.rs +0 -0
  126. {forgery-0.2.0 → forgery-0.3.0}/src/providers/phone.rs +0 -0
  127. {forgery-0.2.0 → forgery-0.3.0}/src/providers/profile.rs +0 -0
  128. {forgery-0.2.0 → forgery-0.3.0}/src/providers/ssn.rs +0 -0
  129. {forgery-0.2.0 → forgery-0.3.0}/src/providers/text.rs +0 -0
  130. {forgery-0.2.0 → forgery-0.3.0}/src/providers/user_agent.rs +0 -0
  131. {forgery-0.2.0 → forgery-0.3.0}/src/providers/vehicle.rs +0 -0
  132. {forgery-0.2.0 → forgery-0.3.0}/src/rng.rs +0 -0
  133. {forgery-0.2.0 → forgery-0.3.0}/tests/__init__.py +0 -0
  134. {forgery-0.2.0 → forgery-0.3.0}/tests/benchmarks/__init__.py +0 -0
  135. {forgery-0.2.0 → forgery-0.3.0}/tests/benchmarks/bench_vs_faker.py +0 -0
  136. {forgery-0.2.0 → forgery-0.3.0}/tests/conftest.py +0 -0
  137. {forgery-0.2.0 → forgery-0.3.0}/tests/test_async.py +0 -0
  138. {forgery-0.2.0 → forgery-0.3.0}/tests/test_barcode.py +0 -0
  139. {forgery-0.2.0 → forgery-0.3.0}/tests/test_batch.py +0 -0
  140. {forgery-0.2.0 → forgery-0.3.0}/tests/test_boolean.py +0 -0
  141. {forgery-0.2.0 → forgery-0.3.0}/tests/test_commerce.py +0 -0
  142. {forgery-0.2.0 → forgery-0.3.0}/tests/test_convenience_new.py +0 -0
  143. {forgery-0.2.0 → forgery-0.3.0}/tests/test_coverage_gaps.py +0 -0
  144. {forgery-0.2.0 → forgery-0.3.0}/tests/test_custom_providers.py +0 -0
  145. {forgery-0.2.0 → forgery-0.3.0}/tests/test_error_paths.py +0 -0
  146. {forgery-0.2.0 → forgery-0.3.0}/tests/test_file.py +0 -0
  147. {forgery-0.2.0 → forgery-0.3.0}/tests/test_geo.py +0 -0
  148. {forgery-0.2.0 → forgery-0.3.0}/tests/test_integration.py +0 -0
  149. {forgery-0.2.0 → forgery-0.3.0}/tests/test_isbn.py +0 -0
  150. {forgery-0.2.0 → forgery-0.3.0}/tests/test_locales.py +0 -0
  151. {forgery-0.2.0 → forgery-0.3.0}/tests/test_new_features.py +0 -0
  152. {forgery-0.2.0 → forgery-0.3.0}/tests/test_pattern.py +0 -0
  153. {forgery-0.2.0 → forgery-0.3.0}/tests/test_phase2_providers.py +0 -0
  154. {forgery-0.2.0 → forgery-0.3.0}/tests/test_profile.py +0 -0
  155. {forgery-0.2.0 → forgery-0.3.0}/tests/test_providers.py +0 -0
  156. {forgery-0.2.0 → forgery-0.3.0}/tests/test_records.py +0 -0
  157. {forgery-0.2.0 → forgery-0.3.0}/tests/test_seeding.py +0 -0
  158. {forgery-0.2.0 → forgery-0.3.0}/tests/test_ssn.py +0 -0
  159. {forgery-0.2.0 → forgery-0.3.0}/tests/test_user_agent_currency_creditcard.py +0 -0
  160. {forgery-0.2.0 → forgery-0.3.0}/tests/test_vehicle.py +0 -0
@@ -7,8 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.3.0] - 2026-03-17
11
+
10
12
  ### Added
11
13
 
14
+ - **Web & HTML Providers**: Full suite of web content generators
15
+ - `url_path()` / `url_paths(n)`: URL paths (e.g., "/blog/products/42")
16
+ - `url_slug()` / `url_slugs(n)`: URL slugs (e.g., "ultimate-guide-2024")
17
+ - `query_string()` / `query_strings(n)`: Query strings (e.g., "?page=2&sort=date")
18
+ - `meta_description()` / `meta_descriptions(n)`: HTML meta description tags
19
+ - `og_tags()` / `og_tags_batch(n)`: Open Graph meta tag sets
20
+ - `hreflang_tags()` / `hreflang_tags_batch(n)`: Hreflang link tags with x-default
21
+ - `img_tag(ratio)` / `img_tags(n, ratio)`: Image tags with configurable missing-alt ratio
22
+ - `http_headers()` / `http_headers_batch(n)`: HTTP response header dicts
23
+ - `content_type_header()` / `content_type_headers(n)`: Content-Type values
24
+ - `robots_txt()` / `robots_txts(n)`: robots.txt file contents
25
+ - `html_page(**kwargs)` / `html_pages(n, **kwargs)`: Full HTML5 pages with 9 configurable parameters
26
+ - `website(pages, domain)`: Interlinked website as dict of URL to HTML with spanning-tree connectivity
27
+ - **Schema integration**: `url_path`, `url_slug`, `query_string` work in `records()` / `records_arrow()`
28
+ - **Criterion benchmarks** for url_paths, html_page, and website generators
29
+
12
30
  - **Custom Providers API** (Phase 3.2): Register your own data providers
13
31
  - `add_provider(name, options)`: Register uniform (equal probability) provider
14
32
  - `add_weighted_provider(name, weighted_options)`: Register weighted provider
@@ -130,5 +148,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
130
148
  - SonarCloud integration for code quality
131
149
  - CodeQL static analysis
132
150
 
133
- [Unreleased]: https://github.com/williajm/forgery/compare/v0.1.0...HEAD
151
+ [Unreleased]: https://github.com/williajm/forgery/compare/v0.3.0...HEAD
152
+ [0.3.0]: https://github.com/williajm/forgery/compare/v0.2.0...v0.3.0
134
153
  [0.1.0]: https://github.com/williajm/forgery/releases/tag/v0.1.0
@@ -452,7 +452,7 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
452
452
 
453
453
  [[package]]
454
454
  name = "forgery"
455
- version = "0.2.0"
455
+ version = "0.3.0"
456
456
  dependencies = [
457
457
  "arrow-array",
458
458
  "arrow-buffer",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "forgery"
3
- version = "0.2.0"
3
+ version = "0.3.0"
4
4
  edition = "2021"
5
5
  description = "Fake data at the speed of Rust"
6
6
  license = "MIT"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: forgery
3
- Version: 0.2.0
3
+ Version: 0.3.0
4
4
  Classifier: Development Status :: 3 - Alpha
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -231,6 +231,44 @@ fake2.emails(100)
231
231
  | `ipv6s(n)` | `ipv6()` | IPv6 addresses |
232
232
  | `mac_addresses(n)` | `mac_address()` | MAC addresses |
233
233
 
234
+ ### Web & HTML
235
+
236
+ | Batch | Single | Description |
237
+ |-------|--------|-------------|
238
+ | `url_paths(n)` | `url_path()` | URL paths (e.g., "/blog/products/42") |
239
+ | `url_slugs(n)` | `url_slug()` | URL slugs (e.g., "ultimate-guide-2024") |
240
+ | `query_strings(n)` | `query_string()` | Query strings (e.g., "?page=2&sort=date") |
241
+ | `meta_descriptions(n)` | `meta_description()` | HTML meta description tags |
242
+ | `og_tags_batch(n)` | `og_tags()` | Open Graph meta tag sets (multi-line) |
243
+ | `hreflang_tags_batch(n)` | `hreflang_tags()` | Hreflang link tag sets with x-default |
244
+ | `img_tags(n, ratio)` | `img_tag(ratio)` | Image tags (configurable missing alt ratio) |
245
+ | `content_type_headers(n)` | `content_type_header()` | Content-Type header values |
246
+ | `http_headers_batch(n)` | `http_headers()` | HTTP response header dicts |
247
+ | `robots_txts(n)` | `robots_txt()` | robots.txt file contents |
248
+ | `html_pages(n, ...)` | `html_page(...)` | Full HTML5 pages with configurable SEO elements |
249
+ | - | `website(pages, domain)` | Interlinked website (dict of URL → HTML) |
250
+
251
+ ```python
252
+ from forgery import Faker
253
+
254
+ fake = Faker()
255
+ fake.seed(42)
256
+
257
+ # Generate a full HTML page with SEO elements
258
+ page = fake.html_page(
259
+ headings=4,
260
+ internal_links=5,
261
+ images=3,
262
+ include_og_tags=True,
263
+ domain="mysite.com",
264
+ )
265
+
266
+ # Generate an interlinked website for crawl testing
267
+ site = fake.website(pages=20, domain="example.com")
268
+ # site = {"https://example.com/": "<html>...", "https://example.com/blog/guide": "<html>...", ...}
269
+ # Every page is reachable from the homepage via link traversal
270
+ ```
271
+
234
272
  ### Finance
235
273
 
236
274
  | Batch | Single | Description |
@@ -571,7 +609,7 @@ pandas, DuckDB, etc.).
571
609
  | Date range | `("date", start, end)` | `("date", "2020-01-01", "2024-12-31")` |
572
610
  | Choice | `("choice", [options])` | `("choice", ["a", "b", "c"])` |
573
611
 
574
- All simple types from the generators above are supported: `name`, `first_name`, `last_name`, `email`, `safe_email`, `free_email`, `phone`, `uuid`, `int`, `float`, `date`, `datetime`, `street_address`, `city`, `state`, `country`, `zip_code`, `address`, `company`, `job`, `catch_phrase`, `url`, `domain_name`, `ipv4`, `ipv6`, `mac_address`, `credit_card`, `iban`, `sentence`, `paragraph`, `text`, `color`, `hex_color`, `rgb_color`, `md5`, `sha256`, `latitude`, `longitude`, `coordinate`, `boolean`, `ssn`, `file_name`, `file_extension`, `mime_type`, `file_path`, `license_plate`, `vehicle_make`, `vehicle_model`, `vehicle_year`, `vin`, `ean13`, `ean8`, `upc_a`, `upc_e`, `isbn10`, `isbn13`, `product_name`, `product_category`, `department`, `product_material`.
612
+ All simple types from the generators above are supported: `name`, `first_name`, `last_name`, `email`, `safe_email`, `free_email`, `phone`, `uuid`, `int`, `float`, `date`, `datetime`, `street_address`, `city`, `state`, `country`, `zip_code`, `address`, `company`, `job`, `catch_phrase`, `url`, `domain_name`, `ipv4`, `ipv6`, `mac_address`, `credit_card`, `iban`, `sentence`, `paragraph`, `text`, `color`, `hex_color`, `rgb_color`, `md5`, `sha256`, `latitude`, `longitude`, `coordinate`, `boolean`, `ssn`, `file_name`, `file_extension`, `mime_type`, `file_path`, `license_plate`, `vehicle_make`, `vehicle_model`, `vehicle_year`, `vin`, `ean13`, `ean8`, `upc_a`, `upc_e`, `isbn10`, `isbn13`, `product_name`, `product_category`, `department`, `product_material`, `url_path`, `url_slug`, `query_string`.
575
613
 
576
614
  ## Async Generation
577
615
 
@@ -197,6 +197,44 @@ fake2.emails(100)
197
197
  | `ipv6s(n)` | `ipv6()` | IPv6 addresses |
198
198
  | `mac_addresses(n)` | `mac_address()` | MAC addresses |
199
199
 
200
+ ### Web & HTML
201
+
202
+ | Batch | Single | Description |
203
+ |-------|--------|-------------|
204
+ | `url_paths(n)` | `url_path()` | URL paths (e.g., "/blog/products/42") |
205
+ | `url_slugs(n)` | `url_slug()` | URL slugs (e.g., "ultimate-guide-2024") |
206
+ | `query_strings(n)` | `query_string()` | Query strings (e.g., "?page=2&sort=date") |
207
+ | `meta_descriptions(n)` | `meta_description()` | HTML meta description tags |
208
+ | `og_tags_batch(n)` | `og_tags()` | Open Graph meta tag sets (multi-line) |
209
+ | `hreflang_tags_batch(n)` | `hreflang_tags()` | Hreflang link tag sets with x-default |
210
+ | `img_tags(n, ratio)` | `img_tag(ratio)` | Image tags (configurable missing alt ratio) |
211
+ | `content_type_headers(n)` | `content_type_header()` | Content-Type header values |
212
+ | `http_headers_batch(n)` | `http_headers()` | HTTP response header dicts |
213
+ | `robots_txts(n)` | `robots_txt()` | robots.txt file contents |
214
+ | `html_pages(n, ...)` | `html_page(...)` | Full HTML5 pages with configurable SEO elements |
215
+ | - | `website(pages, domain)` | Interlinked website (dict of URL → HTML) |
216
+
217
+ ```python
218
+ from forgery import Faker
219
+
220
+ fake = Faker()
221
+ fake.seed(42)
222
+
223
+ # Generate a full HTML page with SEO elements
224
+ page = fake.html_page(
225
+ headings=4,
226
+ internal_links=5,
227
+ images=3,
228
+ include_og_tags=True,
229
+ domain="mysite.com",
230
+ )
231
+
232
+ # Generate an interlinked website for crawl testing
233
+ site = fake.website(pages=20, domain="example.com")
234
+ # site = {"https://example.com/": "<html>...", "https://example.com/blog/guide": "<html>...", ...}
235
+ # Every page is reachable from the homepage via link traversal
236
+ ```
237
+
200
238
  ### Finance
201
239
 
202
240
  | Batch | Single | Description |
@@ -537,7 +575,7 @@ pandas, DuckDB, etc.).
537
575
  | Date range | `("date", start, end)` | `("date", "2020-01-01", "2024-12-31")` |
538
576
  | Choice | `("choice", [options])` | `("choice", ["a", "b", "c"])` |
539
577
 
540
- All simple types from the generators above are supported: `name`, `first_name`, `last_name`, `email`, `safe_email`, `free_email`, `phone`, `uuid`, `int`, `float`, `date`, `datetime`, `street_address`, `city`, `state`, `country`, `zip_code`, `address`, `company`, `job`, `catch_phrase`, `url`, `domain_name`, `ipv4`, `ipv6`, `mac_address`, `credit_card`, `iban`, `sentence`, `paragraph`, `text`, `color`, `hex_color`, `rgb_color`, `md5`, `sha256`, `latitude`, `longitude`, `coordinate`, `boolean`, `ssn`, `file_name`, `file_extension`, `mime_type`, `file_path`, `license_plate`, `vehicle_make`, `vehicle_model`, `vehicle_year`, `vin`, `ean13`, `ean8`, `upc_a`, `upc_e`, `isbn10`, `isbn13`, `product_name`, `product_category`, `department`, `product_material`.
578
+ All simple types from the generators above are supported: `name`, `first_name`, `last_name`, `email`, `safe_email`, `free_email`, `phone`, `uuid`, `int`, `float`, `date`, `datetime`, `street_address`, `city`, `state`, `country`, `zip_code`, `address`, `company`, `job`, `catch_phrase`, `url`, `domain_name`, `ipv4`, `ipv6`, `mac_address`, `credit_card`, `iban`, `sentence`, `paragraph`, `text`, `color`, `hex_color`, `rgb_color`, `md5`, `sha256`, `latitude`, `longitude`, `coordinate`, `boolean`, `ssn`, `file_name`, `file_extension`, `mime_type`, `file_path`, `license_plate`, `vehicle_make`, `vehicle_model`, `vehicle_year`, `vin`, `ean13`, `ean8`, `upc_a`, `upc_e`, `isbn10`, `isbn13`, `product_name`, `product_category`, `department`, `product_material`, `url_path`, `url_slug`, `query_string`.
541
579
 
542
580
  ## Async Generation
543
581
 
@@ -204,6 +204,76 @@ fn bench_records_generation(c: &mut Criterion) {
204
204
  group.finish();
205
205
  }
206
206
 
207
+ fn bench_url_paths(c: &mut Criterion) {
208
+ let mut group = c.benchmark_group("url_paths");
209
+
210
+ for size in [100, 1_000, 10_000].iter() {
211
+ group.throughput(Throughput::Elements(*size as u64));
212
+ group.bench_with_input(
213
+ criterion::BenchmarkId::from_parameter(size),
214
+ size,
215
+ |b, &size| {
216
+ let mut faker = Faker::new("en_US").unwrap();
217
+ faker.seed(42);
218
+ b.iter(|| {
219
+ let paths = faker.url_paths(black_box(size)).unwrap();
220
+ black_box(paths)
221
+ });
222
+ },
223
+ );
224
+ }
225
+ group.finish();
226
+ }
227
+
228
+ fn bench_html_page(c: &mut Criterion) {
229
+ let mut group = c.benchmark_group("html_page");
230
+
231
+ group.bench_function("single", |b| {
232
+ let mut faker = Faker::new("en_US").unwrap();
233
+ faker.seed(42);
234
+ let opts = _forgery::providers::html::HtmlPageOptions::default();
235
+ b.iter(|| black_box(faker.html_page(&opts)));
236
+ });
237
+
238
+ for size in [10, 100, 1_000].iter() {
239
+ group.throughput(Throughput::Elements(*size as u64));
240
+ group.bench_with_input(
241
+ criterion::BenchmarkId::new("batch", size),
242
+ size,
243
+ |b, &size| {
244
+ let mut faker = Faker::new("en_US").unwrap();
245
+ faker.seed(42);
246
+ let opts = _forgery::providers::html::HtmlPageOptions::default();
247
+ b.iter(|| {
248
+ let pages = faker.html_pages(black_box(size), &opts).unwrap();
249
+ black_box(pages)
250
+ });
251
+ },
252
+ );
253
+ }
254
+ group.finish();
255
+ }
256
+
257
+ fn bench_website(c: &mut Criterion) {
258
+ let mut group = c.benchmark_group("website");
259
+
260
+ for size in [5, 20, 100].iter() {
261
+ group.bench_with_input(
262
+ criterion::BenchmarkId::from_parameter(size),
263
+ size,
264
+ |b, &size| {
265
+ let mut faker = Faker::new("en_US").unwrap();
266
+ faker.seed(42);
267
+ b.iter(|| {
268
+ let site = faker.website(black_box(size), "example.com").unwrap();
269
+ black_box(site)
270
+ });
271
+ },
272
+ );
273
+ }
274
+ group.finish();
275
+ }
276
+
207
277
  criterion_group!(
208
278
  benches,
209
279
  bench_name_generation,
@@ -211,6 +281,9 @@ criterion_group!(
211
281
  bench_uuid_generation,
212
282
  bench_integer_generation,
213
283
  bench_single_value_generation,
214
- bench_records_generation
284
+ bench_records_generation,
285
+ bench_url_paths,
286
+ bench_html_page,
287
+ bench_website,
215
288
  );
216
289
  criterion_main!(benches);
@@ -4,7 +4,7 @@ build-backend = "maturin"
4
4
 
5
5
  [project]
6
6
  name = "forgery"
7
- version = "0.2.0"
7
+ version = "0.3.0"
8
8
  description = "Fake data at the speed of Rust"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }