homura-runtime 0.1.5 → 0.2.0
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 +14 -0
- data/README.md +2 -2
- data/bin/cloudflare-workers-build +9 -26
- data/docs/ARCHITECTURE.md +2 -2
- data/lib/cloudflare_workers/build_support.rb +33 -1
- data/lib/cloudflare_workers/version.rb +1 -1
- data/lib/cloudflare_workers.rb +18 -9
- metadata +2 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f8d2b1f63c73d674a1e14a9ad376877678ed2f8697622e1282261f85ae092a2
|
|
4
|
+
data.tar.gz: dbedff400687b46bc6817df021ddc1c6560a4953d6c6a30c7a570980609e749a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6416ec5f4d14535c6a2eeddd47183bc6e1cd59d095ee3620ad5b96d0f019b11e576cfcf2f1c147cf93326189bd3750611438a41e09ec542f8e12f749732f5b75
|
|
7
|
+
data.tar.gz: 344e21cf2f8f9ca6fc7c1291cf363ded09ac1857ccede0d1184599de39170c62c1494c7d9660e6675888cc4b97ef0d083fa83491935adea946e425b4de2ce6db
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.0 (2026-04-23)
|
|
4
|
+
|
|
5
|
+
- Remove the old `cloudflare-workers-build` public executable and expose the
|
|
6
|
+
build pipeline through `homura build`.
|
|
7
|
+
- Keep the internal build implementation in the runtime gem while letting the
|
|
8
|
+
single `homura` CLI dispatch to it.
|
|
9
|
+
|
|
10
|
+
## 0.1.6 (2026-04-23)
|
|
11
|
+
|
|
12
|
+
- Teach `cloudflare-workers-build --standalone --with-db` to add the packaged
|
|
13
|
+
`sequel-d1` gem `vendor/` directory to the Opal load path before the gem's
|
|
14
|
+
`lib/`, so `require 'sequel'` resolves to the bundled Opal-compatible Sequel
|
|
15
|
+
subset instead of the CRuby gem.
|
|
16
|
+
|
|
3
17
|
## 0.1.5 (2026-04-23)
|
|
4
18
|
|
|
5
19
|
- Make `auto-await` emit rewritten files for existing hand-written `.__await__`
|
data/README.md
CHANGED
|
@@ -9,13 +9,13 @@ Core Ruby + Module Worker glue for [Opal](https://opalrb.com/) on [Cloudflare Wo
|
|
|
9
9
|
- `runtime/worker_module.mjs` — fetch / scheduled / queue / DO adapters (**no Opal bundle import**).
|
|
10
10
|
- `runtime/worker.mjs` — thin bootstrap (crypto shim → bundle → `worker_module`) for legacy layouts.
|
|
11
11
|
- `runtime/setup-node-crypto.mjs` — `node:crypto` on `globalThis` before the Opal bundle loads.
|
|
12
|
-
- `
|
|
12
|
+
- `homura build` — single build pipeline (ERB → assets → Opal → patch → `worker.entrypoint.mjs`). Use `--standalone` in generated apps; it now restores `cf-runtime/` automatically and derives standalone template/asset namespaces from the project name by default.
|
|
13
13
|
- `docs/ARCHITECTURE.md` — wrangler `main`, codegen entrypoint, and fixed-import policy.
|
|
14
14
|
|
|
15
15
|
## Quick start (homura monorepo)
|
|
16
16
|
|
|
17
17
|
1. `Gemfile`: `gem 'homura-runtime', path: 'gems/homura-runtime'` and `gem 'opal-homura', '= 1.8.3.rc1', require: 'opal'` (path or exact pin).
|
|
18
|
-
2. `bundle exec
|
|
18
|
+
2. `bundle exec homura build` — writes `build/hello.no-exit.mjs` and `build/worker.entrypoint.mjs`.
|
|
19
19
|
3. `wrangler.toml`: `main = "build/worker.entrypoint.mjs"`, `compatibility_flags = ["nodejs_compat"]`.
|
|
20
20
|
|
|
21
21
|
## Support matrix (indicative)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
# frozen_string_literal: true
|
|
3
3
|
|
|
4
|
-
#
|
|
5
|
-
#
|
|
4
|
+
# homura build: ERB precompile → assets embed → Opal bundle → patch-opal-evals → worker.entrypoint.mjs
|
|
5
|
+
# Use --standalone for generated consumer apps (no homura inline routes).
|
|
6
6
|
|
|
7
7
|
require 'fileutils'
|
|
8
8
|
require 'open3'
|
|
@@ -50,7 +50,7 @@ options = {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
OptionParser.new do |o|
|
|
53
|
-
o.banner = 'usage:
|
|
53
|
+
o.banner = 'usage: homura build [options]'
|
|
54
54
|
o.on('--root PATH', 'Project root (default: cwd)') { |p| options[:root] = p }
|
|
55
55
|
o.on('--standalone', 'Consumer app (skip inline-routes; use Gemfile-resolved load paths)') do
|
|
56
56
|
options[:standalone] = true
|
|
@@ -90,7 +90,7 @@ patch_target = options[:patch_input] || options[:opal_output]
|
|
|
90
90
|
|
|
91
91
|
def run!(argv, chdir:)
|
|
92
92
|
Dir.chdir(chdir) do
|
|
93
|
-
system(*argv) || abort("
|
|
93
|
+
system(*argv) || abort("homura build: failed: #{argv.join(' ')}")
|
|
94
94
|
end
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -113,7 +113,7 @@ def run_opal_homura!(root, opal_input, opal_output)
|
|
|
113
113
|
env = { 'OPAL_PREFORK_DISABLE' => '1' }
|
|
114
114
|
out_err, status = Open3.capture2e(env, *argv, chdir: root.to_s)
|
|
115
115
|
File.write(stderr_log, out_err)
|
|
116
|
-
abort('
|
|
116
|
+
abort('homura build: opal failed') unless status.success?
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
def homura_vendor_from_gemfile(project_root)
|
|
@@ -121,24 +121,7 @@ def homura_vendor_from_gemfile(project_root)
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def run_opal_standalone!(root, opal_input, opal_output, with_db:)
|
|
124
|
-
load_paths =
|
|
125
|
-
hv = homura_vendor_from_gemfile(root)
|
|
126
|
-
load_paths << hv.to_s if hv
|
|
127
|
-
runtime_name = CloudflareWorkers::BuildSupport::RUNTIME_GEM_NAME
|
|
128
|
-
sinatra_name = CloudflareWorkers::BuildSupport::SINATRA_GEM_NAME
|
|
129
|
-
|
|
130
|
-
load_paths += ['build/auto_await/app', 'app']
|
|
131
|
-
[
|
|
132
|
-
CloudflareWorkersBuild.gem_lib(runtime_name),
|
|
133
|
-
CloudflareWorkersBuild.gem_vendor(runtime_name),
|
|
134
|
-
CloudflareWorkersBuild.gem_lib(sinatra_name),
|
|
135
|
-
CloudflareWorkersBuild.gem_vendor(sinatra_name)
|
|
136
|
-
].compact.each do |path|
|
|
137
|
-
load_paths << path
|
|
138
|
-
end
|
|
139
|
-
load_paths << CloudflareWorkersBuild.gem_lib('sequel-d1') if with_db
|
|
140
|
-
load_paths << 'vendor' if root.join('vendor').directory?
|
|
141
|
-
load_paths << 'build'
|
|
124
|
+
load_paths = CloudflareWorkers::BuildSupport.standalone_load_paths(root, with_db: with_db)
|
|
142
125
|
|
|
143
126
|
argv = ['bundle', 'exec', 'opal', '-c', '-E', '--esm', '--no-source-map']
|
|
144
127
|
load_paths.each { |p| argv.push('-I', p) }
|
|
@@ -149,12 +132,12 @@ def run_opal_standalone!(root, opal_input, opal_output, with_db:)
|
|
|
149
132
|
env = { 'OPAL_PREFORK_DISABLE' => '1' }
|
|
150
133
|
out_err, status = Open3.capture2e(env, *argv, chdir: root.to_s)
|
|
151
134
|
File.write(stderr_log, out_err)
|
|
152
|
-
abort('
|
|
135
|
+
abort('homura build: opal failed') unless status.success?
|
|
153
136
|
end
|
|
154
137
|
|
|
155
138
|
def write_entrypoint!(root, out_path, setup:, bundle:, worker_mod:)
|
|
156
139
|
body = <<~JS
|
|
157
|
-
// GENERATED by
|
|
140
|
+
// GENERATED by homura build — do not edit by hand.
|
|
158
141
|
import "#{setup}";
|
|
159
142
|
import "#{bundle}";
|
|
160
143
|
export { default, HomuraCounterDO } from "#{worker_mod}";
|
|
@@ -260,4 +243,4 @@ write_entrypoint!(
|
|
|
260
243
|
worker_mod: options[:worker_module_import]
|
|
261
244
|
)
|
|
262
245
|
|
|
263
|
-
$stderr.puts '
|
|
246
|
+
$stderr.puts 'homura build: ok'
|
data/docs/ARCHITECTURE.md
CHANGED
|
@@ -30,13 +30,13 @@ flowchart LR
|
|
|
30
30
|
## homura 本体
|
|
31
31
|
|
|
32
32
|
- `wrangler.toml` の `main` は `build/worker.entrypoint.mjs`。
|
|
33
|
-
- `bundle exec
|
|
33
|
+
- `bundle exec homura build` が ERB / assets / Opal / patch / entrypoint 生成まで一括実行。
|
|
34
34
|
|
|
35
35
|
## スキャフォールド済みアプリ
|
|
36
36
|
|
|
37
37
|
- プロジェクト直下に `worker.entrypoint.mjs`(`main` と一致)。
|
|
38
38
|
- `cf-runtime/` に `setup-node-crypto.mjs` と `worker_module.mjs` をコピー(gem から)。
|
|
39
|
-
- `bundle exec
|
|
39
|
+
- `bundle exec homura build --standalone` が consumer 向けパイプラインを実行し、`Gemfile` の `path:` から homura の `vendor/` を追加ロードパスへ取り込み(digest / zlib 等の Workers 向け補助ファイル)。
|
|
40
40
|
|
|
41
41
|
## Phase 17 — Email Service(`SEND_EMAIL`)
|
|
42
42
|
|
|
@@ -7,6 +7,7 @@ module CloudflareWorkers
|
|
|
7
7
|
module BuildSupport
|
|
8
8
|
RUNTIME_GEM_NAME = 'homura-runtime'
|
|
9
9
|
SINATRA_GEM_NAME = 'sinatra-homura'
|
|
10
|
+
SEQUEL_D1_GEM_NAME = 'sequel-d1'
|
|
10
11
|
|
|
11
12
|
class << self
|
|
12
13
|
def loaded_spec(name, loaded_specs: Gem.loaded_specs)
|
|
@@ -17,7 +18,7 @@ module CloudflareWorkers
|
|
|
17
18
|
spec = loaded_spec(name, loaded_specs: loaded_specs)
|
|
18
19
|
return spec.full_gem_path if spec
|
|
19
20
|
|
|
20
|
-
raise("
|
|
21
|
+
raise("homura build: gem #{name} not loaded; use bundle exec from app root")
|
|
21
22
|
end
|
|
22
23
|
|
|
23
24
|
def runtime_root(current_file:, loaded_specs: Gem.loaded_specs)
|
|
@@ -53,6 +54,37 @@ module CloudflareWorkers
|
|
|
53
54
|
target_dir
|
|
54
55
|
end
|
|
55
56
|
|
|
57
|
+
def standalone_load_paths(project_root, with_db:, loaded_specs: Gem.loaded_specs)
|
|
58
|
+
root = Pathname(project_root)
|
|
59
|
+
load_paths = []
|
|
60
|
+
|
|
61
|
+
hv = vendor_from_gemfile(root)
|
|
62
|
+
load_paths << hv.to_s if hv
|
|
63
|
+
|
|
64
|
+
load_paths += ['build/auto_await/app', 'app']
|
|
65
|
+
[
|
|
66
|
+
gem_lib(RUNTIME_GEM_NAME, loaded_specs: loaded_specs),
|
|
67
|
+
gem_vendor(RUNTIME_GEM_NAME, loaded_specs: loaded_specs),
|
|
68
|
+
gem_lib(SINATRA_GEM_NAME, loaded_specs: loaded_specs),
|
|
69
|
+
gem_vendor(SINATRA_GEM_NAME, loaded_specs: loaded_specs)
|
|
70
|
+
].compact.each do |path|
|
|
71
|
+
load_paths << path
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
if with_db
|
|
75
|
+
[
|
|
76
|
+
gem_vendor(SEQUEL_D1_GEM_NAME, loaded_specs: loaded_specs),
|
|
77
|
+
gem_lib(SEQUEL_D1_GEM_NAME, loaded_specs: loaded_specs)
|
|
78
|
+
].compact.each do |path|
|
|
79
|
+
load_paths << path
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
load_paths << 'vendor' if root.join('vendor').directory?
|
|
84
|
+
load_paths << 'build'
|
|
85
|
+
load_paths.uniq
|
|
86
|
+
end
|
|
87
|
+
|
|
56
88
|
def standalone_namespace(project_root, suffix)
|
|
57
89
|
base = Pathname(project_root).basename.to_s
|
|
58
90
|
parts = base.split(/[^A-Za-z0-9]+/).reject(&:empty?)
|
data/lib/cloudflare_workers.rb
CHANGED
|
@@ -20,9 +20,10 @@
|
|
|
20
20
|
# style entry point and never sees a Cloudflare-specific symbol.
|
|
21
21
|
#
|
|
22
22
|
# 3. Cloudflare::D1Database / KVNamespace / R2Bucket — tiny Ruby wrappers
|
|
23
|
-
# around the JS bindings.
|
|
24
|
-
#
|
|
25
|
-
#
|
|
23
|
+
# around the JS bindings. At the raw runtime layer they still return
|
|
24
|
+
# Promises, but homura's build-time auto-await pass rewrites the
|
|
25
|
+
# common Sinatra-facing call sites (`db.execute`, `kv.get`, etc.) so
|
|
26
|
+
# app code usually stays sync-shaped.
|
|
26
27
|
#
|
|
27
28
|
# Note: Opal Strings are immutable (they map to JS Strings), so this file
|
|
28
29
|
# uses reassignment (`@buffer = @buffer + str`) instead of `<<` mutation.
|
|
@@ -209,8 +210,10 @@ module Rack
|
|
|
209
210
|
|
|
210
211
|
# Expose D1 / KV / R2 bindings as plain Ruby wrapper objects.
|
|
211
212
|
# The user Sinatra routes reach them via
|
|
212
|
-
# `env['cloudflare.DB']` / `.KV` / `.BUCKET
|
|
213
|
-
# Ruby methods on them
|
|
213
|
+
# `env['cloudflare.DB']` / `.KV` / `.BUCKET` and call ordinary
|
|
214
|
+
# Ruby methods on them. Under the hood those methods are async,
|
|
215
|
+
# but homura's auto-await build step inserts `.__await__` for the
|
|
216
|
+
# common binding/helper patterns so app source usually does not.
|
|
214
217
|
js_db = `#{js_env} && #{js_env}.DB`
|
|
215
218
|
js_kv = `#{js_env} && #{js_env}.KV`
|
|
216
219
|
js_r2 = `#{js_env} && #{js_env}.BUCKET`
|
|
@@ -603,8 +606,10 @@ module Cloudflare
|
|
|
603
606
|
|
|
604
607
|
# ---- sqlite3-ruby compatible high-level API ----------------------
|
|
605
608
|
|
|
606
|
-
# Execute a SQL statement with optional bind parameters
|
|
607
|
-
#
|
|
609
|
+
# Execute a SQL statement with optional bind parameters.
|
|
610
|
+
# Returns a JS Promise resolving to an Array of Hashes; the build-time
|
|
611
|
+
# auto-await pass rewrites the usual Sinatra call sites so app code can
|
|
612
|
+
# stay `db.execute(...)` instead of spelling `.__await__`.
|
|
608
613
|
#
|
|
609
614
|
# db.execute("SELECT * FROM users") → Array<Hash>
|
|
610
615
|
# db.execute("SELECT * FROM users WHERE id = ?", [1]) → Array<Hash>
|
|
@@ -616,6 +621,7 @@ module Cloudflare
|
|
|
616
621
|
end
|
|
617
622
|
|
|
618
623
|
# Execute and return only the first row (or nil).
|
|
624
|
+
# Returns a JS Promise resolving to a Hash or nil.
|
|
619
625
|
#
|
|
620
626
|
# db.get_first_row("SELECT * FROM users WHERE id = ?", [1]) → Hash or nil
|
|
621
627
|
def get_first_row(sql, bind_params = [])
|
|
@@ -626,6 +632,7 @@ module Cloudflare
|
|
|
626
632
|
|
|
627
633
|
# Execute a write statement (INSERT / UPDATE / DELETE) and return
|
|
628
634
|
# a metadata Hash with `changes`, `last_row_id`, `duration`, etc.
|
|
635
|
+
# Returns a JS Promise resolving to that metadata Hash.
|
|
629
636
|
#
|
|
630
637
|
# meta = db.execute_insert("INSERT INTO users (name) VALUES (?)", ["alice"])
|
|
631
638
|
# meta['last_row_id'] # → 7
|
|
@@ -636,7 +643,7 @@ module Cloudflare
|
|
|
636
643
|
end
|
|
637
644
|
|
|
638
645
|
# Execute one or more raw SQL statements separated by semicolons.
|
|
639
|
-
# Useful for schema migrations. Returns the D1 exec result.
|
|
646
|
+
# Useful for schema migrations. Returns the raw async D1 exec result.
|
|
640
647
|
def execute_batch(sql)
|
|
641
648
|
exec(sql)
|
|
642
649
|
end
|
|
@@ -697,6 +704,7 @@ module Cloudflare
|
|
|
697
704
|
end
|
|
698
705
|
|
|
699
706
|
# KV#get returns a JS Promise resolving to a String or nil.
|
|
707
|
+
# In normal Sinatra app code, auto-await usually hides that Promise.
|
|
700
708
|
def get(key)
|
|
701
709
|
js_kv = @js
|
|
702
710
|
err_cls = Cloudflare::KVError
|
|
@@ -705,7 +713,8 @@ module Cloudflare
|
|
|
705
713
|
|
|
706
714
|
# Put a value. `expiration_ttl:` (seconds) maps to the Workers KV
|
|
707
715
|
# `expirationTtl` option so callers can set TTLs without reaching
|
|
708
|
-
# for backticks. Returns a JS Promise
|
|
716
|
+
# for backticks. Returns a JS Promise; common route/helper call sites
|
|
717
|
+
# are auto-awaited during the build.
|
|
709
718
|
def put(key, value, expiration_ttl: nil)
|
|
710
719
|
js_kv = @js
|
|
711
720
|
err_cls = Cloudflare::KVError
|
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kazuhiro NISHIYAMA
|
|
@@ -42,8 +42,7 @@ description: |
|
|
|
42
42
|
Rack handler, D1/KV/R2/AI/Queue/Durable Object adapters, multipart/streaming,
|
|
43
43
|
and Opal corelib patches. Use with the `opal` gem and a Module Worker
|
|
44
44
|
(`runtime/worker.mjs` in this gem).
|
|
45
|
-
executables:
|
|
46
|
-
- cloudflare-workers-build
|
|
45
|
+
executables: []
|
|
47
46
|
extensions: []
|
|
48
47
|
extra_rdoc_files: []
|
|
49
48
|
files:
|