Class: ExpoTurbo::Rails::CompatibilityRegistry
- Inherits:
-
Object
- Object
- ExpoTurbo::Rails::CompatibilityRegistry
- Defined in:
- lib/expo_turbo/rails/compatibility_registry.rb
Defined Under Namespace
Classes: Entry
Constant Summary collapse
- DIGEST_PATTERN =
/\Asha256-128:[0-9a-f]{32}\z/- DESCRIPTOR_VALUE_PATTERN =
/\A[A-Za-z0-9._:+-]+\z/
Class Method Summary collapse
- .from_data(lock:, vocabularies:) ⇒ Object
- .load(lockfile) ⇒ Object
- .parse_descriptor(value) ⇒ Object
Instance Method Summary collapse
-
#initialize(lock:, vocabularies:) ⇒ CompatibilityRegistry
constructor
A new instance of CompatibilityRegistry.
- #resolve(digest) ⇒ Object
Constructor Details
#initialize(lock:, vocabularies:) ⇒ CompatibilityRegistry
Returns a new instance of CompatibilityRegistry.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 79 def initialize(lock:, vocabularies:) unless lock.is_a?(Hash) && lock["lockVersion"] == 1 && lock["history"].is_a?(Array) raise ConfigurationError, "Expo Turbo compatibility lock is invalid" end digests = {} revisions = {} @entries = lock.fetch("history").to_h do |record| digest = record["digest"] revision = record["revision"] unless DIGEST_PATTERN.match?(digest) && revision.is_a?(Integer) && revision.positive? raise ConfigurationError, "Expo Turbo compatibility lock history is invalid" end if revisions.key?(revision) raise ConfigurationError, "Expo Turbo compatibility lock revisions must be unique" end if digests.key?(digest) raise ConfigurationError, "Expo Turbo compatibility lock digests must be unique" end digests[digest] = true revisions[revision] = true components = vocabularies.fetch(digest, {}) [digest, Entry.new(digest:, revision:, components: components.freeze).freeze] end.freeze current = lock["current"] unless @entries.key?(current) raise ConfigurationError, "Expo Turbo compatibility lock current digest is unknown" end freeze rescue KeyError raise ConfigurationError, "Expo Turbo compatibility lock vocabulary is missing" end |
Class Method Details
.from_data(lock:, vocabularies:) ⇒ Object
23 24 25 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 23 def self.from_data(lock:, vocabularies:) new(lock:, vocabularies:) end |
.load(lockfile) ⇒ Object
27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 27 def self.load(lockfile) path = Pathname(lockfile) lock = JSON.parse(File.binread(path)) vocabularies = lock.fetch("history").to_h do |entry| manifest_path = path.dirname.join(entry.fetch("manifest")) manifest = JSON.parse(File.binread(manifest_path)) unless manifest["hash"] == entry.fetch("digest") raise ConfigurationError, "Expo Turbo compatibility manifest digest does not match its lock entry" end digest_source = { "components" => manifest.fetch("components"), "modules" => manifest.fetch("modules"), "protocolVersion" => manifest.fetch("protocolVersion") } computed_digest = "sha256-128:#{Digest::SHA256.hexdigest(JSON.generate(digest_source))[0, 32]}" unless computed_digest == entry.fetch("digest") raise ConfigurationError, "Expo Turbo compatibility manifest computed digest does not match its lock entry" end components = manifest.fetch("components").to_h do |component| [component.fetch("tag"), component.fetch("attributes").map { |attribute| attribute.fetch("name") }] end [entry.fetch("digest"), components] end new(lock:, vocabularies:) rescue SystemCallError, JSON::ParserError, KeyError, TypeError raise ConfigurationError, "Expo Turbo compatibility lock could not be loaded" end |
.parse_descriptor(value) ⇒ Object
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 55 def self.parse_descriptor(value) return unless value.is_a?(String) value = value.dup.force_encoding(Encoding::UTF_8) return unless value.valid_encoding? && value.ascii_only? fields = {} valid = value.split(";", -1).all? do |part| name, field_value = part.strip.split("=", 2) next false unless name && field_value && !fields.key?(name) next false unless DESCRIPTOR_VALUE_PATTERN.match?(name) && DESCRIPTOR_VALUE_PATTERN.match?(field_value) fields[name] = field_value true end return unless valid expected_fields = fields.key?("vocab") ? %w[proto rt v vocab] : %w[proto rt v] return unless fields.keys.sort == expected_fields.sort return unless fields["v"] == "1" && fields["proto"] == PROTOCOL_VERSION && fields["rt"] return unless fields["vocab"].nil? || DIGEST_PATTERN.match?(fields["vocab"]) fields.freeze end |
Instance Method Details
#resolve(digest) ⇒ Object
111 112 113 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 111 def resolve(digest) @entries[digest] end |