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.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- metadata +478 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module Core
|
|
6
|
+
# Sequence type generator (numeric or string sequences)
|
|
7
|
+
#
|
|
8
|
+
# Generates sequential values with thread-safe counter management per schema.
|
|
9
|
+
# Supports both numeric and string sequences with configurable start and step.
|
|
10
|
+
#
|
|
11
|
+
# @example Generate numeric sequence
|
|
12
|
+
# seq = Sequence.new
|
|
13
|
+
# seq.generate_random(rng, context, { start: 1, step: 1 }, :random)
|
|
14
|
+
# # => 1, 2, 3, ...
|
|
15
|
+
#
|
|
16
|
+
# @example Generate string sequence
|
|
17
|
+
# seq.generate_random(rng, context, { start: "A", step: 1 }, :random)
|
|
18
|
+
# # => "A", "B", "C", ...
|
|
19
|
+
#
|
|
20
|
+
class Sequence < Base
|
|
21
|
+
# Class-level counters for thread-safe sequence management
|
|
22
|
+
@counters = {}
|
|
23
|
+
@mutex = Mutex.new
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
attr_reader :counters, :mutex
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Get or create a counter for a schema
|
|
30
|
+
#
|
|
31
|
+
# @param schema_name [String] schema name
|
|
32
|
+
# @return [Integer] current counter value (after increment)
|
|
33
|
+
#
|
|
34
|
+
def self.counter_for(schema_name)
|
|
35
|
+
@mutex.synchronize do
|
|
36
|
+
@counters[schema_name] ||= 0
|
|
37
|
+
@counters[schema_name] += 1
|
|
38
|
+
@counters[schema_name]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Reset counter for a schema
|
|
43
|
+
#
|
|
44
|
+
# @param schema_name [String] schema name
|
|
45
|
+
# @return [void]
|
|
46
|
+
#
|
|
47
|
+
def self.reset_counter(schema_name)
|
|
48
|
+
@mutex.synchronize do
|
|
49
|
+
@counters[schema_name] = 0
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Generate a sequential value
|
|
54
|
+
#
|
|
55
|
+
# @param rng [Generator::RNG] random number generator (not used)
|
|
56
|
+
# @param context [Generator::Context] generation context
|
|
57
|
+
# @param args [Hash] type arguments
|
|
58
|
+
# @option args [Integer, String] :start starting value (default: 1)
|
|
59
|
+
# @option args [Integer] :step step size (default: 1)
|
|
60
|
+
# @option args [String] :format format string (e.g., "ID-%03d")
|
|
61
|
+
# @return [Integer, String] next sequential value
|
|
62
|
+
#
|
|
63
|
+
def generate_random(rng, context, args)
|
|
64
|
+
start = args[:start] || 1
|
|
65
|
+
step = args[:step] || 1
|
|
66
|
+
format_str = args[:format]
|
|
67
|
+
|
|
68
|
+
# Get schema name from context or use default
|
|
69
|
+
schema_name = context&.schema_name || "default"
|
|
70
|
+
counter = self.class.counter_for(schema_name)
|
|
71
|
+
|
|
72
|
+
# Calculate value
|
|
73
|
+
if start.is_a?(String)
|
|
74
|
+
# String sequence: increment character
|
|
75
|
+
if format_str
|
|
76
|
+
# Format with counter
|
|
77
|
+
if format_str.include?("%s")
|
|
78
|
+
format(format_str, start, counter)
|
|
79
|
+
else
|
|
80
|
+
format(format_str, counter)
|
|
81
|
+
end
|
|
82
|
+
else
|
|
83
|
+
# Simple character increment
|
|
84
|
+
base_char = start[0]
|
|
85
|
+
new_char = (base_char.ord + (counter - 1) * step).chr
|
|
86
|
+
new_char
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
# Numeric sequence
|
|
90
|
+
value = start + (counter - 1) * step
|
|
91
|
+
if format_str
|
|
92
|
+
format(format_str, value)
|
|
93
|
+
else
|
|
94
|
+
value
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Generate edge case sequence values
|
|
100
|
+
#
|
|
101
|
+
# @param rng [Generator::RNG] random number generator
|
|
102
|
+
# @param context [Generator::Context] generation context
|
|
103
|
+
# @param args [Hash] type arguments
|
|
104
|
+
# @return [Integer, String] edge case value
|
|
105
|
+
#
|
|
106
|
+
def generate_edge(rng, context, args)
|
|
107
|
+
start = args[:start] || 1
|
|
108
|
+
if start.is_a?(String)
|
|
109
|
+
rng.sample(["A", "Z", "0", ""])
|
|
110
|
+
else
|
|
111
|
+
rng.sample([0, 1, -1, 2_147_483_647])
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Generate invalid sequence values
|
|
116
|
+
#
|
|
117
|
+
# @param rng [Generator::RNG] random number generator
|
|
118
|
+
# @param context [Generator::Context] generation context
|
|
119
|
+
# @param args [Hash] type arguments
|
|
120
|
+
# @return [Object] invalid value
|
|
121
|
+
#
|
|
122
|
+
def generate_invalid(rng, context, args)
|
|
123
|
+
rng.sample([
|
|
124
|
+
nil,
|
|
125
|
+
"not-a-sequence",
|
|
126
|
+
[],
|
|
127
|
+
{}
|
|
128
|
+
])
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Sequential ID type generator
|
|
133
|
+
#
|
|
134
|
+
# Generates sequential integer IDs with thread-safe counter management.
|
|
135
|
+
# Each named sequence maintains its own counter.
|
|
136
|
+
#
|
|
137
|
+
# @example Generate sequential ID
|
|
138
|
+
# id_type = IdSequence.new
|
|
139
|
+
# id_type.generate_random(rng, context, { start: 1 })
|
|
140
|
+
# # => 1
|
|
141
|
+
# id_type.generate_random(rng, context, { start: 1 })
|
|
142
|
+
# # => 2
|
|
143
|
+
#
|
|
144
|
+
class IdSequence < Base
|
|
145
|
+
# Create a new IdSequence generator
|
|
146
|
+
#
|
|
147
|
+
# Initializes thread-safe counters for managing multiple sequences.
|
|
148
|
+
#
|
|
149
|
+
def initialize
|
|
150
|
+
@counters = Hash.new(0)
|
|
151
|
+
@mutex = Mutex.new
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
# Generate a sequential ID
|
|
155
|
+
#
|
|
156
|
+
# @param rng [Generator::RNG] random number generator (not used for sequence)
|
|
157
|
+
# @param context [Generator::Context] generation context
|
|
158
|
+
# @param args [Hash] type arguments
|
|
159
|
+
# @option args [Integer] :start starting value (default: 1)
|
|
160
|
+
# @option args [String, Symbol] :name sequence name (default: "default")
|
|
161
|
+
# @return [Integer] next sequential ID
|
|
162
|
+
#
|
|
163
|
+
def generate_random(rng, context, args)
|
|
164
|
+
start = args[:start] || 1
|
|
165
|
+
sequence_name = args[:name] || "default"
|
|
166
|
+
|
|
167
|
+
@mutex.synchronize do
|
|
168
|
+
@counters[sequence_name] += 1
|
|
169
|
+
start + @counters[sequence_name] - 1
|
|
170
|
+
end
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# Generate edge case ID values
|
|
174
|
+
#
|
|
175
|
+
# @param rng [Generator::RNG] random number generator
|
|
176
|
+
# @param context [Generator::Context] generation context
|
|
177
|
+
# @param args [Hash] type arguments (not used)
|
|
178
|
+
# @return [Integer] edge case ID
|
|
179
|
+
#
|
|
180
|
+
def generate_edge(rng, context, args)
|
|
181
|
+
rng.sample([1, 0, -1, 2_147_483_647])
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Generate invalid ID values
|
|
185
|
+
#
|
|
186
|
+
# @param rng [Generator::RNG] random number generator
|
|
187
|
+
# @param context [Generator::Context] generation context
|
|
188
|
+
# @param args [Hash] type arguments (not used)
|
|
189
|
+
# @return [Object] invalid ID value
|
|
190
|
+
#
|
|
191
|
+
def generate_invalid(rng, context, args)
|
|
192
|
+
rng.sample(["not-an-id", nil, "abc", 1.5])
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Reset counter for a specific sequence
|
|
196
|
+
#
|
|
197
|
+
# @param name [String] sequence name (default: "default")
|
|
198
|
+
# @return [void]
|
|
199
|
+
#
|
|
200
|
+
def reset(name = "default")
|
|
201
|
+
@mutex.synchronize do
|
|
202
|
+
@counters[name] = 0
|
|
203
|
+
end
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
# Reset all sequence counters
|
|
207
|
+
#
|
|
208
|
+
# @return [void]
|
|
209
|
+
#
|
|
210
|
+
def reset_all
|
|
211
|
+
@mutex.synchronize do
|
|
212
|
+
@counters.clear
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Character sequence type generator
|
|
218
|
+
class CharacterSequence < Base
|
|
219
|
+
@counters = {}
|
|
220
|
+
@mutex = Mutex.new
|
|
221
|
+
|
|
222
|
+
class << self
|
|
223
|
+
attr_reader :counters, :mutex
|
|
224
|
+
|
|
225
|
+
def counter_for(schema_name)
|
|
226
|
+
@mutex.synchronize do
|
|
227
|
+
@counters[schema_name] ||= 0
|
|
228
|
+
@counters[schema_name] += 1
|
|
229
|
+
@counters[schema_name]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def reset_counter(schema_name)
|
|
234
|
+
@mutex.synchronize do
|
|
235
|
+
@counters[schema_name] = 0
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def generate_random(rng, context, args)
|
|
241
|
+
start = args[:start] || "A"
|
|
242
|
+
step = args[:step] || 1
|
|
243
|
+
schema_name = context&.schema_name || "default"
|
|
244
|
+
counter = self.class.counter_for(schema_name)
|
|
245
|
+
|
|
246
|
+
base_char = start[0]
|
|
247
|
+
new_char = (base_char.ord + (counter - 1) * step).chr
|
|
248
|
+
new_char
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def generate_edge(rng, context, args)
|
|
252
|
+
rng.sample(["A", "Z", "AA", ""])
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
def generate_invalid(rng, context, args)
|
|
256
|
+
rng.sample([nil, "not-chars", 123, []])
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Digit sequence type generator
|
|
261
|
+
class DigitSequence < Base
|
|
262
|
+
@counters = {}
|
|
263
|
+
@mutex = Mutex.new
|
|
264
|
+
|
|
265
|
+
class << self
|
|
266
|
+
attr_reader :counters, :mutex
|
|
267
|
+
|
|
268
|
+
def counter_for(schema_name)
|
|
269
|
+
@mutex.synchronize do
|
|
270
|
+
@counters[schema_name] ||= 0
|
|
271
|
+
@counters[schema_name] += 1
|
|
272
|
+
@counters[schema_name]
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def reset_counter(schema_name)
|
|
277
|
+
@mutex.synchronize do
|
|
278
|
+
@counters[schema_name] = 0
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def generate_random(rng, context, args)
|
|
284
|
+
start = args[:start] || 0
|
|
285
|
+
step = args[:step] || 1
|
|
286
|
+
format_str = args[:format] || "%d"
|
|
287
|
+
schema_name = context&.schema_name || "default"
|
|
288
|
+
counter = self.class.counter_for(schema_name)
|
|
289
|
+
value = start + (counter - 1) * step
|
|
290
|
+
format(format_str, value)
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
def generate_edge(rng, context, args)
|
|
294
|
+
rng.sample(["0", "9", "00", "999"])
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def generate_invalid(rng, context, args)
|
|
298
|
+
rng.sample([nil, "not-digits", [], {}])
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
end
|
|
304
|
+
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module Crypto
|
|
6
|
+
# BitcoinAddress type
|
|
7
|
+
class BitcoinAddress < Base
|
|
8
|
+
# Base58 characters (Bitcoin uses Base58)
|
|
9
|
+
BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".freeze
|
|
10
|
+
|
|
11
|
+
# Generate random Bitcoin address
|
|
12
|
+
#
|
|
13
|
+
# @param rng [Generator::RNG] random number generator
|
|
14
|
+
# @param context [Generator::Context] generation context
|
|
15
|
+
# @param args [Hash] type arguments
|
|
16
|
+
# @option args [Symbol] :format address format (:legacy, :bech32)
|
|
17
|
+
# @return [String] generated Bitcoin address
|
|
18
|
+
#
|
|
19
|
+
def generate_random(rng, context, args)
|
|
20
|
+
format_type = args[:format] || :legacy
|
|
21
|
+
|
|
22
|
+
case format_type
|
|
23
|
+
when :bech32
|
|
24
|
+
# Bech32 format: bc1 + 42 characters
|
|
25
|
+
"bc1" + generate_base58_string(rng, 42)
|
|
26
|
+
else # :legacy
|
|
27
|
+
# Legacy format: starts with 1 or 3, 26-35 characters
|
|
28
|
+
prefix = rng.sample(["1", "3"])
|
|
29
|
+
prefix + generate_base58_string(rng, rng.int(26, 34))
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_edge(rng, context, args)
|
|
34
|
+
["1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa", "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"].sample(random: rng.instance_variable_get(:@random))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def generate_invalid(rng, context, args)
|
|
38
|
+
[nil, "not an address", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def generate_base58_string(rng, length)
|
|
44
|
+
length.times.map { rng.sample(BASE58_CHARS.chars) }.join
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# EthereumAddress type
|
|
49
|
+
class EthereumAddress < Base
|
|
50
|
+
# Generate random Ethereum address
|
|
51
|
+
#
|
|
52
|
+
# @param rng [Generator::RNG] random number generator
|
|
53
|
+
# @param context [Generator::Context] generation context
|
|
54
|
+
# @param args [Hash] type arguments (not used)
|
|
55
|
+
# @return [String] generated Ethereum address
|
|
56
|
+
#
|
|
57
|
+
def generate_random(rng, context, args)
|
|
58
|
+
# Ethereum addresses are 40 hex characters (20 bytes)
|
|
59
|
+
hex_chars = ("0".."9").to_a + ("a".."f").to_a + ("A".."F").to_a
|
|
60
|
+
address = 40.times.map { rng.sample(hex_chars) }.join
|
|
61
|
+
"0x#{address}"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def generate_edge(rng, context, args)
|
|
65
|
+
["0x0000000000000000000000000000000000000000", "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"].sample(random: rng.instance_variable_get(:@random))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def generate_invalid(rng, context, args)
|
|
69
|
+
[nil, "not an address", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# PrivateKey type
|
|
74
|
+
class PrivateKey < Base
|
|
75
|
+
# Base58 characters for WIF format
|
|
76
|
+
BASE58_CHARS = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".freeze
|
|
77
|
+
|
|
78
|
+
# Generate random private key
|
|
79
|
+
#
|
|
80
|
+
# @param rng [Generator::RNG] random number generator
|
|
81
|
+
# @param context [Generator::Context] generation context
|
|
82
|
+
# @param args [Hash] type arguments
|
|
83
|
+
# @option args [Symbol] :format key format (:hex, :wif, :pem)
|
|
84
|
+
# @option args [Integer] :length key length in bytes (default: 32)
|
|
85
|
+
# @return [String] generated private key
|
|
86
|
+
#
|
|
87
|
+
def generate_random(rng, context, args)
|
|
88
|
+
format_type = args[:format] || :hex
|
|
89
|
+
length = args[:length] || 32
|
|
90
|
+
|
|
91
|
+
case format_type
|
|
92
|
+
when :wif
|
|
93
|
+
# WIF (Wallet Import Format) for Bitcoin: Base58 encoded
|
|
94
|
+
"5" + generate_base58_string(rng, 51)
|
|
95
|
+
when :pem
|
|
96
|
+
# PEM format (simplified)
|
|
97
|
+
"-----BEGIN PRIVATE KEY-----\n" + generate_base64_string(rng, length * 2) + "\n-----END PRIVATE KEY-----"
|
|
98
|
+
else # :hex
|
|
99
|
+
# Hex format: 64 hex characters (32 bytes)
|
|
100
|
+
hex_chars = ("0".."9").to_a + ("a".."f").to_a
|
|
101
|
+
(length * 2).times.map { rng.sample(hex_chars) }.join
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def generate_edge(rng, context, args)
|
|
106
|
+
["0" * 64, "f" * 64].sample(random: rng.instance_variable_get(:@random))
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def generate_invalid(rng, context, args)
|
|
110
|
+
[nil, "not a key", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private
|
|
114
|
+
|
|
115
|
+
def generate_base58_string(rng, length)
|
|
116
|
+
length.times.map { rng.sample(BASE58_CHARS.chars) }.join
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def generate_base64_string(rng, length)
|
|
120
|
+
chars = ('A'..'Z').to_a + ('a'..'z').to_a + ('0'..'9').to_a + ['+', '/', '=']
|
|
121
|
+
length.times.map { rng.sample(chars) }.join
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# HashType type
|
|
126
|
+
class HashType < Base
|
|
127
|
+
# Hash algorithm lengths (in hex characters)
|
|
128
|
+
HASH_LENGTHS = {
|
|
129
|
+
md5: 32,
|
|
130
|
+
sha1: 40,
|
|
131
|
+
sha256: 64,
|
|
132
|
+
sha512: 128
|
|
133
|
+
}.freeze
|
|
134
|
+
|
|
135
|
+
# Generate random hash
|
|
136
|
+
#
|
|
137
|
+
# @param rng [Generator::RNG] random number generator
|
|
138
|
+
# @param context [Generator::Context] generation context
|
|
139
|
+
# @param args [Hash] type arguments
|
|
140
|
+
# @option args [Symbol, String] :algorithm hash algorithm (:md5, :sha1, :sha256, :sha512)
|
|
141
|
+
# @return [String] generated hash string
|
|
142
|
+
#
|
|
143
|
+
def generate_random(rng, context, args)
|
|
144
|
+
algorithm = (args[:algorithm] || :sha256).to_sym
|
|
145
|
+
length = HASH_LENGTHS[algorithm] || 64
|
|
146
|
+
|
|
147
|
+
# Generate random hex string
|
|
148
|
+
hex_chars = ("0".."9").to_a + ("a".."f").to_a
|
|
149
|
+
length.times.map { rng.sample(hex_chars) }.join
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def generate_edge(rng, context, args)
|
|
153
|
+
["0" * 64, "f" * 64].sample(random: rng.instance_variable_get(:@random))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def generate_invalid(rng, context, args)
|
|
157
|
+
[nil, "not a hash", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|