solid_objects 0.4.3 → 0.5.1

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -0
  3. data/README.md +54 -7
  4. data/app/assets/javascripts/solid_objects/component_refresh.js +124 -0
  5. data/app/controllers/solid_objects/components_controller.rb +31 -9
  6. data/app/helpers/solid_objects/actor_helper.rb +8 -1
  7. data/docs/adr/0009-realtime-updates.md +18 -1
  8. data/docs/architecture.md +34 -18
  9. data/docs/authorization.md +24 -0
  10. data/docs/correctness.md +27 -5
  11. data/docs/operations.md +20 -2
  12. data/docs/realtime.md +78 -13
  13. data/docs/roadmap.md +6 -5
  14. data/examples/application/app/views/chat_rooms/show.html.erb +3 -1
  15. data/lib/solid_objects/actor_view.rb +40 -13
  16. data/lib/solid_objects/component_registration.rb +61 -16
  17. data/lib/solid_objects/component_renderer.rb +14 -17
  18. data/lib/solid_objects/component_subscriptions.rb +7 -7
  19. data/lib/solid_objects/component_token.rb +70 -2
  20. data/lib/solid_objects/database_adapters/sqlite.rb +43 -5
  21. data/lib/solid_objects/doctor.rb +46 -8
  22. data/lib/solid_objects/dom_identity.rb +12 -3
  23. data/lib/solid_objects/engine.rb +7 -0
  24. data/lib/solid_objects/synchronous_invocation.rb +16 -1
  25. data/lib/solid_objects/turbo_stream_renderer.rb +14 -5
  26. data/lib/solid_objects/version.rb +1 -1
  27. data/sig/generated/controllers/solid_objects/components_controller.rbs +6 -0
  28. data/sig/generated/lib/solid_objects/actor_view.rbs +10 -4
  29. data/sig/generated/lib/solid_objects/component_registration.rbs +34 -10
  30. data/sig/generated/lib/solid_objects/component_renderer.rbs +4 -8
  31. data/sig/generated/lib/solid_objects/component_token.rbs +24 -2
  32. data/sig/generated/lib/solid_objects/database_adapters/sqlite.rbs +9 -0
  33. data/sig/generated/lib/solid_objects/doctor.rbs +12 -0
  34. data/sig/generated/lib/solid_objects/dom_identity.rbs +5 -2
  35. data/sig/generated/lib/solid_objects/synchronous_invocation.rbs +8 -0
  36. data/sig/generated/lib/solid_objects/turbo_stream_renderer.rbs +3 -0
  37. metadata +3 -2
@@ -7,23 +7,38 @@ module SolidObjects
7
7
  PURPOSE = "solid_objects.actor_component"
8
8
  MAXIMUM_TOKEN_BYTES = 16_384
9
9
  MAXIMUM_DEPENDENCIES = 50
10
+ MAXIMUM_LOCALS = 50
11
+ MAXIMUM_COMPONENT_KEY_BYTES = 512
12
+ REFRESH_METHODS = %w[replace morph].freeze
13
+ RESERVED_LOCALS = %w[actor authorization_context component_key].freeze
14
+ RUBY_KEYWORDS = %w[
15
+ alias and begin break case class def defined do else elsif end ensure
16
+ false for if in module next nil not or redo rescue retry return self
17
+ super then true undef unless until when while yield
18
+ ].freeze
10
19
 
11
20
  module_function
12
21
 
13
- # @rbs (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String) -> String
22
+ # @rbs (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String, ?component_key: untyped, ?locals: Hash[untyped, untyped], ?refresh_method: String | Symbol) -> String
14
23
  def generate(
15
24
  reference:,
16
25
  component_name:,
17
26
  dependencies:,
18
27
  instance_id:,
19
28
  revision:,
20
- refresh_path:
29
+ refresh_path:,
30
+ component_key: nil,
31
+ locals: {},
32
+ refresh_method: "replace"
21
33
  )
22
34
  payload = {
23
35
  "actor_type" => reference.actor_type,
24
36
  "actor_id" => reference.actor_id,
25
37
  "component_name" => component_name,
38
+ "component_key" => Serialization.dump(component_key),
26
39
  "dependencies" => dependencies,
40
+ "locals" => Serialization.dump(locals),
41
+ "refresh_method" => refresh_method.to_s,
27
42
  "instance_id" => instance_id,
28
43
  "revision" => revision,
29
44
  "refresh_path" => refresh_path
@@ -41,6 +56,7 @@ module SolidObjects
41
56
  end
42
57
 
43
58
  payload = verifier.verified(token, purpose: PURPOSE)
59
+ apply_defaults!(payload)
44
60
  validate_payload!(payload)
45
61
  payload
46
62
  rescue ActiveSupport::MessageVerifier::InvalidSignature
@@ -54,6 +70,8 @@ module SolidObjects
54
70
  payload["actor_id"].is_a?(String) &&
55
71
  payload["component_name"].is_a?(String) &&
56
72
  payload["dependencies"].is_a?(Array) &&
73
+ payload["locals"].is_a?(Hash) &&
74
+ payload["refresh_method"].is_a?(String) &&
57
75
  payload["instance_id"].is_a?(Integer) &&
58
76
  payload["revision"].is_a?(Integer) &&
59
77
  payload["refresh_path"].is_a?(String)
@@ -61,7 +79,10 @@ module SolidObjects
61
79
  end
62
80
 
63
81
  validate_component_name!(payload.fetch("component_name"))
82
+ validate_component_key!(payload["component_key"])
64
83
  validate_dependencies!(payload.fetch("dependencies"))
84
+ validate_locals!(payload.fetch("locals"))
85
+ validate_refresh_method!(payload.fetch("refresh_method"))
65
86
  validate_revision!(
66
87
  payload.fetch("instance_id"),
67
88
  payload.fetch("revision")
@@ -71,6 +92,16 @@ module SolidObjects
71
92
  end
72
93
  private_class_method :validate_payload!
73
94
 
95
+ # @rbs (Hash[String, untyped]?) -> void
96
+ def apply_defaults!(payload)
97
+ return unless payload.is_a?(Hash)
98
+
99
+ payload["component_key"] = nil unless payload.key?("component_key")
100
+ payload["locals"] = {} unless payload.key?("locals")
101
+ payload["refresh_method"] = "replace" unless payload.key?("refresh_method")
102
+ end
103
+ private_class_method :apply_defaults!
104
+
74
105
  # @rbs (String) -> void
75
106
  def validate_component_name!(component_name)
76
107
  return if component_name.match?(/\A[a-zA-Z0-9_]+\z/)
@@ -79,6 +110,20 @@ module SolidObjects
79
110
  end
80
111
  private_class_method :validate_component_name!
81
112
 
113
+ # @rbs (untyped) -> void
114
+ def validate_component_key!(component_key)
115
+ return if component_key.nil?
116
+ return if component_key.is_a?(Integer)
117
+ if component_key.is_a?(String) &&
118
+ component_key.bytesize.positive? &&
119
+ component_key.bytesize <= MAXIMUM_COMPONENT_KEY_BYTES
120
+ return
121
+ end
122
+
123
+ raise InvalidComponentToken, "invalid actor component key"
124
+ end
125
+ private_class_method :validate_component_key!
126
+
82
127
  # @rbs (Array[untyped]) -> void
83
128
  def validate_dependencies!(dependencies)
84
129
  valid = dependencies.any? &&
@@ -91,6 +136,29 @@ module SolidObjects
91
136
  end
92
137
  private_class_method :validate_dependencies!
93
138
 
139
+ # @rbs (Hash[untyped, untyped]) -> void
140
+ def validate_locals!(locals)
141
+ valid = locals.length <= MAXIMUM_LOCALS &&
142
+ locals.keys.all? do |name|
143
+ name.is_a?(String) &&
144
+ name.match?(/\A[a-z_][a-zA-Z0-9_]*\z/) &&
145
+ !RESERVED_LOCALS.include?(name) &&
146
+ !RUBY_KEYWORDS.include?(name)
147
+ end
148
+ return if valid
149
+
150
+ raise InvalidComponentToken, "invalid actor component locals"
151
+ end
152
+ private_class_method :validate_locals!
153
+
154
+ # @rbs (String) -> void
155
+ def validate_refresh_method!(refresh_method)
156
+ return if REFRESH_METHODS.include?(refresh_method)
157
+
158
+ raise InvalidComponentToken, "invalid actor component refresh method"
159
+ end
160
+ private_class_method :validate_refresh_method!
161
+
94
162
  # @rbs (Integer, Integer) -> void
95
163
  def validate_revision!(instance_id, revision)
96
164
  return unless instance_id.negative? || revision.negative?
@@ -67,11 +67,49 @@ module SolidObjects
67
67
  def with_transaction_deadline(connection)
68
68
  return yield unless SyncDeadline.active?
69
69
 
70
- previous_timeout = connection.select_value("PRAGMA busy_timeout").to_i
71
- connection.execute("PRAGMA busy_timeout = 0")
72
- yield
73
- ensure
74
- connection.execute("PRAGMA busy_timeout = #{previous_timeout}") if previous_timeout
70
+ busy_wait = restorable_busy_wait(connection)
71
+ return yield unless busy_wait
72
+
73
+ begin
74
+ connection.execute("PRAGMA busy_timeout = 0")
75
+ yield
76
+ ensure
77
+ restore_busy_wait(connection, busy_wait)
78
+ end
79
+ end
80
+
81
+ # @rbs (untyped) -> Hash[Symbol, untyped]?
82
+ def restorable_busy_wait(connection)
83
+ pragma_timeout = connection.select_value("PRAGMA busy_timeout").to_i
84
+ return { pragma_timeout: } if pragma_timeout.positive?
85
+
86
+ handler_timeout = configured_busy_handler_timeout(connection)
87
+ return nil unless handler_timeout
88
+
89
+ { pragma_timeout:, handler_timeout: }
90
+ end
91
+
92
+ # @rbs (untyped, Hash[Symbol, untyped]) -> void
93
+ def restore_busy_wait(connection, busy_wait)
94
+ handler_timeout = busy_wait[:handler_timeout]
95
+ if handler_timeout
96
+ connection.raw_connection.busy_handler_timeout = handler_timeout
97
+ return
98
+ end
99
+
100
+ connection.execute("PRAGMA busy_timeout = #{busy_wait.fetch(:pragma_timeout)}")
101
+ end
102
+
103
+ # @rbs (untyped) -> Integer?
104
+ def configured_busy_handler_timeout(connection)
105
+ return nil unless connection.respond_to?(:raw_connection)
106
+ return nil unless connection.raw_connection.respond_to?(:busy_handler_timeout=)
107
+
108
+ pool = connection.respond_to?(:pool) ? connection.pool : nil
109
+ return nil unless pool.respond_to?(:db_config)
110
+
111
+ timeout = pool.db_config.configuration_hash[:timeout]
112
+ timeout&.to_i
75
113
  end
76
114
 
77
115
  # @rbs (Exception) -> bool
@@ -215,25 +215,63 @@ module SolidObjects
215
215
  # @rbs () -> Check
216
216
  def check_sync_round_trip
217
217
  actor_id = SecureRandom.uuid
218
+ probe_registry = ProcessRegistry.new
219
+ check = run_sync_probe(actor_id, probe_registry)
220
+ leftovers = remove_probe_records(actor_id:, probe_registry:)
221
+ return check if leftovers.empty? || check.failed?
222
+
223
+ warn_check(
224
+ :sync_round_trip,
225
+ "#{check.message}; could not remove the #{leftovers.join(" and ")}"
226
+ )
227
+ end
228
+
229
+ # @rbs (String, ProcessRegistry) -> Check
230
+ def run_sync_probe(actor_id, probe_registry)
231
+ probe_registry.register(kind: "caller", metadata: { execution: "doctor" })
218
232
  value = SecureRandom.hex(8)
219
- process_registry = SolidObjects.caller_process.process_registry
220
- reference = ProbeActor.ref(actor_id)
221
233
  message_reference = Mailbox.new.enqueue(
222
- reference,
234
+ ProbeActor.ref(actor_id),
223
235
  :ping,
224
236
  { value: },
225
237
  kind: "sync"
226
238
  )
227
- result = SynchronousInvocation.new.call(message_reference, timeout: 5.seconds)
239
+ result = SynchronousInvocation
240
+ .new(process_registry: probe_registry)
241
+ .call(message_reference, timeout: 5.seconds)
228
242
  raise Error, "unexpected round-trip result" unless result == value
229
243
 
230
244
  pass(:sync_round_trip, "durable synchronous actor call completed without a worker")
231
245
  rescue => error
232
246
  fail_check(:sync_round_trip, "#{error.class}: #{error.message}")
233
- ensure
234
- Instance.where(actor_type: ProbeActor.actor_type, actor_id:).delete_all if actor_id
235
- process_registry&.stop
236
- process_registry&.process_record&.delete
247
+ end
248
+
249
+ # @rbs (actor_id: String, probe_registry: ProcessRegistry) -> Array[String]
250
+ def remove_probe_records(actor_id:, probe_registry:)
251
+ leftovers = []
252
+ leftovers << "probe actor" unless delete_probe_actor(actor_id)
253
+ leftovers << "probe caller process" unless delete_probe_caller_process(probe_registry)
254
+ leftovers
255
+ end
256
+
257
+ # @rbs (String) -> bool
258
+ def delete_probe_actor(actor_id)
259
+ Instance.where(actor_type: ProbeActor.actor_type, actor_id:).delete_all
260
+ true
261
+ rescue
262
+ false
263
+ end
264
+
265
+ # @rbs (ProcessRegistry) -> bool
266
+ def delete_probe_caller_process(probe_registry)
267
+ process_record = probe_registry.process_record
268
+ return true unless process_record
269
+
270
+ probe_registry.stop
271
+ process_record.delete
272
+ true
273
+ rescue
274
+ false
237
275
  end
238
276
 
239
277
  # @rbs (Check, Check) -> bool
@@ -16,9 +16,12 @@ module SolidObjects
16
16
  "#{scope(reference)}_observable_#{normalized_name(name)}"
17
17
  end
18
18
 
19
- # @rbs (Reference, Symbol | String) -> String
20
- def component(reference, name)
21
- "#{scope(reference)}_component_#{normalized_name(name)}"
19
+ # @rbs (Reference, Symbol | String, ?key: String | Integer?) -> String
20
+ def component(reference, name, key: nil)
21
+ identity = "#{scope(reference)}_component_#{normalized_name(name)}"
22
+ return identity unless key
23
+
24
+ "#{identity}_#{component_key_digest(key)}"
22
25
  end
23
26
 
24
27
  # @rbs (Reference) -> String
@@ -34,5 +37,11 @@ module SolidObjects
34
37
  name.to_s.gsub(/[^a-zA-Z0-9_-]/, "_")
35
38
  end
36
39
  private_class_method :normalized_name
40
+
41
+ # @rbs (String | Integer) -> String
42
+ def component_key_digest(key)
43
+ Digest::SHA256.hexdigest(JSON.generate(key)).first(24)
44
+ end
45
+ private_class_method :component_key_digest
37
46
  end
38
47
  end
@@ -26,6 +26,13 @@ module SolidObjects
26
26
  end
27
27
  end
28
28
 
29
+ initializer "solid_objects.assets" do |application|
30
+ next unless application.config.respond_to?(:assets)
31
+ next unless application.config.assets.respond_to?(:precompile)
32
+
33
+ application.config.assets.precompile << "solid_objects/component_refresh.js"
34
+ end
35
+
29
36
  rake_tasks do
30
37
  tasks_path = File.expand_path("../tasks", __dir__)
31
38
  Dir[File.join(tasks_path, "**/*.rake")].sort.each { |task| load task }
@@ -6,6 +6,13 @@ require "solid_objects/sync_diagnostics"
6
6
 
7
7
  module SolidObjects
8
8
  class SynchronousInvocation
9
+ # @rbs @dedicated_process_registry: ProcessRegistry?
10
+
11
+ # @rbs (?process_registry: ProcessRegistry?) -> void
12
+ def initialize(process_registry: nil)
13
+ @dedicated_process_registry = process_registry
14
+ end
15
+
9
16
  # @rbs (MessageReference, timeout: Numeric) -> untyped
10
17
  def call(message_reference, timeout:)
11
18
  return call_before_deadline(message_reference, timeout:) if SyncDeadline.active?
@@ -92,9 +99,17 @@ module SolidObjects
92
99
  )
93
100
  end
94
101
 
102
+ # @rbs () -> ProcessRegistry
103
+ def process_registry
104
+ dedicated_registry = @dedicated_process_registry
105
+ return SolidObjects.caller_process.process_registry unless dedicated_registry
106
+
107
+ dedicated_registry.tap(&:heartbeat)
108
+ end
109
+
95
110
  # @rbs (Message, deadline: Float) -> Integer
96
111
  def assist(message, deadline:)
97
- process_registry = SolidObjects.caller_process.process_registry
112
+ process_registry = self.process_registry
98
113
  activation = ActivationManager
99
114
  .new(owner_id: process_registry.process_record.id)
100
115
  .claim(instance_id: message.instance_id)
@@ -40,15 +40,14 @@ module SolidObjects
40
40
 
41
41
  # @rbs (ComponentRegistration, Integer, Integer) -> String
42
42
  def component_refresh(registration, instance_id, revision)
43
- target = DomIdentity.component(
44
- registration.reference,
45
- registration.component_name
46
- )
43
+ return morph_component_refresh(registration, instance_id, revision) if registration.morph?
44
+
45
+ target = registration.dom_id
47
46
  source = ERB::Util.html_escape(
48
47
  registration.refresh_url(instance_id, revision)
49
48
  )
50
49
  revision_value = "#{instance_id}:#{revision}"
51
- %(<turbo-stream action="replace" target="#{target}"><template><turbo-frame id="#{target}" src="#{source}" data-solid-objects-revision="#{revision_value}"></turbo-frame></template></turbo-stream>)
50
+ %(<turbo-stream action="replace" target="#{target}"><template><turbo-frame id="#{target}" src="#{source}" data-solid-objects-revision="#{revision_value}" data-solid-objects-refresh="replace"></turbo-frame></template></turbo-stream>)
52
51
  end
53
52
 
54
53
  # @rbs (String) -> Hash[String, untyped]?
@@ -75,5 +74,15 @@ module SolidObjects
75
74
  JSON.generate(value)
76
75
  end
77
76
  private_class_method :display_value
77
+
78
+ # @rbs (ComponentRegistration, Integer, Integer) -> String
79
+ def morph_component_refresh(registration, instance_id, revision)
80
+ source = ERB::Util.html_escape(
81
+ registration.refresh_url(instance_id, revision)
82
+ )
83
+ scope = DomIdentity.scope(registration.reference)
84
+ %(<turbo-stream action="append" target="#{scope}"><template><solid-objects-refresh data-target="#{registration.dom_id}" data-source="#{source}" data-refresh-method="morph"></solid-objects-refresh></template></turbo-stream>)
85
+ end
86
+ private_class_method :morph_component_refresh
78
87
  end
79
88
  end
@@ -1,5 +1,5 @@
1
1
  # rbs_inline: enabled
2
2
 
3
3
  module SolidObjects
4
- VERSION = "0.4.3"
4
+ VERSION = "0.5.1"
5
5
  end
@@ -7,6 +7,12 @@ module SolidObjects
7
7
 
8
8
  private
9
9
 
10
+ # @rbs (Hash[Symbol, untyped]) -> void
11
+ def refresh: (Hash[Symbol, untyped]) -> void
12
+
13
+ # @rbs (ComponentRegistration) -> Hash[Symbol, untyped]
14
+ def registration_payload: (ComponentRegistration) -> Hash[Symbol, untyped]
15
+
10
16
  # @rbs () -> Array[Integer]
11
17
  def requested_revision_key: () -> Array[Integer]
12
18
 
@@ -22,8 +22,8 @@ module SolidObjects
22
22
  # @rbs (Symbol | String) -> untyped
23
23
  def value: (Symbol | String) -> untyped
24
24
 
25
- # @rbs (Symbol | String, ?observes: Symbol | String | Array[Symbol | String]?, ?partial: String?) -> untyped
26
- def component: (Symbol | String, ?observes: Symbol | String | Array[Symbol | String]?, ?partial: String?) -> untyped
25
+ # @rbs (Symbol | String, ?observes: Symbol | String | Array[Symbol | String]?, ?partial: String?, ?key: untyped, ?locals: Hash[untyped, untyped], ?refresh: String | Symbol) -> untyped
26
+ def component: (Symbol | String, ?observes: Symbol | String | Array[Symbol | String]?, ?partial: String?, ?key: untyped, ?locals: Hash[untyped, untyped], ?refresh: String | Symbol) -> untyped
27
27
 
28
28
  # @rbs () -> Array[String]
29
29
  def component_tokens: () -> Array[String]
@@ -31,6 +31,9 @@ module SolidObjects
31
31
  # @rbs () -> Array[String]
32
32
  def scalar_observable_names: () -> Array[String]
33
33
 
34
+ # @rbs () -> bool
35
+ def morph_components?: () -> bool
36
+
34
37
  # @rbs () -> State
35
38
  def state: () -> State
36
39
 
@@ -73,8 +76,11 @@ module SolidObjects
73
76
  # @rbs (Array[String]) -> void
74
77
  def validate_dependencies!: (Array[String]) -> void
75
78
 
76
- # @rbs (String) -> void
77
- def ensure_unique_component!: (String) -> void
79
+ # @rbs (ComponentRegistration) -> void
80
+ def ensure_unique_component!: (ComponentRegistration) -> void
81
+
82
+ # @rbs (key: untyped, locals: Hash[untyped, untyped], refresh: String | Symbol) -> void
83
+ def validate_static_options!: (key: untyped, locals: Hash[untyped, untyped], refresh: String | Symbol) -> void
78
84
 
79
85
  # @rbs () -> Proc | ComponentPathResolver
80
86
  def component_path_resolver: () -> Proc
@@ -2,26 +2,38 @@
2
2
 
3
3
  module SolidObjects
4
4
  class ComponentRegistration
5
- @reference: Reference
5
+ @token: String
6
6
 
7
- @component_name: String
7
+ @refresh_path: String
8
8
 
9
- @dependencies: Array[String]
9
+ @revision: Integer
10
10
 
11
11
  @instance_id: Integer
12
12
 
13
- @revision: Integer
13
+ @refresh_method: String
14
14
 
15
- @refresh_path: String
15
+ @locals: Hash[String, untyped]
16
16
 
17
- @token: String
17
+ @dependencies: Array[String]
18
+
19
+ @component_key: String | Integer?
20
+
21
+ @component_name: String
22
+
23
+ @reference: Reference
18
24
 
19
25
  attr_reader reference: untyped
20
26
 
21
27
  attr_reader component_name: untyped
22
28
 
29
+ attr_reader component_key: untyped
30
+
23
31
  attr_reader dependencies: untyped
24
32
 
33
+ attr_reader locals: untyped
34
+
35
+ attr_reader refresh_method: untyped
36
+
25
37
  attr_reader instance_id: untyped
26
38
 
27
39
  attr_reader revision: untyped
@@ -30,21 +42,33 @@ module SolidObjects
30
42
 
31
43
  attr_reader token: untyped
32
44
 
33
- # @rbs (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String, token: String) -> void
34
- def initialize: (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String, token: String) -> void
45
+ # @rbs (reference: Reference, component_name: String, component_key: String | Integer?, dependencies: Array[String], locals: Hash[String, untyped], refresh_method: String, instance_id: Integer, revision: Integer, refresh_path: String, token: String) -> void
46
+ def initialize: (reference: Reference, component_name: String, component_key: String | Integer?, dependencies: Array[String], locals: Hash[String, untyped], refresh_method: String, instance_id: Integer, revision: Integer, refresh_path: String, token: String) -> void
35
47
 
36
- # @rbs (reference: Reference, component_name: String, dependencies: Array[String], snapshot: ActorSnapshot, refresh_path: String) -> ComponentRegistration
37
- def self.issue: (reference: Reference, component_name: String, dependencies: Array[String], snapshot: ActorSnapshot, refresh_path: String) -> ComponentRegistration
48
+ # @rbs (reference: Reference, component_name: String, component_key: untyped, dependencies: Array[String], locals: Hash[untyped, untyped], refresh_method: String | Symbol, snapshot: ActorSnapshot, refresh_path: String) -> ComponentRegistration
49
+ def self.issue: (reference: Reference, component_name: String, component_key: untyped, dependencies: Array[String], locals: Hash[untyped, untyped], refresh_method: String | Symbol, snapshot: ActorSnapshot, refresh_path: String) -> ComponentRegistration
38
50
 
39
51
  # @rbs (String) -> ComponentRegistration
40
52
  def self.from_token: (String) -> ComponentRegistration
41
53
 
54
+ # @rbs (Hash[String, untyped], token: String) -> ComponentRegistration
55
+ private def self.build: (Hash[String, untyped], token: String) -> ComponentRegistration
56
+
42
57
  # @rbs (Reference, Array[String]) -> void
43
58
  private def self.validate_dependencies!: (Reference, Array[String]) -> void
44
59
 
45
60
  # @rbs () -> Array[Integer]
46
61
  def revision_key: () -> Array[Integer]
47
62
 
63
+ # @rbs () -> String
64
+ def dom_id: () -> String
65
+
66
+ # @rbs () -> bool
67
+ def morph?: () -> bool
68
+
69
+ # @rbs () -> Hash[String, untyped]
70
+ def authorization_arguments: () -> Hash[String, untyped]
71
+
48
72
  # @rbs (Integer, Integer) -> String
49
73
  def refresh_url: (Integer, Integer) -> String
50
74
  end
@@ -4,16 +4,14 @@ module SolidObjects
4
4
  class ComponentRenderer
5
5
  @snapshot: ActorSnapshot
6
6
 
7
- @component_name: String
8
-
9
- @dependencies: Array[String]
7
+ @registration: ComponentRegistration
10
8
 
11
9
  @view_context: untyped
12
10
 
13
11
  @authorization_context: untyped
14
12
 
15
- # @rbs (snapshot: ActorSnapshot, component_name: String, dependencies: Array[String], view_context: untyped, authorization_context: untyped) -> void
16
- def initialize: (snapshot: ActorSnapshot, component_name: String, dependencies: Array[String], view_context: untyped, authorization_context: untyped) -> void
13
+ # @rbs (snapshot: ActorSnapshot, registration: ComponentRegistration, view_context: untyped, authorization_context: untyped) -> void
14
+ def initialize: (snapshot: ActorSnapshot, registration: ComponentRegistration, view_context: untyped, authorization_context: untyped) -> void
17
15
 
18
16
  # @rbs () -> untyped
19
17
  def call: () -> untyped
@@ -22,9 +20,7 @@ module SolidObjects
22
20
 
23
21
  attr_reader snapshot: untyped
24
22
 
25
- attr_reader component_name: untyped
26
-
27
- attr_reader dependencies: untyped
23
+ attr_reader registration: untyped
28
24
 
29
25
  attr_reader view_context: untyped
30
26
 
@@ -8,8 +8,18 @@ module SolidObjects
8
8
 
9
9
  MAXIMUM_DEPENDENCIES: ::Integer
10
10
 
11
- # @rbs (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String) -> String
12
- def self?.generate: (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String) -> String
11
+ MAXIMUM_LOCALS: ::Integer
12
+
13
+ MAXIMUM_COMPONENT_KEY_BYTES: ::Integer
14
+
15
+ REFRESH_METHODS: untyped
16
+
17
+ RESERVED_LOCALS: untyped
18
+
19
+ RUBY_KEYWORDS: untyped
20
+
21
+ # @rbs (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String, ?component_key: untyped, ?locals: Hash[untyped, untyped], ?refresh_method: String | Symbol) -> String
22
+ def self?.generate: (reference: Reference, component_name: String, dependencies: Array[String], instance_id: Integer, revision: Integer, refresh_path: String, ?component_key: untyped, ?locals: Hash[untyped, untyped], ?refresh_method: String | Symbol) -> String
13
23
 
14
24
  # @rbs (String) -> Hash[String, untyped]
15
25
  def self?.verify: (String) -> Hash[String, untyped]
@@ -17,12 +27,24 @@ module SolidObjects
17
27
  # @rbs (Hash[String, untyped]) -> Hash[String, untyped]
18
28
  def self?.validate_payload!: (Hash[String, untyped]) -> Hash[String, untyped]
19
29
 
30
+ # @rbs (Hash[String, untyped]?) -> void
31
+ def self?.apply_defaults!: (Hash[String, untyped]?) -> void
32
+
20
33
  # @rbs (String) -> void
21
34
  def self?.validate_component_name!: (String) -> void
22
35
 
36
+ # @rbs (untyped) -> void
37
+ def self?.validate_component_key!: (untyped) -> void
38
+
23
39
  # @rbs (Array[untyped]) -> void
24
40
  def self?.validate_dependencies!: (Array[untyped]) -> void
25
41
 
42
+ # @rbs (Hash[untyped, untyped]) -> void
43
+ def self?.validate_locals!: (Hash[untyped, untyped]) -> void
44
+
45
+ # @rbs (String) -> void
46
+ def self?.validate_refresh_method!: (String) -> void
47
+
26
48
  # @rbs (Integer, Integer) -> void
27
49
  def self?.validate_revision!: (Integer, Integer) -> void
28
50
 
@@ -26,6 +26,15 @@ module SolidObjects
26
26
  # @rbs (untyped) { () -> untyped } -> untyped
27
27
  def with_transaction_deadline: (untyped) { () -> untyped } -> untyped
28
28
 
29
+ # @rbs (untyped) -> Hash[Symbol, untyped]?
30
+ def restorable_busy_wait: (untyped) -> Hash[Symbol, untyped]?
31
+
32
+ # @rbs (untyped, Hash[Symbol, untyped]) -> void
33
+ def restore_busy_wait: (untyped, Hash[Symbol, untyped]) -> void
34
+
35
+ # @rbs (untyped) -> Integer?
36
+ def configured_busy_handler_timeout: (untyped) -> Integer?
37
+
29
38
  # @rbs (Exception) -> bool
30
39
  def deadline_error?: (Exception) -> bool
31
40
 
@@ -78,6 +78,18 @@ module SolidObjects
78
78
  # @rbs () -> Check
79
79
  def check_sync_round_trip: () -> Check
80
80
 
81
+ # @rbs (String, ProcessRegistry) -> Check
82
+ def run_sync_probe: (String, ProcessRegistry) -> Check
83
+
84
+ # @rbs (actor_id: String, probe_registry: ProcessRegistry) -> Array[String]
85
+ def remove_probe_records: (actor_id: String, probe_registry: ProcessRegistry) -> Array[String]
86
+
87
+ # @rbs (String) -> bool
88
+ def delete_probe_actor: (String) -> bool
89
+
90
+ # @rbs (ProcessRegistry) -> bool
91
+ def delete_probe_caller_process: (ProcessRegistry) -> bool
92
+
81
93
  # @rbs (Check, Check) -> bool
82
94
  def ready_for_round_trip?: (Check, Check) -> bool
83
95
 
@@ -8,13 +8,16 @@ module SolidObjects
8
8
  # @rbs (Reference, Symbol | String) -> String
9
9
  def self?.observable: (Reference, Symbol | String) -> String
10
10
 
11
- # @rbs (Reference, Symbol | String) -> String
12
- def self?.component: (Reference, Symbol | String) -> String
11
+ # @rbs (Reference, Symbol | String, ?key: String | Integer?) -> String
12
+ def self?.component: (Reference, Symbol | String, ?key: String | Integer?) -> String
13
13
 
14
14
  # @rbs (Reference) -> String
15
15
  def self?.identity_digest: (Reference) -> String
16
16
 
17
17
  # @rbs (Symbol | String) -> String
18
18
  def self?.normalized_name: (Symbol | String) -> String
19
+
20
+ # @rbs (String | Integer) -> String
21
+ def self?.component_key_digest: (String | Integer) -> String
19
22
  end
20
23
  end
@@ -2,6 +2,11 @@
2
2
 
3
3
  module SolidObjects
4
4
  class SynchronousInvocation
5
+ @dedicated_process_registry: ProcessRegistry?
6
+
7
+ # @rbs (?process_registry: ProcessRegistry?) -> void
8
+ def initialize: (?process_registry: ProcessRegistry?) -> void
9
+
5
10
  # @rbs (MessageReference, timeout: Numeric) -> untyped
6
11
  def call: (MessageReference, timeout: Numeric) -> untyped
7
12
 
@@ -22,6 +27,9 @@ module SolidObjects
22
27
  # @rbs (Message) -> bot
23
28
  def raise_rejection: (Message) -> bot
24
29
 
30
+ # @rbs () -> ProcessRegistry
31
+ def process_registry: () -> ProcessRegistry
32
+
25
33
  # @rbs (Message, deadline: Float) -> Integer
26
34
  def assist: (Message, deadline: Float) -> Integer
27
35