tina4ruby 3.13.93 → 3.13.96

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +883 -0
  3. data/README.md +1 -1
  4. data/lib/tina4/auth.rb +166 -87
  5. data/lib/tina4/auto_crud.rb +29 -32
  6. data/lib/tina4/cache_backends/base_backend.rb +19 -0
  7. data/lib/tina4/cache_backends/database_backend.rb +29 -0
  8. data/lib/tina4/cache_backends/memcached_backend.rb +124 -13
  9. data/lib/tina4/cache_backends/memory_backend.rb +15 -0
  10. data/lib/tina4/cache_backends/redis_backend.rb +173 -52
  11. data/lib/tina4/cache_backends.rb +10 -1
  12. data/lib/tina4/cli.rb +35 -43
  13. data/lib/tina4/cors.rb +186 -30
  14. data/lib/tina4/database/sqlite3_adapter.rb +4 -1
  15. data/lib/tina4/database.rb +458 -48
  16. data/lib/tina4/database_adapter.rb +178 -0
  17. data/lib/tina4/database_result.rb +63 -17
  18. data/lib/tina4/database_url.rb +363 -0
  19. data/lib/tina4/dev.rb +0 -1
  20. data/lib/tina4/dev_admin.rb +118 -20
  21. data/lib/tina4/dev_mailbox.rb +5 -1
  22. data/lib/tina4/dispatch_pipeline.rb +605 -0
  23. data/lib/tina4/docstore.rb +274 -60
  24. data/lib/tina4/drivers/firebird_driver.rb +118 -4
  25. data/lib/tina4/drivers/mongodb_driver.rb +19 -4
  26. data/lib/tina4/drivers/mssql_driver.rb +73 -10
  27. data/lib/tina4/drivers/mysql_driver.rb +71 -4
  28. data/lib/tina4/drivers/odbc_driver.rb +40 -4
  29. data/lib/tina4/drivers/postgres_driver.rb +97 -10
  30. data/lib/tina4/drivers/sqlite_driver.rb +25 -3
  31. data/lib/tina4/env.rb +176 -34
  32. data/lib/tina4/field_types.rb +12 -0
  33. data/lib/tina4/frond.rb +102 -10
  34. data/lib/tina4/health.rb +30 -14
  35. data/lib/tina4/job.rb +15 -5
  36. data/lib/tina4/log.rb +236 -32
  37. data/lib/tina4/mcp.rb +11 -5
  38. data/lib/tina4/messenger.rb +317 -82
  39. data/lib/tina4/metrics.rb +179 -891
  40. data/lib/tina4/middleware.rb +191 -56
  41. data/lib/tina4/migration.rb +17 -1
  42. data/lib/tina4/orm.rb +114 -17
  43. data/lib/tina4/public/css/tina4.min.css +1 -1
  44. data/lib/tina4/queue.rb +154 -9
  45. data/lib/tina4/queue_backends/kafka_backend.rb +191 -2
  46. data/lib/tina4/queue_backends/lite_backend.rb +121 -25
  47. data/lib/tina4/queue_backends/mongo_backend.rb +146 -10
  48. data/lib/tina4/queue_backends/rabbitmq_backend.rb +194 -1
  49. data/lib/tina4/rack_app.rb +94 -316
  50. data/lib/tina4/request.rb +48 -8
  51. data/lib/tina4/response.rb +42 -1
  52. data/lib/tina4/response_cache.rb +142 -24
  53. data/lib/tina4/router.rb +141 -12
  54. data/lib/tina4/session.rb +243 -29
  55. data/lib/tina4/session_handlers/database_handler.rb +185 -20
  56. data/lib/tina4/session_handlers/file_handler.rb +113 -21
  57. data/lib/tina4/session_handlers/memcached_handler.rb +183 -0
  58. data/lib/tina4/session_handlers/mongo_handler.rb +232 -15
  59. data/lib/tina4/session_handlers/mongo_wire_client.rb +300 -0
  60. data/lib/tina4/session_handlers/redis_handler.rb +20 -6
  61. data/lib/tina4/session_handlers/valkey_handler.rb +18 -4
  62. data/lib/tina4/shutdown.rb +180 -30
  63. data/lib/tina4/sql_translator.rb +110 -0
  64. data/lib/tina4/swagger.rb +50 -18
  65. data/lib/tina4/version.rb +1 -1
  66. data/lib/tina4/webserver.rb +28 -6
  67. data/lib/tina4.rb +301 -38
  68. metadata +35 -17
  69. data/lib/tina4/scss_compiler.rb +0 -349
data/lib/tina4.rb CHANGED
@@ -9,7 +9,10 @@ require_relative "tina4/env"
9
9
  require_relative "tina4/router"
10
10
  require_relative "tina4/request"
11
11
  require_relative "tina4/response"
12
+ require_relative "tina4/dispatch_pipeline"
12
13
  require_relative "tina4/rack_app"
14
+ require_relative "tina4/database_adapter"
15
+ require_relative "tina4/database_url"
13
16
  require_relative "tina4/database"
14
17
  require_relative "tina4/database_result"
15
18
  require_relative "tina4/field_types"
@@ -53,6 +56,51 @@ require_relative "tina4/mcp"
53
56
  require_relative "tina4/realtime"
54
57
 
55
58
  module Tina4
59
+ # Bind address, with the framework name winning over the bare one.
60
+ #
61
+ # TINA4_HOST > HOST (deprecated) > default. See resolve_bind_port.
62
+ def self.resolve_bind_host(default = "0.0.0.0")
63
+ tina4_host = ENV["TINA4_HOST"]
64
+ return tina4_host if tina4_host && !tina4_host.empty?
65
+
66
+ legacy = ENV["HOST"]
67
+ if legacy && !legacy.empty?
68
+ warn_deprecated_bind_var("HOST", "TINA4_HOST", legacy)
69
+ return legacy
70
+ end
71
+ default
72
+ end
73
+
74
+ # Bind port, with the framework name winning over the bare one.
75
+ #
76
+ # TINA4_PORT > PORT (deprecated) > default. Bare PORT is a name anything can
77
+ # set - a shared CI runner, a PaaS, another tool - and it should never
78
+ # outrank the framework's own variable. It is still honoured so no deployment
79
+ # breaks; the warning is what drives the move. Removal is 3.14.
80
+ def self.resolve_bind_port(default = 7147)
81
+ tina4_port = ENV["TINA4_PORT"]
82
+ return tina4_port.to_i if tina4_port && tina4_port.match?(/\A\d+\z/)
83
+
84
+ legacy = ENV["PORT"]
85
+ if legacy && legacy.match?(/\A\d+\z/)
86
+ warn_deprecated_bind_var("PORT", "TINA4_PORT", legacy)
87
+ return legacy.to_i
88
+ end
89
+ default
90
+ end
91
+
92
+ # Warn ONCE per variable. Repeated on every call it is noise people filter.
93
+ def self.warn_deprecated_bind_var(old_name, new_name, value)
94
+ @warned_bind_vars ||= {}
95
+ return if @warned_bind_vars[old_name]
96
+
97
+ @warned_bind_vars[old_name] = true
98
+ Tina4::Log.warning(
99
+ "#{old_name} is deprecated and will be removed in 3.14 - use " \
100
+ "#{new_name} instead (using #{value} from #{old_name})"
101
+ )
102
+ end
103
+
56
104
  # ── Lazy-loaded: database drivers ─────────────────────────────────────
57
105
  module Drivers
58
106
  autoload :SchemaSplit, File.expand_path("tina4/drivers/schema_split", __dir__)
@@ -71,6 +119,7 @@ module Tina4
71
119
  autoload :RedisHandler, File.expand_path("tina4/session_handlers/redis_handler", __dir__)
72
120
  autoload :MongoHandler, File.expand_path("tina4/session_handlers/mongo_handler", __dir__)
73
121
  autoload :ValkeyHandler, File.expand_path("tina4/session_handlers/valkey_handler", __dir__)
122
+ autoload :MemcachedHandler, File.expand_path("tina4/session_handlers/memcached_handler", __dir__)
74
123
  autoload :DatabaseHandler, File.expand_path("tina4/session_handlers/database_handler", __dir__)
75
124
  end
76
125
 
@@ -127,10 +176,148 @@ module Tina4
127
176
  autoload :Messenger, File.expand_path("tina4/messenger", __dir__)
128
177
  autoload :MessengerError, File.expand_path("tina4/messenger", __dir__)
129
178
  autoload :MessengerConnectionError, File.expand_path("tina4/messenger", __dir__)
130
- autoload :DevMessengerProxy, File.expand_path("tina4/messenger", __dir__)
179
+
180
+ # Factory for a Messenger configured from the environment.
181
+ #
182
+ # Defined HERE, eagerly, and not in messenger.rb: `autoload` only fires on a
183
+ # CONSTANT reference, and a module function is not a constant. A cold
184
+ # `require "tina4"; Tina4.create_messenger` therefore raised NoMethodError until
185
+ # something else happened to touch Tina4::Messenger first -- the documented entry
186
+ # point was unreachable on a fresh process. Naming the constant below is what
187
+ # triggers the autoload.
188
+ def self.create_messenger(**options)
189
+ Tina4::Messenger.create_messenger(**options)
190
+ end
191
+
192
+ # ── dotenv surface, at the top level ──────────────────────────────
193
+ #
194
+ # Python, PHP and Node all expose these as plain module/namespace functions.
195
+ # Ruby had them only as Tina4::Env.*, so the obvious cross-framework call
196
+ # (Tina4.load_env) raised NoMethodError - a parity defect by the audit's rule
197
+ # that a concept carries one name in all four, differing only in casing.
198
+ #
199
+ # These are thin delegators, defined EAGERLY for the same reason
200
+ # create_messenger above is: `autoload` fires on a CONSTANT reference, and a
201
+ # module function is not a constant, so a cold `require "tina4"` would
202
+ # otherwise leave the documented entry point unreachable until something else
203
+ # happened to touch Tina4::Env first. Naming Tina4::Env inside each body is
204
+ # what triggers the autoload.
205
+ #
206
+ # Tina4::Env.* stays as the implementation namespace (PHP keeps Env:: the same
207
+ # way). Neither is an alias of the other - one is the surface, one is the home.
208
+
209
+ # Load .env.local then .env from a ROOT DIRECTORY (canonical in all four).
210
+ def self.load_env(root = nil)
211
+ root.nil? ? Tina4::Env.load_env : Tina4::Env.load_env(root)
212
+ end
213
+
214
+ def self.get_env(key, default = nil)
215
+ Tina4::Env.get_env(key, default)
216
+ end
217
+
218
+ # Raises KeyError naming EVERY missing variable; returns the requested map.
219
+ def self.require_env(*keys)
220
+ Tina4::Env.require_env(*keys)
221
+ end
222
+
223
+ def self.has_env?(key)
224
+ Tina4::Env.has_env?(key)
225
+ end
226
+
227
+ def self.all_env
228
+ Tina4::Env.all_env
229
+ end
230
+
231
+ def self.reset_env
232
+ Tina4::Env.reset_env
233
+ end
234
+
235
+ def self.truthy?(value)
236
+ Tina4::Env.is_truthy(value)
237
+ end
238
+
239
+ # Parsed TINA4_TRUSTED_PROXIES, cached on the raw env string so a change is
240
+ # picked up but the parse does not run per request. [raw_value, networks]
241
+ @trusted_proxy_cache = [nil, [].freeze]
242
+
243
+ # The configured trusted-proxy networks, from TINA4_TRUSTED_PROXIES.
244
+ #
245
+ # Comma-separated exact addresses and/or CIDR ranges, IPv4 and IPv6:
246
+ # "10.0.0.0/8, 192.168.1.5, ::1, fd00::/8". Empty or unset means trust
247
+ # NOTHING, which is the secure default: X-Forwarded-For is then ignored
248
+ # entirely and the raw socket peer identifies the client. See ADR-0019.
249
+ def self.trusted_proxy_networks
250
+ raw = ENV["TINA4_TRUSTED_PROXIES"].to_s
251
+ cached_raw, cached_networks = @trusted_proxy_cache
252
+ return cached_networks if raw == cached_raw
253
+
254
+ networks = []
255
+ raw.split(",").each do |entry|
256
+ entry = normalise_ip(entry)
257
+ next if entry.empty?
258
+
259
+ begin
260
+ networks << IPAddr.new(entry)
261
+ rescue IPAddr::Error, ArgumentError
262
+ # Loud, and exactly once per distinct config value (the cache below
263
+ # means this parse runs once). A silently-skipped entry would leave a
264
+ # real proxy untrusted, which looks like the app over-limiting every
265
+ # client - a very expensive typo to debug.
266
+ Tina4::Log.error(
267
+ "TINA4_TRUSTED_PROXIES: ignoring invalid entry '#{entry}' - " \
268
+ "expected an IP address or CIDR range, e.g. 10.0.0.0/8 or 192.168.1.5"
269
+ )
270
+ end
271
+ end
272
+ @trusted_proxy_cache = [raw, networks.freeze]
273
+ @trusted_proxy_cache[1]
274
+ end
275
+
276
+ # Is this address a configured trusted proxy?
277
+ def self.trusted_proxy?(address)
278
+ networks = trusted_proxy_networks
279
+ return false if networks.empty?
280
+
281
+ address = normalise_ip(address)
282
+ return false if address.empty?
283
+
284
+ begin
285
+ parsed = IPAddr.new(address)
286
+ rescue IPAddr::Error, ArgumentError
287
+ return false
288
+ end
289
+ # A peer arriving as ::ffff:10.0.0.1 must match an allow-list entry of
290
+ # 10.0.0.0/8 - dual-stack listeners hand out the mapped form routinely.
291
+ parsed = parsed.native if parsed.ipv4_mapped?
292
+ networks.any? { |network| network.include?(parsed) }
293
+ end
294
+
295
+ # Strip the decorations a peer address can arrive with: the "[::1]" bracket
296
+ # form and an IPv6 zone id ("fe80::1%eth0").
297
+ def self.normalise_ip(value)
298
+ value = value.to_s.strip
299
+ value = value[1...value.index("]")].to_s if value.start_with?("[") && value.include?("]")
300
+ value = value.split("%", 2).first.to_s if value.include?("%")
301
+ value
302
+ end
303
+
304
+ def self.env_bool(name, default: false)
305
+ Tina4::Env.bool(name, default: default)
306
+ end
307
+
308
+ def self.env_int(name, default: 0)
309
+ Tina4::Env.int(name, default: default)
310
+ end
311
+
312
+ def self.env_float(name, default: 0.0)
313
+ Tina4::Env.float(name, default: default)
314
+ end
315
+
316
+ def self.env_str(name, default: "")
317
+ Tina4::Env.str(name, default: default)
318
+ end
131
319
  autoload :IMAP_CONNECTION_ERRORS, File.expand_path("tina4/messenger", __dir__)
132
320
  autoload :DocStore, File.expand_path("tina4/docstore", __dir__)
133
- autoload :ScssCompiler, File.expand_path("tina4/scss_compiler", __dir__)
134
321
  autoload :FakeData, File.expand_path("tina4/seeder", __dir__)
135
322
  autoload :WSDL, File.expand_path("tina4/wsdl", __dir__)
136
323
  # Validator is a counted framework feature and belongs on the main
@@ -245,8 +432,19 @@ module Tina4
245
432
  # (.env.local loads first, both first-wins, so a real env var always wins).
246
433
  Tina4::Env.load_env(root_dir)
247
434
 
248
- # Setup debug logging
249
- Tina4::Log.configure(root_dir)
435
+ # Setup debug logging.
436
+ #
437
+ # No argument here, deliberately (ADR-0041). This used to pass `root_dir`,
438
+ # and since Ruby correctly lets an explicit argument beat the environment,
439
+ # that had two measured consequences in every booted Ruby app:
440
+ # TINA4_LOG_DIR was ENTIRELY DEAD -- the documented variable could not
441
+ # move the logs anywhere -- and because root_dir is an existing directory
442
+ # it became the log directory itself, so tina4.log and error.log were
443
+ # written into the PROJECT ROOT rather than the documented logs/.
444
+ # root_dir is the framework's default, not a user instruction, so it
445
+ # belongs below the environment. Resolution is now TINA4_LOG_DIR, then
446
+ # logs/, matching Python and Node.
447
+ Tina4::Log.configure
250
448
  Tina4::Log.info("Tina4 Ruby v#{VERSION} initializing...")
251
449
 
252
450
  # Fail-safe dev secret: in dev (and NOT CI/prod) mint a per-machine
@@ -267,6 +465,13 @@ module Tina4
267
465
  # Connect database if configured
268
466
  setup_database
269
467
 
468
+ # Framework routes FIRST, then the app's. Routes resolve in registration
469
+ # order, so anything registered later cannot shadow these - and an app
470
+ # with an ordinary catch-all (`any("/{slug}")`) would otherwise swallow
471
+ # /__health, which is exactly what a container health check probes.
472
+ # Python registers its built-ins first for the same reason.
473
+ register_builtin_routes!
474
+
270
475
  # Auto-discover routes
271
476
  auto_discover(root_dir)
272
477
 
@@ -333,16 +538,30 @@ module Tina4
333
538
 
334
539
  initialize!(root_dir) unless @root_dir
335
540
 
336
- # Built-in routes. These used to be registered ONLY by the CLI's
337
- # cmd_start, so an app booted the documented way -- `Tina4.run!` from
338
- # app.rb, which is exactly what `tina4 init ruby` scaffolds -- served 404
339
- # on /health while the identical code under `tina4ruby serve` served 200.
340
- # A container health check pointed at /health therefore failed against a
341
- # perfectly healthy app.
541
+ # Built-in routes now register inside initialize!, BEFORE route discovery,
542
+ # so they cannot be shadowed by an app catch-all. Kept here as a safety net
543
+ # for a caller that reaches run! without initialize! having run (the guard
544
+ # above skips it when @root_dir is already set). register! is idempotent
545
+ # and Router.add replaces in place, so a second call is a no-op.
546
+ #
547
+ # They used to be registered ONLY by the CLI's cmd_start, so an app booted
548
+ # the documented way -- `Tina4.run!` from app.rb, which is what
549
+ # `tina4 init ruby` scaffolds -- served 404 on /health while the identical
550
+ # code under `tina4ruby serve` served 200.
342
551
  register_builtin_routes!
343
552
 
344
- host = ENV.fetch("HOST", ENV.fetch("TINA4_HOST", "0.0.0.0"))
345
- port = ENV.fetch("PORT", ENV.fetch("TINA4_PORT", "7147")).to_i
553
+ # TINA4_HOST/TINA4_PORT BEFORE the bare names, not after.
554
+ #
555
+ # This had both the wrong way round: ENV.fetch("PORT", ...) returns PORT
556
+ # whenever it is set, so a stray OS-level HOST or PORT outranked the
557
+ # framework's own variable - the one the CLI documents and prefers, and
558
+ # the one Tina4::WebServer a few files away already reads first. Two code
559
+ # paths in one framework disagreeing about the same variable.
560
+ #
561
+ # Bare HOST/PORT stay honoured so nothing breaks, and warn so the
562
+ # migration happens. Removal is 3.14.
563
+ host = Tina4.resolve_bind_host
564
+ port = Tina4.resolve_bind_port(7147)
346
565
 
347
566
  actual_port = find_available_port(port)
348
567
  if actual_port != port
@@ -357,32 +576,10 @@ module Tina4
357
576
  is_debug = Tina4::Env.is_truthy(ENV["TINA4_DEBUG"])
358
577
 
359
578
  # Try Puma first (production-grade), fall back to WEBrick
360
- if !is_debug
361
- begin
362
- require "puma"
363
- require "puma/configuration"
364
- require "puma/launcher"
365
-
366
- config = Puma::Configuration.new do |user_config|
367
- user_config.bind "tcp://#{host}:#{port}"
368
- user_config.app app
369
- user_config.threads 0, 16
370
- user_config.workers 0
371
- user_config.environment "production"
372
- user_config.log_requests false
373
- user_config.quiet
374
- end
375
-
376
- Tina4::Log.info("Production server: puma")
377
- Tina4::Shutdown.setup
378
-
379
- open_browser(url)
380
- launcher = Puma::Launcher.new(config)
381
- launcher.run
382
- return
383
- rescue LoadError
384
- # Puma not installed, fall through to WEBrick
385
- end
579
+ if !is_debug && !builtin_webserver_pinned? && puma_available?
580
+ open_browser(url)
581
+ start_puma_server(app, host: host, port: port)
582
+ return
386
583
  end
387
584
 
388
585
  Tina4::Log.info("Development server: WEBrick")
@@ -391,6 +588,72 @@ module Tina4
391
588
  server.start
392
589
  end
393
590
 
591
+ # TINA4_DEFAULT_WEBSERVER=TRUE pins Tina4's BUILT-IN server (WEBrick) even
592
+ # in production, where Puma would otherwise be chosen. Unset/FALSE keeps
593
+ # today's behaviour, so this is non-breaking.
594
+ #
595
+ # This is NOT the remedy for a production shutdown problem - an operator
596
+ # must never have to give up Puma to get their database connections closed.
597
+ # It exists so CI can pin the built-in server deterministically without
598
+ # having to switch TINA4_DEBUG on to get there.
599
+ def builtin_webserver_pinned?
600
+ Tina4::Env.is_truthy(ENV["TINA4_DEFAULT_WEBSERVER"])
601
+ end
602
+
603
+ # Is Puma loadable? Separate from start_puma_server so the caller can fall
604
+ # back to WEBrick BEFORE any side effect (opening a browser) happens.
605
+ def puma_available?
606
+ require "puma"
607
+ require "puma/configuration"
608
+ require "puma/launcher"
609
+ true
610
+ rescue LoadError
611
+ false
612
+ end
613
+
614
+ # Boot the production server (Puma) with Tina4's shutdown contract wired in.
615
+ #
616
+ # Feature 9's OUTCOMES are the framework's contract whichever server owns
617
+ # the socket; the MECHANISM differs per server. Puma already stops
618
+ # accepting and drains in-flight requests properly, so that is CONFIGURED,
619
+ # not reimplemented:
620
+ #
621
+ # * TINA4_SHUTDOWN_TIMEOUT -> Puma's force_shutdown_after. Puma's default
622
+ # is :forever, so before this the documented env var meant NOTHING on
623
+ # the path operators actually run.
624
+ # * raise_exception_on_sigterm false -> Puma's default is true, which
625
+ # re-raises SignalException after the graceful stop and terminates the
626
+ # process BY the signal. The contract is a clean exit 0.
627
+ #
628
+ # What Puma cannot know about is ours, and runs in the ensure: live
629
+ # WebSocket peers get RFC 6455 1001, background tasks stop, and every
630
+ # database connection is closed. Nothing but Tina4 knows those connections
631
+ # exist, so without this they leaked on every production shutdown.
632
+ def start_puma_server(app, host:, port:)
633
+ # Puma owns INT/TERM here, so Tina4 installs no handlers of its own.
634
+ Tina4::Shutdown.setup(trap_signals: false)
635
+ shutdown_timeout = Tina4::Shutdown.timeout
636
+
637
+ config = Puma::Configuration.new do |user_config|
638
+ user_config.bind "tcp://#{host}:#{port}"
639
+ user_config.app app
640
+ user_config.threads 0, 16
641
+ user_config.workers 0
642
+ user_config.environment "production"
643
+ user_config.log_requests false
644
+ user_config.quiet
645
+ user_config.force_shutdown_after shutdown_timeout
646
+ user_config.raise_exception_on_sigterm false
647
+ end
648
+
649
+ Tina4::Log.info("Production server: puma (TINA4_SHUTDOWN_TIMEOUT=#{shutdown_timeout}s)")
650
+ begin
651
+ Puma::Launcher.new(config).run
652
+ ensure
653
+ Tina4::Shutdown.release_resources
654
+ end
655
+ end
656
+
394
657
  # DSL methods for route registration
395
658
  # GET is public by default (matching tina4_python behavior)
396
659
  # POST/PUT/PATCH/DELETE are secured by default — use auth: false to make public
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tina4ruby
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.13.93
4
+ version: 3.13.96
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tina4 Team
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-07-27 00:00:00.000000000 Z
11
+ date: 2026-08-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -52,20 +52,6 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '6.0'
55
- - !ruby/object:Gem::Dependency
56
- name: jwt
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '2.7'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '2.7'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: net-smtp
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -206,6 +192,20 @@ dependencies:
206
192
  - - "~>"
207
193
  - !ruby/object:Gem::Version
208
194
  version: '2.19'
195
+ - !ruby/object:Gem::Dependency
196
+ name: bigdecimal
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: '4.0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: '4.0'
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: pg
211
211
  requirement: !ruby/object:Gem::Requirement
@@ -290,6 +290,20 @@ dependencies:
290
290
  - - "~>"
291
291
  - !ruby/object:Gem::Version
292
292
  version: '1.50'
293
+ - !ruby/object:Gem::Dependency
294
+ name: openapi3_parser
295
+ requirement: !ruby/object:Gem::Requirement
296
+ requirements:
297
+ - - "~>"
298
+ - !ruby/object:Gem::Version
299
+ version: '0.10'
300
+ type: :development
301
+ prerelease: false
302
+ version_requirements: !ruby/object:Gem::Requirement
303
+ requirements:
304
+ - - "~>"
305
+ - !ruby/object:Gem::Version
306
+ version: '0.10'
293
307
  description: 'TINA4: The Intelligent Native Application 4ramework for Ruby — Simple.
294
308
  Fast. Human. Built for AI. Built for you. 54 built-in features, minimal dependencies
295
309
  (Rack-based; the runtime gems are production-deployment essentials, not framework
@@ -330,11 +344,14 @@ files:
330
344
  - lib/tina4/crud.rb
331
345
  - lib/tina4/database.rb
332
346
  - lib/tina4/database/sqlite3_adapter.rb
347
+ - lib/tina4/database_adapter.rb
333
348
  - lib/tina4/database_result.rb
349
+ - lib/tina4/database_url.rb
334
350
  - lib/tina4/debug.rb
335
351
  - lib/tina4/dev.rb
336
352
  - lib/tina4/dev_admin.rb
337
353
  - lib/tina4/dev_mailbox.rb
354
+ - lib/tina4/dispatch_pipeline.rb
338
355
  - lib/tina4/docs.rb
339
356
  - lib/tina4/docstore.rb
340
357
  - lib/tina4/drivers/firebird_driver.rb
@@ -434,14 +451,15 @@ files:
434
451
  - lib/tina4/scss/tina4css/base.scss
435
452
  - lib/tina4/scss/tina4css/colors.scss
436
453
  - lib/tina4/scss/tina4css/tina4.scss
437
- - lib/tina4/scss_compiler.rb
438
454
  - lib/tina4/seeder.rb
439
455
  - lib/tina4/service.rb
440
456
  - lib/tina4/service_runner.rb
441
457
  - lib/tina4/session.rb
442
458
  - lib/tina4/session_handlers/database_handler.rb
443
459
  - lib/tina4/session_handlers/file_handler.rb
460
+ - lib/tina4/session_handlers/memcached_handler.rb
444
461
  - lib/tina4/session_handlers/mongo_handler.rb
462
+ - lib/tina4/session_handlers/mongo_wire_client.rb
445
463
  - lib/tina4/session_handlers/redis_handler.rb
446
464
  - lib/tina4/session_handlers/resp_client.rb
447
465
  - lib/tina4/session_handlers/valkey_handler.rb