terminalwire-core 0.3.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c16ed0f8c654d33471f56add7eb3637b8439b2283c41a764b852999a3cb0eb2a
4
+ data.tar.gz: 5bfe86bc41fe0e368739a7997348612fc14ef63c9289a8c7520957a64983e6de
5
+ SHA512:
6
+ metadata.gz: 8ce7a3ca741099a128f01cc3470b99fa16f8b446d60218be22136a4bfa8d03453273e82e72c2765ebf9150728a30c9e3b243fb46e668099ef3425ada7f85680e
7
+ data.tar.gz: 1f25dda033f5402f7ebb0f26384441a0604645868d06006d779d7e9100556023d0c209d257dbdf5372b9ab0ccea1ad4e0dc161e8d119ff28729f33be0ed96298
@@ -0,0 +1,53 @@
1
+ require 'msgpack'
2
+
3
+ module Terminalwire::Adapter
4
+ # Works with Test, TCP, Unix, WebSocket, and other socket-like abstractions.
5
+ class Socket
6
+ include Terminalwire::Logging
7
+
8
+ attr_reader :transport
9
+
10
+ def initialize(transport)
11
+ @transport = transport
12
+ end
13
+
14
+ def write(data)
15
+ logger.debug "Adapter: Sending #{data.inspect}"
16
+ packed_data = MessagePack.pack(data, symbolize_keys: true)
17
+ @transport.write(packed_data)
18
+ end
19
+
20
+ def read
21
+ logger.debug "Adapter: Reading"
22
+ packed_data = @transport.read
23
+ return nil if packed_data.nil?
24
+ data = MessagePack.unpack(packed_data, symbolize_keys: true)
25
+ logger.debug "Adapter: Received #{data.inspect}"
26
+ data
27
+ end
28
+
29
+ def close
30
+ @transport.close
31
+ end
32
+ end
33
+
34
+ # This is a test adapter that can be used for testing purposes.
35
+ class Test
36
+ attr_reader :responses
37
+
38
+ def initialize(responses = [])
39
+ @responses = responses
40
+ end
41
+
42
+ def write(**data)
43
+ @responses << data
44
+ end
45
+
46
+ def response
47
+ @responses.pop
48
+ end
49
+
50
+ def close
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,116 @@
1
+ require "pathname"
2
+ require "msgpack"
3
+ require "base64"
4
+ require "time"
5
+ require "fileutils"
6
+
7
+ # Caches used on the client side for licesens, HTTP requests, etc.
8
+ module Terminalwire::Cache
9
+ module File
10
+ # Hoist the File class to avoid conflicts with the standard library.
11
+ File = ::File
12
+
13
+ class Store
14
+ include Enumerable
15
+
16
+ def initialize(path:)
17
+ @path = Pathname.new(path).expand_path
18
+ FileUtils.mkdir_p(@path) unless @path.directory?
19
+ end
20
+
21
+ def entry(key)
22
+ Entry.new(path: @path.join(Entry.key_path(key)))
23
+ end
24
+ alias :[] :entry
25
+
26
+ def evict
27
+ each(&:evict)
28
+ end
29
+
30
+ def destroy
31
+ each(&:destroy)
32
+ end
33
+
34
+ def each
35
+ @path.each_child do |path|
36
+ yield Entry.new(path:)
37
+ end
38
+ end
39
+ end
40
+
41
+ class Entry
42
+ VERSION = "1.0"
43
+
44
+ def self.key_path(value)
45
+ Base64.urlsafe_encode64(value)
46
+ end
47
+
48
+ attr_accessor :value, :expires
49
+
50
+ def initialize(path:)
51
+ @path = path
52
+ deserialize if persisted?
53
+ end
54
+
55
+ def nil?
56
+ @value.nil?
57
+ end
58
+
59
+ def present?
60
+ not nil?
61
+ end
62
+
63
+ def persisted?
64
+ File.exist? @path
65
+ end
66
+
67
+ def expired?(time: Time.now)
68
+ @expires && @expires < time.utc
69
+ end
70
+
71
+ def fresh?(...)
72
+ not expired?(...)
73
+ end
74
+
75
+ def hit?
76
+ persisted? and fresh?
77
+ end
78
+
79
+ def miss?
80
+ not hit?
81
+ end
82
+
83
+ def save
84
+ File.open(@path, "wb") do |file| # Write in binary mode
85
+ file.write(serialize)
86
+ end
87
+ end
88
+
89
+ def evict
90
+ destroy if expired?
91
+ end
92
+
93
+ def deserialize
94
+ case MessagePack.unpack(File.open(@path, "rb", &:read), symbolize_keys: true)
95
+ in { value:, expires:, version: VERSION }
96
+ @value = value
97
+ @expires = Time.parse(expires).utc if expires
98
+ end
99
+ end
100
+
101
+ def destroy
102
+ File.delete(@path)
103
+ end
104
+
105
+ private
106
+
107
+ def serialize
108
+ MessagePack.pack(
109
+ value: @value,
110
+ expires: @expires&.utc&.iso8601,
111
+ version: VERSION
112
+ )
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,8 @@
1
+ require 'logger'
2
+
3
+ module Terminalwire
4
+ module Logging
5
+ DEVICE = Logger.new($stdout, level: ENV.fetch("TERMINALWIRE_LOG_LEVEL", "info"))
6
+ def logger = DEVICE
7
+ end
8
+ end
@@ -0,0 +1,58 @@
1
+ require 'uri'
2
+ require 'async/websocket/client'
3
+
4
+ module Terminalwire
5
+ module Transport
6
+ class Base
7
+ def self.connect(url)
8
+ raise NotImplementedError, "Subclass must implement .connect"
9
+ end
10
+
11
+ def self.listen(url)
12
+ raise NotImplementedError, "Subclass must implement .listen"
13
+ end
14
+
15
+ def read
16
+ raise NotImplementedError, "Subclass must implement #read"
17
+ end
18
+
19
+ def write(data)
20
+ raise NotImplementedError, "Subclass must implement #write"
21
+ end
22
+
23
+ def close
24
+ raise NotImplementedError, "Subclass must implement #close"
25
+ end
26
+ end
27
+
28
+ class WebSocket < Base
29
+ def self.connect(url)
30
+ uri = URI(url)
31
+ endpoint = Async::HTTP::Endpoint.parse(uri)
32
+ adapter = Async::WebSocket::Client.connect(endpoint)
33
+ new(adapter)
34
+ end
35
+
36
+ def self.listen(url)
37
+ # This would need to be implemented with a WebSocket server library
38
+ raise NotImplementedError, "WebSocket server not implemented"
39
+ end
40
+
41
+ def initialize(websocket)
42
+ @websocket = websocket
43
+ end
44
+
45
+ def read
46
+ @websocket.read&.buffer
47
+ end
48
+
49
+ def write(data)
50
+ @websocket.write(data)
51
+ end
52
+
53
+ def close
54
+ @websocket.close
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Terminalwire
4
+ VERSION = "0.3.0.alpha1"
5
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'terminalwire'
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "terminalwire/version"
4
+
5
+ require 'forwardable'
6
+ require 'uri'
7
+
8
+ require 'async'
9
+ require 'async/http/endpoint'
10
+ require 'async/websocket/client'
11
+ require 'async/websocket/adapters/rack'
12
+ require 'uri-builder'
13
+
14
+ require "zeitwerk"
15
+ Zeitwerk::Loader.for_gem.tap do |loader|
16
+ loader.ignore File.join(__dir__, "terminalwire-core.rb")
17
+ loader.setup
18
+ end
19
+
20
+ module Terminalwire
21
+ class Error < StandardError; end
22
+
23
+ # Used by Terminalwire client to connect to Terminalire.com for license
24
+ # validations, etc.
25
+ TERMINALWIRE_URL = "https://terminalwire.com".freeze
26
+ def self.url = URI.build(TERMINALWIRE_URL)
27
+
28
+ module Resource
29
+ class Base
30
+ attr_reader :name, :adapter
31
+
32
+ def initialize(name, adapter)
33
+ @name = name.to_s
34
+ @adapter = adapter
35
+ end
36
+
37
+ def connect; end
38
+ def disconnect; end
39
+
40
+ def fail(response, **data)
41
+ respond(status: "failure", response:, **data)
42
+ end
43
+
44
+ def succeed(response, **data)
45
+ respond(status: "success", response:, **data)
46
+ end
47
+
48
+ private
49
+
50
+ def respond(**response)
51
+ adapter.write(event: "resource", name: @name, **response)
52
+ end
53
+ end
54
+ end
55
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: terminalwire-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0.alpha1
5
+ platform: ruby
6
+ authors:
7
+ - Brad Gessler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: async-websocket
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.30'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.30'
27
+ - !ruby/object:Gem::Dependency
28
+ name: zeitwerk
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: msgpack
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.7'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.7'
55
+ - !ruby/object:Gem::Dependency
56
+ name: uri-builder
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.1.9
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.1.9
69
+ - !ruby/object:Gem::Dependency
70
+ name: base64
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.2.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '13.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '13.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ description: Stream command-line apps from your server without a web API
112
+ email:
113
+ - brad@terminalwire.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - lib/terminalwire-core.rb
119
+ - lib/terminalwire.rb
120
+ - lib/terminalwire/adapter.rb
121
+ - lib/terminalwire/cache.rb
122
+ - lib/terminalwire/logging.rb
123
+ - lib/terminalwire/transport.rb
124
+ - lib/terminalwire/version.rb
125
+ homepage: https://terminalwire.com/ruby
126
+ licenses:
127
+ - Proprietary (https://terminalwire.com/license)
128
+ metadata:
129
+ allowed_push_host: https://rubygems.org/
130
+ homepage_uri: https://terminalwire.com/ruby
131
+ source_code_uri: https://github.com/terminalwire/ruby
132
+ changelog_uri: https://github.com/terminalwire/ruby/tags
133
+ funding_uri: https://terminalwire.com/funding
134
+ post_install_message:
135
+ rdoc_options: []
136
+ require_paths:
137
+ - lib
138
+ required_ruby_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: 3.0.0
143
+ required_rubygems_version: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ requirements: []
149
+ rubygems_version: 3.5.9
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Ship a CLI for your web app. No API required.
153
+ test_files: []