wasmify-rails 0.1.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +7 -0
  3. data/LICENSE.txt +23 -0
  4. data/README.md +168 -0
  5. data/lib/active_record/connection_adapters/nulldb_adapter/checkpoint.rb +13 -0
  6. data/lib/active_record/connection_adapters/nulldb_adapter/column.rb +4 -0
  7. data/lib/active_record/connection_adapters/nulldb_adapter/configuration.rb +5 -0
  8. data/lib/active_record/connection_adapters/nulldb_adapter/core.rb +391 -0
  9. data/lib/active_record/connection_adapters/nulldb_adapter/empty_result.rb +41 -0
  10. data/lib/active_record/connection_adapters/nulldb_adapter/index_definition.rb +5 -0
  11. data/lib/active_record/connection_adapters/nulldb_adapter/null_object.rb +13 -0
  12. data/lib/active_record/connection_adapters/nulldb_adapter/quoting.rb +3 -0
  13. data/lib/active_record/connection_adapters/nulldb_adapter/statement.rb +15 -0
  14. data/lib/active_record/connection_adapters/nulldb_adapter/table_definition.rb +23 -0
  15. data/lib/active_record/connection_adapters/nulldb_adapter.rb +67 -0
  16. data/lib/active_record/connection_adapters/sqlite3_wasm_adapter.rb +163 -0
  17. data/lib/active_storage/null_delivery.rb +15 -0
  18. data/lib/generators/wasmify/install/USAGE +5 -0
  19. data/lib/generators/wasmify/install/install_generator.rb +73 -0
  20. data/lib/generators/wasmify/install/templates/config/environments/wasm.rb +30 -0
  21. data/lib/generators/wasmify/install/templates/config/wasmify.yml +17 -0
  22. data/lib/generators/wasmify/pwa/USAGE +5 -0
  23. data/lib/generators/wasmify/pwa/pwa_generator.rb +13 -0
  24. data/lib/generators/wasmify/pwa/templates/pwa/README.md +40 -0
  25. data/lib/generators/wasmify/pwa/templates/pwa/boot.html +102 -0
  26. data/lib/generators/wasmify/pwa/templates/pwa/boot.js +96 -0
  27. data/lib/generators/wasmify/pwa/templates/pwa/database.js +18 -0
  28. data/lib/generators/wasmify/pwa/templates/pwa/index.html +2 -0
  29. data/lib/generators/wasmify/pwa/templates/pwa/package.json.tt +20 -0
  30. data/lib/generators/wasmify/pwa/templates/pwa/rails.sw.js +115 -0
  31. data/lib/generators/wasmify/pwa/templates/pwa/vite.config.js +29 -0
  32. data/lib/image_processing/null.rb +22 -0
  33. data/lib/wasmify/rails/builder.rb +45 -0
  34. data/lib/wasmify/rails/configuration.rb +41 -0
  35. data/lib/wasmify/rails/packer.rb +60 -0
  36. data/lib/wasmify/rails/railtie.rb +11 -0
  37. data/lib/wasmify/rails/shim.rb +65 -0
  38. data/lib/wasmify/rails/shims/io/console/size.rb +0 -0
  39. data/lib/wasmify/rails/shims/io/console.rb +11 -0
  40. data/lib/wasmify/rails/shims/io/wait.rb +0 -0
  41. data/lib/wasmify/rails/shims/ipaddr.rb +7 -0
  42. data/lib/wasmify/rails/shims/nio.rb +0 -0
  43. data/lib/wasmify/rails/shims/nokogiri/nokogiri.rb +0 -0
  44. data/lib/wasmify/rails/shims/nokogiri.rb +0 -0
  45. data/lib/wasmify/rails/shims/rails-html-sanitizer.rb +163 -0
  46. data/lib/wasmify/rails/shims/socket.rb +21 -0
  47. data/lib/wasmify/rails/shims/sqlite3.rb +0 -0
  48. data/lib/wasmify/rails/tasks.rake +120 -0
  49. data/lib/wasmify/rails/version.rb +7 -0
  50. data/lib/wasmify/rails/wasmtimer.rb +31 -0
  51. data/lib/wasmify-rails.rb +25 -0
  52. metadata +200 -0
@@ -0,0 +1,15 @@
1
+ class ActiveRecord::ConnectionAdapters::NullDBAdapter
2
+
3
+ class Statement
4
+ attr_reader :entry_point, :content
5
+
6
+ def initialize(entry_point, content = "")
7
+ @entry_point, @content = entry_point, content
8
+ end
9
+
10
+ def ==(other)
11
+ self.entry_point == other.entry_point
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,23 @@
1
+ class ActiveRecord::ConnectionAdapters::NullDBAdapter
2
+
3
+ class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition
4
+ attr_accessor :name
5
+ alias_method :enum, :string
6
+ alias_method :uuid, :string
7
+ alias_method :citext, :text
8
+ alias_method :interval, :text
9
+ alias_method :geometry, :text
10
+ alias_method :serial, :integer
11
+ alias_method :bigserial, :integer
12
+ alias_method :inet, :string
13
+ alias_method :jsonb, :json if method_defined? :json
14
+ alias_method :hstore, :json
15
+
16
+ if ::ActiveRecord::VERSION::MAJOR == 7 && ::ActiveRecord::VERSION::MINOR >= 1
17
+ # Avoid check for option validity
18
+ def create_column_definition(name, type, options)
19
+ ActiveRecord::ConnectionAdapters::ColumnDefinition.new(name, type, options)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,67 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+ require 'stringio'
5
+ require 'singleton'
6
+ require 'pathname'
7
+
8
+ require 'active_support'
9
+ require 'active_support/deprecation'
10
+ require 'active_record/connection_adapters/nulldb_adapter'
11
+
12
+ module NullDB
13
+ class Configuration < Struct.new(:project_root); end
14
+
15
+ class << self
16
+ def configure
17
+ @configuration = Configuration.new.tap {|c| yield c}
18
+ end
19
+
20
+ def configuration
21
+ if @configuration.nil?
22
+ raise "NullDB not configured. Require a framework, ex 'nulldb/rails'"
23
+ end
24
+
25
+ @configuration
26
+ end
27
+
28
+ def nullify(options={})
29
+ begin
30
+ @prev_connection = ActiveRecord::Base.connection_pool.try(:spec)
31
+ rescue ActiveRecord::ConnectionNotEstablished
32
+ end
33
+ ActiveRecord::Base.establish_connection(options.merge(:adapter => :nulldb))
34
+ end
35
+
36
+ def restore
37
+ if @prev_connection
38
+ ActiveRecord::Base.establish_connection(@prev_connection.config)
39
+ end
40
+ end
41
+
42
+ def checkpoint
43
+ ActiveRecord::Base.connection.checkpoint!
44
+ end
45
+ end
46
+ end
47
+
48
+ # Need to defer calling Rails.root because when bundler loads, Rails.root is nil
49
+ NullDB.configure {|ndb| def ndb.project_root;Rails.root;end}
50
+
51
+ class ActiveRecord::Base
52
+ # Instantiate a new NullDB connection. Used by ActiveRecord internally.
53
+ def self.nulldb_connection(config)
54
+ ActiveRecord::ConnectionAdapters::NullDBAdapter.new(config)
55
+ end
56
+ end
57
+
58
+ require 'active_record/connection_adapters/nulldb_adapter/core'
59
+ require 'active_record/connection_adapters/nulldb_adapter/statement'
60
+ require 'active_record/connection_adapters/nulldb_adapter/checkpoint'
61
+ require 'active_record/connection_adapters/nulldb_adapter/column'
62
+ require 'active_record/connection_adapters/nulldb_adapter/configuration'
63
+ require 'active_record/connection_adapters/nulldb_adapter/empty_result'
64
+ require 'active_record/connection_adapters/nulldb_adapter/index_definition'
65
+ require 'active_record/connection_adapters/nulldb_adapter/null_object'
66
+ require 'active_record/connection_adapters/nulldb_adapter/table_definition'
67
+ require 'active_record/connection_adapters/nulldb_adapter/quoting'
@@ -0,0 +1,163 @@
1
+ require "active_record/connection_adapters/sqlite3_adapter"
2
+
3
+ module SQLite3
4
+ module Database
5
+ def self.quote(s) = s.gsub("'", "''")
6
+ end
7
+
8
+ module Pragmas
9
+ end
10
+ end
11
+
12
+ module ActiveRecord
13
+ module ConnectionHandling # :nodoc:
14
+ def sqlite3_wasm_adapter_class
15
+ ConnectionAdapters::SQLite3WasmAdapter
16
+ end
17
+
18
+ def sqlite3_wasm_connection(config)
19
+ sqlite3_wasm_adapter_class.new(config)
20
+ end
21
+ end
22
+
23
+ module ConnectionAdapters
24
+ class SQLite3WasmAdapter < SQLite3Adapter
25
+ class ExternalInterface
26
+ private attr_reader :js_interface
27
+
28
+ def initialize(config)
29
+ @js_interface = config.fetch(:js_interface, "sqlite4rails").to_sym
30
+ end
31
+
32
+ def exec(...)
33
+ JS.global[js_interface].exec(...)
34
+ end
35
+
36
+ def busy_timeout(...) = nil
37
+
38
+ def execute(sql)
39
+ @last_statement = Statement.new(self, sql)
40
+ @last_statement.result
41
+ end
42
+
43
+ def prepare(sql) = Statement.new(self, sql)
44
+
45
+ def closed? = false
46
+
47
+ def transaction(mode = nil)
48
+ # deferred doesn't work 🤷‍♂️
49
+ mode = nil if mode.nil?
50
+ execute "begin #{mode} transaction"
51
+
52
+ if block_given?
53
+ abort = false
54
+ begin
55
+ yield self
56
+ rescue
57
+ abort = true
58
+ raise
59
+ ensure
60
+ abort and rollback or commit
61
+ end
62
+ else
63
+ true
64
+ end
65
+ end
66
+
67
+ def commit
68
+ execute "commit transaction"
69
+ true
70
+ end
71
+
72
+ def rollback
73
+ execute "rollback transaction"
74
+ true
75
+ end
76
+
77
+ def changes
78
+ JS.global[js_interface].changes.to_i
79
+ end
80
+ end
81
+
82
+ class Statement
83
+ attr_reader :interface, :sql
84
+
85
+ def initialize(interface, sql)
86
+ @interface = interface
87
+ @sql = sql
88
+ @columns = nil
89
+ @rows = nil
90
+ end
91
+
92
+ def close = nil
93
+
94
+ def columns
95
+ execute
96
+ @columns
97
+ end
98
+
99
+ def result
100
+ execute
101
+ @rows.map { @columns.zip(_1).to_h }.freeze
102
+ end
103
+
104
+ def to_a
105
+ execute
106
+ @rows
107
+ end
108
+
109
+ def execute
110
+ return if @rows
111
+
112
+ res = interface.exec(sql)
113
+ # unwrap column names
114
+ cols = res[:cols].to_a.map(&:to_s)
115
+ # unwrap row values
116
+ rows = res[:rows].to_a.map do |row|
117
+ row.to_a.map do |val|
118
+ str_val = val.to_s
119
+ next str_val if val.typeof == "string"
120
+ next str_val == "true" if val.typeof == "boolean"
121
+
122
+ # handle integers and floats
123
+ next str_val.include?(".") ? val.to_f : val.to_i if val.typeof == "number"
124
+
125
+ str_val
126
+ end
127
+ end
128
+
129
+ @columns = cols
130
+ @rows = rows
131
+ end
132
+
133
+ def reset!
134
+ @rows = nil
135
+ @columns = nil
136
+ end
137
+ end
138
+
139
+ class << self
140
+ def database_exists?(config)
141
+ true
142
+ end
143
+
144
+ def new_client(config) = ExternalInterface.new(config)
145
+ end
146
+
147
+ attr_reader :external_interface
148
+
149
+ def initialize(...)
150
+ AbstractAdapter.instance_method(:initialize).bind_call(self, ...)
151
+
152
+ @prepared_statements = false
153
+ @memory_database = false
154
+ @connection_parameters = @config.merge(database: @config[:database].to_s, results_as_hash: true)
155
+ @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true
156
+ end
157
+
158
+ def database_exists? = true
159
+
160
+ def database_version = SQLite3Adapter::Version.new("3.45.1")
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionMailer
4
+ class NullDeliveryMethod
5
+ def initialize(*)
6
+ end
7
+
8
+ def deliver!(_)
9
+ end
10
+ end
11
+ end
12
+
13
+ ActiveSupport.on_load(:action_mailer) do
14
+ ActionMailer::Base.add_delivery_method :null
15
+ end
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Configures the application for Wasm-ification
3
+
4
+ Example:
5
+ bin/rails generate wasmify:install
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Wasmify::InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def copy_files
7
+ template "config/wasmify.yml"
8
+ template "config/environments/wasm.rb"
9
+ end
10
+
11
+ def configure_database
12
+ append_to_file "config/database.yml", <<~EOF
13
+ wasm:
14
+ adapter: <%= ENV.fetch("ACTIVE_RECORD_ADAPTER") { "nulldb" } %>
15
+ EOF
16
+ end
17
+
18
+ def configure_action_cable
19
+ in_root do
20
+ next unless File.file?("config/cable.yml")
21
+
22
+ append_to_file "config/cable.yml", <<~EOF
23
+
24
+ wasm:
25
+ adapter: <%= ENV.fetch("ACTION_CABLE_ADAPTER") { "inline" } %>
26
+ EOF
27
+ end
28
+ end
29
+
30
+ def configure_gitignore
31
+ append_to_file ".gitignore", <<~EOF
32
+ # Ignore the compiled WebAssembly modules
33
+ *.wasm
34
+ # Ignore ruby.wasm build artefacts
35
+ build/
36
+ rubies/
37
+ dist/
38
+ EOF
39
+ end
40
+
41
+ def inject_wasmify_shim_into_environment
42
+ inject_into_file "config/application.rb", after: /require_relative\s+['"]boot['"]\n/ do
43
+ <<~RUBY
44
+
45
+ require "wasmify/rails/shim"
46
+ RUBY
47
+ end
48
+ end
49
+
50
+ def skip_bundler_setup_in_boot
51
+ inject_into_file "config/boot.rb", after: /require ['"]bundler\/setup['"]/ do
52
+ " unless RUBY_PLATFORM =~ /wasm/"
53
+ end
54
+ end
55
+
56
+ def add_tzinfo_data_to_gemfile
57
+ append_to_file "Gemfile", <<~RUBY
58
+
59
+ group :wasm do
60
+ gem "tzinfo-data"
61
+ end
62
+ RUBY
63
+
64
+ run "bundle install"
65
+ end
66
+
67
+ def finish
68
+ say_status :info, "✅ The application is prepared for Wasm-ificaiton!\n\n" \
69
+ "Next steps are:\n" \
70
+ " - Gemfile: Add `group: [:default, :wasm]` to the dependencies required in Wasm runtime" \
71
+ " - Run `bin/rails wasmify:build:core:verify` to see if the bundle compiles"
72
+ end
73
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "production"
4
+
5
+ Rails.application.configure do
6
+ config.enable_reloading = false
7
+
8
+ config.assume_ssl = false
9
+ config.force_ssl = false
10
+
11
+ # FIXME: Tags are not being reset right now
12
+ config.log_tags = []
13
+
14
+ if ENV["DEBUG"] == "1"
15
+ config.consider_all_requests_local = true
16
+ config.action_dispatch.show_exceptions = :none
17
+ config.log_level = :debug
18
+ config.logger = Logger.new($stdout)
19
+ end
20
+
21
+ config.cache_store = :memory_store
22
+ config.active_job.queue_adapter = :inline
23
+ config.action_mailer.delivery_method = :null
24
+
25
+ if config.respond_to?(:active_storage)
26
+ config.active_storage.variant_processor = :null
27
+ end
28
+
29
+ config.secret_key_base = "<change-me>"
30
+ end
@@ -0,0 +1,17 @@
1
+ # Specify the list of directories to be included into the final
2
+ # app.wasm file.
3
+ pack_directories:
4
+ - app
5
+ - lib
6
+ - config
7
+ - db
8
+ - public
9
+
10
+ # Specify the list of gems to skip during the base module compiliation.
11
+ # Usually, you want to specify the gems with native extensions that are
12
+ # not currently Wasm-compatible.
13
+ exclude_gems:
14
+ - bigdecimal
15
+ - nio4r
16
+ - io-console
17
+ - psych
@@ -0,0 +1,5 @@
1
+ Description:
2
+ Creates a new PWA JS application to server the Wasm version of the app.
3
+
4
+ Example:
5
+ bin/rails generate wasmify:pwa
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Wasmify::PwaGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("templates", __dir__)
5
+
6
+ def copy_files
7
+ directory "pwa", "pwa"
8
+ end
9
+
10
+ private
11
+
12
+ def wasmify_rails_version = Wasmify::Rails::VERSION
13
+ end
@@ -0,0 +1,40 @@
1
+ # PWA app to run a Rails Wasm app
2
+
3
+ The app provides a Service Worker to serve requests through the Rails/Wasm app.
4
+
5
+ ## Running locallly
6
+
7
+ ```sh
8
+ yarn install
9
+
10
+ yarn dev
11
+ ```
12
+
13
+ Then go to [http://localhost:5173](http://localhost:5173).
14
+
15
+ > [!NOTE]
16
+ > Use Chrome or another browser supporting [CookieStore API](https://caniuse.com/?search=cookiestore).
17
+
18
+ ## Serving via HTTPS
19
+
20
+ Although `localhost` should work fine for development, you can also run the app over `https://` for a more production-like experience (and to attach a worker to a custom domain).
21
+
22
+ For that, we recommend using [puma-dev](https://github.com/puma/puma-dev).
23
+
24
+ Install `puma-dev` and add the port 5173 to its configuration:
25
+
26
+ ```sh
27
+ echo "5173" > ~/.puma-dev/rails-wasm
28
+ ```
29
+
30
+ Then, run the server as follows:
31
+
32
+ ```sh
33
+ yarn dev --host 0.0.0.0
34
+ ```
35
+
36
+ Go to [https://rails-wasm.test](https://rails-wasm.test) to open the app.
37
+
38
+ ## Credits
39
+
40
+ The launcher HTML/JS is based on the [Yuta Saito](https://github.com/kateinoigakukun)'s work on [Mastodon in the browser](https://github.com/kateinoigakukun/mastodon/tree/katei/wasmify).
@@ -0,0 +1,102 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <meta name="og:title" content="Rails on Wasm" />
7
+ <meta name="og:description" content="Rails on Wasm launcher" />
8
+ <title>Rails on Wasm | Launch</title>
9
+ <script type="module" src="/boot.js"></script>
10
+ <style>
11
+ #boot-console-output {
12
+ background-color: #0c0c0c;
13
+ color: #f8f8f2;
14
+ font-family: monospace;
15
+ font-size: 0.8em;
16
+ padding: 1em;
17
+ white-space: pre-wrap;
18
+ min-height: 200px;
19
+ overflow-y: scroll;
20
+ }
21
+ #launch-button {
22
+ background-color: #007bff;
23
+ color: #fff;
24
+ border: none;
25
+ padding: 0.5em 1em;
26
+ font-size: 1em;
27
+ cursor: pointer;
28
+ border: 1px solid #ccc;
29
+ }
30
+ #launch-button:disabled {
31
+ background-color: #ccc;
32
+ color: #666;
33
+ }
34
+ #reboot-button {
35
+ background-color: #dc3545;
36
+ color: #fff;
37
+ border: none;
38
+ padding: 0.5em 1em;
39
+ font-size: 1em;
40
+ cursor: pointer;
41
+ border: 1px solid #ccc;
42
+ }
43
+ #reboot-button:disabled {
44
+ background-color: #ccc;
45
+ color: #666;
46
+ }
47
+ #reload-button {
48
+ background-color: #eeeeee;
49
+ color: #333;
50
+ border: none;
51
+ padding: 0.5em 1em;
52
+ font-size: 1em;
53
+ cursor: pointer;
54
+ border: 1px solid #333;
55
+ }
56
+ #reload-button:disabled {
57
+ background-color: #ccc;
58
+ color: #666;
59
+ }
60
+ #reload-debug-button {
61
+ background-color: rgb(159, 166, 40);
62
+ color: #000;
63
+ border: none;
64
+ padding: 0.5em 1em;
65
+ font-size: 1em;
66
+ cursor: pointer;
67
+ border: 1px solid #000;
68
+ }
69
+ #reload-debug-button:disabled {
70
+ background-color: #ccc;
71
+ color: #666;
72
+ }
73
+ </style>
74
+ </head>
75
+ <body>
76
+ <h1>Rails on Wasm 🚀</h1>
77
+ <p>
78
+ Wait for the Service Worker to install and boot the app. This may
79
+ take a while.
80
+ </p>
81
+
82
+ <p>
83
+ <button id="launch-button" disabled>Launch</button>
84
+ <button id="reload-button" disabled>Reload Rails</button>
85
+ <button id="reload-debug-button" disabled>
86
+ Reload Rails (debug mode)
87
+ </button>
88
+ <button id="reboot-button" disabled>Hard Reset*</button>
89
+ </p>
90
+
91
+ <div id="boot-message"></div>
92
+ <progress id="boot-progress" max="100" value="0"></progress>
93
+ <pre id="boot-console-output"></pre>
94
+ <hr />
95
+ <p>
96
+ <small>
97
+ * Please ensure you close all tabs of this app before rebooting
98
+ to unregister the old Service Worker.
99
+ </small>
100
+ </p>
101
+ </body>
102
+ </html>
@@ -0,0 +1,96 @@
1
+ async function registerServiceWorker() {
2
+ const oldRegistrations = await navigator.serviceWorker.getRegistrations();
3
+ for (const registration of oldRegistrations) {
4
+ if (registration.installing.state === "installing") {
5
+ return;
6
+ }
7
+ }
8
+
9
+ const workerUrl =
10
+ import.meta.env.MODE === "production"
11
+ ? "/rails.sw.js"
12
+ : "/dev-sw.js?dev-sw";
13
+
14
+ await navigator.serviceWorker.register(workerUrl, {
15
+ scope: "/",
16
+ type: "module",
17
+ });
18
+ }
19
+
20
+ async function boot({ bootMessage, bootProgress, bootConsoleOutput }) {
21
+ if (!("serviceWorker" in navigator)) {
22
+ console.error("Service Worker is not supported in this browser.");
23
+ return;
24
+ }
25
+
26
+ if (!navigator.serviceWorker.controller) {
27
+ await registerServiceWorker();
28
+
29
+ bootMessage.textContent = "Waiting for Service Worker to activate...";
30
+ } else {
31
+ console.log("Service Worker already active.");
32
+ }
33
+
34
+ navigator.serviceWorker.addEventListener("message", function (event) {
35
+ switch (event.data.type) {
36
+ case "progress": {
37
+ bootMessage.textContent = event.data.step;
38
+ bootProgress.value = event.data.value;
39
+ break;
40
+ }
41
+ case "console": {
42
+ bootConsoleOutput.textContent += event.data.message + "\n";
43
+ break;
44
+ }
45
+ default: {
46
+ console.log("Unknown message type:", event.data.type);
47
+ }
48
+ }
49
+ });
50
+
51
+ return await navigator.serviceWorker.ready;
52
+ }
53
+
54
+ async function init() {
55
+ const bootMessage = document.getElementById("boot-message");
56
+ const bootProgress = document.getElementById("boot-progress");
57
+ const bootConsoleOutput = document.getElementById("boot-console-output");
58
+ const registration = await boot({
59
+ bootMessage,
60
+ bootProgress,
61
+ bootConsoleOutput,
62
+ });
63
+ if (!registration) {
64
+ return;
65
+ }
66
+ bootMessage.textContent = "Service Worker Ready";
67
+ bootProgress.value = 100;
68
+
69
+ const launchButton = document.getElementById("launch-button");
70
+ launchButton.disabled = false;
71
+ launchButton.addEventListener("click", async function () {
72
+ // Open in a new window
73
+ window.open("/", "_blank");
74
+ });
75
+
76
+ const rebootButton = document.getElementById("reboot-button");
77
+ rebootButton.disabled = false;
78
+ rebootButton.addEventListener("click", async function () {
79
+ await registration.unregister();
80
+ window.location.reload();
81
+ });
82
+
83
+ const reloadButton = document.getElementById("reload-button");
84
+ reloadButton.disabled = false;
85
+ reloadButton.addEventListener("click", async function () {
86
+ registration.active.postMessage({ type: "reload-rails" });
87
+ });
88
+
89
+ const reloadDebugButton = document.getElementById("reload-debug-button");
90
+ reloadDebugButton.disabled = false;
91
+ reloadDebugButton.addEventListener("click", async function () {
92
+ registration.active.postMessage({ type: "reload-rails", debug: true });
93
+ });
94
+ }
95
+
96
+ init();
@@ -0,0 +1,18 @@
1
+ import sqlite3InitModule from "@sqlite.org/sqlite-wasm";
2
+
3
+ export const setupSQLiteDatabase = async () => {
4
+ const sqlite3 = await sqlite3InitModule();
5
+
6
+ console.log("Running SQLite3 version", sqlite3.version.libVersion);
7
+ const db =
8
+ "opfs" in sqlite3
9
+ ? new sqlite3.oo1.OpfsDb("/railsdb.sqlite3")
10
+ : new sqlite3.oo1.DB("/railsdb.sqlite3", "ct");
11
+ console.log(
12
+ "opfs" in sqlite3
13
+ ? `OPFS is available, created persisted database at ${db.filename}`
14
+ : `OPFS is not available, created transient database ${db.filename}`,
15
+ );
16
+
17
+ return db;
18
+ };