homura-runtime 0.3.7 → 0.3.9
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 +17 -0
- data/exe/auto-await +23 -23
- data/exe/compile-assets +68 -61
- data/exe/compile-erb +263 -255
- data/exe/homura-build +74 -21
- data/lib/homura/runtime/ai.rb +104 -85
- data/lib/homura/runtime/async_registry.rb +124 -109
- data/lib/homura/runtime/auto_await/analyzer.rb +28 -15
- data/lib/homura/runtime/auto_await/transformer.rb +1 -0
- data/lib/homura/runtime/build_support.rb +90 -11
- data/lib/homura/runtime/cache.rb +21 -18
- data/lib/homura/runtime/durable_object.rb +27 -17
- data/lib/homura/runtime/email.rb +14 -14
- data/lib/homura/runtime/http.rb +4 -3
- data/lib/homura/runtime/multipart.rb +11 -4
- data/lib/homura/runtime/queue.rb +53 -23
- data/lib/homura/runtime/scheduled.rb +12 -14
- data/lib/homura/runtime/stream.rb +18 -14
- data/lib/homura/runtime/version.rb +1 -1
- data/lib/homura/runtime.rb +148 -91
- data/lib/homura_vendor_tempfile.rb +4 -2
- data/lib/homura_vendor_tilt.rb +5 -3
- data/lib/homura_vendor_zlib.rb +3 -0
- data/lib/opal_patches.rb +83 -66
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7d14417c1dedeec08cd1ee906d177d1c70dacb4d2a8e8d5979f815dd17340fc4
|
|
4
|
+
data.tar.gz: 0d3e76edc794a886048959f63f258f581d4f8f8aae43fbe74807ab6417f2fd88
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a4aaa01b5c0a5953f5c5b4e57ece0344ab2de76081dc7f04c1844729fe1e3447f8e60935845808adfb8d12f7970a8a062134e9c11f02159d628d3a42f178ba4a
|
|
7
|
+
data.tar.gz: 79be638101406487bf5409d0a6b2e4b6a516f75acf113b3b3bd7f2d9ac2e0532cd493579a22993b6dd9932d198748f3d2f3355ac9c9a58864b01c6ead3155dc8
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.9 (2026-06-25)
|
|
4
|
+
|
|
5
|
+
- Add the Homura Opal gem prelude for published Phlex/Literal apps. The build
|
|
6
|
+
support now discovers the supported Opal entry gems, adds their runtime
|
|
7
|
+
dependencies such as Zeitwerk to the standalone load path, and emits
|
|
8
|
+
`build/homura_opal_gems.rb` so examples can compile against Rubygems packages
|
|
9
|
+
instead of monorepo `path:` gems.
|
|
10
|
+
|
|
11
|
+
## 0.3.8 (2026-05-06)
|
|
12
|
+
|
|
13
|
+
- Discover and attach arbitrary Durable Object bindings from `env`, not just
|
|
14
|
+
the built-in `COUNTER` demo binding. This lets app code use
|
|
15
|
+
`durable_object(:voice_limit, ...)` and other custom DO names without
|
|
16
|
+
gem-side shims.
|
|
17
|
+
- `examples/ai-voice-chat` now uses that generic DO binding path for its
|
|
18
|
+
daily request cap.
|
|
19
|
+
|
|
3
20
|
## 0.3.7 (2026-05-06)
|
|
4
21
|
|
|
5
22
|
- Add Ruby-shaped Workers AI helpers for the staged voice examples:
|
data/exe/auto-await
CHANGED
|
@@ -15,17 +15,18 @@
|
|
|
15
15
|
|
|
16
16
|
require "fileutils"
|
|
17
17
|
require "pathname"
|
|
18
|
+
|
|
18
19
|
require_relative "../lib/homura/runtime/build_support"
|
|
19
20
|
|
|
20
21
|
# homura-runtime lib path resolution (with legacy alias fallback)
|
|
21
22
|
runtime_lib = ENV["CFW_RUNTIME_LIB"]
|
|
22
23
|
unless runtime_lib
|
|
23
|
-
runtime_lib =
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
.to_s
|
|
24
|
+
runtime_lib = HomuraRuntime::BuildSupport
|
|
25
|
+
.runtime_root(current_file: __FILE__)
|
|
26
|
+
.join("lib")
|
|
27
|
+
.to_s
|
|
28
28
|
end
|
|
29
|
+
|
|
29
30
|
$LOAD_PATH.unshift(runtime_lib) unless $LOAD_PATH.include?(runtime_lib)
|
|
30
31
|
|
|
31
32
|
require "homura/runtime/async_registry"
|
|
@@ -69,12 +70,11 @@ changed = 0
|
|
|
69
70
|
skipped = 0
|
|
70
71
|
errors = 0
|
|
71
72
|
|
|
72
|
-
paths =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
end
|
|
73
|
+
paths = if File.directory?(input_root)
|
|
74
|
+
Dir.glob(input_root.join("**", "*.rb")).sort
|
|
75
|
+
else
|
|
76
|
+
[input_root.to_s]
|
|
77
|
+
end
|
|
78
78
|
|
|
79
79
|
paths.each do |path|
|
|
80
80
|
rel = Pathname(path).relative_path_from(input_root)
|
|
@@ -89,36 +89,36 @@ paths.each do |path|
|
|
|
89
89
|
has_existing_await = source.include?(".__await__")
|
|
90
90
|
|
|
91
91
|
begin
|
|
92
|
-
analyzer =
|
|
93
|
-
HomuraRuntime::AutoAwait::Analyzer.new(registry, debug: options[:debug])
|
|
92
|
+
analyzer = HomuraRuntime::AutoAwait::Analyzer.new(registry, debug: options[:debug])
|
|
94
93
|
buffer, nodes = analyzer.process(source, path)
|
|
95
94
|
needs_magic_only_output = has_existing_await && !has_magic
|
|
96
95
|
|
|
97
96
|
if nodes.empty? && !needs_magic_only_output
|
|
98
|
-
puts
|
|
97
|
+
puts("[auto-await] skip (no changes) #{rel}") if options[:debug]
|
|
99
98
|
skipped += 1
|
|
100
99
|
next
|
|
101
100
|
end
|
|
102
101
|
|
|
103
|
-
transformed =
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
102
|
+
transformed = if nodes.empty?
|
|
103
|
+
source
|
|
104
|
+
else
|
|
105
|
+
HomuraRuntime::AutoAwait::Transformer.transform(source, nodes, buffer)
|
|
106
|
+
end
|
|
107
|
+
|
|
109
108
|
transformed = "# await: true\n" + transformed unless has_magic
|
|
110
109
|
out_path = output_root.join(rel)
|
|
111
110
|
FileUtils.mkdir_p(out_path.dirname)
|
|
112
111
|
File.write(out_path, transformed)
|
|
113
112
|
if options[:debug]
|
|
114
|
-
puts
|
|
113
|
+
puts("[auto-await] write #{rel} (#{nodes.length} await#{"s" if nodes.length > 1})")
|
|
115
114
|
end
|
|
115
|
+
|
|
116
116
|
changed += 1
|
|
117
117
|
rescue => e
|
|
118
|
-
$stderr.puts
|
|
118
|
+
$stderr.puts("[auto-await] ERROR #{rel}: #{e.message}")
|
|
119
119
|
errors += 1
|
|
120
120
|
end
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
puts
|
|
123
|
+
puts("[auto-await] done: #{changed} changed, #{skipped} skipped, #{errors} errors")
|
|
124
124
|
exit(1) if errors > 0
|
data/exe/compile-assets
CHANGED
|
@@ -41,10 +41,10 @@ def mime_for(path)
|
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def binary_content_type?(content_type)
|
|
44
|
-
!(
|
|
45
|
-
content_type.
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
!(content_type.start_with?("text/") ||
|
|
45
|
+
content_type.include?("javascript") ||
|
|
46
|
+
content_type.include?("json") ||
|
|
47
|
+
content_type.include?("xml"))
|
|
48
48
|
end
|
|
49
49
|
|
|
50
50
|
HELP = <<~USAGE
|
|
@@ -56,8 +56,8 @@ HELP = <<~USAGE
|
|
|
56
56
|
USAGE
|
|
57
57
|
|
|
58
58
|
if ARGV.include?("-h") || ARGV.include?("--help")
|
|
59
|
-
puts
|
|
60
|
-
exit
|
|
59
|
+
puts(HELP)
|
|
60
|
+
exit(0)
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
options = {
|
|
@@ -72,9 +72,11 @@ OptionParser
|
|
|
72
72
|
op.on("--input DIR", "Root directory to embed (recursive)") do |d|
|
|
73
73
|
options[:input_dir] = d
|
|
74
74
|
end
|
|
75
|
+
|
|
75
76
|
op.on("--output PATH", "Generated Ruby output path") do |p|
|
|
76
77
|
options[:output] = p
|
|
77
78
|
end
|
|
79
|
+
|
|
78
80
|
op.on("--namespace NAME", "Ruby module name") do |n|
|
|
79
81
|
options[:namespace] = n
|
|
80
82
|
end
|
|
@@ -83,88 +85,93 @@ OptionParser
|
|
|
83
85
|
|
|
84
86
|
public_dir = File.join(Dir.pwd, options[:input_dir])
|
|
85
87
|
unless File.directory?(public_dir)
|
|
86
|
-
warn
|
|
87
|
-
exit
|
|
88
|
+
warn("compile-assets: no directory #{public_dir.inspect}")
|
|
89
|
+
exit(1)
|
|
88
90
|
end
|
|
89
91
|
|
|
90
92
|
ns = options[:namespace]
|
|
91
93
|
|
|
92
|
-
files =
|
|
93
|
-
Dir.glob(File.join(public_dir, "**", "*")).select { |f| File.file?(f) }.sort
|
|
94
|
+
files = Dir.glob(File.join(public_dir, "**", "*")).select { |f| File.file?(f) }.sort
|
|
94
95
|
|
|
95
96
|
out_path = options[:output]
|
|
96
97
|
FileUtils.mkdir_p(File.dirname(out_path))
|
|
97
98
|
|
|
98
99
|
File.open(out_path, "w") do |io|
|
|
99
|
-
io.puts
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
100
|
+
io.puts(
|
|
101
|
+
<<~RUBY
|
|
102
|
+
# frozen_string_literal: true
|
|
103
|
+
# Auto-generated by bin/compile-assets — DO NOT EDIT BY HAND.
|
|
104
|
+
#
|
|
105
|
+
# Embeds every file under #{options[:input_dir]}/ as a Ruby constant, then installs a
|
|
106
|
+
# Rack middleware that serves matching paths before they reach Sinatra.
|
|
107
|
+
# This replaces Sinatra's `static!` (which needs a real filesystem)
|
|
108
|
+
# with an in-memory lookup that works on Cloudflare Workers.
|
|
109
|
+
|
|
110
|
+
module #{ns}
|
|
111
|
+
ASSETS = {}
|
|
112
|
+
|
|
113
|
+
class Middleware
|
|
114
|
+
def initialize(app)
|
|
115
|
+
@app = app
|
|
116
|
+
end
|
|
115
117
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
118
|
+
def call(env)
|
|
119
|
+
path = env['PATH_INFO']
|
|
120
|
+
if (asset = #{ns}::ASSETS[path])
|
|
121
|
+
headers = {
|
|
122
|
+
'content-type' => asset[:content_type],
|
|
123
|
+
'cache-control' => 'public, max-age=3600',
|
|
124
|
+
}
|
|
125
|
+
if asset[:binary]
|
|
126
|
+
body = ::Cloudflare::EmbeddedBinaryBody.new(
|
|
127
|
+
asset[:body_base64],
|
|
128
|
+
asset[:content_type],
|
|
129
|
+
headers['cache-control']
|
|
130
|
+
)
|
|
131
|
+
[200, headers, [body.raw_response(200, headers)]]
|
|
132
|
+
else
|
|
133
|
+
headers['content-length'] = asset[:body].bytesize.to_s
|
|
134
|
+
[200, headers, [asset[:body]]]
|
|
135
|
+
end
|
|
130
136
|
else
|
|
131
|
-
|
|
132
|
-
[200, headers, [asset[:body]]]
|
|
137
|
+
@app.call(env)
|
|
133
138
|
end
|
|
134
|
-
else
|
|
135
|
-
@app.call(env)
|
|
136
139
|
end
|
|
137
140
|
end
|
|
138
141
|
end
|
|
139
|
-
|
|
140
|
-
|
|
142
|
+
RUBY
|
|
143
|
+
)
|
|
141
144
|
|
|
142
145
|
files.each do |full_path|
|
|
143
|
-
|
|
146
|
+
# e.g. "/style.css"
|
|
147
|
+
rel = full_path.sub(public_dir, "")
|
|
144
148
|
content = File.binread(full_path)
|
|
145
149
|
ct = mime_for(full_path)
|
|
146
150
|
binary = binary_content_type?(ct)
|
|
147
151
|
|
|
148
152
|
io.puts
|
|
149
|
-
io.puts
|
|
150
|
-
io.puts
|
|
151
|
-
io.puts
|
|
152
|
-
io.puts
|
|
153
|
+
io.puts("# #{rel} (#{content.bytesize} bytes)")
|
|
154
|
+
io.puts("#{ns}::ASSETS[#{rel.inspect}] = {")
|
|
155
|
+
io.puts(" content_type: #{ct.inspect},")
|
|
156
|
+
io.puts(" binary: #{binary},")
|
|
153
157
|
if binary
|
|
154
|
-
io.puts
|
|
158
|
+
io.puts(" body_base64: #{Base64.strict_encode64(content).inspect}")
|
|
155
159
|
else
|
|
156
|
-
io.puts
|
|
160
|
+
io.puts(" body: #{content.inspect}")
|
|
157
161
|
end
|
|
158
|
-
|
|
162
|
+
|
|
163
|
+
io.puts("}")
|
|
159
164
|
end
|
|
160
165
|
|
|
161
|
-
io.puts
|
|
166
|
+
io.puts(
|
|
167
|
+
<<~RUBY
|
|
162
168
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
169
|
+
# Auto-install the middleware on Sinatra::Base if a real Sinatra app is loaded.
|
|
170
|
+
if defined?(::Sinatra::Base) && ::Sinatra::Base.respond_to?(:use)
|
|
171
|
+
::Sinatra::Base.use #{ns}::Middleware
|
|
172
|
+
end
|
|
173
|
+
RUBY
|
|
174
|
+
)
|
|
168
175
|
end
|
|
169
176
|
|
|
170
|
-
warn
|
|
177
|
+
warn("compile-assets: wrote #{out_path} (#{files.size} assets, #{File.size(out_path)} bytes)")
|