capsium 0.1.1 → 0.2.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/.github/workflows/rake.yml +1 -4
- data/.github/workflows/release.yml +1 -2
- data/.gitignore +1 -0
- data/.rubocop.yml +34 -3
- data/CHANGELOG.md +58 -0
- data/Gemfile +4 -4
- data/LICENSE +21 -0
- data/README.adoc +177 -135
- data/Rakefile +8 -1
- data/bin/console +1 -1
- data/capsium.gemspec +16 -23
- data/exe/capsium +1 -1
- data/lib/capsium/cli/convert.rb +18 -0
- data/lib/capsium/cli/package.rb +86 -0
- data/lib/capsium/cli/reactor.rb +25 -0
- data/lib/capsium/cli.rb +5 -79
- data/lib/capsium/converters/jekyll.rb +62 -31
- data/lib/capsium/converters.rb +7 -0
- data/lib/capsium/package/dataset.rb +76 -56
- data/lib/capsium/package/manifest.rb +41 -50
- data/lib/capsium/package/manifest_config.rb +64 -0
- data/lib/capsium/package/metadata.rb +11 -29
- data/lib/capsium/package/metadata_config.rb +105 -0
- data/lib/capsium/package/routes.rb +51 -111
- data/lib/capsium/package/routes_config.rb +150 -0
- data/lib/capsium/package/security.rb +123 -0
- data/lib/capsium/package/security_config.rb +60 -0
- data/lib/capsium/package/storage.rb +26 -26
- data/lib/capsium/package/storage_config.rb +106 -0
- data/lib/capsium/package/validator.rb +149 -0
- data/lib/capsium/package.rb +84 -70
- data/lib/capsium/packager.rb +60 -62
- data/lib/capsium/reactor/introspection.rb +88 -0
- data/lib/capsium/reactor.rb +108 -36
- data/lib/capsium/version.rb +1 -1
- data/lib/capsium.rb +7 -9
- data/sig/capsium/package/dataset.rbs +28 -0
- data/sig/capsium/package/manifest.rbs +34 -0
- data/sig/capsium/package/manifest_config.rbs +38 -0
- data/sig/capsium/package/metadata.rbs +26 -0
- data/sig/capsium/package/metadata_config.rbs +49 -0
- data/sig/capsium/package/routes.rbs +37 -0
- data/sig/capsium/package/routes_config.rbs +74 -0
- data/sig/capsium/package/security.rbs +65 -0
- data/sig/capsium/package/security_config.rbs +57 -0
- data/sig/capsium/package/storage.rbs +28 -0
- data/sig/capsium/package/storage_config.rbs +61 -0
- data/sig/capsium/package/validator.rbs +29 -0
- data/sig/capsium/package.rbs +53 -0
- data/sig/capsium/packager.rbs +23 -0
- data/sig/capsium/reactor/introspection.rbs +31 -0
- data/sig/capsium/reactor.rbs +30 -0
- data/sig/capsium.rbs +3 -1
- metadata +63 -146
- data/images/005df7415a331c466ad2d9a42efdd212b6f42fe50b5fd3b3174c86c706f58244.png +0 -0
- data/images/0374025b3af99b8a473282c8cbbf9fcd29573cf41e586982f328f86c0690f43d.png +0 -0
- data/images/0bb4da785be40ef58e219470ebccb979325928b75453dc46bac23c6ee8a7a7cb.png +0 -0
- data/images/6aa294dccc81af594aacbe804e7ddffdc17eacc28357338108aea5d021d831ff.png +0 -0
- data/images/72dd3fbf3f4b475e27a0e7fb8137c475c32c41f8d222bcf62d6a9ccf102d9532.png +0 -0
- data/images/8772b6961d169738d7b0fa0b669b06fc2f40632d4c62586c7634fc17b93182a3.png +0 -0
- data/images/a998d842405933d45723606ff3f70162ec95b4ef30db25464a366184fd08fb9b.png +0 -0
- data/images/aa8980547e8c003d33273ab4d80e62da7f317bd7581b293c06d67f5331f24f31.png +0 -0
- data/images/bb78a872b539e0e9b2d80dee58acbb688f3f2727b324a5bf8bf417a69d94a166.png +0 -0
- data/images/c48fc83b17725d85fbb64d971196ebfccd8c5c757fe6aa5845303f6e315879b6.png +0 -0
- data/images/f08ef07308d08119ac2124bb7428c8bef17ef1ca70045696604d6e83015a9b91.png +0 -0
- data/images/f7514206111b695647eae9adfcf498ba3e0ff83ecfe25f3fc3ed8e9f04c5c726-1.png +0 -0
- data/images/f7514206111b695647eae9adfcf498ba3e0ff83ecfe25f3fc3ed8e9f04c5c726.png +0 -0
- data/lib/capsium/protector.rb +0 -95
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,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,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,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,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
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# A single route entry (ARCHITECTURE.md section 4). Kinds are
|
|
4
|
+
# discriminated by key, MECE: static file, dataset route, or dynamic
|
|
5
|
+
# handler (accepted-and-ignored; reactors respond 501).
|
|
6
|
+
class Route
|
|
7
|
+
DATASET_PATH_PREFIX: String
|
|
8
|
+
|
|
9
|
+
attr_accessor path: String?
|
|
10
|
+
attr_accessor resource: String?
|
|
11
|
+
attr_accessor headers: Hash[String, String]?
|
|
12
|
+
attr_accessor headers_file: String?
|
|
13
|
+
attr_accessor visibility: String
|
|
14
|
+
attr_accessor dataset: String?
|
|
15
|
+
attr_accessor access_control: Hash[String, untyped]?
|
|
16
|
+
attr_accessor http_method: String?
|
|
17
|
+
attr_accessor handler: String?
|
|
18
|
+
|
|
19
|
+
def initialize: (?path: String? path, ?resource: String? resource,
|
|
20
|
+
?headers: Hash[String, String]? headers,
|
|
21
|
+
?headers_file: String? headers_file,
|
|
22
|
+
?visibility: String? visibility, ?dataset: String? dataset,
|
|
23
|
+
?access_control: Hash[String, untyped]? access_control,
|
|
24
|
+
?http_method: String? http_method,
|
|
25
|
+
?handler: String? handler) -> void
|
|
26
|
+
|
|
27
|
+
def self.from_json: (String json) -> instance
|
|
28
|
+
|
|
29
|
+
def kind: () -> (:resource | :dataset | :handler)
|
|
30
|
+
|
|
31
|
+
def dataset_route?: () -> bool
|
|
32
|
+
|
|
33
|
+
def handler_route?: () -> bool
|
|
34
|
+
|
|
35
|
+
def fs_path: (String package_path) -> String?
|
|
36
|
+
|
|
37
|
+
def mime: (Manifest manifest) -> String?
|
|
38
|
+
|
|
39
|
+
def validate_target: (String package_path, Storage storage) -> void
|
|
40
|
+
|
|
41
|
+
def to_json: (*untyped args) -> String
|
|
42
|
+
def to_hash: () -> Hash[String, untyped]
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Canonical routes.json model: optional top-level "index" plus a
|
|
46
|
+
# "routes" array. The legacy object/target form is normalized on
|
|
47
|
+
# read; writers emit only the canonical form.
|
|
48
|
+
class RoutesConfig
|
|
49
|
+
attr_accessor index: String?
|
|
50
|
+
attr_accessor routes: Array[Route]
|
|
51
|
+
|
|
52
|
+
def initialize: (?index: String? index, ?routes: Array[Route] routes) -> void
|
|
53
|
+
|
|
54
|
+
def self.from_json: (String json) -> instance
|
|
55
|
+
|
|
56
|
+
def routes_from_json: (RoutesConfig model, untyped value) -> void
|
|
57
|
+
|
|
58
|
+
def routes_to_json: (RoutesConfig model, Hash[String, untyped] doc) -> void
|
|
59
|
+
|
|
60
|
+
def resolve: (String path) -> Route?
|
|
61
|
+
|
|
62
|
+
def add: (String path, String target) -> Route
|
|
63
|
+
|
|
64
|
+
def update: (String path, String updated_path, String updated_target) -> Route
|
|
65
|
+
|
|
66
|
+
def remove: (String path) -> Array[Route]
|
|
67
|
+
|
|
68
|
+
def sort!: () -> RoutesConfig
|
|
69
|
+
|
|
70
|
+
def to_json: (*untyped args) -> String
|
|
71
|
+
def to_hash: () -> Hash[String, untyped]
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Loads, generates and verifies security.json (ARCHITECTURE.md
|
|
4
|
+
# section 6). Checksums cover every file in the package except
|
|
5
|
+
# security.json itself.
|
|
6
|
+
class Security
|
|
7
|
+
class IntegrityError < Capsium::Error
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# A file whose SHA-256 does not match the recorded checksum.
|
|
11
|
+
class ChecksumMismatch
|
|
12
|
+
attr_reader path: String
|
|
13
|
+
attr_reader expected: String
|
|
14
|
+
attr_reader actual: String
|
|
15
|
+
|
|
16
|
+
def initialize: (path: String path, expected: String expected, actual: String actual) -> void
|
|
17
|
+
|
|
18
|
+
def message: () -> String
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# A checksum listed for a file that is missing on disk.
|
|
22
|
+
class MissingFile
|
|
23
|
+
attr_reader path: String
|
|
24
|
+
attr_reader expected: String
|
|
25
|
+
|
|
26
|
+
def initialize: (path: String path, expected: String expected) -> void
|
|
27
|
+
|
|
28
|
+
def message: () -> String
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# A file on disk not covered by any checksum.
|
|
32
|
+
class UncheckedFile
|
|
33
|
+
attr_reader path: String
|
|
34
|
+
|
|
35
|
+
def initialize: (path: String path) -> void
|
|
36
|
+
|
|
37
|
+
def message: () -> String
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
attr_reader path: String
|
|
41
|
+
attr_reader config: SecurityConfig?
|
|
42
|
+
|
|
43
|
+
def initialize: (String path, ?SecurityConfig? config) -> void
|
|
44
|
+
|
|
45
|
+
def self.generate: (String package_path) -> Security
|
|
46
|
+
|
|
47
|
+
def self.checksums_for: (String package_path) -> Hash[String, String]
|
|
48
|
+
|
|
49
|
+
def self.package_files: (String package_path) -> Array[String]
|
|
50
|
+
|
|
51
|
+
def present?: () -> bool
|
|
52
|
+
|
|
53
|
+
def checksums: () -> Hash[String, String]
|
|
54
|
+
|
|
55
|
+
def verify: (String package_path) -> Array[ChecksumMismatch | MissingFile | UncheckedFile]
|
|
56
|
+
|
|
57
|
+
def verify!: (String package_path) -> void
|
|
58
|
+
|
|
59
|
+
def save_to_file: (?String output_path) -> void
|
|
60
|
+
|
|
61
|
+
def to_json: (*untyped args) -> String
|
|
62
|
+
def to_hash: () -> Hash[String, untyped]
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# The "integrityChecks" object of security.json: SHA-256 checksums
|
|
4
|
+
# keyed by package-relative path.
|
|
5
|
+
class IntegrityChecks
|
|
6
|
+
ALGORITHMS: Array[String]
|
|
7
|
+
|
|
8
|
+
attr_accessor checksum_algorithm: String
|
|
9
|
+
attr_accessor checksums: Hash[String, String]
|
|
10
|
+
|
|
11
|
+
def initialize: (?checksum_algorithm: String? checksum_algorithm,
|
|
12
|
+
?checksums: Hash[String, String] checksums) -> void
|
|
13
|
+
|
|
14
|
+
def to_json: (*untyped args) -> String
|
|
15
|
+
def to_hash: () -> Hash[String, untyped]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# The "digitalSignatures" object of security.json (parsed only;
|
|
19
|
+
# signing is a later phase).
|
|
20
|
+
class DigitalSignatures
|
|
21
|
+
attr_accessor public_key: String?
|
|
22
|
+
attr_accessor signature_file: String?
|
|
23
|
+
|
|
24
|
+
def initialize: (?public_key: String? public_key,
|
|
25
|
+
?signature_file: String? signature_file) -> void
|
|
26
|
+
|
|
27
|
+
def to_json: (*untyped args) -> String
|
|
28
|
+
def to_hash: () -> Hash[String, untyped]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# The "security" object of security.json.
|
|
32
|
+
class SecurityData
|
|
33
|
+
attr_accessor integrity_checks: IntegrityChecks?
|
|
34
|
+
attr_accessor digital_signatures: DigitalSignatures?
|
|
35
|
+
|
|
36
|
+
def initialize: (?integrity_checks: IntegrityChecks? integrity_checks,
|
|
37
|
+
?digital_signatures: DigitalSignatures? digital_signatures) -> void
|
|
38
|
+
|
|
39
|
+
def to_json: (*untyped args) -> String
|
|
40
|
+
def to_hash: () -> Hash[String, untyped]
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Canonical security.json model (generated at pack time).
|
|
44
|
+
class SecurityConfig
|
|
45
|
+
attr_accessor security: SecurityData?
|
|
46
|
+
|
|
47
|
+
def initialize: (?security: SecurityData? security) -> void
|
|
48
|
+
|
|
49
|
+
def self.from_json: (String json) -> instance
|
|
50
|
+
|
|
51
|
+
def checksums: () -> Hash[String, String]
|
|
52
|
+
|
|
53
|
+
def to_json: (*untyped args) -> String
|
|
54
|
+
def to_hash: () -> Hash[String, untyped]
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Loads and writes storage.json and instantiates its datasets
|
|
4
|
+
# (ARCHITECTURE.md section 5).
|
|
5
|
+
class Storage
|
|
6
|
+
attr_reader path: String
|
|
7
|
+
attr_reader config: StorageConfig
|
|
8
|
+
attr_reader datasets: Array[Dataset]
|
|
9
|
+
|
|
10
|
+
def initialize: (String path) -> void
|
|
11
|
+
|
|
12
|
+
def data_sets: () -> Hash[String, DatasetConfig]
|
|
13
|
+
|
|
14
|
+
def load_datasets: () -> Array[Dataset]
|
|
15
|
+
|
|
16
|
+
def dataset: (String name) -> Dataset?
|
|
17
|
+
|
|
18
|
+
def dataset_names: () -> Array[String]
|
|
19
|
+
|
|
20
|
+
def empty?: () -> bool
|
|
21
|
+
|
|
22
|
+
def save_to_file: (?String output_path) -> void
|
|
23
|
+
|
|
24
|
+
def to_json: (*untyped args) -> String
|
|
25
|
+
def to_hash: () -> Hash[String, untyped]
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# A single dataset entry (ARCHITECTURE.md section 5): schema-backed
|
|
4
|
+
# file (via "source") or SQLite ("databaseFile" + "table").
|
|
5
|
+
class DatasetConfig
|
|
6
|
+
FORMATS: Hash[String, String]
|
|
7
|
+
SCHEMA_TYPES: Array[String]
|
|
8
|
+
|
|
9
|
+
attr_accessor source: String?
|
|
10
|
+
attr_accessor schema_file: String?
|
|
11
|
+
attr_accessor schema_type: String?
|
|
12
|
+
attr_accessor database_file: String?
|
|
13
|
+
attr_accessor table: String?
|
|
14
|
+
|
|
15
|
+
def initialize: (?source: String? source, ?schema_file: String? schema_file,
|
|
16
|
+
?schema_type: String? schema_type,
|
|
17
|
+
?database_file: String? database_file,
|
|
18
|
+
?table: String? table) -> void
|
|
19
|
+
|
|
20
|
+
def format: () -> String
|
|
21
|
+
|
|
22
|
+
def sqlite?: () -> bool
|
|
23
|
+
|
|
24
|
+
def backing_file: () -> String
|
|
25
|
+
|
|
26
|
+
def to_dataset: (String name, String package_path) -> Dataset
|
|
27
|
+
|
|
28
|
+
def to_json: (*untyped args) -> String
|
|
29
|
+
def to_hash: () -> Hash[String, untyped]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# The "storage" object holding the dataSets map.
|
|
33
|
+
class StorageData
|
|
34
|
+
attr_accessor data_sets: Hash[String, DatasetConfig]
|
|
35
|
+
|
|
36
|
+
def initialize: (?data_sets: Hash[String, DatasetConfig] data_sets) -> void
|
|
37
|
+
|
|
38
|
+
def data_sets_from_json: (StorageData model, untyped value) -> void
|
|
39
|
+
|
|
40
|
+
def data_sets_to_json: (StorageData model, Hash[String, untyped] doc) -> void
|
|
41
|
+
|
|
42
|
+
def to_json: (*untyped args) -> String
|
|
43
|
+
def to_hash: () -> Hash[String, untyped]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Canonical storage.json model. The legacy array form is normalized
|
|
47
|
+
# on read; writers emit only the canonical form.
|
|
48
|
+
class StorageConfig
|
|
49
|
+
attr_accessor storage: StorageData?
|
|
50
|
+
|
|
51
|
+
def initialize: (?storage: StorageData? storage) -> void
|
|
52
|
+
|
|
53
|
+
def self.from_json: (String json) -> instance
|
|
54
|
+
|
|
55
|
+
def data_sets: () -> Hash[String, DatasetConfig]
|
|
56
|
+
|
|
57
|
+
def to_json: (*untyped args) -> String
|
|
58
|
+
def to_hash: () -> Hash[String, untyped]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Package
|
|
3
|
+
# Validates a package directory or .cap file against the canonical
|
|
4
|
+
# schemas (ARCHITECTURE.md sections 2-6) and reports one result per
|
|
5
|
+
# check. Used by `capsium package validate`.
|
|
6
|
+
class Validator
|
|
7
|
+
EXTERNAL_REFERENCE_PATTERN: Regexp
|
|
8
|
+
TEXT_CONTENT_MIME: Regexp
|
|
9
|
+
|
|
10
|
+
# One named validation check with its problems; ok when messages
|
|
11
|
+
# is empty.
|
|
12
|
+
class CheckResult
|
|
13
|
+
attr_reader name: String
|
|
14
|
+
attr_reader ok: bool
|
|
15
|
+
attr_reader messages: Array[String]
|
|
16
|
+
|
|
17
|
+
def initialize: (name: String name, ok: bool ok, messages: Array[String] messages) -> void
|
|
18
|
+
|
|
19
|
+
def ok?: () -> bool
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def initialize: (String package_path) -> void
|
|
23
|
+
|
|
24
|
+
def run: () -> Array[CheckResult]
|
|
25
|
+
|
|
26
|
+
def valid?: () -> bool
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
# A Capsium package loaded from a directory or a .cap file
|
|
3
|
+
# (ARCHITECTURE.md section 1).
|
|
4
|
+
class Package
|
|
5
|
+
MANIFEST_FILE: String
|
|
6
|
+
METADATA_FILE: String
|
|
7
|
+
STORAGE_FILE: String
|
|
8
|
+
ROUTES_FILE: String
|
|
9
|
+
SECURITY_FILE: String
|
|
10
|
+
CONTENT_DIR: String
|
|
11
|
+
DATA_DIR: String
|
|
12
|
+
|
|
13
|
+
attr_reader name: String
|
|
14
|
+
attr_reader path: String
|
|
15
|
+
attr_reader manifest: Manifest
|
|
16
|
+
attr_reader metadata: Metadata
|
|
17
|
+
attr_reader routes: Routes
|
|
18
|
+
attr_reader storage: Storage
|
|
19
|
+
attr_reader security: Security
|
|
20
|
+
attr_reader load_type: Symbol
|
|
21
|
+
|
|
22
|
+
def initialize: (String | Pathname path, ?load_type: Symbol? load_type) -> void
|
|
23
|
+
|
|
24
|
+
def prepare_package: (Pathname path) -> (String | Pathname)
|
|
25
|
+
|
|
26
|
+
def solidify: () -> void
|
|
27
|
+
|
|
28
|
+
def decompress_cap_file: (String | Pathname file_path) -> String
|
|
29
|
+
|
|
30
|
+
def load_package: () -> void
|
|
31
|
+
|
|
32
|
+
# Removes the temporary extraction directory of a .cap-loaded
|
|
33
|
+
# package; a no-op for directory loads.
|
|
34
|
+
def cleanup: () -> void
|
|
35
|
+
|
|
36
|
+
def datasets: () -> Array[Dataset]
|
|
37
|
+
|
|
38
|
+
# The .cap file this package was loaded from, or nil when loaded
|
|
39
|
+
# from a directory.
|
|
40
|
+
def cap_file_path: () -> String?
|
|
41
|
+
|
|
42
|
+
def content_files: () -> Array[String]
|
|
43
|
+
|
|
44
|
+
def determine_load_type: (String | Pathname path) -> Symbol
|
|
45
|
+
|
|
46
|
+
# Verifies the package against security.json (ARCHITECTURE.md
|
|
47
|
+
# section 6). Returns a list of typed errors; empty when no
|
|
48
|
+
# security.json is present or all checksums match.
|
|
49
|
+
def verify_integrity: () -> Array[Security::ChecksumMismatch | Security::MissingFile | Security::UncheckedFile]
|
|
50
|
+
|
|
51
|
+
def verify_integrity!: () -> void
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
# Packs a package directory into a .cap file (generating
|
|
3
|
+
# security.json) and unpacks .cap files with zip-slip protection.
|
|
4
|
+
class Packager
|
|
5
|
+
class FileAlreadyExistsError < StandardError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Raised when a .cap archive contains an entry whose name would be
|
|
9
|
+
# written outside the destination directory (zip-slip).
|
|
10
|
+
class UnsafeEntryError < Capsium::Error
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
DRIVE_LETTER_PATTERN: Regexp
|
|
14
|
+
|
|
15
|
+
def pack: (Package package, ?Hash[Symbol, untyped] options) -> String
|
|
16
|
+
|
|
17
|
+
def unpack: (String cap_file_path, String destination) -> void
|
|
18
|
+
|
|
19
|
+
def compress_package: (Package package, String cap_file) -> void
|
|
20
|
+
|
|
21
|
+
def relative_path_current: (String absolute_path) -> String
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Capsium
|
|
2
|
+
class Reactor
|
|
3
|
+
# Monitoring HTTP API reports for the package this reactor serves
|
|
4
|
+
# (ARCHITECTURE.md section 7).
|
|
5
|
+
class Introspection
|
|
6
|
+
METADATA_PATH: String
|
|
7
|
+
ROUTES_PATH: String
|
|
8
|
+
CONTENT_HASHES_PATH: String
|
|
9
|
+
CONTENT_VALIDITY_PATH: String
|
|
10
|
+
PATHS: Array[String]
|
|
11
|
+
|
|
12
|
+
attr_reader package: Package
|
|
13
|
+
|
|
14
|
+
def initialize: (Package package) -> void
|
|
15
|
+
|
|
16
|
+
def endpoint?: (String path) -> bool
|
|
17
|
+
|
|
18
|
+
# The report body for an introspection endpoint, or nil when the
|
|
19
|
+
# path is not an introspection endpoint.
|
|
20
|
+
def report_for: (String path) -> Hash[Symbol, untyped]?
|
|
21
|
+
|
|
22
|
+
def metadata_report: () -> Hash[Symbol, untyped]
|
|
23
|
+
|
|
24
|
+
def routes_report: () -> Hash[Symbol, untyped]
|
|
25
|
+
|
|
26
|
+
def content_hashes_report: () -> Hash[Symbol, untyped]
|
|
27
|
+
|
|
28
|
+
def content_validity_report: () -> Hash[Symbol, untyped]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|