synthra 0.1.0

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 (302) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +40 -0
  4. data/.yardopts +18 -0
  5. data/ACCESS_DOCS.md +116 -0
  6. data/ADVANCED_FEATURES_SUMMARY.md +245 -0
  7. data/CHANGELOG.md +498 -0
  8. data/CODE_OF_CONDUCT.md +122 -0
  9. data/CONTRIBUTING.md +307 -0
  10. data/DOCUMENTATION.md +204 -0
  11. data/DOCUMENTATION_GUIDE.md +194 -0
  12. data/LICENSE +22 -0
  13. data/README.md +1458 -0
  14. data/Rakefile +128 -0
  15. data/benchmark/README.md +591 -0
  16. data/benchmark/batch_generation.rb +63 -0
  17. data/benchmark/engine_comparison.rb +295 -0
  18. data/benchmark/single_record.rb +108 -0
  19. data/benchmark/streaming.rb +74 -0
  20. data/docs/ACTIVERECORD_INFERENCE.md +524 -0
  21. data/docs/API_SERVER.md +278 -0
  22. data/docs/CONFIG_FILE.md +315 -0
  23. data/docs/DATA_CONTRACTS.md +312 -0
  24. data/docs/ENHANCED_REPL.md +304 -0
  25. data/docs/FACTORY_BOT.md +271 -0
  26. data/docs/GITHUB_ACTION.md +420 -0
  27. data/docs/GRAPHQL_EXPORT.md +303 -0
  28. data/docs/GRAPHQL_FEDERATION.md +651 -0
  29. data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
  30. data/docs/LSP.md +189 -0
  31. data/docs/MIGRATION_GENERATOR.md +547 -0
  32. data/docs/MOCK_SERVER.md +280 -0
  33. data/docs/NATIVE_ENGINE.md +298 -0
  34. data/docs/OPENAPI_EXPORT.md +252 -0
  35. data/docs/PERFORMANCE_MODE.md +228 -0
  36. data/docs/PERSONAS.md +547 -0
  37. data/docs/PROPERTY_TESTING.md +284 -0
  38. data/docs/PROTOBUF_EXPORT.md +276 -0
  39. data/docs/QUALITY_METRICS.md +555 -0
  40. data/docs/QUICK_REFERENCE.md +88 -0
  41. data/docs/RAILS_ENGINE.md +404 -0
  42. data/docs/RAILS_INTEGRATION.md +454 -0
  43. data/docs/README.md +175 -0
  44. data/docs/REPL_QUICK_REFERENCE.md +149 -0
  45. data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
  46. data/docs/SCENARIOS.md +576 -0
  47. data/docs/SECURITY_TESTING.md +318 -0
  48. data/docs/SNAPSHOT_TESTING.md +308 -0
  49. data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
  50. data/docs/TERRAFORM_EXPORT.md +447 -0
  51. data/docs/TIME_TRAVEL.md +507 -0
  52. data/docs/USAGE_GUIDE.md +980 -0
  53. data/docs/WEBHOOK_SIMULATOR.md +579 -0
  54. data/docs/advanced_features.md +183 -0
  55. data/docs/api_reference.md +153 -0
  56. data/docs/behaviors.md +138 -0
  57. data/docs/best_practices.md +191 -0
  58. data/docs/dsl_reference.md +172 -0
  59. data/docs/examples.md +162 -0
  60. data/docs/generation_modes.md +125 -0
  61. data/docs/getting_started.md +92 -0
  62. data/docs/index.html +177 -0
  63. data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
  64. data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
  65. data/docs/troubleshooting.md +215 -0
  66. data/docs/type_reference.md +224 -0
  67. data/examples/hostile_mode_demo.rb +64 -0
  68. data/exe/synthra +6 -0
  69. data/ext/synthra_native/Cargo.lock +972 -0
  70. data/ext/synthra_native/Cargo.toml +46 -0
  71. data/ext/synthra_native/extconf.rb +41 -0
  72. data/ext/synthra_native/src/generator.rs +397 -0
  73. data/ext/synthra_native/src/lib.rs +229 -0
  74. data/ext/synthra_native/src/types.rs +1485 -0
  75. data/lib/generators/synthra/install_generator.rb +86 -0
  76. data/lib/generators/synthra/templates/api_response.dsl +15 -0
  77. data/lib/generators/synthra/templates/fake_data.rake +116 -0
  78. data/lib/generators/synthra/templates/synthra.yml +31 -0
  79. data/lib/generators/synthra/templates/synthra_support.rb +19 -0
  80. data/lib/generators/synthra/templates/user.dsl +11 -0
  81. data/lib/synthra/activerecord_inference.rb +394 -0
  82. data/lib/synthra/api.rb +381 -0
  83. data/lib/synthra/api_server.rb +718 -0
  84. data/lib/synthra/behaviors/applicator.rb +165 -0
  85. data/lib/synthra/behaviors/base.rb +155 -0
  86. data/lib/synthra/behaviors/close_connection.rb +60 -0
  87. data/lib/synthra/behaviors/deprecated.rb +100 -0
  88. data/lib/synthra/behaviors/failure.rb +69 -0
  89. data/lib/synthra/behaviors/latency.rb +103 -0
  90. data/lib/synthra/behaviors/partial_data.rb +83 -0
  91. data/lib/synthra/behaviors/randomize_order.rb +74 -0
  92. data/lib/synthra/behaviors/registry.rb +238 -0
  93. data/lib/synthra/behaviors/simulate_error.rb +79 -0
  94. data/lib/synthra/cli/commands/base.rb +105 -0
  95. data/lib/synthra/cli/commands/diff.rb +227 -0
  96. data/lib/synthra/cli/commands/docs.rb +67 -0
  97. data/lib/synthra/cli/commands/export.rb +150 -0
  98. data/lib/synthra/cli/commands/generate.rb +72 -0
  99. data/lib/synthra/cli/commands/import.rb +80 -0
  100. data/lib/synthra/cli/commands/lint.rb +53 -0
  101. data/lib/synthra/cli/commands/live.rb +57 -0
  102. data/lib/synthra/cli/commands/seed.rb +99 -0
  103. data/lib/synthra/cli/commands/validate.rb +28 -0
  104. data/lib/synthra/cli.rb +2471 -0
  105. data/lib/synthra/config_file.rb +129 -0
  106. data/lib/synthra/configuration.rb +281 -0
  107. data/lib/synthra/contracts_registry.rb +408 -0
  108. data/lib/synthra/database_seeder.rb +268 -0
  109. data/lib/synthra/deterministic_ids.rb +218 -0
  110. data/lib/synthra/documentation_generator.rb +414 -0
  111. data/lib/synthra/engine.rb +251 -0
  112. data/lib/synthra/errors.rb +1169 -0
  113. data/lib/synthra/export/base.rb +85 -0
  114. data/lib/synthra/export/csv.rb +101 -0
  115. data/lib/synthra/export/graphql.rb +266 -0
  116. data/lib/synthra/export/graphql_federation.rb +377 -0
  117. data/lib/synthra/export/graphviz.rb +258 -0
  118. data/lib/synthra/export/javascript.rb +327 -0
  119. data/lib/synthra/export/json_data.rb +61 -0
  120. data/lib/synthra/export/json_schema.rb +290 -0
  121. data/lib/synthra/export/openapi.rb +514 -0
  122. data/lib/synthra/export/protobuf.rb +483 -0
  123. data/lib/synthra/export/python.rb +560 -0
  124. data/lib/synthra/export/sql.rb +381 -0
  125. data/lib/synthra/export/sql_insert.rb +152 -0
  126. data/lib/synthra/export/terraform.rb +501 -0
  127. data/lib/synthra/export/type_mapping.rb +389 -0
  128. data/lib/synthra/export/typescript.rb +300 -0
  129. data/lib/synthra/export/xml_data.rb +104 -0
  130. data/lib/synthra/export/yaml_data.rb +56 -0
  131. data/lib/synthra/export.rb +404 -0
  132. data/lib/synthra/factory_bot_integration.rb +157 -0
  133. data/lib/synthra/field.rb +440 -0
  134. data/lib/synthra/functions/registry.rb +104 -0
  135. data/lib/synthra/generator/context.rb +372 -0
  136. data/lib/synthra/generator/engine.rb +336 -0
  137. data/lib/synthra/generator/faker_adapter.rb +425 -0
  138. data/lib/synthra/generator/modes.rb +444 -0
  139. data/lib/synthra/generator/resolver.rb +256 -0
  140. data/lib/synthra/generator/rng.rb +273 -0
  141. data/lib/synthra/generator/streamer.rb +63 -0
  142. data/lib/synthra/generator/uniqueness.rb +118 -0
  143. data/lib/synthra/initializer.rb +97 -0
  144. data/lib/synthra/limits.rb +279 -0
  145. data/lib/synthra/live_preview.rb +518 -0
  146. data/lib/synthra/loader_config.rb +78 -0
  147. data/lib/synthra/lsp/server.rb +689 -0
  148. data/lib/synthra/migration_generator.rb +301 -0
  149. data/lib/synthra/mixin.rb +174 -0
  150. data/lib/synthra/mock_server.rb +458 -0
  151. data/lib/synthra/native_engine.rb +304 -0
  152. data/lib/synthra/openapi_importer.rb +228 -0
  153. data/lib/synthra/output/json_formatter.rb +40 -0
  154. data/lib/synthra/output/ndjson_formatter.rb +55 -0
  155. data/lib/synthra/parser/ast.rb +1287 -0
  156. data/lib/synthra/parser/lexer.rb +1152 -0
  157. data/lib/synthra/parser/parser.rb +1664 -0
  158. data/lib/synthra/parser/tokens.rb +461 -0
  159. data/lib/synthra/performance_mode.rb +364 -0
  160. data/lib/synthra/personas.rb +397 -0
  161. data/lib/synthra/property_testing.rb +172 -0
  162. data/lib/synthra/quality_metrics.rb +405 -0
  163. data/lib/synthra/rails_test_helper.rb +248 -0
  164. data/lib/synthra/registry.rb +533 -0
  165. data/lib/synthra/relationships.rb +193 -0
  166. data/lib/synthra/repl/enhanced_repl.rb +607 -0
  167. data/lib/synthra/repl/formatter.rb +191 -0
  168. data/lib/synthra/scenarios.rb +423 -0
  169. data/lib/synthra/schema.rb +605 -0
  170. data/lib/synthra/schema_inheritance.rb +104 -0
  171. data/lib/synthra/schema_versioning.rb +212 -0
  172. data/lib/synthra/snapshot_testing.rb +199 -0
  173. data/lib/synthra/time_travel.rb +338 -0
  174. data/lib/synthra/type_definitions.rb +274 -0
  175. data/lib/synthra/types/address_location/addresses.rb +125 -0
  176. data/lib/synthra/types/address_location/airports.rb +200 -0
  177. data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
  178. data/lib/synthra/types/address_location/locations.rb +406 -0
  179. data/lib/synthra/types/base.rb +48 -0
  180. data/lib/synthra/types/commerce_products/commerce.rb +119 -0
  181. data/lib/synthra/types/commerce_products/companies.rb +71 -0
  182. data/lib/synthra/types/commerce_products/construction.rb +126 -0
  183. data/lib/synthra/types/commerce_products/products.rb +205 -0
  184. data/lib/synthra/types/core/collections.rb +513 -0
  185. data/lib/synthra/types/core/defaults.rb +76 -0
  186. data/lib/synthra/types/core/enums.rb +102 -0
  187. data/lib/synthra/types/core/identifiers.rb +445 -0
  188. data/lib/synthra/types/core/primitives.rb +586 -0
  189. data/lib/synthra/types/core/references.rb +466 -0
  190. data/lib/synthra/types/core/sequences.rb +304 -0
  191. data/lib/synthra/types/crypto/crypto.rb +162 -0
  192. data/lib/synthra/types/date_time/dates.rb +387 -0
  193. data/lib/synthra/types/finance_banking/banking.rb +424 -0
  194. data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
  195. data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
  196. data/lib/synthra/types/formula.rb +184 -0
  197. data/lib/synthra/types/health_medical/medical.rb +150 -0
  198. data/lib/synthra/types/hostile_payloads.rb +122 -0
  199. data/lib/synthra/types/json_array.rb +51 -0
  200. data/lib/synthra/types/media_entertainment/media.rb +62 -0
  201. data/lib/synthra/types/naughty_string.rb +30 -0
  202. data/lib/synthra/types/personal_names/chinese.rb +41 -0
  203. data/lib/synthra/types/personal_names/identifiers.rb +187 -0
  204. data/lib/synthra/types/personal_names/names.rb +246 -0
  205. data/lib/synthra/types/personal_names/national_id.rb +89 -0
  206. data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
  207. data/lib/synthra/types/regex.rb +248 -0
  208. data/lib/synthra/types/registry.rb +182 -0
  209. data/lib/synthra/types/repeating_element.rb +50 -0
  210. data/lib/synthra/types/scenario.rb +29 -0
  211. data/lib/synthra/types/technology_internet/apps.rb +67 -0
  212. data/lib/synthra/types/technology_internet/communication.rb +162 -0
  213. data/lib/synthra/types/technology_internet/devices.rb +80 -0
  214. data/lib/synthra/types/technology_internet/formats.rb +139 -0
  215. data/lib/synthra/types/technology_internet/networking.rb +143 -0
  216. data/lib/synthra/types/template.rb +92 -0
  217. data/lib/synthra/types/text_content/business.rb +128 -0
  218. data/lib/synthra/types/text_content/colors.rb +70 -0
  219. data/lib/synthra/types/text_content/misc.rb +237 -0
  220. data/lib/synthra/types/text_content/security.rb +130 -0
  221. data/lib/synthra/types/text_content/text_generation.rb +532 -0
  222. data/lib/synthra/types/travel/travel.rb +150 -0
  223. data/lib/synthra/utils/string_distance.rb +133 -0
  224. data/lib/synthra/validator/dsl_validator.rb +339 -0
  225. data/lib/synthra/validator/path_validator.rb +615 -0
  226. data/lib/synthra/version.rb +35 -0
  227. data/lib/synthra/webhook_simulator.rb +341 -0
  228. data/lib/synthra.rb +259 -0
  229. data/schemas/address.dsl +16 -0
  230. data/schemas/api_response.dsl +8 -0
  231. data/schemas/error_payload.dsl +9 -0
  232. data/schemas/order.dsl +10 -0
  233. data/schemas/order_item.dsl +8 -0
  234. data/schemas/payment.dsl +11 -0
  235. data/schemas/social_post.dsl +13 -0
  236. data/schemas/user.dsl +10 -0
  237. data/scripts/batch_fix_all.rb +96 -0
  238. data/scripts/delete_old_files.rb +32 -0
  239. data/scripts/fix_all_grouped_files.rb +137 -0
  240. data/scripts/fix_all_indentation.rb +48 -0
  241. data/scripts/fix_all_syntax.rb +124 -0
  242. data/scripts/fix_grouped_files.rb +184 -0
  243. data/scripts/fix_syntax_errors.rb +108 -0
  244. data/scripts/group_domain_types.rb +112 -0
  245. data/scripts/group_domain_types_fixed.rb +150 -0
  246. data/scripts/merge_domains_to_one_file.rb +68 -0
  247. data/scripts/move_existing_types.rb +130 -0
  248. data/scripts/split_grouped_types.rb +142 -0
  249. data/tech_docs/README.md +134 -0
  250. data/tech_docs/advanced/streaming.md +405 -0
  251. data/tech_docs/advanced/thread_safety.md +253 -0
  252. data/tech_docs/api/overview.md +485 -0
  253. data/tech_docs/appendices/type_chart.md +193 -0
  254. data/tech_docs/basic_concepts.md +383 -0
  255. data/tech_docs/behaviors/overview.md +346 -0
  256. data/tech_docs/dsl/complex_types.md +790 -0
  257. data/tech_docs/dsl/core_types.md +464 -0
  258. data/tech_docs/dsl/datetime_types.md +325 -0
  259. data/tech_docs/dsl/field_modifiers.md +414 -0
  260. data/tech_docs/dsl/grammar.md +431 -0
  261. data/tech_docs/dsl/schema_definition.md +399 -0
  262. data/tech_docs/export/README.md +276 -0
  263. data/tech_docs/installation.md +273 -0
  264. data/tech_docs/integration/ci_cd.md +707 -0
  265. data/tech_docs/integration/ci_cd_guide.md +579 -0
  266. data/tech_docs/integration/factory_bot.md +485 -0
  267. data/tech_docs/integration/rails.md +630 -0
  268. data/tech_docs/integration/rspec.md +449 -0
  269. data/tech_docs/modes/overview.md +350 -0
  270. data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
  271. data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
  272. data/tech_docs/quick_start.md +256 -0
  273. data/tech_docs/templates/README.md +805 -0
  274. data/tech_docs/tutorials/advanced.md +371 -0
  275. data/tech_docs/tutorials/getting_started.md +189 -0
  276. data/tech_docs/tutorials/intermediate.md +231 -0
  277. data/tech_docs/tutorials/template_gallery.md +569 -0
  278. data/tech_example/01_basic_usage.rb +238 -0
  279. data/tech_example/02_types_demo.rb +336 -0
  280. data/tech_example/04_cli_usage.md +429 -0
  281. data/tech_example/05_database_seeding.rb +283 -0
  282. data/tech_example/07_rspec_integration.rb +359 -0
  283. data/tech_example/10_custom_types.rb +387 -0
  284. data/tech_example/12_behaviors.rb +372 -0
  285. data/tech_example/13_twitter_dm_example.rb +120 -0
  286. data/tech_example/14_exact_json_structure.rb +253 -0
  287. data/tech_example/16_lsp_server.rb +56 -0
  288. data/tech_example/17_property_testing.rb +92 -0
  289. data/tech_example/18_enhanced_repl.rb +120 -0
  290. data/tech_example/19_security_fuzzing.rb +145 -0
  291. data/tech_example/NEW_FEATURES.md +169 -0
  292. data/tech_example/README.md +118 -0
  293. data/tech_example/schemas/api_response.dsl +123 -0
  294. data/tech_example/schemas/ecommerce.dsl +121 -0
  295. data/tech_example/schemas/twitter_dm.dsl +109 -0
  296. data/tech_example/schemas/user.dsl +36 -0
  297. data/vscode-extension/README.md +246 -0
  298. data/vscode-extension/language-configuration.json +31 -0
  299. data/vscode-extension/package.json +55 -0
  300. data/vscode-extension/snippets/fakedatadsl.json +198 -0
  301. data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
  302. metadata +478 -0
@@ -0,0 +1,389 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Export
5
+ # Shared type mapping for all export formats
6
+ #
7
+ # This module provides consistent type mapping across all exporters,
8
+ # reducing code duplication and ensuring consistency.
9
+ #
10
+ # rubocop:disable Metrics/ModuleLength -- this is a set of large lookup tables, not logic.
11
+ module TypeMapping
12
+ # Map Synthra types to JSON Schema types
13
+ JSON_SCHEMA_TYPES = {
14
+ # Identifiers
15
+ "uuid" => { "type" => "string", "format" => "uuid" },
16
+ "ulid" => { "type" => "string", "pattern" => "^[0-9A-Z]{26}$" },
17
+ "id_sequence" => { "type" => "integer" },
18
+ "snowflake_id" => { "type" => "string" },
19
+ "numeric_string_id" => { "type" => "string", "pattern" => "^[0-9]+$" },
20
+
21
+ # Strings
22
+ "text" => { "type" => "string" },
23
+ "name" => { "type" => "string" },
24
+ "first_name" => { "type" => "string" },
25
+ "last_name" => { "type" => "string" },
26
+ "full_name" => { "type" => "string" },
27
+ "username" => { "type" => "string" },
28
+ "email" => { "type" => "string", "format" => "email" },
29
+ "phone" => { "type" => "string" },
30
+ "url" => { "type" => "string", "format" => "uri" },
31
+ "domain" => { "type" => "string", "format" => "hostname" },
32
+ "ip" => { "type" => "string", "format" => "ipv4" },
33
+ "ipv6" => { "type" => "string", "format" => "ipv6" },
34
+ "address" => { "type" => "string" },
35
+ "city" => { "type" => "string" },
36
+ "state" => { "type" => "string" },
37
+ "country" => { "type" => "string" },
38
+ "country_code" => { "type" => "string" },
39
+ "postal_code" => { "type" => "string" },
40
+ "password" => { "type" => "string" },
41
+ "color" => { "type" => "string" },
42
+ "hex_color" => { "type" => "string", "pattern" => "^#[0-9a-fA-F]{6}$" },
43
+ "user_agent" => { "type" => "string" },
44
+
45
+ # Numbers (integers)
46
+ "number" => { "type" => "integer" },
47
+ "integer" => { "type" => "integer" },
48
+ "age" => { "type" => "integer", "minimum" => 0, "maximum" => 150 },
49
+ "airport_elevation" => { "type" => "integer" },
50
+ "discount" => { "type" => "integer" },
51
+ "formula" => { "type" => "integer" },
52
+ "row_number" => { "type" => "integer" },
53
+ "sequence" => { "type" => "integer" },
54
+
55
+ # Numbers (floats)
56
+ "float" => { "type" => "number" },
57
+ "money" => { "type" => "number" },
58
+ "latitude" => { "type" => "number", "minimum" => -90, "maximum" => 90 },
59
+ "longitude" => { "type" => "number", "minimum" => -180, "maximum" => 180 },
60
+ "airport_latitude" => { "type" => "number", "minimum" => -90, "maximum" => 90 },
61
+ "airport_longitude" => { "type" => "number", "minimum" => -180, "maximum" => 180 },
62
+ "product_price" => { "type" => "number" },
63
+ "tax_rate" => { "type" => "number" },
64
+ "transaction_amount" => { "type" => "number" },
65
+
66
+ # Boolean
67
+ "boolean" => { "type" => "boolean" },
68
+
69
+ # Date
70
+ "date" => { "type" => "string", "format" => "date" },
71
+ "date_between" => { "type" => "string", "format" => "date" },
72
+ "past_date" => { "type" => "string", "format" => "date" },
73
+ "future_date" => { "type" => "string", "format" => "date" },
74
+ "mobile_device_release_date" => { "type" => "string", "format" => "date" },
75
+ "now" => { "type" => "string", "format" => "date" },
76
+
77
+ # Date/Time
78
+ "timestamp" => { "type" => "string", "format" => "date-time" },
79
+ "datetime" => { "type" => "string", "format" => "date-time" },
80
+ "time" => { "type" => "string", "format" => "time" },
81
+
82
+ # Structural
83
+ "object" => { "type" => "object" },
84
+ "map_by_field" => { "type" => "object" },
85
+ "array" => { "type" => "array" },
86
+ "empty_array" => { "type" => "array", "maxItems" => 0 },
87
+ "json_array" => { "type" => "array" },
88
+ "indices_pair" => { "type" => "array", "items" => { "type" => "integer" }, "minItems" => 2, "maxItems" => 2 }
89
+ }.freeze
90
+
91
+ # Map Synthra types to TypeScript types
92
+ TYPESCRIPT_TYPES = {
93
+ # Identifiers
94
+ "uuid" => "string",
95
+ "ulid" => "string",
96
+ "id_sequence" => "number",
97
+ "snowflake_id" => "string",
98
+ "numeric_string_id" => "string",
99
+
100
+ # Strings
101
+ "text" => "string",
102
+ "name" => "string",
103
+ "first_name" => "string",
104
+ "last_name" => "string",
105
+ "full_name" => "string",
106
+ "username" => "string",
107
+ "email" => "string",
108
+ "phone" => "string",
109
+ "url" => "string",
110
+ "domain" => "string",
111
+ "ip" => "string",
112
+ "ipv6" => "string",
113
+ "address" => "string",
114
+ "city" => "string",
115
+ "state" => "string",
116
+ "country" => "string",
117
+ "country_code" => "string",
118
+ "postal_code" => "string",
119
+ "password" => "string",
120
+ "color" => "string",
121
+ "hex_color" => "string",
122
+ "user_agent" => "string",
123
+ "mac_address" => "string",
124
+
125
+ # Numbers (integers + floats both map to TS number)
126
+ "number" => "number",
127
+ "integer" => "number",
128
+ "age" => "number",
129
+ "airport_elevation" => "number",
130
+ "discount" => "number",
131
+ "formula" => "number",
132
+ "row_number" => "number",
133
+ "sequence" => "number",
134
+ "float" => "number",
135
+ "money" => "number",
136
+ "latitude" => "number",
137
+ "longitude" => "number",
138
+ "airport_latitude" => "number",
139
+ "airport_longitude" => "number",
140
+ "product_price" => "number",
141
+ "tax_rate" => "number",
142
+ "transaction_amount" => "number",
143
+
144
+ # Boolean
145
+ "boolean" => "boolean",
146
+
147
+ # Date/Time (string in JSON)
148
+ "date" => "string",
149
+ "date_between" => "string",
150
+ "past_date" => "string",
151
+ "future_date" => "string",
152
+ "mobile_device_release_date" => "string",
153
+ "now" => "string",
154
+ "timestamp" => "string",
155
+ "datetime" => "string",
156
+ "time" => "string",
157
+
158
+ # Structural
159
+ "object" => "Record<string, unknown>",
160
+ "map_by_field" => "Record<string, unknown>",
161
+ "array" => "unknown[]",
162
+ "empty_array" => "never[]",
163
+ "json_array" => "unknown[]",
164
+ "indices_pair" => "[number, number]"
165
+ }.freeze
166
+
167
+ # Map Synthra types to Python types
168
+ PYTHON_TYPES = {
169
+ # Identifiers
170
+ "uuid" => "UUID",
171
+ "ulid" => "str",
172
+ "id_sequence" => "int",
173
+ "snowflake_id" => "str",
174
+ "numeric_string_id" => "str",
175
+
176
+ # Strings
177
+ "text" => "str",
178
+ "name" => "str",
179
+ "first_name" => "str",
180
+ "last_name" => "str",
181
+ "full_name" => "str",
182
+ "username" => "str",
183
+ "email" => "EmailStr",
184
+ "phone" => "str",
185
+ "url" => "HttpUrl",
186
+ "domain" => "str",
187
+ "ip" => "IPv4Address",
188
+ "ipv6" => "IPv6Address",
189
+ "address" => "str",
190
+ "city" => "str",
191
+ "state" => "str",
192
+ "country" => "str",
193
+ "country_code" => "str",
194
+ "postal_code" => "str",
195
+ "password" => "str",
196
+ "color" => "str",
197
+ "hex_color" => "str",
198
+ "user_agent" => "str",
199
+ "mac_address" => "str",
200
+
201
+ # Numbers (integers)
202
+ "number" => "int",
203
+ "integer" => "int",
204
+ "age" => "int",
205
+ "airport_elevation" => "int",
206
+ "discount" => "int",
207
+ "formula" => "int",
208
+ "row_number" => "int",
209
+ "sequence" => "int",
210
+
211
+ # Numbers (floats)
212
+ "float" => "float",
213
+ "money" => "Decimal",
214
+ "latitude" => "float",
215
+ "longitude" => "float",
216
+ "airport_latitude" => "float",
217
+ "airport_longitude" => "float",
218
+ "product_price" => "float",
219
+ "tax_rate" => "float",
220
+ "transaction_amount" => "float",
221
+
222
+ # Boolean
223
+ "boolean" => "bool",
224
+
225
+ # Date
226
+ "date" => "date",
227
+ "date_between" => "date",
228
+ "past_date" => "date",
229
+ "future_date" => "date",
230
+ "mobile_device_release_date" => "date",
231
+ "now" => "date",
232
+
233
+ # Date/Time
234
+ "timestamp" => "datetime",
235
+ "datetime" => "datetime",
236
+ "time" => "time",
237
+
238
+ # Structural
239
+ "object" => "Dict[str, Any]",
240
+ "map_by_field" => "Dict[str, Any]",
241
+ "array" => "List[Any]",
242
+ "empty_array" => "List[Any]",
243
+ "json_array" => "List[Any]",
244
+ "indices_pair" => "Tuple[int, int]"
245
+ }.freeze
246
+
247
+ # Map Synthra types to SQL types
248
+ SQL_TYPES = {
249
+ postgresql: {
250
+ "uuid" => "UUID",
251
+ "ulid" => "VARCHAR(26)",
252
+ "id_sequence" => "SERIAL",
253
+ "snowflake_id" => "BIGINT",
254
+ "numeric_string_id" => "VARCHAR(20)",
255
+ "text" => "TEXT",
256
+ "name" => "VARCHAR(255)",
257
+ "first_name" => "VARCHAR(100)",
258
+ "last_name" => "VARCHAR(100)",
259
+ "full_name" => "VARCHAR(255)",
260
+ "username" => "VARCHAR(100)",
261
+ "email" => "VARCHAR(255)",
262
+ "phone" => "VARCHAR(20)",
263
+ "url" => "TEXT",
264
+ "domain" => "VARCHAR(255)",
265
+ "ip" => "INET",
266
+ "ipv6" => "INET",
267
+ "address" => "TEXT",
268
+ "city" => "VARCHAR(100)",
269
+ "state" => "VARCHAR(100)",
270
+ "country" => "VARCHAR(100)",
271
+ "country_code" => "CHAR(2)",
272
+ "postal_code" => "VARCHAR(20)",
273
+ "password" => "VARCHAR(255)",
274
+ "color" => "VARCHAR(50)",
275
+ "hex_color" => "CHAR(7)",
276
+ "number" => "INTEGER",
277
+ "integer" => "INTEGER",
278
+ "age" => "INTEGER",
279
+ "airport_elevation" => "INTEGER",
280
+ "discount" => "INTEGER",
281
+ "formula" => "INTEGER",
282
+ "row_number" => "INTEGER",
283
+ "sequence" => "INTEGER",
284
+ "float" => "REAL",
285
+ "money" => "DECIMAL(10, 2)",
286
+ "latitude" => "DECIMAL(10, 8)",
287
+ "longitude" => "DECIMAL(11, 8)",
288
+ "airport_latitude" => "DECIMAL(10, 8)",
289
+ "airport_longitude" => "DECIMAL(11, 8)",
290
+ "product_price" => "DECIMAL(10, 2)",
291
+ "tax_rate" => "DECIMAL(10, 4)",
292
+ "transaction_amount" => "DECIMAL(15, 2)",
293
+ "boolean" => "BOOLEAN",
294
+ "date" => "DATE",
295
+ "date_between" => "DATE",
296
+ "past_date" => "DATE",
297
+ "future_date" => "DATE",
298
+ "mobile_device_release_date" => "DATE",
299
+ "now" => "DATE",
300
+ "timestamp" => "TIMESTAMP",
301
+ "datetime" => "TIMESTAMP",
302
+ "time" => "TIME",
303
+ "object" => "JSONB",
304
+ "map_by_field" => "JSONB",
305
+ "array" => "JSONB",
306
+ "empty_array" => "JSONB",
307
+ "json_array" => "JSONB",
308
+ "indices_pair" => "INTEGER[]"
309
+ },
310
+ mysql: {
311
+ "uuid" => "CHAR(36)",
312
+ "ulid" => "CHAR(26)",
313
+ "id_sequence" => "INT AUTO_INCREMENT",
314
+ "snowflake_id" => "BIGINT",
315
+ "numeric_string_id" => "VARCHAR(20)",
316
+ "text" => "TEXT",
317
+ "name" => "VARCHAR(255)",
318
+ "email" => "VARCHAR(255)",
319
+ "number" => "INT",
320
+ "integer" => "INT",
321
+ "float" => "FLOAT",
322
+ "money" => "DECIMAL(10, 2)",
323
+ "boolean" => "TINYINT(1)",
324
+ "date" => "DATE",
325
+ "timestamp" => "DATETIME",
326
+ "datetime" => "DATETIME"
327
+ },
328
+ sqlite: {
329
+ "uuid" => "TEXT",
330
+ "ulid" => "TEXT",
331
+ "id_sequence" => "INTEGER PRIMARY KEY AUTOINCREMENT",
332
+ "text" => "TEXT",
333
+ "name" => "TEXT",
334
+ "email" => "TEXT",
335
+ "number" => "INTEGER",
336
+ "integer" => "INTEGER",
337
+ "float" => "REAL",
338
+ "money" => "REAL",
339
+ "boolean" => "INTEGER",
340
+ "date" => "TEXT",
341
+ "timestamp" => "TEXT"
342
+ }
343
+ }.freeze
344
+
345
+ # Get JSON Schema type for a Synthra type
346
+ #
347
+ # @param type_name [String] the type name
348
+ # @return [Hash] JSON Schema type definition
349
+ #
350
+ def json_schema_type(type_name)
351
+ JSON_SCHEMA_TYPES[type_name] || { "type" => "string" }
352
+ end
353
+
354
+ # Get TypeScript type for a Synthra type
355
+ #
356
+ # @param type_name [String] the type name
357
+ # @return [String] TypeScript type
358
+ #
359
+ def typescript_type(type_name)
360
+ # Default to the language's string type: the overwhelming majority of
361
+ # Synthra types generate strings, so an unmapped type is a string.
362
+ TYPESCRIPT_TYPES[type_name] || "string"
363
+ end
364
+
365
+ # Get Python type for a Synthra type
366
+ #
367
+ # @param type_name [String] the type name
368
+ # @return [String] Python type
369
+ #
370
+ def python_type(type_name)
371
+ # Default to the language's string type: the overwhelming majority of
372
+ # Synthra types generate strings, so an unmapped type is a string.
373
+ PYTHON_TYPES[type_name] || "str"
374
+ end
375
+
376
+ # Get SQL type for a Synthra type
377
+ #
378
+ # @param type_name [String] the type name
379
+ # @param dialect [Symbol] SQL dialect (:postgresql, :mysql, :sqlite)
380
+ # @return [String] SQL type
381
+ #
382
+ def sql_type(type_name, dialect: :postgresql)
383
+ dialect_types = SQL_TYPES[dialect] || SQL_TYPES[:postgresql]
384
+ dialect_types[type_name] || "VARCHAR(255)"
385
+ end
386
+ end
387
+ # rubocop:enable Metrics/ModuleLength
388
+ end
389
+ end
@@ -0,0 +1,300 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :nocov:
4
+ module Synthra
5
+ module Export
6
+ # Exports schema as TypeScript interfaces
7
+ #
8
+ # @example Export a schema
9
+ # exporter = Typescript.new(schema, registry: registry)
10
+ # typescript = exporter.export
11
+ #
12
+ class Typescript < Base
13
+ # Type mappings from DSL types to TypeScript types
14
+ TYPE_MAPPINGS = {
15
+ # Strings
16
+ "uuid" => "string",
17
+ "ulid" => "string",
18
+ "email" => "string",
19
+ "url" => "string",
20
+ "ip" => "string",
21
+ "ipv6" => "string",
22
+ "date" => "string",
23
+ "past_date" => "string",
24
+ "future_date" => "string",
25
+ "timestamp" => "string",
26
+ "text" => "string",
27
+ "name" => "string",
28
+ "full_name" => "string",
29
+ "first_name" => "string",
30
+ "last_name" => "string",
31
+ "phone" => "string",
32
+ "city" => "string",
33
+ "country" => "string",
34
+ "country_code" => "string",
35
+ "postal_code" => "string",
36
+ "state" => "string",
37
+ "street" => "string",
38
+ "address" => "string",
39
+ "username" => "string",
40
+ "social_handle" => "string",
41
+ "domain" => "string",
42
+ "user_agent" => "string",
43
+ "mac_address" => "string",
44
+ "iban" => "string",
45
+ "credit_card" => "string",
46
+ "currency" => "string",
47
+ "currency_code" => "string",
48
+ "paragraph" => "string",
49
+ "sentence" => "string",
50
+ "word" => "string",
51
+ "message_text" => "string",
52
+ "hashtag_text" => "string",
53
+ "snowflake_id" => "string",
54
+ "numeric_string_id" => "string",
55
+
56
+ # Numbers (integers + floats both map to TS number)
57
+ "number" => "number",
58
+ "integer" => "number",
59
+ "age" => "number",
60
+ "airport_elevation" => "number",
61
+ "discount" => "number",
62
+ "formula" => "number",
63
+ "row_number" => "number",
64
+ "sequence" => "number",
65
+ "id_sequence" => "number",
66
+ "float" => "number",
67
+ "money" => "number",
68
+ "latitude" => "number",
69
+ "longitude" => "number",
70
+ "airport_latitude" => "number",
71
+ "airport_longitude" => "number",
72
+ "product_price" => "number",
73
+ "tax_rate" => "number",
74
+ "transaction_amount" => "number",
75
+
76
+ # Boolean
77
+ "boolean" => "boolean",
78
+
79
+ # Date (string in JSON)
80
+ "date_between" => "string",
81
+ "mobile_device_release_date" => "string",
82
+ "now" => "string",
83
+ "datetime" => "string",
84
+
85
+ # Objects
86
+ "object" => "Record<string, unknown>",
87
+ "map_by_field" => "Record<string, unknown>",
88
+
89
+ # Arrays
90
+ "array" => "unknown[]",
91
+ "empty_array" => "never[]",
92
+ "json_array" => "unknown[]",
93
+ "indices_pair" => "[number, number]"
94
+ }.freeze
95
+
96
+ def export
97
+ lines = []
98
+
99
+ # Add header comment
100
+ lines << "// Generated from Synthra schema: #{schema.name}"
101
+ lines << "// Version: #{schema.version}" if schema.version
102
+ lines << "// DEPRECATED: #{schema.deprecation_message}" if schema.deprecated?
103
+ lines << ""
104
+
105
+ # Export referenced schemas first
106
+ exported = Set.new
107
+ collect_referenced_schemas.each do |ref_schema|
108
+ lines << build_interface(ref_schema)
109
+ lines << ""
110
+ exported << ref_schema.name
111
+ end
112
+
113
+ # Export main schema
114
+ lines << build_interface(schema) unless exported.include?(schema.name)
115
+
116
+ lines.join("\n")
117
+ end
118
+
119
+ # Export all schemas in a registry
120
+ #
121
+ # @param registry [Registry] the registry
122
+ # @param options [Hash] export options
123
+ # @return [String] TypeScript interfaces
124
+ #
125
+ def self.export_all(registry, **options)
126
+ lines = []
127
+ lines << "// Generated from Synthra schemas"
128
+ lines << "// Schemas: #{registry.names.join(', ')}"
129
+ lines << ""
130
+
131
+ registry.schemas.each do |name, schema|
132
+ exporter = new(schema, registry: registry, **options)
133
+ lines << exporter.build_interface(schema)
134
+ lines << ""
135
+ end
136
+
137
+ lines.join("\n")
138
+ end
139
+
140
+ def build_interface(target_schema)
141
+ lines = []
142
+
143
+ if target_schema.deprecated?
144
+ lines << "/** @deprecated #{target_schema.deprecation_message || 'This interface is deprecated'} */"
145
+ end
146
+
147
+ lines << "interface #{target_schema.name} {"
148
+
149
+ target_schema.fields.each do |field|
150
+ ts_type = field_to_typescript(field)
151
+ optional = field.optional? ? "?" : ""
152
+ nullable = field.nullable? ? " | null" : ""
153
+
154
+ # Add JSDoc for complex fields
155
+ comment = build_field_comment(field)
156
+ lines << " #{comment}" if comment
157
+
158
+ lines << " #{field.name}#{optional}: #{ts_type}#{nullable};"
159
+ end
160
+
161
+ lines << "}"
162
+ lines.join("\n")
163
+ end
164
+
165
+ private
166
+
167
+ def collect_referenced_schemas
168
+ schemas = []
169
+ schema.fields.each do |field|
170
+ collect_field_schemas(field, schemas)
171
+ end
172
+ schemas.uniq(&:name)
173
+ end
174
+
175
+ def collect_field_schemas(field, schemas)
176
+ type_name = field.type_name
177
+
178
+ if schema_reference?(type_name)
179
+ ref_schema = get_schema(type_name)
180
+ if ref_schema && !schemas.any? { |s| s.name == ref_schema.name }
181
+ schemas << ref_schema
182
+ # Recursively collect nested schemas
183
+ ref_schema.fields.each { |f| collect_field_schemas(f, schemas) }
184
+ end
185
+ end
186
+
187
+ if type_name == "array"
188
+ element = field.type_args[:element]
189
+ if element && schema_reference?(element.to_s)
190
+ ref_schema = get_schema(element.to_s)
191
+ if ref_schema && !schemas.any? { |s| s.name == ref_schema.name }
192
+ schemas << ref_schema
193
+ ref_schema.fields.each { |f| collect_field_schemas(f, schemas) }
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ def field_to_typescript(field)
200
+ type_name = field.type_name
201
+ type_args = field.type_args
202
+
203
+ case type_name
204
+ when "enum"
205
+ build_enum_type(type_args)
206
+ when "const"
207
+ build_const_type(type_args)
208
+ when "array"
209
+ build_array_type(type_args)
210
+ when "one_of"
211
+ build_one_of_type(type_args)
212
+ when "map_by_field"
213
+ build_map_by_field_type(type_args)
214
+ else
215
+ if TYPE_MAPPINGS[type_name]
216
+ TYPE_MAPPINGS[type_name]
217
+ elsif schema_reference?(type_name)
218
+ type_name
219
+ else
220
+ # Most Synthra types generate strings, so an unmapped, non-structural
221
+ # type is a string rather than unknown.
222
+ "string"
223
+ end
224
+ end
225
+ end
226
+
227
+ def build_enum_type(args)
228
+ values = args[:values]&.map { |v| v.respond_to?(:value) ? v.value : v } || []
229
+ values.map { |v| "\"#{v}\"" }.join(" | ")
230
+ end
231
+
232
+ def build_const_type(args)
233
+ value = args[:value]
234
+ case value
235
+ when String
236
+ "\"#{value}\""
237
+ when TrueClass, FalseClass
238
+ value.to_s
239
+ when Numeric
240
+ value.to_s
241
+ else
242
+ "\"#{value}\""
243
+ end
244
+ end
245
+
246
+ def build_array_type(args)
247
+ element = args[:element]
248
+ if element
249
+ if schema_reference?(element.to_s)
250
+ "#{element}[]"
251
+ elsif TYPE_MAPPINGS[element.to_s]
252
+ "#{TYPE_MAPPINGS[element.to_s]}[]"
253
+ else
254
+ # Unknown element types are string-producing types (e.g. array(string)).
255
+ "string[]"
256
+ end
257
+ else
258
+ "unknown[]"
259
+ end
260
+ end
261
+
262
+ def build_one_of_type(args)
263
+ schemas = args[:schemas] || []
264
+ types = schemas.map do |s|
265
+ schema_name = s.is_a?(Hash) ? (s[:schema] || s["schema"]) : s.to_s
266
+ schema_reference?(schema_name) ? schema_name : "unknown"
267
+ end
268
+ types.join(" | ")
269
+ end
270
+
271
+ def build_map_by_field_type(args)
272
+ entries = args[:entries] || {}
273
+ if entries.empty?
274
+ "Record<string, unknown>"
275
+ else
276
+ # Create a more specific type for known entries
277
+ entry_types = entries.map do |name, info|
278
+ schema_name = info.is_a?(Hash) ? (info[:schema] || info["schema"]) : info.to_s
279
+ type = schema_reference?(schema_name) ? schema_name : "unknown"
280
+ "#{name}: #{type}"
281
+ end
282
+ "{ [key: string]: { #{entry_types.join('; ')} }[typeof key] }"
283
+ end
284
+ end
285
+
286
+ def build_field_comment(field)
287
+ return nil unless options[:comments]
288
+
289
+ comments = []
290
+ comments << "@type #{field.type_name}" if field.type_name
291
+ comments << "@optional" if field.optional?
292
+ comments << "@nullable" if field.nullable?
293
+
294
+ return nil if comments.empty?
295
+ "/** #{comments.join(' ')} */"
296
+ end
297
+ end
298
+ # :nocov:
299
+ end
300
+ end