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.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.rubocop.yml +46 -7
  4. data/CHANGELOG.md +183 -0
  5. data/Gemfile +6 -0
  6. data/LICENSE +21 -0
  7. data/README.adoc +496 -127
  8. data/Rakefile +8 -1
  9. data/bin/console +1 -1
  10. data/capsium.gemspec +12 -19
  11. data/exe/capsium +1 -1
  12. data/lib/capsium/cli/convert.rb +18 -0
  13. data/lib/capsium/cli/formatting.rb +27 -0
  14. data/lib/capsium/cli/package.rb +148 -0
  15. data/lib/capsium/cli/reactor.rb +33 -0
  16. data/lib/capsium/cli.rb +4 -96
  17. data/lib/capsium/converters/jekyll.rb +5 -7
  18. data/lib/capsium/converters.rb +7 -0
  19. data/lib/capsium/package/authentication.rb +38 -0
  20. data/lib/capsium/package/authentication_config.rb +78 -0
  21. data/lib/capsium/package/cipher.rb +197 -0
  22. data/lib/capsium/package/composition.rb +77 -0
  23. data/lib/capsium/package/dataset.rb +67 -34
  24. data/lib/capsium/package/dependency_resolver.rb +61 -0
  25. data/lib/capsium/package/encryption_config.rb +38 -0
  26. data/lib/capsium/package/manifest.rb +33 -22
  27. data/lib/capsium/package/manifest_config.rb +52 -7
  28. data/lib/capsium/package/merged_view.rb +168 -0
  29. data/lib/capsium/package/metadata.rb +4 -8
  30. data/lib/capsium/package/metadata_config.rb +97 -9
  31. data/lib/capsium/package/preparation.rb +81 -0
  32. data/lib/capsium/package/routes.rb +39 -41
  33. data/lib/capsium/package/routes_config.rb +174 -44
  34. data/lib/capsium/package/security.rb +140 -0
  35. data/lib/capsium/package/security_config.rb +60 -0
  36. data/lib/capsium/package/signer.rb +201 -0
  37. data/lib/capsium/package/storage.rb +20 -45
  38. data/lib/capsium/package/storage_config.rb +128 -3
  39. data/lib/capsium/package/store.rb +109 -0
  40. data/lib/capsium/package/testing/config_test.rb +70 -0
  41. data/lib/capsium/package/testing/context.rb +11 -0
  42. data/lib/capsium/package/testing/data_validation_test.rb +63 -0
  43. data/lib/capsium/package/testing/file_test.rb +45 -0
  44. data/lib/capsium/package/testing/report.rb +33 -0
  45. data/lib/capsium/package/testing/route_test.rb +64 -0
  46. data/lib/capsium/package/testing/test_case.rb +60 -0
  47. data/lib/capsium/package/testing/test_suite.rb +86 -0
  48. data/lib/capsium/package/testing.rb +20 -0
  49. data/lib/capsium/package/validator.rb +160 -0
  50. data/lib/capsium/package/verification.rb +32 -0
  51. data/lib/capsium/package/version.rb +204 -0
  52. data/lib/capsium/package.rb +92 -106
  53. data/lib/capsium/packager.rb +75 -6
  54. data/lib/capsium/reactor/authenticator.rb +180 -0
  55. data/lib/capsium/reactor/deploy.rb +71 -0
  56. data/lib/capsium/reactor/htpasswd.rb +154 -0
  57. data/lib/capsium/reactor/introspection.rb +91 -0
  58. data/lib/capsium/reactor/oauth2.rb +106 -0
  59. data/lib/capsium/reactor/serving.rb +75 -0
  60. data/lib/capsium/reactor/session.rb +82 -0
  61. data/lib/capsium/reactor.rb +93 -43
  62. data/lib/capsium/thor_ext.rb +1 -1
  63. data/lib/capsium/version.rb +1 -1
  64. data/lib/capsium.rb +7 -9
  65. data/sig/capsium/package/authentication.rbs +23 -0
  66. data/sig/capsium/package/authentication_config.rbs +71 -0
  67. data/sig/capsium/package/cipher.rbs +51 -0
  68. data/sig/capsium/package/composition.rbs +20 -0
  69. data/sig/capsium/package/dataset.rbs +28 -0
  70. data/sig/capsium/package/dependency_resolver.rbs +49 -0
  71. data/sig/capsium/package/encryption_config.rbs +35 -0
  72. data/sig/capsium/package/manifest.rbs +34 -0
  73. data/sig/capsium/package/manifest_config.rbs +38 -0
  74. data/sig/capsium/package/merged_view.rbs +46 -0
  75. data/sig/capsium/package/metadata.rbs +26 -0
  76. data/sig/capsium/package/metadata_config.rbs +49 -0
  77. data/sig/capsium/package/preparation.rbs +23 -0
  78. data/sig/capsium/package/routes.rbs +37 -0
  79. data/sig/capsium/package/routes_config.rbs +107 -0
  80. data/sig/capsium/package/security.rbs +72 -0
  81. data/sig/capsium/package/security_config.rbs +57 -0
  82. data/sig/capsium/package/signer.rbs +59 -0
  83. data/sig/capsium/package/storage.rbs +28 -0
  84. data/sig/capsium/package/storage_config.rbs +85 -0
  85. data/sig/capsium/package/store.rbs +37 -0
  86. data/sig/capsium/package/testing/config_test.rbs +21 -0
  87. data/sig/capsium/package/testing/context.rbs +14 -0
  88. data/sig/capsium/package/testing/data_validation_test.rbs +22 -0
  89. data/sig/capsium/package/testing/file_test.rbs +17 -0
  90. data/sig/capsium/package/testing/report.rbs +20 -0
  91. data/sig/capsium/package/testing/route_test.rbs +22 -0
  92. data/sig/capsium/package/testing/test_case.rbs +41 -0
  93. data/sig/capsium/package/testing/test_suite.rbs +20 -0
  94. data/sig/capsium/package/testing.rbs +7 -0
  95. data/sig/capsium/package/validator.rbs +29 -0
  96. data/sig/capsium/package/verification.rbs +24 -0
  97. data/sig/capsium/package/version.rbs +39 -0
  98. data/sig/capsium/package.rbs +61 -0
  99. data/sig/capsium/packager.rbs +32 -0
  100. data/sig/capsium/reactor/authenticator.rbs +40 -0
  101. data/sig/capsium/reactor/deploy.rbs +34 -0
  102. data/sig/capsium/reactor/htpasswd.rbs +36 -0
  103. data/sig/capsium/reactor/introspection.rbs +31 -0
  104. data/sig/capsium/reactor/oauth2.rbs +31 -0
  105. data/sig/capsium/reactor/serving.rbs +22 -0
  106. data/sig/capsium/reactor/session.rbs +36 -0
  107. data/sig/capsium/reactor.rbs +35 -0
  108. data/sig/capsium.rbs +3 -1
  109. metadata +108 -170
  110. data/lib/capsium/package/dataset_config.rb +0 -28
  111. data/lib/capsium/protector.rb +0 -103
@@ -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)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Capsium
4
- VERSION = "0.1.2"
4
+ VERSION = "0.3.0"
5
5
  end
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
- # Your code goes here...
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