homura-runtime 0.3.8 → 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 +4 -4
- data/CHANGELOG.md +8 -0
- data/exe/auto-await +23 -23
- data/exe/compile-assets +68 -61
- data/exe/compile-erb +263 -255
- data/exe/homura-build +74 -21
- data/lib/homura/runtime/ai.rb +104 -85
- data/lib/homura/runtime/async_registry.rb +124 -109
- data/lib/homura/runtime/auto_await/analyzer.rb +28 -15
- data/lib/homura/runtime/auto_await/transformer.rb +1 -0
- data/lib/homura/runtime/build_support.rb +90 -11
- data/lib/homura/runtime/cache.rb +21 -18
- data/lib/homura/runtime/durable_object.rb +27 -17
- data/lib/homura/runtime/email.rb +14 -14
- data/lib/homura/runtime/http.rb +4 -3
- data/lib/homura/runtime/multipart.rb +11 -4
- data/lib/homura/runtime/queue.rb +53 -23
- data/lib/homura/runtime/scheduled.rb +12 -14
- data/lib/homura/runtime/stream.rb +18 -14
- data/lib/homura/runtime/version.rb +1 -1
- data/lib/homura/runtime.rb +129 -93
- data/lib/homura_vendor_tempfile.rb +4 -2
- data/lib/homura_vendor_tilt.rb +5 -3
- data/lib/homura_vendor_zlib.rb +3 -0
- data/lib/opal_patches.rb +83 -66
- metadata +1 -1
|
@@ -88,6 +88,7 @@ module Cloudflare
|
|
|
88
88
|
# a chunked streaming response.
|
|
89
89
|
def each
|
|
90
90
|
end
|
|
91
|
+
|
|
91
92
|
def close
|
|
92
93
|
end
|
|
93
94
|
|
|
@@ -113,6 +114,7 @@ module Cloudflare
|
|
|
113
114
|
rescue StandardError
|
|
114
115
|
# best-effort; never let logging fail the cleanup branch
|
|
115
116
|
end
|
|
117
|
+
|
|
116
118
|
ensure
|
|
117
119
|
# `.__await__` here too, for the same reason: the ensure clause
|
|
118
120
|
# must not return until the writer is actually closed (otherwise
|
|
@@ -170,10 +172,10 @@ module Cloudflare
|
|
|
170
172
|
# enforced but the promise graph stays flat (Copilot review #3 —
|
|
171
173
|
# prior implementation pushed every write into an unbounded
|
|
172
174
|
# array which would leak memory on long-running streams).
|
|
173
|
-
@tail =
|
|
174
|
-
`#{@tail}.then(function() { return #{w}.write(#{enc}.encode(#{s})); })`
|
|
175
|
+
@tail = `#{@tail}.then(function() { return #{w}.write(#{enc}.encode(#{s})); })`
|
|
175
176
|
self
|
|
176
177
|
end
|
|
178
|
+
|
|
177
179
|
alias_method :<<, :write
|
|
178
180
|
|
|
179
181
|
# Helper: emit a well-formed SSE event. `data` is split on LF and
|
|
@@ -217,7 +219,8 @@ module Cloudflare
|
|
|
217
219
|
# error to the client via the HTTP layer. Single-line x-string
|
|
218
220
|
# so Opal emits it as an expression (see Multipart#to_uint8_array
|
|
219
221
|
# for the same gotcha).
|
|
220
|
-
`(async function(t, wr){ try { await t; } catch(e) {} try { await wr.close(); } catch(e) {} })(#{tail}, #{w})
|
|
222
|
+
`(async function(t, wr){ try { await t; } catch(e) {} try { await wr.close(); } catch(e) {} })(#{tail}, #{w})`
|
|
223
|
+
.__await__
|
|
221
224
|
self
|
|
222
225
|
end
|
|
223
226
|
|
|
@@ -273,22 +276,23 @@ module Sinatra
|
|
|
273
276
|
def stream(keep_open: false, type: :plain, headers: nil, &block)
|
|
274
277
|
ctx = env["cloudflare.ctx"]
|
|
275
278
|
extra_headers = headers || {}
|
|
276
|
-
merged =
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
279
|
+
merged = case type
|
|
280
|
+
when :sse, :event_stream
|
|
281
|
+
# SSE defaults は SSEStream 側で入る
|
|
282
|
+
extra_headers
|
|
283
|
+
else
|
|
284
|
+
# Plain streaming — start from an empty default set so
|
|
285
|
+
# the SSE headers don't get force-injected into e.g. a
|
|
286
|
+
# log-tailing or chunked-JSON endpoint.
|
|
287
|
+
{"content-type" => "text/plain; charset=utf-8"}.merge(extra_headers)
|
|
288
|
+
end
|
|
289
|
+
|
|
286
290
|
::Cloudflare::SSEStream.new(headers: merged, ctx: ctx, &block)
|
|
287
291
|
end
|
|
288
292
|
|
|
289
293
|
# Register the helper on a Sinatra app. Use `register Sinatra::Streaming`.
|
|
290
294
|
def self.registered(app)
|
|
291
|
-
app.helpers
|
|
295
|
+
app.helpers(Streaming)
|
|
292
296
|
end
|
|
293
297
|
end
|
|
294
298
|
end
|
data/lib/homura/runtime.rb
CHANGED
|
@@ -37,7 +37,8 @@ require "await"
|
|
|
37
37
|
|
|
38
38
|
class HomuraRuntimeIO
|
|
39
39
|
def initialize(channel)
|
|
40
|
-
|
|
40
|
+
# 'log' or 'error'
|
|
41
|
+
@channel = channel
|
|
41
42
|
@buffer = ""
|
|
42
43
|
end
|
|
43
44
|
|
|
@@ -48,6 +49,7 @@ class HomuraRuntimeIO
|
|
|
48
49
|
@buffer = @buffer + str
|
|
49
50
|
written += str.length
|
|
50
51
|
end
|
|
52
|
+
|
|
51
53
|
flush_lines
|
|
52
54
|
written
|
|
53
55
|
end
|
|
@@ -57,14 +59,17 @@ class HomuraRuntimeIO
|
|
|
57
59
|
emit("")
|
|
58
60
|
return nil
|
|
59
61
|
end
|
|
62
|
+
|
|
60
63
|
args.each do |arg|
|
|
61
64
|
if arg.is_a?(Array)
|
|
62
65
|
puts(*arg)
|
|
63
66
|
next
|
|
64
67
|
end
|
|
68
|
+
|
|
65
69
|
line = arg.to_s
|
|
66
70
|
@buffer = @buffer + (line.end_with?("\n") ? line : line + "\n")
|
|
67
71
|
end
|
|
72
|
+
|
|
68
73
|
flush_lines
|
|
69
74
|
nil
|
|
70
75
|
end
|
|
@@ -85,14 +90,18 @@ class HomuraRuntimeIO
|
|
|
85
90
|
def sync
|
|
86
91
|
true
|
|
87
92
|
end
|
|
93
|
+
|
|
88
94
|
def sync=(_)
|
|
89
95
|
end
|
|
96
|
+
|
|
90
97
|
def tty?
|
|
91
98
|
false
|
|
92
99
|
end
|
|
100
|
+
|
|
93
101
|
def isatty
|
|
94
102
|
false
|
|
95
103
|
end
|
|
104
|
+
|
|
96
105
|
def closed?
|
|
97
106
|
false
|
|
98
107
|
end
|
|
@@ -119,6 +128,7 @@ $stderr = HomuraRuntimeIO.new("error")
|
|
|
119
128
|
unless Object.const_defined?(:STDOUT) && STDOUT.is_a?(HomuraRuntimeIO)
|
|
120
129
|
Object.const_set(:STDOUT, $stdout)
|
|
121
130
|
end
|
|
131
|
+
|
|
122
132
|
unless Object.const_defined?(:STDERR) && STDERR.is_a?(HomuraRuntimeIO)
|
|
123
133
|
Object.const_set(:STDERR, $stderr)
|
|
124
134
|
end
|
|
@@ -184,18 +194,23 @@ module Rack
|
|
|
184
194
|
def self.call(js_req, js_env, js_ctx, body_text = "")
|
|
185
195
|
if @app.nil?
|
|
186
196
|
if defined?(::Sinatra::Homura) &&
|
|
187
|
-
|
|
197
|
+
::Sinatra::Homura.respond_to?(:ensure_rack_app!)
|
|
188
198
|
::Sinatra::Homura.ensure_rack_app!
|
|
189
199
|
end
|
|
200
|
+
|
|
190
201
|
if @app.nil?
|
|
191
|
-
raise
|
|
202
|
+
raise(
|
|
203
|
+
"`run app` was never called from user code, and no Sinatra app was discoverable (define `class App < Sinatra::Base` or use top-level classic Sinatra routes)"
|
|
204
|
+
)
|
|
192
205
|
end
|
|
193
206
|
end
|
|
194
207
|
|
|
195
208
|
env = build_rack_env(js_req, js_env, js_ctx, body_text)
|
|
196
209
|
result = @app.call(env)
|
|
197
|
-
|
|
198
|
-
|
|
210
|
+
if defined?(::Cloudflare) &&
|
|
211
|
+
::Cloudflare.js_promise?(result)
|
|
212
|
+
result = result.__await__
|
|
213
|
+
end
|
|
199
214
|
|
|
200
215
|
status, headers, body = result
|
|
201
216
|
build_js_response(status, headers, body)
|
|
@@ -223,7 +238,8 @@ module Rack
|
|
|
223
238
|
path = `#{url_obj}.pathname`
|
|
224
239
|
# Phase 16 docs: Sinatra + Opal builds responses with String#<< and raises on PATH_INFO `/docs/`.
|
|
225
240
|
path = "/docs" if path == "/docs/"
|
|
226
|
-
|
|
241
|
+
# includes leading '?' or empty string
|
|
242
|
+
raw_qs = `#{url_obj}.search`
|
|
227
243
|
qs = raw_qs && raw_qs.length > 0 ? raw_qs[1..-1] : ""
|
|
228
244
|
scheme = `#{url_obj}.protocol`.sub(/:\z/, "")
|
|
229
245
|
host = `#{url_obj}.hostname`
|
|
@@ -240,14 +256,13 @@ module Rack
|
|
|
240
256
|
"SERVER_PROTOCOL" => "HTTP/1.1",
|
|
241
257
|
"HTTPS" => scheme == "https" ? "on" : "off",
|
|
242
258
|
"rack.url_scheme" => scheme,
|
|
243
|
-
"rack.input" =>
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
),
|
|
259
|
+
"rack.input" => (
|
|
260
|
+
if body_text.nil? || body_text.empty?
|
|
261
|
+
EMPTY_STRING_IO
|
|
262
|
+
else
|
|
263
|
+
StringIO.new(body_text)
|
|
264
|
+
end
|
|
265
|
+
),
|
|
251
266
|
"rack.errors" => $stderr,
|
|
252
267
|
"rack.multithread" => false,
|
|
253
268
|
"rack.multiprocess" => false,
|
|
@@ -304,14 +319,14 @@ module Rack
|
|
|
304
319
|
# the JS object through unchanged — any reconstruction
|
|
305
320
|
# would strip runtime-only properties the client depends on.
|
|
306
321
|
first_body = (body.first if body.respond_to?(:first))
|
|
307
|
-
first_body_ruby =
|
|
308
|
-
!`(#{first_body} == null || #{first_body}.$$class == null)`
|
|
322
|
+
first_body_ruby = !`(#{first_body} == null || #{first_body}.$$class == null)`
|
|
309
323
|
raw = nil
|
|
310
324
|
if body.is_a?(::Cloudflare::RawResponse)
|
|
311
325
|
raw = body
|
|
312
326
|
elsif first_body_ruby && first_body.is_a?(::Cloudflare::RawResponse)
|
|
313
327
|
raw = first_body
|
|
314
328
|
end
|
|
329
|
+
|
|
315
330
|
if raw
|
|
316
331
|
js_resp = raw.js_response
|
|
317
332
|
return js_resp
|
|
@@ -320,7 +335,7 @@ module Rack
|
|
|
320
335
|
# Binary body fast-path: pass the JS ReadableStream directly
|
|
321
336
|
# to Response without touching Opal's String encoding.
|
|
322
337
|
if body.is_a?(::Cloudflare::BinaryBody) ||
|
|
323
|
-
|
|
338
|
+
(first_body_ruby && first_body.is_a?(::Cloudflare::BinaryBody))
|
|
324
339
|
bin = body.is_a?(::Cloudflare::BinaryBody) ? body : first_body
|
|
325
340
|
js_stream = bin.stream
|
|
326
341
|
ct = bin.content_type
|
|
@@ -331,20 +346,16 @@ module Rack
|
|
|
331
346
|
vs = v.to_s
|
|
332
347
|
`#{js_headers}[#{ks}] = #{vs}`
|
|
333
348
|
end
|
|
349
|
+
|
|
334
350
|
`#{js_headers}['content-type'] = #{ct}` if ct
|
|
335
351
|
`#{js_headers}['cache-control'] = #{cc}` if cc
|
|
336
|
-
return(
|
|
337
|
-
`new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`
|
|
338
|
-
)
|
|
352
|
+
return (`new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`)
|
|
339
353
|
end
|
|
340
354
|
|
|
341
355
|
if body.is_a?(::Cloudflare::EmbeddedBinaryBody) ||
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
)
|
|
346
|
-
bin =
|
|
347
|
-
body.is_a?(::Cloudflare::EmbeddedBinaryBody) ? body : first_body
|
|
356
|
+
(first_body_ruby &&
|
|
357
|
+
first_body.is_a?(::Cloudflare::EmbeddedBinaryBody))
|
|
358
|
+
bin = body.is_a?(::Cloudflare::EmbeddedBinaryBody) ? body : first_body
|
|
348
359
|
js_stream = bin.stream
|
|
349
360
|
ct = bin.content_type
|
|
350
361
|
cc = bin.cache_control
|
|
@@ -354,11 +365,10 @@ module Rack
|
|
|
354
365
|
vs = v.to_s
|
|
355
366
|
`#{js_headers}[#{ks}] = #{vs}`
|
|
356
367
|
end
|
|
368
|
+
|
|
357
369
|
`#{js_headers}['content-type'] = #{ct}` if ct
|
|
358
370
|
`#{js_headers}['cache-control'] = #{cc}` if cc
|
|
359
|
-
return(
|
|
360
|
-
`new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`
|
|
361
|
-
)
|
|
371
|
+
return (`new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`)
|
|
362
372
|
end
|
|
363
373
|
|
|
364
374
|
# Phase 10 — Workers AI streaming: a Cloudflare::AI::Stream wraps
|
|
@@ -366,16 +376,17 @@ module Rack
|
|
|
366
376
|
# ("data: {json}\n\n"). Pass it straight through so the client
|
|
367
377
|
# receives the chunks as they arrive.
|
|
368
378
|
first_body = (body.first if body.respond_to?(:first))
|
|
369
|
-
first_body_ruby =
|
|
370
|
-
!`(#{first_body} == null || #{first_body}.$$class == null)`
|
|
379
|
+
first_body_ruby = !`(#{first_body} == null || #{first_body}.$$class == null)`
|
|
371
380
|
|
|
372
381
|
stream_obj = nil
|
|
373
382
|
if body.respond_to?(:sse_stream?) && body.sse_stream?
|
|
374
383
|
stream_obj = body
|
|
375
|
-
elsif first_body_ruby &&
|
|
376
|
-
|
|
384
|
+
elsif first_body_ruby &&
|
|
385
|
+
first_body.respond_to?(:sse_stream?) &&
|
|
386
|
+
first_body.sse_stream?
|
|
377
387
|
stream_obj = first_body
|
|
378
388
|
end
|
|
389
|
+
|
|
379
390
|
if stream_obj
|
|
380
391
|
js_stream = stream_obj.js_stream
|
|
381
392
|
js_headers = `({})`
|
|
@@ -390,6 +401,7 @@ module Rack
|
|
|
390
401
|
vs = v.to_s
|
|
391
402
|
`#{js_headers}[#{ks}] = #{vs}`
|
|
392
403
|
end
|
|
404
|
+
|
|
393
405
|
if stream_obj.respond_to?(:response_headers)
|
|
394
406
|
stream_obj.response_headers.each do |k, v|
|
|
395
407
|
ks = k.to_s
|
|
@@ -404,18 +416,19 @@ module Rack
|
|
|
404
416
|
`#{js_headers}['cache-control'] = 'no-cache, no-transform'`
|
|
405
417
|
`#{js_headers}['x-accel-buffering'] = 'no'`
|
|
406
418
|
end
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
)
|
|
419
|
+
|
|
420
|
+
return (`new Response(#{js_stream}, { status: #{status.to_i}, headers: #{js_headers} })`)
|
|
410
421
|
end
|
|
411
422
|
|
|
412
423
|
raw_response = nil
|
|
413
424
|
if body.respond_to?(:raw_response?) && body.raw_response?
|
|
414
425
|
raw_response = body
|
|
415
|
-
elsif first_body_ruby &&
|
|
416
|
-
|
|
426
|
+
elsif first_body_ruby &&
|
|
427
|
+
first_body.respond_to?(:raw_response?) &&
|
|
428
|
+
first_body.raw_response?
|
|
417
429
|
raw_response = first_body
|
|
418
430
|
end
|
|
431
|
+
|
|
419
432
|
return raw_response.js_response if raw_response
|
|
420
433
|
|
|
421
434
|
chunks = []
|
|
@@ -425,50 +438,47 @@ module Rack
|
|
|
425
438
|
chunks << body
|
|
426
439
|
end
|
|
427
440
|
|
|
428
|
-
raw_chunk =
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
441
|
+
raw_chunk = chunks.find do |chunk|
|
|
442
|
+
chunk_ruby = !`(#{chunk} == null || #{chunk}.$$class == null)`
|
|
443
|
+
chunk_ruby &&
|
|
444
|
+
chunk.respond_to?(:raw_response?) &&
|
|
445
|
+
chunk.raw_response?
|
|
446
|
+
end
|
|
447
|
+
|
|
434
448
|
return raw_chunk.js_response if raw_chunk
|
|
435
449
|
|
|
436
|
-
binary_chunk =
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
(
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
end
|
|
450
|
+
binary_chunk = chunks.find do |chunk|
|
|
451
|
+
chunk_ruby = !`(#{chunk} == null || #{chunk}.$$class == null)`
|
|
452
|
+
(chunk_ruby &&
|
|
453
|
+
chunk.respond_to?(:stream) &&
|
|
454
|
+
chunk.respond_to?(:content_type)) ||
|
|
455
|
+
`#{chunk} != null && #{chunk}.stream != null && #{chunk}.content_type != null`
|
|
456
|
+
end
|
|
457
|
+
|
|
445
458
|
if binary_chunk
|
|
446
|
-
binary_chunk_ruby =
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
end
|
|
459
|
+
binary_chunk_ruby = !`(#{binary_chunk} == null || #{binary_chunk}.$$class == null)`
|
|
460
|
+
stream = if binary_chunk_ruby && binary_chunk.respond_to?(:stream)
|
|
461
|
+
binary_chunk.stream
|
|
462
|
+
else
|
|
463
|
+
`#{binary_chunk}.stream`
|
|
464
|
+
end
|
|
465
|
+
|
|
466
|
+
content_type = if binary_chunk_ruby && binary_chunk.respond_to?(:content_type)
|
|
467
|
+
binary_chunk.content_type
|
|
468
|
+
else
|
|
469
|
+
`#{binary_chunk}.content_type`
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
cache_control = if binary_chunk_ruby && binary_chunk.respond_to?(:cache_control)
|
|
473
|
+
binary_chunk.cache_control
|
|
474
|
+
else
|
|
475
|
+
`#{binary_chunk}.cache_control`
|
|
476
|
+
end
|
|
477
|
+
|
|
466
478
|
body_headers = {}
|
|
467
479
|
body_headers["content-type"] = content_type if content_type
|
|
468
480
|
body_headers["cache-control"] = cache_control if cache_control
|
|
469
|
-
return(
|
|
470
|
-
`new Response(#{stream}, { status: #{status.to_i}, headers: #{Cloudflare.headers_to_js(body_headers)} })`
|
|
471
|
-
)
|
|
481
|
+
return (`new Response(#{stream}, { status: #{status.to_i}, headers: #{Cloudflare.headers_to_js(body_headers)} })`)
|
|
472
482
|
end
|
|
473
483
|
|
|
474
484
|
# Build JS-side headers. Set-Cookie is the one HTTP response
|
|
@@ -496,8 +506,7 @@ module Rack
|
|
|
496
506
|
# Convert any `{ __multi__: true, values: [...] }` markers into
|
|
497
507
|
# a real `Headers` object that Workers' `new Response(headers:)`
|
|
498
508
|
# accepts. Single-valued headers stay as plain string values.
|
|
499
|
-
js_headers =
|
|
500
|
-
`(function(h) {
|
|
509
|
+
js_headers = `(function(h) {
|
|
501
510
|
var hasMulti = false;
|
|
502
511
|
for (var key in h) {
|
|
503
512
|
if (h[key] && typeof h[key] === 'object' && h[key].__multi__ === true) {
|
|
@@ -526,8 +535,7 @@ module Rack
|
|
|
526
535
|
has_promise = false
|
|
527
536
|
chunks.each do |c|
|
|
528
537
|
`#{js_chunks}.push(#{c})`
|
|
529
|
-
has_promise =
|
|
530
|
-
true if `#{c} != null && typeof #{c}.then === 'function'`
|
|
538
|
+
has_promise = true if `#{c} != null && typeof #{c}.then === 'function'`
|
|
531
539
|
end
|
|
532
540
|
|
|
533
541
|
if has_promise
|
|
@@ -543,11 +551,12 @@ module Rack
|
|
|
543
551
|
# used for 101 WebSocket upgrades where the Workers
|
|
544
552
|
# runtime's own Response carries runtime-only properties
|
|
545
553
|
# (`.webSocket`) that a reconstructed Response would lose.
|
|
546
|
-
`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
|
|
554
|
+
`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 && typeof v.value === 'string') { return v.value; } if (v != null && typeof v.$to_s === 'function') { return bodyToText(v.$to_s()); } if (v != null && typeof v.toString === 'function') { var s = v.toString(); if (typeof s === 'string' && s !== '[object Object]') return s; } 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++) { parts.push(bodyToText(resolved[i])); } return new Response(parts.join(''), { status: #{status_int}, headers: #{js_headers} }); })`
|
|
547
555
|
else
|
|
548
556
|
body_str = ""
|
|
549
557
|
chunks.each { |c| body_str = body_str + c.to_s }
|
|
550
|
-
`
|
|
558
|
+
js_body = `(function bodyToText(v) { if (v == null) return ''; if (typeof v === 'string') return v; if (v != null && typeof v.value === 'string') return v.value; if (v != null && typeof v.$to_s === 'function') return bodyToText(v.$to_s()); if (v != null && typeof v.toString === 'function') { var s = v.toString(); if (typeof s === 'string' && s !== '[object Object]') return s; } return String(v); })(#{body_str})`
|
|
559
|
+
`new Response(#{js_body}, { status: #{status_int}, headers: #{js_headers} })`
|
|
551
560
|
end
|
|
552
561
|
end
|
|
553
562
|
end
|
|
@@ -605,8 +614,10 @@ module Cloudflare
|
|
|
605
614
|
|
|
606
615
|
class D1Error < BindingError
|
|
607
616
|
end
|
|
617
|
+
|
|
608
618
|
class KVError < BindingError
|
|
609
619
|
end
|
|
620
|
+
|
|
610
621
|
class R2Error < BindingError
|
|
611
622
|
end
|
|
612
623
|
|
|
@@ -630,6 +641,7 @@ module Cloudflare
|
|
|
630
641
|
out << js_object_to_hash(js_row)
|
|
631
642
|
i += 1
|
|
632
643
|
end
|
|
644
|
+
|
|
633
645
|
out
|
|
634
646
|
end
|
|
635
647
|
|
|
@@ -652,9 +664,11 @@ module Cloudflare
|
|
|
652
664
|
elsif `typeof #{v} === 'object' && !Array.isArray(#{v}) && !(#{v} instanceof Date)`
|
|
653
665
|
v = js_object_to_hash(v)
|
|
654
666
|
end
|
|
667
|
+
|
|
655
668
|
h[k] = v
|
|
656
669
|
i += 1
|
|
657
670
|
end
|
|
671
|
+
|
|
658
672
|
h
|
|
659
673
|
end
|
|
660
674
|
|
|
@@ -670,6 +684,7 @@ module Cloudflare
|
|
|
670
684
|
`#{js_headers}[#{ks}] = #{vs}`
|
|
671
685
|
end
|
|
672
686
|
end
|
|
687
|
+
|
|
673
688
|
js_headers
|
|
674
689
|
end
|
|
675
690
|
|
|
@@ -755,6 +770,7 @@ module Cloudflare
|
|
|
755
770
|
vs = v.to_s
|
|
756
771
|
`#{js_headers}[#{ks}] = #{vs}`
|
|
757
772
|
end
|
|
773
|
+
|
|
758
774
|
`#{js_headers}['content-type'] = #{@content_type}` if @content_type
|
|
759
775
|
`#{js_headers}['cache-control'] = #{@cache_control}` if @cache_control
|
|
760
776
|
RawResponse.new(
|
|
@@ -859,6 +875,7 @@ module Cloudflare
|
|
|
859
875
|
if defined?(::Cloudflare) && ::Cloudflare.js_promise?(result)
|
|
860
876
|
result = result.__await__
|
|
861
877
|
end
|
|
878
|
+
|
|
862
879
|
return result unless result.is_a?(Hash)
|
|
863
880
|
nested = result["meta"]
|
|
864
881
|
return result unless nested.is_a?(Hash)
|
|
@@ -1039,8 +1056,10 @@ module Cloudflare
|
|
|
1039
1056
|
vs = v.to_s
|
|
1040
1057
|
`#{js_include}.push(#{vs})`
|
|
1041
1058
|
end
|
|
1059
|
+
|
|
1042
1060
|
`#{opts}.include = #{js_include}`
|
|
1043
1061
|
end
|
|
1062
|
+
|
|
1044
1063
|
`#{js_bucket}.list(#{opts}).then(function(res) { var rows = []; var arr = res && res.objects ? res.objects : []; for (var i = 0; i < arr.length; i++) { var o = arr[i]; var ct = (o.httpMetadata && o.httpMetadata.contentType) || 'application/octet-stream'; var h = new Map(); h.set('key', o.key); h.set('size', o.size|0); h.set('uploaded', o.uploaded ? o.uploaded.toISOString() : null); h.set('content_type', ct); rows.push(h); } return rows; })`
|
|
1045
1064
|
end
|
|
1046
1065
|
end
|
|
@@ -1055,9 +1074,10 @@ module Cloudflare
|
|
|
1055
1074
|
|
|
1056
1075
|
def attach!(env, js_env, js_ctx = nil)
|
|
1057
1076
|
env["cloudflare.env"] = js_env
|
|
1058
|
-
|
|
1059
|
-
"cloudflare.ctx"
|
|
1060
|
-
|
|
1077
|
+
unless `(#{js_ctx} == null || #{js_ctx} === undefined || #{js_ctx} === Opal.nil)`
|
|
1078
|
+
env["cloudflare.ctx"] = js_ctx
|
|
1079
|
+
end
|
|
1080
|
+
|
|
1061
1081
|
if `(#{js_env} == null || #{js_env} === undefined || #{js_env} === Opal.nil)`
|
|
1062
1082
|
return env
|
|
1063
1083
|
end
|
|
@@ -1094,6 +1114,7 @@ module Cloudflare
|
|
|
1094
1114
|
if `(#{js_binding} == null || #{js_binding} === undefined || #{js_binding} === Opal.nil)`
|
|
1095
1115
|
return env
|
|
1096
1116
|
end
|
|
1117
|
+
|
|
1097
1118
|
return env unless defined?(::Cloudflare::DurableObjectNamespace)
|
|
1098
1119
|
|
|
1099
1120
|
suffix = normalize_binding_name(name)
|
|
@@ -1113,11 +1134,11 @@ module Cloudflare
|
|
|
1113
1134
|
while i < len
|
|
1114
1135
|
key = `#{keys}[#{i}]`
|
|
1115
1136
|
js_binding = `#{js_env}[#{key}]`
|
|
1116
|
-
is_do =
|
|
1117
|
-
`#{js_binding} != null && typeof #{js_binding}.idFromName === 'function' && typeof #{js_binding}.get === 'function'`
|
|
1137
|
+
is_do = `#{js_binding} != null && typeof #{js_binding}.idFromName === 'function' && typeof #{js_binding}.get === 'function'`
|
|
1118
1138
|
attach_durable_object!(env, key, js_binding) if is_do
|
|
1119
1139
|
i += 1
|
|
1120
1140
|
end
|
|
1141
|
+
|
|
1121
1142
|
env
|
|
1122
1143
|
end
|
|
1123
1144
|
|
|
@@ -1125,6 +1146,7 @@ module Cloudflare
|
|
|
1125
1146
|
if `(#{js_binding} == null || #{js_binding} === undefined || #{js_binding} === Opal.nil)`
|
|
1126
1147
|
return env
|
|
1127
1148
|
end
|
|
1149
|
+
|
|
1128
1150
|
return env unless defined?(::Cloudflare::Queue)
|
|
1129
1151
|
|
|
1130
1152
|
suffix = normalize_binding_name(name)
|
|
@@ -1137,12 +1159,13 @@ module Cloudflare
|
|
|
1137
1159
|
|
|
1138
1160
|
js_send_email = `#{js_env} && #{js_env}.SEND_EMAIL`
|
|
1139
1161
|
if `#{js_send_email} == null || #{js_send_email} === undefined`
|
|
1140
|
-
js_send_email =
|
|
1141
|
-
|
|
1162
|
+
js_send_email = `(typeof globalThis !== 'undefined' && globalThis.__OPAL_WORKERS__ && globalThis.__OPAL_WORKERS__.sendEmailBinding) || null`
|
|
1163
|
+
end
|
|
1164
|
+
|
|
1165
|
+
if `#{js_send_email} != null`
|
|
1166
|
+
env["cloudflare.SEND_EMAIL"] = Email.new(js_send_email)
|
|
1142
1167
|
end
|
|
1143
|
-
|
|
1144
|
-
js_send_email
|
|
1145
|
-
) if `#{js_send_email} != null`
|
|
1168
|
+
|
|
1146
1169
|
env
|
|
1147
1170
|
end
|
|
1148
1171
|
|
|
@@ -1164,10 +1187,12 @@ module Cloudflare
|
|
|
1164
1187
|
if `(#{raw} == null || #{raw} === undefined || #{raw} === Opal.nil)`
|
|
1165
1188
|
return nil
|
|
1166
1189
|
end
|
|
1190
|
+
|
|
1167
1191
|
if defined?(::Cloudflare::AI::Binding) &&
|
|
1168
|
-
|
|
1192
|
+
`(#{raw} != null && #{raw}.$$class === #{::Cloudflare::AI::Binding})`
|
|
1169
1193
|
return raw
|
|
1170
1194
|
end
|
|
1195
|
+
|
|
1171
1196
|
if defined?(::Cloudflare::AI::Binding)
|
|
1172
1197
|
return ::Cloudflare::AI::Binding.new(raw)
|
|
1173
1198
|
end
|
|
@@ -1180,36 +1205,47 @@ module Cloudflare
|
|
|
1180
1205
|
def cf_env
|
|
1181
1206
|
env["cloudflare.env"]
|
|
1182
1207
|
end
|
|
1208
|
+
|
|
1183
1209
|
def cf_ctx
|
|
1184
1210
|
env["cloudflare.ctx"]
|
|
1185
1211
|
end
|
|
1212
|
+
|
|
1186
1213
|
def d1
|
|
1187
1214
|
env["cloudflare.DB"]
|
|
1188
1215
|
end
|
|
1216
|
+
|
|
1189
1217
|
def db
|
|
1190
1218
|
d1
|
|
1191
1219
|
end
|
|
1220
|
+
|
|
1192
1221
|
def kv
|
|
1193
1222
|
env["cloudflare.KV"]
|
|
1194
1223
|
end
|
|
1224
|
+
|
|
1195
1225
|
def bucket
|
|
1196
1226
|
env["cloudflare.BUCKET"]
|
|
1197
1227
|
end
|
|
1228
|
+
|
|
1198
1229
|
def ai
|
|
1199
1230
|
Cloudflare::Bindings.ai(env)
|
|
1200
1231
|
end
|
|
1232
|
+
|
|
1201
1233
|
def send_email
|
|
1202
1234
|
env["cloudflare.SEND_EMAIL"]
|
|
1203
1235
|
end
|
|
1236
|
+
|
|
1204
1237
|
def jobs_queue
|
|
1205
1238
|
env["cloudflare.QUEUE_JOBS"]
|
|
1206
1239
|
end
|
|
1240
|
+
|
|
1207
1241
|
def jobs_dlq
|
|
1208
1242
|
env["cloudflare.QUEUE_JOBS_DLQ"]
|
|
1209
1243
|
end
|
|
1244
|
+
|
|
1210
1245
|
def do_counter
|
|
1211
1246
|
env["cloudflare.DO_COUNTER"]
|
|
1212
1247
|
end
|
|
1248
|
+
|
|
1213
1249
|
def cache
|
|
1214
1250
|
@__homura_cache ||= Cloudflare::Cache.default
|
|
1215
1251
|
end
|
|
@@ -8,8 +8,10 @@ class Tempfile < StringIO
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def self.open(*)
|
|
11
|
-
raise
|
|
12
|
-
|
|
11
|
+
raise(
|
|
12
|
+
NotImplementedError,
|
|
13
|
+
"Tempfile is stubbed in homura (Workers have no writable FS)"
|
|
14
|
+
)
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
def path
|
data/lib/homura_vendor_tilt.rb
CHANGED
|
@@ -56,9 +56,11 @@ module Tilt
|
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def new(file = nil, line = nil, options = {}, &block)
|
|
59
|
-
raise
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
raise(
|
|
60
|
+
NotImplementedError,
|
|
61
|
+
"Tilt template rendering is not available in homura Phase 2 " \
|
|
62
|
+
"(stubbed). Return Strings or arrays from your Sinatra handlers."
|
|
63
|
+
)
|
|
62
64
|
end
|
|
63
65
|
end
|
|
64
66
|
end
|