homura-runtime 0.2.7 → 0.2.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: b1a1cd099709c81d7dfba2a41d071ab47f343c253f5826a031107011903706c9
4
- data.tar.gz: 54dcad51d26b66db5da83a6245ee3402418bd1af891cee59627b61f0c7760fc7
3
+ metadata.gz: d41fae3606bacf17566d5d2b423547fffcb490a70fde78bb9e0e34e9cff357cd
4
+ data.tar.gz: 2c7f98a9cfa158acdb8287788176af5515d3bf736c791de2d849e8ee4c29d06d
5
5
  SHA512:
6
- metadata.gz: 17be34147d20fe3c90c5bf50b5e1aa3fc0f0fa659e519659243566b3828bf7ffae88b1d129589ff34916b7406a650d1aed652500de4f5ba6e0400c5e00e5aae9
7
- data.tar.gz: e5a79b2eff5c805f2b06c83843dd99a1614d99defab71d1be52a8634046b1eb97f51f2e07f80ab8b41543e3b0b559c88d483bc70a00e429fafbc8b4ec9daf41d
6
+ metadata.gz: adeef4db820e46d80748ebd3aa9ebc8d8224d1e3a796a07582d47c73eee6ea21541805b087d1aa16a6dbfb19434b4fe9f3c7ff2ab36943d01df1b8263fc22137
7
+ data.tar.gz: a3b35377f1d55af94598f1728edec1a6a06acc9f42ffe02090116dfa022a2548349c5da1fa5b061d96cf5b734372ce54c6b3dbbf5192b78e0988a7464ddfb210
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.9 (2026-04-24)
4
+
5
+ - Skip auto-await rewrites when the source expression is already followed by
6
+ `.__await__`, avoiding noisy double-application in rewritten standalone app
7
+ code.
8
+
9
+ ## 0.2.8 (2026-04-24)
10
+
11
+ - Compile standalone `config.ru` inputs from `build/auto_await/` so ordinary
12
+ `require_relative 'app/app'` entrypoints pick up the auto-awaited app copy
13
+ instead of silently falling back to the original source.
14
+ - Teach the auto-await analyzer to prefer locally inferred helper return types
15
+ over generic registry defaults, and infer memoized `foo ||= ...` helper bodies.
16
+ - Preserve redirect headers when awaited routes resolve to Rack tuples through
17
+ the Promise response builder.
18
+ - Add `layout.erb` by default in the precompiled ERB runtime so `erb :index`
19
+ behaves like ordinary Sinatra unless the app passes `layout: false`.
20
+
3
21
  ## 0.2.7 (2026-04-24)
4
22
 
5
23
  - Auto-detect standalone app entrypoints from `config.ru`, `app/hello.rb`, then
data/exe/compile-erb CHANGED
@@ -264,6 +264,9 @@ def emit_sinatra_patch(io, namespace)
264
264
 
265
265
  layout = options[:layout]
266
266
  layout = false if layout.nil? && options.include?(:layout)
267
+ if layout.nil? && template.to_sym != :layout && ::#{namespace}.registered?(:layout)
268
+ layout = :layout
269
+ end
267
270
  if layout
268
271
  layout = :layout if layout == true
269
272
  layout = layout.to_sym
data/exe/homura-build CHANGED
@@ -163,7 +163,10 @@ def prepare_opal_input(root, input_path)
163
163
  rel = root.join(rel).relative_path_from(root) if rel.absolute?
164
164
  return [rel.to_s, nil] unless rel.extname == '.ru'
165
165
 
166
- tmp = root.join(".homura-build-#{rel.basename}.rb")
166
+ tmp_dir = root.join('build', 'auto_await')
167
+ tmp_dir = root unless tmp_dir.directory?
168
+ tmp = tmp_dir.join(".homura-build-#{rel.basename}.rb")
169
+ FileUtils.mkdir_p(tmp.dirname)
167
170
  File.write(tmp, root.join(rel).read)
168
171
  [tmp.relative_path_from(root).to_s, tmp]
169
172
  end
@@ -144,16 +144,20 @@ module CloudflareWorkers
144
144
  when :send
145
145
  receiver, method_name = *node
146
146
  if receiver.nil?
147
- return @env[method_name] if @env.key?(method_name)
148
147
  return @method_returns[method_name] if @method_returns.key?(method_name)
148
+ return @env[method_name] if @env.key?(method_name)
149
149
  end
150
150
  infer_send_class(node)
151
151
  when :index
152
152
  infer_index_class(node)
153
+ when :begin
154
+ infer_class(node.children.last)
153
155
  when :lvar
154
156
  @env[node.children[0]]
155
157
  when :ivar
156
158
  nil
159
+ when :or_asgn
160
+ infer_class(node.children.last)
157
161
  when :const
158
162
  const_path(node)
159
163
  else
@@ -10,10 +10,15 @@ module CloudflareWorkers
10
10
  await_nodes.each do |node|
11
11
  range = node.loc.expression
12
12
  next unless range
13
+ next if already_awaited?(buffer, range)
13
14
  rewriter.replace(range, "#{range.source}.__await__")
14
15
  end
15
16
  rewriter.process
16
17
  end
18
+
19
+ def self.already_awaited?(buffer, range)
20
+ buffer.source[range.end_pos..]&.match?(/\A\s*\.__await__\b/)
21
+ end
17
22
  end
18
23
  end
19
24
  end
@@ -61,7 +61,7 @@ module CloudflareWorkers
61
61
  hv = vendor_from_gemfile(root)
62
62
  load_paths << hv.to_s if hv
63
63
 
64
- load_paths += ['.', 'build/auto_await/app', 'app']
64
+ load_paths += ['.', 'build/auto_await', 'build/auto_await/app', 'app']
65
65
  [
66
66
  gem_lib(RUNTIME_GEM_NAME, loaded_specs: loaded_specs),
67
67
  gem_vendor(RUNTIME_GEM_NAME, loaded_specs: loaded_specs),
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloudflareWorkers
4
- VERSION = '0.2.7'
4
+ VERSION = '0.2.9'
5
5
  end
@@ -405,7 +405,7 @@ module Rack
405
405
  # used for 101 WebSocket upgrades where the Workers
406
406
  # runtime's own Response carries runtime-only properties
407
407
  # (`.webSocket`) that a reconstructed Response would lose.
408
- `Promise.all(#{js_chunks}).then(function(resolved) { var bodyToText = function(v) { if (v == null) { return ''; } if (Array.isArray(v)) { var joined = ''; for (var j = 0; j < v.length; j++) { joined += bodyToText(v[j]); } return joined; } if (typeof v === 'string') { return v; } if (v != null && v.$$is_string) { return v.toString(); } try { return JSON.stringify(v); } catch (e) { return String(v); } }; for (var i = 0; i < resolved.length; i++) { var r = resolved[i]; if (r != null && typeof r === 'object' && typeof r['$raw_response?'] === 'function' && typeof r['$js_response'] === 'function') { try { if (r['$raw_response?']()) { return r['$js_response'](); } } catch (_) {} } } for (var i = 0; i < resolved.length; i++) { var r = resolved[i]; if (r != null && r.stream != null && r.content_type != null) { var bh = {}; bh['content-type'] = r.content_type; if (r.cache_control) bh['cache-control'] = r.cache_control; return new Response(r.stream, { status: #{status_int}, headers: bh }); } } if (resolved.length === 1 && resolved[0] != null && Array.isArray(resolved[0]) && resolved[0].length >= 1 && typeof resolved[0][0] === 'number') { var ov = resolved[0]; var ovs = ov[0]|0; var ovh = #{js_headers}; var ovb = ''; if (ov.length >= 3 && ov[1] != null && typeof ov[1] === 'object' && !Array.isArray(ov[1])) { ovh = {}; var keys = Object.keys(ov[1]); for (var k = 0; k < keys.length; k++) { var key = keys[k]; ovh[key] = String(ov[1][key]); } ovb = bodyToText(ov[2]); } else if (ov.length >= 2) { ovb = bodyToText(ov[ov.length - 1]); } return new Response(ovb, { status: ovs, headers: ovh }); } var parts = []; for (var i = 0; i < resolved.length; i++) { var r = resolved[i]; if (r == null) { parts.push(''); continue; } if (typeof r === 'string') { parts.push(r); continue; } if (r != null && r.$$is_string) { parts.push(r.toString()); continue; } try { parts.push(JSON.stringify(r)); } catch (e) { parts.push(String(r)); } } return new Response(parts.join(''), { status: #{status_int}, headers: #{js_headers} }); })`
408
+ `Promise.all(#{js_chunks}).then(function(resolved) { var bodyToText = function(v) { if (v == null) { return ''; } if (Array.isArray(v)) { var joined = ''; for (var j = 0; j < v.length; j++) { joined += bodyToText(v[j]); } return joined; } if (typeof v === 'string') { return v; } if (v != null && v.$$is_string) { return v.toString(); } try { return JSON.stringify(v); } catch (e) { return String(v); } }; for (var i = 0; i < resolved.length; i++) { var r = resolved[i]; if (r != null && typeof r === 'object' && typeof r['$raw_response?'] === 'function' && typeof r['$js_response'] === 'function') { try { if (r['$raw_response?']()) { return r['$js_response'](); } } catch (_) {} } } for (var i = 0; i < resolved.length; i++) { var r = resolved[i]; if (r != null && r.stream != null && r.content_type != null) { var bh = {}; bh['content-type'] = r.content_type; if (r.cache_control) bh['cache-control'] = r.cache_control; return new Response(r.stream, { status: #{status_int}, headers: bh }); } } if (resolved.length === 1 && resolved[0] != null && Array.isArray(resolved[0]) && resolved[0].length >= 1 && typeof resolved[0][0] === 'number') { var ov = resolved[0]; var ovs = ov[0]|0; var ovh = #{Cloudflare}.$headers_to_js(nil, #{js_headers}); var ovb = ''; if (ov.length >= 3 && ov[1] != null) { ovh = #{Cloudflare}.$headers_to_js(ov[1], #{js_headers}); ovb = bodyToText(ov[2]); } else if (ov.length >= 2) { ovb = bodyToText(ov[ov.length - 1]); } return new Response(ovb, { status: ovs, headers: ovh }); } var parts = []; for (var i = 0; i < resolved.length; i++) { var r = resolved[i]; if (r == null) { parts.push(''); continue; } if (typeof r === 'string') { parts.push(r); continue; } if (r != null && r.$$is_string) { parts.push(r.toString()); continue; } try { parts.push(JSON.stringify(r)); } catch (e) { parts.push(String(r)); } } return new Response(parts.join(''), { status: #{status_int}, headers: #{js_headers} }); })`
409
409
  else
410
410
  body_str = ''
411
411
  chunks.each { |c| body_str = body_str + c.to_s }
@@ -512,6 +512,21 @@ module Cloudflare
512
512
  h
513
513
  end
514
514
 
515
+ def self.headers_to_js(headers, fallback = nil)
516
+ return fallback || `({})` if headers.nil?
517
+ return headers if `#{headers} != null && #{headers}.constructor === Object`
518
+
519
+ js_headers = fallback || `({})`
520
+ if headers.respond_to?(:each)
521
+ headers.each do |key, value|
522
+ ks = key.to_s
523
+ vs = value.to_s
524
+ `#{js_headers}[#{ks}] = #{vs}`
525
+ end
526
+ end
527
+ js_headers
528
+ end
529
+
515
530
  # RawResponse wraps an already-constructed JS `Response` so routes
516
531
  # can return it through Sinatra and have `build_js_response` pass
517
532
  # it through to the Workers runtime untouched. Needed when the
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.2.7
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuhiro Homma