skyfall 0.6.0 → 0.7.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -10
  3. data/LICENSE.txt +1 -1
  4. data/README.md +10 -11
  5. data/lib/skyfall/car_archive.rb +42 -6
  6. data/lib/skyfall/cid.rb +2 -0
  7. data/lib/skyfall/collection.rb +23 -1
  8. data/lib/skyfall/errors.rb +58 -4
  9. data/lib/skyfall/events.rb +19 -0
  10. data/lib/skyfall/extensions.rb +4 -0
  11. data/lib/skyfall/firehose/account_message.rb +32 -4
  12. data/lib/skyfall/firehose/commit_message.rb +45 -5
  13. data/lib/skyfall/firehose/identity_message.rb +30 -2
  14. data/lib/skyfall/firehose/info_message.rb +35 -1
  15. data/lib/skyfall/firehose/labels_message.rb +28 -10
  16. data/lib/skyfall/firehose/message.rb +133 -24
  17. data/lib/skyfall/firehose/operation.rb +57 -5
  18. data/lib/skyfall/firehose/sync_message.rb +31 -0
  19. data/lib/skyfall/firehose/unknown_message.rb +8 -0
  20. data/lib/skyfall/firehose.rb +103 -9
  21. data/lib/skyfall/jetstream/account_message.rb +21 -1
  22. data/lib/skyfall/jetstream/commit_message.rb +35 -2
  23. data/lib/skyfall/jetstream/identity_message.rb +22 -1
  24. data/lib/skyfall/jetstream/message.rb +94 -7
  25. data/lib/skyfall/jetstream/operation.rb +56 -4
  26. data/lib/skyfall/jetstream/unknown_message.rb +8 -0
  27. data/lib/skyfall/jetstream.rb +95 -13
  28. data/lib/skyfall/label.rb +33 -0
  29. data/lib/skyfall/stream.rb +304 -53
  30. data/lib/skyfall/version.rb +1 -1
  31. metadata +9 -14
  32. data/example/block_tracker.rb +0 -84
  33. data/example/jet_monitor_phrases.rb +0 -54
  34. data/example/print_all_posts.rb +0 -34
  35. data/example/push_notifications.rb +0 -262
  36. data/lib/skyfall/firehose/handle_message.rb +0 -14
  37. data/lib/skyfall/firehose/tombstone_message.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 17dd2389545a6bd5c9c3483a89f95e40a62cf2014e263f69d3590cf651920865
4
- data.tar.gz: 9fe379e3d713c50e4a4700941660b529827e2329e200859ddea58b7113da1c3b
3
+ metadata.gz: a3e987d12df25109508c0f673894e9a334a81d60d02f57c118e699de5e68dc4d
4
+ data.tar.gz: 152dfdced7c5fc6414f8cbf04d7cff6d184c592a0ed42463f0b156b373c8c8ac
5
5
  SHA512:
6
- metadata.gz: e9f7c603360a483241cbd6b2af8b49b315149b308c2064332b42d9fa7b27fd302c1b181c6fc7a60871f1dfead09cc3621ed2b9106db2680e0919f12119af881d
7
- data.tar.gz: e8c7da9dc57730ded46de52cc7e500d84129782bd534c2163205330143bb1a5ec0f24feb39a86e440c073972b1c4af3840ce688e9caf55177003d140267c6473
6
+ metadata.gz: 59c9da3c3d60b953f3963c16a86c56bb9e75561696ea47d0b8ce5a3274c00a8d831529f22aacace27df0d1dba55b5dc7db58301f1baa7aa19528756d77eb7ccd
7
+ data.tar.gz: 0575c825184297417f40a5f7248e2560fc24f19913631c23371b1f535feea3d3b38b62b76c0a4c610dafc2e52783ba58521eaf1f3407aa59699d26640fb0f94c
data/CHANGELOG.md CHANGED
@@ -1,6 +1,56 @@
1
+ ## [0.7.0] - 2026-02-13
2
+
3
+ The main change in this version is that inline YARD documentation has been added. This was also a good opportunity to review some APIs and tweak some things in order to get Skyfall a bit closer to 1.0.
4
+
5
+ New APIs:
6
+
7
+ - the `Skyfall::Firehose` initializer now allows skipping `:subscribe_repos`, i.e. `.new(host)` or `.new(host, cursor)`
8
+ - added `Skyfall::Jetstream::CommitMessage#operation` (aliased as `op`) which returns the (always single) operation in the `operations` array
9
+ - added `#kind` as alias for `#type` in both `Message` classes
10
+ - added a base class for error types, `Skyfall::Error`
11
+ - added `#blocks` to `Skyfall::Firehose::SyncMessage`
12
+ - added `#rev`, `#since` and `#prev_data` to `Skyfall::Firehose::CommitMessage`
13
+
14
+ Deprecated & removed APIs:
15
+
16
+ - removed deprecated `HandleMessage` and `TombstoneMessage` message classes
17
+ - removed deprecated `CommitMessage#prev`
18
+ - deprecated `#path` in both `Operation` classes
19
+
20
+ Optimizations:
21
+
22
+ - much faster `Skyfall::Firehose::Message#time` parsing on Ruby 3.2+
23
+ - lazy decoding of sections in `CarArchive` – saves quite a lot of work if sections are only accessed through `Operation#raw_record`
24
+ - added `frozen_string_literal: true` in all files to reduce garbage collection
25
+
26
+ Access level changes:
27
+
28
+ - restricted `Stream#start_heartbeat_timer` & `Stream#stop_heartbeat_timer` methods' access to private
29
+ - restricted `Stream#handle_message` method access to protected
30
+ - restricted `Stream#last_update` to read-only access
31
+ - restricted `#inspectable_variables` access to either private or protected
32
+ - relaxed `Stream#build_websocket_url` & `Stream#build_websocket_client` access from private to protected
33
+ - fixed private class method `Skyfall::Firehose::Message.decode_cbor_objects` which wasn't actually private
34
+
35
+ Additional validations and other changes:
36
+
37
+ - `Stream#connect` throws an error if neither `on_message` nor `on_raw_message` handlers have been configured
38
+ - `Message` subclasses do additional checks if the fields they require to not be nil aren't nil
39
+ - `Message` subclasses raise an error if `.new` is called on a subclass (and not on the base `Message`) passing the data of a wrong kind of message (instead of returning e.g. a `CommitMessage` from `AccountMessage.new` as it worked previously)
40
+ - made `LabelsMessage` a subclass of `Firehose::Message`
41
+ - fixed the `require`s config in some files so they can be loaded in any order
42
+
43
+
44
+ ## [0.6.1] - 2026-01-08
45
+
46
+ - added `:bsky_notif_declaration` shortcode for `app.bsky.notification.declaration` collection
47
+ - throw error when trying to run two streams in one process (see b4a1514f5da28983205765e55724b5c4abe6c5e4 for details)
48
+ - added protected `#send_data` and `#socket` methods in `Stream` for use in `Stream` subclasses (currently for the Tapfall gem)
49
+ - added a way to customize headers sent when connecting in `Stream` subclasses through the `#request_headers` method
50
+
1
51
  ## [0.6.0] - 2025-06-25
2
52
 
3
- - significantly speeded up reading of events from the binary firehose (`Skyfall::Firehose`) - up to 4-5x faster than before
53
+ - significantly speeded up reading of events from the binary firehose (`Skyfall::Firehose`) up to 4-5x faster than before
4
54
  - removed the `Skyfall::Stream.new` constructor deprecated in 0.5.0
5
55
 
6
56
  ## [0.5.1] - 2025-05-18
@@ -23,11 +73,11 @@ This required some breaking changes in the existing API:
23
73
 
24
74
  In most cases, you should only need to update the `Skyfall::Stream` class name in the constructor. If you've referenced message classes like `Skyfall::CommitMessage` directly, it's probably better to just check the `#type` property instead.
25
75
 
26
- Also, small change to the user agent API: `Skyfall::Stream` now has an additional metod `version_string`, which will always return `Skyfall/0.x.y` - it's recommended to use that instead of `default_user_agent` to build your own user agent string that includes the library version. `default_user_agent` now passes through to `version_string`, but it could be changed in future to return something else.
76
+ Also, small change to the user agent API: `Skyfall::Stream` now has an additional metod `version_string`, which will always return `Skyfall/0.x.y` it's recommended to use that instead of `default_user_agent` to build your own user agent string that includes the library version. `default_user_agent` now passes through to `version_string`, but it could be changed in future to return something else.
27
77
 
28
78
  ## [0.4.1] - 2024-10-04
29
79
 
30
- - performance fix - don't decode CAR sections which aren't needed, which is most of them; this cuts the amount of memory that GC has to free up by about one third, and should speed up processing by around ~10%
80
+ - performance fix don't decode CAR sections which aren't needed, which is most of them; this cuts the amount of memory that GC has to free up by about one third, and should speed up processing by around ~10%
31
81
 
32
82
  ## [0.4.0] - 2024-09-23
33
83
 
@@ -41,15 +91,15 @@ Also, small change to the user agent API: `Skyfall::Stream` now has an additiona
41
91
  - added `#account` event type (`AccountMessage`)
42
92
  - added `handle` field to `IdentityMessage`
43
93
  - fixed param validation on `Stream` initialization
44
- - reverted the change that added Ruby stdlib dependencies explicitly to the gemspec, since this causes more problems than it's worth - only `base64` is left there, since it's the one now required to be listed
94
+ - reverted the change that added Ruby stdlib dependencies explicitly to the gemspec, since this causes more problems than it's worth only `base64` is left there, since it's the one now required to be listed
45
95
 
46
96
  ## [0.3.0] - 2024-03-21
47
97
 
48
98
  - added support for labeller firehose, served by labeller services at the `com.atproto.label.subscribeLabels` endpoint (aliased as `:subscribe_labels`)
49
99
  - the `#labels` messages from the labeller firehose are parsed into a `LabelsMessage`, which includes a `labels` array of `Label` objects
50
100
  - `Stream` callbacks can now also be assigned via setters, e.g. `stream.on_message = proc { ... }`
51
- - added default error handler to `Stream` which logs the error to `$stdout` - set `stream.on_error = nil` to disable
52
- - added Ruby stdlib dependencies explicitly to the gemspec - fixes a warning in Ruby 3.3 when requiring `base64`, which will be extracted as an optional gem in 3.4
101
+ - added default error handler to `Stream` which logs the error to `$stdout` set `stream.on_error = nil` to disable
102
+ - added Ruby stdlib dependencies explicitly to the gemspec fixes a warning in Ruby 3.3 when requiring `base64`, which will be extracted as an optional gem in 3.4
53
103
 
54
104
  ## [0.2.5] - 2024-03-14
55
105
 
@@ -76,7 +126,7 @@ Also, small change to the user agent API: `Skyfall::Stream` now has an additiona
76
126
 
77
127
  ## [0.2.1] - 2023-08-19
78
128
 
79
- - optimized `WebsocketMessage` parsing performance - lazy parsing of most properties (message decoding should be over 50% faster on average)
129
+ - optimized `WebsocketMessage` parsing performance lazy parsing of most properties (message decoding should be over 50% faster on average)
80
130
  - added separate subclasses of `WebsocketMessage` for different message types
81
131
  - added support for `#handle`, `#info` and `#tombstone` message types
82
132
  - `UnknownMessage` is returned for unrecognized message types
@@ -84,13 +134,13 @@ Also, small change to the user agent API: `Skyfall::Stream` now has an additiona
84
134
  ## [0.2.0] - 2023-07-24
85
135
 
86
136
  - switched the websocket library from `websocket-client-simple` to `faye-websocket`, which should make event parsing up to ~30× faster (!)
87
- - added `auto_reconnect` property to `Stream` (on by default) - if true, it will try to reconnect with an exponential backoff when the websocket disconnects, until you call `Stream#disconnect`
137
+ - added `auto_reconnect` property to `Stream` (on by default) if true, it will try to reconnect with an exponential backoff when the websocket disconnects, until you call `Stream#disconnect`
88
138
 
89
139
  Note:
90
140
 
91
- - calling `sleep` is no longer needed after connecting - call `connect` on a new thread instead to get previously default behavior of running the event loop asynchronously
141
+ - calling `sleep` is no longer needed after connecting call `connect` on a new thread instead to get previously default behavior of running the event loop asynchronously
92
142
  - the disconnect event no longer passes an error object in the argument
93
- - there is currently no "heartbeat" feature as in 0.1.x that checks for a stuck connection - but it doesn't seem to be needed
143
+ - there is currently no "heartbeat" feature as in 0.1.x that checks for a stuck connection but it doesn't seem to be needed
94
144
 
95
145
  ## [0.1.3] - 2023-07-04
96
146
 
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The zlib License
2
2
 
3
- Copyright (c) 2023-2024 Jakub Suder
3
+ Copyright (c) 2026 Jakub Suder
4
4
 
5
5
  This software is provided 'as-is', without any express or implied
6
6
  warranty. In no event will the authors be held liable for any damages
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  A Ruby gem for streaming data from the Bluesky/ATProto firehose 🦋
4
4
 
5
5
  > [!NOTE]
6
- > ATProto Ruby gems collection: [skyfall](https://github.com/mackuba/skyfall) | [blue_factory](https://github.com/mackuba/blue_factory) | [minisky](https://github.com/mackuba/minisky) | [didkit](https://github.com/mackuba/didkit)
6
+ > Part of ATProto Ruby SDK: [ruby.sdk.blue](https://ruby.sdk.blue)
7
7
 
8
8
 
9
9
  ## What does it do
@@ -15,13 +15,15 @@ Since version 0.5, Skyfall also supports connecting to [Jetstream](https://githu
15
15
 
16
16
  ## Installation
17
17
 
18
- From the command line:
18
+ To use Skyfall, you need a reasonably new version of Ruby – it should run on Ruby 2.6 and above, although it's recommended to use a version that's still getting maintainance updates, i.e. currently 3.2+. A compatible version should be preinstalled on macOS Big Sur and above and on many Linux systems. Otherwise, you can install one using tools such as [RVM](https://rvm.io), [asdf](https://asdf-vm.com), [ruby-install](https://github.com/postmodern/ruby-install) or [ruby-build](https://github.com/rbenv/ruby-build), or `rpm` or `apt-get` on Linux (see more installation options on [ruby-lang.org](https://www.ruby-lang.org/en/downloads/)).
19
19
 
20
- gem install skyfall
20
+ To install the gem, run the command:
21
21
 
22
- Or, add this to your `Gemfile`:
22
+ [sudo] gem install skyfall
23
23
 
24
- gem 'skyfall', '~> 0.5'
24
+ Or add this to your app's `Gemfile`:
25
+
26
+ gem 'skyfall', '~> 0.6'
25
27
 
26
28
 
27
29
  ## Usage
@@ -128,14 +130,11 @@ Each message passed to `on_message` is an instance of a subclass of either `Skyf
128
130
  - `CommitMessage` (`#commit`) - represents a change in a user's repo; most messages are of this type
129
131
  - `IdentityMessage` (`#identity`) - notifies about a change in user's DID document, e.g. a handle change or a migration to a new PDS
130
132
  - `AccountMessage` (`#account`) - notifies about a change of an account's status (de/activation, suspension, deletion)
131
- - `HandleMessage` (`#handle` - deprecated) - when a different handle is assigned to a user's DID
132
- - `TombstoneMessage` (`#tombstone` - deprecated) - when an account is deleted
133
+ - `SyncMessage` (`#sync`) - updates repository state, can be used to trigger account resynchronization
133
134
  - `LabelsMessage` (`#labels`) - only used in `subscribe_labels` endpoint
134
135
  - `InfoMessage` (`#info`) - a protocol error message, e.g. about an invalid cursor parameter
135
136
  - `UnknownMessage` is used for other unrecognized message types
136
137
 
137
- `#handle` and `#tombstone` events are considered deprecated, replaced by `#identity` and `#account` respectively. They are still being emitted at the moment (in parallel with the newer event types), but they might stop being sent at any moment, so it's recommended that you don't rely on those.
138
-
139
138
  `Skyfall::Firehose::Message` and `Skyfall::Jetstream::Message` variants of message classes should have more or less the same interface, except when a given field is not included in one of the formats.
140
139
 
141
140
  All message objects have the following shared properties:
@@ -198,7 +197,7 @@ sky.on_message do |m|
198
197
  end
199
198
  ```
200
199
 
201
- For more examples, see the [example](https://github.com/mackuba/skyfall/blob/master/example) folder or the [bluesky-feeds-rb](https://github.com/mackuba/bluesky-feeds-rb/blob/master/app/firehose_stream.rb) project, which implements a feed generator service.
200
+ For more examples, see the [examples page](https://ruby.sdk.blue/examples/) on [ruby.sdk.blue](https://ruby.sdk.blue), or the [bluesky-feeds-rb](https://tangled.org/mackuba.eu/bluesky-feeds-rb/blob/master/app/firehose_stream.rb) project, which implements a feed generator service.
202
201
 
203
202
 
204
203
  ### Note on custom lexicons
@@ -305,7 +304,7 @@ See [Jetstream docs](https://github.com/bluesky-social/jetstream?tab=readme-ov-f
305
304
 
306
305
  ## Credits
307
306
 
308
- Copyright © 2024 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/mackuba.eu)).
307
+ Copyright © 2026 Kuba Suder ([@mackuba.eu](https://bsky.app/profile/did:plc:oio4hkxaop4ao4wz2pp3f4cr)).
309
308
 
310
309
  The code is available under the terms of the [zlib license](https://choosealicense.com/licenses/zlib/) (permissive, similar to MIT).
311
310
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'cid'
2
4
  require_relative 'errors'
3
5
  require_relative 'extensions'
@@ -30,15 +32,34 @@ module Skyfall
30
32
 
31
33
  def initialize(data)
32
34
  @sections = []
35
+ @buffer = StringIO.new(data)
33
36
 
34
- buffer = StringIO.new(data)
35
- read_header(buffer)
36
- read_section(buffer) until buffer.eof?
37
+ read_header(@buffer)
37
38
  end
38
39
 
39
40
  def section_with_cid(cid)
40
- section = @sections.detect { |s| s.cid == cid }
41
- section && section.body
41
+ if section = @sections.detect { |s| s.cid == cid }
42
+ return section.body
43
+ end
44
+
45
+ if @buffer
46
+ while !@buffer.eof?
47
+ section = read_section(@buffer)
48
+ return section.body if section.cid == cid
49
+ end
50
+ end
51
+
52
+ @buffer = nil
53
+ nil
54
+ end
55
+
56
+ def sections
57
+ if @buffer
58
+ read_section(@buffer) while !@buffer.eof?
59
+ @buffer = nil
60
+ end
61
+
62
+ @sections
42
63
  end
43
64
 
44
65
  def self.convert_data(object)
@@ -75,6 +96,18 @@ module Skyfall
75
96
  { '$bytes' => Base64.encode64(data).chomp.gsub(/=+$/, '') }
76
97
  end
77
98
 
99
+ def inspect
100
+ vars = instance_variables.map { |v|
101
+ if v == :@sections && @buffer
102
+ "#{v}=[...]"
103
+ else
104
+ "#{v}=#{instance_variable_get(v).inspect}"
105
+ end
106
+ }
107
+
108
+ "#<#{self.class}:0x#{object_id} #{vars.join(", ")}>"
109
+ end
110
+
78
111
  private
79
112
 
80
113
  def read_header(buffer)
@@ -116,7 +149,10 @@ module Skyfall
116
149
  cid = CID.new(prefix + cid_data)
117
150
 
118
151
  body_data = sbuffer.read
119
- @sections << CarSection.new(cid, body_data)
152
+ new_section = CarSection.new(cid, body_data)
153
+
154
+ @sections << new_section
155
+ new_section
120
156
  end
121
157
  end
122
158
  end
data/lib/skyfall/cid.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative 'errors'
2
4
 
3
5
  require 'base32'
@@ -1,4 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skyfall
4
+
5
+ #
6
+ # This module defines constants for known Bluesky record collection types, and a mapping of those
7
+ # names to symbol short codes which can be used as shorthand when processing events or in
8
+ # Jetstream filters.
9
+ #
10
+
2
11
  module Collection
3
12
  BSKY_PROFILE = "app.bsky.actor.profile"
4
13
  BSKY_ACTOR_STATUS = "app.bsky.actor.status"
@@ -17,7 +26,10 @@ module Skyfall
17
26
  BSKY_VERIFICATION = "app.bsky.graph.verification"
18
27
  BSKY_LABELER = "app.bsky.labeler.service"
19
28
 
20
- BSKY_CHAT_DECLARATION = "chat.bsky.actor.declaration"
29
+ BSKY_NOTIF_DECLARATION = "app.bsky.notification.declaration"
30
+ BSKY_CHAT_DECLARATION = "chat.bsky.actor.declaration"
31
+
32
+ # Mapping of NSID collection names to symbol short codes
21
33
 
22
34
  SHORT_CODES = {
23
35
  BSKY_ACTOR_STATUS => :bsky_actor_status,
@@ -37,12 +49,22 @@ module Skyfall
37
49
  BSKY_THREADGATE => :bsky_threadgate,
38
50
  BSKY_VERIFICATION => :bsky_verification,
39
51
  BSKY_CHAT_DECLARATION => :bsky_chat_declaration,
52
+ BSKY_NOTIF_DECLARATION => :bsky_notif_declaration
40
53
  }
41
54
 
55
+ # Returns a symbol short code for a given collection NSID, or `:unknown`
56
+ # if NSID is not on the list.
57
+ # @param collection [String] collection NSID
58
+ # @return [Symbol] short code or :unknown
59
+
42
60
  def self.short_code(collection)
43
61
  SHORT_CODES[collection] || :unknown
44
62
  end
45
63
 
64
+ # Returns a collection NSID assigned to a given short code symbol, if one is defined.
65
+ # @param code [Symbol] one of the symbols listed in {SHORT_CODES}
66
+ # @return [String, nil] assigned NSID string, or nil when code is not known
67
+
46
68
  def self.from_short_code(code)
47
69
  SHORT_CODES.detect { |k, v| v == code }&.first
48
70
  end
@@ -1,13 +1,60 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Skyfall
2
- class DecodeError < StandardError
4
+ #
5
+ # Wrapper base class for Skyfall error classes.
6
+ #
7
+ class Error < StandardError
8
+ end
9
+
10
+ #
11
+ # Raised when some code is not configured or configured incorrectly.
12
+ #
13
+ class ConfigError < Error
14
+ end
15
+
16
+ #
17
+ # Raised when some part of the message being decoded has invalid format.
18
+ #
19
+ class DecodeError < Error
3
20
  end
4
21
 
5
- class UnsupportedError < StandardError
22
+ #
23
+ # Raised when {Stream#connect} is called and there's already another instance of {Stream} or its
24
+ # subclass like {Firehose} that's connected to another websocket.
25
+ #
26
+ # This is currently not supported in Skyfall, because it uses EventMachine behind the scenes, which
27
+ # runs everything on a single "reactor" thread, and there can be only one such reactor thread in
28
+ # a given process. In theory, it should be possible for two connections to run inside a single
29
+ # shared EventMachine event loop, but it would require some more coordination and it might have
30
+ # unexpected side effects - e.g. synchronous work (including I/O and network requests) done during
31
+ # processing of an event from one connection would be blocking the other connection.
32
+ #
33
+ class ReactorActiveError < Error
34
+ def initialize
35
+ super(
36
+ "An EventMachine reactor thread is already running, but it seems to have been launched by another Stream. " +
37
+ "Skyfall doesn't currently support running two different Stream instances in a single process."
38
+ )
39
+ end
6
40
  end
7
41
 
8
- class SubscriptionError < StandardError
9
- attr_reader :error_type, :error_message
42
+ #
43
+ # Raised when the server sends a message which is formatted correctly, but describes some kind of
44
+ # error condition that the server has detected.
45
+ #
46
+ class SubscriptionError < Error
47
+
48
+ # @return [String] a short machine-readable error code
49
+ attr_reader :error_type
50
+
51
+ # @return [String] a human-readable error message
52
+ attr_reader :error_message
10
53
 
54
+ #
55
+ # @param error_type [String] a short machine-readable error code
56
+ # @param error_message [String, nil] a human-readable error message
57
+ #
11
58
  def initialize(error_type, error_message = nil)
12
59
  @error_type = error_type
13
60
  @error_message = error_message
@@ -15,4 +62,11 @@ module Skyfall
15
62
  super("Subscription error: #{error_type}" + (error_message ? " (#{error_message})" : ""))
16
63
  end
17
64
  end
65
+
66
+ #
67
+ # Raised when the server sends a message which is formatted correctly, but written in a version
68
+ # that's not supported by this library.
69
+ #
70
+ class UnsupportedError < Error
71
+ end
18
72
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Skyfall
4
+
5
+ # @private
6
+ module Events
7
+ protected
8
+
9
+ def event_handler(name)
10
+ define_method("on_#{name}") do |&block|
11
+ @handlers[name.to_sym] = block
12
+ end
13
+
14
+ define_method("on_#{name}=") do |block|
15
+ @handlers[name.to_sym] = block
16
+ end
17
+ end
18
+ end
19
+ end
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cbor'
2
4
  require 'stringio'
3
5
 
4
6
  module Skyfall
7
+
8
+ # @private
5
9
  module Extensions
6
10
 
7
11
  refine StringIO do
@@ -1,13 +1,41 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../firehose'
4
+ require_relative 'message'
2
5
 
3
6
  module Skyfall
7
+
8
+ #
9
+ # Firehose message sent when the status of an account changes. This can be:
10
+ #
11
+ # - an account being created, sending its initial state (should be active)
12
+ # - an account being deactivated or suspended
13
+ # - an account being restored back to an active state from deactivation/suspension
14
+ # - an account being deleted (the status returning `:deleted`)
15
+ #
16
+
4
17
  class Firehose::AccountMessage < Firehose::Message
5
- def active?
6
- @data_object['active']
18
+
19
+ #
20
+ # @private
21
+ # @param type_object [Hash] first decoded CBOR frame with metadata
22
+ # @param data_object [Hash] second decoded CBOR frame with payload
23
+ # @raise [DecodeError] if the message doesn't include required data
24
+ #
25
+ def initialize(type_object, data_object)
26
+ super
27
+ check_if_not_nil 'seq', 'did', 'time', 'active'
28
+
29
+ @active = @data_object['active']
30
+ @status = @data_object['status']&.to_sym
7
31
  end
8
32
 
9
- def status
10
- @data_object['status']&.to_sym
33
+ # @return [Boolean] true if the account is active, false if it's deactivated/suspended etc.
34
+ def active?
35
+ @active
11
36
  end
37
+
38
+ # @return [Symbol, nil] for inactive accounts, specifies the exact state; nil for active accounts
39
+ attr_reader :status
12
40
  end
13
41
  end
@@ -1,27 +1,67 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../car_archive'
2
4
  require_relative '../cid'
3
5
  require_relative '../firehose'
6
+ require_relative 'message'
4
7
  require_relative 'operation'
5
8
 
6
9
  module Skyfall
10
+
11
+ #
12
+ # Firehose message which includes one or more operations on records in the repo (a record was
13
+ # created, updated or deleted). In most cases this is a single record operation.
14
+ #
15
+ # Most of the messages received from the firehose are of this type, and this is the type you
16
+ # will usually be most interested in.
17
+ #
18
+
7
19
  class Firehose::CommitMessage < Firehose::Message
8
- def commit
9
- @commit ||= @data_object['commit'] && CID.from_cbor_tag(@data_object['commit'])
20
+
21
+ #
22
+ # @private
23
+ # @param type_object [Hash] first decoded CBOR frame with metadata
24
+ # @param data_object [Hash] second decoded CBOR frame with payload
25
+ # @raise [DecodeError] if the message doesn't include required data
26
+ #
27
+ def initialize(type_object, data_object)
28
+ super
29
+ check_if_not_nil 'seq', 'repo', 'commit', 'blocks', 'ops', 'time', 'rev'
30
+ end
31
+
32
+ # @return [String] current revision of the repo
33
+ def rev
34
+ @data_object['rev']
35
+ end
36
+
37
+ # @return [String, nil] revision of the previous commit in the repo
38
+ def since
39
+ @data_object['since']
10
40
  end
11
41
 
12
- def prev
13
- STDERR.puts "Warning: `prev` property has been deprecated and will be removed in a future version."
14
- @prev ||= @data_object['prev'] && CID.from_cbor_tag(@data_object['prev'])
42
+ # @return [CID, nil] CID (Content Identifier) of data of the previous commit in the repo
43
+ def prev_data
44
+ @prev_data ||= CID.from_cbor_tag(@data_object['prevData'])
45
+ end
46
+
47
+ # @return [CID] CID (Content Identifier) of the commit
48
+ def commit
49
+ @commit ||= CID.from_cbor_tag(@data_object['commit'])
15
50
  end
16
51
 
52
+ # @return [Skyfall::CarArchive] commit data in the form of a parsed CAR archive
17
53
  def blocks
18
54
  @blocks ||= CarArchive.new(@data_object['blocks'])
19
55
  end
20
56
 
57
+ # @return [Array<Firehose::Operation>] record operations (usually one) included in the commit
21
58
  def operations
22
59
  @operations ||= @data_object['ops'].map { |op| Firehose::Operation.new(self, op) }
23
60
  end
24
61
 
62
+ # Looks up record data assigned to a given operation in the commit's CAR archive.
63
+ # @param op [Firehose::Operation]
64
+ # @return [Hash, nil]
25
65
  def raw_record_for_operation(op)
26
66
  op.cid && blocks.section_with_cid(op.cid)
27
67
  end
@@ -1,9 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../firehose'
4
+ require_relative 'message'
2
5
 
3
6
  module Skyfall
7
+
8
+ #
9
+ # Firehose message sent when a new DID is created or when the details of someone's DID document
10
+ # are changed (usually either a handle change or a migration to a different PDS). The message
11
+ # may include currently assigned handle, though it's not required that this field is set.
12
+ #
13
+ # Note: the message is originally emitted from the account's PDS and is passed as is by relays,
14
+ # which means you can't fully trust that the handle is actually correctly assigned to the DID
15
+ # and verified by DNS or well-known. To confirm that, use `DID.resolve_handle` from
16
+ # [DIDKit](https://ruby.sdk.blue/didkit/).
17
+ #
18
+
4
19
  class Firehose::IdentityMessage < Firehose::Message
5
- def handle
6
- @data_object['handle']
20
+
21
+ #
22
+ # @private
23
+ # @param type_object [Hash] first decoded CBOR frame with metadata
24
+ # @param data_object [Hash] second decoded CBOR frame with payload
25
+ # @raise [DecodeError] if the message doesn't include required data
26
+ #
27
+ def initialize(type_object, data_object)
28
+ super
29
+ check_if_not_nil 'seq', 'did', 'time'
30
+
31
+ @handle = @data_object['handle']
7
32
  end
33
+
34
+ # @return [String, nil] current handle assigned to the DID
35
+ attr_reader :handle
8
36
  end
9
37
  end
@@ -1,22 +1,56 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require_relative '../firehose'
4
+ require_relative 'message'
2
5
 
3
6
  module Skyfall
7
+
8
+ #
9
+ # An informational firehose message from the websocket service itself, unrelated to any repos.
10
+ #
11
+ # Currently there is only one type of message defined, `"OutdatedCursor"`, which is sent when
12
+ # the client connects with a cursor that is older than the oldest event currently kept in the
13
+ # backfill buffer. This message means that you're likely missing some events that were sent
14
+ # since the last time the client was connected but which were already deleted from the buffer.
15
+ #
16
+ # Note: the {#did}, {#seq} and {#time} properties are always `nil` for `#info` messages.
17
+ #
18
+
4
19
  class Firehose::InfoMessage < Firehose::Message
5
- attr_reader :name, :message
6
20
 
21
+ # @return [String] short machine-readable code of the info message
22
+ attr_reader :name
23
+
24
+ # @return [String, nil] a human-readable description
25
+ attr_reader :message
26
+
27
+ # Message which means that the cursor passed when connecting is older than the oldest event
28
+ # currently kept in the backfill buffer, and that you've likely missed some events that have
29
+ # already been deleted
7
30
  OUTDATED_CURSOR = "OutdatedCursor"
8
31
 
32
+ #
33
+ # @private
34
+ # @param type_object [Hash] first decoded CBOR frame with metadata
35
+ # @param data_object [Hash] second decoded CBOR frame with payload
36
+ # @raise [DecodeError] if the message doesn't include required data
37
+ #
9
38
  def initialize(type_object, data_object)
10
39
  super
40
+ check_if_not_nil 'name'
11
41
 
12
42
  @name = @data_object['name']
13
43
  @message = @data_object['message']
14
44
  end
15
45
 
46
+ # @return [String] a formatted summary
16
47
  def to_s
17
48
  (@name || "InfoMessage") + (@message ? ": #{@message}" : "")
18
49
  end
19
50
 
51
+ protected
52
+
53
+ # @return [Array<Symbol>] list of instance variables to be printed in the {#inspect} output
20
54
  def inspectable_variables
21
55
  super - [:@did, :@seq]
22
56
  end