homura-runtime 0.1.2 → 0.1.3

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: 99d546ac1db58f957cf5bd81723cfc0e15019f11c27c8b0bcd78fc2ee833b751
4
- data.tar.gz: 02b08e0580b05b710cfba5d5bc6255ba463a4675a297476583dcacece6b158b6
3
+ metadata.gz: 328d7e98210fb60bb229a46166aa605b2c9e2a85baca8418cac31a0ac3688311
4
+ data.tar.gz: 3e3c0562902a031343eddf788a078d1556649c286ec79e44295626a0be2058a3
5
5
  SHA512:
6
- metadata.gz: 864e05461b1321d1094bdf27ae5bd971472194df29a440ff24f946449db7b98ec93c14ea53a25956ec1e9254b6e1e9015ac2d25978d8f1aadab02f91c69f287a
7
- data.tar.gz: 20c08fdaad6cef869cb4256ffed49be996a3bd6a0e3dd9038bb05f4240a27e75791fefe9bdae1c8dc3f73622413ea95ed90c166dd4810c19cfdf709db6a185f2
6
+ metadata.gz: 971c5697869ed0ecc0ba57d6aa5263c6f3b36f5e2bfa0c5e6efc67efe5bb5aade01dd0b522aaa685c4226074924b5a6e8269135fd6a12bdf1f8a6592d3b120cb
7
+ data.tar.gz: 0ef026fa796bfedb3960b42c1f2ea6a09a1d382d9883d16fd744767d3967a3649f0ee9cb322e80f1ddff6db752f05f12da94c62b04c238fe8f43aa4143b667d8
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.3 (2026-04-23)
4
+
5
+ - Fix binary static asset embedding so image responses preserve exact bytes on
6
+ Workers instead of being mangled through text encoding.
7
+ - Add regression coverage for binary-vs-text compile-assets output.
8
+ - Convert shipped mascot/icon assets to real PNG payloads so their bytes match
9
+ their `.png` filenames and `image/png` content type.
10
+
3
11
  ## 0.1.2 (2026-04-23)
4
12
 
5
13
  - Package the runtime's Opal compile-time vendor shims (`digest`, `zlib`,
@@ -7,7 +15,6 @@
7
15
  - Teach `cloudflare-workers-build --standalone` to add packaged gem `vendor/`
8
16
  directories to the Opal load path, so published gems no longer depend on the
9
17
  monorepo root `vendor/`.
10
-
11
18
  ## 0.1.1 (2026-04-23)
12
19
 
13
20
  - Fix `cloudflare-workers-build --standalone` and `exe/auto-await` to resolve only
data/exe/compile-assets CHANGED
@@ -12,6 +12,7 @@
12
12
  # Usage:
13
13
  # ruby bin/compile-assets --input public --output build/homura_assets.rb --namespace HomuraAssets
14
14
 
15
+ require 'base64'
15
16
  require 'fileutils'
16
17
  require 'optparse'
17
18
 
@@ -39,6 +40,13 @@ def mime_for(path)
39
40
  MIME_TYPES[ext] || 'application/octet-stream'
40
41
  end
41
42
 
43
+ def binary_content_type?(content_type)
44
+ !(content_type.start_with?('text/') ||
45
+ content_type.include?('javascript') ||
46
+ content_type.include?('json') ||
47
+ content_type.include?('xml'))
48
+ end
49
+
42
50
  HELP = <<~USAGE
43
51
  Usage:
44
52
  ruby bin/compile-assets [--input DIR] [--output FILE] [--namespace NAME]
@@ -105,10 +113,19 @@ File.open(out_path, 'w') do |io|
105
113
  if (asset = #{ns}::ASSETS[path])
106
114
  headers = {
107
115
  'content-type' => asset[:content_type],
108
- 'content-length' => asset[:body].bytesize.to_s,
109
116
  'cache-control' => 'public, max-age=3600',
110
117
  }
111
- [200, headers, [asset[:body]]]
118
+ if asset[:binary]
119
+ body = ::Cloudflare::EmbeddedBinaryBody.new(
120
+ asset[:body_base64],
121
+ asset[:content_type],
122
+ headers['cache-control']
123
+ )
124
+ [200, headers, [body.raw_response(200, headers)]]
125
+ else
126
+ headers['content-length'] = asset[:body].bytesize.to_s
127
+ [200, headers, [asset[:body]]]
128
+ end
112
129
  else
113
130
  @app.call(env)
114
131
  end
@@ -119,14 +136,20 @@ File.open(out_path, 'w') do |io|
119
136
 
120
137
  files.each do |full_path|
121
138
  rel = full_path.sub(public_dir, '') # e.g. "/style.css"
122
- content = File.read(full_path)
139
+ content = File.binread(full_path)
123
140
  ct = mime_for(full_path)
141
+ binary = binary_content_type?(ct)
124
142
 
125
143
  io.puts
126
144
  io.puts "# #{rel} (#{content.bytesize} bytes)"
127
145
  io.puts "#{ns}::ASSETS[#{rel.inspect}] = {"
128
146
  io.puts " content_type: #{ct.inspect},"
129
- io.puts " body: #{content.inspect}"
147
+ io.puts " binary: #{binary},"
148
+ if binary
149
+ io.puts " body_base64: #{Base64.strict_encode64(content).inspect}"
150
+ else
151
+ io.puts " body: #{content.inspect}"
152
+ end
130
153
  io.puts "}"
131
154
  end
132
155
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloudflareWorkers
4
- VERSION = '0.1.2'
4
+ VERSION = '0.1.3'
5
5
  end
@@ -313,6 +313,18 @@ module Rack
313
313
  return `new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`
314
314
  end
315
315
 
316
+ if body.is_a?(::Cloudflare::EmbeddedBinaryBody) || (body.respond_to?(:first) && body.first.is_a?(::Cloudflare::EmbeddedBinaryBody))
317
+ bin = body.is_a?(::Cloudflare::EmbeddedBinaryBody) ? body : body.first
318
+ js_stream = bin.stream
319
+ ct = bin.content_type
320
+ cc = bin.cache_control
321
+ js_headers = `({})`
322
+ headers.each { |k, v| ks = k.to_s; vs = v.to_s; `#{js_headers}[#{ks}] = #{vs}` }
323
+ `#{js_headers}['content-type'] = #{ct}` if ct
324
+ `#{js_headers}['cache-control'] = #{cc}` if cc
325
+ return `new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`
326
+ end
327
+
316
328
  # Phase 10 — Workers AI streaming: a Cloudflare::AI::Stream wraps
317
329
  # a JS ReadableStream<Uint8Array> emitting SSE-formatted bytes
318
330
  # ("data: {json}\n\n"). Pass it straight through so the client
@@ -530,6 +542,35 @@ module Cloudflare
530
542
  def close; end
531
543
  end
532
544
 
545
+ # EmbeddedBinaryBody carries a base64-encoded asset payload produced at
546
+ # build time by `compile-assets`, then reconstructs a Uint8Array in the
547
+ # Worker before building the Response stream.
548
+ class EmbeddedBinaryBody
549
+ attr_reader :body_base64, :content_type, :cache_control
550
+
551
+ def initialize(body_base64, content_type = 'application/octet-stream', cache_control = nil)
552
+ @body_base64 = body_base64 || ''
553
+ @content_type = content_type
554
+ @cache_control = cache_control
555
+ end
556
+
557
+ def each; end
558
+
559
+ def close; end
560
+
561
+ def raw_response(status, headers = {})
562
+ js_headers = `({})`
563
+ headers.each { |k, v| ks = k.to_s; vs = v.to_s; `#{js_headers}[#{ks}] = #{vs}` }
564
+ `#{js_headers}['content-type'] = #{@content_type}` if @content_type
565
+ `#{js_headers}['cache-control'] = #{@cache_control}` if @cache_control
566
+ RawResponse.new(`new Response(#{stream}, { status: #{status.to_i}, headers: #{js_headers} })`)
567
+ end
568
+
569
+ def stream
570
+ `(function(b64) { return new ReadableStream({ start(controller) { var bin = globalThis.atob(b64); var len = bin.length; var out = new Uint8Array(len); for (var i = 0; i < len; i++) { out[i] = bin.charCodeAt(i) & 0xff; } controller.enqueue(out); controller.close(); } }); })(#{@body_base64})`
571
+ end
572
+ end
573
+
533
574
  # NOTE: the single-line backtick `...` form is used below instead of the
534
575
  # multi-line `%x{ ... }` or multi-line backtick form. Opal's compiler
535
576
  # treats a *multi-line* x-string as a raw statement and refuses to use
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.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kazuhiro NISHIYAMA