wasmify-rails 0.3.1 → 0.4.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: 3ba6a11376e388d3ded351a2c72bfed1bab2063c2b4eb98d8c63f1a5d50214e4
4
- data.tar.gz: 8444b60b24fbfac498cf489c09ad0d4787552cee1e921cb3c9f5dece19643666
3
+ metadata.gz: 4888ee4d3efedd0bd93074f7856cf0be0789bad2ab092c460af84e79603bd0cf
4
+ data.tar.gz: 163b3dbd14d60250ab04f5e6c1d2b2e601b65e79f51b827bcd50e19cf0bb614a
5
5
  SHA512:
6
- metadata.gz: 653e2e65cbeb368db2da8b6c357e25d6b017750d906b08a2e60bd71e24c9b5f6410970223a5c13cb806a99b495eb3256b3e1c5964181f0ad0e51a0d85e2190d3
7
- data.tar.gz: 105c28242943f9055901e116fb4bac929028b225751d49f5745a3304683a8fb8cb7f8a917119aa63fa0bab1d127b7d96bd1ac2ff12dff3b31e053507549f7d28
6
+ metadata.gz: '00814ba133827f4b932fe3ee8bce0ff3096fe10ff7b41c28d10ac64353888d9aa0b4078b2c6572b082acf83c693abc7071dfd48eece93fd6d6f8e8e8942bb9b2'
7
+ data.tar.gz: 2ebed2d0efa0f87e3a8685be5799382ef20dc8b93c7817777c3eb8387e88617197a24a36a7931b8e5790fa6771706c4d689c9382ba12e50e1b9adefae08c2191
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.4.0
6
+
7
+ - Backport various patches from Rails tutorial.
8
+
9
+ ## 0.3.2
10
+
11
+ - Add `Rack::WASI::IncomingHandler` mimicking `wasi/http:proxy` interface.
12
+
5
13
  ## 0.3.1
6
14
 
7
15
  - Use latest patch versions for Ruby 3.3 and 3.4.
@@ -325,7 +325,7 @@ class ActiveRecord::ConnectionAdapters::NullDBAdapter < ActiveRecord::Connection
325
325
 
326
326
  def new_table_definition(adapter = nil, table_name = nil, is_temporary = nil, options = {})
327
327
  case ::ActiveRecord::VERSION::MAJOR
328
- when 6, 7
328
+ when 6, 7, 8
329
329
  TableDefinition.new(self, table_name, temporary: is_temporary, options: options.except(:id))
330
330
  when 5
331
331
  TableDefinition.new(table_name, is_temporary, options.except(:id), nil)
@@ -107,11 +107,19 @@ module ActiveRecord
107
107
  private attr_reader :js_interface
108
108
 
109
109
  def initialize(config)
110
- @js_interface = config.fetch(:js_interface, "pglite4rails").to_sym
110
+ @js_interface =
111
+ if config[:js_interface]
112
+ config[:js_interface]
113
+ else
114
+ # Set up the database in JS and get the idenfier back
115
+ JS.global[:pglite].create_interface(config[:database]).await.to_s
116
+ end.to_sym
111
117
  @last_result = nil
112
118
  @prepared_statements_map = {}
113
119
  end
114
120
 
121
+ def finished? = true
122
+
115
123
  def set_client_encoding(encoding)
116
124
  end
117
125
 
@@ -52,6 +52,10 @@ module ActiveRecord
52
52
 
53
53
  def busy_timeout(...) = nil
54
54
 
55
+ def busy_handler_timeout=(...)
56
+ nil
57
+ end
58
+
55
59
  def execute(sql)
56
60
  @last_statement = Statement.new(self, sql)
57
61
  @last_statement.result
@@ -0,0 +1,24 @@
1
+ module ActiveRecord
2
+ module Tasks # :nodoc:
3
+ class PGliteDatabaseTasks # :nodoc:
4
+ def self.using_database_configurations?
5
+ true
6
+ end
7
+
8
+ attr_reader :db_config, :configuration_hash
9
+
10
+ def initialize(db_config)
11
+ @db_config = db_config
12
+ @configuration_hash = db_config.configuration_hash
13
+ end
14
+
15
+ def create(connection_already_established = false)
16
+ JS.global[:pglite].create_interface(db_config.database).await
17
+ end
18
+
19
+ def purge(...)
20
+ # skip for now
21
+ end
22
+ end
23
+ end
24
+ end
@@ -19,7 +19,9 @@ module Rack
19
19
 
20
20
  request = Rack::Request.new(env)
21
21
 
22
- if request.post? || request.put? || request.patch?
22
+ if (
23
+ request.post? || request.put? || request.patch?
24
+ ) && request.get_header("HTTP_CONTENT_TYPE").match?(%r{multipart/form-data})
23
25
  transform_params(request.params)
24
26
  env["action_dispatch.request.request_parameters"] = request.params
25
27
  end
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "stringio"
4
+ require "uri"
5
+ require "base64"
6
+ require "rack"
7
+ require "rack/test"
8
+
9
+ require "rack/data_uri_uploads"
10
+
11
+ module Rack
12
+ module WASI
13
+ class Result < Data.define(:value, :error)
14
+ def tag = error ? "error" : "ok"
15
+ end
16
+
17
+ # resource incoming-request {
18
+ # method: func() -> method;
19
+ # path-with-query: func() -> option<string>;
20
+ # scheme: func() -> option<scheme>;
21
+ # authority: func() -> option<string>;
22
+ # headers: func() -> headers;
23
+ # consume: func() -> result<incoming-body>;
24
+ # }
25
+ # https://github.com/WebAssembly/wasi-http/blob/d163277b8684483a2334363ca1492ca298ea526d/wit/types.wit#L274
26
+ class IncomingRequest
27
+ # We use a reference to the global JS object to access the incoming request data.
28
+ def initialize(js_object_id)
29
+ @js_object = ::JS.global[js_object_id]
30
+ end
31
+
32
+ def method = @js_object.call(:method).to_s
33
+
34
+ def path_with_query
35
+ path = @js_object.call(:pathWithQuery)
36
+ if path.typeof == "string"
37
+ path.to_s
38
+ end
39
+ end
40
+
41
+ def scheme
42
+ sch = @js_object.call(:scheme)
43
+ if sch.typeof == "string"
44
+ sch.to_s
45
+ end
46
+ end
47
+
48
+ def authority
49
+ auth = @js_object.call(:authority)
50
+ if auth.typeof == "string"
51
+ auth.to_s
52
+ end
53
+ end
54
+
55
+ def headers
56
+ entries = ::JS.global[:Object].entries(@js_object.call(:headers))
57
+ entries.to_a.each.with_object({}) do |entry_val, acc|
58
+ key, val = entry_val.to_a
59
+ acc[key.to_s] = val.to_s
60
+ acc
61
+ end
62
+ end
63
+
64
+ # NOTE: Currently, we only support text bodies
65
+ def consume
66
+ body = @js_object.call(:consume)
67
+ if body.typeof == "string"
68
+ body.to_s
69
+ end
70
+ end
71
+ end
72
+
73
+ # resource response-outparam {
74
+ # set: static func(
75
+ # param: response-outparam,
76
+ # response: result<outgoing-response, error-code>,
77
+ # );
78
+ # }
79
+ #
80
+ # https://github.com/WebAssembly/wasi-http/blob/d163277b8684483a2334363ca1492ca298ea526d/wit/types.wit#L437
81
+ class ResponseOutparam
82
+ # We use a reference to the global JS object to access the incoming request data
83
+ def initialize(js_object_id)
84
+ @js_object = ::JS.global[js_object_id]
85
+ end
86
+
87
+ def set(result)
88
+ @js_object.call(:set, ::JS::Object.wrap(result))
89
+ end
90
+ end
91
+
92
+ # resource outgoing-response {
93
+ # constructor(headers: headers);
94
+ # status-code: func() -> status-code;
95
+ # set-status-code: func(status-code: status-code) -> result;
96
+ # headers: func() -> headers;
97
+ # body: func() -> result<outgoing-body>;
98
+ # }
99
+ #
100
+ # resource outgoing-body {
101
+ # write: func() -> result<output-stream>;
102
+ # finish: static func(
103
+ # this: outgoing-body,
104
+ # trailers: option<trailers>
105
+ # ) -> result<_, error-code>;
106
+ # }
107
+ # }
108
+ #
109
+ # https://github.com/WebAssembly/wasi-http/blob/d163277b8684483a2334363ca1492ca298ea526d/wit/types.wit#L572
110
+ class OutgoingResponse
111
+ attr_reader :status_code, :headers, :body
112
+
113
+ def initialize(headers:, status_code: 200)
114
+ @headers = headers
115
+ @status_code = status_code
116
+ @body = nil
117
+ end
118
+
119
+ def write(response_body)
120
+ @body = response_body
121
+ end
122
+ end
123
+
124
+ # wasi:http/proxy-like handler implementation for Rack apps
125
+ class IncomingHandler
126
+ private attr_reader :base_url
127
+
128
+ def initialize(app, base_url: "http://localhost:3000", skip_data_uri_uploads: false)
129
+ @app = app
130
+
131
+ @app = Rack::DataUriUploads.new(@app) unless skip_data_uri_uploads
132
+
133
+ @base_url = base_url
134
+ end
135
+
136
+ # Takes Wasi request, converts it to Rack request,
137
+ # calls the Rack app, and write Rack response back to the Wasi response.
138
+ #
139
+ # @param [Rack::WASI::HTTP::IncomingRequest] req
140
+ # @param [Rack::WASI::HTTP::ResponseOutparam] res
141
+ def handle(req, res)
142
+ uri = URI.join(base_url, req.path_with_query || "")
143
+ headers = req.headers.each.with_object({}) do |(key, value), headers|
144
+ headers["HTTP_#{key.upcase.gsub("-", "_")}"] = value
145
+ headers
146
+ end
147
+
148
+ http_method = req.method.upcase
149
+ headers[:method] = http_method
150
+
151
+ body = req.consume
152
+ headers[:input] = StringIO.new(body) if body
153
+
154
+ request = Rack::MockRequest.env_for(uri.to_s, headers)
155
+ begin
156
+ response = Rack::Response[*@app.call(request)]
157
+ response_status, response_headers, bodyiter = *response.finish
158
+
159
+ out_response = OutgoingResponse.new(headers: response_headers, status_code: response_status)
160
+
161
+ body = ""
162
+ body_is_set = false
163
+
164
+ bodyiter.each do |part|
165
+ body += part
166
+ body_is_set = true
167
+ end
168
+
169
+ # Serve images as base64 from Ruby and decode back in JS
170
+ # FIXME: extract into a separate middleware and add a header to indicate the transformation
171
+ if response_headers["Content-Type"]&.start_with?("image/")
172
+ body = Base64.strict_encode64(body)
173
+ end
174
+
175
+ out_response.write(body) if body_is_set
176
+ res.set(Result.new(out_response, nil))
177
+ rescue Exception => e
178
+ res.set(Result.new(e.message, 503))
179
+ end
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,16 @@
1
+ module Rackup
2
+ module Handler
3
+ class WASIServer
4
+ def self.run(app, **options)
5
+ require "rack/wasi/incoming_handler"
6
+ port = options[:Port]
7
+
8
+ $incoming_handler = ::Rack::WASI::IncomingHandler.new(app)
9
+
10
+ ::Wasmify::ExternalCommands.server(port)
11
+ end
12
+ end
13
+
14
+ register :wasi, WASIServer
15
+ end
16
+ end
@@ -0,0 +1,23 @@
1
+ module Wasmify
2
+ module ExternalCommands
3
+ class << self
4
+ attr_reader :command
5
+
6
+ def register(*names)
7
+ names.each do |name|
8
+ module_eval <<~RUBY, __FILE__, __LINE__ + 1
9
+ def self.#{name}(...)
10
+ raise ArgumentError, "Command has been already defined: #{command}" if command
11
+
12
+ ::JS.global[:externalCommands].#{name}(...)
13
+
14
+ @command = :#{name}
15
+ end
16
+ RUBY
17
+ end
18
+ end
19
+
20
+ def any? = !!command
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,40 @@
1
+ # Use TracePoint to wait for a particular class to load,
2
+ # so we can apply a patch right away
3
+ module Wasmify
4
+ module Patcha
5
+ class << self
6
+ def on_load(name, &callback)
7
+ callbacks[name] = callback
8
+ end
9
+
10
+ def on_class(event)
11
+ # Ignore singletons
12
+ return if event.self.singleton_class?
13
+
14
+ class_name = name_method.bind_call(event.self)
15
+
16
+ return unless callbacks[class_name]
17
+
18
+ clbk = callbacks.delete(class_name)
19
+ tracer.disable if callbacks.empty?
20
+
21
+ clbk.call
22
+ end
23
+
24
+ def setup!
25
+ return if callbacks.empty?
26
+
27
+ @tracer = TracePoint.new(:end, &method(:on_class))
28
+ tracer.enable
29
+ # Use `Module#name` instead of `self.name` to handle overwritten `name` method
30
+ @name_method = Module.instance_method(:name)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :tracer, :name_method
36
+
37
+ def callbacks = @callbacks ||= {}
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ Wasmify::Patcha.on_load("ActionText::PlainTextConversion") do
4
+ module ActionText::PlainTextConversion
5
+ def plain_text_for_node(node)
6
+ html = node.to_html
7
+ # FIXME: use external interface?
8
+ html.gsub(/<[^>]*>/, " ").strip
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,35 @@
1
+ Wasmify::Patcha.on_load("Rails::Server") do
2
+ Rails::Server.prepend(Module.new do
3
+ def initialize(options)
4
+ # disable pid files
5
+ options.delete(:pid)
6
+ super
7
+ end
8
+
9
+ # Change the after_stop_callback logic
10
+ def start(after_stop_callback = nil)
11
+ Kernel.at_exit(&after_stop_callback) if after_stop_callback
12
+ super()
13
+ end
14
+ end)
15
+ end
16
+
17
+ Wasmify::Patcha.on_load("Rails::Generators::Actions") do
18
+ Rails::Generators::Actions.prepend(Module.new do
19
+ # Always run Rails commands inline (we cannot spawn new processes)
20
+ def rails_command(command, options = {})
21
+ super(command, options.merge(inline: true))
22
+ end
23
+ end)
24
+ end
25
+
26
+ Wasmify::Patcha.on_load("Rails::Console::IRBConsole") do
27
+ Rails::Console::IRBConsole.prepend(Module.new do
28
+ def start
29
+ # Disable default IRB behaviour but keep the configuration around
30
+ ::IRB::Irb.prepend(Module.new { def run(*); end })
31
+ super
32
+ ::Wasmify::ExternalCommands.console
33
+ end
34
+ end)
35
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "wasmify/patcha"
4
+
5
+ require "wasmify/rails/patches/rails"
6
+ require "wasmify/rails/patches/action_text"
7
+
8
+ Wasmify::Patcha.setup!
@@ -16,7 +16,6 @@ module Wasmify
16
16
  end
17
17
  end
18
18
 
19
-
20
19
  require "action_mailer/null_delivery"
21
20
 
22
21
  # NullDB for Active Record
@@ -27,3 +26,6 @@ ActiveRecord::ConnectionAdapters.register("sqlite3_wasm", "ActiveRecord::Connect
27
26
 
28
27
  # PGlite adapter
29
28
  ActiveRecord::ConnectionAdapters.register("pglite", "ActiveRecord::ConnectionAdapters::PGliteAdapter", "active_record/connection_adapters/pglite_adapter")
29
+
30
+ require "active_record/tasks/pglite_database_tasks"
31
+ ActiveRecord::Tasks::DatabaseTasks.register_task(/pglite/, "ActiveRecord::Tasks::PGliteDatabaseTasks")
@@ -19,11 +19,40 @@ require "bundler"
19
19
  # Load core classes and deps patches
20
20
  $LOAD_PATH.unshift File.expand_path("shims", __dir__)
21
21
 
22
+ # Prevent features:
23
+ # - `bundler/setup` — we do that manually via `/bundle/setup`#
24
+ %w[
25
+ bundler/setup
26
+ ].each do |feature|
27
+ $LOAD_PATH.resolve_feature_path(feature)&.then { $LOADED_FEATURES << _1[1] }
28
+ end
29
+
22
30
  # Misc patches
23
31
 
32
+ # Stub drb/unix for test parallelization
33
+ $LOADED_FEATURES << $LOAD_PATH.resolve_feature_path("drb/unix")[1]
34
+
24
35
  # Make gem no-op
25
36
  define_singleton_method(:gem) { |*| nil }
26
37
 
38
+ module FileUtils
39
+ # Touch doesn't work for some reason
40
+ def self.touch(path) = File.write(path, "")
41
+ end
42
+
43
+ # Support Kernel.at_exit
44
+ module Kernel
45
+ @@at_exit_hooks = []
46
+
47
+ def at_exit(&block)
48
+ @@at_exit_hooks << block
49
+ end
50
+
51
+ def execute_at_exit_hooks
52
+ @@at_exit_hooks.reverse_each { _1.call }
53
+ end
54
+ end
55
+
27
56
  # Patch Bundler.require to simply require files without looking at specs
28
57
  def Bundler.require(*groups)
29
58
  Bundler.definition.dependencies_for([:wasm]).each do |dep|
@@ -60,6 +89,7 @@ class Thread
60
89
  def self.new(...)
61
90
  f = Fiber.new(...)
62
91
  def f.value = resume
92
+ def f.join = value
63
93
  f
64
94
  end
65
95
  end
@@ -0,0 +1,165 @@
1
+ # This is a minimal Nokogiri stub to make Action Text basic features work (no embeddings)
2
+ module Nokogiri
3
+ module XML
4
+ class DocumentFragment
5
+ end
6
+
7
+ module Node
8
+ module SaveOptions
9
+ AS_HTML = 1
10
+ end
11
+ end
12
+ end
13
+
14
+ module HTML5
15
+ class Document
16
+ def initialize
17
+ # TODO
18
+ end
19
+
20
+ def encoding=(enc)
21
+ end
22
+
23
+ def fragment(html)
24
+ DocumentFragment.new(html)
25
+ end
26
+
27
+ def create_element(tag_name, attributes = {})
28
+ # Create an element with given tag name and attributes
29
+ end
30
+ end
31
+
32
+ class DocumentFragment < XML::DocumentFragment
33
+ attr_reader :elements, :name
34
+
35
+ def initialize(html_string)
36
+ @html_string = html_string
37
+ @elements = []
38
+ @name = html_string.match(/<(\w+)/).then { next unless _1; _1[1] }
39
+ end
40
+
41
+ def css(selector)
42
+ # Return array of matching nodes
43
+ # Must support selectors like:
44
+ # - "a[href]" - anchor tags with href attribute
45
+ # - "action-text-attachment" - elements by tag name
46
+ # - Complex selectors for attachment galleries
47
+ []
48
+ end
49
+
50
+ def children
51
+ []
52
+ end
53
+
54
+ def dup
55
+ self.class.new(@html_string)
56
+ end
57
+
58
+ def to_html(options = {})
59
+ # Must respect options[:save_with] if provided
60
+ @html_string
61
+ end
62
+
63
+ def elements
64
+ self
65
+ end
66
+
67
+ def deconstruct
68
+ children
69
+ end
70
+ end
71
+
72
+ class Node
73
+ attr_accessor :parent
74
+
75
+ def initialize(name, attributes = {})
76
+ @name = name
77
+ @attributes = attributes
78
+ @children = []
79
+ @text = ""
80
+ end
81
+
82
+ # Node type and content
83
+ def name
84
+ @name # e.g., "p", "div", "text", etc.
85
+ end
86
+
87
+ def text
88
+ # Return text content (for text nodes) or aggregate text of children
89
+ end
90
+
91
+ def text?
92
+ # Return true if this is a text node
93
+ name == "text" || name == "#text"
94
+ end
95
+
96
+ # Attribute access
97
+ def [](attribute_name)
98
+ # Get attribute value
99
+ @attributes[attribute_name]
100
+ end
101
+
102
+ def []=(attribute_name, value)
103
+ # Set attribute value
104
+ @attributes[attribute_name] = value
105
+ end
106
+
107
+ def key?(attribute_name)
108
+ # Check if attribute exists
109
+ @attributes.key?(attribute_name)
110
+ end
111
+
112
+ def remove_attribute(attribute_name)
113
+ # Remove attribute and return its value
114
+ @attributes.delete(attribute_name)
115
+ end
116
+
117
+ # DOM traversal
118
+ def children
119
+ # Return array of child nodes
120
+ @children
121
+ end
122
+
123
+ def ancestors
124
+ # Return array of ancestor nodes (parent, grandparent, etc.)
125
+ result = []
126
+ node = parent
127
+ while node
128
+ result << node
129
+ node = node.parent
130
+ end
131
+ result
132
+ end
133
+
134
+ # DOM manipulation
135
+ def replace(replacement)
136
+ # Replace this node with the replacement (string or node)
137
+ # If replacement is a string, parse it as HTML
138
+ end
139
+
140
+ # CSS matching
141
+ def matches?(selector)
142
+ # Check if this node matches the given CSS selector
143
+ end
144
+
145
+ # HTML output
146
+ def to_html(options = {})
147
+ # Convert to HTML string
148
+ end
149
+
150
+ def to_s
151
+ to_html
152
+ end
153
+
154
+ def inspect
155
+ # For debugging
156
+ "#<Node:#{name} #{@attributes.inspect}>"
157
+ end
158
+ end
159
+ end
160
+
161
+ module HTML4
162
+ Document = HTML5::Document
163
+ DocumentFragment = HTML5::DocumentFragment
164
+ end
165
+ end
@@ -138,6 +138,9 @@ module Rails
138
138
  def full_sanitizer = self
139
139
  def link_sanitizer = self
140
140
  def safe_list_sanitizer = self
141
+ def allowed_tags = []
142
+ def allowed_attributes = []
143
+ def allowed_styles = []
141
144
  end
142
145
 
143
146
  def sanitize(html, ...) = html
@@ -9,6 +9,10 @@ end
9
9
  class Socket < BasicSocket
10
10
  AF_UNSPEC = 0
11
11
  AF_INET = 1
12
+
13
+ def self.gethostname
14
+ "localhost"
15
+ end
12
16
  end
13
17
 
14
18
  class IPSocket < Socket
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Wasmify
4
4
  module Rails # :nodoc:
5
- VERSION = "0.3.1"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wasmify-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vladimir Dementyev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-05-07 00:00:00.000000000 Z
11
+ date: 2025-06-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -137,6 +137,7 @@ files:
137
137
  - lib/active_record/connection_adapters/pglite_adapter.rb
138
138
  - lib/active_record/connection_adapters/pglite_shims/pg.rb
139
139
  - lib/active_record/connection_adapters/sqlite3_wasm_adapter.rb
140
+ - lib/active_record/tasks/pglite_database_tasks.rb
140
141
  - lib/generators/wasmify/install/USAGE
141
142
  - lib/generators/wasmify/install/install_generator.rb
142
143
  - lib/generators/wasmify/install/templates/config/environments/wasm.rb
@@ -153,10 +154,17 @@ files:
153
154
  - lib/generators/wasmify/pwa/templates/pwa/vite.config.js
154
155
  - lib/image_processing/null.rb
155
156
  - lib/rack/data_uri_uploads.rb
157
+ - lib/rack/wasi/incoming_handler.rb
158
+ - lib/rackup/handler/wasi.rb
156
159
  - lib/wasmify-rails.rb
160
+ - lib/wasmify/external_commands.rb
161
+ - lib/wasmify/patcha.rb
157
162
  - lib/wasmify/rails/builder.rb
158
163
  - lib/wasmify/rails/configuration.rb
159
164
  - lib/wasmify/rails/packer.rb
165
+ - lib/wasmify/rails/patches.rb
166
+ - lib/wasmify/rails/patches/action_text.rb
167
+ - lib/wasmify/rails/patches/rails.rb
160
168
  - lib/wasmify/rails/railtie.rb
161
169
  - lib/wasmify/rails/shim.rb
162
170
  - lib/wasmify/rails/shims/io/console.rb