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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3d8286719ce164088136af4d1caf0476c026d4ec2e42b98b08db8c84df6766d
4
- data.tar.gz: 849f3bb4ae14131b950de3a89efff6032a4204e9878a19c84cfe9dcdfa618e8b
3
+ metadata.gz: 2f8d2b1f63c73d674a1e14a9ad376877678ed2f8697622e1282261f85ae092a2
4
+ data.tar.gz: dbedff400687b46bc6817df021ddc1c6560a4953d6c6a30c7a570980609e749a
5
5
  SHA512:
6
- metadata.gz: c692cab5b2375646806e28b72b2e3c82da48563dcfc6a9074b8a8f3d9a756a92caee4ce100eee22bfaf81d00a459b3b85541176565fc1c3de5545f5c08b5216b
7
- data.tar.gz: 0eda9cfebebb75b1efd6764d6be2b27710769e30a215e9c9cb5b9d635534afbf685cc8aeac632ad09378ecf106768d9b395093f4055c2293179b81817484f8a6
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
- - `bin/cloudflare-workers-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.
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 cloudflare-workers-build` — writes `build/hello.no-exit.mjs` and `build/worker.entrypoint.mjs`.
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
- # Single happy path: ERB precompile → assets embed → Opal bundle → patch-opal-evals → worker.entrypoint.mjs
5
- # No subcommands. Use --standalone for generated consumer apps (no homura inline routes).
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: cloudflare-workers-build [options]'
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("cloudflare-workers-build: failed: #{argv.join(' ')}")
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('cloudflare-workers-build: opal failed') unless status.success?
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('cloudflare-workers-build: opal failed') unless status.success?
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 cloudflare-workers-build — do not edit by hand.
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 'cloudflare-workers-build: ok'
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 cloudflare-workers-build` が ERB / assets / Opal / patch / entrypoint 生成まで一括実行。
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 cloudflare-workers-build --standalone` が consumer 向けパイプラインを実行し、`Gemfile` の `path:` から homura の `vendor/` を追加ロードパスへ取り込み(digest / zlib 等の Workers 向け補助ファイル)。
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("cloudflare-workers-build: gem #{name} not loaded; use bundle exec from app root")
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?)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module CloudflareWorkers
4
- VERSION = '0.1.5'
4
+ VERSION = '0.2.0'
5
5
  end
@@ -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. They expose the binding methods as regular
24
- # Ruby method calls returning native JS Promises, which the user
25
- # routes can `.__await__` inside a `# await: true` block.
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`, call normal-looking
213
- # Ruby methods on them, and `.__await__` the resulting JS Promise.
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 and return
607
- # all result rows as an Array of Hashes.
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.1.5
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: