From e387aad8b7da6a6d146648b464ffaade724e93b8 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Mon, 7 Sep 2026 18:05:47 +0200 Subject: [PATCH 1/2] Load the Castle browser SDK as a UMD from npm. --- app/assets/javascripts/castle.js | 5 +++-- app/controllers/vendor/castle_js_controller.rb | 17 ++++++++++++----- app/views/layouts/_castle_js.html.erb | 17 ++++++++++++----- package-lock.json | 2 +- package.json | 2 +- .../vendor/castle_js_controller_spec.rb | 7 +++++++ 6 files changed, 36 insertions(+), 14 deletions(-) diff --git a/app/assets/javascripts/castle.js b/app/assets/javascripts/castle.js index d551705..b235218 100644 --- a/app/assets/javascripts/castle.js +++ b/app/assets/javascripts/castle.js @@ -21,14 +21,15 @@ var submitted = false; form.addEventListener("submit", function (event) { - var sdkReady = window.Castle && typeof Castle.createRequestToken === "function"; + var sdk = window.__castle || window.Castle; + var sdkReady = sdk && typeof sdk.createRequestToken === "function"; if (submitted || !sdkReady) { return; // already handled, or no SDK configured — submit as-is } event.preventDefault(); - Castle.createRequestToken() + sdk.createRequestToken() .then(function (token) { setToken(form, token); }) diff --git a/app/controllers/vendor/castle_js_controller.rb b/app/controllers/vendor/castle_js_controller.rb index 03384dd..9d98710 100644 --- a/app/controllers/vendor/castle_js_controller.rb +++ b/app/controllers/vendor/castle_js_controller.rb @@ -4,6 +4,11 @@ module Vendor # Serves the Castle browser SDK from the npm install (node_modules). class CastleJsController < ActionController::Base DIST = Rails.root.join('node_modules/@castleio/castle-js/dist') + # 2.x ships castle.browser.js; 3.x ships castle.umd.js. The HTML always requests castle.umd.js. + ALIASES = { + 'castle.umd.js' => %w[castle.umd.js castle.browser.js], + 'castle.browser.js' => %w[castle.browser.js castle.umd.js] + }.freeze skip_forgery_protection @@ -18,11 +23,13 @@ def show def resolved_file root = DIST.expand_path - candidate = root.join(params[:filename].to_s).expand_path - return unless candidate.to_s.start_with?("#{root}#{File::SEPARATOR}") - return unless candidate.file? - - candidate + names = ALIASES[params[:filename].to_s] || [params[:filename].to_s] + names.each do |name| + candidate = root.join(name).expand_path + next unless candidate.to_s.start_with?("#{root}#{File::SEPARATOR}") + return candidate if candidate.file? + end + nil end end end diff --git a/app/views/layouts/_castle_js.html.erb b/app/views/layouts/_castle_js.html.erb index f937508..c5d15e2 100644 --- a/app/views/layouts/_castle_js.html.erb +++ b/app/views/layouts/_castle_js.html.erb @@ -1,10 +1,17 @@ <% if ENV["CASTLE_PK"].present? %> - <%# Castle browser SDK. Mints the request token that ties the browser to the %> - <%# server-side risk/filter calls. See app/assets/javascripts/castle.js. %> - + <%# The 3.x UMD build is named @castleio/castle-js, so seed module.exports as window.Castle first. %> + + <% end %> diff --git a/package-lock.json b/package-lock.json index 7e8a8ef..6ca9f7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@castleio/castle-js": "^2.8.4" + "@castleio/castle-js": "^2.8.5" } }, "node_modules/@castleio/castle-js": { diff --git a/package.json b/package.json index 292d10b..d417aa7 100644 --- a/package.json +++ b/package.json @@ -9,6 +9,6 @@ }, "license": "MIT", "dependencies": { - "@castleio/castle-js": "^2.8.4" + "@castleio/castle-js": "^2.8.5" } } diff --git a/spec/controllers/vendor/castle_js_controller_spec.rb b/spec/controllers/vendor/castle_js_controller_spec.rb index 69683bc..5369c0c 100644 --- a/spec/controllers/vendor/castle_js_controller_spec.rb +++ b/spec/controllers/vendor/castle_js_controller_spec.rb @@ -20,5 +20,12 @@ expect(response).to have_http_status(:ok) expect(response.media_type).to eq('application/javascript') end + + it 'serves castle.umd.js from the npm install' do + get :show, params: { filename: 'castle.umd.js' } + + expect(response).to have_http_status(:ok) + expect(response.media_type).to eq('application/javascript') + end end end From f0c7585f1fb41f404ef55580655b18f5f4759798 Mon Sep 17 00:00:00 2001 From: Bartosz Date: Mon, 7 Sep 2026 18:35:50 +0200 Subject: [PATCH 2/2] Serve only the Castle UMD build from npm. --- app/controllers/vendor/castle_js_controller.rb | 17 +++++------------ package.json | 3 +++ scripts/ensure-castle-umd.js | 15 +++++++++++++++ .../vendor/castle_js_controller_spec.rb | 7 ------- 4 files changed, 23 insertions(+), 19 deletions(-) create mode 100644 scripts/ensure-castle-umd.js diff --git a/app/controllers/vendor/castle_js_controller.rb b/app/controllers/vendor/castle_js_controller.rb index 9d98710..03384dd 100644 --- a/app/controllers/vendor/castle_js_controller.rb +++ b/app/controllers/vendor/castle_js_controller.rb @@ -4,11 +4,6 @@ module Vendor # Serves the Castle browser SDK from the npm install (node_modules). class CastleJsController < ActionController::Base DIST = Rails.root.join('node_modules/@castleio/castle-js/dist') - # 2.x ships castle.browser.js; 3.x ships castle.umd.js. The HTML always requests castle.umd.js. - ALIASES = { - 'castle.umd.js' => %w[castle.umd.js castle.browser.js], - 'castle.browser.js' => %w[castle.browser.js castle.umd.js] - }.freeze skip_forgery_protection @@ -23,13 +18,11 @@ def show def resolved_file root = DIST.expand_path - names = ALIASES[params[:filename].to_s] || [params[:filename].to_s] - names.each do |name| - candidate = root.join(name).expand_path - next unless candidate.to_s.start_with?("#{root}#{File::SEPARATOR}") - return candidate if candidate.file? - end - nil + candidate = root.join(params[:filename].to_s).expand_path + return unless candidate.to_s.start_with?("#{root}#{File::SEPARATOR}") + return unless candidate.file? + + candidate end end end diff --git a/package.json b/package.json index d417aa7..a5cf03e 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,9 @@ "url": "git+https://github.com/castle/castle-ruby-example.git" }, "license": "MIT", + "scripts": { + "postinstall": "node scripts/ensure-castle-umd.js" + }, "dependencies": { "@castleio/castle-js": "^2.8.5" } diff --git a/scripts/ensure-castle-umd.js b/scripts/ensure-castle-umd.js new file mode 100644 index 0000000..6bd1f7f --- /dev/null +++ b/scripts/ensure-castle-umd.js @@ -0,0 +1,15 @@ +const fs = require('fs'); +const path = require('path'); + +const dist = path.join(__dirname, '..', 'node_modules', '@castleio', 'castle-js', 'dist'); +const dest = path.join(dist, 'castle.umd.js'); +if (!fs.existsSync(dist) || fs.existsSync(dest)) { + process.exit(0); +} + +const source = fs.readdirSync(dist).find((name) => ( + name.startsWith('castle.') && name.endsWith('.js') && name !== 'castle.js' +)); +if (source) { + fs.copyFileSync(path.join(dist, source), dest); +} diff --git a/spec/controllers/vendor/castle_js_controller_spec.rb b/spec/controllers/vendor/castle_js_controller_spec.rb index 5369c0c..fcdf4b9 100644 --- a/spec/controllers/vendor/castle_js_controller_spec.rb +++ b/spec/controllers/vendor/castle_js_controller_spec.rb @@ -14,13 +14,6 @@ expect(response).to have_http_status(:not_found) end - it 'serves castle.browser.js from the npm install' do - get :show, params: { filename: 'castle.browser.js' } - - expect(response).to have_http_status(:ok) - expect(response.media_type).to eq('application/javascript') - end - it 'serves castle.umd.js from the npm install' do get :show, params: { filename: 'castle.umd.js' }