@tutorialkit-rb/cli 0.1.5 → 0.1.7

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 (185) hide show
  1. package/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/template/.claude/skills/rails-file-management/SKILL.md +211 -0
  4. package/template/.claude/skills/rails-lesson-recipes/SKILL.md +415 -0
  5. package/template/.claude/skills/rails-wasm-author-constraints/SKILL.md +181 -0
  6. package/template/.claude/skills/tutorial-content-structure/SKILL.md +377 -0
  7. package/template/.claude/skills/tutorial-lesson-config/SKILL.md +389 -0
  8. package/template/.claude/skills/tutorial-quickstart/SKILL.md +440 -0
  9. package/template/.github/workflows/deploy.yml +85 -0
  10. package/template/.gitignore +10 -0
  11. package/template/CLAUDE.md +47 -0
  12. package/template/astro.config.ts +12 -0
  13. package/template/bin/build-pack +84 -0
  14. package/template/bin/build-ruby-base +180 -0
  15. package/template/bin/link-local +13 -0
  16. package/template/bin/unlink-local +7 -0
  17. package/template/cors-proxy/README.md +63 -0
  18. package/template/cors-proxy/package-lock.json +1504 -0
  19. package/template/cors-proxy/package.json +12 -0
  20. package/template/cors-proxy/src/index.ts +87 -0
  21. package/template/cors-proxy/wrangler.toml +7 -0
  22. package/template/netlify.toml +9 -0
  23. package/template/package.json +12 -4
  24. package/template/ruby-wasm/Gemfile +6 -0
  25. package/template/ruby-wasm/Gemfile.base +19 -0
  26. package/template/ruby-wasm/Gemfile.base.lock +50 -0
  27. package/template/ruby-wasm/Gemfile.lock +8 -0
  28. package/template/ruby-wasm/bin/pack-gems +368 -0
  29. package/template/ruby-wasm/package.json +6 -0
  30. package/template/src/components/FileManager.tsx +33 -0
  31. package/template/src/components/HeadTags.astro +6 -6
  32. package/template/src/components/HelpDropdown.tsx +1 -1
  33. package/template/src/components/RailsPathLinkHandler.tsx +2 -2
  34. package/template/src/components/ShellConfigurator.tsx +6 -1
  35. package/template/src/content/tutorial/1-getting-started/1-creating-your-first-rails-app/content.md +4 -4
  36. package/template/src/content/tutorial/1-getting-started/2-rails-console/content.md +4 -4
  37. package/template/src/content/tutorial/2-controllers/2-crud-operations/content.md +2 -2
  38. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/.tk-config.json +3 -0
  39. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/app/controllers/http_demo_controller.rb +65 -0
  40. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/app/views/http_demo/index.html.erb +172 -0
  41. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/_files/workspace/config/routes.rb +8 -0
  42. package/template/src/content/tutorial/9-outbound-http/1-making-http-requests/content.md +97 -0
  43. package/template/src/content/tutorial/9-outbound-http/meta.md +4 -0
  44. package/template/src/content/tutorial/meta.md +5 -0
  45. package/template/src/middleware.ts +14 -0
  46. package/template/src/templates/default/lib/boot-progress.js +49 -0
  47. package/template/src/templates/default/lib/http-bridge.js +55 -0
  48. package/template/src/templates/default/lib/patches/http_bridge.rb +167 -0
  49. package/template/src/templates/default/lib/rails.js +52 -5
  50. package/template/src/templates/default/lib/server.js +33 -1
  51. package/template/src/templates/default/package.json +4 -1
  52. package/template/src/templates/default/scripts/rails.js +1 -1
  53. package/template/src/templates/default/scripts/smoke-test.js +349 -0
  54. package/template/src/templates/default/scripts/wasi-loader.mjs +10 -0
  55. package/template/src/templates/default/workspace/_debug_app/.github/dependabot.yml +12 -0
  56. package/template/src/templates/default/workspace/_debug_app/.github/workflows/ci.yml +51 -0
  57. package/template/src/templates/default/workspace/_debug_app/.ruby-version +1 -0
  58. package/template/src/templates/default/workspace/_debug_app/Gemfile +37 -0
  59. package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/application.html.erb +1 -2
  60. package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/pwa/manifest.json.erb +2 -2
  61. package/template/src/templates/default/workspace/_debug_app/bin/dev +2 -0
  62. package/template/src/templates/default/workspace/_debug_app/bin/rake +4 -0
  63. package/template/src/templates/default/workspace/_debug_app/bin/setup +34 -0
  64. package/template/src/templates/default/workspace/_debug_app/config/application.rb +30 -0
  65. package/template/src/templates/default/workspace/_debug_app/config/cable.yml +10 -0
  66. package/template/src/templates/default/workspace/_debug_app/config/credentials.yml.enc +1 -0
  67. package/template/src/templates/default/workspace/_debug_app/config/master.key +1 -0
  68. package/template/src/templates/default/workspace/_debug_app/config/routes.rb +14 -0
  69. package/template/src/templates/default/workspace/_debug_app/test/models/.keep +0 -0
  70. package/template/src/templates/default/workspace/_debug_app/test/test_helper.rb +15 -0
  71. package/template/src/templates/default/workspace/_debug_app/tmp/.keep +0 -0
  72. package/template/src/templates/default/workspace/_debug_app/tmp/pids/.keep +0 -0
  73. package/template/src/templates/default/workspace/_debug_app/tmp/storage/.keep +0 -0
  74. package/template/src/templates/default/workspace/_debug_app/vendor/.keep +0 -0
  75. package/template/src/templates/rails-app/workspace/README.md +24 -0
  76. package/template/src/templates/rails-app/workspace/Rakefile +6 -0
  77. package/template/src/templates/rails-app/workspace/app/assets/images/.keep +0 -0
  78. package/template/src/templates/rails-app/workspace/app/assets/stylesheets/application.css +534 -0
  79. package/template/src/templates/rails-app/workspace/app/controllers/application_controller.rb +2 -0
  80. package/template/src/templates/rails-app/workspace/app/helpers/application_helper.rb +2 -0
  81. package/template/src/templates/rails-app/workspace/app/jobs/application_job.rb +7 -0
  82. package/template/src/templates/rails-app/workspace/app/mailers/application_mailer.rb +4 -0
  83. package/template/src/templates/rails-app/workspace/app/models/application_record.rb +3 -0
  84. package/template/src/templates/rails-app/workspace/app/models/concerns/.keep +0 -0
  85. package/template/src/templates/rails-app/workspace/app/views/layouts/application.html.erb +38 -0
  86. package/template/src/templates/rails-app/workspace/app/views/layouts/mailer.html.erb +13 -0
  87. package/template/src/templates/rails-app/workspace/app/views/layouts/mailer.text.erb +1 -0
  88. package/template/src/templates/rails-app/workspace/bin/rails +4 -0
  89. package/template/src/templates/rails-app/workspace/{store/config → config}/application.rb +1 -1
  90. package/template/src/templates/rails-app/workspace/config/boot.rb +3 -0
  91. package/template/src/templates/rails-app/workspace/config/database.yml +32 -0
  92. package/template/src/templates/rails-app/workspace/config/environment.rb +5 -0
  93. package/template/src/templates/rails-app/workspace/config/environments/development.rb +69 -0
  94. package/template/src/templates/rails-app/workspace/config/environments/production.rb +89 -0
  95. package/template/src/templates/rails-app/workspace/config/environments/test.rb +54 -0
  96. package/template/src/templates/rails-app/workspace/config/initializers/assets.rb +7 -0
  97. package/template/src/templates/rails-app/workspace/config/initializers/content_security_policy.rb +25 -0
  98. package/template/src/templates/rails-app/workspace/config/initializers/filter_parameter_logging.rb +8 -0
  99. package/template/src/templates/rails-app/workspace/config/initializers/inflections.rb +16 -0
  100. package/template/src/templates/rails-app/workspace/config/locales/en.yml +31 -0
  101. package/template/src/templates/rails-app/workspace/config/puma.rb +41 -0
  102. package/template/src/templates/rails-app/workspace/{store/config → config}/routes.rb +1 -1
  103. package/template/src/templates/rails-app/workspace/config/storage.yml +34 -0
  104. package/template/src/templates/rails-app/workspace/config.ru +6 -0
  105. package/template/src/templates/rails-app/workspace/db/seeds.rb +9 -0
  106. package/template/src/templates/rails-app/workspace/log/.keep +0 -0
  107. package/template/src/templates/rails-app/workspace/public/400.html +114 -0
  108. package/template/src/templates/rails-app/workspace/public/404.html +114 -0
  109. package/template/src/templates/rails-app/workspace/public/406-unsupported-browser.html +114 -0
  110. package/template/src/templates/rails-app/workspace/public/422.html +114 -0
  111. package/template/src/templates/rails-app/workspace/public/500.html +114 -0
  112. package/template/src/templates/rails-app/workspace/public/icon.png +0 -0
  113. package/template/src/templates/rails-app/workspace/public/icon.svg +3 -0
  114. package/template/src/templates/rails-app/workspace/public/robots.txt +1 -0
  115. package/template/src/templates/rails-app/workspace/script/.keep +0 -0
  116. package/template/src/templates/rails-app/workspace/storage/.keep +0 -0
  117. package/template/src/templates/rails-app/workspace/test/controllers/.keep +0 -0
  118. package/template/src/templates/rails-app/workspace/test/helpers/.keep +0 -0
  119. package/template/src/templates/rails-app/workspace/test/integration/.keep +0 -0
  120. package/template/src/templates/rails-app/workspace/tmp/.keep +0 -0
  121. package/template/src/templates/rails-app/workspace/tmp/cache/.keep +0 -0
  122. package/template/src/templates/rails-app/workspace/tmp/pids/.keep +0 -0
  123. package/template/src/templates/rails-app/workspace/tmp/sockets/.keep +0 -0
  124. package/template/src/templates/rails-app/workspace/tmp/storage/.keep +0 -0
  125. package/template/src/templates/rails-app/workspace/vendor/javascripts/.keep +0 -0
  126. package/template/tsconfig.json +3 -1
  127. package/template/uno.config.ts +17 -0
  128. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/README.md +0 -0
  129. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/Rakefile +0 -0
  130. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/assets/images/.keep +0 -0
  131. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/assets/stylesheets/application.css +0 -0
  132. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/controllers/application_controller.rb +0 -0
  133. /package/template/src/templates/{rails-app/workspace/store/app/models → default/workspace/_debug_app/app/controllers}/concerns/.keep +0 -0
  134. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/helpers/application_helper.rb +0 -0
  135. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/jobs/application_job.rb +0 -0
  136. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/mailers/application_mailer.rb +0 -0
  137. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/models/application_record.rb +0 -0
  138. /package/template/src/templates/{rails-app/workspace/store/log → default/workspace/_debug_app/app/models/concerns}/.keep +0 -0
  139. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/mailer.html.erb +0 -0
  140. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/layouts/mailer.text.erb +0 -0
  141. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/app/views/pwa/service-worker.js +0 -0
  142. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/bin/rails +0 -0
  143. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/boot.rb +0 -0
  144. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/database.yml +0 -0
  145. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environment.rb +0 -0
  146. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/development.rb +0 -0
  147. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/production.rb +0 -0
  148. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/environments/test.rb +0 -0
  149. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/assets.rb +0 -0
  150. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/content_security_policy.rb +0 -0
  151. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/filter_parameter_logging.rb +0 -0
  152. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/initializers/inflections.rb +0 -0
  153. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/locales/en.yml +0 -0
  154. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/puma.rb +0 -0
  155. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config/storage.yml +0 -0
  156. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/config.ru +0 -0
  157. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/db/seeds.rb +0 -0
  158. /package/template/src/templates/{rails-app/workspace/store/script → default/workspace/_debug_app/lib/tasks}/.keep +0 -0
  159. /package/template/src/templates/{rails-app/workspace/store/storage → default/workspace/_debug_app/log}/.keep +0 -0
  160. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/400.html +0 -0
  161. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/404.html +0 -0
  162. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/406-unsupported-browser.html +0 -0
  163. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/422.html +0 -0
  164. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/500.html +0 -0
  165. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/icon.png +0 -0
  166. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/icon.svg +0 -0
  167. /package/template/src/templates/{rails-app/workspace/store → default/workspace/_debug_app}/public/robots.txt +0 -0
  168. /package/template/src/templates/{rails-app/workspace/store/test/controllers → default/workspace/_debug_app/script}/.keep +0 -0
  169. /package/template/src/templates/{rails-app/workspace/store/test/helpers → default/workspace/_debug_app/storage}/.keep +0 -0
  170. /package/template/src/templates/{rails-app/workspace/store/test/integration → default/workspace/_debug_app/test/controllers}/.keep +0 -0
  171. /package/template/src/templates/{rails-app/workspace/store/tmp → default/workspace/_debug_app/test/fixtures/files}/.keep +0 -0
  172. /package/template/src/templates/{rails-app/workspace/store/tmp/pids → default/workspace/_debug_app/test/helpers}/.keep +0 -0
  173. /package/template/src/templates/{rails-app/workspace/store/tmp/storage → default/workspace/_debug_app/test/integration}/.keep +0 -0
  174. /package/template/src/templates/{rails-app/workspace/store/vendor/javascripts → default/workspace/_debug_app/test/mailers}/.keep +0 -0
  175. /package/template/src/templates/rails-app/workspace/{store/.ruby-version → .ruby-version} +0 -0
  176. /package/template/src/templates/rails-app/workspace/{store/Gemfile → Gemfile} +0 -0
  177. /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/application.js +0 -0
  178. /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/controllers/application.js +0 -0
  179. /package/template/src/templates/rails-app/workspace/{store/app → app}/javascript/controllers/index.js +0 -0
  180. /package/template/src/templates/rails-app/workspace/{store/bin → bin}/importmap +0 -0
  181. /package/template/src/templates/rails-app/workspace/{store/config → config}/cable.yml +0 -0
  182. /package/template/src/templates/rails-app/workspace/{store/config → config}/credentials.yml.enc +0 -0
  183. /package/template/src/templates/rails-app/workspace/{store/config → config}/importmap.rb +0 -0
  184. /package/template/src/templates/rails-app/workspace/{store/config → config}/master.key +0 -0
  185. /package/template/src/templates/rails-app/workspace/{store/test → test}/test_helper.rb +0 -0
@@ -0,0 +1,349 @@
1
+ /**
2
+ * Smoke test for the Rails WASM boot sequence.
3
+ *
4
+ * Exercises the same code paths as WebContainer (WASM load → VM init →
5
+ * Rails bootstrap) directly on the host via Node.js.
6
+ *
7
+ * Usage:
8
+ * npm run smoke # run all offline tests
9
+ * npm run smoke -- --rails-new # only rails new (chmod compat)
10
+ * npm run smoke -- --generate # only generator infrastructure
11
+ * npm run smoke -- --boot-app # only app boot (native ext compat)
12
+ * npm run smoke -- --server # only full server init (Rails.application.initialize!)
13
+ * npm run smoke -- --http # only Express/Rack bridge
14
+ * npm run smoke -- --fetch # only outbound HTTP fetch (needs network)
15
+ * npm run smoke -- --skip-rails # VM init only (no Rails bootstrap)
16
+ * npm run smoke -- --rails-new --boot-app # combine specific tests
17
+ *
18
+ * Dependencies are resolved automatically:
19
+ * --generate and --boot-app require rails new, which runs first.
20
+ *
21
+ * Limitations (node:wasi vs WebContainer):
22
+ * - Dir.chdir doesn't work (no WASI chdir syscall) — use full paths
23
+ * - Console REPL is interactive, can't be smoke-tested — but --boot-app
24
+ * exercises the same app boot path (Bundler.require) that console uses
25
+ * - Server is tested via --http (createRackServer), not bin/rails server
26
+ *
27
+ * WASM binary resolution (first match wins):
28
+ * 1. RAILS_WASM_PATH env var
29
+ * 2. ../../public/ruby.wasm (dynamic packing output)
30
+ * 3. node_modules/@ruby/wasm-wasi/dist/ruby.wasm (WebContainer placement)
31
+ * 4. node_modules/@rails-tutorial/wasm/dist/rails.wasm (npm package)
32
+ *
33
+ * Requires --no-turbo-fast-api-calls to work around a V8 GC bug in node:wasi's
34
+ * PathFilestatGet fast API callback. The npm script includes this flag.
35
+ */
36
+
37
+ import { existsSync } from 'node:fs';
38
+ import { performance } from 'node:perf_hooks';
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // CLI flags
42
+ // ---------------------------------------------------------------------------
43
+ const args = new Set(process.argv.slice(2));
44
+ const skipRails = args.has('--skip-rails');
45
+
46
+ // Known test flags (everything except --skip-rails)
47
+ const testFlags = ['--rails-new', '--generate', '--boot-app', '--server', '--http', '--fetch'];
48
+ const hasExplicitFlags = testFlags.some(f => args.has(f));
49
+
50
+ // No flags = run all tests. With flags = run only those (+ auto-resolved dependencies).
51
+ const runRailsNew = !hasExplicitFlags || args.has('--rails-new') || args.has('--generate') || args.has('--boot-app') || args.has('--server');
52
+ const runGenerate = !hasExplicitFlags || args.has('--generate');
53
+ const runBootApp = !hasExplicitFlags || args.has('--boot-app') || args.has('--server');
54
+ const runServer = !hasExplicitFlags || args.has('--server');
55
+ const runHttp = !hasExplicitFlags || args.has('--http');
56
+ const runFetch = !hasExplicitFlags || args.has('--fetch');
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Helpers
60
+ // ---------------------------------------------------------------------------
61
+ function timer() {
62
+ const start = performance.now();
63
+ return () => {
64
+ const ms = performance.now() - start;
65
+ return ms < 1000 ? `${Math.round(ms)}ms` : `${(ms / 1000).toFixed(1)}s`;
66
+ };
67
+ }
68
+
69
+ function log(msg) {
70
+ console.log(`[smoke] ${msg}`);
71
+ }
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Resolve WASM binary
75
+ // ---------------------------------------------------------------------------
76
+ const nodeModules = new URL('../node_modules', import.meta.url).pathname;
77
+
78
+ if (!existsSync(nodeModules)) {
79
+ log('node_modules/ not found — running npm install --ignore-scripts...');
80
+ const { execSync } = await import('node:child_process');
81
+ const templateDir = new URL('..', import.meta.url).pathname;
82
+ execSync('npm install --ignore-scripts', { cwd: templateDir, stdio: 'inherit' });
83
+ }
84
+
85
+ const wasmCandidates = [
86
+ { path: process.env.RAILS_WASM_PATH, label: 'RAILS_WASM_PATH env var' },
87
+ { path: new URL('../../../../public/ruby.wasm', import.meta.url).pathname, label: 'public/ruby.wasm (dynamic packing)' },
88
+ { path: new URL('../node_modules/@ruby/wasm-wasi/dist/ruby.wasm', import.meta.url).pathname, label: '@ruby/wasm-wasi' },
89
+ { path: new URL('../node_modules/@rails-tutorial/wasm/dist/rails.wasm', import.meta.url).pathname, label: '@rails-tutorial/wasm' },
90
+ ].filter(c => c.path);
91
+
92
+ const resolved = wasmCandidates.find(c => existsSync(c.path));
93
+
94
+ if (!resolved) {
95
+ console.error(
96
+ '\n Could not find a Ruby WASM binary. Searched:\n' +
97
+ wasmCandidates.map(c => ` - ${c.label}: ${c.path}`).join('\n') + '\n\n' +
98
+ ' Set RAILS_WASM_PATH to the path of your monolithic ruby.wasm binary,\n' +
99
+ ' or ensure @rails-tutorial/wasm is installed.\n',
100
+ );
101
+ process.exit(1);
102
+ }
103
+
104
+ log(`Using WASM binary from ${resolved.label}: ${resolved.path}`);
105
+
106
+ // ---------------------------------------------------------------------------
107
+ // Main
108
+ // ---------------------------------------------------------------------------
109
+ console.log('\n=== Rails WASM Smoke Test ===\n');
110
+ const totalTimer = timer();
111
+
112
+ // --- Step 0: Ensure workspace directory exists (WASI preopen requires it) ---
113
+ {
114
+ const { mkdirSync } = await import('node:fs');
115
+ const workspaceDir = new URL('../workspace', import.meta.url).pathname;
116
+ mkdirSync(workspaceDir, { recursive: true });
117
+ }
118
+
119
+ // --- Step 1: Load WASM + init VM (+ optional Rails bootstrap) ---
120
+ let vm;
121
+ {
122
+ log(skipRails ? 'Initializing VM (skip-rails)...' : 'Initializing VM + Rails...');
123
+ const initVM = (await import('../lib/rails.js')).default;
124
+ vm = await initVM({ skipRails, wasmPath: resolved.path });
125
+ }
126
+
127
+ if (skipRails) {
128
+ log('VM ready — Rails bootstrap skipped');
129
+ console.log(`\n=== PASSED — skip-rails (total ${totalTimer()}) ===\n`);
130
+ process.exit(0);
131
+ }
132
+
133
+ // --- Step 2: Verify Rails is loadable ---
134
+ {
135
+ vm.eval('require "rails"');
136
+ const version = vm.eval('Rails.version').toString();
137
+ log(`Rails.version = ${version}`);
138
+ }
139
+
140
+ // --- Step 3: Fetch bridge test ---
141
+ if (runFetch) {
142
+ log('Testing HTTP fetch bridge...');
143
+ const fetchResult = await vm.evalAsync(`
144
+ require "net/http"
145
+ require "uri"
146
+ require "json"
147
+ response = Net::HTTP.get(URI("https://httpbin.org/get?source=smoke_test"))
148
+ parsed = JSON.parse(response)
149
+ parsed["args"]["source"]
150
+ `);
151
+ if (fetchResult.toString() !== "smoke_test") {
152
+ throw new Error("HTTP fetch bridge test failed");
153
+ }
154
+ log('HTTP fetch bridge: PASS');
155
+ }
156
+
157
+ // --- Step 4: rails new + generate + boot-app + server ---
158
+ // These share a generated app, so they run as a group with cleanup.
159
+ if (runRailsNew || runGenerate || runBootApp || runServer) {
160
+ const { existsSync: existsSyncFs, rmSync, mkdirSync } = await import('node:fs');
161
+ const { join } = await import('node:path');
162
+
163
+ const workspaceHost = new URL('../workspace', import.meta.url).pathname;
164
+ const appName = '_smoke_test_app';
165
+ const appDirHost = join(workspaceHost, appName);
166
+
167
+ // Clean up any leftover from a previous failed run
168
+ if (existsSyncFs(appDirHost)) rmSync(appDirHost, { recursive: true, force: true });
169
+ mkdirSync(workspaceHost, { recursive: true });
170
+
171
+ try {
172
+ // --- rails new (always runs as prerequisite) ---
173
+ const railsNewTimer = timer();
174
+ log('Testing rails new (app generator + chmod compat)...');
175
+ await vm.evalAsync(`
176
+ ENV["HOME"] = "/workspace" unless ENV["HOME"]
177
+ require "rails/command"
178
+ require "rails/commands/application/application_command"
179
+ ARGV.replace(["new", "/workspace/${appName}", "--skip-bundle", "--skip-git",
180
+ "--skip-bootsnap", "--skip-brakeman", "--skip-dev-gems",
181
+ "--skip-kamal", "--skip-thruster", "--skip-docker",
182
+ "--skip-system-test", "--skip-rubocop",
183
+ "--skip-decrypted-diffs"])
184
+ Rails::Command.invoke(:application, ARGV)
185
+ `);
186
+
187
+ // Verify key files were created
188
+ const checks = ['Gemfile', 'Rakefile', 'config.ru', 'bin/rails', 'app/controllers/application_controller.rb'];
189
+ const missing = checks.filter(f => !existsSyncFs(join(appDirHost, f)));
190
+ if (missing.length > 0) {
191
+ throw new Error(`rails new: missing files: ${missing.join(', ')}`);
192
+ }
193
+ log(`rails new: PASS (${railsNewTimer()}) — ${checks.length} key files verified`);
194
+
195
+ // --- generator infrastructure ---
196
+ if (runGenerate) {
197
+ const genTimer = timer();
198
+ log('Testing generator infrastructure...');
199
+ await vm.evalAsync(`
200
+ require "rails/generators"
201
+ require "rails/generators/rails/model/model_generator"
202
+ require "rails/generators/rails/scaffold_controller/scaffold_controller_generator"
203
+
204
+ # Verify generator classes are loaded and functional
205
+ gen = Rails::Generators::ModelGenerator.new(["Post", "title:string"])
206
+ raise "destination_root mismatch" unless gen.destination_root == "/workspace/${appName}"
207
+ raise "behavior should be :invoke" unless gen.behavior == :invoke
208
+
209
+ # Verify template engine can write files through WASI
210
+ gen.create_file("app/models/post.rb", <<~RUBY)
211
+ class Post < ApplicationRecord
212
+ end
213
+ RUBY
214
+
215
+ # Verify scaffold controller generator loads
216
+ sgen = Rails::Generators::ScaffoldControllerGenerator.new(["Post"])
217
+ raise "scaffold dest mismatch" unless sgen.destination_root == "/workspace/${appName}"
218
+ `);
219
+
220
+ const genChecks = ['app/models/post.rb'];
221
+ const genMissing = genChecks.filter(f => !existsSyncFs(join(appDirHost, f)));
222
+ if (genMissing.length > 0) {
223
+ throw new Error(`generator infra: missing: ${genMissing.join(', ')}`);
224
+ }
225
+ log(`generator infrastructure: PASS (${genTimer()}) — classes loaded, WASI file creation works`);
226
+ }
227
+
228
+ // --- boot generated app ---
229
+ if (runBootApp) {
230
+ const bootTimer = timer();
231
+ log('Testing app boot (config/application.rb → Bundler.require)...');
232
+ await vm.evalAsync(`
233
+ require "/workspace/${appName}/config/application.rb"
234
+ `);
235
+ log(`app boot: PASS (${bootTimer()}) — config/application.rb loaded, Bundler.require succeeded`);
236
+ }
237
+
238
+ // --- full server boot + first request ---
239
+ // Exercises the same path as `rails server` + first HTTP request.
240
+ // Rails eager-loads code on the first request in development mode,
241
+ // loading 1600+ files. This catches WASM memory issues (e.g. "index out
242
+ // of bounds") that only manifest under this heavy load.
243
+ if (runServer) {
244
+ const serverTimer = timer();
245
+ log('Testing full server boot + first request...');
246
+
247
+ // Boot Rails app via config.ru (same as real server).
248
+ // config.ru loads config/environment.rb which calls initialize!
249
+ await vm.evalAsync(`
250
+ require "rack/builder"
251
+ require "rack/wasi/incoming_handler"
252
+ app = Rack::Builder.load_file("/workspace/${appName}/config.ru")
253
+ $incoming_handler = Rack::WASI::IncomingHandler.new(app)
254
+ `);
255
+
256
+ // Serve actual HTTP request through the full Rails stack
257
+ const { createRackServer } = await import('../lib/server.js');
258
+ const rackApp = await createRackServer(vm, { skipRackup: true });
259
+
260
+ await new Promise((resolve, reject) => {
261
+ const server = rackApp.listen(0, async () => {
262
+ const port = server.address().port;
263
+ log(`Rails app listening on port ${port}`);
264
+ try {
265
+ const res = await fetch(`http://localhost:${port}/up`);
266
+ log(`GET /up → ${res.status}`);
267
+ if (res.status >= 500) {
268
+ throw new Error(`server test: GET /up returned ${res.status}`);
269
+ }
270
+ } catch (err) {
271
+ server.close();
272
+ reject(err);
273
+ return;
274
+ }
275
+ server.close(() => resolve());
276
+ });
277
+ });
278
+
279
+ log(`server: PASS (${serverTimer()}) — Rails.application.initialize! + first request succeeded`);
280
+ }
281
+ } finally {
282
+ if (existsSyncFs(appDirHost)) rmSync(appDirHost, { recursive: true, force: true });
283
+ }
284
+ }
285
+
286
+ // --- Step 5: HTTP bridge test ---
287
+ if (runHttp) {
288
+ log('Starting HTTP bridge test...');
289
+
290
+ // Set up a minimal Rack app inline (no config.ru needed).
291
+ // This tests the Express→Rack bridge without requiring a full Rails app.
292
+ await vm.evalAsync(`
293
+ require "rack/builder"
294
+ require "rack/wasi/incoming_handler"
295
+
296
+ app = Rack::Builder.new do
297
+ run ->(env) { [200, {"content-type" => "text/plain"}, ["ok"]] }
298
+ end
299
+
300
+ $incoming_handler = Rack::WASI::IncomingHandler.new(app)
301
+ `);
302
+
303
+ const { createRackServer } = await import('../lib/server.js');
304
+ const app = await createRackServer(vm, { skipRackup: true });
305
+
306
+ await new Promise((resolve, reject) => {
307
+ const server = app.listen(0, async () => {
308
+ const port = server.address().port;
309
+ log(`Express listening on port ${port}`);
310
+
311
+ try {
312
+ const res = await fetch(`http://localhost:${port}/smoke-test`);
313
+ log(`GET /smoke-test → ${res.status}`);
314
+ if (res.status !== 200) {
315
+ throw new Error(`HTTP test failed with status ${res.status}`);
316
+ }
317
+ const body = await res.text();
318
+ if (body !== 'ok') {
319
+ throw new Error(`HTTP test: expected "ok", got "${body}"`);
320
+ }
321
+ log('HTTP bridge: PASS');
322
+ } catch (err) {
323
+ server.close();
324
+ reject(err);
325
+ return;
326
+ }
327
+
328
+ server.close(() => resolve());
329
+ });
330
+ });
331
+ }
332
+
333
+ // --- Cleanup: remove PGLite data created during smoke test ---
334
+ {
335
+ const { existsSync: existsSyncFs, rmSync, readdirSync } = await import('node:fs');
336
+ const { join } = await import('node:path');
337
+ const pgDataDir = new URL('../pgdata', import.meta.url).pathname;
338
+
339
+ if (existsSyncFs(pgDataDir)) {
340
+ for (const entry of readdirSync(pgDataDir)) {
341
+ if (entry === '.keep') continue;
342
+ rmSync(join(pgDataDir, entry), { recursive: true, force: true });
343
+ }
344
+ log('Cleaned up pgdata/');
345
+ }
346
+ }
347
+
348
+ console.log(`\n=== PASSED (total ${totalTimer()}) ===\n`);
349
+ process.exit(0);
@@ -0,0 +1,10 @@
1
+ // Node.js module resolution hook: remaps bare 'wasi' → 'node:wasi'
2
+ // WebContainer provides 'wasi' as a bare specifier; Node.js needs 'node:wasi'.
3
+ // Usage: node --loader ./scripts/wasi-loader.mjs
4
+
5
+ export function resolve(specifier, context, next) {
6
+ if (specifier === 'wasi') {
7
+ return { url: 'node:wasi', shortCircuit: true };
8
+ }
9
+ return next(specifier, context);
10
+ }
@@ -0,0 +1,12 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: daily
7
+ open-pull-requests-limit: 10
8
+ - package-ecosystem: github-actions
9
+ directory: "/"
10
+ schedule:
11
+ interval: daily
12
+ open-pull-requests-limit: 10
@@ -0,0 +1,51 @@
1
+ name: CI
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches: [ main ]
7
+
8
+ jobs:
9
+ scan_js:
10
+ runs-on: ubuntu-latest
11
+
12
+ steps:
13
+ - name: Checkout code
14
+ uses: actions/checkout@v4
15
+
16
+ - name: Set up Ruby
17
+ uses: ruby/setup-ruby@v1
18
+ with:
19
+ ruby-version: .ruby-version
20
+ bundler-cache: true
21
+
22
+ - name: Scan for security vulnerabilities in JavaScript dependencies
23
+ run: bin/importmap audit
24
+
25
+ test:
26
+ runs-on: ubuntu-latest
27
+
28
+ # services:
29
+ # redis:
30
+ # image: redis
31
+ # ports:
32
+ # - 6379:6379
33
+ # options: --health-cmd "redis-cli ping" --health-interval 10s --health-timeout 5s --health-retries 5
34
+ steps:
35
+ - name: Install packages
36
+ run: sudo apt-get update && sudo apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config
37
+
38
+ - name: Checkout code
39
+ uses: actions/checkout@v4
40
+
41
+ - name: Set up Ruby
42
+ uses: ruby/setup-ruby@v1
43
+ with:
44
+ ruby-version: .ruby-version
45
+ bundler-cache: true
46
+
47
+ - name: Run tests
48
+ env:
49
+ RAILS_ENV: test
50
+ # REDIS_URL: redis://localhost:6379/0
51
+ run: bin/rails db:test:prepare test
@@ -0,0 +1,37 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Bundle edge Rails instead: gem "rails", github: "rails/rails", branch: "main"
4
+ gem "rails", "~> 8.0.2"
5
+ # The modern asset pipeline for Rails [https://github.com/rails/propshaft]
6
+ gem "propshaft"
7
+ # Use sqlite3 as the database for Active Record
8
+ gem "sqlite3", ">= 2.1"
9
+ # Use the Puma web server [https://github.com/puma/puma]
10
+ gem "puma", ">= 5.0"
11
+ # Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
12
+ gem "importmap-rails"
13
+ # Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
14
+ gem "turbo-rails"
15
+ # Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
16
+ gem "stimulus-rails"
17
+ # Build JSON APIs with ease [https://github.com/rails/jbuilder]
18
+ gem "jbuilder"
19
+
20
+ # Use Active Model has_secure_password [https://guides.rubyonrails.org/active_model_basics.html#securepassword]
21
+ # gem "bcrypt", "~> 3.1.7"
22
+
23
+ # Windows does not include zoneinfo files, so bundle the tzinfo-data gem
24
+ gem "tzinfo-data", platforms: %i[ windows jruby ]
25
+
26
+ # Use the database-backed adapters for Rails.cache, Active Job, and Action Cable
27
+ gem "solid_cache"
28
+ gem "solid_queue"
29
+ gem "solid_cable"
30
+
31
+ # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images]
32
+ # gem "image_processing", "~> 1.2"
33
+
34
+ group :development, :test do
35
+ # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem
36
+ gem "debug", platforms: %i[ mri windows ], require: "debug/prelude"
37
+ end
@@ -1,7 +1,7 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title><%= content_for(:title) || "Store" %></title>
4
+ <title><%= content_for(:title) || "Debug App" %></title>
5
5
  <meta name="viewport" content="width=device-width,initial-scale=1">
6
6
  <meta name="apple-mobile-web-app-capable" content="yes">
7
7
  <meta name="mobile-web-app-capable" content="yes">
@@ -19,7 +19,6 @@
19
19
 
20
20
  <%# Includes all stylesheet files in app/assets/stylesheets %>
21
21
  <%= stylesheet_link_tag :app, "data-turbo-track": "reload" %>
22
- <%= javascript_importmap_tags %>
23
22
  </head>
24
23
 
25
24
  <body>
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "Store",
2
+ "name": "DebugApp",
3
3
  "icons": [
4
4
  {
5
5
  "src": "/icon.png",
@@ -16,7 +16,7 @@
16
16
  "start_url": "/",
17
17
  "display": "standalone",
18
18
  "scope": "/",
19
- "description": "Store.",
19
+ "description": "DebugApp.",
20
20
  "theme_color": "red",
21
21
  "background_color": "red"
22
22
  }
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec "./bin/rails", "server", *ARGV
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ APP_ROOT = File.expand_path("..", __dir__)
5
+
6
+ def system!(*args)
7
+ system(*args, exception: true)
8
+ end
9
+
10
+ FileUtils.chdir APP_ROOT do
11
+ # This script is a way to set up or update your development environment automatically.
12
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
13
+ # Add necessary setup steps to this file.
14
+
15
+ puts "== Installing dependencies =="
16
+ system("bundle check") || system!("bundle install")
17
+
18
+ # puts "\n== Copying sample files =="
19
+ # unless File.exist?("config/database.yml")
20
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
21
+ # end
22
+
23
+ puts "\n== Preparing database =="
24
+ system! "bin/rails db:prepare"
25
+
26
+ puts "\n== Removing old logs and tempfiles =="
27
+ system! "bin/rails log:clear tmp:clear"
28
+
29
+ unless ARGV.include?("--skip-server")
30
+ puts "\n== Starting development server =="
31
+ STDOUT.flush # flush the output before exec(2) so that it displays
32
+ exec "bin/dev"
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ require_relative "boot"
2
+
3
+ require "rails/all"
4
+
5
+ # Require the gems listed in Gemfile, including any gems
6
+ # you've limited to :test, :development, or :production.
7
+ Bundler.require(*Rails.groups)
8
+
9
+ module DebugApp
10
+ class Application < Rails::Application
11
+ # Initialize configuration defaults for originally generated Rails version.
12
+ config.load_defaults 8.0
13
+
14
+ # Please, add to the `ignore` list any other `lib` subdirectories that do
15
+ # not contain `.rb` files, or that should not be reloaded or eager loaded.
16
+ # Common ones are `templates`, `generators`, or `middleware`, for example.
17
+ config.autoload_lib(ignore: %w[assets tasks])
18
+
19
+ # Configuration for the application, engines, and railties goes here.
20
+ #
21
+ # These settings can be overridden in specific environments using the files
22
+ # in config/environments, which are processed later.
23
+ #
24
+ # config.time_zone = "Central Time (US & Canada)"
25
+ # config.eager_load_paths << Rails.root.join("extras")
26
+
27
+ # Don't generate system test files.
28
+ config.generators.system_tests = nil
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ development:
2
+ adapter: async
3
+
4
+ test:
5
+ adapter: test
6
+
7
+ production:
8
+ adapter: redis
9
+ url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10
+ channel_prefix: _debug_app_production
@@ -0,0 +1 @@
1
+ fmFP/4SmyTa9OK493+lQuvTxwNKPGEfIrIl+CUPcABSGhhVnzNZRKlWJN8dGx4vsSuFEY4cirFTPi6SszAwL6IWYpi+RrnSYU945a5rmXNO5G0QwQ93AtlL5vcOe0Tj4UYumbHWsn0/lthdqFRY4TOmDtxL+LWcsJRIS26fqdm/oEKaLZ8T2S3IikkthzwuUHzmSpXfW6dZlEvwIze0NZqqQJhnsQSMxHDAM/ROEqzWFdABW8mksHI8C89eY8lAX+cBcp+QHb30MMTYYayjWkn5A+iMOT+L95At3ICETkrQMrXt6Yz3SX/1sYti2lfI8z7XAQPJ54RlHiXc40V4wjfzPc/YkFvR1ubQXm1NE6yh/hGoC2OzWuKAcp+kSjO7wPJ9O2SCM8CgUhYVLyGAavIT3PUjKpxYKRY/YuE2ij3bSePp6PowXCnHUhxVUmUvSOmlSmS+ayP0yrXdV+kj1hTCZXgqzaGUhKpIRzvkVUzJfDR3j6PfhvPdj--PXMuLZ2/BnSPJCaI--9xCqfbZIAFLPcKcdRo7H5A==
@@ -0,0 +1 @@
1
+ 0f07fe4aaff8cfc33878e6f96768fbef
@@ -0,0 +1,14 @@
1
+ Rails.application.routes.draw do
2
+ # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html
3
+
4
+ # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
5
+ # Can be used by load balancers and uptime monitors to verify that the app is live.
6
+ get "up" => "rails/health#show", as: :rails_health_check
7
+
8
+ # Render dynamic PWA files from app/views/pwa/* (remember to link manifest in application.html.erb)
9
+ # get "manifest" => "rails/pwa#manifest", as: :pwa_manifest
10
+ # get "service-worker" => "rails/pwa#service_worker", as: :pwa_service_worker
11
+
12
+ # Defines the root path route ("/")
13
+ # root "posts#index"
14
+ end
@@ -0,0 +1,15 @@
1
+ ENV["RAILS_ENV"] ||= "test"
2
+ require_relative "../config/environment"
3
+ require "rails/test_help"
4
+
5
+ module ActiveSupport
6
+ class TestCase
7
+ # Run tests in parallel with specified workers
8
+ parallelize(workers: :number_of_processors, with: :threads)
9
+
10
+ # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
11
+ fixtures :all
12
+
13
+ # Add more helper methods to be used by all tests here...
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # README
2
+
3
+ This README would normally document whatever steps are necessary to get the
4
+ application up and running.
5
+
6
+ Things you may want to cover:
7
+
8
+ * Ruby version
9
+
10
+ * System dependencies
11
+
12
+ * Configuration
13
+
14
+ * Database creation
15
+
16
+ * Database initialization
17
+
18
+ * How to run the test suite
19
+
20
+ * Services (job queues, cache servers, search engines, etc.)
21
+
22
+ * Deployment instructions
23
+
24
+ * ...
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative "config/application"
5
+
6
+ Rails.application.load_tasks