inertia_rails 3.19.0 → 3.20.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/inertia_rails/configuration.rb +6 -0
- data/lib/inertia_rails/props_resolver.rb +2 -0
- data/lib/inertia_rails/ssr.rb +25 -0
- data/lib/inertia_rails/ssr_renderer.rb +6 -17
- data/lib/inertia_rails/version.rb +1 -1
- data/lib/inertia_rails.rb +3 -0
- data/lib/puma/plugin/inertia_ssr.rb +167 -0
- metadata +5 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e56b13d7d02abe1f35709de08b6873cc0d407215a97891502bb4ee7439e0d005
|
|
4
|
+
data.tar.gz: a627501dd94ab5d9ad747c2decce31ab47fe4870e677d2197395680cb4dfaa3e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5f9b35e5039a9516daaa7ff6d9c1b702011d3ddd2161fb40922143fde7951522ca3e8268691d2b3490baae217febb046b04d07d677facf39b81dad9f1f5bd076
|
|
7
|
+
data.tar.gz: 3523dadfe1c2877767cf534b4a6c95ef979250bbf3c78a86af31e6220247cb860d020402b359a32c8b5295b1d246aa30d8a5a590dd8fae44de89623b24521880
|
data/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## [3.20.0] - 2026-04-04
|
|
8
|
+
|
|
9
|
+
* Fix partial reload filtering with arrays
|
|
10
|
+
* Docs update for the v3.0 upgrade
|
|
11
|
+
* Add plugin for Puma so that SSR Just Works in production for apps using Puma
|
|
12
|
+
|
|
7
13
|
## [3.19.0] - 2026-03-25
|
|
8
14
|
|
|
9
15
|
* Upgrades to support Inertia.js 3.x! All thanks to @skyrukov!
|
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
module InertiaRails
|
|
4
4
|
class Configuration
|
|
5
|
+
DEFAULT_SSR_URL = 'http://localhost:13714'
|
|
6
|
+
|
|
5
7
|
DEFAULTS = {
|
|
6
8
|
# Whether to combine hashes with the same keys instead of replacing them.
|
|
7
9
|
deep_merge_shared_data: false,
|
|
@@ -24,6 +26,7 @@ module InertiaRails
|
|
|
24
26
|
|
|
25
27
|
# SSR options.
|
|
26
28
|
ssr_enabled: false,
|
|
29
|
+
# URL of the SSR server. When nil, defaults to DEFAULT_SSR_URL.
|
|
27
30
|
ssr_url: nil,
|
|
28
31
|
ssr_raise_on_error: false,
|
|
29
32
|
on_ssr_error: nil,
|
|
@@ -35,6 +38,9 @@ module InertiaRails
|
|
|
35
38
|
# Accepts true, false/nil, or a Hash of Rails.cache.fetch options.
|
|
36
39
|
# Lambdas are supported (instance_exec'd in controller context).
|
|
37
40
|
ssr_cache: nil,
|
|
41
|
+
# JavaScript runtime used to run the SSR bundle (e.g. "node", "bun", "deno").
|
|
42
|
+
# When nil, auto-detects from lockfiles or falls back to "node".
|
|
43
|
+
ssr_runtime: nil,
|
|
38
44
|
|
|
39
45
|
# Used to detect version drift between server and client.
|
|
40
46
|
version: nil,
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module InertiaRails
|
|
4
|
+
module SSR
|
|
5
|
+
class << self
|
|
6
|
+
def vite_dev_server_url
|
|
7
|
+
# vite_rails: TCP probe
|
|
8
|
+
if defined?(ViteRuby) && ViteRuby.instance.dev_server_running?
|
|
9
|
+
config = ViteRuby.config
|
|
10
|
+
return "#{config.protocol}://#{config.host_with_port}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# rails_vite + jsbundling: file-based
|
|
14
|
+
path = Rails.root.join('tmp/rails-vite.json')
|
|
15
|
+
JSON.parse(path.read)['url'] if path.exist?
|
|
16
|
+
rescue StandardError
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def vite_dev_server_running?
|
|
21
|
+
!vite_dev_server_url.nil?
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -29,7 +29,10 @@ module InertiaRails
|
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def request
|
|
32
|
-
|
|
32
|
+
uri = URI.parse(url)
|
|
33
|
+
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
|
|
34
|
+
http.post(uri.request_uri, page_json, 'Content-Type' => 'application/json')
|
|
35
|
+
end
|
|
33
36
|
|
|
34
37
|
unless response.is_a?(Net::HTTPSuccess)
|
|
35
38
|
body = begin
|
|
@@ -74,14 +77,14 @@ module InertiaRails
|
|
|
74
77
|
elsif (dev_url = vite_dev_server_url)
|
|
75
78
|
"#{dev_url}/__inertia_ssr"
|
|
76
79
|
else
|
|
77
|
-
|
|
80
|
+
"#{InertiaRails::Configuration::DEFAULT_SSR_URL}/render"
|
|
78
81
|
end
|
|
79
82
|
end
|
|
80
83
|
|
|
81
84
|
def vite_dev_server_url
|
|
82
85
|
return @vite_dev_server_url if defined?(@vite_dev_server_url)
|
|
83
86
|
|
|
84
|
-
@vite_dev_server_url =
|
|
87
|
+
@vite_dev_server_url = InertiaRails::SSR.vite_dev_server_url
|
|
85
88
|
end
|
|
86
89
|
|
|
87
90
|
def bundle_exists?
|
|
@@ -92,19 +95,5 @@ module InertiaRails
|
|
|
92
95
|
|
|
93
96
|
Array(bundle).any? { |path| File.exist?(path) }
|
|
94
97
|
end
|
|
95
|
-
|
|
96
|
-
def detect_vite_dev_url
|
|
97
|
-
# vite_rails: TCP probe, no metadata file
|
|
98
|
-
if defined?(ViteRuby) && ViteRuby.instance.dev_server_running?
|
|
99
|
-
config = ViteRuby.config
|
|
100
|
-
return "#{config.protocol}://#{config.host_with_port}"
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
# rails_vite + jsbundling: file-based
|
|
104
|
-
path = Rails.root.join('tmp/rails-vite.json')
|
|
105
|
-
JSON.parse(path.read)['url'] if path.exist?
|
|
106
|
-
rescue JSON::ParserError
|
|
107
|
-
nil
|
|
108
|
-
end
|
|
109
98
|
end
|
|
110
99
|
end
|
data/lib/inertia_rails.rb
CHANGED
|
@@ -27,6 +27,9 @@ require_relative 'inertia_rails/scroll_prop'
|
|
|
27
27
|
require_relative 'inertia_rails/prop_evaluator'
|
|
28
28
|
require_relative 'inertia_rails/props_resolver'
|
|
29
29
|
|
|
30
|
+
# ssr
|
|
31
|
+
require_relative 'inertia_rails/ssr'
|
|
32
|
+
|
|
30
33
|
# rendering
|
|
31
34
|
require_relative 'inertia_rails/meta_tag'
|
|
32
35
|
require_relative 'inertia_rails/meta_tag_builder'
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'net/http'
|
|
4
|
+
|
|
5
|
+
Puma::Plugin.create do
|
|
6
|
+
BOOT_TIMEOUT = 30 # rubocop:disable Lint/ConstantDefinitionInBlock
|
|
7
|
+
|
|
8
|
+
def start(launcher)
|
|
9
|
+
@log_writer = launcher.log_writer
|
|
10
|
+
@running = true
|
|
11
|
+
@boot_signal = Queue.new
|
|
12
|
+
|
|
13
|
+
in_background do
|
|
14
|
+
@boot_signal.pop
|
|
15
|
+
run_ssr_loop
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
events = launcher.events
|
|
19
|
+
|
|
20
|
+
if events.respond_to?(:after_booted)
|
|
21
|
+
events.after_booted { @boot_signal.push(true) }
|
|
22
|
+
events.after_stopped { stop_ssr }
|
|
23
|
+
events.before_restart { stop_ssr }
|
|
24
|
+
else
|
|
25
|
+
events.on_booted { @boot_signal.push(true) }
|
|
26
|
+
events.on_stopped { stop_ssr }
|
|
27
|
+
events.on_restart { stop_ssr }
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def run_ssr_loop
|
|
34
|
+
return unless ssr_enabled?
|
|
35
|
+
return if vite_dev_server_running?
|
|
36
|
+
|
|
37
|
+
bundle = resolve_bundle
|
|
38
|
+
return unless bundle
|
|
39
|
+
|
|
40
|
+
runtime = resolve_runtime
|
|
41
|
+
uri = URI(resolve_base_url)
|
|
42
|
+
consecutive_crashes = 0
|
|
43
|
+
delay = 1
|
|
44
|
+
|
|
45
|
+
while @running
|
|
46
|
+
break if vite_dev_server_running?
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
@pid = spawn(runtime, bundle)
|
|
50
|
+
rescue Errno::ENOENT
|
|
51
|
+
log "! Inertia SSR: '#{runtime}' not found in PATH"
|
|
52
|
+
break
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
if wait_for_server(uri)
|
|
56
|
+
log "* Inertia SSR: server ready (pid: #{@pid})"
|
|
57
|
+
consecutive_crashes = 0
|
|
58
|
+
delay = 1
|
|
59
|
+
else
|
|
60
|
+
log "! Inertia SSR: server failed to respond within #{BOOT_TIMEOUT}s"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
_, status = Process.wait2(@pid)
|
|
64
|
+
break unless @running
|
|
65
|
+
|
|
66
|
+
consecutive_crashes += 1
|
|
67
|
+
if consecutive_crashes >= 10
|
|
68
|
+
log '! Inertia SSR: too many crashes, giving up'
|
|
69
|
+
break
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
log "! Inertia SSR: process exited (status: #{status.exitstatus}), restarting in #{delay}s..."
|
|
73
|
+
sleep delay
|
|
74
|
+
delay = [delay * 2, 16].min
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def stop_ssr
|
|
79
|
+
@running = false
|
|
80
|
+
return unless @pid
|
|
81
|
+
|
|
82
|
+
Process.waitpid(@pid, Process::WNOHANG)
|
|
83
|
+
|
|
84
|
+
# /shutdown correctly handles cluster mode
|
|
85
|
+
begin
|
|
86
|
+
uri = URI(resolve_base_url)
|
|
87
|
+
Net::HTTP.start(uri.host, uri.port, open_timeout: 2, read_timeout: 5) do |http|
|
|
88
|
+
http.post('/shutdown', '')
|
|
89
|
+
end
|
|
90
|
+
Process.wait(@pid)
|
|
91
|
+
rescue StandardError
|
|
92
|
+
Process.kill('TERM', @pid)
|
|
93
|
+
Process.wait(@pid)
|
|
94
|
+
end
|
|
95
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
|
96
|
+
# already exited
|
|
97
|
+
ensure
|
|
98
|
+
@pid = nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def wait_for_server(uri) # rubocop:disable Naming/PredicateMethod
|
|
102
|
+
BOOT_TIMEOUT.times do
|
|
103
|
+
return false unless @running
|
|
104
|
+
|
|
105
|
+
begin
|
|
106
|
+
Net::HTTP.start(uri.host, uri.port, open_timeout: 1, read_timeout: 1) do |http|
|
|
107
|
+
http.get('/health')
|
|
108
|
+
end
|
|
109
|
+
return true
|
|
110
|
+
rescue Errno::ECONNREFUSED, Errno::EADDRNOTAVAIL, Net::OpenTimeout, Net::ReadTimeout
|
|
111
|
+
sleep 1
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
false
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def ssr_enabled?
|
|
118
|
+
InertiaRails.configuration.ssr_enabled
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def resolve_runtime
|
|
122
|
+
InertiaRails.configuration.ssr_runtime || detect_runtime
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def detect_runtime
|
|
126
|
+
root = defined?(Rails) ? Rails.root : Pathname.new(Dir.pwd)
|
|
127
|
+
if File.exist?(root.join('bun.lockb')) || File.exist?(root.join('bun.lock'))
|
|
128
|
+
'bun'
|
|
129
|
+
elsif File.exist?(root.join('deno.lock'))
|
|
130
|
+
'deno'
|
|
131
|
+
else
|
|
132
|
+
'node'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def resolve_bundle
|
|
137
|
+
config = InertiaRails.configuration
|
|
138
|
+
|
|
139
|
+
return Array(config.ssr_bundle).find { |path| File.exist?(path) } if config.ssr_bundle
|
|
140
|
+
|
|
141
|
+
if defined?(ViteRuby)
|
|
142
|
+
ssr_dir = ViteRuby.config.ssr_output_dir
|
|
143
|
+
candidates = Dir.glob(File.join(ssr_dir, 'inertia.*')) if ssr_dir
|
|
144
|
+
return candidates.first if candidates&.any?
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
path = 'public/assets-ssr/inertia.js'
|
|
148
|
+
path if File.exist?(path)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def resolve_base_url
|
|
152
|
+
url = InertiaRails.configuration.ssr_url
|
|
153
|
+
if url
|
|
154
|
+
url.sub(%r{/(render|__inertia_ssr)\z}, '')
|
|
155
|
+
else
|
|
156
|
+
InertiaRails::Configuration::DEFAULT_SSR_URL
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def vite_dev_server_running?
|
|
161
|
+
InertiaRails::SSR.vite_dev_server_running?
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def log(message)
|
|
165
|
+
@log_writer.log(message)
|
|
166
|
+
end
|
|
167
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: inertia_rails
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.20.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Brian Knoles
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
- Eugene Granovsky
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: railties
|
|
@@ -218,9 +218,11 @@ files:
|
|
|
218
218
|
- lib/inertia_rails/rspec/deprecated.rb
|
|
219
219
|
- lib/inertia_rails/scroll_metadata.rb
|
|
220
220
|
- lib/inertia_rails/scroll_prop.rb
|
|
221
|
+
- lib/inertia_rails/ssr.rb
|
|
221
222
|
- lib/inertia_rails/ssr_renderer.rb
|
|
222
223
|
- lib/inertia_rails/testing.rb
|
|
223
224
|
- lib/inertia_rails/version.rb
|
|
225
|
+
- lib/puma/plugin/inertia_ssr.rb
|
|
224
226
|
- lib/tasks/inertia_rails.rake
|
|
225
227
|
homepage: https://github.com/inertiajs/inertia-rails
|
|
226
228
|
licenses:
|
|
@@ -246,7 +248,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
246
248
|
- !ruby/object:Gem::Version
|
|
247
249
|
version: '0'
|
|
248
250
|
requirements: []
|
|
249
|
-
rubygems_version:
|
|
251
|
+
rubygems_version: 4.0.6
|
|
250
252
|
specification_version: 4
|
|
251
253
|
summary: Inertia.js adapter for Rails
|
|
252
254
|
test_files: []
|