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.
100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 100 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, {}) components.each_with_index do |(tag, attributes), component_index| RegistryIdentifier.validate!(tag, "components[#{component_index}].tag") Array(attributes).each_with_index do |attribute, attribute_index| RegistryIdentifier.validate!(attribute, "components[#{component_index}].attributes[#{attribute_index}].name") end end [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 54 |
# 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)) validate_manifest_identifiers!(manifest) 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, NoMethodError, TypeError raise ConfigurationError, "Expo Turbo compatibility lock could not be loaded" end |
.parse_descriptor(value) ⇒ Object
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 76 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
138 139 140 |
# File 'lib/expo_turbo/rails/compatibility_registry.rb', line 138 def resolve(digest) @entries[digest] end |