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,607 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+ # Synthra Enhanced REPL with History, Aliases, Export, and Tab Completion
5
+ # =============================================================================
6
+ #
7
+ # Provides enhanced REPL features:
8
+ # - Command aliases (t: table, d: debug, i: inspect)
9
+ # - Readline-based command history with arrow key navigation
10
+ # - Tab completion for schema names and commands
11
+ # - Export to file (gen User 100 > users.json)
12
+ # - Improved error messages with "did you mean" suggestions
13
+ #
14
+ # Note: Most methods in this file are interactive and marked with :nocov:
15
+ # as they require stdin/stdout interaction that's difficult to test.
16
+ #
17
+ # =============================================================================
18
+
19
+ require_relative "formatter"
20
+
21
+ module Synthra
22
+ module REPL
23
+ # Command aliases for the REPL
24
+ # Maps short aliases to full command names
25
+ COMMAND_ALIASES = {
26
+ "t" => "table",
27
+ "d" => "debug",
28
+ "i" => "inspect",
29
+ "g" => "gen",
30
+ "l" => "list",
31
+ "h" => "help",
32
+ "q" => "quit"
33
+ }.freeze
34
+
35
+ # All available REPL commands
36
+ COMMANDS = %w[
37
+ load list info gen table debug inspect seed mode help quit exit
38
+ ].freeze
39
+
40
+ # Enhanced REPL with history, completion, and export
41
+ class EnhancedREPL
42
+ # @return [Registry] the schema registry
43
+ attr_reader :registry
44
+
45
+ # @return [Integer, nil] the current seed for deterministic generation
46
+ attr_reader :current_seed
47
+
48
+ # @return [Symbol] the current generation mode
49
+ attr_reader :current_mode
50
+
51
+ # @return [Array<String>] command history
52
+ attr_reader :history
53
+
54
+ # Initialize the Enhanced REPL
55
+ #
56
+ # @param registry [Registry] optional pre-loaded registry
57
+ # @param verbose [Boolean] enable verbose output
58
+ #
59
+ def initialize(registry: nil, verbose: false)
60
+ @registry = registry || Registry.new
61
+ @current_seed = nil
62
+ @current_mode = :random
63
+ @verbose = verbose
64
+ @history = []
65
+ @running = false
66
+ setup_readline
67
+ end
68
+
69
+ # Check if readline is available
70
+ #
71
+ # @return [Boolean] true if readline is available
72
+ #
73
+ def readline_available?
74
+ @readline_available
75
+ end
76
+
77
+ # Run the REPL
78
+ #
79
+ # @return [Integer] exit code
80
+ #
81
+ # :nocov:
82
+ def run
83
+ @running = true
84
+ print_welcome
85
+
86
+ while @running
87
+ input = read_input
88
+ break if input.nil?
89
+
90
+ input = input.strip
91
+ next if input.empty?
92
+
93
+ @history << input
94
+ process_command(input)
95
+ end
96
+
97
+ CLI::EXIT_SUCCESS
98
+ end
99
+ # :nocov:
100
+
101
+ # Process a single command
102
+ #
103
+ # @param input [String] the command input
104
+ # @return [void]
105
+ #
106
+ def process_command(input)
107
+ # Check for export redirect syntax: command > filename
108
+ if input.include?(" > ")
109
+ command_part, filename = input.split(" > ", 2)
110
+ execute_with_export(command_part.strip, filename.strip)
111
+ else
112
+ execute_command(input)
113
+ end
114
+ rescue => e
115
+ puts "āŒ Error: #{e.message}"
116
+ puts e.backtrace.first(3).join("\n") if @verbose
117
+ end
118
+
119
+ # Stop the REPL
120
+ #
121
+ # @return [void]
122
+ #
123
+ def stop
124
+ @running = false
125
+ end
126
+
127
+ private
128
+
129
+ # Set up readline if available
130
+ #
131
+ # @return [void]
132
+ #
133
+ def setup_readline
134
+ begin
135
+ require "readline"
136
+ @readline_available = true
137
+ setup_completion
138
+ rescue LoadError
139
+ @readline_available = false
140
+ end
141
+ end
142
+
143
+ # Set up tab completion
144
+ #
145
+ # @return [void]
146
+ #
147
+ def setup_completion
148
+ return unless @readline_available
149
+
150
+ Readline.completion_proc = proc do |input|
151
+ completions_for(input)
152
+ end
153
+ Readline.completion_append_character = " "
154
+ end
155
+
156
+ # Get completions for the current input
157
+ #
158
+ # @param input [String] partial input
159
+ # @return [Array<String>] matching completions
160
+ #
161
+ def completions_for(input)
162
+ # Get the line buffer to understand context
163
+ line = Readline.line_buffer rescue input
164
+
165
+ if line.nil? || line.strip.empty? || !line.include?(" ")
166
+ # Complete commands
167
+ all_commands = COMMANDS + COMMAND_ALIASES.keys
168
+ all_commands.select { |c| c.start_with?(input.downcase) }
169
+ else
170
+ # Complete schema names for commands that take them
171
+ command = line.split.first&.downcase
172
+ if %w[gen table debug inspect info g t d i].include?(command)
173
+ @registry.names.select { |name| name.downcase.start_with?(input.downcase) }
174
+ else
175
+ []
176
+ end
177
+ end
178
+ end
179
+
180
+ # Read input from user
181
+ #
182
+ # @return [String, nil] user input or nil on EOF
183
+ #
184
+ # :nocov:
185
+ def read_input
186
+ if @readline_available
187
+ Readline.readline("synthra> ", true)
188
+ else
189
+ print "synthra> "
190
+ $stdin.gets
191
+ end
192
+ end
193
+ # :nocov:
194
+
195
+ # Print welcome message
196
+ #
197
+ # @return [void]
198
+ #
199
+ # :nocov:
200
+ def print_welcome
201
+ puts "=" * 60
202
+ puts "Synthra Interactive REPL (Enhanced)"
203
+ puts "=" * 60
204
+ puts
205
+ puts "Commands (aliases in parentheses):"
206
+ puts " load <path> - Load schemas from file/directory"
207
+ puts " list (l) - List loaded schemas"
208
+ puts " info <Schema> - Show schema details"
209
+ puts " gen <Schema> [N] (g) - Generate records (JSON)"
210
+ puts " table <Schema> [N] (t)- Generate records (table view)"
211
+ puts " debug <Schema> (d) - Step-through debug generation"
212
+ puts " inspect <Schema> (i) - Generate and show detailed inspection"
213
+ puts " seed <number> - Set seed for deterministic output"
214
+ puts " mode <mode> - Set mode (random/edge/invalid/hostile/mixed)"
215
+ puts " help (h) - Show this help"
216
+ puts " quit/exit (q) - Exit REPL"
217
+ puts
218
+ puts "Export to file:"
219
+ puts " gen User 100 > users.json - Export to JSON file"
220
+ puts " table User 10 > users.txt - Export table to text file"
221
+ puts
222
+ if @readline_available
223
+ puts "Tab completion and command history enabled!"
224
+ else
225
+ puts "Install 'readline' gem for history and tab completion"
226
+ end
227
+ puts
228
+ end
229
+ # :nocov:
230
+
231
+ # Execute command with export to file
232
+ #
233
+ # @param command [String] the command to execute
234
+ # @param filename [String] the file to export to
235
+ # @return [void]
236
+ #
237
+ def execute_with_export(command, filename)
238
+ # Capture output
239
+ output = capture_output { execute_command(command) }
240
+
241
+ if output && !output.strip.empty?
242
+ File.write(filename, output)
243
+ puts "āœ… Exported to #{filename} (#{output.bytesize} bytes)"
244
+ else
245
+ puts "āŒ No output to export"
246
+ end
247
+ end
248
+
249
+ # Capture stdout output from a block
250
+ #
251
+ # @yield block to execute
252
+ # @return [String] captured output
253
+ #
254
+ def capture_output
255
+ original_stdout = $stdout
256
+ $stdout = StringIO.new
257
+ yield
258
+ $stdout.string
259
+ ensure
260
+ $stdout = original_stdout
261
+ end
262
+
263
+ # Execute a single command
264
+ #
265
+ # @param input [String] command input
266
+ # @return [void]
267
+ #
268
+ def execute_command(input)
269
+ return if input.nil? || input.strip.empty?
270
+
271
+ # Expand aliases
272
+ parts = input.split(/\s+/, 2)
273
+ command = parts[0]&.downcase
274
+ rest = parts[1]
275
+
276
+ return if command.nil? || command.empty?
277
+
278
+ # Check for alias
279
+ if COMMAND_ALIASES.key?(command)
280
+ command = COMMAND_ALIASES[command]
281
+ end
282
+
283
+ # Reconstruct full input
284
+ full_input = rest ? "#{command} #{rest}" : command
285
+
286
+ case full_input
287
+ when /^load\s+(.+)$/
288
+ cmd_load($1.strip)
289
+ when "list", "l"
290
+ cmd_list
291
+ when /^info\s+(\w+)$/
292
+ cmd_info($1)
293
+ when /^gen\s+(\w+)(?:\s+(\d+))?$/
294
+ cmd_gen($1, ($2 || "1").to_i)
295
+ when /^table\s+(\w+)(?:\s+(\d+))?$/
296
+ cmd_table($1, ($2 || "1").to_i)
297
+ when /^debug\s+(\w+)$/
298
+ cmd_debug($1)
299
+ when /^inspect\s+(\w+)$/
300
+ cmd_inspect($1)
301
+ when /^seed\s+(\d+)$/
302
+ cmd_seed($1.to_i)
303
+ when /^mode\s+(random|edge|invalid|hostile|mixed)$/
304
+ cmd_mode($1.to_sym)
305
+ when "help", "h"
306
+ cmd_help
307
+ when "quit", "exit", "q"
308
+ cmd_quit
309
+ else
310
+ suggest_command(command)
311
+ end
312
+ end
313
+
314
+ # Suggest similar command for typos
315
+ #
316
+ # @param unknown [String] unknown command
317
+ # @return [void]
318
+ #
319
+ def suggest_command(unknown)
320
+ all_commands = COMMANDS + COMMAND_ALIASES.keys
321
+ suggestions = Utils::StringDistance.similar_strings(unknown, all_commands, max_distance: 2)
322
+
323
+ if suggestions.any?
324
+ puts "Unknown command: #{unknown}"
325
+ puts Utils::StringDistance.format_suggestions(suggestions)
326
+ else
327
+ puts "Unknown command: #{unknown}. Type 'help' for available commands."
328
+ end
329
+ end
330
+
331
+ # Load schemas from path
332
+ #
333
+ # @param path [String] file or directory path
334
+ # @return [void]
335
+ #
336
+ def cmd_load(path)
337
+ if File.directory?(path)
338
+ Dir.glob(File.join(path, "*.dsl")).each do |f|
339
+ @registry.load_file(f, validate: false)
340
+ end
341
+ else
342
+ @registry.load_file(path, validate: false)
343
+ end
344
+ puts "āœ… Loaded #{@registry.size} schema(s)"
345
+ rescue => e
346
+ puts "āŒ Error: #{e.message}"
347
+ end
348
+
349
+ # List loaded schemas
350
+ #
351
+ # @return [void]
352
+ #
353
+ def cmd_list
354
+ if @registry.size == 0
355
+ puts "No schemas loaded. Use 'load <path>' first."
356
+ else
357
+ puts "Loaded schemas:"
358
+ @registry.schemas.each do |name, schema|
359
+ version = schema.version ? " v#{schema.version}" : ""
360
+ deprecated = schema.deprecated? ? " [DEPRECATED]" : ""
361
+ puts " - #{name}#{version}#{deprecated}"
362
+ end
363
+ end
364
+ end
365
+
366
+ # Show schema info
367
+ #
368
+ # @param name [String] schema name
369
+ # @return [void]
370
+ #
371
+ def cmd_info(name)
372
+ schema = find_schema(name)
373
+ return unless schema
374
+
375
+ puts "Schema: #{name}"
376
+ puts " Version: #{schema.version || 'not specified'}"
377
+ puts " Fields: #{schema.fields.length}"
378
+ schema.fields.each do |f|
379
+ optional = f.optional? ? "?" : ""
380
+ nullable = f.nullable? ? " (nullable)" : ""
381
+ puts " - #{f.name}#{optional}: #{f.type_name}#{nullable}"
382
+ end
383
+ end
384
+
385
+ # Generate records
386
+ #
387
+ # @param name [String] schema name
388
+ # @param count [Integer] number of records
389
+ # @return [void]
390
+ #
391
+ def cmd_gen(name, count)
392
+ schema = find_schema(name)
393
+ return unless schema
394
+
395
+ opts = build_generation_opts
396
+
397
+ if count == 1
398
+ record = schema.generate(**opts)
399
+ puts JSON.pretty_generate(record)
400
+ else
401
+ records = schema.generate_many(count, **opts)
402
+ puts JSON.pretty_generate(records)
403
+ end
404
+ end
405
+
406
+ # Generate table output
407
+ #
408
+ # @param name [String] schema name
409
+ # @param count [Integer] number of records
410
+ # @return [void]
411
+ #
412
+ def cmd_table(name, count)
413
+ schema = find_schema(name)
414
+ return unless schema
415
+
416
+ opts = build_generation_opts
417
+
418
+ if count == 1
419
+ record = schema.generate(**opts)
420
+ puts Formatter.table(record)
421
+ else
422
+ records = schema.generate_many(count, **opts)
423
+ puts Formatter.table(records)
424
+ end
425
+ end
426
+
427
+ # Debug generation
428
+ #
429
+ # @param name [String] schema name
430
+ # @return [void]
431
+ #
432
+ # :nocov:
433
+ def cmd_debug(name)
434
+ schema = find_schema(name)
435
+ return unless schema
436
+
437
+ puts "šŸ› Debug Mode: Generating #{schema.name}"
438
+ puts "=" * 60
439
+ puts "Press Enter to continue after each field, 'q' to quit, 'c' to continue without pausing"
440
+ puts
441
+
442
+ field_index = 0
443
+ generated_fields = {}
444
+ auto_continue = false
445
+
446
+ original_callback = Synthra.configuration.on_field_generated
447
+
448
+ Synthra.configuration.on_field_generated = lambda do |schema_name, field_name, value, duration|
449
+ field_index += 1
450
+ generated_fields[field_name] = value
451
+
452
+ puts "\n[Field #{field_index}] #{field_name}"
453
+ puts "─" * 60
454
+ puts "Value: #{Formatter.format_value(value, 80)}"
455
+ puts "Duration: #{duration}ms" if duration
456
+ puts "Schema: #{schema_name}"
457
+
458
+ # Show context state - all previously generated fields
459
+ if generated_fields.size > 1
460
+ puts "\nšŸ“‹ Context (#{generated_fields.size - 1} prior fields):"
461
+ generated_fields.each do |k, v|
462
+ next if k == field_name
463
+ puts " #{k.to_s.ljust(20)} = #{Formatter.format_value(v, 50)}"
464
+ end
465
+ end
466
+ puts
467
+
468
+ return if auto_continue
469
+
470
+ loop do
471
+ print "Press Enter to continue, 'q' to quit, 'c' to continue without pausing: "
472
+ response = $stdin.gets&.strip&.downcase
473
+
474
+ case response
475
+ when "q", "quit"
476
+ Synthra.configuration.on_field_generated = original_callback
477
+ puts "Debug cancelled."
478
+ throw :debug_cancelled
479
+ when "c", "continue"
480
+ auto_continue = true
481
+ puts "Continuing without pausing..."
482
+ break
483
+ when "", nil
484
+ break
485
+ else
486
+ puts "Invalid input. Press Enter, 'q', or 'c'."
487
+ end
488
+ end
489
+ end
490
+
491
+ begin
492
+ catch(:debug_cancelled) do
493
+ opts = build_generation_opts
494
+ result = schema.generate(**opts)
495
+
496
+ puts
497
+ puts "=" * 60
498
+ puts "āœ… Generation Complete!"
499
+ puts "=" * 60
500
+ puts
501
+ puts "Final Record:"
502
+ puts Formatter.table(result)
503
+ end
504
+ ensure
505
+ Synthra.configuration.on_field_generated = original_callback
506
+ end
507
+ end
508
+ # :nocov:
509
+
510
+ # Inspect generation
511
+ #
512
+ # @param name [String] schema name
513
+ # @return [void]
514
+ #
515
+ def cmd_inspect(name)
516
+ schema = find_schema(name)
517
+ return unless schema
518
+
519
+ opts = build_generation_opts
520
+ record = schema.generate(**opts)
521
+
522
+ puts "šŸ“Š Generated Record:"
523
+ puts "=" * 60
524
+ puts Formatter.table(record)
525
+ puts
526
+ puts "šŸ“‹ JSON Format:"
527
+ puts JSON.pretty_generate(record)
528
+ puts
529
+ puts "šŸ“ Statistics:"
530
+ puts " Fields: #{record.keys.length}"
531
+ puts " Total size: #{JSON.generate(record).bytesize} bytes"
532
+ record.each do |key, value|
533
+ type = value.class.name
534
+ size = case value
535
+ when String then "#{value.bytesize} bytes"
536
+ when Hash then "#{value.keys.length} keys"
537
+ when Array then "#{value.length} items"
538
+ else value.inspect.length.to_s + " chars"
539
+ end
540
+ puts " #{key}: #{type} (#{size})"
541
+ end
542
+ end
543
+
544
+ # Set seed
545
+ #
546
+ # @param seed [Integer] seed value
547
+ # @return [void]
548
+ #
549
+ def cmd_seed(seed)
550
+ @current_seed = seed
551
+ puts "āœ… Seed set to #{@current_seed}"
552
+ end
553
+
554
+ # Set mode
555
+ #
556
+ # @param mode [Symbol] generation mode
557
+ # @return [void]
558
+ #
559
+ def cmd_mode(mode)
560
+ @current_mode = mode
561
+ puts "āœ… Mode set to #{@current_mode}"
562
+ end
563
+
564
+ # Show help
565
+ #
566
+ # @return [void]
567
+ #
568
+ def cmd_help
569
+ puts "Commands: load, list (l), info, gen (g), table (t), debug (d), inspect (i), seed, mode, help (h), quit (q)"
570
+ puts "Export: Use '> filename' to export output (e.g., 'gen User 100 > users.json')"
571
+ end
572
+
573
+ # Quit REPL
574
+ #
575
+ # @return [void]
576
+ #
577
+ def cmd_quit
578
+ puts "Goodbye!"
579
+ stop
580
+ end
581
+
582
+ # Find schema by name with suggestions
583
+ #
584
+ # @param name [String] schema name
585
+ # @return [Schema, nil] schema or nil if not found
586
+ #
587
+ def find_schema(name)
588
+ @registry.schema(name)
589
+ rescue KeyError
590
+ suggestions = Utils::StringDistance.similar_strings(name, @registry.names, max_distance: 3)
591
+ puts "āŒ Schema '#{name}' not found"
592
+ puts Utils::StringDistance.format_suggestions(suggestions) if suggestions.any?
593
+ nil
594
+ end
595
+
596
+ # Build generation options hash
597
+ #
598
+ # @return [Hash] generation options
599
+ #
600
+ def build_generation_opts
601
+ opts = { registry: @registry, mode: @current_mode }
602
+ opts[:seed] = @current_seed if @current_seed
603
+ opts
604
+ end
605
+ end
606
+ end
607
+ end