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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 90ff82f40bdd001631e5dd1626579eaa0c3e62b2b8569b5d33829203b44c7a17
4
- data.tar.gz: 865e7c3bc07aa6b48362afb2c436eaf09c33f8dd41c98ac6c59ccacbb8b4f718
3
+ metadata.gz: 7d14417c1dedeec08cd1ee906d177d1c70dacb4d2a8e8d5979f815dd17340fc4
4
+ data.tar.gz: 0d3e76edc794a886048959f63f258f581d4f8f8aae43fbe74807ab6417f2fd88
5
5
  SHA512:
6
- metadata.gz: 7814e73863240b28be6da34ab8043da6e6ad64eadc29a083b6feb115b5ee55e1549c9271980bc83ef30a1b009a1e31ab9624b9fbc9abb8f43dd8f273c1ea4837
7
- data.tar.gz: a7a9fd42c426e784c6938816c83c6f55924ad0256f953978dd6fbce1ce827c94634cb5d3b0b2a08bcc98375b40a261749e4884bd547c6500cb13c7b9e5847633
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
- HomuraRuntime::BuildSupport
25
- .runtime_root(current_file: __FILE__)
26
- .join("lib")
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
- if File.directory?(input_root)
74
- Dir.glob(input_root.join("**", "*.rb")).sort
75
- else
76
- [input_root.to_s]
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 "[auto-await] skip (no changes) #{rel}" if options[:debug]
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
- if nodes.empty?
105
- source
106
- else
107
- HomuraRuntime::AutoAwait::Transformer.transform(source, nodes, buffer)
108
- end
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 "[auto-await] write #{rel} (#{nodes.length} await#{"s" if nodes.length > 1})"
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 "[auto-await] ERROR #{rel}: #{e.message}"
118
+ $stderr.puts("[auto-await] ERROR #{rel}: #{e.message}")
119
119
  errors += 1
120
120
  end
121
121
  end
122
122
 
123
- puts "[auto-await] done: #{changed} changed, #{skipped} skipped, #{errors} errors"
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.start_with?("text/") || content_type.include?("javascript") ||
46
- content_type.include?("json") || content_type.include?("xml")
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 HELP
60
- exit 0
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 "compile-assets: no directory #{public_dir.inspect}"
87
- exit 1
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 <<~RUBY
100
- # frozen_string_literal: true
101
- # Auto-generated by bin/compile-assets — DO NOT EDIT BY HAND.
102
- #
103
- # Embeds every file under #{options[:input_dir]}/ as a Ruby constant, then installs a
104
- # Rack middleware that serves matching paths before they reach Sinatra.
105
- # This replaces Sinatra's `static!` (which needs a real filesystem)
106
- # with an in-memory lookup that works on Cloudflare Workers.
107
-
108
- module #{ns}
109
- ASSETS = {}
110
-
111
- class Middleware
112
- def initialize(app)
113
- @app = app
114
- end
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
- def call(env)
117
- path = env['PATH_INFO']
118
- if (asset = #{ns}::ASSETS[path])
119
- headers = {
120
- 'content-type' => asset[:content_type],
121
- 'cache-control' => 'public, max-age=3600',
122
- }
123
- if asset[:binary]
124
- body = ::Cloudflare::EmbeddedBinaryBody.new(
125
- asset[:body_base64],
126
- asset[:content_type],
127
- headers['cache-control']
128
- )
129
- [200, headers, [body.raw_response(200, headers)]]
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
- headers['content-length'] = asset[:body].bytesize.to_s
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
- end
140
- RUBY
142
+ RUBY
143
+ )
141
144
 
142
145
  files.each do |full_path|
143
- rel = full_path.sub(public_dir, "") # e.g. "/style.css"
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 "# #{rel} (#{content.bytesize} bytes)"
150
- io.puts "#{ns}::ASSETS[#{rel.inspect}] = {"
151
- io.puts " content_type: #{ct.inspect},"
152
- io.puts " binary: #{binary},"
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 " body_base64: #{Base64.strict_encode64(content).inspect}"
158
+ io.puts(" body_base64: #{Base64.strict_encode64(content).inspect}")
155
159
  else
156
- io.puts " body: #{content.inspect}"
160
+ io.puts(" body: #{content.inspect}")
157
161
  end
158
- io.puts "}"
162
+
163
+ io.puts("}")
159
164
  end
160
165
 
161
- io.puts <<~RUBY
166
+ io.puts(
167
+ <<~RUBY
162
168
 
163
- # Auto-install the middleware on Sinatra::Base if a real Sinatra app is loaded.
164
- if defined?(::Sinatra::Base) && ::Sinatra::Base.respond_to?(:use)
165
- ::Sinatra::Base.use #{ns}::Middleware
166
- end
167
- RUBY
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 "compile-assets: wrote #{out_path} (#{files.size} assets, #{File.size(out_path)} bytes)"
177
+ warn("compile-assets: wrote #{out_path} (#{files.size} assets, #{File.size(out_path)} bytes)")