@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.
- package/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.js +1384 -0
- package/package.json +66 -0
- package/template/.gitignore +13 -0
- package/template/.vscode/extensions.json +4 -0
- package/template/.vscode/launch.json +11 -0
- package/template/README.md +179 -0
- package/template/astro.config.ts +21 -0
- package/template/bin/build-wasm +30 -0
- package/template/icons/languages/css.svg +1 -0
- package/template/icons/languages/html.svg +1 -0
- package/template/icons/languages/js.svg +1 -0
- package/template/icons/languages/json.svg +1 -0
- package/template/icons/languages/markdown.svg +1 -0
- package/template/icons/languages/ruby.svg +1 -0
- package/template/icons/languages/sass.svg +1 -0
- package/template/icons/languages/ts.svg +1 -0
- package/template/icons/phosphor/file-erb.svg +1 -0
- package/template/icons/phosphor/file-rb.svg +5 -0
- package/template/package.json +37 -0
- package/template/package.json.bak +37 -0
- package/template/public/favicon.svg +4 -0
- package/template/public/logo-dark.svg +4 -0
- package/template/public/logo.svg +4 -0
- package/template/ruby-wasm/.railsrc +12 -0
- package/template/ruby-wasm/Gemfile +22 -0
- package/template/ruby-wasm/Gemfile.lock +292 -0
- package/template/ruby-wasm/README.md +19 -0
- package/template/ruby-wasm/bin/pack +16 -0
- package/template/ruby-wasm/boot.rb +32 -0
- package/template/ruby-wasm/config/wasmify.yml +23 -0
- package/template/src/components/FileManager.tsx +116 -0
- package/template/src/components/GitHubLink.astro +17 -0
- package/template/src/components/HeadTags.astro +65 -0
- package/template/src/components/HelpDropdown.tsx +72 -0
- package/template/src/components/RailsPathLinkHandler.tsx +107 -0
- package/template/src/components/ShellConfigurator.tsx +95 -0
- package/template/src/components/TopBar.astro +48 -0
- package/template/src/content/config.ts +9 -0
- package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/_files/workspace/.keep +0 -0
- package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/content.md +34 -0
- package/template/src/content/tutorial/1-getting-started/2-rails-console/_files/.tk-config.json +3 -0
- package/template/src/content/tutorial/1-getting-started/2-rails-console/_files/workspace/.keep +0 -0
- package/template/src/content/tutorial/1-getting-started/2-rails-console/content.md +37 -0
- package/template/src/content/tutorial/1-getting-started/meta.md +4 -0
- package/template/src/content/tutorial/2-controllers/2-crud-operations/_files/.tk-config.json +3 -0
- package/template/src/content/tutorial/2-controllers/2-crud-operations/_files/workspace/.keep +0 -0
- package/template/src/content/tutorial/2-controllers/2-crud-operations/content.md +99 -0
- package/template/src/content/tutorial/2-controllers/meta.md +4 -0
- package/template/src/content/tutorial/meta.md +18 -0
- package/template/src/env.d.ts +3 -0
- package/template/src/plugins/remarkRailsPathLinks.ts +39 -0
- package/template/src/templates/crud-products/.tk-config.json +3 -0
- package/template/src/templates/crud-products/workspace/.keep +0 -0
- package/template/src/templates/crud-products/workspace/store/app/controllers/products_controller.rb +48 -0
- package/template/src/templates/crud-products/workspace/store/app/models/product.rb +3 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/_form.html.erb +10 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/edit.html.erb +4 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/index.html.erb +11 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/new.html.erb +4 -0
- package/template/src/templates/crud-products/workspace/store/app/views/products/show.html.erb +5 -0
- package/template/src/templates/crud-products/workspace/store/config/routes.rb +6 -0
- package/template/src/templates/crud-products/workspace/store/db/migrate/20250521010850_create_products.rb +9 -0
- package/template/src/templates/crud-products/workspace/store/db/schema.rb +22 -0
- package/template/src/templates/crud-products/workspace/store/db/seeds.rb +3 -0
- package/template/src/templates/crud-products/workspace/store/test/fixtures/products.yml +7 -0
- package/template/src/templates/crud-products/workspace/store/test/models/product_test.rb +7 -0
- package/template/src/templates/default/bin/console +9 -0
- package/template/src/templates/default/bin/rackup +11 -0
- package/template/src/templates/default/bin/rails +41 -0
- package/template/src/templates/default/bin/ruby +37 -0
- package/template/src/templates/default/lib/commands.js +39 -0
- package/template/src/templates/default/lib/database.js +46 -0
- package/template/src/templates/default/lib/irb.js +110 -0
- package/template/src/templates/default/lib/patches/app_generator.rb +43 -0
- package/template/src/templates/default/lib/patches/authentication.rb +24 -0
- package/template/src/templates/default/lib/rails.js +69 -0
- package/template/src/templates/default/lib/server/frame_location_middleware.js +77 -0
- package/template/src/templates/default/lib/server.js +307 -0
- package/template/src/templates/default/package-lock.json +1830 -0
- package/template/src/templates/default/package.json +23 -0
- package/template/src/templates/default/pgdata/.keep +0 -0
- package/template/src/templates/default/scripts/createdb.js +7 -0
- package/template/src/templates/default/scripts/rails.js +52 -0
- package/template/src/templates/default/scripts/wait-for-wasm.js +103 -0
- package/template/src/templates/default/workspace/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/.ruby-version +1 -0
- package/template/src/templates/rails-app/workspace/store/Gemfile +37 -0
- package/template/src/templates/rails-app/workspace/store/README.md +24 -0
- package/template/src/templates/rails-app/workspace/store/Rakefile +6 -0
- package/template/src/templates/rails-app/workspace/store/app/assets/images/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/app/assets/stylesheets/application.css +10 -0
- package/template/src/templates/rails-app/workspace/store/app/controllers/application_controller.rb +4 -0
- package/template/src/templates/rails-app/workspace/store/app/helpers/application_helper.rb +2 -0
- package/template/src/templates/rails-app/workspace/store/app/javascript/application.js +4 -0
- package/template/src/templates/rails-app/workspace/store/app/javascript/controllers/application.js +9 -0
- package/template/src/templates/rails-app/workspace/store/app/javascript/controllers/index.js +4 -0
- package/template/src/templates/rails-app/workspace/store/app/jobs/application_job.rb +7 -0
- package/template/src/templates/rails-app/workspace/store/app/mailers/application_mailer.rb +4 -0
- package/template/src/templates/rails-app/workspace/store/app/models/application_record.rb +3 -0
- package/template/src/templates/rails-app/workspace/store/app/models/concerns/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/app/views/layouts/application.html.erb +28 -0
- package/template/src/templates/rails-app/workspace/store/app/views/layouts/mailer.html.erb +13 -0
- package/template/src/templates/rails-app/workspace/store/app/views/layouts/mailer.text.erb +1 -0
- package/template/src/templates/rails-app/workspace/store/app/views/pwa/manifest.json.erb +22 -0
- package/template/src/templates/rails-app/workspace/store/app/views/pwa/service-worker.js +26 -0
- package/template/src/templates/rails-app/workspace/store/bin/importmap +4 -0
- package/template/src/templates/rails-app/workspace/store/bin/rails +4 -0
- package/template/src/templates/rails-app/workspace/store/config/application.rb +30 -0
- package/template/src/templates/rails-app/workspace/store/config/boot.rb +3 -0
- package/template/src/templates/rails-app/workspace/store/config/cable.yml +10 -0
- package/template/src/templates/rails-app/workspace/store/config/credentials.yml.enc +1 -0
- package/template/src/templates/rails-app/workspace/store/config/database.yml +32 -0
- package/template/src/templates/rails-app/workspace/store/config/environment.rb +5 -0
- package/template/src/templates/rails-app/workspace/store/config/environments/development.rb +69 -0
- package/template/src/templates/rails-app/workspace/store/config/environments/production.rb +89 -0
- package/template/src/templates/rails-app/workspace/store/config/environments/test.rb +53 -0
- package/template/src/templates/rails-app/workspace/store/config/importmap.rb +9 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/assets.rb +7 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/content_security_policy.rb +25 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/filter_parameter_logging.rb +8 -0
- package/template/src/templates/rails-app/workspace/store/config/initializers/inflections.rb +16 -0
- package/template/src/templates/rails-app/workspace/store/config/locales/en.yml +31 -0
- package/template/src/templates/rails-app/workspace/store/config/master.key +1 -0
- package/template/src/templates/rails-app/workspace/store/config/puma.rb +41 -0
- package/template/src/templates/rails-app/workspace/store/config/routes.rb +4 -0
- package/template/src/templates/rails-app/workspace/store/config/storage.yml +34 -0
- package/template/src/templates/rails-app/workspace/store/config.ru +6 -0
- package/template/src/templates/rails-app/workspace/store/db/seeds.rb +9 -0
- package/template/src/templates/rails-app/workspace/store/log/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/public/400.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/404.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/406-unsupported-browser.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/422.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/500.html +114 -0
- package/template/src/templates/rails-app/workspace/store/public/icon.png +0 -0
- package/template/src/templates/rails-app/workspace/store/public/icon.svg +3 -0
- package/template/src/templates/rails-app/workspace/store/public/robots.txt +1 -0
- package/template/src/templates/rails-app/workspace/store/script/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/storage/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/controllers/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/helpers/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/integration/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/test/test_helper.rb +15 -0
- package/template/src/templates/rails-app/workspace/store/tmp/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/tmp/pids/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/tmp/storage/.keep +0 -0
- package/template/src/templates/rails-app/workspace/store/vendor/javascripts/.keep +0 -0
- package/template/tsconfig.json +16 -0
- package/template/uno.config.ts +10 -0
|
@@ -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;
|