igniter-ledger 0.5.2
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 +7 -0
- data/README.md +481 -0
- data/examples/intelligent_ledger/availability_boundary_ledger.rb +1190 -0
- data/examples/intelligent_ledger/availability_deriver.rb +150 -0
- data/examples/intelligent_ledger/availability_ledger.rb +197 -0
- data/examples/intelligent_ledger/ledger_boundary.rb +180 -0
- data/examples/store_poc.rb +45 -0
- data/exe/igniter-ledger-server +111 -0
- data/exe/igniter-store-server +6 -0
- data/ext/igniter_store_native/Cargo.toml +28 -0
- data/ext/igniter_store_native/extconf.rb +6 -0
- data/ext/igniter_store_native/src/fact.rs +303 -0
- data/ext/igniter_store_native/src/fact_log.rs +180 -0
- data/ext/igniter_store_native/src/file_backend.rs +91 -0
- data/ext/igniter_store_native/src/lib.rs +55 -0
- data/lib/igniter/ledger.rb +7 -0
- data/lib/igniter/store/access_path.rb +84 -0
- data/lib/igniter/store/change_event.rb +65 -0
- data/lib/igniter/store/changefeed_buffer.rb +585 -0
- data/lib/igniter/store/codecs.rb +253 -0
- data/lib/igniter/store/contractable_receipt_sink.rb +172 -0
- data/lib/igniter/store/fact.rb +121 -0
- data/lib/igniter/store/fact_log.rb +103 -0
- data/lib/igniter/store/file_backend.rb +269 -0
- data/lib/igniter/store/http_adapter.rb +413 -0
- data/lib/igniter/store/igniter_store.rb +838 -0
- data/lib/igniter/store/mcp_adapter.rb +403 -0
- data/lib/igniter/store/native.rb +80 -0
- data/lib/igniter/store/network_backend.rb +159 -0
- data/lib/igniter/store/protocol/handlers/access_path_handler.rb +38 -0
- data/lib/igniter/store/protocol/handlers/command_handler.rb +59 -0
- data/lib/igniter/store/protocol/handlers/derivation_handler.rb +27 -0
- data/lib/igniter/store/protocol/handlers/effect_handler.rb +65 -0
- data/lib/igniter/store/protocol/handlers/history_handler.rb +24 -0
- data/lib/igniter/store/protocol/handlers/projection_handler.rb +41 -0
- data/lib/igniter/store/protocol/handlers/relation_handler.rb +43 -0
- data/lib/igniter/store/protocol/handlers/store_handler.rb +24 -0
- data/lib/igniter/store/protocol/handlers/subscription_handler.rb +24 -0
- data/lib/igniter/store/protocol/interpreter.rb +447 -0
- data/lib/igniter/store/protocol/receipt.rb +96 -0
- data/lib/igniter/store/protocol/sync_profile.rb +53 -0
- data/lib/igniter/store/protocol/wire_envelope.rb +214 -0
- data/lib/igniter/store/protocol.rb +27 -0
- data/lib/igniter/store/read_cache.rb +163 -0
- data/lib/igniter/store/schema_graph.rb +248 -0
- data/lib/igniter/store/segmented_file_backend.rb +699 -0
- data/lib/igniter/store/server_config.rb +55 -0
- data/lib/igniter/store/server_logger.rb +64 -0
- data/lib/igniter/store/server_metrics.rb +222 -0
- data/lib/igniter/store/store_server.rb +597 -0
- data/lib/igniter/store/subscription_registry.rb +73 -0
- data/lib/igniter/store/tbackend_adapter_descriptor.rb +307 -0
- data/lib/igniter/store/tcp_adapter.rb +127 -0
- data/lib/igniter/store/wire_protocol.rb +42 -0
- data/lib/igniter/store.rb +64 -0
- data/lib/igniter-ledger.rb +4 -0
- data/lib/igniter-store.rb +5 -0
- metadata +212 -0
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
# Command descriptors are metadata-only. Ledger records the app-owned
|
|
8
|
+
# mutation contract but never executes application commands.
|
|
9
|
+
class CommandHandler
|
|
10
|
+
REQUIRED = %i[name owner operation].freeze
|
|
11
|
+
OPERATIONS = %i[record_append record_update history_append none].freeze
|
|
12
|
+
|
|
13
|
+
def initialize(store) = @store = store
|
|
14
|
+
|
|
15
|
+
def call(descriptor)
|
|
16
|
+
missing = REQUIRED.select { |field| descriptor[field].nil? }
|
|
17
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :command) if missing.any?
|
|
18
|
+
|
|
19
|
+
name = descriptor[:name].to_sym
|
|
20
|
+
owner = descriptor[:owner].to_sym
|
|
21
|
+
operation = descriptor[:operation].to_sym
|
|
22
|
+
unless OPERATIONS.include?(operation)
|
|
23
|
+
return Receipt.rejection("Unsupported command operation: #{operation.inspect}", kind: :command)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
normalized = descriptor.merge(
|
|
27
|
+
name: name,
|
|
28
|
+
owner: owner,
|
|
29
|
+
operation: operation,
|
|
30
|
+
target_shape: token(descriptor[:target_shape] || target_shape_for(operation)),
|
|
31
|
+
boundary: token(descriptor[:boundary] || :app),
|
|
32
|
+
mutation_intent: token(descriptor[:mutation_intent] || operation)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
@store.schema_graph.register_command_descriptor(normalized)
|
|
36
|
+
Receipt.accepted(kind: :command, name: name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def target_shape_for(operation)
|
|
42
|
+
case operation
|
|
43
|
+
when :record_append, :record_update
|
|
44
|
+
:store
|
|
45
|
+
when :history_append
|
|
46
|
+
:history
|
|
47
|
+
else
|
|
48
|
+
:none
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def token(value)
|
|
53
|
+
value.nil? || value.is_a?(Symbol) ? value : value.to_sym
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
# Derivation descriptors are metadata-only in OP1.
|
|
8
|
+
# Execution requires a rule callable which external descriptor packets cannot carry.
|
|
9
|
+
# The descriptor is stored in the schema graph for OP2 introspection.
|
|
10
|
+
class DerivationHandler
|
|
11
|
+
REQUIRED = %i[name inputs output].freeze
|
|
12
|
+
|
|
13
|
+
def initialize(store) = @store = store
|
|
14
|
+
|
|
15
|
+
def call(descriptor)
|
|
16
|
+
missing = REQUIRED.select { |f| descriptor[f].nil? }
|
|
17
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :derivation) if missing.any?
|
|
18
|
+
|
|
19
|
+
name = descriptor[:name].to_sym
|
|
20
|
+
warnings = ["derivation: #{name.inspect} registered as metadata only; attach a rule callable via register_derivation to enable execution"]
|
|
21
|
+
Receipt.accepted(kind: :derivation, name: name, warnings: warnings)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
# Effect descriptors describe the app-boundary persistence intent that a
|
|
8
|
+
# command lowers to. They are metadata-only and do not execute effects.
|
|
9
|
+
class EffectHandler
|
|
10
|
+
REQUIRED = %i[name owner store_op write_kind].freeze
|
|
11
|
+
STORE_OPS = %i[store_write store_append none].freeze
|
|
12
|
+
WRITE_KINDS = %i[insert update append none].freeze
|
|
13
|
+
|
|
14
|
+
def initialize(store) = @store = store
|
|
15
|
+
|
|
16
|
+
def call(descriptor)
|
|
17
|
+
missing = REQUIRED.select { |field| descriptor[field].nil? }
|
|
18
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :effect) if missing.any?
|
|
19
|
+
|
|
20
|
+
name = descriptor[:name].to_sym
|
|
21
|
+
owner = descriptor[:owner].to_sym
|
|
22
|
+
store_op = descriptor[:store_op].to_sym
|
|
23
|
+
write_kind = descriptor[:write_kind].to_sym
|
|
24
|
+
unless STORE_OPS.include?(store_op)
|
|
25
|
+
return Receipt.rejection("Unsupported effect store_op: #{store_op.inspect}", kind: :effect)
|
|
26
|
+
end
|
|
27
|
+
unless WRITE_KINDS.include?(write_kind)
|
|
28
|
+
return Receipt.rejection("Unsupported effect write_kind: #{write_kind.inspect}", kind: :effect)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
normalized = descriptor.merge(
|
|
32
|
+
name: name,
|
|
33
|
+
owner: owner,
|
|
34
|
+
store_op: store_op,
|
|
35
|
+
write_kind: write_kind,
|
|
36
|
+
lowers_to: token(descriptor[:lowers_to] || lowers_to_for(store_op)),
|
|
37
|
+
boundary: token(descriptor[:boundary] || :app)
|
|
38
|
+
)
|
|
39
|
+
normalized[:source_operation] = token(descriptor[:source_operation]) if descriptor.key?(:source_operation)
|
|
40
|
+
|
|
41
|
+
@store.schema_graph.register_effect_descriptor(normalized)
|
|
42
|
+
Receipt.accepted(kind: :effect, name: name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def lowers_to_for(store_op)
|
|
48
|
+
case store_op
|
|
49
|
+
when :store_write
|
|
50
|
+
:store_t
|
|
51
|
+
when :store_append
|
|
52
|
+
:history_t
|
|
53
|
+
else
|
|
54
|
+
:none
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def token(value)
|
|
59
|
+
value.nil? || value.is_a?(Symbol) ? value : value.to_sym
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
class HistoryHandler
|
|
8
|
+
REQUIRED = %i[name key].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(store) = @store = store
|
|
11
|
+
|
|
12
|
+
def call(descriptor)
|
|
13
|
+
missing = REQUIRED.select { |f| descriptor[f].nil? }
|
|
14
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :history) if missing.any?
|
|
15
|
+
|
|
16
|
+
name = descriptor[:name].to_sym
|
|
17
|
+
@store.schema_graph.register_history_descriptor(descriptor.merge(name: name))
|
|
18
|
+
Receipt.accepted(kind: :history, name: name)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
class ProjectionHandler
|
|
8
|
+
def initialize(store) = @store = store
|
|
9
|
+
|
|
10
|
+
def call(descriptor)
|
|
11
|
+
return Receipt.rejection("Missing required fields: name", kind: :projection) if descriptor[:name].nil?
|
|
12
|
+
if descriptor[:reads].nil? && descriptor[:source].nil?
|
|
13
|
+
return Receipt.rejection("Missing required fields: reads or source", kind: :projection)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
name = descriptor[:name].to_sym
|
|
17
|
+
source = descriptor[:source]
|
|
18
|
+
reads = descriptor[:reads] || source
|
|
19
|
+
reads = Array(reads).map(&:to_sym)
|
|
20
|
+
relations = Array(descriptor[:relations]).map(&:to_sym)
|
|
21
|
+
consumer_hint = (descriptor[:consumer_hint] || :protocol_client).to_sym
|
|
22
|
+
mode = descriptor.fetch(:mode, :on_demand)
|
|
23
|
+
reactive = descriptor.key?(:reactive) ? !!descriptor[:reactive] : mode == :materialized
|
|
24
|
+
|
|
25
|
+
@store.register_projection(
|
|
26
|
+
ProjectionPath.new(
|
|
27
|
+
name: name,
|
|
28
|
+
reads: reads,
|
|
29
|
+
relations: relations,
|
|
30
|
+
consumer_hint: consumer_hint,
|
|
31
|
+
reactive: reactive
|
|
32
|
+
)
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
Receipt.accepted(kind: :projection, name: name)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
class RelationHandler
|
|
8
|
+
REQUIRED = %i[name from to].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(store) = @store = store
|
|
11
|
+
|
|
12
|
+
def call(descriptor)
|
|
13
|
+
missing = REQUIRED.select { |f| descriptor[f].nil? }
|
|
14
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :relation) if missing.any?
|
|
15
|
+
|
|
16
|
+
name = descriptor[:name].to_sym
|
|
17
|
+
from_desc = descriptor[:from]
|
|
18
|
+
to_desc = descriptor[:to]
|
|
19
|
+
|
|
20
|
+
unless from_desc.is_a?(Hash) && from_desc[:store] && from_desc[:key]
|
|
21
|
+
return Receipt.rejection("from: must be { store:, key: }", kind: :relation, name: name)
|
|
22
|
+
end
|
|
23
|
+
unless to_desc.is_a?(Hash) && to_desc[:store] && to_desc[:field]
|
|
24
|
+
return Receipt.rejection("to: must be { store:, field: }", kind: :relation, name: name)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
cardinality = descriptor.fetch(:cardinality, :many)
|
|
28
|
+
warnings = cardinality == :one ? ["cardinality: :one is informational only; engine always stores as G-Set"] : []
|
|
29
|
+
|
|
30
|
+
@store.register_relation(
|
|
31
|
+
name,
|
|
32
|
+
source: to_desc[:store].to_sym,
|
|
33
|
+
partition: to_desc[:field].to_sym,
|
|
34
|
+
target: from_desc[:store].to_sym
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
Receipt.accepted(kind: :relation, name: name, warnings: warnings)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
class StoreHandler
|
|
8
|
+
REQUIRED = %i[name key].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(store) = @store = store
|
|
11
|
+
|
|
12
|
+
def call(descriptor)
|
|
13
|
+
missing = REQUIRED.select { |f| descriptor[f].nil? }
|
|
14
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :store) if missing.any?
|
|
15
|
+
|
|
16
|
+
name = descriptor[:name].to_sym
|
|
17
|
+
@store.schema_graph.register_store_descriptor(descriptor.merge(name: name))
|
|
18
|
+
Receipt.accepted(kind: :store, name: name)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Igniter
|
|
4
|
+
module Store
|
|
5
|
+
module Protocol
|
|
6
|
+
module Handlers
|
|
7
|
+
class SubscriptionHandler
|
|
8
|
+
REQUIRED = %i[name source].freeze
|
|
9
|
+
|
|
10
|
+
def initialize(store) = @store = store
|
|
11
|
+
|
|
12
|
+
def call(descriptor)
|
|
13
|
+
missing = REQUIRED.select { |f| descriptor[f].nil? }
|
|
14
|
+
return Receipt.rejection("Missing required fields: #{missing.join(", ")}", kind: :subscription) if missing.any?
|
|
15
|
+
|
|
16
|
+
name = descriptor[:name].to_sym
|
|
17
|
+
@store.schema_graph.register_subscription_descriptor(descriptor.merge(name: name))
|
|
18
|
+
Receipt.accepted(kind: :subscription, name: name)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|