envless-sdk 0.0.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 (91) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +24 -0
  3. data/README.md +171 -0
  4. data/lib/envless/sdk/api_error.rb +54 -0
  5. data/lib/envless/sdk/bodies.rb +49 -0
  6. data/lib/envless/sdk/client.rb +39 -0
  7. data/lib/envless/sdk/clock.rb +24 -0
  8. data/lib/envless/sdk/connection_pool.rb +83 -0
  9. data/lib/envless/sdk/connector.rb +26 -0
  10. data/lib/envless/sdk/constants.rb +262 -0
  11. data/lib/envless/sdk/crypto/cipher.rb +58 -0
  12. data/lib/envless/sdk/crypto/codec.rb +23 -0
  13. data/lib/envless/sdk/crypto/envelope.rb +41 -0
  14. data/lib/envless/sdk/crypto/keys.rb +42 -0
  15. data/lib/envless/sdk/crypto/passphrase.rb +38 -0
  16. data/lib/envless/sdk/crypto/passphrase_rules.rb +42 -0
  17. data/lib/envless/sdk/crypto/values.rb +31 -0
  18. data/lib/envless/sdk/crypto/workspace_key.rb +40 -0
  19. data/lib/envless/sdk/crypto_messages.rb +31 -0
  20. data/lib/envless/sdk/default_client.rb +55 -0
  21. data/lib/envless/sdk/defaults.rb +32 -0
  22. data/lib/envless/sdk/envelope.rb +48 -0
  23. data/lib/envless/sdk/environment.rb +42 -0
  24. data/lib/envless/sdk/errors.rb +23 -0
  25. data/lib/envless/sdk/exchange.rb +27 -0
  26. data/lib/envless/sdk/helpers.rb +49 -0
  27. data/lib/envless/sdk/http.rb +46 -0
  28. data/lib/envless/sdk/listing.rb +54 -0
  29. data/lib/envless/sdk/messages.rb +43 -0
  30. data/lib/envless/sdk/net_http_adapter.rb +84 -0
  31. data/lib/envless/sdk/numbers.rb +44 -0
  32. data/lib/envless/sdk/options.rb +56 -0
  33. data/lib/envless/sdk/pagination.rb +31 -0
  34. data/lib/envless/sdk/paths.rb +70 -0
  35. data/lib/envless/sdk/received.rb +28 -0
  36. data/lib/envless/sdk/redirects.rb +56 -0
  37. data/lib/envless/sdk/resource.rb +50 -0
  38. data/lib/envless/sdk/resources/environments.rb +61 -0
  39. data/lib/envless/sdk/resources/invites.rb +28 -0
  40. data/lib/envless/sdk/resources/keys.rb +36 -0
  41. data/lib/envless/sdk/resources/me.rb +11 -0
  42. data/lib/envless/sdk/resources/members.rb +36 -0
  43. data/lib/envless/sdk/resources/product_access.rb +30 -0
  44. data/lib/envless/sdk/resources/products.rb +62 -0
  45. data/lib/envless/sdk/resources/projects.rb +42 -0
  46. data/lib/envless/sdk/resources/roles.rb +40 -0
  47. data/lib/envless/sdk/resources/variable_changes.rb +57 -0
  48. data/lib/envless/sdk/resources/variables.rb +49 -0
  49. data/lib/envless/sdk/resources/versions.rb +60 -0
  50. data/lib/envless/sdk/resources/webhook_deliveries.rb +33 -0
  51. data/lib/envless/sdk/resources/webhooks.rb +44 -0
  52. data/lib/envless/sdk/resources/workspace.rb +26 -0
  53. data/lib/envless/sdk/resources/workspace_domain.rb +31 -0
  54. data/lib/envless/sdk/resources/workspaces.rb +24 -0
  55. data/lib/envless/sdk/results.rb +43 -0
  56. data/lib/envless/sdk/retry_policy.rb +36 -0
  57. data/lib/envless/sdk/rotation/entry_rotator.rb +61 -0
  58. data/lib/envless/sdk/rotation/key_ring.rb +34 -0
  59. data/lib/envless/sdk/rotation/ledger.rb +36 -0
  60. data/lib/envless/sdk/rotation/runner.rb +45 -0
  61. data/lib/envless/sdk/rotation/variable_rotator.rb +38 -0
  62. data/lib/envless/sdk/text.rb +71 -0
  63. data/lib/envless/sdk/transport.rb +62 -0
  64. data/lib/envless/sdk/update_notice.rb +66 -0
  65. data/lib/envless/sdk/urls.rb +70 -0
  66. data/lib/envless/sdk/variable_names.rb +55 -0
  67. data/lib/envless/sdk/version.rb +5 -0
  68. data/lib/envless/sdk/webhooks/headers.rb +49 -0
  69. data/lib/envless/sdk/webhooks/verify.rb +58 -0
  70. data/lib/envless/sdk/wire.rb +77 -0
  71. data/lib/envless/sdk.rb +77 -0
  72. data/sig/envless/sdk/adapter.rbs +60 -0
  73. data/sig/envless/sdk/client.rbs +31 -0
  74. data/sig/envless/sdk/crypto.rbs +92 -0
  75. data/sig/envless/sdk/errors.rbs +37 -0
  76. data/sig/envless/sdk/http.rbs +31 -0
  77. data/sig/envless/sdk/pagination.rbs +81 -0
  78. data/sig/envless/sdk/results.rbs +62 -0
  79. data/sig/envless/sdk/rotation.rbs +86 -0
  80. data/sig/envless/sdk/settings.rbs +81 -0
  81. data/sig/envless/sdk/stdlib.rbs +8 -0
  82. data/sig/envless/sdk/text.rbs +52 -0
  83. data/sig/envless/sdk/transport.rbs +79 -0
  84. data/sig/envless/sdk/urls.rbs +73 -0
  85. data/sig/envless/sdk/webhooks.rbs +23 -0
  86. data/sig/envless/sdk.rbs +36 -0
  87. data/sig/generated/constants.rbs +57 -0
  88. data/sig/generated/resources.rbs +209 -0
  89. data/sig/generated/types.rbs +466 -0
  90. data/sig/manifest.yaml +3 -0
  91. metadata +134 -0
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Envless
4
+ module Urls
5
+ SCHEMES = %w[http https].freeze
6
+ TRAILING_SLASHES = %r{/+\z}
7
+
8
+ module_function
9
+
10
+ def valid_base_url(value)
11
+ text = value.is_a?(URI::Generic) ? value.to_s : value
12
+ raise ArgumentError, "#{value.inspect} #{Messages::BASE_URL_SHAPE}" unless text.is_a?(String)
13
+
14
+ trimmed = Text.js_trim(text).sub(TRAILING_SLASHES, "")
15
+ uri = parse(trimmed)
16
+ raise ArgumentError, "#{value.inspect} #{Messages::BASE_URL_SHAPE}" unless usable?(uri)
17
+ raise ArgumentError, Messages::BASE_URL_USERINFO unless uri&.userinfo.nil?
18
+
19
+ trimmed
20
+ end
21
+
22
+ def usable?(uri)
23
+ return false if uri.nil?
24
+
25
+ SCHEMES.include?(uri.scheme.to_s.downcase) && !uri.host.to_s.empty? && uri.query.nil? && uri.fragment.nil?
26
+ end
27
+
28
+ def parse(text)
29
+ URI.parse(text)
30
+ rescue URI::InvalidURIError
31
+ nil
32
+ end
33
+
34
+ def api_url(base_url, path)
35
+ unless path.is_a?(String) && path.start_with?("/") && !path.start_with?("//", "/\\")
36
+ raise ArgumentError, "#{path.inspect} #{Messages::PATH_SHAPE}"
37
+ end
38
+
39
+ "#{base_url}#{path}"
40
+ end
41
+
42
+ def build_url(base_url, path, query = nil)
43
+ url = api_url(base_url, path)
44
+ pairs = (query || {}).filter_map do |key, value|
45
+ text = value.nil? ? "" : query_value(value)
46
+ "#{form(key.to_s)}=#{form(text)}" unless text.empty?
47
+ end
48
+
49
+ pairs.empty? ? url : "#{url}?#{pairs.join("&")}"
50
+ end
51
+
52
+ def query_value(value)
53
+ case value
54
+ when String, Symbol then value.to_s
55
+ when true, false, Integer then value.to_s
56
+ when Float then Numbers.text(value)
57
+ else raise ArgumentError, format(Messages::QUERY_SHAPE, kind: value.class)
58
+ end
59
+ end
60
+
61
+ def form(text)
62
+ URI.encode_www_form_component(Text.utf8(text))
63
+ end
64
+
65
+ def origin(url)
66
+ uri = URI.parse(url)
67
+ [uri.scheme.to_s.downcase, uri.host.to_s.downcase, uri.port]
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Envless
4
+ module VariableNames
5
+ LIMITS = Constants::INPUT_LIMITS[:VARIABLE_NAME]
6
+ ERRORS = Constants::VARIABLE_NAME_ERRORS
7
+ REFUSAL = Constants::LOCAL_REFUSAL
8
+
9
+ module_function
10
+
11
+ def validation(name)
12
+ text = (name.is_a?(String) || name.is_a?(Symbol)) ? Text.utf8(name.to_s) : nil
13
+ return refused(ERRORS[:REQUIRED]) if text.nil? || Text.utf16_length(text) < LIMITS[:MIN]
14
+ return refused(ERRORS[:TOO_LONG].sub("{{count}}", LIMITS[:MAX].to_s)) if Text.utf16_length(text) > LIMITS[:MAX]
15
+ return refused(ERRORS[:SHAPE]) unless Constants::ENV_KEY_PATTERN.match?(text)
16
+
17
+ Validation.new(valid: true, error: nil)
18
+ end
19
+
20
+ def refused(error)
21
+ Validation.new(valid: false, error:)
22
+ end
23
+
24
+ def assert(name)
25
+ check = validation(name)
26
+ return if check.valid?
27
+
28
+ raise ApiError.new("#{check.error} Received: #{received(name)}", status: REFUSAL[:STATUS],
29
+ code: Constants::ERROR_CODES[:BODY_INVALID], resource: REFUSAL[:VARIABLE_RESOURCE], field: REFUSAL[:NAME_FIELD])
30
+ end
31
+
32
+ def received(name)
33
+ return "\"#{name}\"" if name.is_a?(String) || name.is_a?(Symbol)
34
+
35
+ name.nil? ? "nil" : name.to_s
36
+ end
37
+
38
+ def assert_input(body)
39
+ assert(Bodies.field(body, "name"))
40
+ end
41
+
42
+ def assert_patch(patch)
43
+ assert(Bodies.field(patch, "name")) if Bodies.field?(patch, "name")
44
+ end
45
+
46
+ def assert_inputs(items)
47
+ items.each { |item| assert_input(item) }
48
+ end
49
+
50
+ def assert_changes(changes)
51
+ creates = Bodies.field(changes, "creates")
52
+ assert_inputs(Bodies.list(creates, "creates")) if Wire.truthy?(creates)
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Envless
4
+ VERSION = "0.0.1"
5
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Envless
4
+ module Webhooks
5
+ module Headers
6
+ SEPARATOR = / /
7
+
8
+ module_function
9
+
10
+ def read(headers, name)
11
+ value = (headers.respond_to?(:each_pair) || headers.is_a?(Array)) ? scan(headers, name) : lookup(headers, name)
12
+ value = value.first if value.is_a?(Array)
13
+ value.nil? ? "" : Text.utf8(value.to_s)
14
+ end
15
+
16
+ def scan(headers, name)
17
+ wanted = name.downcase
18
+ rack = "HTTP_#{name.upcase.tr("-", "_")}"
19
+ pairs = headers.is_a?(Array) ? headers : headers.each_pair
20
+
21
+ pairs.each do |pair|
22
+ key, value = pair
23
+ return value if key.to_s.downcase == wanted || key.to_s == rack
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ def lookup(headers, name)
30
+ return nil if headers.nil? || headers.is_a?(Numeric) || headers.is_a?(String) || !headers.respond_to?(:[])
31
+
32
+ headers[name]
33
+ rescue ArgumentError, TypeError, NoMethodError
34
+ nil
35
+ end
36
+
37
+ def candidates(provided)
38
+ provided.split(SEPARATOR, -1).filter_map do |part|
39
+ version, candidate = part.split(Constants::WEBHOOK_SIGNATURE[:SEPARATOR], -1)
40
+ candidate.to_s if version == Constants::WEBHOOK_SIGNATURE[:VERSION]
41
+ end
42
+ end
43
+
44
+ def same?(expected, candidate)
45
+ candidate.bytesize == expected.bytesize && OpenSSL.fixed_length_secure_compare(expected, candidate)
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Envless
4
+ module Webhooks
5
+ module Verify
6
+ HEADERS = Constants::WEBHOOK_HEADERS
7
+ MESSAGES = CryptoMessages
8
+
9
+ module_function
10
+
11
+ def call(payload:, headers:, secret:, tolerance_seconds: nil)
12
+ raise ArgumentError, MESSAGES::WEBHOOK_PAYLOAD_SHAPE unless payload.is_a?(String)
13
+
14
+ tolerance = tolerance_of(tolerance_seconds)
15
+ id = Headers.read(headers, HEADERS[:ID])
16
+ timestamp = Headers.read(headers, HEADERS[:TIMESTAMP])
17
+ provided = Headers.read(headers, HEADERS[:SIGNATURE])
18
+ raise WebhookVerificationError, MESSAGES::WEBHOOK_MISSING_HEADERS if id.empty? || timestamp.empty? || provided.empty?
19
+
20
+ fresh!(timestamp, tolerance)
21
+ raise WebhookVerificationError, MESSAGES::WEBHOOK_MISSING_SECRET unless secret.is_a?(String) && !secret.empty?
22
+
23
+ body = payload.b
24
+ expected = Crypto::Codec.to_base64(OpenSSL::HMAC.digest("SHA256", Text.utf8(secret), "#{id}.#{timestamp}.".b + body))
25
+ matched = Headers.candidates(provided).any? { |candidate| Headers.same?(expected, candidate) }
26
+ raise WebhookVerificationError, MESSAGES::WEBHOOK_MISMATCH unless matched
27
+
28
+ parsed(body)
29
+ end
30
+
31
+ def tolerance_of(value)
32
+ return Constants::WEBHOOK_SIGNATURE[:TOLERANCE_SECONDS] if value.nil?
33
+
34
+ number = value.is_a?(Integer) || value.is_a?(Float) || value.is_a?(Rational)
35
+ return value if number && value.finite? && value >= 0
36
+
37
+ raise ArgumentError, MESSAGES::WEBHOOK_TOLERANCE_SHAPE
38
+ end
39
+
40
+ def fresh!(timestamp, tolerance)
41
+ seconds = Numbers.js_number(timestamp)
42
+ raise WebhookVerificationError, MESSAGES::WEBHOOK_TIMESTAMP_SHAPE unless seconds.finite?
43
+
44
+ drift = (Time.now.to_i - seconds).abs
45
+ return unless drift > tolerance
46
+
47
+ raise WebhookVerificationError, format(MESSAGES::WEBHOOK_OUT_OF_TOLERANCE, drift: Numbers.text(drift),
48
+ tolerance: Numbers.text(tolerance))
49
+ end
50
+
51
+ def parsed(body)
52
+ Wire.decode(Text.utf8(body))
53
+ rescue JSON::ParserError
54
+ raise WebhookVerificationError, MESSAGES::WEBHOOK_NOT_JSON
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Envless
4
+ module Wire
5
+ LONE_SURROGATE = /\\u(d[89ab]\h\h)(\\u(d[c-f]\h\h))?|\\u(d[c-f]\h\h)|\\./i
6
+ SURROGATE_ESCAPE = /\\u[dD][89a-fA-F]/
7
+ REPLACEMENT_ESCAPE = "\\ufffd"
8
+ ISO_INSTANT = "%Y-%m-%dT%H:%M:%S.%LZ"
9
+ MAX_DEPTH = 100
10
+
11
+ module_function
12
+
13
+ def encode(value)
14
+ JSON.generate(ready(value), max_nesting: false)
15
+ end
16
+
17
+ def ready(value, depth = 0)
18
+ raise ArgumentError, Messages::JSON_DEPTH if depth > MAX_DEPTH
19
+
20
+ case value
21
+ when Hash then value.to_h { |key, item| [key_text(key), ready(item, depth + 1)] }
22
+ when Array, Set then value.map { |item| ready(item, depth + 1) }
23
+ when String, Symbol then Text.utf8(value.to_s)
24
+ when Integer, true, false, nil then value
25
+ when Float then finite(value)
26
+ else instant(value) || ready(structure(value), depth + 1)
27
+ end
28
+ end
29
+
30
+ def key_text(key)
31
+ return Text.utf8(key.to_s) if key.is_a?(String) || key.is_a?(Symbol) || key.is_a?(Integer)
32
+
33
+ raise ArgumentError, format(Messages::JSON_KEY_SHAPE, kind: key.class)
34
+ end
35
+
36
+ def finite(value)
37
+ return value if value.finite?
38
+
39
+ raise ArgumentError, format(Messages::NOT_FINITE, value:)
40
+ end
41
+
42
+ def instant(value)
43
+ return value.getutc.strftime(ISO_INSTANT) if value.is_a?(Time)
44
+ return value.new_offset("+00:00").strftime(ISO_INSTANT) if value.is_a?(DateTime)
45
+
46
+ value.iso8601 if value.is_a?(Date)
47
+ end
48
+
49
+ def structure(value)
50
+ return value.to_hash if value.respond_to?(:to_hash)
51
+ return value.to_h if value.is_a?(Struct) || value.is_a?(Data)
52
+
53
+ raise ArgumentError, format(Messages::JSON_SHAPE, kind: value.class)
54
+ end
55
+
56
+ def decode(text)
57
+ JSON.parse(SURROGATE_ESCAPE.match?(text) ? without_lone_surrogates(text) : text, symbolize_names: true)
58
+ end
59
+
60
+ def without_lone_surrogates(text)
61
+ text.gsub(LONE_SURROGATE) do |escape|
62
+ high, pair, _low, lone_low = Regexp.last_match&.captures
63
+ next escape if high.nil? && lone_low.nil?
64
+ next escape if high && pair
65
+
66
+ REPLACEMENT_ESCAPE
67
+ end
68
+ end
69
+
70
+ def truthy?(value)
71
+ return false if value.nil? || value == false || value == ""
72
+ return !value.zero? && !(value.is_a?(Float) && value.nan?) if value.is_a?(Numeric)
73
+
74
+ true
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "json"
5
+ require "net/http"
6
+ require "openssl"
7
+ require "timeout"
8
+ require "uri"
9
+ require "zlib"
10
+
11
+ require_relative "sdk/version"
12
+ require_relative "sdk/constants"
13
+ require_relative "sdk/defaults"
14
+ require_relative "sdk/messages"
15
+ require_relative "sdk/crypto_messages"
16
+ require_relative "sdk/errors"
17
+ require_relative "sdk/api_error"
18
+ require_relative "sdk/text"
19
+ require_relative "sdk/numbers"
20
+ require_relative "sdk/wire"
21
+ require_relative "sdk/results"
22
+ require_relative "sdk/http"
23
+ require_relative "sdk/clock"
24
+ require_relative "sdk/options"
25
+ require_relative "sdk/urls"
26
+ require_relative "sdk/paths"
27
+ require_relative "sdk/environment"
28
+ require_relative "sdk/envelope"
29
+ require_relative "sdk/retry_policy"
30
+ require_relative "sdk/redirects"
31
+ require_relative "sdk/exchange"
32
+ require_relative "sdk/connector"
33
+ require_relative "sdk/connection_pool"
34
+ require_relative "sdk/received"
35
+ require_relative "sdk/net_http_adapter"
36
+ require_relative "sdk/update_notice"
37
+ require_relative "sdk/transport"
38
+ require_relative "sdk/listing"
39
+ require_relative "sdk/pagination"
40
+ require_relative "sdk/bodies"
41
+ require_relative "sdk/variable_names"
42
+ require_relative "sdk/resource"
43
+ require_relative "sdk/resources/me"
44
+ require_relative "sdk/resources/workspace_domain"
45
+ require_relative "sdk/resources/workspace"
46
+ require_relative "sdk/resources/workspaces"
47
+ require_relative "sdk/resources/product_access"
48
+ require_relative "sdk/resources/products"
49
+ require_relative "sdk/resources/projects"
50
+ require_relative "sdk/resources/environments"
51
+ require_relative "sdk/resources/variables"
52
+ require_relative "sdk/resources/variable_changes"
53
+ require_relative "sdk/resources/versions"
54
+ require_relative "sdk/resources/members"
55
+ require_relative "sdk/resources/invites"
56
+ require_relative "sdk/resources/roles"
57
+ require_relative "sdk/resources/keys"
58
+ require_relative "sdk/resources/webhooks"
59
+ require_relative "sdk/resources/webhook_deliveries"
60
+ require_relative "sdk/client"
61
+ require_relative "sdk/default_client"
62
+ require_relative "sdk/crypto/codec"
63
+ require_relative "sdk/crypto/envelope"
64
+ require_relative "sdk/crypto/workspace_key"
65
+ require_relative "sdk/crypto/keys"
66
+ require_relative "sdk/crypto/cipher"
67
+ require_relative "sdk/crypto/values"
68
+ require_relative "sdk/crypto/passphrase_rules"
69
+ require_relative "sdk/crypto/passphrase"
70
+ require_relative "sdk/rotation/key_ring"
71
+ require_relative "sdk/rotation/ledger"
72
+ require_relative "sdk/rotation/variable_rotator"
73
+ require_relative "sdk/rotation/entry_rotator"
74
+ require_relative "sdk/rotation/runner"
75
+ require_relative "sdk/webhooks/headers"
76
+ require_relative "sdk/webhooks/verify"
77
+ require_relative "sdk/helpers"
@@ -0,0 +1,60 @@
1
+ module Envless
2
+ module Connector
3
+ def self?.open: (URI::Generic uri, Float? timeout, Integer | Float keep_alive_timeout) -> Net::HTTP
4
+ def self?.decoded: (String? text) -> String?
5
+ end
6
+
7
+ class ConnectionPool
8
+ CLOSE_ERRORS: Array[singleton(Exception)]
9
+
10
+ @max_idle: Integer
11
+ @keep_alive_timeout: Integer | Float
12
+ @idle: Hash[[String, String, Integer?], Array[Net::HTTP]]
13
+ @lock: Thread::Mutex
14
+ @pid: Integer
15
+
16
+ def initialize: (max_idle: Integer, keep_alive_timeout: Integer | Float) -> void
17
+ def checkout: (URI::Generic uri, Float? timeout) -> [Net::HTTP, bool]
18
+ def checkin: (URI::Generic uri, Net::HTTP connection) -> void
19
+ def discard: (Net::HTTP connection) -> void
20
+ def close: () -> nil
21
+
22
+ private
23
+
24
+ def take: (URI::Generic uri) -> Net::HTTP?
25
+ def usable?: (Net::HTTP connection) -> bool
26
+ def forget_inherited: () -> void
27
+ def key: (URI::Generic uri) -> [String, String, Integer?]
28
+ end
29
+
30
+ class Received
31
+ @body: String
32
+
33
+ attr_accessor head: Net::HTTPResponse?
34
+
35
+ def initialize: () -> void
36
+ def append: (String chunk) -> String
37
+ def truncate: () -> String
38
+ def response: () -> HttpResponse
39
+ end
40
+
41
+ class NetHttpAdapter
42
+ STALE_ERRORS: Array[singleton(Exception)]
43
+ BODY_ERRORS: Array[singleton(Exception)]
44
+ EMPTY_WRITES: Array[String]
45
+
46
+ @pool: ConnectionPool
47
+
48
+ def initialize: (?max_idle: Integer, ?keep_alive_timeout: Integer | Float) -> void
49
+ def call: (HttpRequest request) -> HttpResponse
50
+ def close: () -> nil
51
+ def inspect: () -> String
52
+
53
+ private
54
+
55
+ def attempt: (URI::HTTP uri, HttpRequest request, Float? deadline, bool retried) -> HttpResponse
56
+ def transfer: (Net::HTTP connection, Net::HTTPGenericRequest outbound, Received received, Float? deadline) -> bool
57
+ def outbound: (URI::HTTP uri, HttpRequest request) -> Net::HTTPGenericRequest
58
+ def limit: (Net::HTTP connection, Float? deadline) -> void
59
+ end
60
+ end
@@ -0,0 +1,31 @@
1
+ module Envless
2
+ class Client
3
+ attr_reader raw: Transport
4
+
5
+ def initialize: (?String? token_argument, ?token: String?, ?base_url: (String | URI::Generic)?, ?adapter: _Adapter?, ?max_retries: Integer?, ?timeout: seconds?, ?user_agent: String?, ?disable_update_notice: bool) -> void
6
+ def close: () -> nil
7
+ def inspect: () -> String
8
+ end
9
+
10
+ module Resources
11
+ class Workspace < Resource
12
+ @domain: WorkspaceDomain
13
+ end
14
+
15
+ class WorkspaceDomain < Resource
16
+ private
17
+
18
+ def domain_of: (untyped data) -> custom_domain_resource?
19
+ end
20
+
21
+ class Products < Resource
22
+ @access: ProductAccess
23
+ end
24
+
25
+ class Environments < Resource
26
+ private
27
+
28
+ def set_exposed: (String project, String environment, bool exposed, (String | Symbol)? product, seconds? timeout) -> environment_resource
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,92 @@
1
+ module Envless
2
+ class WorkspaceKey
3
+ BITS: Integer
4
+ BYTES: Integer
5
+
6
+ @raw: String
7
+
8
+ def initialize: (String raw) -> void
9
+ def to_bytes: () -> String
10
+ def ==: (untyped other) -> bool
11
+ def eql?: (untyped other) -> bool
12
+ def hash: () -> Integer
13
+ def inspect: () -> String
14
+ def to_s: () -> String
15
+ end
16
+
17
+ module Crypto
18
+ type key = WorkspaceKey | String
19
+
20
+ class CiphertextEnvelope < Data
21
+ attr_reader payload: String
22
+ attr_reader iterations: Integer?
23
+
24
+ def self.new: (String payload, Integer? iterations) -> CiphertextEnvelope
25
+ | (payload: String, iterations: Integer?) -> CiphertextEnvelope
26
+ end
27
+
28
+ module Codec
29
+ def self?.to_base64: (String bytes) -> String
30
+ def self?.from_base64: (String value) -> String
31
+ def self?.from_base64_url: (String value) -> String
32
+ end
33
+
34
+ module Envelope
35
+ CRYPTO: untyped
36
+ V1: String
37
+ V2: String
38
+ UNSUPPORTED: String
39
+
40
+ def self?.parse: (untyped encoded) -> CiphertextEnvelope
41
+ def self?.versioned: (String rest) -> CiphertextEnvelope
42
+ def self?.iterations: (String text) -> Integer
43
+ end
44
+
45
+ module Keys
46
+ CRYPTO: untyped
47
+ DIGEST: String
48
+
49
+ def self?.key_of: (untyped key) -> WorkspaceKey
50
+ def self?.workspace_salt: (String workspace_unique_id) -> String
51
+ def self?.derive_key: (untyped passphrase, String salt, ?Integer iterations) -> WorkspaceKey
52
+ def self?.derive_workspace_key: (untyped passphrase, untyped workspace_unique_id) -> WorkspaceKey
53
+ def self?.import_workspace_key: (untyped value) -> WorkspaceKey
54
+ def self?.text: (untyped value, String name) -> String
55
+ end
56
+
57
+ module Cipher
58
+ CRYPTO: untyped
59
+ ALGORITHM: String
60
+ TAG_BYTES: Integer
61
+
62
+ def self?.encrypt_with_key: (untyped key, untyped plaintext) -> String
63
+ def self?.decrypt: (untyped key, untyped encoded) -> String
64
+ def self?.unseal: (WorkspaceKey key, String iv, String sealed) -> String
65
+ def self?.decrypt_with_key: (untyped key, untyped ciphertext) -> String
66
+ end
67
+
68
+ module Values
69
+ def self?.encrypt_value: (untyped plaintext, untyped passphrase, untyped workspace_unique_id) -> String
70
+ def self?.decrypt_value: (untyped ciphertext, untyped passphrase, untyped workspace_unique_id) -> String
71
+ def self?.ciphertext?: (untyped value) -> bool
72
+ end
73
+
74
+ module Passphrase
75
+ RULES: untyped
76
+ ERRORS: untyped
77
+
78
+ def self?.strength: (untyped passphrase) -> Validation
79
+ def self?.refused: (String error) -> Validation
80
+ def self?.with_count: (String message, Integer count) -> String
81
+ end
82
+
83
+ module PassphraseRules
84
+ RULES: untyped
85
+ NOT_ALPHANUMERIC: Regexp
86
+
87
+ def self?.repeated_run?: (Array[Integer] units) -> bool
88
+ def self?.sequential_run?: (Array[Integer] units) -> bool
89
+ def self?.weak_fragment?: (String text) -> bool
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,37 @@
1
+ module Envless
2
+ class Error < StandardError
3
+ end
4
+
5
+ class NetworkError < Error
6
+ @timeout: bool
7
+
8
+ def initialize: (?String? message, ?timeout: bool) -> void
9
+ def timeout?: () -> bool
10
+ end
11
+
12
+ class DecryptionError < Error
13
+ end
14
+
15
+ class WebhookVerificationError < Error
16
+ end
17
+
18
+ class ApiError < Error
19
+ attr_reader status: Integer?
20
+ attr_reader code: String
21
+ attr_reader resource: String?
22
+ attr_reader field: String?
23
+ attr_reader request_id: String?
24
+ attr_reader retry_after_seconds: Numeric?
25
+
26
+ def initialize: (?String? message, ?status: Integer?, ?code: String, ?resource: String?, ?field: String?, ?request_id: String?, ?retry_after_seconds: Numeric?) -> void
27
+ def detailed_message: (?highlight: bool, **untyped options) -> String
28
+ def inspect: () -> String
29
+ def auth?: () -> bool
30
+ def scope_missing?: () -> bool
31
+ def validation?: () -> bool
32
+ def not_found?: () -> bool
33
+ def conflict?: () -> bool
34
+ def rate_limited?: () -> bool
35
+ def needs_upgrade?: () -> bool
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ module Envless
2
+ class HttpRequest < Data
3
+ attr_reader method: String
4
+ attr_reader url: String
5
+ attr_reader headers: Hash[String, String]
6
+ attr_reader body: String?
7
+ attr_reader timeout: seconds?
8
+
9
+ def self.new: (String method, String url, Hash[String, String] headers, String? body, seconds? timeout) -> HttpRequest
10
+ | (method: String, url: String, headers: Hash[String, String], body: String?, timeout: seconds?) -> HttpRequest
11
+
12
+ def with: (?method: String, ?url: String, ?headers: Hash[String, String], ?body: String?, ?timeout: seconds?) -> HttpRequest
13
+ def inspect: () -> String
14
+ def to_s: () -> String
15
+ def pretty_print: (untyped printer) -> void
16
+ end
17
+
18
+ class HttpResponse < Data
19
+ attr_reader status: Integer
20
+ attr_reader headers: Hash[String, String]
21
+ attr_reader body: String
22
+ attr_reader reason: String
23
+
24
+ def self.new: (Integer status, Hash[String, String] headers, String body, String reason) -> HttpResponse
25
+ | (status: Integer, headers: Hash[String, String], body: String, reason: String) -> HttpResponse
26
+
27
+ def self.from: (untyped response) -> HttpResponse
28
+ def self.header_text: (untyped value) -> String
29
+ def header: (String name) -> String?
30
+ end
31
+ end