shipeasy-sdk 3.4.0 → 3.5.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: c1b1f478bea3279f979e85e04476381f719d3db27aa29ffb23447768ceefc560
4
- data.tar.gz: c0d4f8f1ca49a6a1b34ac3b1198a3788dc115f640eb5845fb388a66175e1384c
3
+ metadata.gz: 0453ca27510264bd268b40868bb5c55a942835b7a20076b11e19305b4eeb27a7
4
+ data.tar.gz: c9c0b1ee3c80cf28abac5af4ec8bdaec1613f8b3235708b79b27223e38d2d463
5
5
  SHA512:
6
- metadata.gz: 94790b0a88b1524f35faf706245339db83908565b5adec3a7f8b9f5775d476cb703ce00ac55a697d79a343a1abe0c94145c2fd693538c1aecbdab81385525148
7
- data.tar.gz: f35b460dd560167674a1a278c1e76600fd59040016ad06b3129ee77c5dd95d68d57d0c7695ca06dee7f7870f15489655ee20ca644b6d1a24d416fdf280c2daf7
6
+ metadata.gz: d92ea1c2016d7e81791a7d7b8d6a0cd631da001893227685297e570c86372dc905da89cf54641d85ef7715e6ffd5ae0498a8781714326352fc139cf5adae0320
7
+ data.tar.gz: bfd396eeac063d46053f15fc865f8444f8284dae480cee7f064809a43bb288cd48ebd7cf559d08d5d3f0f68c3252a360800a8a2f787c5b63e2a3c25601e8d555
data/docs/skill/SKILL.md CHANGED
@@ -155,6 +155,7 @@ the loader tag (public client key).
155
155
  - Anon bucketing: `Shipeasy::SDK::RackMiddleware` mints the shared `__se_anon_id`
156
156
  cookie (Rails Railtie auto-mounts it); anonymous `get_flag` then just works.
157
157
  - `c.private_attributes = ["email"]` strips keys from outbound events.
158
+ - `c.clean_backtrace` (default on) filters `see()` stacks to app frames via `Rails.backtrace_cleaner`; set `false` for raw backtraces.
158
159
  - `c.sticky_store = Shipeasy::SDK::InMemoryStickyStore.new` pins experiment assignment.
159
160
  - SSR: `Shipeasy.bootstrap_script_tag(user)` + `Shipeasy.i18n_script_tag(client_key, "en:prod")`.
160
161
  - `Shipeasy.on_change { ... }` (requires `c.poll = true`) fires after a poll fetches new data.
@@ -46,9 +46,14 @@ module Shipeasy
46
46
  # swallows one of its OWN internal errors it normally ships a structured
47
47
  # see event to Shipeasy's own project (NOT yours) so the SDK team can
48
48
  # track SDK bugs; set true to disable that entirely.
49
+ # - clean_backtrace (default true): pass see() error backtraces through the
50
+ # host framework's own backtrace cleaner so reports carry only your
51
+ # application frames (gem/framework noise stripped). We do not invent the
52
+ # filtering rules — this leverages `Rails.backtrace_cleaner` and is a
53
+ # no-op outside Rails. Set false to always report the raw backtrace.
49
54
  attr_accessor :env, :is_network_enabled, :disable_telemetry, :telemetry_url,
50
55
  :private_attributes, :sticky_store,
51
- :disable_internal_error_reporting
56
+ :disable_internal_error_reporting, :clean_backtrace
52
57
 
53
58
  # SDK-wide diagnostic verbosity for the leveled logger (Shipeasy::Logging).
54
59
  # One of :silent, :error, :warn (default), :info, :debug (strings accepted
@@ -103,6 +108,7 @@ module Shipeasy
103
108
  @is_network_enabled = nil
104
109
  @disable_telemetry = nil
105
110
  @disable_internal_error_reporting = false
111
+ @clean_backtrace = true
106
112
  @telemetry_url = nil
107
113
  @private_attributes = nil
108
114
  @sticky_store = nil
@@ -197,6 +203,7 @@ module Shipeasy
197
203
  sticky_store: cfg.sticky_store,
198
204
  log_level: cfg.log_level,
199
205
  disable_internal_error_reporting: cfg.disable_internal_error_reporting,
206
+ clean_backtrace: cfg.clean_backtrace,
200
207
  )
201
208
  @engine = engine
202
209
  # Capture +engine+ in the closure (not the @engine ivar, which a concurrent
@@ -36,7 +36,7 @@ module Shipeasy
36
36
  # /sdk/i18n/loader.js) — distinct from the edge API the blobs are fetched from.
37
37
  DEFAULT_CDN_BASE = "https://cdn.shipeasy.ai"
38
38
 
39
- def initialize(api_key:, base_url: nil, env: "prod", is_network_enabled: nil, disable_telemetry: nil, telemetry_url: nil, test_mode: false, private_attributes: nil, sticky_store: nil, log_level: nil, disable_internal_error_reporting: false)
39
+ def initialize(api_key:, base_url: nil, env: "prod", is_network_enabled: nil, disable_telemetry: nil, telemetry_url: nil, test_mode: false, private_attributes: nil, sticky_store: nil, log_level: nil, disable_internal_error_reporting: false, clean_backtrace: true)
40
40
  # SDK-wide diagnostic verbosity. Set the leveled logger from the passed
41
41
  # level (default :warn; unknown falls back to :warn). The logger is
42
42
  # module-scoped, so the last-built engine wins — mirrors the TS SDK,
@@ -52,6 +52,13 @@ module Shipeasy
52
52
  # locally so private attrs never leave for evaluation; the only egress is
53
53
  # track(), where the listed keys are dropped from the props bag.
54
54
  @private_attributes = (private_attributes || []).map(&:to_s)
55
+ # When true (default), see() error stacks are passed through the host
56
+ # framework's own backtrace cleaner so a report carries only application
57
+ # frames (gem/framework noise stripped). We never invent the filtering —
58
+ # today this leverages Rails.backtrace_cleaner and is a no-op outside
59
+ # Rails. Set false to always send the raw backtrace. See
60
+ # see_backtrace_cleaner.
61
+ @clean_backtrace = clean_backtrace != false
55
62
  # Pluggable sticky-bucketing store (doc 20 §2). Absent ⇒ deterministic.
56
63
  # Threaded into experiment eval so an enrolled unit locks to its first
57
64
  # assigned variant. Built-in: InMemoryStickyStore.
@@ -645,6 +652,7 @@ module Shipeasy
645
652
  strip_private(built.extras),
646
653
  sdk_version: Shipeasy::SDK::VERSION,
647
654
  env: @env,
655
+ backtrace_cleaner: see_backtrace_cleaner,
648
656
  )
649
657
  return unless @see_limiter.should_send?(ev)
650
658
 
@@ -658,6 +666,26 @@ module Shipeasy
658
666
  Shipeasy::Logging.error "[shipeasy] see() failed: #{e.message}"
659
667
  end
660
668
 
669
+ # The framework-provided backtrace cleaner used to strip gem/framework
670
+ # frames from see() stacks, or nil to send the raw backtrace. Resolved
671
+ # lazily (not memoized) because Rails installs its cleaner during boot,
672
+ # which can finish after Shipeasy.configure runs. Today the only supported
673
+ # cleaner is Rails' own `ActiveSupport::BacktraceCleaner` — we leverage it
674
+ # rather than reimplementing the app-vs-gem frame rules ourselves. Returns
675
+ # nil when disabled or when not running under Rails.
676
+ def see_backtrace_cleaner
677
+ return nil unless @clean_backtrace
678
+ return nil unless defined?(::Rails) && ::Rails.respond_to?(:backtrace_cleaner)
679
+
680
+ cleaner = ::Rails.backtrace_cleaner
681
+ return nil unless cleaner.respond_to?(:clean)
682
+
683
+ ->(bt) { cleaner.clean(bt) }
684
+ rescue StandardError
685
+ # Rails present but the cleaner blew up while resolving: fall back to raw.
686
+ nil
687
+ end
688
+
661
689
  # Drop caller-marked private attributes from an outbound props bag. Handles
662
690
  # both string and symbol keys against the stringified private list.
663
691
  def strip_private(props)
@@ -121,7 +121,15 @@ module Shipeasy
121
121
  # ---- Wire event construction ----
122
122
 
123
123
  # Build the type:"error" event accepted by POST /collect.
124
- def build_event(problem, subject, outcome, extras, sdk_version:, env:)
124
+ #
125
+ # `backtrace_cleaner` (optional) is a callable that takes the raw backtrace
126
+ # array and returns an application-only subset (framework/gem frames
127
+ # stripped) — e.g. `->(bt) { Rails.backtrace_cleaner.clean(bt) }`. It is
128
+ # applied before the stack is joined; if it strips every frame (a stack
129
+ # that is purely framework/gem code) we fall back to the raw backtrace so
130
+ # an error is never left with an empty stack. We never invent the filtering
131
+ # rules — the callable is supplied by the framework's own cleaner.
132
+ def build_event(problem, subject, outcome, extras, sdk_version:, env:, backtrace_cleaner: nil)
125
133
  stack = nil
126
134
 
127
135
  if problem.is_a?(Violation)
@@ -131,7 +139,7 @@ module Shipeasy
131
139
  elsif problem.is_a?(Exception)
132
140
  error_type = problem.class.name || "Error"
133
141
  message = (problem.message.to_s.empty? ? error_type : problem.message)
134
- bt = problem.backtrace
142
+ bt = clean_backtrace(problem.backtrace, backtrace_cleaner)
135
143
  stack = bt.join("\n") if bt && !bt.empty?
136
144
  kind = "caught"
137
145
  else
@@ -158,6 +166,21 @@ module Shipeasy
158
166
  ev
159
167
  end
160
168
 
169
+ # Apply the framework-provided backtrace cleaner to a raw backtrace,
170
+ # returning an application-only frame array. No cleaner (or no backtrace)
171
+ # ⇒ the input is returned untouched. If the cleaner raises, or strips every
172
+ # frame, fall back to the raw backtrace — a cleaned-to-empty stack would
173
+ # lose all debugging signal for an error that lives entirely in framework
174
+ # code. Never raises.
175
+ def clean_backtrace(bt, cleaner)
176
+ return bt if cleaner.nil? || bt.nil? || bt.empty?
177
+
178
+ cleaned = cleaner.call(bt)
179
+ cleaned && !cleaned.empty? ? cleaned : bt
180
+ rescue StandardError
181
+ bt
182
+ end
183
+
161
184
  # ---- Spam limiter (mirror SeeLimiter) ----
162
185
 
163
186
  # Per-process spam guard: identical events within 30s collapse to one
@@ -1,5 +1,5 @@
1
1
  module Shipeasy
2
2
  module SDK
3
- VERSION = "3.4.0"
3
+ VERSION = "3.5.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shipeasy-sdk
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.4.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shipeasy, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-07-14 00:00:00.000000000 Z
11
+ date: 2026-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec