capsium 0.1.2 → 0.3.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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.rubocop.yml +46 -7
- data/CHANGELOG.md +183 -0
- data/Gemfile +6 -0
- data/LICENSE +21 -0
- data/README.adoc +496 -127
- data/Rakefile +8 -1
- data/bin/console +1 -1
- data/capsium.gemspec +12 -19
- data/exe/capsium +1 -1
- data/lib/capsium/cli/convert.rb +18 -0
- data/lib/capsium/cli/formatting.rb +27 -0
- data/lib/capsium/cli/package.rb +148 -0
- data/lib/capsium/cli/reactor.rb +33 -0
- data/lib/capsium/cli.rb +4 -96
- data/lib/capsium/converters/jekyll.rb +5 -7
- data/lib/capsium/converters.rb +7 -0
- data/lib/capsium/package/authentication.rb +38 -0
- data/lib/capsium/package/authentication_config.rb +78 -0
- data/lib/capsium/package/cipher.rb +197 -0
- data/lib/capsium/package/composition.rb +77 -0
- data/lib/capsium/package/dataset.rb +67 -34
- data/lib/capsium/package/dependency_resolver.rb +61 -0
- data/lib/capsium/package/encryption_config.rb +38 -0
- data/lib/capsium/package/manifest.rb +33 -22
- data/lib/capsium/package/manifest_config.rb +52 -7
- data/lib/capsium/package/merged_view.rb +168 -0
- data/lib/capsium/package/metadata.rb +4 -8
- data/lib/capsium/package/metadata_config.rb +97 -9
- data/lib/capsium/package/preparation.rb +81 -0
- data/lib/capsium/package/routes.rb +39 -41
- data/lib/capsium/package/routes_config.rb +174 -44
- data/lib/capsium/package/security.rb +140 -0
- data/lib/capsium/package/security_config.rb +60 -0
- data/lib/capsium/package/signer.rb +201 -0
- data/lib/capsium/package/storage.rb +20 -45
- data/lib/capsium/package/storage_config.rb +128 -3
- data/lib/capsium/package/store.rb +109 -0
- data/lib/capsium/package/testing/config_test.rb +70 -0
- data/lib/capsium/package/testing/context.rb +11 -0
- data/lib/capsium/package/testing/data_validation_test.rb +63 -0
- data/lib/capsium/package/testing/file_test.rb +45 -0
- data/lib/capsium/package/testing/report.rb +33 -0
- data/lib/capsium/package/testing/route_test.rb +64 -0
- data/lib/capsium/package/testing/test_case.rb +60 -0
- data/lib/capsium/package/testing/test_suite.rb +86 -0
- data/lib/capsium/package/testing.rb +20 -0
- data/lib/capsium/package/validator.rb +160 -0
- data/lib/capsium/package/verification.rb +32 -0
- data/lib/capsium/package/version.rb +204 -0
- data/lib/capsium/package.rb +92 -106
- data/lib/capsium/packager.rb +75 -6
- data/lib/capsium/reactor/authenticator.rb +180 -0
- data/lib/capsium/reactor/deploy.rb +71 -0
- data/lib/capsium/reactor/htpasswd.rb +154 -0
- data/lib/capsium/reactor/introspection.rb +91 -0
- data/lib/capsium/reactor/oauth2.rb +106 -0
- data/lib/capsium/reactor/serving.rb +75 -0
- data/lib/capsium/reactor/session.rb +82 -0
- data/lib/capsium/reactor.rb +93 -43
- data/lib/capsium/thor_ext.rb +1 -1
- data/lib/capsium/version.rb +1 -1
- data/lib/capsium.rb +7 -9
- data/sig/capsium/package/authentication.rbs +23 -0
- data/sig/capsium/package/authentication_config.rbs +71 -0
- data/sig/capsium/package/cipher.rbs +51 -0
- data/sig/capsium/package/composition.rbs +20 -0
- data/sig/capsium/package/dataset.rbs +28 -0
- data/sig/capsium/package/dependency_resolver.rbs +49 -0
- data/sig/capsium/package/encryption_config.rbs +35 -0
- data/sig/capsium/package/manifest.rbs +34 -0
- data/sig/capsium/package/manifest_config.rbs +38 -0
- data/sig/capsium/package/merged_view.rbs +46 -0
- data/sig/capsium/package/metadata.rbs +26 -0
- data/sig/capsium/package/metadata_config.rbs +49 -0
- data/sig/capsium/package/preparation.rbs +23 -0
- data/sig/capsium/package/routes.rbs +37 -0
- data/sig/capsium/package/routes_config.rbs +107 -0
- data/sig/capsium/package/security.rbs +72 -0
- data/sig/capsium/package/security_config.rbs +57 -0
- data/sig/capsium/package/signer.rbs +59 -0
- data/sig/capsium/package/storage.rbs +28 -0
- data/sig/capsium/package/storage_config.rbs +85 -0
- data/sig/capsium/package/store.rbs +37 -0
- data/sig/capsium/package/testing/config_test.rbs +21 -0
- data/sig/capsium/package/testing/context.rbs +14 -0
- data/sig/capsium/package/testing/data_validation_test.rbs +22 -0
- data/sig/capsium/package/testing/file_test.rbs +17 -0
- data/sig/capsium/package/testing/report.rbs +20 -0
- data/sig/capsium/package/testing/route_test.rbs +22 -0
- data/sig/capsium/package/testing/test_case.rbs +41 -0
- data/sig/capsium/package/testing/test_suite.rbs +20 -0
- data/sig/capsium/package/testing.rbs +7 -0
- data/sig/capsium/package/validator.rbs +29 -0
- data/sig/capsium/package/verification.rbs +24 -0
- data/sig/capsium/package/version.rbs +39 -0
- data/sig/capsium/package.rbs +61 -0
- data/sig/capsium/packager.rbs +32 -0
- data/sig/capsium/reactor/authenticator.rbs +40 -0
- data/sig/capsium/reactor/deploy.rbs +34 -0
- data/sig/capsium/reactor/htpasswd.rbs +36 -0
- data/sig/capsium/reactor/introspection.rbs +31 -0
- data/sig/capsium/reactor/oauth2.rbs +31 -0
- data/sig/capsium/reactor/serving.rbs +22 -0
- data/sig/capsium/reactor/session.rbs +36 -0
- data/sig/capsium/reactor.rbs +35 -0
- data/sig/capsium.rbs +3 -1
- metadata +108 -170
- data/lib/capsium/package/dataset_config.rb +0 -28
- data/lib/capsium/protector.rb +0 -103
data/lib/capsium/thor_ext.rb
CHANGED
|
@@ -60,7 +60,7 @@ module Capsium
|
|
|
60
60
|
return if error.is_a?(Errno::EPIPE)
|
|
61
61
|
raise if ENV["VERBOSE"] || !config.fetch(:exit_on_failure, true)
|
|
62
62
|
|
|
63
|
-
message = error.message.to_s
|
|
63
|
+
message = +error.message.to_s
|
|
64
64
|
message.prepend("[#{error.class}] ") if message.empty? || !error.is_a?(Thor::Error)
|
|
65
65
|
|
|
66
66
|
config[:shell]&.say_error(message, :red)
|
data/lib/capsium/version.rb
CHANGED
data/lib/capsium.rb
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "capsium/version"
|
|
4
|
-
require "shale"
|
|
5
|
-
require "shale/adapter/nokogiri"
|
|
6
|
-
Shale.xml_adapter = Shale::Adapter::Nokogiri
|
|
7
|
-
|
|
8
3
|
module Capsium
|
|
9
4
|
class Error < StandardError; end
|
|
10
5
|
|
|
11
|
-
|
|
6
|
+
autoload :VERSION, "capsium/version"
|
|
7
|
+
autoload :Cli, "capsium/cli"
|
|
8
|
+
autoload :Converters, "capsium/converters"
|
|
9
|
+
autoload :Package, "capsium/package"
|
|
10
|
+
autoload :Packager, "capsium/packager"
|
|
11
|
+
autoload :Reactor, "capsium/reactor"
|
|
12
|
+
autoload :ThorExt, "capsium/thor_ext"
|
|
12
13
|
end
|
|
13
|
-
|
|
14
|
-
require_relative "capsium/package"
|
|
15
|
-
require_relative "capsium/packager"
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Loads a package's authentication.json (ARCHITECTURE.md section 4b).
|
|
4
|
+
class Authentication
|
|
5
|
+
attr_reader path: String
|
|
6
|
+
attr_reader config: AuthenticationConfig?
|
|
7
|
+
|
|
8
|
+
def initialize: (String path) -> void
|
|
9
|
+
|
|
10
|
+
def present?: () -> bool
|
|
11
|
+
|
|
12
|
+
def basic_auth: () -> BasicAuthConfig?
|
|
13
|
+
|
|
14
|
+
def oauth2: () -> OAuth2Config?
|
|
15
|
+
|
|
16
|
+
# Whether any authentication method is enabled.
|
|
17
|
+
def enabled?: () -> bool
|
|
18
|
+
|
|
19
|
+
def to_json: (*untyped args) -> String
|
|
20
|
+
def to_hash: () -> Hash[String, untyped]
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# The "basicAuth" object of authentication.json (ARCHITECTURE.md
|
|
4
|
+
# section 4b): Apache-style Basic authentication verified against an
|
|
5
|
+
# htpasswd file.
|
|
6
|
+
class BasicAuthConfig
|
|
7
|
+
attr_accessor enabled: bool
|
|
8
|
+
attr_accessor passwd_file: String?
|
|
9
|
+
attr_accessor realm: String
|
|
10
|
+
|
|
11
|
+
def initialize: (?enabled: bool enabled, ?passwd_file: String? passwd_file,
|
|
12
|
+
?realm: String? realm) -> void
|
|
13
|
+
|
|
14
|
+
def enabled?: () -> bool
|
|
15
|
+
|
|
16
|
+
def to_json: (*untyped args) -> String
|
|
17
|
+
def to_hash: () -> Hash[String, untyped]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# The "oauth2" object of authentication.json: an OAuth2
|
|
21
|
+
# authorization-code flow. The client secret is NEVER read from the
|
|
22
|
+
# package — it comes from deploy.json or reactor configuration.
|
|
23
|
+
class OAuth2Config
|
|
24
|
+
attr_accessor enabled: bool
|
|
25
|
+
attr_accessor provider: String?
|
|
26
|
+
attr_accessor client_id: String?
|
|
27
|
+
attr_accessor authorization_url: String?
|
|
28
|
+
attr_accessor token_url: String?
|
|
29
|
+
attr_accessor userinfo_url: String?
|
|
30
|
+
attr_accessor redirect_path: String
|
|
31
|
+
attr_accessor scopes: Array[String]
|
|
32
|
+
|
|
33
|
+
def initialize: (?enabled: bool enabled, ?provider: String? provider,
|
|
34
|
+
?client_id: String? client_id,
|
|
35
|
+
?authorization_url: String? authorization_url,
|
|
36
|
+
?token_url: String? token_url,
|
|
37
|
+
?userinfo_url: String? userinfo_url,
|
|
38
|
+
?redirect_path: String? redirect_path,
|
|
39
|
+
?scopes: Array[String] scopes) -> void
|
|
40
|
+
|
|
41
|
+
def enabled?: () -> bool
|
|
42
|
+
|
|
43
|
+
def to_json: (*untyped args) -> String
|
|
44
|
+
def to_hash: () -> Hash[String, untyped]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The "authentication" object of authentication.json.
|
|
48
|
+
class AuthenticationData
|
|
49
|
+
attr_accessor basic_auth: BasicAuthConfig?
|
|
50
|
+
attr_accessor oauth2: OAuth2Config?
|
|
51
|
+
|
|
52
|
+
def initialize: (?basic_auth: BasicAuthConfig? basic_auth,
|
|
53
|
+
?oauth2: OAuth2Config? oauth2) -> void
|
|
54
|
+
|
|
55
|
+
def to_json: (*untyped args) -> String
|
|
56
|
+
def to_hash: () -> Hash[String, untyped]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Canonical authentication.json model (ARCHITECTURE.md section 4b).
|
|
60
|
+
class AuthenticationConfig
|
|
61
|
+
attr_accessor authentication: AuthenticationData?
|
|
62
|
+
|
|
63
|
+
def initialize: (?authentication: AuthenticationData? authentication) -> void
|
|
64
|
+
|
|
65
|
+
def self.from_json: (String json) -> instance
|
|
66
|
+
|
|
67
|
+
def to_json: (*untyped args) -> String
|
|
68
|
+
def to_hash: () -> Hash[String, untyped]
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Encrypts and decrypts whole Capsium packages (05x-packaging
|
|
4
|
+
# "Encryption", 05x-security "Encrypted information"). An encrypted
|
|
5
|
+
# .cap is a zip containing metadata.json (cleartext), signature.json
|
|
6
|
+
# (the encryption envelope) and package.enc (AES-256-GCM of the inner
|
|
7
|
+
# plaintext .cap zip). The random DEK is wrapped with the recipient's
|
|
8
|
+
# RSA public key using OAEP with SHA-256. OCB/OpenPGP are out of
|
|
9
|
+
# scope.
|
|
10
|
+
class Cipher
|
|
11
|
+
ALGORITHM: String
|
|
12
|
+
KEY_MANAGEMENT: String
|
|
13
|
+
CIPHER_NAME: String
|
|
14
|
+
ENCRYPTED_FILE: String
|
|
15
|
+
ENVELOPE_FILE: String
|
|
16
|
+
RSA_OPTIONS: Hash[String, String]
|
|
17
|
+
|
|
18
|
+
# Structural problems: unreadable input, missing zip entries,
|
|
19
|
+
# unsupported envelope algorithms, unloadable keys.
|
|
20
|
+
class CipherError < Capsium::Error
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# An encrypted package was opened without a private key.
|
|
24
|
+
class KeyRequiredError < CipherError
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Decryption failed: wrong private key or tampered ciphertext.
|
|
28
|
+
class DecryptionError < CipherError
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Whether the path (.cap file or uncompressed directory) is an
|
|
32
|
+
# encrypted package, i.e. contains package.enc.
|
|
33
|
+
def self.encrypted?: (String | Pathname path) -> bool
|
|
34
|
+
|
|
35
|
+
# Encrypts the package at source_path (a .cap file, or a package
|
|
36
|
+
# directory which is packed first) for the recipient's RSA public
|
|
37
|
+
# key (or X.509 certificate) and writes the encrypted .cap to
|
|
38
|
+
# output_path. Returns output_path.
|
|
39
|
+
def encrypt: (String source_path, String public_key_path, String output_path) -> String
|
|
40
|
+
|
|
41
|
+
# Decrypts the encrypted package at encrypted_path (.cap file or
|
|
42
|
+
# uncompressed directory) with the recipient's RSA private key and
|
|
43
|
+
# writes the plaintext .cap to output_path. Returns output_path.
|
|
44
|
+
def decrypt: (String | Pathname encrypted_path, String private_key_path, String output_path) -> String
|
|
45
|
+
|
|
46
|
+
# Decrypts an encrypted package into a fresh temporary directory
|
|
47
|
+
# and returns the directory path.
|
|
48
|
+
def self.decrypt_to_directory: (String | Pathname source_path, String private_key_path) -> String
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Composite-package support (ARCHITECTURE.md section 4a), mixed into
|
|
4
|
+
# Package: metadata.dependencies resolution against a package store
|
|
5
|
+
# and load-time validation of dependency resource references.
|
|
6
|
+
module Composition
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def resolve_dependencies: ((Store | String)? store, Array[String] chain) -> Array[ResolvedDependency]
|
|
10
|
+
|
|
11
|
+
def validate_dependency_references!: () -> void
|
|
12
|
+
|
|
13
|
+
def validate_dependency_reference: (Route route, ResolvedDependency dependency, String reference) -> void
|
|
14
|
+
|
|
15
|
+
def dependency_for: (String reference) -> ResolvedDependency?
|
|
16
|
+
|
|
17
|
+
def dependency_views: () -> Array[[String, MergedView]]
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# A loaded dataset. "source"/"schemaFile"/"databaseFile" are
|
|
4
|
+
# package-relative paths resolved against the package directory.
|
|
5
|
+
class Dataset
|
|
6
|
+
attr_reader name: String
|
|
7
|
+
attr_reader config: DatasetConfig
|
|
8
|
+
attr_reader data: untyped
|
|
9
|
+
attr_reader package_path: String
|
|
10
|
+
|
|
11
|
+
def initialize: (name: String name, config: DatasetConfig config,
|
|
12
|
+
package_path: String package_path) -> void
|
|
13
|
+
|
|
14
|
+
def source_path: () -> String
|
|
15
|
+
|
|
16
|
+
def schema_path: () -> String?
|
|
17
|
+
|
|
18
|
+
def load_data: () -> untyped
|
|
19
|
+
|
|
20
|
+
def validate: () -> bool
|
|
21
|
+
|
|
22
|
+
# File-existence and schema validations; empty when valid.
|
|
23
|
+
def validation_errors: () -> Array[String]
|
|
24
|
+
|
|
25
|
+
def to_json: (*untyped args) -> String
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Base error for composite-package dependency resolution
|
|
4
|
+
# (ARCHITECTURE.md section 4a).
|
|
5
|
+
class DependencyError < Capsium::Error
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# No package in the store provides the dependency GUID.
|
|
9
|
+
class DependencyNotFoundError < DependencyError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# The store provides the GUID but no version satisfies the range.
|
|
13
|
+
class UnsatisfiableDependencyError < DependencyError
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# A dependency cycle was detected while resolving.
|
|
17
|
+
class CircularDependencyError < DependencyError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# A dependent route references a dependency resource that is not
|
|
21
|
+
# exported (private) and therefore not visible to dependents.
|
|
22
|
+
class DependencyVisibilityError < DependencyError
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# One resolved dependency: the declared GUID and range, the .cap it
|
|
26
|
+
# resolved to and the loaded package.
|
|
27
|
+
class ResolvedDependency
|
|
28
|
+
def self.new: (String guid, String range, String path, Package package) -> void
|
|
29
|
+
|
|
30
|
+
def guid: () -> String
|
|
31
|
+
def range: () -> String
|
|
32
|
+
def path: () -> String
|
|
33
|
+
def package: () -> Package
|
|
34
|
+
def version: () -> String?
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Orchestrates metadata.dependencies resolution against a package
|
|
38
|
+
# store: circularity checks plus store lookup.
|
|
39
|
+
class DependencyResolver
|
|
40
|
+
attr_reader store: Store
|
|
41
|
+
|
|
42
|
+
def initialize: (Store | String? store) -> void
|
|
43
|
+
|
|
44
|
+
# The newest store .cap satisfying the dependency, raising typed
|
|
45
|
+
# errors for circular, missing or unsatisfiable dependencies.
|
|
46
|
+
def resolve_path: (String guid, String range, chain: Array[String] chain) -> String
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# The "encryption" object of signature.json in an encrypted package:
|
|
4
|
+
# the AES-256-GCM envelope with the RSA-OAEP-SHA256 wrapped DEK, all
|
|
5
|
+
# binary values Base64-encoded.
|
|
6
|
+
class EncryptionEnvelope
|
|
7
|
+
attr_accessor algorithm: String?
|
|
8
|
+
attr_accessor key_management: String?
|
|
9
|
+
attr_accessor encrypted_dek: String?
|
|
10
|
+
attr_accessor iv: String?
|
|
11
|
+
attr_accessor auth_tag: String?
|
|
12
|
+
|
|
13
|
+
def initialize: (?algorithm: String? algorithm,
|
|
14
|
+
?key_management: String? key_management,
|
|
15
|
+
?encrypted_dek: String? encrypted_dek,
|
|
16
|
+
?iv: String? iv,
|
|
17
|
+
?auth_tag: String? auth_tag) -> void
|
|
18
|
+
|
|
19
|
+
def to_json: (*untyped args) -> String
|
|
20
|
+
def to_hash: () -> Hash[String, untyped]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The signature.json file of an encrypted package.
|
|
24
|
+
class EncryptionConfig
|
|
25
|
+
attr_accessor encryption: EncryptionEnvelope?
|
|
26
|
+
|
|
27
|
+
def initialize: (?encryption: EncryptionEnvelope? encryption) -> void
|
|
28
|
+
|
|
29
|
+
def self.from_json: (String json) -> instance
|
|
30
|
+
|
|
31
|
+
def to_json: (*untyped args) -> String
|
|
32
|
+
def to_hash: () -> Hash[String, untyped]
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Loads, generates and writes manifest.json (ARCHITECTURE.md
|
|
4
|
+
# section 3).
|
|
5
|
+
class Manifest
|
|
6
|
+
attr_reader path: String
|
|
7
|
+
attr_reader content_path: String
|
|
8
|
+
attr_reader config: ManifestConfig
|
|
9
|
+
|
|
10
|
+
def initialize: (String path) -> void
|
|
11
|
+
|
|
12
|
+
# Auto-generation: scan content/ recursively, detect MIME types,
|
|
13
|
+
# default visibility "exported", deterministic (sorted) output.
|
|
14
|
+
def generate_manifest: () -> Hash[String, Resource]
|
|
15
|
+
|
|
16
|
+
def resources: () -> Hash[String, Resource]
|
|
17
|
+
|
|
18
|
+
def lookup: (String path) -> Resource?
|
|
19
|
+
|
|
20
|
+
def type_for: (String path) -> String?
|
|
21
|
+
|
|
22
|
+
def to_json: (*untyped args) -> String
|
|
23
|
+
def to_hash: () -> Hash[String, untyped]
|
|
24
|
+
|
|
25
|
+
def save_to_file: (?String output_path) -> void
|
|
26
|
+
|
|
27
|
+
def path_to_content_file: (String path) -> Pathname
|
|
28
|
+
|
|
29
|
+
def content_file_exists?: (String path) -> bool
|
|
30
|
+
|
|
31
|
+
def relative_path: (String path) -> String
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# A single manifest resource entry (ARCHITECTURE.md section 3).
|
|
4
|
+
class Resource
|
|
5
|
+
VISIBILITIES: Array[String]
|
|
6
|
+
|
|
7
|
+
attr_accessor type: String?
|
|
8
|
+
attr_accessor visibility: String
|
|
9
|
+
attr_accessor version: String?
|
|
10
|
+
|
|
11
|
+
def initialize: (?type: String? type, ?visibility: String? visibility,
|
|
12
|
+
?version: String? version) -> void
|
|
13
|
+
|
|
14
|
+
def to_json: (*untyped args) -> String
|
|
15
|
+
def to_hash: () -> Hash[String, untyped]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Canonical manifest.json model: an object keyed by package-relative
|
|
19
|
+
# resource path. The legacy array form is normalized on read;
|
|
20
|
+
# writers emit only the object form.
|
|
21
|
+
class ManifestConfig
|
|
22
|
+
attr_accessor resources: Hash[String, Resource]
|
|
23
|
+
|
|
24
|
+
def initialize: (?resources: Hash[String, Resource] resources) -> void
|
|
25
|
+
|
|
26
|
+
def self.from_json: (String json) -> instance
|
|
27
|
+
|
|
28
|
+
def resources_from_json: (ManifestConfig model, untyped value) -> void
|
|
29
|
+
|
|
30
|
+
def resources_to_json: (ManifestConfig model, Hash[String, untyped] doc) -> void
|
|
31
|
+
|
|
32
|
+
def sort!: () -> ManifestConfig
|
|
33
|
+
|
|
34
|
+
def to_json: (*untyped args) -> String
|
|
35
|
+
def to_hash: () -> Hash[String, untyped]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# The merged (overlay) view of a package's content, per ARCHITECTURE.md
|
|
4
|
+
# section 5a. Shared by Package (validation) and Reactor (serving).
|
|
5
|
+
class MergedView
|
|
6
|
+
# A single read layer: an absolute root directory mirroring the
|
|
7
|
+
# content/ tree, plus its parsed tombstone set.
|
|
8
|
+
class Layer
|
|
9
|
+
def self.new: (String root, String visibility, Set[String] tombstones) -> void
|
|
10
|
+
|
|
11
|
+
def file?: (String content_relative_path) -> bool
|
|
12
|
+
|
|
13
|
+
def absolute: (String content_relative_path) -> String
|
|
14
|
+
|
|
15
|
+
def root: () -> String
|
|
16
|
+
|
|
17
|
+
def visibility: () -> String
|
|
18
|
+
|
|
19
|
+
def tombstones: () -> Set[String]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
TOMBSTONE_FILE: String
|
|
23
|
+
CONTENT_PREFIX: String
|
|
24
|
+
|
|
25
|
+
attr_reader package_path: String
|
|
26
|
+
attr_reader layers: Array[Layer]
|
|
27
|
+
attr_reader dependency_views: Array[[String, MergedView]]
|
|
28
|
+
|
|
29
|
+
def initialize: (String package_path, storage: Storage storage,
|
|
30
|
+
manifest: Manifest manifest,
|
|
31
|
+
?dependency_views: Array[[String, MergedView]] dependency_views,
|
|
32
|
+
?exported_only: bool exported_only) -> void
|
|
33
|
+
|
|
34
|
+
# The absolute filesystem path serving a resource reference: a
|
|
35
|
+
# package-relative content path ("content/app.js"), or a dependency
|
|
36
|
+
# reference ("<dependency-guid>/content/app.js"). nil when no layer
|
|
37
|
+
# provides the path or it is tombstoned. Non-content paths never
|
|
38
|
+
# resolve through the view.
|
|
39
|
+
def resolve: (String? reference) -> String?
|
|
40
|
+
|
|
41
|
+
# The [GUID, view] pair whose GUID prefixes the reference (longest
|
|
42
|
+
# GUID first, so nested GUIDs address the innermost dependency).
|
|
43
|
+
def dependency_pair_for: (String reference) -> [String, MergedView]?
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Loads and writes metadata.json; field access is delegated to the
|
|
4
|
+
# wrapped MetadataData config (ARCHITECTURE.md section 2).
|
|
5
|
+
class Metadata
|
|
6
|
+
attr_reader path: String
|
|
7
|
+
attr_reader config: MetadataData
|
|
8
|
+
|
|
9
|
+
def initialize: (String path) -> void
|
|
10
|
+
|
|
11
|
+
def save_to_file: (?String output_path) -> void
|
|
12
|
+
|
|
13
|
+
def to_json: (*untyped args) -> String
|
|
14
|
+
def to_hash: () -> Hash[String, untyped]
|
|
15
|
+
def name: () -> String?
|
|
16
|
+
def version: () -> String?
|
|
17
|
+
def description: () -> String?
|
|
18
|
+
def guid: () -> String?
|
|
19
|
+
def uuid: () -> String?
|
|
20
|
+
def author: () -> String?
|
|
21
|
+
def license: () -> String?
|
|
22
|
+
def repository: () -> Repository?
|
|
23
|
+
def dependencies: () -> Hash[String, String]
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# The repository object of metadata.json.
|
|
4
|
+
class Repository
|
|
5
|
+
attr_accessor type: String?
|
|
6
|
+
attr_accessor url: String?
|
|
7
|
+
|
|
8
|
+
def initialize: (?type: String? type, ?url: String? url) -> void
|
|
9
|
+
|
|
10
|
+
def to_json: (*untyped args) -> String
|
|
11
|
+
def to_hash: () -> Hash[String, untyped]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Canonical metadata.json model (ARCHITECTURE.md section 2). The
|
|
15
|
+
# legacy array form of "dependencies" is normalized on read; writers
|
|
16
|
+
# emit only the object form.
|
|
17
|
+
class MetadataData
|
|
18
|
+
KEBAB_CASE_PATTERN: Regexp
|
|
19
|
+
SEMVER_PATTERN: Regexp
|
|
20
|
+
UUID_PATTERN: Regexp
|
|
21
|
+
|
|
22
|
+
attr_accessor name: String?
|
|
23
|
+
attr_accessor version: String?
|
|
24
|
+
attr_accessor description: String?
|
|
25
|
+
attr_accessor guid: String?
|
|
26
|
+
attr_accessor uuid: String?
|
|
27
|
+
attr_accessor author: String?
|
|
28
|
+
attr_accessor license: String?
|
|
29
|
+
attr_accessor repository: Repository?
|
|
30
|
+
attr_accessor dependencies: Hash[String, String]
|
|
31
|
+
attr_accessor read_only: bool?
|
|
32
|
+
|
|
33
|
+
def initialize: (?name: String? name, ?version: String? version,
|
|
34
|
+
?description: String? description, ?guid: String? guid,
|
|
35
|
+
?uuid: String? uuid, ?author: String? author,
|
|
36
|
+
?license: String? license, ?repository: Repository? repository,
|
|
37
|
+
?dependencies: Hash[String, String] dependencies,
|
|
38
|
+
?read_only: bool? read_only) -> void
|
|
39
|
+
|
|
40
|
+
def self.from_json: (String json) -> instance
|
|
41
|
+
|
|
42
|
+
# Field-level format validations; empty when valid.
|
|
43
|
+
def format_errors: () -> Array[String]
|
|
44
|
+
|
|
45
|
+
def to_json: (*untyped args) -> String
|
|
46
|
+
def to_hash: () -> Hash[String, untyped]
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Package-source preparation (ARCHITECTURE.md section 1), mixed into
|
|
4
|
+
# Package: directory/.cap/encrypted-cap detection, extraction and
|
|
5
|
+
# decryption into a readable package directory.
|
|
6
|
+
module Preparation
|
|
7
|
+
def prepare_package: (Pathname path) -> (String | Pathname)
|
|
8
|
+
|
|
9
|
+
def decompress_cap_file: (String | Pathname file_path) -> String
|
|
10
|
+
|
|
11
|
+
# Decrypts an encrypted package (.cap file or uncompressed directory)
|
|
12
|
+
# into a temporary directory and returns its path. Raises
|
|
13
|
+
# Cipher::KeyRequiredError when no decryption_key was given.
|
|
14
|
+
def decrypt_cap_file: (String | Pathname source_path) -> String
|
|
15
|
+
|
|
16
|
+
def determine_load_type: (String | Pathname path) -> Symbol
|
|
17
|
+
|
|
18
|
+
# Whether the package was loaded from an encrypted source
|
|
19
|
+
# (ARCHITECTURE.md section 6b layout).
|
|
20
|
+
def encrypted?: () -> bool
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Loads, generates and writes routes.json (ARCHITECTURE.md
|
|
4
|
+
# section 4).
|
|
5
|
+
class Routes
|
|
6
|
+
INDEX_ROUTE: String
|
|
7
|
+
INDEX_RESOURCE: String
|
|
8
|
+
DATASET_ROUTE_PREFIX: String
|
|
9
|
+
|
|
10
|
+
attr_reader path: String
|
|
11
|
+
attr_reader config: RoutesConfig
|
|
12
|
+
attr_reader manifest: Manifest
|
|
13
|
+
attr_reader storage: Storage
|
|
14
|
+
|
|
15
|
+
def initialize: (String path, Manifest manifest, Storage storage) -> void
|
|
16
|
+
|
|
17
|
+
def resolve: (String url_path) -> Route?
|
|
18
|
+
|
|
19
|
+
def add_route: (String route, String target) -> Route
|
|
20
|
+
|
|
21
|
+
def update_route: (String route, String updated_route, String updated_target) -> Route
|
|
22
|
+
|
|
23
|
+
def remove_route: (String route) -> Array[Route]
|
|
24
|
+
|
|
25
|
+
def to_json: (*untyped args) -> String
|
|
26
|
+
def to_hash: () -> Hash[String, untyped]
|
|
27
|
+
|
|
28
|
+
def save_to_file: (?String output_path) -> void
|
|
29
|
+
|
|
30
|
+
# Auto-generation (ARCHITECTURE.md section 4): every manifest
|
|
31
|
+
# resource gets a route at its path relative to content/; HTML
|
|
32
|
+
# files get two routes; the index HTML additionally gets "/";
|
|
33
|
+
# every dataset gets /api/v1/data/<id>. Deterministic output.
|
|
34
|
+
def generate_routes: () -> RoutesConfig
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|