homura-runtime 0.2.27 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +39 -0
- data/exe/auto-await +9 -9
- data/exe/homura-build +23 -23
- data/lib/{cloudflare_workers → homura/runtime}/ai.rb +2 -2
- data/lib/{cloudflare_workers → homura/runtime}/async_registry.rb +2 -2
- data/lib/{cloudflare_workers → homura/runtime}/auto_await/analyzer.rb +1 -1
- data/lib/{cloudflare_workers → homura/runtime}/auto_await/transformer.rb +1 -1
- data/lib/{cloudflare_workers → homura/runtime}/build_support.rb +1 -1
- data/lib/{cloudflare_workers → homura/runtime}/cache.rb +1 -1
- data/lib/{cloudflare_workers → homura/runtime}/durable_object.rb +4 -4
- data/lib/{cloudflare_workers → homura/runtime}/http.rb +1 -1
- data/lib/{cloudflare_workers → homura/runtime}/multipart.rb +1 -1
- data/lib/{cloudflare_workers → homura/runtime}/queue.rb +2 -2
- data/lib/{cloudflare_workers → homura/runtime}/scheduled.rb +5 -5
- data/lib/{cloudflare_workers → homura/runtime}/stream.rb +1 -1
- data/lib/homura/runtime/version.rb +5 -0
- data/lib/{cloudflare_workers.rb → homura/runtime.rb} +25 -25
- data/runtime/worker_module.mjs +7 -7
- metadata +16 -16
- data/lib/cloudflare_workers/version.rb +0 -5
- /data/lib/{cloudflare_workers → homura/runtime}/email.rb +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8add04211534a53fdb39d920d9b6dc143005acabe260f15d45c3b3a54b95dc9a
|
|
4
|
+
data.tar.gz: ab5115722674df8f52a7839a7b80f135f84f3e686988b85e1649d1785009ce0d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3147e261629b3ba277fce2729dcf4deb47dbea060db34111d716e36d188b4dc15f6f3e0a88186c774a9d9884d70f289ac8bfb03cd57b6c6d4d517018d9ca88cf
|
|
7
|
+
data.tar.gz: d5304208f712e6b4ac7f609489558b28e1609927180ae7e5387423363f9d536cbbb31730667d7084d903fecf9c63bc9bebb4cd558bf2acbdef911807042f2a31
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.1 (2026-04-29)
|
|
4
|
+
|
|
5
|
+
- Fix `release-gems.yml` `Resolve gem target` step: now reads
|
|
6
|
+
`gems/homura-runtime/lib/homura/runtime/version.rb` (was the
|
|
7
|
+
pre-rename `gems/homura-runtime/lib/cloudflare_workers/version.rb`,
|
|
8
|
+
which 0.3.0's tag-push publish failed on).
|
|
9
|
+
- No code changes since 0.3.0; this version exists only because
|
|
10
|
+
re-tagging a published version is forbidden by the project's
|
|
11
|
+
release rules. 0.3.0 was never published to RubyGems.
|
|
12
|
+
|
|
13
|
+
## 0.3.0 (2026-04-29) — BREAKING: cloudflare_workers naming eliminated
|
|
14
|
+
|
|
15
|
+
The "cloudflare_workers" branding everywhere inside the gem is gone.
|
|
16
|
+
gem name is `homura-runtime`, the module is now `HomuraRuntime`, the
|
|
17
|
+
require path is `require 'homura/runtime'`, and the Rack handler is
|
|
18
|
+
`Rack::Handler::Homura`. Apps that pinned ≤ 0.2.x and referenced
|
|
19
|
+
either `CloudflareWorkers::*` or `require 'cloudflare_workers'`
|
|
20
|
+
must update.
|
|
21
|
+
|
|
22
|
+
Renames:
|
|
23
|
+
|
|
24
|
+
| was | now |
|
|
25
|
+
|---|---|
|
|
26
|
+
| `module CloudflareWorkers` | `module HomuraRuntime` |
|
|
27
|
+
| `CloudflareWorkersIO` (stdout/stderr replacement class) | `HomuraRuntimeIO` |
|
|
28
|
+
| `Rack::Handler::CloudflareWorkers` | `Rack::Handler::Homura` |
|
|
29
|
+
| `require 'cloudflare_workers'` | `require 'homura/runtime'` |
|
|
30
|
+
| `lib/cloudflare_workers.rb` | `lib/homura/runtime.rb` |
|
|
31
|
+
| `lib/cloudflare_workers/*.rb` | `lib/homura/runtime/*.rb` |
|
|
32
|
+
|
|
33
|
+
`Cloudflare` namespace (binding wrappers like `Cloudflare::HTTP`,
|
|
34
|
+
`Cloudflare::D1Error`, `Cloudflare.js_promise?`) and Cloudflare-
|
|
35
|
+
specific Rack env keys (`env['cloudflare.DB']`, `env['cloudflare.KV']`,
|
|
36
|
+
…) are unchanged — those are factual descriptions of the underlying
|
|
37
|
+
Cloudflare Workers bindings, not branding.
|
|
38
|
+
|
|
39
|
+
No backward-compat shim. Per the project's `v1未満は破壊的に置き換え`
|
|
40
|
+
rule, old constant aliases are not preserved.
|
|
41
|
+
|
|
3
42
|
## 0.2.27 (2026-04-29)
|
|
4
43
|
|
|
5
44
|
- `Rack::Handler::CloudflareWorkers.run` now goes through
|
data/exe/auto-await
CHANGED
|
@@ -15,18 +15,18 @@
|
|
|
15
15
|
|
|
16
16
|
require 'fileutils'
|
|
17
17
|
require 'pathname'
|
|
18
|
-
require_relative '../lib/
|
|
18
|
+
require_relative '../lib/homura/runtime/build_support'
|
|
19
19
|
|
|
20
20
|
# homura-runtime lib path resolution (with legacy alias fallback)
|
|
21
21
|
runtime_lib = ENV['CFW_RUNTIME_LIB']
|
|
22
22
|
unless runtime_lib
|
|
23
|
-
runtime_lib =
|
|
23
|
+
runtime_lib = HomuraRuntime::BuildSupport.runtime_root(current_file: __FILE__).join('lib').to_s
|
|
24
24
|
end
|
|
25
25
|
$LOAD_PATH.unshift(runtime_lib) unless $LOAD_PATH.include?(runtime_lib)
|
|
26
26
|
|
|
27
|
-
require '
|
|
28
|
-
require '
|
|
29
|
-
require '
|
|
27
|
+
require 'homura/runtime/async_registry'
|
|
28
|
+
require 'homura/runtime/auto_await/analyzer'
|
|
29
|
+
require 'homura/runtime/auto_await/transformer'
|
|
30
30
|
|
|
31
31
|
options = { input: nil, output: nil, debug: ENV['CLOUDFLARE_WORKERS_AUTO_AWAIT_DEBUG'] == '1' }
|
|
32
32
|
|
|
@@ -43,10 +43,10 @@ abort('Usage: auto-await --input DIR --output DIR [--debug]') unless options[:in
|
|
|
43
43
|
input_root = Pathname(options[:input]).expand_path
|
|
44
44
|
output_root = Pathname(options[:output]).expand_path
|
|
45
45
|
|
|
46
|
-
registry =
|
|
46
|
+
registry = HomuraRuntime::AsyncRegistry.instance
|
|
47
47
|
|
|
48
48
|
# Auto-load async source registrations from all loaded gems.
|
|
49
|
-
|
|
49
|
+
HomuraRuntime::AsyncRegistry.auto_load_gem_async_sources(debug: options[:debug])
|
|
50
50
|
|
|
51
51
|
# Load project-specific async source registrations if present.
|
|
52
52
|
project_async = File.join(Dir.pwd, 'lib', 'homura_async_sources.rb')
|
|
@@ -75,7 +75,7 @@ paths.each do |path|
|
|
|
75
75
|
has_existing_await = source.include?('.__await__')
|
|
76
76
|
|
|
77
77
|
begin
|
|
78
|
-
analyzer =
|
|
78
|
+
analyzer = HomuraRuntime::AutoAwait::Analyzer.new(registry, debug: options[:debug])
|
|
79
79
|
buffer, nodes = analyzer.process(source, path)
|
|
80
80
|
needs_magic_only_output = has_existing_await && !has_magic
|
|
81
81
|
|
|
@@ -89,7 +89,7 @@ paths.each do |path|
|
|
|
89
89
|
if nodes.empty?
|
|
90
90
|
source
|
|
91
91
|
else
|
|
92
|
-
|
|
92
|
+
HomuraRuntime::AutoAwait::Transformer.transform(source, nodes, buffer)
|
|
93
93
|
end
|
|
94
94
|
unless has_magic
|
|
95
95
|
transformed = "# await: true\n" + transformed
|
data/exe/homura-build
CHANGED
|
@@ -8,12 +8,12 @@ require 'fileutils'
|
|
|
8
8
|
require 'open3'
|
|
9
9
|
require 'optparse'
|
|
10
10
|
require 'pathname'
|
|
11
|
-
require_relative '../lib/
|
|
11
|
+
require_relative '../lib/homura/runtime/build_support'
|
|
12
12
|
|
|
13
|
-
module
|
|
13
|
+
module HomuraRuntimeBuild
|
|
14
14
|
class << self
|
|
15
15
|
def gem_root
|
|
16
|
-
|
|
16
|
+
HomuraRuntime::BuildSupport.runtime_root(current_file: __FILE__)
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def runtime_dir
|
|
@@ -25,11 +25,11 @@ module CloudflareWorkersBuild
|
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def gem_lib(*names)
|
|
28
|
-
|
|
28
|
+
HomuraRuntime::BuildSupport.gem_lib(*names)
|
|
29
29
|
end
|
|
30
30
|
|
|
31
31
|
def gem_vendor(*names)
|
|
32
|
-
|
|
32
|
+
HomuraRuntime::BuildSupport.gem_vendor(*names)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
end
|
|
@@ -72,8 +72,8 @@ end.parse!
|
|
|
72
72
|
options[:standalone] = true if options[:with_db]
|
|
73
73
|
|
|
74
74
|
root = Pathname(options[:root]).expand_path
|
|
75
|
-
options[:templates_namespace] ||=
|
|
76
|
-
options[:assets_namespace] ||=
|
|
75
|
+
options[:templates_namespace] ||= HomuraRuntime::BuildSupport.standalone_namespace(root, 'Templates') if options[:standalone]
|
|
76
|
+
options[:assets_namespace] ||= HomuraRuntime::BuildSupport.standalone_namespace(root, 'Assets') if options[:standalone]
|
|
77
77
|
|
|
78
78
|
if options[:standalone]
|
|
79
79
|
Dir.chdir(root) { require 'bundler/setup' }
|
|
@@ -109,7 +109,7 @@ def run_opal_homura!(root, opal_input, opal_output)
|
|
|
109
109
|
'-I', 'gems/sinatra-homura/lib',
|
|
110
110
|
'-I', 'gems/sequel-d1/lib',
|
|
111
111
|
'-I', 'lib', '-I', 'vendor', '-I', 'build',
|
|
112
|
-
'-r', 'opal_patches', '-r', '
|
|
112
|
+
'-r', 'opal_patches', '-r', 'homura/runtime',
|
|
113
113
|
'-r', 'homura_templates', '-r', 'homura_assets',
|
|
114
114
|
'-o', opal_output,
|
|
115
115
|
opal_input
|
|
@@ -123,15 +123,15 @@ def run_opal_homura!(root, opal_input, opal_output)
|
|
|
123
123
|
end
|
|
124
124
|
|
|
125
125
|
def homura_vendor_from_gemfile(project_root)
|
|
126
|
-
|
|
126
|
+
HomuraRuntime::BuildSupport.vendor_from_gemfile(project_root)
|
|
127
127
|
end
|
|
128
128
|
|
|
129
129
|
def run_opal_standalone!(root, opal_input, opal_output, with_db:)
|
|
130
|
-
load_paths =
|
|
130
|
+
load_paths = HomuraRuntime::BuildSupport.standalone_load_paths(root, with_db: with_db)
|
|
131
131
|
|
|
132
132
|
argv = ['bundle', 'exec', 'opal', '-c', '-E', '--esm', '--no-source-map']
|
|
133
133
|
load_paths.each { |p| argv.push('-I', p) }
|
|
134
|
-
argv += %w[-r opal_patches -r
|
|
134
|
+
argv += %w[-r opal_patches -r homura/runtime -r app_templates -r app_assets -o] + [opal_output, opal_input]
|
|
135
135
|
|
|
136
136
|
stderr_log = root.join('build/opal.stderr.log')
|
|
137
137
|
FileUtils.mkdir_p(root.join('build'))
|
|
@@ -185,7 +185,7 @@ unless options[:standalone]
|
|
|
185
185
|
run!(['ruby', 'bin/inline-routes-for-opal'], chdir: root)
|
|
186
186
|
run!(
|
|
187
187
|
[
|
|
188
|
-
'ruby',
|
|
188
|
+
'ruby', HomuraRuntimeBuild.exe_path('auto-await').to_s,
|
|
189
189
|
'--input', 'app',
|
|
190
190
|
'--output', 'build/auto_await/app'
|
|
191
191
|
],
|
|
@@ -193,7 +193,7 @@ unless options[:standalone]
|
|
|
193
193
|
)
|
|
194
194
|
run!(
|
|
195
195
|
[
|
|
196
|
-
'ruby',
|
|
196
|
+
'ruby', HomuraRuntimeBuild.exe_path('auto-await').to_s,
|
|
197
197
|
'--input', 'build/routes_app_class_eval.rb',
|
|
198
198
|
'--output', 'build/auto_await/routes_app_class_eval.rb'
|
|
199
199
|
],
|
|
@@ -201,7 +201,7 @@ unless options[:standalone]
|
|
|
201
201
|
)
|
|
202
202
|
run!(
|
|
203
203
|
[
|
|
204
|
-
'ruby',
|
|
204
|
+
'ruby', HomuraRuntimeBuild.exe_path('compile-erb').to_s,
|
|
205
205
|
'--input', 'views',
|
|
206
206
|
'--output', 'build/homura_templates.rb',
|
|
207
207
|
'--namespace', 'HomuraTemplates'
|
|
@@ -210,7 +210,7 @@ unless options[:standalone]
|
|
|
210
210
|
)
|
|
211
211
|
run!(
|
|
212
212
|
[
|
|
213
|
-
'ruby',
|
|
213
|
+
'ruby', HomuraRuntimeBuild.exe_path('compile-assets').to_s,
|
|
214
214
|
'--input', 'public',
|
|
215
215
|
'--output', 'build/homura_assets.rb',
|
|
216
216
|
'--namespace', 'HomuraAssets'
|
|
@@ -229,10 +229,10 @@ unless options[:standalone]
|
|
|
229
229
|
FileUtils.rm_f(temp_input) if temp_input
|
|
230
230
|
end
|
|
231
231
|
else
|
|
232
|
-
|
|
232
|
+
HomuraRuntime::BuildSupport.ensure_standalone_runtime(root, current_file: __FILE__)
|
|
233
233
|
run!(
|
|
234
234
|
[
|
|
235
|
-
'ruby',
|
|
235
|
+
'ruby', HomuraRuntimeBuild.exe_path('compile-erb').to_s,
|
|
236
236
|
'--input', 'views',
|
|
237
237
|
'--output', 'build/app_templates.rb',
|
|
238
238
|
'--namespace', options[:templates_namespace]
|
|
@@ -241,7 +241,7 @@ else
|
|
|
241
241
|
)
|
|
242
242
|
run!(
|
|
243
243
|
[
|
|
244
|
-
'ruby',
|
|
244
|
+
'ruby', HomuraRuntimeBuild.exe_path('compile-assets').to_s,
|
|
245
245
|
'--input', 'public',
|
|
246
246
|
'--output', 'build/app_assets.rb',
|
|
247
247
|
'--namespace', options[:assets_namespace]
|
|
@@ -256,7 +256,7 @@ else
|
|
|
256
256
|
if root.join('app').directory?
|
|
257
257
|
run!(
|
|
258
258
|
[
|
|
259
|
-
'ruby',
|
|
259
|
+
'ruby', HomuraRuntimeBuild.exe_path('auto-await').to_s,
|
|
260
260
|
'--input', 'app',
|
|
261
261
|
'--output', 'build/auto_await/app'
|
|
262
262
|
],
|
|
@@ -265,7 +265,7 @@ else
|
|
|
265
265
|
elsif root.join('app.rb').file?
|
|
266
266
|
run!(
|
|
267
267
|
[
|
|
268
|
-
'ruby',
|
|
268
|
+
'ruby', HomuraRuntimeBuild.exe_path('auto-await').to_s,
|
|
269
269
|
'--input', 'app.rb',
|
|
270
270
|
'--output', 'build/auto_await/app.rb'
|
|
271
271
|
],
|
|
@@ -282,7 +282,7 @@ else
|
|
|
282
282
|
# `build/auto_await/gem_<basename>/<sub>` and
|
|
283
283
|
# `standalone_load_paths` puts those rewritten copies ahead of the
|
|
284
284
|
# gem's untransformed `lib/`.
|
|
285
|
-
|
|
285
|
+
HomuraRuntime::BuildSupport.opal_gem_paths(root).each do |gem_path|
|
|
286
286
|
%w[lib].each do |sub|
|
|
287
287
|
src = gem_path.join(sub)
|
|
288
288
|
next unless src.directory?
|
|
@@ -290,7 +290,7 @@ else
|
|
|
290
290
|
FileUtils.mkdir_p(out)
|
|
291
291
|
run!(
|
|
292
292
|
[
|
|
293
|
-
'ruby',
|
|
293
|
+
'ruby', HomuraRuntimeBuild.exe_path('auto-await').to_s,
|
|
294
294
|
'--input', src.to_s,
|
|
295
295
|
'--output', out.to_s
|
|
296
296
|
],
|
|
@@ -317,7 +317,7 @@ patch_rel = root.join(patch_rel) unless patch_rel.absolute?
|
|
|
317
317
|
run!(
|
|
318
318
|
[
|
|
319
319
|
'node',
|
|
320
|
-
|
|
320
|
+
HomuraRuntimeBuild.runtime_dir.join('patch-opal-evals.mjs').to_s,
|
|
321
321
|
patch_rel.relative_path_from(root).to_s
|
|
322
322
|
],
|
|
323
323
|
chdir: root
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
#
|
|
22
22
|
# Streaming (`stream: true`) returns the raw JS ReadableStream wrapped
|
|
23
23
|
# in `Cloudflare::AI::Stream` so a route can hand it to a Server-Sent
|
|
24
|
-
# Events response. See `lib/
|
|
24
|
+
# Events response. See `lib/homura/runtime.rb#build_js_response`
|
|
25
25
|
# for the SSE / ReadableStream pass-through.
|
|
26
26
|
|
|
27
27
|
require 'json'
|
|
@@ -71,7 +71,7 @@ module Cloudflare
|
|
|
71
71
|
# keeps the returned value alive through the local). Do NOT
|
|
72
72
|
# refactor this so the backtick is the method's last expression
|
|
73
73
|
# or the Promise will be silently dropped (same pitfall
|
|
74
|
-
# documented in lib/
|
|
74
|
+
# documented in lib/homura/runtime/{cache,queue}.rb —
|
|
75
75
|
# Phase 11B audit).
|
|
76
76
|
js_promise = `
|
|
77
77
|
(async function() {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'set'
|
|
4
4
|
|
|
5
|
-
module
|
|
5
|
+
module HomuraRuntime
|
|
6
6
|
class AsyncRegistry
|
|
7
7
|
class Builder
|
|
8
8
|
def initialize(registry)
|
|
@@ -137,7 +137,7 @@ end
|
|
|
137
137
|
# Phase 17.5 — Auto-Await: register runtime gem async sources.
|
|
138
138
|
# Each binding declares which methods return Promises so the
|
|
139
139
|
# build-time analyzer can insert .__await__ automatically.
|
|
140
|
-
|
|
140
|
+
HomuraRuntime::AsyncRegistry.register_async_source do
|
|
141
141
|
async_method 'Cloudflare::D1Database', :execute
|
|
142
142
|
async_method 'Cloudflare::D1Database', :get_first_row
|
|
143
143
|
async_method 'Cloudflare::D1Database', :execute_insert
|
|
@@ -165,7 +165,7 @@ module Cloudflare
|
|
|
165
165
|
# gets dropped and the caller's `__await__` receives `undefined`
|
|
166
166
|
# instead of waiting for `cache.put` to resolve. That was the
|
|
167
167
|
# silent bug: the inner `await` ran, but the outer await had
|
|
168
|
-
# already proceeded. See lib/
|
|
168
|
+
# already proceeded. See lib/homura/runtime/scheduled.rb for
|
|
169
169
|
# the same Opal multi-line x-string constraint.
|
|
170
170
|
# Warn ONCE per isolate on a nil cache. Non-Workers runtimes
|
|
171
171
|
# hit `Cache.new(nil, ...)` intentionally (tests, safe fall-back
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
# session servers, and collaborative state machines without any external
|
|
12
12
|
# store.
|
|
13
13
|
#
|
|
14
|
-
# Three responsibilities here, analogous to `
|
|
14
|
+
# Three responsibilities here, analogous to `homura/runtime.rb`'s
|
|
15
15
|
# D1 / KV / R2 section:
|
|
16
16
|
#
|
|
17
17
|
# 1. `Cloudflare::DurableObjectNamespace` — wraps the binding JS object
|
|
@@ -163,7 +163,7 @@ module Cloudflare
|
|
|
163
163
|
response_klass = Cloudflare::HTTPResponse
|
|
164
164
|
do_class_label = 'DurableObjectStub'
|
|
165
165
|
|
|
166
|
-
# Single-line IIFE — see `lib/
|
|
166
|
+
# Single-line IIFE — see `lib/homura/runtime/cache.rb#put`
|
|
167
167
|
# for why Opal can silently drop a multi-line x-string Promise.
|
|
168
168
|
js_promise = `(async function(stub, url_str, method_str, js_headers, js_body, Kernel, err_klass, do_class_label) { var init = { method: method_str, headers: js_headers }; if (js_body !== null && js_body !== undefined && js_body !== Opal.nil) { init.body = js_body; } var resp; try { resp = await stub.fetch(url_str, init); } catch (e) { Kernel.$raise(err_klass.$new(e && e.message ? e.message : String(e), Opal.hash({ operation: 'stub.fetch', do_class: do_class_label }))); } var text = ''; try { text = await resp.text(); } catch (_) { text = ''; } var hk = []; var hv = []; if (resp.headers && typeof resp.headers.forEach === 'function') { resp.headers.forEach(function(v, k) { hk.push(String(k).toLowerCase()); hv.push(String(v)); }); } return { status: resp.status|0, text: text, hkeys: hk, hvals: hv }; })(#{js_stub}, #{url_str}, #{method_str}, #{js_headers}, #{js_body}, #{Kernel}, #{err_klass}, #{do_class_label})`
|
|
169
169
|
|
|
@@ -357,7 +357,7 @@ module Cloudflare
|
|
|
357
357
|
#
|
|
358
358
|
# Kept as a single-line backtick x-string — Opal's compiler refuses
|
|
359
359
|
# multi-line backticks as expressions (same constraint documented
|
|
360
|
-
# in `lib/
|
|
360
|
+
# in `lib/homura/runtime/scheduled.rb#install_dispatcher`).
|
|
361
361
|
# Installs FOUR hooks: fetch dispatcher + 3 websocket event
|
|
362
362
|
# dispatchers. Each wraps Ruby exceptions in a console.error so a
|
|
363
363
|
# bad handler doesn't crash the DO.
|
|
@@ -516,7 +516,7 @@ module Cloudflare
|
|
|
516
516
|
|
|
517
517
|
# The incoming `request` argument passed to DO handlers. `body_text`
|
|
518
518
|
# is pre-awaited by the JS dispatcher because Ruby runs synchronously
|
|
519
|
-
# under Opal (same pattern as `Rack::Handler::
|
|
519
|
+
# under Opal (same pattern as `Rack::Handler::Homura.call`).
|
|
520
520
|
class DurableObjectRequest
|
|
521
521
|
attr_reader :js_request, :body
|
|
522
522
|
|
|
@@ -85,7 +85,7 @@ module Cloudflare
|
|
|
85
85
|
# at end-of-method silently drops the Promise. Do NOT refactor
|
|
86
86
|
# this into `def fetch ... end` with the backtick as the last
|
|
87
87
|
# expression. See the single-line IIFE pattern used in
|
|
88
|
-
# lib/
|
|
88
|
+
# lib/homura/runtime/{cache,queue,durable_object}.rb#put for
|
|
89
89
|
# the alternative that survives either position. (Phase 11B audit.)
|
|
90
90
|
js_promise = `
|
|
91
91
|
(async function() {
|
|
@@ -83,7 +83,7 @@ module Cloudflare
|
|
|
83
83
|
# NOTE: single-line backtick x-string so Opal emits it as an
|
|
84
84
|
# expression (multi-line x-strings compile to raw statements and
|
|
85
85
|
# would silently return `undefined`). Same gotcha documented
|
|
86
|
-
# elsewhere in this codebase (see lib/
|
|
86
|
+
# elsewhere in this codebase (see lib/homura/runtime.rb).
|
|
87
87
|
def to_uint8_array
|
|
88
88
|
`(function(s) { var len = s.length; var out = new Uint8Array(len); for (var i = 0; i < len; i++) { out[i] = s.charCodeAt(i) & 0xff; } return out; })(#{@bytes_binstr})`
|
|
89
89
|
end
|
|
@@ -63,7 +63,7 @@ module Cloudflare
|
|
|
63
63
|
def available?
|
|
64
64
|
js = @js
|
|
65
65
|
# Opal's Ruby nil is a runtime sentinel (Opal.nil), not JS null.
|
|
66
|
-
# See `lib/
|
|
66
|
+
# See `lib/homura/runtime/cache.rb#available?` for the same
|
|
67
67
|
# pattern and rationale.
|
|
68
68
|
!!`(#{js} !== null && #{js} !== undefined && #{js} !== Opal.nil)`
|
|
69
69
|
end
|
|
@@ -86,7 +86,7 @@ module Cloudflare
|
|
|
86
86
|
`#{js_opts}.delaySeconds = #{delay_seconds.to_i}` if delay_seconds
|
|
87
87
|
`#{js_opts}.contentType = #{content_type.to_s}` if content_type
|
|
88
88
|
|
|
89
|
-
# Single-line IIFE — see `lib/
|
|
89
|
+
# Single-line IIFE — see `lib/homura/runtime/cache.rb#put`
|
|
90
90
|
# for the Opal multi-line x-string quirk. Passing arguments in
|
|
91
91
|
# explicitly (rather than interpolating inside the template)
|
|
92
92
|
# keeps the Promise a first-class expression.
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
#
|
|
24
24
|
# 3. Exposes `Cloudflare::Scheduled.app=` so non-Sinatra Rack apps
|
|
25
25
|
# can hook the dispatcher too. By default, `Rack::Handler::
|
|
26
|
-
#
|
|
26
|
+
# HomuraRuntime.app` (set by `run app` in user code) is used.
|
|
27
27
|
#
|
|
28
28
|
# Test entry point: `Cloudflare::Scheduled.dispatch(cron, scheduled_time, js_env, js_ctx)`
|
|
29
29
|
# — used by `test/scheduled_smoke.rb` so the same code path that the
|
|
@@ -91,7 +91,7 @@ module Cloudflare
|
|
|
91
91
|
|
|
92
92
|
class << self
|
|
93
93
|
# Override the dispatch target. By default the dispatcher uses
|
|
94
|
-
# `Rack::Handler::
|
|
94
|
+
# `Rack::Handler::Homura.app`, which is whatever the
|
|
95
95
|
# user passed to top-level `run app`. Tests use this to plug
|
|
96
96
|
# a fake Sinatra subclass without booting the full handler.
|
|
97
97
|
attr_accessor :app
|
|
@@ -154,13 +154,13 @@ module Cloudflare
|
|
|
154
154
|
|
|
155
155
|
# Resolve which app class should receive the dispatch. Priority:
|
|
156
156
|
# 1. `Cloudflare::Scheduled.app = SomeApp` (explicit override)
|
|
157
|
-
# 2. `Rack::Handler::
|
|
157
|
+
# 2. `Rack::Handler::Homura.app` (set by `run app`)
|
|
158
158
|
# Returns the class itself (not an instance), because
|
|
159
159
|
# `dispatch_scheduled` is a class method on Sinatra apps.
|
|
160
160
|
def self.resolve_app
|
|
161
161
|
candidate = @app
|
|
162
|
-
if candidate.nil? && defined?(::Rack::Handler::
|
|
163
|
-
candidate = ::Rack::Handler::
|
|
162
|
+
if candidate.nil? && defined?(::Rack::Handler::Homura)
|
|
163
|
+
candidate = ::Rack::Handler::Homura.app
|
|
164
164
|
end
|
|
165
165
|
return nil if candidate.nil?
|
|
166
166
|
# Sinatra app classes respond to `dispatch_scheduled` (added by
|
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
#
|
|
11
11
|
# Three responsibilities, modelled after existing Ruby conventions:
|
|
12
12
|
#
|
|
13
|
-
# 1.
|
|
13
|
+
# 1. HomuraRuntimeIO — replaces nodejs.rb's $stdout / $stderr (which
|
|
14
14
|
# try to write to a closed Socket on Workers) with shims that route
|
|
15
15
|
# Ruby `puts` / `print` to V8's globalThis.console.log/error.
|
|
16
16
|
#
|
|
17
|
-
# 2. Rack::Handler::
|
|
17
|
+
# 2. Rack::Handler::Homura — a standard Rack handler. Same
|
|
18
18
|
# shape as Rack::Handler::Puma, Rack::Handler::WEBrick, etc. User
|
|
19
19
|
# code uses the conventional top-level `run app` from a config.ru-
|
|
20
20
|
# style entry point and never sees a Cloudflare-specific symbol.
|
|
@@ -35,7 +35,7 @@ require 'await'
|
|
|
35
35
|
# 1. stdout / stderr → console.log / console.error
|
|
36
36
|
# ---------------------------------------------------------------------------
|
|
37
37
|
|
|
38
|
-
class
|
|
38
|
+
class HomuraRuntimeIO
|
|
39
39
|
def initialize(channel)
|
|
40
40
|
@channel = channel # 'log' or 'error'
|
|
41
41
|
@buffer = ''
|
|
@@ -105,13 +105,13 @@ class CloudflareWorkersIO
|
|
|
105
105
|
end
|
|
106
106
|
end
|
|
107
107
|
|
|
108
|
-
$stdout =
|
|
109
|
-
$stderr =
|
|
110
|
-
Object.const_set(:STDOUT, $stdout) unless Object.const_defined?(:STDOUT) && STDOUT.is_a?(
|
|
111
|
-
Object.const_set(:STDERR, $stderr) unless Object.const_defined?(:STDERR) && STDERR.is_a?(
|
|
108
|
+
$stdout = HomuraRuntimeIO.new('log')
|
|
109
|
+
$stderr = HomuraRuntimeIO.new('error')
|
|
110
|
+
Object.const_set(:STDOUT, $stdout) unless Object.const_defined?(:STDOUT) && STDOUT.is_a?(HomuraRuntimeIO)
|
|
111
|
+
Object.const_set(:STDERR, $stderr) unless Object.const_defined?(:STDERR) && STDERR.is_a?(HomuraRuntimeIO)
|
|
112
112
|
|
|
113
113
|
# ---------------------------------------------------------------------------
|
|
114
|
-
# 2. Rack::Handler::
|
|
114
|
+
# 2. Rack::Handler::Homura
|
|
115
115
|
# ---------------------------------------------------------------------------
|
|
116
116
|
#
|
|
117
117
|
# Conforms to the Rack handler convention: a module with a `run` class
|
|
@@ -121,7 +121,7 @@ Object.const_set(:STDERR, $stderr) unless Object.const_defined?(:STDERR) && STDE
|
|
|
121
121
|
|
|
122
122
|
module Rack
|
|
123
123
|
module Handler
|
|
124
|
-
module
|
|
124
|
+
module Homura
|
|
125
125
|
EMPTY_STRING_IO = StringIO.new('').freeze
|
|
126
126
|
|
|
127
127
|
def self.run(app, **_options)
|
|
@@ -142,7 +142,7 @@ module Rack
|
|
|
142
142
|
# before `run` was called (e.g. classic-style apps that omit the
|
|
143
143
|
# trailing `run Sinatra::Application`) still gets routed into our
|
|
144
144
|
# `call` method, which can then discover the user's Sinatra app
|
|
145
|
-
# lazily via `Sinatra::
|
|
145
|
+
# lazily via `Sinatra::Homura.ensure_rack_app!`. This
|
|
146
146
|
# is what makes the canonical sinatrarb.com snippet work
|
|
147
147
|
# verbatim on Workers — `at_exit` is unreliable here because the
|
|
148
148
|
# isolate never actually exits between fetches.
|
|
@@ -170,9 +170,9 @@ module Rack
|
|
|
170
170
|
# handing control to Ruby because Opal runs synchronously).
|
|
171
171
|
def self.call(js_req, js_env, js_ctx, body_text = '')
|
|
172
172
|
if @app.nil?
|
|
173
|
-
if defined?(::Sinatra::
|
|
174
|
-
::Sinatra::
|
|
175
|
-
::Sinatra::
|
|
173
|
+
if defined?(::Sinatra::Homura) &&
|
|
174
|
+
::Sinatra::Homura.respond_to?(:ensure_rack_app!)
|
|
175
|
+
::Sinatra::Homura.ensure_rack_app!
|
|
176
176
|
end
|
|
177
177
|
raise '`run app` was never called from user code, and no Sinatra app was discoverable (define `class App < Sinatra::Base` or use top-level classic Sinatra routes)' if @app.nil?
|
|
178
178
|
end
|
|
@@ -191,7 +191,7 @@ module Rack
|
|
|
191
191
|
private
|
|
192
192
|
|
|
193
193
|
# Legacy alias, kept so out-of-tree code calling
|
|
194
|
-
# `Rack::Handler::
|
|
194
|
+
# `Rack::Handler::Homura.send(:install_dispatcher)`
|
|
195
195
|
# keeps working. New code should go through
|
|
196
196
|
# `ensure_dispatcher_installed!` (it's idempotent and tracks
|
|
197
197
|
# state via `@dispatcher_installed`).
|
|
@@ -504,7 +504,7 @@ module Kernel
|
|
|
504
504
|
private
|
|
505
505
|
|
|
506
506
|
def run(app, **options)
|
|
507
|
-
Rack::Handler::
|
|
507
|
+
Rack::Handler::Homura.run(app, **options)
|
|
508
508
|
end
|
|
509
509
|
end
|
|
510
510
|
|
|
@@ -950,7 +950,7 @@ end
|
|
|
950
950
|
# Phase 6 — HTTP client foundation. Loaded as part of the Cloudflare
|
|
951
951
|
# Workers adapter so user code can simply `require 'sinatra/base'`
|
|
952
952
|
# and use Net::HTTP / Cloudflare::HTTP.fetch without an extra require.
|
|
953
|
-
require '
|
|
953
|
+
require 'homura/runtime/http'
|
|
954
954
|
|
|
955
955
|
# Phase 9 — Scheduled (Cron Triggers) dispatcher. Installs the JS
|
|
956
956
|
# `globalThis.__HOMURA_SCHEDULED_DISPATCH__` hook that
|
|
@@ -958,11 +958,11 @@ require 'cloudflare_workers/http'
|
|
|
958
958
|
# Must be loaded after the Cloudflare::* binding wrappers above
|
|
959
959
|
# because it constructs D1Database/KVNamespace/R2Bucket instances
|
|
960
960
|
# inside the dispatcher's per-job env.
|
|
961
|
-
require '
|
|
961
|
+
require 'homura/runtime/scheduled'
|
|
962
962
|
|
|
963
963
|
# Phase 10 — Workers AI binding wrapper. Loaded here so any Sinatra
|
|
964
964
|
# route can call Cloudflare::AI.run(...) without an extra require.
|
|
965
|
-
require '
|
|
965
|
+
require 'homura/runtime/ai'
|
|
966
966
|
|
|
967
967
|
# Phase 11A — HTTP foundations.
|
|
968
968
|
#
|
|
@@ -971,17 +971,17 @@ require 'cloudflare_workers/ai'
|
|
|
971
971
|
# `stream` adds `Cloudflare::SSEStream` + `Sinatra::Streaming#sse`
|
|
972
972
|
# so a route can `sse do |out| ... end` and flush chunks
|
|
973
973
|
# through a Workers ReadableStream.
|
|
974
|
-
require '
|
|
975
|
-
require '
|
|
974
|
+
require 'homura/runtime/multipart'
|
|
975
|
+
require 'homura/runtime/stream'
|
|
976
976
|
|
|
977
977
|
# Phase 11B — Cloudflare native bindings (Durable Objects / Cache /
|
|
978
978
|
# Queues). Each file registers its own globalThis dispatcher hook
|
|
979
979
|
# where applicable (DO / Queue consumer). Loaded here so user code
|
|
980
980
|
# just needs `require 'sinatra/base'` — no extra `require` per
|
|
981
981
|
# binding.
|
|
982
|
-
require '
|
|
983
|
-
require '
|
|
984
|
-
require '
|
|
985
|
-
require '
|
|
982
|
+
require 'homura/runtime/cache'
|
|
983
|
+
require 'homura/runtime/queue'
|
|
984
|
+
require 'homura/runtime/email'
|
|
985
|
+
require 'homura/runtime/durable_object'
|
|
986
986
|
|
|
987
|
-
require '
|
|
987
|
+
require 'homura/runtime/async_registry'
|
data/runtime/worker_module.mjs
CHANGED
|
@@ -156,7 +156,7 @@ export default {
|
|
|
156
156
|
const dispatch = rackDispatch();
|
|
157
157
|
if (typeof dispatch !== "function") {
|
|
158
158
|
return new Response(
|
|
159
|
-
"homura: Rack dispatcher not installed (Rack::Handler::
|
|
159
|
+
"homura: Rack dispatcher not installed (Rack::Handler::Homura.run never called)\n",
|
|
160
160
|
{ status: 500, headers: { "content-type": "text/plain; charset=utf-8" } },
|
|
161
161
|
);
|
|
162
162
|
}
|
|
@@ -180,7 +180,7 @@ export default {
|
|
|
180
180
|
// The Workers runtime invokes this `scheduled` export once per
|
|
181
181
|
// matching `[triggers] crons` entry in wrangler.toml. We forward
|
|
182
182
|
// the (event, env, ctx) triple to the Ruby-side dispatcher
|
|
183
|
-
// installed by `lib/
|
|
183
|
+
// installed by `lib/homura/runtime/scheduled.rb`
|
|
184
184
|
// (which registers `globalThis.__HOMURA_SCHEDULED_DISPATCH__`).
|
|
185
185
|
//
|
|
186
186
|
// The Ruby dispatcher walks every job registered via the
|
|
@@ -199,12 +199,12 @@ export default {
|
|
|
199
199
|
const dispatch = scheduledDispatch();
|
|
200
200
|
if (typeof dispatch !== "function") {
|
|
201
201
|
// No Ruby dispatcher installed — the Opal bundle didn't
|
|
202
|
-
// require '
|
|
202
|
+
// require 'homura/runtime/scheduled'. Log loudly so this
|
|
203
203
|
// misconfiguration surfaces instead of silently dropping
|
|
204
204
|
// every cron firing.
|
|
205
205
|
try {
|
|
206
206
|
globalThis.console.error(
|
|
207
|
-
"homura: scheduled dispatcher not installed (require '
|
|
207
|
+
"homura: scheduled dispatcher not installed (require 'homura/runtime/scheduled' missing)",
|
|
208
208
|
);
|
|
209
209
|
} catch (e) {
|
|
210
210
|
// ignore — console may itself be broken in pathological cases
|
|
@@ -246,7 +246,7 @@ export default {
|
|
|
246
246
|
if (typeof dispatch !== "function") {
|
|
247
247
|
try {
|
|
248
248
|
globalThis.console.error(
|
|
249
|
-
"homura: queue dispatcher not installed (require '
|
|
249
|
+
"homura: queue dispatcher not installed (require 'homura/runtime/queue' missing)",
|
|
250
250
|
);
|
|
251
251
|
} catch (e) {}
|
|
252
252
|
return;
|
|
@@ -272,7 +272,7 @@ export default {
|
|
|
272
272
|
// must be a named export on this module. We export ONE generic class
|
|
273
273
|
// (`HomuraCounterDO`) that forwards every `fetch(req)` it receives
|
|
274
274
|
// to the Ruby-side dispatcher installed by
|
|
275
|
-
// `lib/
|
|
275
|
+
// `lib/homura/runtime/durable_object.rb`
|
|
276
276
|
// (`globalThis.__HOMURA_DO_DISPATCH__`).
|
|
277
277
|
//
|
|
278
278
|
// The Ruby handler is registered via:
|
|
@@ -361,7 +361,7 @@ export class HomuraCounterDO {
|
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
// Hibernation API callbacks — routed into Ruby via the hooks
|
|
364
|
-
// installed by `lib/
|
|
364
|
+
// installed by `lib/homura/runtime/durable_object.rb`. Each
|
|
365
365
|
// hook is optional on the Ruby side; missing hooks are a no-op.
|
|
366
366
|
async webSocketMessage(ws, message) {
|
|
367
367
|
const fn = durableObjectWsMessage();
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: homura-runtime
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kazuhiro Homma
|
|
@@ -53,21 +53,21 @@ files:
|
|
|
53
53
|
- exe/compile-assets
|
|
54
54
|
- exe/compile-erb
|
|
55
55
|
- exe/homura-build
|
|
56
|
-
- lib/
|
|
57
|
-
- lib/
|
|
58
|
-
- lib/
|
|
59
|
-
- lib/
|
|
60
|
-
- lib/
|
|
61
|
-
- lib/
|
|
62
|
-
- lib/
|
|
63
|
-
- lib/
|
|
64
|
-
- lib/
|
|
65
|
-
- lib/
|
|
66
|
-
- lib/
|
|
67
|
-
- lib/
|
|
68
|
-
- lib/
|
|
69
|
-
- lib/
|
|
70
|
-
- lib/
|
|
56
|
+
- lib/homura/runtime.rb
|
|
57
|
+
- lib/homura/runtime/ai.rb
|
|
58
|
+
- lib/homura/runtime/async_registry.rb
|
|
59
|
+
- lib/homura/runtime/auto_await/analyzer.rb
|
|
60
|
+
- lib/homura/runtime/auto_await/transformer.rb
|
|
61
|
+
- lib/homura/runtime/build_support.rb
|
|
62
|
+
- lib/homura/runtime/cache.rb
|
|
63
|
+
- lib/homura/runtime/durable_object.rb
|
|
64
|
+
- lib/homura/runtime/email.rb
|
|
65
|
+
- lib/homura/runtime/http.rb
|
|
66
|
+
- lib/homura/runtime/multipart.rb
|
|
67
|
+
- lib/homura/runtime/queue.rb
|
|
68
|
+
- lib/homura/runtime/scheduled.rb
|
|
69
|
+
- lib/homura/runtime/stream.rb
|
|
70
|
+
- lib/homura/runtime/version.rb
|
|
71
71
|
- lib/homura_vendor_tempfile.rb
|
|
72
72
|
- lib/homura_vendor_tilt.rb
|
|
73
73
|
- lib/homura_vendor_zlib.rb
|
|
File without changes
|