featurevisor 0.3.0 → 1.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42c8ec3e5bf6293eeae189365b0383588a8cf7fdd5a5ee249a6fd44a2676403c
4
- data.tar.gz: ce5794865b3dd5a057b9e0841814dab1c276b52237f230e23da6317f8dea3f70
3
+ metadata.gz: 5951351cf7893e83e9f3994999f48389e1998bb8dec84092f3e7627f986bfd02
4
+ data.tar.gz: 3d24e02b8fd0708084b7716863396119728bcd83cbd0ac209b7a1c62fb982f68
5
5
  SHA512:
6
- metadata.gz: 41aefa056806a97bc1f5bf13d847007fd4b38fb176f97d1a09868434644fda2ed8cd58698ad9f5002a6ef5d30f7fc105accf7d505eb41fb71abe28a5bda6e47c
7
- data.tar.gz: e4376d46699b8fe730d4dc4f73890fd43c6e879c446bc484dc6a47cc6b2c31f715af41b83b5f6189f9b8eeed3fd61b48213ec589855d909fe7ebccb702c04303
6
+ metadata.gz: 368e45cb26cb6a98d13bc14b4fe9116bd36af47acadad3fbc99fc1926c8ce768edf5c9ade31aa36dd3c85ff95fc6dab2d3433d4d607bbeee99076a21838e0dae
7
+ data.tar.gz: 160b9eb960915113e3f9dfa3507b9f06664224ac3517d7a84432ec64d2d26d26c2ba379a09aeba64efe78a310972b5f497ad5d5ef1bbef1cc41ffa1408ffa70f
data/README.md CHANGED
@@ -1,12 +1,13 @@
1
1
  # Featurevisor Ruby SDK <!-- omit in toc -->
2
2
 
3
- This is a port of Featurevisor [JavaScript SDK](https://featurevisor.com/docs/sdks/javascript/) v2.x to Ruby, providing a way to evaluate feature flags, variations, and variables in your Ruby applications.
3
+ This is a port of Featurevisor [JavaScript SDK](https://featurevisor.com/docs/sdks/javascript/) v3.x to Ruby, providing a way to evaluate feature flags, variations, and variables in your Ruby applications.
4
4
 
5
- This SDK is compatible with [Featurevisor](https://featurevisor.com/) v2.0 projects and above.
5
+ This SDK is compatible with Featurevisor v3 projects and v2 datafiles.
6
6
 
7
7
  ## Table of contents <!-- omit in toc -->
8
8
 
9
9
  - [Installation](#installation)
10
+ - [Public API](#public-api)
10
11
  - [Initialization](#initialization)
11
12
  - [Evaluation types](#evaluation-types)
12
13
  - [Context](#context)
@@ -23,20 +24,23 @@ This SDK is compatible with [Featurevisor](https://featurevisor.com/) v2.0 proje
23
24
  - [Initialize with sticky](#initialize-with-sticky)
24
25
  - [Set sticky afterwards](#set-sticky-afterwards)
25
26
  - [Setting datafile](#setting-datafile)
27
+ - [Merging by default](#merging-by-default)
28
+ - [Replacing](#replacing)
29
+ - [Loading datafiles on demand](#loading-datafiles-on-demand)
26
30
  - [Updating datafile](#updating-datafile)
27
31
  - [Interval-based update](#interval-based-update)
28
- - [Logging](#logging)
32
+ - [Evaluation details](#evaluation-details)
33
+ - [Diagnostics](#diagnostics)
29
34
  - [Levels](#levels)
30
- - [Customizing levels](#customizing-levels)
31
35
  - [Handler](#handler)
32
36
  - [Events](#events)
33
37
  - [`datafile_set`](#datafile_set)
34
38
  - [`context_set`](#context_set)
35
39
  - [`sticky_set`](#sticky_set)
36
- - [Evaluation details](#evaluation-details)
37
- - [Hooks](#hooks)
38
- - [Defining a hook](#defining-a-hook)
39
- - [Registering hooks](#registering-hooks)
40
+ - [`error`](#error)
41
+ - [Modules](#modules)
42
+ - [Defining a module](#defining-a-module)
43
+ - [Registering modules](#registering-modules)
40
44
  - [Child instance](#child-instance)
41
45
  - [Close](#close)
42
46
  - [CLI usage](#cli-usage)
@@ -72,6 +76,18 @@ Or install it yourself as:
72
76
  $ gem install featurevisor
73
77
  ```
74
78
 
79
+ ## Public API
80
+
81
+ The main runtime API is `Featurevisor.create_featurevisor`:
82
+
83
+ ```ruby
84
+ f = Featurevisor.create_featurevisor(
85
+ datafile: datafile_content
86
+ )
87
+ ```
88
+
89
+ Most applications only need this factory and the returned `Featurevisor::Instance`. Public extension and observability APIs include modules, diagnostics, events, and the datafile structures accepted by the factory.
90
+
75
91
  ## Initialization
76
92
 
77
93
  The SDK can be initialized by passing [datafile](https://featurevisor.com/docs/building-datafiles/) content directly:
@@ -89,7 +105,7 @@ response = Net::HTTP.get_response(URI(datafile_url))
89
105
  datafile_content = JSON.parse(response.body, symbolize_names: true)
90
106
 
91
107
  # Create SDK instance
92
- f = Featurevisor.create_instance(
108
+ f = Featurevisor.create_featurevisor(
93
109
  datafile: datafile_content
94
110
  )
95
111
  ```
@@ -101,10 +117,10 @@ Alternatively, you can pass a JSON string directly and the SDK will parse it aut
101
117
  ```ruby
102
118
  # Option 1: Parse JSON yourself (recommended)
103
119
  datafile_content = JSON.parse(json_string, symbolize_names: true)
104
- f = Featurevisor.create_instance(datafile: datafile_content)
120
+ f = Featurevisor.create_featurevisor(datafile: datafile_content)
105
121
 
106
122
  # Option 2: Pass JSON string directly (automatic parsing)
107
- f = Featurevisor.create_instance(datafile: json_string)
123
+ f = Featurevisor.create_featurevisor(datafile: json_string)
108
124
  ```
109
125
 
110
126
  ## Evaluation types
@@ -142,7 +158,7 @@ You can set context at the time of initialization:
142
158
  ```ruby
143
159
  require 'featurevisor'
144
160
 
145
- f = Featurevisor.create_instance(
161
+ f = Featurevisor.create_featurevisor(
146
162
  context: {
147
163
  deviceId: '123',
148
164
  country: 'nl'
@@ -274,6 +290,8 @@ f.get_variable_object(feature_key, variable_key, context = {})
274
290
  f.get_variable_json(feature_key, variable_key, context = {})
275
291
  ```
276
292
 
293
+ Type specific methods do not coerce values. `get_variable_integer` returns `nil` for the string `"1"`, and boolean getters return `nil` for non-boolean values.
294
+
277
295
  ## Getting all evaluations
278
296
 
279
297
  You can get evaluations of all features available in the SDK instance:
@@ -304,12 +322,14 @@ This is handy especially when you want to pass all evaluations from a backend ap
304
322
 
305
323
  For the lifecycle of the SDK instance in your application, you can set some features with sticky values, meaning that they will not be evaluated against the fetched [datafile](https://featurevisor.com/docs/building-datafiles/):
306
324
 
325
+ Sticky values belong to an SDK or child instance. Evaluation options do not accept sticky overrides; use `spawn(context, sticky: ...)` when a child needs its own sticky state.
326
+
307
327
  ### Initialize with sticky
308
328
 
309
329
  ```ruby
310
330
  require 'featurevisor'
311
331
 
312
- f = Featurevisor.create_instance(
332
+ f = Featurevisor.create_featurevisor(
313
333
  sticky: {
314
334
  myFeatureKey: {
315
335
  enabled: true,
@@ -362,6 +382,49 @@ f.set_datafile(json_string)
362
382
 
363
383
  **Important**: When calling `set_datafile()`, ensure JSON is parsed with `symbolize_names: true` if you're parsing it yourself.
364
384
 
385
+ ### Merging by default
386
+
387
+ By default, `set_datafile(datafile)` merges the incoming datafile into the SDK's current datafile:
388
+
389
+ - top-level metadata such as `schemaVersion`, `revision`, and `featurevisorVersion` comes from the incoming datafile
390
+ - `segments` are merged, with incoming entries overriding existing ones
391
+ - `features` are merged, with incoming entries overriding existing ones
392
+
393
+ This means you can call `set_datafile` more than once with different datafiles, and the SDK instance accumulates their features and segments together.
394
+
395
+ ### Replacing
396
+
397
+ To fully replace the stored datafile, pass `true` as the second argument:
398
+
399
+ ```ruby
400
+ f.set_datafile(datafile_content, true)
401
+ ```
402
+
403
+ ### Loading datafiles on demand
404
+
405
+ Because merging is the default, a single SDK instance can start with a small datafile and load more datafiles later as your application needs them, instead of downloading every feature upfront.
406
+
407
+ This pairs well with [targets](https://featurevisor.com/docs/targets/), where each target produces a smaller datafile for a specific part of your application:
408
+
409
+ ```ruby
410
+ require "open-uri"
411
+
412
+ f = Featurevisor.create_featurevisor({})
413
+
414
+ def load_datafile(f, target)
415
+ url = "https://cdn.yoursite.com/production/featurevisor-#{target}.json"
416
+ datafile = JSON.parse(URI.open(url).read, symbolize_names: true)
417
+
418
+ # merges into whatever was loaded before
419
+ f.set_datafile(datafile)
420
+ end
421
+
422
+ load_datafile(f, "products")
423
+
424
+ # later, when the user reaches checkout
425
+ load_datafile(f, "checkout")
426
+ ```
427
+
365
428
  ### Updating datafile
366
429
 
367
430
  You can set the datafile as many times as you want in your application, which will result in emitting a [`datafile_set`](#datafile_set) event that you can listen and react to accordingly.
@@ -400,65 +463,38 @@ end
400
463
  Thread.new { update_datafile(f, datafile_url) }
401
464
  ```
402
465
 
403
- ## Logging
466
+ ## Diagnostics
404
467
 
405
- By default, Featurevisor SDKs will print out logs to the console for `info` level and above.
468
+ By default, Featurevisor reports diagnostics to the console for `info` level and above with a `[Featurevisor]` prefix.
406
469
 
407
470
  ### Levels
408
471
 
409
- These are all the available log levels:
410
-
411
- - `error`
412
- - `warn`
413
- - `info`
414
- - `debug`
415
-
416
- ### Customizing levels
417
-
418
- If you choose `debug` level to make the logs more verbose, you can set it at the time of SDK initialization.
419
-
420
- Setting `debug` level will print out all logs, including `info`, `warn`, and `error` levels.
472
+ Available diagnostic levels are `fatal`, `error`, `warn`, `info`, and `debug`.
421
473
 
422
- ```ruby
423
- require 'featurevisor'
424
-
425
- f = Featurevisor.create_instance(
426
- logger: Featurevisor.create_logger(level: 'debug')
427
- )
428
- ```
429
-
430
- Alternatively, you can also set `log_level` directly:
474
+ Set the level during initialization or update it afterwards:
431
475
 
432
476
  ```ruby
433
- f = Featurevisor.create_instance(
434
- log_level: 'debug'
435
- )
436
- ```
437
-
438
- You can also set log level from SDK instance afterwards:
439
-
440
- ```ruby
441
- f.set_log_level('debug')
477
+ f = Featurevisor.create_featurevisor(log_level: "debug")
478
+ f.set_log_level("info")
442
479
  ```
443
480
 
444
481
  ### Handler
445
482
 
446
- You can also pass your own log handler, if you do not wish to print the logs to the console:
483
+ Use `on_diagnostic` to send structured diagnostics to your observability system:
447
484
 
448
485
  ```ruby
449
- require 'featurevisor'
450
-
451
- f = Featurevisor.create_instance(
452
- logger: Featurevisor.create_logger(
453
- level: 'info',
454
- handler: ->(level, message, details) {
455
- # do something with the log
456
- }
457
- )
486
+ f = Featurevisor.create_featurevisor(
487
+ log_level: "info",
488
+ on_diagnostic: ->(diagnostic) {
489
+ puts "#{diagnostic[:level]} #{diagnostic[:code]} #{diagnostic[:message]}"
490
+ }
458
491
  )
459
492
  ```
460
493
 
461
- Further log levels like `info` and `debug` will help you understand how the feature variations and variables are evaluated in the runtime against given context.
494
+ Every diagnostic has `:level`, `:code`, `:message`, and an object-shaped `:details` hash. Optional `:module`, `:moduleName`, and `:originalError` fields describe provenance. Evaluation metadata belongs in `:details`.
495
+
496
+ Diagnostic handlers are isolated from SDK behavior. An exception in a handler does not stop other handlers or evaluations.
497
+
462
498
 
463
499
  ## Events
464
500
 
@@ -471,8 +507,8 @@ You can listen to these events that can occur at various stages in your applicat
471
507
  ```ruby
472
508
  unsubscribe = f.on('datafile_set') do |event|
473
509
  revision = event[:revision] # new revision
474
- previous_revision = event[:previous_revision]
475
- revision_changed = event[:revision_changed] # true if revision has changed
510
+ previous_revision = event[:previousRevision]
511
+ revision_changed = event[:revisionChanged] # true if revision has changed
476
512
 
477
513
  # list of feature keys that have new updates,
478
514
  # and you should re-evaluate them
@@ -493,6 +529,8 @@ The `features` array will contain keys of features that have either been:
493
529
 
494
530
  compared to the previous datafile content that existed in the SDK instance.
495
531
 
532
+ The event also includes `replaced`, which is `true` when the datafile replaced the previous content instead of merging into it.
533
+
496
534
  ### `context_set`
497
535
 
498
536
  ```ruby
@@ -515,6 +553,20 @@ unsubscribe = f.on('sticky_set') do |event|
515
553
  end
516
554
  ```
517
555
 
556
+ ### `error`
557
+
558
+ ```ruby
559
+ unsubscribe = f.on('error') do |event|
560
+ diagnostic = event[:diagnostic]
561
+ code = diagnostic[:code]
562
+ message = diagnostic[:message]
563
+
564
+ puts "Featurevisor error: #{code} #{message}"
565
+ end
566
+ ```
567
+
568
+ The `error` event is emitted for diagnostics reported with `level: "error"`.
569
+
518
570
  ## Evaluation details
519
571
 
520
572
  Besides logging with debug level enabled, you can also get more details about how the feature variations and variables are evaluated in the runtime against given context:
@@ -547,22 +599,33 @@ And optionally these properties depending on whether you are evaluating a featur
547
599
  - `variable_value`: the variable value
548
600
  - `variable_schema`: the variable schema
549
601
 
550
- ## Hooks
602
+ ## Modules
603
+
604
+ Modules allow you to intercept the evaluation process and customize SDK behavior.
551
605
 
552
- Hooks allow you to intercept the evaluation process and customize it further as per your needs.
606
+ ### Defining a module
553
607
 
554
- ### Defining a hook
608
+ A module is a simple hash with a unique recommended `name` and optional lifecycle functions:
555
609
 
556
- A hook is a simple hash with a unique required `name` and optional functions:
610
+ If `setup` raises an exception, the module is not registered. Featurevisor removes subscriptions created during setup, reports `module_setup_error`, and calls `close` when present.
557
611
 
558
612
  ```ruby
559
613
  require 'featurevisor'
560
614
 
561
- my_custom_hook = {
562
- # only required property
563
- name: 'my-custom-hook',
615
+ my_custom_module = {
616
+ # recommended, and used for duplicate detection/removal
617
+ name: 'my-custom-module',
618
+
619
+ # rest of the properties below are all optional per module
620
+
621
+ # setup once, when the module is registered
622
+ setup: ->(api) {
623
+ revision = api[:get_revision].call
564
624
 
565
- # rest of the properties below are all optional per hook
625
+ api[:on_diagnostic].call(->(diagnostic) {
626
+ puts diagnostic[:message]
627
+ })
628
+ },
566
629
 
567
630
  # before evaluation
568
631
  before: ->(options) {
@@ -592,26 +655,43 @@ my_custom_hook = {
592
655
  bucket_value: ->(options) {
593
656
  # return custom bucket value
594
657
  options[:bucket_value]
658
+ },
659
+
660
+ # cleanup when module is removed or SDK is closed
661
+ close: -> {
662
+ # cleanup here
595
663
  }
596
664
  }
597
665
  ```
598
666
 
599
- ### Registering hooks
667
+ The module API passed to `setup` exposes:
668
+
669
+ - `get_revision`
670
+ - `on_diagnostic`
671
+ - `report_diagnostic`
600
672
 
601
- You can register hooks at the time of SDK initialization:
673
+ ### Registering modules
674
+
675
+ You can register modules at the time of SDK initialization:
602
676
 
603
677
  ```ruby
604
678
  require 'featurevisor'
605
679
 
606
- f = Featurevisor.create_instance(
607
- hooks: [my_custom_hook]
680
+ f = Featurevisor.create_featurevisor(
681
+ modules: [my_custom_module]
608
682
  )
609
683
  ```
610
684
 
611
685
  Or after initialization:
612
686
 
613
687
  ```ruby
614
- f.add_hook(my_custom_hook)
688
+ remove_module = f.add_module(my_custom_module)
689
+
690
+ # remove later by calling the returned function
691
+ remove_module.call
692
+
693
+ # or remove by name
694
+ f.remove_module('my-custom-module')
615
695
  ```
616
696
 
617
697
  ## Child instance
@@ -687,29 +767,24 @@ $ bundle exec featurevisor test \
687
767
  --quiet|--verbose \
688
768
  --onlyFailures \
689
769
  --keyPattern="myFeatureKey" \
690
- --assertionPattern="#1" \
691
- --with-scopes \
692
- --with-tags
770
+ --assertionPattern="#1"
693
771
  ```
694
772
 
695
- `--with-scopes` and `--with-tags` make the Ruby test runner build scoped/tagged datafiles in memory (via `npx featurevisor build --json`) and evaluate matching assertions against those exact datafiles.
696
-
697
- If an assertion references `scope` and `--with-scopes` is not provided, the runner still evaluates the assertion by merging that scope's configured context into the assertion context (without building scoped datafiles).
773
+ The Ruby test runner builds base datafiles and Target datafiles in memory via `npx featurevisor build --json`. When an assertion contains `target`, it is evaluated against the matching Target datafile.
698
774
 
699
- For compatibility, camelCase aliases are also supported: `--withScopes` and `--withTags`.
775
+ All three commands accept repeatable `--target=<target>` options. `test` builds only the selected Target datafiles and runs untargeted assertions plus assertions for those targets. `benchmark` and `assess-distribution` run independently against every selected Target datafile. Without `--target`, existing project-wide behavior is preserved. Project definitions, test specs, Target discovery, and datafile generation continue to come from the Node.js CLI.
700
776
 
701
777
  ### Test against local monorepo's example-1
702
778
 
703
779
  ```bash
704
780
  $ cd /absolute/path/to/featurevisor-ruby
705
- $ bundle exec featurevisor test --projectDirectoryPath=./monorepo/examples/example-1
706
- $ bundle exec featurevisor test --projectDirectoryPath=./monorepo/examples/example-1 --with-scopes
707
- $ bundle exec featurevisor test --projectDirectoryPath=./monorepo/examples/example-1 --with-tags
781
+ $ bundle exec ruby bin/featurevisor test --projectDirectoryPath=/Users/fahad/Projects/featurevisor/featurevisor/examples/example-1 --onlyFailures
782
+ $ make test-example-1
708
783
  ```
709
784
 
710
785
  ### Benchmark
711
786
 
712
- Learn more about benchmarking [here](https://featurevisor.com/docs/cmd/#benchmarking).
787
+ Learn more about benchmarking [here](https://featurevisor.com/docs/cli/#benchmarking).
713
788
 
714
789
  ```bash
715
790
  $ bundle exec featurevisor benchmark \
@@ -722,7 +797,7 @@ $ bundle exec featurevisor benchmark \
722
797
 
723
798
  ### Assess distribution
724
799
 
725
- Learn more about assessing distribution [here](https://featurevisor.com/docs/cmd/#assess-distribution).
800
+ Learn more about assessing distribution [here](https://featurevisor.com/docs/cli/#assess-distribution).
726
801
 
727
802
  ```bash
728
803
  $ bundle exec featurevisor assess-distribution \
data/bin/cli.rb CHANGED
@@ -7,12 +7,13 @@ module FeaturevisorCLI
7
7
  attr_accessor :command, :assertion_pattern, :context, :environment, :feature,
8
8
  :key_pattern, :n, :only_failures, :quiet, :variable, :variation,
9
9
  :verbose, :inflate, :show_datafile, :schema_version, :project_directory_path,
10
- :populate_uuid, :with_scopes, :with_tags
10
+ :populate_uuid, :with_scopes, :with_tags, :targets
11
11
 
12
12
  def initialize
13
13
  @n = 1000
14
14
  @project_directory_path = Dir.pwd
15
15
  @populate_uuid = []
16
+ @targets = []
16
17
  end
17
18
  end
18
19
 
@@ -82,15 +83,15 @@ module FeaturevisorCLI
82
83
  options.show_datafile = true
83
84
  end
84
85
 
85
- opts.on("--schemaVersion=VERSION", "Schema version") do |v|
86
+ opts.on("--schemaVersion=VERSION", "--schema-version=VERSION", "Legacy schema version option accepted and ignored") do |v|
86
87
  options.schema_version = v
87
88
  end
88
89
 
89
- opts.on("--with-scopes", "--withScopes", "Test scoped assertions against scoped datafiles") do
90
+ opts.on("--with-scopes", "--withScopes", "Legacy scope option accepted and ignored") do
90
91
  options.with_scopes = true
91
92
  end
92
93
 
93
- opts.on("--with-tags", "--withTags", "Test tagged assertions against tagged datafiles") do
94
+ opts.on("--with-tags", "--withTags", "Legacy tag option accepted and ignored") do
94
95
  options.with_tags = true
95
96
  end
96
97
 
@@ -102,6 +103,10 @@ module FeaturevisorCLI
102
103
  options.populate_uuid << v
103
104
  end
104
105
 
106
+ opts.on("--target=TARGET", "Target datafile; repeat for multiple targets") do |v|
107
+ options.targets << v unless options.targets.include?(v)
108
+ end
109
+
105
110
  opts.on("-h", "--help", "Show this help message") do
106
111
  puts opts
107
112
  exit
@@ -29,13 +29,21 @@ module FeaturevisorCLI
29
29
  exit 1
30
30
  end
31
31
 
32
- puts ""
33
- puts "Assessing distribution for feature: \"#{@options.feature}\"..."
34
- puts ""
35
-
36
- # Parse context if provided
37
32
  context = parse_context
38
33
 
34
+ targets = resolve_targets
35
+ (targets.empty? ? [nil] : targets).each { |target| run_for_target(context, target) }
36
+ end
37
+
38
+ private
39
+
40
+ def run_for_target(context, target)
41
+ puts "\nAssess Featurevisor distribution"
42
+ puts " Feature: #{@options.feature}"
43
+ puts " Environment: #{@options.environment}"
44
+ puts " Target: #{target}" if target
45
+ puts " Iterations: #{@options.n}"
46
+
39
47
  # Print context information
40
48
  if @options.context
41
49
  puts "Against context: #{@options.context}"
@@ -47,10 +55,10 @@ module FeaturevisorCLI
47
55
  puts ""
48
56
 
49
57
  # Build datafile
50
- datafile = build_datafile(@options.environment)
58
+ datafile = build_datafile(@options.environment, target)
51
59
 
52
60
  # Create SDK instance
53
- instance = create_instance(datafile)
61
+ instance = create_featurevisor(datafile)
54
62
 
55
63
  # Check if feature has variations
56
64
  feature = instance.get_feature(@options.feature)
@@ -104,7 +112,21 @@ module FeaturevisorCLI
104
112
  end
105
113
  end
106
114
 
107
- private
115
+ def resolve_targets
116
+ return [] if @options.targets.empty?
117
+ stdout, stderr, status = Open3.capture3("npx", "featurevisor", "list", "--targets", "--json", chdir: @project_path)
118
+ unless status.success?
119
+ puts stderr
120
+ exit 1
121
+ end
122
+ available = JSON.parse(stdout).map { |item| item.is_a?(Hash) ? (item["key"] || item["name"]) : item }
123
+ unknown = @options.targets.find { |target| !available.include?(target) }
124
+ if unknown
125
+ puts "Unknown target \"#{unknown}\". Available targets: #{available.empty? ? "none" : available.join(", ")}."
126
+ exit 1
127
+ end
128
+ @options.targets
129
+ end
108
130
 
109
131
  def parse_context
110
132
  if @options.context
@@ -121,15 +143,12 @@ module FeaturevisorCLI
121
143
  end
122
144
  end
123
145
 
124
- def build_datafile(environment)
146
+ def build_datafile(environment, target = nil)
125
147
  puts "Building datafile for environment: #{environment}..."
126
148
 
127
149
  # Build the command similar to Go implementation
128
150
  command_parts = ["cd", @project_path, "&&", "npx", "featurevisor", "build", "--environment=#{environment}", "--json"]
129
-
130
- if @options.schema_version
131
- command_parts << "--schemaVersion=#{@options.schema_version}"
132
- end
151
+ command_parts << "--target=#{target}" if target
133
152
 
134
153
  if @options.inflate
135
154
  command_parts << "--inflate=#{@options.inflate}"
@@ -159,9 +178,9 @@ module FeaturevisorCLI
159
178
  [stdout, stderr, exit_status.exitstatus]
160
179
  end
161
180
 
162
- def create_instance(datafile)
181
+ def create_featurevisor(datafile)
163
182
  # Create SDK instance
164
- Featurevisor.create_instance(
183
+ Featurevisor.create_featurevisor(
165
184
  datafile: datafile,
166
185
  log_level: get_logger_level
167
186
  )