tina4ruby 3.13.36 → 3.13.38
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/lib/tina4/auth.rb +118 -7
- data/lib/tina4/cli.rb +106 -2
- data/lib/tina4/database.rb +356 -46
- data/lib/tina4/dev_admin.rb +54 -11
- data/lib/tina4/drivers/sqlite_driver.rb +23 -0
- data/lib/tina4/env.rb +40 -4
- data/lib/tina4/events.rb +54 -8
- data/lib/tina4/graphql.rb +68 -12
- data/lib/tina4/html_element.rb +55 -7
- data/lib/tina4/mcp.rb +10 -3
- data/lib/tina4/messenger.rb +130 -25
- data/lib/tina4/metrics.rb +238 -47
- data/lib/tina4/middleware.rb +136 -13
- data/lib/tina4/migration.rb +6 -4
- data/lib/tina4/orm.rb +13 -10
- data/lib/tina4/public/js/tina4-dev-admin.js +212 -212
- data/lib/tina4/public/js/tina4-dev-admin.min.js +212 -212
- data/lib/tina4/rack_app.rb +17 -10
- data/lib/tina4/response.rb +31 -11
- data/lib/tina4/seeder.rb +433 -84
- data/lib/tina4/session.rb +94 -17
- data/lib/tina4/version.rb +1 -1
- data/lib/tina4/websocket.rb +354 -18
- data/lib/tina4/wsdl.rb +25 -2
- data/lib/tina4.rb +11 -9
- metadata +6 -47
data/lib/tina4/rack_app.rb
CHANGED
|
@@ -351,9 +351,12 @@ module Tina4
|
|
|
351
351
|
env["tina4.request"] = request # Store for session save after response
|
|
352
352
|
response = Tina4::Response.new
|
|
353
353
|
|
|
354
|
-
# Run global middleware (block-based + class-based before_* methods)
|
|
354
|
+
# Run global middleware (block-based + class-based before_* methods).
|
|
355
|
+
# M2 — AFTER-ON-4xx RULE: when a before_* short-circuits (4xx/skip) or
|
|
356
|
+
# throws (clean 500), the after-pass STILL runs so after_* can add
|
|
357
|
+
# headers/logging — consistent across all 4 frameworks.
|
|
355
358
|
unless Tina4::Middleware.run_before(Tina4::Middleware.global_middleware, request, response)
|
|
356
|
-
|
|
359
|
+
Tina4::Middleware.run_after(Tina4::Middleware.global_middleware, request, response)
|
|
357
360
|
return response.to_rack
|
|
358
361
|
end
|
|
359
362
|
|
|
@@ -885,18 +888,23 @@ module Tina4
|
|
|
885
888
|
# Wire the route handler into the WebSocket engine events
|
|
886
889
|
handler = ws_route.handler
|
|
887
890
|
|
|
888
|
-
# Create a dedicated WebSocket engine for this route so
|
|
891
|
+
# Create a dedicated WebSocket *event* engine for this route so each
|
|
892
|
+
# upgrade keeps its own isolated open/message/close handlers (the engine's
|
|
893
|
+
# on() appends handlers, so a single shared event engine would cross-wire
|
|
894
|
+
# routes).
|
|
889
895
|
ws = Tina4::WebSocket.new
|
|
890
896
|
|
|
891
|
-
# The
|
|
892
|
-
#
|
|
893
|
-
#
|
|
894
|
-
# Python's single
|
|
897
|
+
# The connection itself is OWNED by a process-wide shared manager so that
|
|
898
|
+
# broadcasts, rooms, the multi-instance backplane and the idle reaper span
|
|
899
|
+
# every route's connections — not just this one per-socket event engine.
|
|
900
|
+
# Mirrors Python's single WebSocketManager. The dev-reload channel uses its
|
|
901
|
+
# own shared manager (Tina4::DevReload) so POST /__dev/api/reload reaches
|
|
902
|
+
# every browser.
|
|
895
903
|
dev_reload = ws_route.path == "/__dev_reload"
|
|
904
|
+
manager = dev_reload ? Tina4::DevReload.manager : @websocket_engine
|
|
896
905
|
|
|
897
906
|
ws.on(:open) do |connection|
|
|
898
907
|
connection.params = ws_params
|
|
899
|
-
Tina4::DevReload.add(connection) if dev_reload
|
|
900
908
|
handler.call(connection, :open, nil)
|
|
901
909
|
end
|
|
902
910
|
|
|
@@ -905,7 +913,6 @@ module Tina4
|
|
|
905
913
|
end
|
|
906
914
|
|
|
907
915
|
ws.on(:close) do |connection|
|
|
908
|
-
Tina4::DevReload.remove(connection) if dev_reload
|
|
909
916
|
handler.call(connection, :close, nil)
|
|
910
917
|
end
|
|
911
918
|
|
|
@@ -913,7 +920,7 @@ module Tina4
|
|
|
913
920
|
Tina4::Log.error("WebSocket error on #{ws_route.path}: #{error.message}")
|
|
914
921
|
end
|
|
915
922
|
|
|
916
|
-
ws.handle_upgrade(env, socket)
|
|
923
|
+
ws.handle_upgrade(env, socket, manager: manager)
|
|
917
924
|
|
|
918
925
|
# Return async response (-1 signals Rack the response is handled via hijack)
|
|
919
926
|
[-1, {}, []]
|
data/lib/tina4/response.rb
CHANGED
|
@@ -347,18 +347,38 @@ module Tina4
|
|
|
347
347
|
gen = @_stream_generator
|
|
348
348
|
blk = @_stream_block
|
|
349
349
|
body = Enumerator.new do |yielder|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
350
|
+
# SSE hardening: a streaming source that raises mid-stream (a
|
|
351
|
+
# generator/block error, or the client disconnecting and the server
|
|
352
|
+
# tearing the body down) must NEVER crash the worker. We catch the
|
|
353
|
+
# error, log it, and end the stream cleanly — the chunks emitted
|
|
354
|
+
# before the failure are still delivered.
|
|
355
|
+
#
|
|
356
|
+
# A client disconnect surfaces in a hijack/Puma streaming body as a
|
|
357
|
+
# write-side IOError/Errno on the socket; that is propagated up as a
|
|
358
|
+
# normal stop and re-raised so Rack/Puma can close the connection,
|
|
359
|
+
# while a *source* error is swallowed after logging.
|
|
360
|
+
begin
|
|
361
|
+
if gen
|
|
362
|
+
if gen.respond_to?(:each)
|
|
363
|
+
# Enumerator / array / any Enumerable of string chunks
|
|
364
|
+
gen.each { |chunk| yielder << chunk }
|
|
365
|
+
elsif gen.respond_to?(:call)
|
|
366
|
+
# Callable that receives the yielder, like the block form
|
|
367
|
+
gen.call(yielder)
|
|
368
|
+
else
|
|
369
|
+
yielder << gen.to_s
|
|
370
|
+
end
|
|
371
|
+
elsif blk
|
|
372
|
+
blk.call(yielder)
|
|
359
373
|
end
|
|
360
|
-
|
|
361
|
-
|
|
374
|
+
rescue IOError, Errno::EPIPE, Errno::ECONNRESET => e
|
|
375
|
+
# Client disconnected mid-stream — stop cleanly, do not crash, and
|
|
376
|
+
# do not log loudly (a normal browser closing an SSE stream).
|
|
377
|
+
Tina4::Log.debug("SSE/stream client disconnected: #{e.class}: #{e.message}") if defined?(Tina4::Log)
|
|
378
|
+
rescue StandardError => e
|
|
379
|
+
# The source (generator/block) itself raised mid-stream. Log it and
|
|
380
|
+
# end the stream cleanly rather than crashing the handler/worker.
|
|
381
|
+
Tina4::Log.error("SSE/stream source error: #{e.class}: #{e.message}") if defined?(Tina4::Log)
|
|
362
382
|
end
|
|
363
383
|
end
|
|
364
384
|
return [@status_code, final_headers, body]
|