@tutorialkit-rb/cli 1.5.2-rb.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 (151) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +15 -0
  3. package/dist/index.js +1384 -0
  4. package/package.json +66 -0
  5. package/template/.gitignore +13 -0
  6. package/template/.vscode/extensions.json +4 -0
  7. package/template/.vscode/launch.json +11 -0
  8. package/template/README.md +179 -0
  9. package/template/astro.config.ts +21 -0
  10. package/template/bin/build-wasm +30 -0
  11. package/template/icons/languages/css.svg +1 -0
  12. package/template/icons/languages/html.svg +1 -0
  13. package/template/icons/languages/js.svg +1 -0
  14. package/template/icons/languages/json.svg +1 -0
  15. package/template/icons/languages/markdown.svg +1 -0
  16. package/template/icons/languages/ruby.svg +1 -0
  17. package/template/icons/languages/sass.svg +1 -0
  18. package/template/icons/languages/ts.svg +1 -0
  19. package/template/icons/phosphor/file-erb.svg +1 -0
  20. package/template/icons/phosphor/file-rb.svg +5 -0
  21. package/template/package.json +37 -0
  22. package/template/package.json.bak +37 -0
  23. package/template/public/favicon.svg +4 -0
  24. package/template/public/logo-dark.svg +4 -0
  25. package/template/public/logo.svg +4 -0
  26. package/template/ruby-wasm/.railsrc +12 -0
  27. package/template/ruby-wasm/Gemfile +22 -0
  28. package/template/ruby-wasm/Gemfile.lock +292 -0
  29. package/template/ruby-wasm/README.md +19 -0
  30. package/template/ruby-wasm/bin/pack +16 -0
  31. package/template/ruby-wasm/boot.rb +32 -0
  32. package/template/ruby-wasm/config/wasmify.yml +23 -0
  33. package/template/src/components/FileManager.tsx +116 -0
  34. package/template/src/components/GitHubLink.astro +17 -0
  35. package/template/src/components/HeadTags.astro +65 -0
  36. package/template/src/components/HelpDropdown.tsx +72 -0
  37. package/template/src/components/RailsPathLinkHandler.tsx +107 -0
  38. package/template/src/components/ShellConfigurator.tsx +95 -0
  39. package/template/src/components/TopBar.astro +48 -0
  40. package/template/src/content/config.ts +9 -0
  41. package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/_files/workspace/.keep +0 -0
  42. package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/content.md +34 -0
  43. package/template/src/content/tutorial/1-getting-started/2-rails-console/_files/.tk-config.json +3 -0
  44. package/template/src/content/tutorial/1-getting-started/2-rails-console/_files/workspace/.keep +0 -0
  45. package/template/src/content/tutorial/1-getting-started/2-rails-console/content.md +37 -0
  46. package/template/src/content/tutorial/1-getting-started/meta.md +4 -0
  47. package/template/src/content/tutorial/2-controllers/2-crud-operations/_files/.tk-config.json +3 -0
  48. package/template/src/content/tutorial/2-controllers/2-crud-operations/_files/workspace/.keep +0 -0
  49. package/template/src/content/tutorial/2-controllers/2-crud-operations/content.md +99 -0
  50. package/template/src/content/tutorial/2-controllers/meta.md +4 -0
  51. package/template/src/content/tutorial/meta.md +18 -0
  52. package/template/src/env.d.ts +3 -0
  53. package/template/src/plugins/remarkRailsPathLinks.ts +39 -0
  54. package/template/src/templates/crud-products/.tk-config.json +3 -0
  55. package/template/src/templates/crud-products/workspace/.keep +0 -0
  56. package/template/src/templates/crud-products/workspace/store/app/controllers/products_controller.rb +48 -0
  57. package/template/src/templates/crud-products/workspace/store/app/models/product.rb +3 -0
  58. package/template/src/templates/crud-products/workspace/store/app/views/products/_form.html.erb +10 -0
  59. package/template/src/templates/crud-products/workspace/store/app/views/products/edit.html.erb +4 -0
  60. package/template/src/templates/crud-products/workspace/store/app/views/products/index.html.erb +11 -0
  61. package/template/src/templates/crud-products/workspace/store/app/views/products/new.html.erb +4 -0
  62. package/template/src/templates/crud-products/workspace/store/app/views/products/show.html.erb +5 -0
  63. package/template/src/templates/crud-products/workspace/store/config/routes.rb +6 -0
  64. package/template/src/templates/crud-products/workspace/store/db/migrate/20250521010850_create_products.rb +9 -0
  65. package/template/src/templates/crud-products/workspace/store/db/schema.rb +22 -0
  66. package/template/src/templates/crud-products/workspace/store/db/seeds.rb +3 -0
  67. package/template/src/templates/crud-products/workspace/store/test/fixtures/products.yml +7 -0
  68. package/template/src/templates/crud-products/workspace/store/test/models/product_test.rb +7 -0
  69. package/template/src/templates/default/bin/console +9 -0
  70. package/template/src/templates/default/bin/rackup +11 -0
  71. package/template/src/templates/default/bin/rails +41 -0
  72. package/template/src/templates/default/bin/ruby +37 -0
  73. package/template/src/templates/default/lib/commands.js +39 -0
  74. package/template/src/templates/default/lib/database.js +46 -0
  75. package/template/src/templates/default/lib/irb.js +110 -0
  76. package/template/src/templates/default/lib/patches/app_generator.rb +43 -0
  77. package/template/src/templates/default/lib/patches/authentication.rb +24 -0
  78. package/template/src/templates/default/lib/rails.js +69 -0
  79. package/template/src/templates/default/lib/server/frame_location_middleware.js +77 -0
  80. package/template/src/templates/default/lib/server.js +307 -0
  81. package/template/src/templates/default/package-lock.json +1830 -0
  82. package/template/src/templates/default/package.json +23 -0
  83. package/template/src/templates/default/pgdata/.keep +0 -0
  84. package/template/src/templates/default/scripts/createdb.js +7 -0
  85. package/template/src/templates/default/scripts/rails.js +52 -0
  86. package/template/src/templates/default/scripts/wait-for-wasm.js +103 -0
  87. package/template/src/templates/default/workspace/.keep +0 -0
  88. package/template/src/templates/rails-app/workspace/store/.ruby-version +1 -0
  89. package/template/src/templates/rails-app/workspace/store/Gemfile +37 -0
  90. package/template/src/templates/rails-app/workspace/store/README.md +24 -0
  91. package/template/src/templates/rails-app/workspace/store/Rakefile +6 -0
  92. package/template/src/templates/rails-app/workspace/store/app/assets/images/.keep +0 -0
  93. package/template/src/templates/rails-app/workspace/store/app/assets/stylesheets/application.css +10 -0
  94. package/template/src/templates/rails-app/workspace/store/app/controllers/application_controller.rb +4 -0
  95. package/template/src/templates/rails-app/workspace/store/app/helpers/application_helper.rb +2 -0
  96. package/template/src/templates/rails-app/workspace/store/app/javascript/application.js +4 -0
  97. package/template/src/templates/rails-app/workspace/store/app/javascript/controllers/application.js +9 -0
  98. package/template/src/templates/rails-app/workspace/store/app/javascript/controllers/index.js +4 -0
  99. package/template/src/templates/rails-app/workspace/store/app/jobs/application_job.rb +7 -0
  100. package/template/src/templates/rails-app/workspace/store/app/mailers/application_mailer.rb +4 -0
  101. package/template/src/templates/rails-app/workspace/store/app/models/application_record.rb +3 -0
  102. package/template/src/templates/rails-app/workspace/store/app/models/concerns/.keep +0 -0
  103. package/template/src/templates/rails-app/workspace/store/app/views/layouts/application.html.erb +28 -0
  104. package/template/src/templates/rails-app/workspace/store/app/views/layouts/mailer.html.erb +13 -0
  105. package/template/src/templates/rails-app/workspace/store/app/views/layouts/mailer.text.erb +1 -0
  106. package/template/src/templates/rails-app/workspace/store/app/views/pwa/manifest.json.erb +22 -0
  107. package/template/src/templates/rails-app/workspace/store/app/views/pwa/service-worker.js +26 -0
  108. package/template/src/templates/rails-app/workspace/store/bin/importmap +4 -0
  109. package/template/src/templates/rails-app/workspace/store/bin/rails +4 -0
  110. package/template/src/templates/rails-app/workspace/store/config/application.rb +30 -0
  111. package/template/src/templates/rails-app/workspace/store/config/boot.rb +3 -0
  112. package/template/src/templates/rails-app/workspace/store/config/cable.yml +10 -0
  113. package/template/src/templates/rails-app/workspace/store/config/credentials.yml.enc +1 -0
  114. package/template/src/templates/rails-app/workspace/store/config/database.yml +32 -0
  115. package/template/src/templates/rails-app/workspace/store/config/environment.rb +5 -0
  116. package/template/src/templates/rails-app/workspace/store/config/environments/development.rb +69 -0
  117. package/template/src/templates/rails-app/workspace/store/config/environments/production.rb +89 -0
  118. package/template/src/templates/rails-app/workspace/store/config/environments/test.rb +53 -0
  119. package/template/src/templates/rails-app/workspace/store/config/importmap.rb +9 -0
  120. package/template/src/templates/rails-app/workspace/store/config/initializers/assets.rb +7 -0
  121. package/template/src/templates/rails-app/workspace/store/config/initializers/content_security_policy.rb +25 -0
  122. package/template/src/templates/rails-app/workspace/store/config/initializers/filter_parameter_logging.rb +8 -0
  123. package/template/src/templates/rails-app/workspace/store/config/initializers/inflections.rb +16 -0
  124. package/template/src/templates/rails-app/workspace/store/config/locales/en.yml +31 -0
  125. package/template/src/templates/rails-app/workspace/store/config/master.key +1 -0
  126. package/template/src/templates/rails-app/workspace/store/config/puma.rb +41 -0
  127. package/template/src/templates/rails-app/workspace/store/config/routes.rb +4 -0
  128. package/template/src/templates/rails-app/workspace/store/config/storage.yml +34 -0
  129. package/template/src/templates/rails-app/workspace/store/config.ru +6 -0
  130. package/template/src/templates/rails-app/workspace/store/db/seeds.rb +9 -0
  131. package/template/src/templates/rails-app/workspace/store/log/.keep +0 -0
  132. package/template/src/templates/rails-app/workspace/store/public/400.html +114 -0
  133. package/template/src/templates/rails-app/workspace/store/public/404.html +114 -0
  134. package/template/src/templates/rails-app/workspace/store/public/406-unsupported-browser.html +114 -0
  135. package/template/src/templates/rails-app/workspace/store/public/422.html +114 -0
  136. package/template/src/templates/rails-app/workspace/store/public/500.html +114 -0
  137. package/template/src/templates/rails-app/workspace/store/public/icon.png +0 -0
  138. package/template/src/templates/rails-app/workspace/store/public/icon.svg +3 -0
  139. package/template/src/templates/rails-app/workspace/store/public/robots.txt +1 -0
  140. package/template/src/templates/rails-app/workspace/store/script/.keep +0 -0
  141. package/template/src/templates/rails-app/workspace/store/storage/.keep +0 -0
  142. package/template/src/templates/rails-app/workspace/store/test/controllers/.keep +0 -0
  143. package/template/src/templates/rails-app/workspace/store/test/helpers/.keep +0 -0
  144. package/template/src/templates/rails-app/workspace/store/test/integration/.keep +0 -0
  145. package/template/src/templates/rails-app/workspace/store/test/test_helper.rb +15 -0
  146. package/template/src/templates/rails-app/workspace/store/tmp/.keep +0 -0
  147. package/template/src/templates/rails-app/workspace/store/tmp/pids/.keep +0 -0
  148. package/template/src/templates/rails-app/workspace/store/tmp/storage/.keep +0 -0
  149. package/template/src/templates/rails-app/workspace/store/vendor/javascripts/.keep +0 -0
  150. package/template/tsconfig.json +16 -0
  151. package/template/uno.config.ts +10 -0
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
+
3
+ one:
4
+ name: MyString
5
+
6
+ two:
7
+ name: MyString
@@ -0,0 +1,7 @@
1
+ require "test_helper"
2
+
3
+ class ProductTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env -S node --disable-warning=ExperimentalWarning
2
+
3
+ import initVM from "../lib/rails.js";
4
+ import IRBRepl from "../lib/irb.js";
5
+
6
+ const vm = await initVM();
7
+ const irb = new IRBRepl(vm);
8
+
9
+ irb.start();
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env -S node --disable-warning=ExperimentalWarning --watch --watch-path=./
2
+
3
+ import initVM from "../lib/rails.js";
4
+ import { createRackServer } from "../lib/server.js";
5
+
6
+ const vm = await initVM();
7
+ const server = await createRackServer(vm);
8
+
9
+ server.listen(3000, () => {
10
+ console.log("Server started on port 3000");
11
+ });
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env -S node --disable-warning=ExperimentalWarning
2
+
3
+ import initVM from "../lib/rails.js";
4
+ import ExternalCommands from "../lib/commands.js";
5
+ import util from "node:util";
6
+
7
+ const vm = await initVM({env: {"HOME": "/rails-vm"}});
8
+ const args = util.inspect(process.argv.slice(2));
9
+ const commands = new ExternalCommands();
10
+ // Make commands accessible from Ruby via global JS object
11
+ global.externalCommands = commands;
12
+
13
+ await vm.evalAsync(`
14
+ args = ${args}
15
+ ARGV.replace(args)
16
+
17
+ begin
18
+ if File.file?("config/application.rb")
19
+ APP_PATH = File.expand_path("config/application", Dir.pwd)
20
+ require File.expand_path("../boot", APP_PATH)
21
+ require "rails/commands"
22
+ else
23
+ require "rails/command"
24
+ case ARGV.first
25
+ when Rails::Command::HELP_MAPPINGS, "help", nil
26
+ ARGV.shift
27
+ Rails::Command.invoke :gem_help, ARGV
28
+ when "plugin"
29
+ ARGV.shift
30
+ Rails::Command.invoke :plugin, ARGV
31
+ else
32
+ Rails::Command.invoke :application, ARGV
33
+ end
34
+ end
35
+ execute_at_exit_hooks unless Wasmify::ExternalCommands.any?
36
+ rescue SystemExit
37
+ # expected, do nothing
38
+ end
39
+ `)
40
+
41
+ commands.invoke(vm)
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env -S node --disable-warning=ExperimentalWarning
2
+
3
+ import initVM from "../lib/rails.js";
4
+ import ExternalCommands from "../lib/commands.js";
5
+ import util from "node:util";
6
+ import fs from "node:fs";
7
+
8
+ const commands = new ExternalCommands();
9
+ // Make commands accessible from Ruby via global JS object
10
+ global.externalCommands = commands;
11
+
12
+ const args = process.argv.slice(2);
13
+
14
+ if (args[0] && fs.existsSync(process.cwd() + '/' + args[0])) {
15
+ const firstLine = fs.readFileSync(process.cwd() + '/' + args[0], 'utf8').split('\n')[0];
16
+ // Check if called via shebang
17
+ if (firstLine.includes('#!/usr/bin/env ruby')) {
18
+ const path = args.shift();
19
+ const vm = await initVM({ env: { "HOME": "/rails-vm" } });
20
+
21
+ await vm.evalAsync(`
22
+ args = ${util.inspect(args)}
23
+ ARGV.replace(args)
24
+
25
+ begin
26
+ load "./${path}"
27
+ execute_at_exit_hooks unless Wasmify::ExternalCommands.any?
28
+ rescue SystemExit
29
+ end
30
+ `);
31
+
32
+ commands.invoke(vm)
33
+ }
34
+ } else {
35
+ const vm = await initVM({ env: { "HOME": "/rails-vm" }, args, skipRails: true });
36
+ commands.invoke(vm)
37
+ }
@@ -0,0 +1,39 @@
1
+ import { createRackServer } from "./server.js";
2
+ import IRBRepl from "./irb.js";
3
+
4
+ export default class ExternalCommands {
5
+ constructor() {
6
+ this.command = undefined;
7
+ }
8
+
9
+ server(port) {
10
+ this.command = async function(vm) {
11
+ const server = await createRackServer(vm, {skipRackup: true});
12
+
13
+ server.listen(port, () => {
14
+ console.log(`Express.js server started on port ${port}`);
15
+ console.log(`Use Ctrl-C to stop`);
16
+ });
17
+
18
+ // FIXME: doesn't work; do WebContainers/jsh support signals at all?
19
+ process.on('exit', async () => {
20
+ console.log('Express.js server is shutting down');
21
+ await vm.evalAsync(`execute_at_exit_hooks`)
22
+ });
23
+ }
24
+ }
25
+
26
+ console() {
27
+ this.command = async function(vm) {
28
+ const irb = new IRBRepl(vm);
29
+ return irb.start();
30
+ }
31
+ }
32
+
33
+ // Invokes a registered command if any
34
+ invoke(vm) {
35
+ if (!this.command) return;
36
+
37
+ return this.command(vm);
38
+ }
39
+ }
@@ -0,0 +1,46 @@
1
+ import { PGlite } from '@electric-sql/pglite'
2
+ import { join } from "node:path";
3
+
4
+ const MULTILINE_RX = /;\s*(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP|TRUNCATE|WITH|EXPLAIN|ANALYZE|VACUUM|GRANT|REVOKE|BEGIN|COMMIT|ROLLBACK)/i
5
+
6
+ class ExternalInterface {
7
+ constructor(db, identifier) {
8
+ this.db = db;
9
+ this.identifier = identifier;
10
+ }
11
+
12
+ async query(sql, params) {
13
+ let res;
14
+
15
+ if (MULTILINE_RX.test(sql)) {
16
+ res = (await this.db.exec(sql, params))[0];
17
+ } else {
18
+ res = await this.db.query(sql, params);
19
+ }
20
+
21
+ return res
22
+ }
23
+ }
24
+
25
+ export class PGLite4Rails {
26
+ constructor(dataDir) {
27
+ // Created databases
28
+ this.dbs = {};
29
+ // Base directory for all databases
30
+ this.dataDir = dataDir;
31
+ }
32
+
33
+ async create_interface(dbname) {
34
+ if (this.dbs[dbname]) return this.dbs[dbname].identifier;
35
+
36
+ const dataDir = join(this.dataDir, dbname);
37
+
38
+ const db = await PGlite.create({ dataDir })
39
+ const ei = new ExternalInterface(db, `pglite4rails_${dbname}`)
40
+
41
+ const identifier = ei.identifier
42
+ global[identifier] = this.dbs[dbname] = ei
43
+
44
+ return identifier
45
+ }
46
+ }
@@ -0,0 +1,110 @@
1
+ import repl from "node:repl";
2
+
3
+ const isRecoverableError = (error) => {
4
+ if (error.message.includes('SyntaxError')) {
5
+ return true;
6
+ }
7
+
8
+ return false;
9
+ }
10
+
11
+ const rubyWriter = (output) => {
12
+ if (!output) return;
13
+
14
+ if (typeof output === 'string') {
15
+ return output;
16
+ }
17
+
18
+ return output.toString().replace(/\n$/, "");
19
+ }
20
+
21
+ export default class IRBRepl {
22
+ constructor(vm) {
23
+ this.vm = vm;
24
+ this.eval = this.eval.bind(this);
25
+ }
26
+
27
+ async eval (cmd, context, filename, callback) {
28
+ let result;
29
+ try {
30
+ result = await this.vm.evalAsync(`
31
+ __code__ = <<~'RUBY'
32
+ ${cmd}
33
+ RUBY
34
+
35
+ $irb.eval_code(__code__)
36
+ `);
37
+ } catch (e) {
38
+ if (e.message.includes('SystemExit')) {
39
+ process.exit();
40
+ }
41
+ if (isRecoverableError(e)) {
42
+ return callback(new repl.Recoverable(e));
43
+ }
44
+
45
+ return callback(null, e.message);
46
+ }
47
+ callback(null, result);
48
+ }
49
+
50
+ async start() {
51
+ // Set up IRB
52
+ const promptVal = await this.vm.evalAsync(`
53
+ require "irb"
54
+
55
+ STDOUT.sync = true
56
+ if IRB.conf.empty?
57
+ ap_path = __FILE__
58
+ $0 = File::basename(ap_path, ".rb") if ap_path
59
+ IRB.setup(ap_path)
60
+ end
61
+
62
+ class NonBlockingIO
63
+ def gets
64
+ raise NonImplementedError
65
+ end
66
+
67
+ def external_encoding
68
+ "UTF-8"
69
+ end
70
+
71
+ def wait_readable(timeout = nil)
72
+ true
73
+ end
74
+
75
+ def getc = "x"
76
+ def ungetc(c) = nil
77
+ end
78
+
79
+ class IRB::Irb
80
+ def eval_code(code)
81
+ statement = parse_input(code)
82
+
83
+ context.evaluate(statement, @line_no)
84
+ @line_no += code.count("\n")
85
+ context.inspect_last_value
86
+ rescue SystemExit, SignalException, SyntaxError
87
+ raise
88
+ rescue Interrupt, Exception => exc
89
+ handle_exception(exc)
90
+ context.workspace.local_variable_set(:_, exc)
91
+ end
92
+ end
93
+
94
+ $irb = IRB::Irb.new(nil, IRB::StdioInputMethod.new)
95
+
96
+ # return configured prompt
97
+ IRB.conf[:PROMPT][IRB.conf[:PROMPT_MODE]][:PROMPT_I]
98
+ .gsub(/(%\\d+)?n/, "") # no line number support
99
+ .then { $irb.send(:format_prompt, _1, nil, 0, 0) }
100
+ `)
101
+
102
+ const prompt = promptVal.toJS()
103
+ const local = repl.start({prompt, eval: this.eval, writer: rubyWriter});
104
+
105
+ local.on('exit', () => {
106
+ // TODO: save history?
107
+ process.exit();
108
+ });
109
+ }
110
+ }
@@ -0,0 +1,43 @@
1
+ # Patch Rails app generator to use pglite as a database adapter
2
+ Wasmify::Patcha.on_load("Rails::AppBuilder") do
3
+ Rails::AppBuilder.prepend(Module.new do
4
+ def database_yml
5
+ create_file "config/database.yml" do
6
+ <<-YAML
7
+ default: &default
8
+ adapter: pglite
9
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
10
+ timeout: 5000
11
+
12
+ development:
13
+ <<: *default
14
+ database: store_development
15
+
16
+ # Warning: The database defined as "test" will be erased and
17
+ # re-generated from your development database when you run "rake".
18
+ # Do not set this db to the same as development or production.
19
+ test:
20
+ <<: *default
21
+ database: store_test
22
+
23
+ production:
24
+ primary:
25
+ <<: *default
26
+ # database: path/to/persistent/storage/production.sqlite3
27
+ cache:
28
+ <<: *default
29
+ # database: path/to/persistent/storage/production_cache.sqlite3
30
+ migrations_paths: db/cache_migrate
31
+ queue:
32
+ <<: *default
33
+ # database: path/to/persistent/storage/production_queue.sqlite3
34
+ migrations_paths: db/queue_migrate
35
+ cable:
36
+ <<: *default
37
+ # database: path/to/persistent/storage/production_cable.sqlite3
38
+ migrations_paths: db/cable_migrate
39
+ YAML
40
+ end
41
+ end
42
+ end)
43
+ end
@@ -0,0 +1,24 @@
1
+ # Automatically authenticate a user of first request if the corresponding file is present.
2
+ # This way, we can skip the authentication in the second part of the tutorial
3
+ Wasmify::Patcha.on_load("Authentication") do
4
+ Authentication.prepend(Module.new do
5
+ def find_session_by_cookie
6
+ return super if $__pre_authenticated
7
+
8
+ $__pre_authenticated = true
9
+
10
+ session = super
11
+
12
+ return session if session
13
+
14
+ return unless Rails.root.join("tmp/authenticated-user.txt").exist?
15
+
16
+ $_user_email = Rails.root.join("tmp/authenticated-user.txt").read.chomp
17
+
18
+ user = User.find_by(email_address: $_user_email)
19
+ return unless user
20
+
21
+ start_new_session_for(user)
22
+ end
23
+ end)
24
+ end
@@ -0,0 +1,69 @@
1
+ import { RubyVM } from "@ruby/wasm-wasi";
2
+ import { WASI } from "wasi";
3
+ import fs from "fs/promises";
4
+ import { PGLite4Rails } from "./database.js";
5
+
6
+ const rubyWasm = new URL("../node_modules/@ruby/wasm-wasi/dist/ruby.wasm", import.meta.url).pathname;
7
+
8
+ const railsRootDir = new URL("../workspace/store", import.meta.url).pathname;
9
+ const pgDataDir = new URL("../pgdata", import.meta.url).pathname;
10
+
11
+ export default async function initVM(vmopts = {}) {
12
+ const { args, skipRails } = vmopts;
13
+ const env = vmopts.env || {};
14
+ const binary = await fs.readFile(rubyWasm);
15
+ const module = await WebAssembly.compile(binary);
16
+
17
+ const RAILS_ENV = env.RAILS_ENV || process.env.RAILS_ENV;
18
+ if (RAILS_ENV) env.RAILS_ENV = RAILS_ENV;
19
+
20
+ const workspaceDir = new URL("../workspace", import.meta.url).pathname;
21
+ const workdir = process.cwd().startsWith(workspaceDir) ?
22
+ `/workspace${process.cwd().slice(workspaceDir.length)}` :
23
+ "";
24
+
25
+ const cliArgs = args?.length ? ['ruby.wasm'].concat(args) : undefined;
26
+
27
+ const wasi = new WASI(
28
+ {
29
+ env: {"RUBYOPT": "-EUTF-8 -W0", ...env},
30
+ version: "preview1",
31
+ returnOnExit: true,
32
+ preopens: {
33
+ "/workspace": workspaceDir
34
+ },
35
+ args: cliArgs
36
+ }
37
+ );
38
+
39
+ const { vm } = await RubyVM.instantiateModule({
40
+ module,
41
+ wasip1: wasi,
42
+ args: cliArgs
43
+ });
44
+
45
+ if (!skipRails) {
46
+ const pglite = new PGLite4Rails(pgDataDir);
47
+ global.pglite = pglite;
48
+
49
+ const authenticationPatch = await fs.readFile(new URL("./patches/authentication.rb", import.meta.url).pathname, 'utf8');
50
+ const appGeneratorPatch = await fs.readFile(new URL("./patches/app_generator.rb", import.meta.url).pathname, 'utf8');
51
+
52
+ vm.eval(`
53
+ Dir.chdir("${workdir}") unless "${workdir}".empty?
54
+
55
+ ENV["RACK_HANDLER"] = "wasi"
56
+
57
+ require "/rails-vm/boot"
58
+
59
+ require "js"
60
+
61
+ Wasmify::ExternalCommands.register(:server, :console)
62
+
63
+ ${authenticationPatch}
64
+ ${appGeneratorPatch}
65
+ `)
66
+ }
67
+
68
+ return vm;
69
+ }
@@ -0,0 +1,77 @@
1
+ const createFrameLocationTrackingMiddleware = (options = {}) => {
2
+ const {
3
+ injectionPoint = '</body>'
4
+ } = options;
5
+
6
+ const trackingScript = `
7
+ <script>
8
+ (function() {
9
+ // Avoid double initialization
10
+ if (window.__locationTrackingInitialized) return;
11
+ window.__locationTrackingInitialized = true;
12
+
13
+ function notifyParentLocation() {
14
+ if (window.parent === window) return;
15
+
16
+ try {
17
+ window.parent.postMessage({
18
+ type: '$locationChange',
19
+ location: {
20
+ href: window.location.href,
21
+ },
22
+ timestamp: Date.now()
23
+ }, '*');
24
+ console.log('notified location change', window.location.href);
25
+ } catch (e) {
26
+ console.error('Failed to notify parent:', e);
27
+ }
28
+ }
29
+
30
+ if (document.readyState === 'loading') {
31
+ document.addEventListener('DOMContentLoaded', notifyParentLocation);
32
+ } else {
33
+ notifyParentLocation();
34
+ }
35
+
36
+ window.addEventListener('popstate', notifyParentLocation);
37
+ window.addEventListener('hashchange', notifyParentLocation);
38
+
39
+ const originalPushState = history.pushState;
40
+ const originalReplaceState = history.replaceState;
41
+
42
+ history.pushState = function() {
43
+ const result = originalPushState.apply(history, arguments);
44
+ setTimeout(notifyParentLocation, 0);
45
+ return result;
46
+ };
47
+
48
+ history.replaceState = function() {
49
+ const result = originalReplaceState.apply(history, arguments);
50
+ setTimeout(notifyParentLocation, 0);
51
+ return result;
52
+ };
53
+ })();
54
+ </script>
55
+ `;
56
+
57
+ return (req, res, next) => {
58
+ // Store the original send method
59
+ const originalSend = res.send;
60
+
61
+ res.send = function(data) {
62
+ const contentType = res.get('Content-Type');
63
+
64
+ if (contentType && contentType.includes('text/html') && typeof data === 'string') {
65
+ if (data.includes(injectionPoint)) {
66
+ data = data.replace(injectionPoint, trackingScript + injectionPoint);
67
+ }
68
+ }
69
+
70
+ return originalSend.call(this, data);
71
+ };
72
+
73
+ next();
74
+ };
75
+ };
76
+
77
+ export default createFrameLocationTrackingMiddleware;