funicular 0.3.0 → 0.5.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.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +486 -1
  3. data/demo/local_notes.html +207 -0
  4. data/demo/test_chartjs.html +9 -9
  5. data/demo/test_component.html +8 -8
  6. data/demo/test_error_boundary.html +44 -41
  7. data/demo/test_router.html +48 -48
  8. data/demo/tic-tac-toe.html +25 -25
  9. data/docs/architecture.md +227 -12
  10. data/docs/local_database.md +1035 -0
  11. data/lib/funicular/assets/funicular.rb +14 -0
  12. data/lib/funicular/configuration.rb +65 -0
  13. data/lib/funicular/epoch_header.rb +69 -0
  14. data/lib/funicular/epoch_stamping.rb +66 -0
  15. data/lib/funicular/helpers/picoruby_helper.rb +96 -1
  16. data/lib/funicular/railtie.rb +30 -0
  17. data/lib/funicular/schema.rb +45 -12
  18. data/lib/funicular/session_epoch.rb +110 -0
  19. data/lib/funicular/ssr/runtime.rb +58 -12
  20. data/lib/funicular/ssr.rb +25 -0
  21. data/lib/funicular/testing/node_runner.mjs +19 -0
  22. data/lib/funicular/testing.rb +47 -0
  23. data/lib/funicular/vendor/mrbc/VERSION +1 -1
  24. data/lib/funicular/vendor/mrbc/mrbc.js +655 -574
  25. data/lib/funicular/vendor/mrbc/mrbc.wasm +0 -0
  26. data/lib/funicular/vendor/picoruby/VERSION +1 -1
  27. data/lib/funicular/vendor/picoruby/debug/picoruby.js +800 -530
  28. data/lib/funicular/vendor/picoruby/debug/picoruby.wasm +0 -0
  29. data/lib/funicular/vendor/picoruby/dist/picoruby.js +2 -2
  30. data/lib/funicular/vendor/picoruby/dist/picoruby.wasm +0 -0
  31. data/lib/funicular/vendor/picoruby-test-node/VERSION +1 -1
  32. data/lib/funicular/vendor/picoruby-test-node/picoruby.js +2 -6909
  33. data/lib/funicular/vendor/picoruby-test-node/picoruby.wasm +0 -0
  34. data/lib/funicular/version.rb +1 -1
  35. data/lib/funicular.rb +1 -0
  36. data/lib/generators/funicular/chat/templates/funicular_chat_component.rb.tt +37 -38
  37. data/lib/tasks/funicular.rake +10 -2
  38. data/minitest/callback_error_visibility_test.rb +48 -0
  39. data/minitest/configuration_test.rb +78 -0
  40. data/minitest/dsl_test.rb +264 -0
  41. data/minitest/epoch_header_test.rb +149 -0
  42. data/minitest/epoch_stamping_test.rb +225 -0
  43. data/minitest/fixtures/funicular_app/components/greeting_component.rb +5 -5
  44. data/minitest/fixtures/funicular_app/components/probe_component.rb +15 -0
  45. data/minitest/form_for_test.rb +2 -2
  46. data/minitest/hydration_test.rb +2 -2
  47. data/minitest/navigation_guard_test.rb +65 -0
  48. data/minitest/picoruby_helper_test.rb +236 -0
  49. data/minitest/schema_test.rb +47 -0
  50. data/minitest/session_epoch_test.rb +122 -0
  51. data/minitest/sig_tags_test.rb +30 -0
  52. data/minitest/ssr_database_test.rb +78 -0
  53. data/minitest/ssr_reload_test.rb +106 -0
  54. data/minitest/ssr_test.rb +41 -0
  55. data/minitest/testing_ensure_compiled_test.rb +52 -0
  56. data/minitest/validations_test.rb +35 -5
  57. data/minitest/view_context_test.rb +15 -15
  58. data/mrbgem.rake +2 -0
  59. data/mrblib/0_tags.rb +62 -0
  60. data/mrblib/cable.rb +1 -1
  61. data/mrblib/component.rb +226 -24
  62. data/mrblib/db.rb +3116 -0
  63. data/mrblib/error_boundary.rb +25 -19
  64. data/mrblib/file_upload.rb +17 -7
  65. data/mrblib/form_builder.rb +10 -10
  66. data/mrblib/funicular.rb +136 -17
  67. data/mrblib/http.rb +84 -107
  68. data/mrblib/model.rb +1178 -23
  69. data/mrblib/relation.rb +342 -0
  70. data/mrblib/router.rb +45 -4
  71. data/mrblib/styles.rb +122 -12
  72. data/mrblib/view_context.rb +3 -32
  73. data/sig/component.rbs +25 -4
  74. data/sig/db.rbs +328 -0
  75. data/sig/error_boundary.rbs +4 -4
  76. data/sig/funicular.rbs +5 -0
  77. data/sig/http.rbs +8 -21
  78. data/sig/model.rbs +101 -7
  79. data/sig/relation.rbs +44 -0
  80. data/sig/router.rbs +1 -0
  81. data/sig/styles.rbs +19 -5
  82. data/sig/tags.rbs +54 -0
  83. data/sig/view_context.rbs +47 -34
  84. metadata +23 -2
  85. data/lib/funicular/vendor/picoruby-test-node/picoruby.wasm.map +0 -1
data/sig/component.rbs CHANGED
@@ -17,6 +17,8 @@ module Funicular
17
17
  def error: (Symbol key) -> untyped
18
18
  end
19
19
 
20
+ include Tags
21
+
20
22
  attr_accessor props: Hash[Symbol, untyped]
21
23
  attr_accessor vdom: VDOM::VNode | VDOM::Text | nil
22
24
  attr_accessor dom_element: JS::Element
@@ -29,8 +31,13 @@ module Funicular
29
31
  type suspense_definition = { loader: untyped, on_resolve: untyped, min_delay: Integer? }
30
32
 
31
33
  def initialize: (?Hash[Symbol, untyped] props) -> void
34
+ def self.allow_dsl_override: (*Symbol names) -> void
35
+ def self.dsl_overrides: () -> Array[Symbol]
36
+ def self.method_added: (Symbol name) -> void
37
+ def self.validate_dsl_conflicts!: () -> void
32
38
  def self.styles: () { (StyleBuilder) -> void } -> void
33
39
  def self.styles_definitions: () -> Hash[Symbol, Hash[Symbol, untyped]]
40
+ def self.style_accessor_class: () -> singleton(StyleAccessor)
34
41
  def self.use_suspense: (Symbol name, untyped loader, ?on_resolve: untyped, ?min_delay: Integer) -> void
35
42
  def self.suspense_definitions: () -> Hash[Symbol, suspense_definition]
36
43
 
@@ -40,28 +47,42 @@ module Funicular
40
47
  def initialize_state: () -> Hash[Symbol, untyped]
41
48
  def seed_state: (Hash[untyped, untyped]? state_hash) -> self
42
49
 
50
+ @__watches: Hash[Symbol, Integer]?
51
+ def watch: (Symbol key) { () -> untyped } -> nil
52
+ def evaluate_watch: (Symbol key, untyped block) -> nil
53
+ def cleanup_watches: () -> nil
54
+
43
55
  def load_suspense_data: () -> void
44
56
  def load_single_suspense: (Symbol name, ?suspense_definition? definition) -> void
45
57
  def reload_suspense: (Symbol name) -> void
46
58
  def suspense_loading?: (*Symbol names) -> bool
47
59
  def suspense_error?: (Symbol name) -> bool
48
60
  def suspense_error: (Symbol name) -> untyped
49
- def render_suspense: (ViewContext h, Symbol name, fallback: untyped, ?error: untyped) { (ViewContext, ResourceAccessor) -> untyped } -> untyped
61
+ def render_suspense: (Symbol name, fallback: untyped, ?error: untyped) { (ResourceAccessor) -> untyped } -> untyped
50
62
 
63
+ def navigation_guard: () -> String?
51
64
  def patch: (Hash[Symbol, untyped] new_state) -> void
52
65
  def mount: (JS::Element container) -> void
53
66
  def hydrate: (JS::Element dom_element) -> void
54
67
  def unmount: () -> void
55
- def render: (ViewContext h) -> (VDOM::VNode | String | Integer | Float | Array[untyped] | nil)
68
+ def render: () -> (VDOM::VNode | String | Integer | Float | Array[untyped] | nil)
56
69
  def build_vdom: () -> (VDOM::VNode | VDOM::Text | nil)
57
70
 
58
71
  def bind_events: (JS::Element dom_element, VDOM::VNode | VDOM::Text | nil vnode) -> void
72
+ def report_handler_error: (String event_name, String handler, StandardError error) -> void
59
73
  def collect_refs: (JS::Element dom_element, VDOM::VNode | VDOM::Text | nil vnode, ?Hash[Symbol, JS::Element] refs_map) -> Hash[Symbol, JS::Element]
60
74
  def normalize_vnode_for_view: (untyped value) -> (VDOM::Element | VDOM::Text | VDOM::Component | nil)
75
+ def __view__: () -> ViewContext
76
+ def routes: () -> untyped
77
+ def component: (singleton(Component) component_class, ?Hash[Symbol, untyped] props) ?{ () -> untyped } -> VDOM::Component
78
+ def form_for: (Symbol model_key, ?Hash[Symbol, untyped] options) { (FormBuilder) -> void } -> VDOM::Element
79
+ def link_to: (String path, **untyped options) ?{ -> untyped } -> VDOM::Element
80
+ def button_to: (String path, ?method: Symbol, **untyped options) ?{ -> untyped } -> VDOM::Element
81
+ def suspense: (Symbol name, fallback: untyped, ?error: untyped) { (ResourceAccessor) -> untyped } -> untyped
61
82
  def add_child_from_view: (untyped child) -> void
62
83
  def build_form_for: (ViewContext h, Symbol model_key, ?Hash[Symbol, untyped] options) { (FormBuilder) -> void } -> VDOM::Element
63
- def build_link_to: (ViewContext h, String path, **untyped options) { (ViewContext) -> untyped } -> VDOM::Element
64
- def build_button_to: (ViewContext h, String path, ?method: Symbol, **untyped options) { (ViewContext) -> untyped } -> VDOM::Element
84
+ def build_link_to: (ViewContext h, String path, **untyped options) ?{ () -> untyped } -> VDOM::Element
85
+ def build_button_to: (ViewContext h, String path, ?method: Symbol, **untyped options) ?{ () -> untyped } -> VDOM::Element
65
86
 
66
87
  def component_will_mount: () -> void
67
88
  def component_mounted: () -> void
data/sig/db.rbs ADDED
@@ -0,0 +1,328 @@
1
+ module Funicular
2
+ class RecordNotFound < StandardError
3
+ end
4
+
5
+ module DB
6
+ class Error < StandardError
7
+ end
8
+
9
+ class NoTableError < Error
10
+ end
11
+
12
+ class ReadOnlyTabError < Error
13
+ end
14
+
15
+ class ReplicaWriteError < Error
16
+ end
17
+
18
+ class SchemaTooNewError < Error
19
+ end
20
+
21
+ class UnavailableError < Error
22
+ end
23
+
24
+ class ConfigError < Error
25
+ end
26
+
27
+ self.@subscriptions: Hash[Integer, [Symbol, String, Proc]]?
28
+ self.@subscription_serial: Integer?
29
+ self.@deferral_depths: Hash[Symbol, Integer]?
30
+ self.@pending_seen: Hash[Symbol, Hash[String, bool]]?
31
+ self.@pending_orders: Hash[Symbol, Array[[Symbol, String]]]?
32
+ self.@tick_events: Hash[String, bool]?
33
+ self.@tick_order: Array[[Symbol, String]]?
34
+ self.@tick_generation: Integer?
35
+ self.@drain_scheduled: bool?
36
+ self.@tick_scheduler: Proc?
37
+
38
+ def self.subscribe: (Symbol role, untyped table) { (Symbol role, String table) -> void } -> Integer
39
+ def self.unsubscribe: (Integer id) -> nil
40
+ def self.notify_changed: (untyped target, ?untyped table) -> nil
41
+ def self.validate_role: (untyped role) -> Symbol
42
+ private def self.deferral_depth: (Symbol role) -> Integer
43
+ private def self.pending_seen: (Symbol role) -> Hash[String, bool]
44
+ private def self.pending_order: (Symbol role) -> Array[[Symbol, String]]
45
+ def self.__begin_deferral: (Symbol role) -> void
46
+ def self.__commit_deferral: (Symbol role) -> void
47
+ def self.__rollback_deferral: (Symbol role) -> void
48
+ private def self.clear_pending: (Symbol role) -> nil
49
+ private def self.enqueue_delivery: (Symbol role, String table) -> nil
50
+ def self.__set_tick_scheduler: (Proc? scheduler) -> Proc?
51
+ private def self.schedule_drain: () -> nil
52
+ def self.__drain_events: () -> nil
53
+ private def self.deliver_event: (Symbol role, String table, ?Integer? generation) -> void
54
+
55
+ class GuardedStatement
56
+ @stmt: untyped
57
+ @guard: GuardedDatabase
58
+
59
+ def initialize: (untyped stmt, GuardedDatabase guard) -> void
60
+ def bind_params: (*untyped bind_vars) -> untyped
61
+ def readonly?: () -> bool
62
+ def columns: () -> untyped
63
+ def close: () -> untyped
64
+ def closed?: () -> bool
65
+ def execute: (*untyped bind_vars) -> Array[untyped]
66
+ def step: () -> untyped
67
+
68
+ private def check_writable: () -> void
69
+ end
70
+
71
+ class GuardedResultSet
72
+ @rs: untyped
73
+ @stmt: untyped
74
+ @guard: GuardedDatabase
75
+
76
+ def initialize: (untyped rs, untyped stmt, GuardedDatabase guard) -> void
77
+ def next: () -> untyped
78
+ def each: () { (untyped row) -> void } -> GuardedResultSet
79
+ def to_a: () -> Array[untyped]
80
+ def reset: (*untyped bind_params) -> untyped
81
+ def eof?: () -> bool
82
+ def close: () -> untyped
83
+ def closed?: () -> bool
84
+ def columns: () -> untyped
85
+ def types: () -> untyped
86
+
87
+ private def check_writable: () -> void
88
+ end
89
+
90
+ def self.enforce_read_only: (bool read_only, untyped raw_stmt) -> void
91
+
92
+ class GuardedDatabase
93
+ @db: untyped
94
+ @role: Symbol
95
+ @read_only: bool
96
+
97
+ def initialize: (untyped db, ?Symbol role, ?bool read_only) -> void
98
+ def role: () -> Symbol
99
+ def read_only?: () -> bool
100
+ def __become_read_only: () -> void
101
+ def execute: (String sql, ?Array[untyped] bind_vars) ?{ (untyped row) -> void } -> untyped
102
+ def prepare: (String sql) ?{ (GuardedStatement stmt) -> untyped } -> untyped
103
+ def query: (String sql, ?Array[untyped] bind_vars) ?{ (GuardedResultSet result) -> untyped } -> untyped
104
+ def get_first_row: (String sql, ?Array[untyped] bind_vars) -> untyped
105
+ def get_first_value: (String sql, ?Array[untyped] bind_vars) -> untyped
106
+ def transaction: (?Symbol mode) ?{ (GuardedDatabase proxy) -> untyped } -> untyped
107
+ def commit: () -> bool
108
+ def rollback: () -> bool
109
+ private def __execute_control: (String sql) -> nil
110
+ end
111
+
112
+ TRANSACTION_CONTROL_WORDS: Array[String]
113
+ def self.guard_statement_sql: (untyped sql) -> void
114
+ private def self.statement_head: (untyped sql) -> String
115
+ private def self.pragma_name: (String s, Integer pos) -> String
116
+ private def self.skip_sql_blanks: (String s, Integer i) -> Integer
117
+ private def self.read_sql_word: (String s, Integer i) -> [String, Integer]
118
+ private def self.read_sql_token: (String s, Integer i) -> [String, Integer]
119
+ LOCK_SHIM_JS: String
120
+ self.@durability: Symbol?
121
+ self.@writer_lock_name: String?
122
+ self.@lock_shim_installed: bool?
123
+
124
+ def self.durability: () -> Symbol
125
+ def self.__set_durability: (Symbol state) -> Symbol
126
+ def self.elect_writer: (String lock_name) -> Symbol
127
+ private def self.install_lock_shim: () -> void
128
+ def self.release_writer_lock: () -> bool
129
+
130
+ class Config
131
+ @replica_debounce_ms: Integer
132
+ @local_debounce_ms: Integer
133
+ @request_persistent_storage: bool
134
+ @on_persist_error: Proc?
135
+ @on_boot_error: Proc?
136
+ @on_session_change: Proc?
137
+
138
+ attr_accessor replica_debounce_ms: Integer
139
+ attr_accessor local_debounce_ms: Integer
140
+ attr_accessor request_persistent_storage: bool
141
+ attr_accessor on_persist_error: Proc?
142
+ attr_accessor on_boot_error: Proc?
143
+ attr_accessor on_session_change: Proc?
144
+
145
+ def initialize: () -> void
146
+ end
147
+
148
+ self.@config: Config?
149
+ self.@databases: Hash[Symbol, [untyped, Array[untyped]]]?
150
+ self.@snapshot_identity: String?
151
+ self.@snapshot_store: untyped
152
+ self.@snapshot_store_unavailable: bool?
153
+ self.@persist_timers: Hash[Symbol, untyped]?
154
+ self.@persist_timer_tokens: Hash[Symbol, Integer]?
155
+ self.@persist_deferred: Hash[Symbol, bool]?
156
+ self.@persist_inflight: Integer?
157
+ self.@mutation_generation: Integer?
158
+ self.@visibility_hook_installed: bool?
159
+
160
+ SNAPSHOT_DB_NAME: String
161
+ STORAGE_PERSIST_JS: String
162
+
163
+ def self.config: () -> Config
164
+ def self.configure: () { () -> void } -> nil
165
+ def self.__reset_config: () -> nil
166
+ def self.__register_database: (Symbol role, untyped raw_db, Array[untyped] models) -> nil
167
+ def self.__registered_database: (Symbol role) -> untyped
168
+ def self.__set_snapshot_identity: (String? identity) -> String?
169
+ def self.__set_snapshot_store: (untyped store) -> untyped
170
+ def self.snapshot_store: () -> untyped
171
+ def self.open_snapshot_store: () -> untyped
172
+ private def self.__become_volatile: (untyped error) -> nil
173
+ private def self.report_persist_error: (untyped error) -> nil
174
+ private def self.invoke_persist_error_hook: (untyped error) -> nil
175
+ def self.mutation_generation: () -> Integer
176
+ def self.persist_snapshot: (Symbol role) -> bool
177
+ def self.restore_snapshot: (Symbol role) -> bool
178
+ private def self.schedule_persist: (Symbol role) -> nil
179
+ def self.__persist_timer_fired: (Symbol role, Integer? token) -> nil
180
+ def self.__persist_timer_token: (Symbol role) -> Integer?
181
+ private def self.resume_deferred_persist: (Symbol role) -> nil
182
+ def self.cancel_persist_timers: () -> nil
183
+ private def self.persist_inflight: () -> Integer
184
+ private def self.await_inflight_persists: () -> nil
185
+ def self.flush: () -> bool
186
+ def self.__install_visibility_hook: () -> bool
187
+ def self.__visibility_flush: () -> nil
188
+ def self.request_persistent_storage: () -> Symbol
189
+ def self.stale_generation?: (Integer token) -> bool
190
+ def self.stale_response_error: () -> Error
191
+
192
+ self.@boot_state: Symbol?
193
+ self.@session_epoch: untyped
194
+ self.@page_metadata: Hash[Symbol, untyped]?
195
+ self.@page_metadata_latched: bool?
196
+ self.@local_database_enabled: bool?
197
+ self.@local_database_latched: bool?
198
+ self.@local_handle: GuardedDatabase?
199
+ self.@replica_handle: GuardedDatabase?
200
+ self.@schema_lockdown: untyped
201
+
202
+ def self.boot_state: () -> Symbol
203
+ def self.__set_boot_state: (Symbol state) -> Symbol
204
+ def self.session_epoch: () -> untyped
205
+ PAGE_METADATA_JS: String
206
+
207
+ def self.boot: (?models: Array[untyped]?, ?metadata: Hash[Symbol, untyped]?) -> bool
208
+ def self.__fail_boot: (Array[untyped] errors) -> bool
209
+ def self.__report_boot_errors: (Array[untyped] errors) -> bool
210
+ def self.read_page_metadata: () -> Hash[Symbol, untyped]
211
+ def self.__page_metadata: () -> Hash[Symbol, untyped]
212
+ def self.local_database_enabled?: () -> bool
213
+ def self.__set_local_database_enabled: (untyped value) -> bool
214
+ def self.__ensure_local_database_enabled: (untyped operation) -> bool
215
+
216
+ self.@session_terminated: bool?
217
+ self.@page_epoch_latched: bool?
218
+
219
+ def self.__set_session_epoch: (untyped value) -> untyped
220
+ def self.__latch_page_epoch: () -> untyped
221
+ def self.session_terminated?: () -> bool
222
+ def self.__session_epoch_ok?: (String? header) -> bool
223
+ def self.__terminate_session: (String? header) -> nil
224
+ private def self.__boot_steps: (Array[untyped] models, Hash[Symbol, untyped] metadata) -> nil
225
+ private def self.ensure_boot_not_terminated: () -> nil
226
+ private def self.metadata_value: (Hash[Symbol, untyped]? metadata, Symbol key, untyped default) -> untyped
227
+ private def self.report_boot_error: (Array[untyped] errors) -> nil
228
+ private def self.__install_handles: (untyped local_db, untyped replica_db) -> nil
229
+ def self.local: () -> GuardedDatabase
230
+ def self.replica: () -> GuardedDatabase
231
+ def self.__model_local_db: (untyped model) -> GuardedDatabase
232
+ def self.__model_replica_db: () -> GuardedDatabase?
233
+ private def self.engage_schema_lockdown: (untyped db, untyped error) -> nil
234
+ def self.schema_lockdown: () -> untyped
235
+ private def self.__check_schema_lockdown: (String context) -> nil
236
+ def self.reset_local_table: (untyped model) -> bool
237
+ private def self.revalidate_schema_lockdown: (untyped db) -> nil
238
+ def self.__reset_boot: () -> nil
239
+ def self.wipe: () -> bool
240
+ private def self.tick_generation: () -> Integer
241
+ private def self.clear_tick_events: () -> nil
242
+ private def self.ensure_session_not_terminated: (Symbol operation) -> nil
243
+ private def self.ensure_not_in_transaction: (Symbol role) -> nil
244
+ private def self.wipe_role: (Symbol role) -> nil
245
+ private def self.notify_wiped: (Symbol role) -> nil
246
+ private def self.drop_all_tables: (untyped db) -> nil
247
+
248
+ def self.namespace_identity: (untyped application_id, untyped user_key, bool anonymous) -> String
249
+ def self.resolve_namespace: (application_id: untyped, user_key: untyped, user_key_configured: bool, anonymous_only: bool) -> String
250
+ def self.snapshot_key: (String identity, Symbol role) -> String
251
+ def self.lock_name: (String identity) -> String
252
+
253
+ module Codec
254
+ def self.encode: (Symbol? `type`, untyped value) -> untyped
255
+ def self.decode: (Symbol? `type`, untyped value) -> untyped
256
+ def self.encode_bind: (untyped value) -> untyped
257
+ def self.time_to_iso: (Time time) -> String
258
+ def self.iso_to_time: (String str) -> Time
259
+ def self.civil_from_days: (Integer days) -> Array[Integer]
260
+ def self.days_from_civil: (Integer y, Integer m, Integer d) -> Integer
261
+ def self.days_in_month: (Integer year, Integer mon) -> Integer
262
+ def self.zone_offset: (String str, Integer pos) -> Integer
263
+ def self.digits_at: (String str, Integer pos, Integer len) -> Integer
264
+ def self.sep_at: (String str, Integer pos, Integer code) -> void
265
+ def self.zpad: (Integer n, Integer width) -> String
266
+ end
267
+
268
+ class TableBuilder
269
+ @ops: Array[untyped]
270
+
271
+ attr_reader ops: Array[untyped]
272
+
273
+ def initialize: () -> void
274
+ def string: (untyped name, ?default: untyped, ?null: bool) -> void
275
+ def text: (untyped name, ?default: untyped, ?null: bool) -> void
276
+ def integer: (untyped name, ?default: untyped, ?null: bool) -> void
277
+ def float: (untyped name, ?default: untyped, ?null: bool) -> void
278
+ def boolean: (untyped name, ?default: untyped, ?null: bool) -> void
279
+ def datetime: (untyped name, ?default: untyped, ?null: bool) -> void
280
+ def timestamps: () -> void
281
+ def index: (*untyped columns) -> void
282
+ def remove_index: (*untyped columns) -> void
283
+ def rename: (untyped old_name, untyped new_name) -> void
284
+ def remove: (untyped name) -> void
285
+ def execute: (String sql) -> void
286
+
287
+ private def add_column: (Symbol `type`, untyped name, untyped default, bool null) -> void
288
+ private def identifier_list: (Array[untyped] columns) -> Array[String]
289
+ end
290
+
291
+ SQL_TYPES: Hash[Symbol, String]
292
+ META_TABLE: String
293
+ REPLICA_FINGERPRINT_KEY: String
294
+ self.@reserved_column_names: Array[Symbol]?
295
+
296
+ def self.validate_identifier: (untyped name) -> String
297
+ private def self.guard_reserved_table: (String table) -> String
298
+ private def self.baseline_index: (untyped migrations) -> Integer
299
+ def self.fold_local_columns: (untyped model) -> Hash[String, Symbol]
300
+ private def self.fold_builders: (Array[TableBuilder] builders, untyped migrations, untyped model) -> Hash[String, Symbol]
301
+ def self.apply_local_migrations: (untyped db, untyped model) -> Integer
302
+ def self.rebuild_local_table: (untyped db, untyped model, ?Array[TableBuilder]? builders) -> Integer
303
+ def self.stored_table_version: (untyped db, String table) -> Integer
304
+ def self.store_table_version: (untyped db, String table, Integer version) -> void
305
+ def self.read_meta: (untyped db, String key) -> String?
306
+ def self.store_meta: (untyped db, String key, String value) -> void
307
+ private def self.ensure_meta_table: (untyped db) -> void
308
+ def self.replica_table_ddl: (untyped model) -> String
309
+ def self.canonical_replica_schema: (Array[untyped] models) -> String
310
+ def self.build_replica_tables: (untyped db, Array[untyped] models) -> bool
311
+ private def self.stale_replica_tables: (String? stored) -> Array[String]
312
+ def self.replica_upsert: (untyped db, untyped model, Hash[untyped, untyped] attrs) -> bool
313
+ def self.replica_upsert_all: (untyped db, untyped model, Array[untyped] rows) -> bool
314
+ private def self.replica_upsert_row: (untyped db, untyped model, Hash[untyped, untyped] attrs) -> void
315
+ def self.replica_delete: (untyped db, untyped model, untyped id) -> bool
316
+ private def self.collect_builders: (untyped model) -> Array[TableBuilder]
317
+ private def self.fold_ops: (Hash[String, Symbol] columns, Array[untyped] ops, untyped model) -> void
318
+ private def self.guard_id: (String name, untyped model) -> void
319
+ private def self.guard_reserved_column: (String name, untyped model) -> void
320
+ private def self.apply_blocks: (untyped db, untyped model, Integer from_version, Array[TableBuilder] builders) -> void
321
+ private def self.run_block: (untyped db, String table, TableBuilder builder, bool create_mode) -> void
322
+ private def self.run_alter_op: (untyped db, String table, Array[untyped] op) -> void
323
+ private def self.column_ddl: (Array[untyped] op) -> String
324
+ private def self.default_literal: (Symbol `type`, untyped value) -> String
325
+ private def self.index_name: (String table, Array[String] columns) -> String
326
+ private def self.quoted_list: (Array[String] columns) -> String
327
+ end
328
+ end
@@ -5,10 +5,10 @@ module Funicular
5
5
  def initialize_state: () -> Hash[Symbol, untyped]
6
6
  def catch_error: (Exception error, ?Hash[Symbol, untyped]? error_info) -> bool
7
7
  def reset: () -> void
8
- def render: (ViewContext h) -> (VDOM::VNode | String | Integer | Float | Array[untyped] | nil)
8
+ def render: () -> (VDOM::VNode | String | Integer | Float | Array[untyped] | nil)
9
9
 
10
- private def render_fallback: (ViewContext h) -> (VDOM::VNode | String)
11
- private def default_fallback: (ViewContext h) -> VDOM::VNode
12
- private def render_children: (ViewContext h) -> VDOM::VNode
10
+ private def render_fallback: () -> (VDOM::VNode | String)
11
+ private def default_fallback: () -> VDOM::VNode
12
+ private def render_children: () -> VDOM::VNode
13
13
  end
14
14
  end
data/sig/funicular.rbs CHANGED
@@ -8,6 +8,8 @@ module Funicular
8
8
  def self.env: () -> EnvironmentInquirer
9
9
  def self.env=: (EnvironmentInquirer | String environment) -> EnvironmentInquirer?
10
10
  def self.router: () -> Router?
11
+ def self.confirm_handler=: ((^(String) -> boolish)? handler) -> (^(String) -> boolish)?
12
+ def self.confirm: (String message) -> bool
11
13
 
12
14
  # SSR / hydration support
13
15
  def self.server?: () -> bool
@@ -17,6 +19,9 @@ module Funicular
17
19
  def self.first_element_child: (JS::Element container_element) -> JS::Element?
18
20
 
19
21
  def self.load_schemas: (Hash[singleton(Model), String] models) ?{ () -> void } -> void
22
+ def self.__request_schema: (untyped model_class, String schema_name, Array[untyped] errors, Proc settle) -> nil
23
+ def self.__settle_boot_barrier: (Array[untyped] errors) ?{ () -> void } -> nil
24
+ def self.__boot_for_start: () -> bool
20
25
  def self.start: (?singleton(Component)? component_class, ?container: String | JS::Element, ?props: Hash[Symbol, untyped], ?hydrate: bool) ?{ (Router router) -> void } -> (Component | Router | nil)
21
26
  def self.configure_forms: () ?{ (Hash[Symbol, String]) -> void } -> void
22
27
  def self.form_builder_config: () -> Hash[Symbol, String]?
data/sig/http.rbs CHANGED
@@ -1,10 +1,5 @@
1
1
  module Funicular
2
2
  module HTTP
3
- CACHE_DB_NAME: String
4
- CACHE_STORE: String
5
-
6
- self.@cache: IndexedDB::KVS?
7
-
8
3
  class Response
9
4
  attr_reader data: untyped
10
5
  attr_reader status: Integer
@@ -15,24 +10,16 @@ module Funicular
15
10
  def error_message: () -> String?
16
11
  end
17
12
 
18
- def self.cache_init!: () -> IndexedDB::KVS
19
- def self.cache_purge: (String url) -> nil
20
- def self.cache_clear: () -> nil
21
- def self.cache_lookup: (String url) -> untyped
22
- def self.cache_write: (String url, Hash[String, untyped] entry) -> nil
23
-
24
- def self.get: (String url, ?cache: Integer?) { (Response) -> void } -> void
25
- def self.post: (String url, ?Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
26
- def self.patch: (String url, ?Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
27
- def self.delete: (String url, ?cache: Integer?) { (Response) -> void } -> void
28
- def self.put: (String url, ?Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
13
+ def self.get: (String url) { (Response) -> void } -> void
14
+ def self.post: (String url, ?Hash[untyped, untyped]? body) { (Response) -> void } -> void
15
+ def self.patch: (String url, ?Hash[untyped, untyped]? body) { (Response) -> void } -> void
16
+ def self.delete: (String url) { (Response) -> void } -> void
17
+ def self.put: (String url, ?Hash[untyped, untyped]? body) { (Response) -> void } -> void
29
18
  def self.csrf_token: () -> String?
30
19
 
31
- private def self.warn_unsupported_cache: (String verb) -> void
32
- private def self.now_seconds: () -> Integer
33
- private def self.cache_hit?: (untyped entry, Integer ttl) -> bool
34
- private def self.serve_from_cache: (Hash[String, untyped] entry) { (Response) -> void } -> void
35
20
  private def self.parse_response_body: (untyped text) -> untyped
36
- private def self.request: (String method, String url, Hash[untyped, untyped]? body, ?cache: Integer?) { (Response) -> void } -> void
21
+ private def self.request: (String method, String url, Hash[untyped, untyped]? body) { (Response) -> void } -> void
22
+ private def self.session_changed_response: () -> Response
23
+ private def self.response_epoch: (untyped response) -> String?
37
24
  end
38
25
  end
data/sig/model.rbs CHANGED
@@ -5,24 +5,118 @@ module Funicular
5
5
 
6
6
  attr_reader id: untyped
7
7
  @changed_attributes: Hash[String, untyped]
8
+ @local_baseline: Hash[String, untyped]?
9
+ @provided_attributes: Hash[String, bool]?
10
+
11
+ self.@registered_models: Array[untyped]?
12
+ self.@storage_kind: Symbol?
13
+ self.@local_migrations: Array[Hash[Symbol, untyped]]?
14
+ self.@collecting_migrations: bool?
15
+ self.@refresh_mode: Symbol?
16
+ self.@table_name: String?
17
+ self.@associations: Hash[Symbol, Hash[Symbol, untyped]]?
18
+ self.@local_columns: Hash[String, Symbol]?
8
19
 
9
20
  def self.schema: () -> Hash[String, Hash[String, untyped]]
10
21
  def self.schema=: (Hash[String, Hash[String, untyped]] schema) -> Hash[String, Hash[String, untyped]]
11
22
  def self.endpoints: () -> Hash[String, Hash[String, String]]
12
23
  def self.endpoints=: (Hash[String, Hash[String, String]] endpoints) -> Hash[String, Hash[String, String]]
13
24
 
25
+ def self.inherited: (untyped subclass) -> void
26
+ def self.__register_model: (untyped subclass) -> nil
27
+ def self.__registered_models: () -> Array[untyped]
28
+
14
29
  def initialize: (?Hash[untyped, untyped] attributes) -> void
15
30
  def self.load_schema: (Hash[String, untyped] schema_data) -> void
16
31
  def self.register_schema_validations: (untyped validations) -> void
17
32
  def self.normalize_validation_options: (Symbol | String kind, untyped opts) -> untyped
18
33
 
19
- def self.all: (?Hash[untyped, untyped] params) ?{ (Array[Model]? instances, String? error) -> void } -> void
20
- def self.find: (?untyped id, ?endpoint_name: String, ?model_class: singleton(Model)) ?{ (Model? instance, String? error) -> void } -> void
21
- def self.create: (Hash[untyped, untyped] attrs, ?model_class: singleton(Model)) ?{ (Model? instance, untyped error) -> void } -> void
22
- def self.destroy: (?untyped id) ?{ (bool success, untyped result) -> void } -> void
34
+ # Local-database declaration DSL (docs/local_database.md). Migrate
35
+ # blocks are recorded at class eval and executed at DB boot.
36
+ def self.storage: (Symbol kind) ?{ () -> void } -> Symbol
37
+ def self.storage_kind: () -> Symbol
38
+ def self.replica?: () -> bool
39
+ def self.ephemeral?: () -> bool
40
+ def self.local?: () -> bool
41
+ def self.migrate: (Integer version, ?reset: bool) { (untyped t) -> void } -> void
42
+ def self.local_migrations: () -> Array[Hash[Symbol, untyped]]?
43
+ def self.validate_local_migrations: () -> void
44
+ def self.refresh: (Symbol mode) -> Symbol
45
+ def self.refresh_mode: () -> Symbol
46
+
47
+ # Associations: declaration-time metadata plus lazily resolved
48
+ # targets. The generated readers are dynamic, like the accessors.
49
+ def self.belongs_to: (Symbol | String name, ?class_name: (String | Symbol)?, ?foreign_key: (String | Symbol)?, **untyped rest) -> nil
50
+ def self.has_many: (Symbol | String name, ?class_name: (String | Symbol)?, ?foreign_key: (String | Symbol)?, **untyped rest) -> nil
51
+ def self.__reject_association_options: (Symbol kind, Symbol | String name, Hash[Symbol, untyped] rest) -> nil
52
+ def self.__register_association: (Symbol key) -> nil
53
+ def self.__assert_association_name_free: (Symbol key) -> nil
54
+ def self.__associations: () -> Hash[Symbol, Hash[Symbol, untyped]]
55
+ def self.__association_target: (Symbol key, String class_name) -> untyped
56
+ def self.__resolve_association_constant: (Symbol key, String class_name) -> untyped
57
+ def self.__unresolvable_association: (Symbol key, String class_name) -> String
58
+ def self.__assert_no_association_conflict: (Array[String] names) -> nil
59
+
60
+ def self.table_name: (?(String | Symbol)? explicit) -> String
61
+ def self.derive_table_name: () -> String
62
+ def self.demodulized_snake_name: () -> String
63
+ def self.__pluralize: (String name) -> String
64
+ def self.__singularize: (String name) -> String
65
+ def self.__camelize: (String name) -> String
66
+ def self.local: () -> Relation
67
+
68
+ # Relation protocol (mrblib/relation.rb)
69
+ def self.local_columns: () -> Hash[String, Symbol]
70
+ def self.derive_replica_columns: () -> Hash[String, Symbol]
71
+ def self.local_db: () -> untyped
72
+ def self.replica_db: () -> untyped
73
+ def self.build_from_local: (Hash[untyped, untyped] attrs) -> Model
74
+ def self.local_table_changed: () -> void
75
+ def self.on_change: () { (Symbol role, String table) -> void } -> Integer
76
+ def self.off_change: (Integer subscription) -> nil
77
+ def self.rest_attribute_type: (String name) -> Symbol?
78
+ def self.__write_through_upsert: (untyped attrs) -> void
79
+ def self.__write_through_upsert_all: (untyped rows) -> void
80
+ def self.__write_through_delete: (untyped id) -> void
81
+
82
+ # Unified callback contract (0.5.0): every REST callback is
83
+ # (result, error). On success `result` is the payload and `error` is
84
+ # nil; on failure `result` is nil. destroy's success payload is `true`.
85
+ # On storage :local models these methods are synchronous instead
86
+ # (bare-class alias): no blocks, direct return values.
87
+ def self.all: (?Hash[untyped, untyped] params) ?{ (Array[Model]? instances, untyped error) -> void } -> untyped
88
+ def self.find: (?untyped id, ?endpoint_name: String, ?model_class: singleton(Model)) ?{ (Model? instance, untyped error) -> void } -> untyped
89
+ def self.create: (?Hash[untyped, untyped] attrs, **untyped kw) ?{ (Model? instance, untyped error) -> void } -> untyped
90
+ def self.destroy: (?untyped id) ?{ (bool? success, untyped error) -> void } -> untyped
91
+
92
+ def update: (?Hash[untyped, untyped]? attrs) ?{ (Model? instance, untyped error) -> void } -> untyped
93
+ def destroy: () ?{ (bool? success, untyped error) -> void } -> untyped
94
+ def reload: () ?{ (Model? instance, untyped error) -> void } -> untyped
95
+
96
+ # storage :local CRUD and the bare-class alias
97
+ def self.define_local_accessors: (Hash[String, Symbol] columns) -> void
98
+ def self.define_local_accessor: (String name, Array[Symbol] existing) -> void
99
+ def self.merge_keyword_attrs: (Hash[untyped, untyped] attrs, Hash[Symbol, untyped] kw) -> Hash[untyped, untyped]
100
+ def self.local_create: (?Hash[untyped, untyped] attrs) -> Model
101
+ def self.reset_local: () -> bool
102
+ def self.local_query: (String method_name) -> Relation
103
+ def self.where: (?Hash[untyped, untyped] | String | nil conditions, *untyped binds) -> Relation
104
+ def self.order: (*untyped args) -> Relation
105
+ def self.limit: (Integer? n) -> Relation
106
+ def self.offset: (Integer? n) -> Relation
107
+ def self.count: () -> Integer
108
+ def self.first: () -> untyped
109
+ def self.exists?: () -> bool
110
+ def self.find_by: (Hash[untyped, untyped] conditions) -> untyped
111
+ def self.delete_all: () -> Integer
23
112
 
24
- def update: (?Hash[untyped, untyped]? attrs) ?{ (bool success, untyped result) -> void } -> void
25
- def destroy: () ?{ (bool success, untyped result) -> void } -> void
26
- def reload: () ?{ (Model? instance, String? error) -> void } -> void
113
+ def new_record?: () -> bool
114
+ def __track_local_change: (String name, untyped value, untyped old) -> void
115
+ def __local_insert: () -> Model
116
+ def __local_update: (Hash[untyped, untyped]? attrs) -> bool
117
+ def __local_destroy: () -> bool
118
+ def __sync_from_row: () -> void
119
+ def __hydrate_local: (Hash[untyped, untyped] attrs) -> void
120
+ def __set_local_baseline: (Hash[untyped, untyped] attrs) -> void
27
121
  end
28
122
  end
data/sig/relation.rbs ADDED
@@ -0,0 +1,44 @@
1
+ module Funicular
2
+ class Relation
3
+ @model: untyped
4
+ @where_sql: Array[String]
5
+ @where_binds: Array[untyped]
6
+ @order_sql: Array[String]
7
+ @limit: Integer?
8
+ @offset: Integer?
9
+
10
+ def initialize: (untyped model, ?Array[String] where_sql,
11
+ ?Array[untyped] where_binds, ?Array[String] order_sql,
12
+ ?Integer? limit, ?Integer? offset) -> void
13
+
14
+ def where: (?Hash[untyped, untyped] | String | nil conditions, *untyped binds) -> Relation
15
+ def order: (*untyped args) -> Relation
16
+ def limit: (Integer? n) -> Relation
17
+ def offset: (Integer? n) -> Relation
18
+
19
+ def to_a: () -> Array[untyped]
20
+ def each: () { (untyped record) -> void } -> Array[untyped]
21
+ def first: () -> untyped
22
+ def count: () -> Integer
23
+ def exists?: () -> bool
24
+ def find: (untyped id) -> untyped
25
+ def find_by: (Hash[untyped, untyped] conditions) -> untyped
26
+ def delete_all: () -> Integer
27
+ def to_sql: () -> String
28
+ def __event_source: () -> [Symbol, String]
29
+
30
+ private def append_condition: (Array[String] w, Array[untyped] b, untyped col, untyped value) -> void
31
+ private def order_term: (untyped col, untyped dir) -> String
32
+ private def validate_column: (untyped col) -> String
33
+ private def quote: (String name) -> String
34
+ private def quoted_table: () -> String
35
+ private def select_sql: () -> String
36
+ private def where_clause: () -> String
37
+ private def order_clause: () -> String
38
+ private def slice_clause: () -> String
39
+ private def slice_arg: (Integer? n, String label) -> Integer?
40
+ private def decode_row: (Array[String] cols, untyped row) -> Hash[String, untyped]
41
+ private def single_value: (String sql) -> untyped
42
+ private def model_label: () -> untyped
43
+ end
44
+ end
data/sig/router.rbs CHANGED
@@ -21,6 +21,7 @@ module Funicular
21
21
  def start: (?hydrate: bool) -> void
22
22
  def stop: () -> void
23
23
  def navigate: (String path) -> void
24
+ def leave_allowed?: () -> bool
24
25
  def current_location_path: () -> String
25
26
 
26
27
  private def add_route_with_method: (Symbol method, String path, singleton(Component) component_class, String? name, ?route_constraints_t? constraints) -> void