textus 0.53.0 → 0.54.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/CHANGELOG.md +34 -0
- data/SPEC.md +2 -5
- data/docs/architecture/README.md +149 -159
- data/lib/textus/action/accept.rb +0 -9
- data/lib/textus/action/audit.rb +0 -2
- data/lib/textus/action/blame.rb +0 -2
- data/lib/textus/action/boot.rb +2 -11
- data/lib/textus/action/data_mv.rb +1 -3
- data/lib/textus/action/deps.rb +1 -15
- data/lib/textus/action/doctor.rb +0 -2
- data/lib/textus/action/drain.rb +4 -6
- data/lib/textus/action/enqueue.rb +3 -5
- data/lib/textus/action/get.rb +1 -3
- data/lib/textus/action/ingest.rb +135 -0
- data/lib/textus/action/jobs.rb +1 -3
- data/lib/textus/action/key_delete.rb +0 -10
- data/lib/textus/action/key_delete_prefix.rb +1 -3
- data/lib/textus/action/key_mv.rb +1 -15
- data/lib/textus/action/key_mv_prefix.rb +1 -3
- data/lib/textus/action/list.rb +0 -2
- data/lib/textus/action/propose.rb +0 -2
- data/lib/textus/action/published.rb +0 -2
- data/lib/textus/action/pulse.rb +7 -38
- data/lib/textus/action/put.rb +1 -12
- data/lib/textus/action/rdeps.rb +2 -12
- data/lib/textus/action/reject.rb +0 -12
- data/lib/textus/action/rule_explain.rb +0 -4
- data/lib/textus/action/rule_lint.rb +1 -3
- data/lib/textus/action/rule_list.rb +0 -4
- data/lib/textus/action/schema_envelope.rb +0 -2
- data/lib/textus/action/uid.rb +0 -2
- data/lib/textus/action/where.rb +0 -2
- data/lib/textus/action/write_verb.rb +4 -18
- data/lib/textus/boot.rb +33 -109
- data/lib/textus/command.rb +6 -1
- data/lib/textus/container.rb +1 -1
- data/lib/textus/core/freshness/evaluator.rb +18 -34
- data/lib/textus/doctor/check/notebook_sources.rb +50 -0
- data/lib/textus/doctor/check/raw_asset_paths.rb +50 -0
- data/lib/textus/doctor/check.rb +0 -1
- data/lib/textus/doctor.rb +2 -42
- data/lib/textus/envelope/reader.rb +46 -0
- data/lib/textus/envelope/writer.rb +209 -0
- data/lib/textus/errors.rb +8 -4
- data/lib/textus/{entry → format}/base.rb +1 -6
- data/lib/textus/{entry → format}/json.rb +1 -4
- data/lib/textus/{entry → format}/markdown.rb +2 -6
- data/lib/textus/{entry → format}/text.rb +2 -6
- data/lib/textus/{entry → format}/yaml.rb +1 -4
- data/lib/textus/{entry.rb → format.rb} +9 -10
- data/lib/textus/gate/auth.rb +58 -12
- data/lib/textus/gate.rb +2 -0
- data/lib/textus/init.rb +12 -75
- data/lib/textus/jobs/base.rb +23 -0
- data/lib/textus/jobs/materialize.rb +20 -0
- data/lib/textus/{background → jobs}/plan.rb +1 -1
- data/lib/textus/jobs/planner.rb +101 -0
- data/lib/textus/jobs/retention.rb +48 -0
- data/lib/textus/jobs/sweep.rb +27 -0
- data/lib/textus/{background → jobs}/worker.rb +2 -2
- data/lib/textus/jobs.rb +15 -0
- data/lib/textus/key/path.rb +1 -1
- data/lib/textus/manifest/entry/base.rb +4 -19
- data/lib/textus/manifest/entry/parser.rb +27 -10
- data/lib/textus/manifest/entry/produced.rb +13 -34
- data/lib/textus/manifest/entry/publish/to_paths.rb +4 -4
- data/lib/textus/manifest/entry/publish.rb +0 -12
- data/lib/textus/manifest/entry/validators/format_matrix.rb +1 -1
- data/lib/textus/manifest/entry/validators.rb +0 -1
- data/lib/textus/manifest/policy/source.rb +14 -62
- data/lib/textus/manifest/policy.rb +2 -5
- data/lib/textus/manifest/resolver.rb +2 -2
- data/lib/textus/manifest/schema/keys.rb +6 -20
- data/lib/textus/manifest/schema/validator.rb +3 -11
- data/lib/textus/manifest/schema/vocabulary.rb +1 -0
- data/lib/textus/ports/{queue → job_store}/job.rb +1 -1
- data/lib/textus/ports/{queue.rb → job_store.rb} +6 -13
- data/lib/textus/produce/engine.rb +50 -0
- data/lib/textus/produce/render.rb +18 -0
- data/lib/textus/session.rb +1 -1
- data/lib/textus/store.rb +1 -10
- data/lib/textus/surfaces/mcp/catalog.rb +1 -1
- data/lib/textus/surfaces/mcp/errors.rb +0 -9
- data/lib/textus/surfaces/mcp/routing.rb +51 -0
- data/lib/textus/surfaces/mcp/server.rb +101 -62
- data/lib/textus/surfaces/role_scope.rb +0 -4
- data/lib/textus/surfaces/watcher.rb +3 -3
- data/lib/textus/version.rb +1 -1
- data/lib/textus/workflow/collector.rb +27 -0
- data/lib/textus/workflow/context.rb +5 -0
- data/lib/textus/workflow/dsl.rb +44 -0
- data/lib/textus/workflow/loader.rb +17 -0
- data/lib/textus/workflow/pattern.rb +18 -0
- data/lib/textus/workflow/registry.rb +21 -0
- data/lib/textus/workflow/runner.rb +84 -0
- data/lib/textus/workflow.rb +28 -0
- data/lib/textus.rb +12 -1
- metadata +33 -61
- data/lib/textus/action/pulse/scanner.rb +0 -118
- data/lib/textus/background/job/base.rb +0 -27
- data/lib/textus/background/job/materialize.rb +0 -31
- data/lib/textus/background/job/refresh.rb +0 -22
- data/lib/textus/background/job/sweep.rb +0 -31
- data/lib/textus/background/job.rb +0 -19
- data/lib/textus/background/planner/plan.rb +0 -113
- data/lib/textus/background/retention/apply.rb +0 -50
- data/lib/textus/doctor/check/handler_permit.rb +0 -34
- data/lib/textus/doctor/check/hooks.rb +0 -25
- data/lib/textus/doctor/check/intake_registration.rb +0 -46
- data/lib/textus/envelope/io/reader.rb +0 -48
- data/lib/textus/envelope/io/writer.rb +0 -211
- data/lib/textus/events.rb +0 -21
- data/lib/textus/init/templates/machine_intake.rb +0 -49
- data/lib/textus/init/templates/orientation_reducer.rb +0 -21
- data/lib/textus/manifest/entry/validators/events.rb +0 -22
- data/lib/textus/manifest/policy/handler_permit.rb +0 -17
- data/lib/textus/pipeline/acquire/handler.rb +0 -29
- data/lib/textus/pipeline/acquire/intake.rb +0 -132
- data/lib/textus/pipeline/acquire/projection.rb +0 -129
- data/lib/textus/pipeline/acquire/serializer/json.rb +0 -31
- data/lib/textus/pipeline/acquire/serializer/text.rb +0 -16
- data/lib/textus/pipeline/acquire/serializer/yaml.rb +0 -31
- data/lib/textus/pipeline/acquire/serializer.rb +0 -17
- data/lib/textus/pipeline/engine.rb +0 -97
- data/lib/textus/pipeline/render.rb +0 -25
- data/lib/textus/ports/audit_subscriber.rb +0 -42
- data/lib/textus/projection.rb +0 -103
- data/lib/textus/step/base.rb +0 -35
- data/lib/textus/step/builtin/csv_fetch.rb +0 -19
- data/lib/textus/step/builtin/ical_events_fetch.rb +0 -30
- data/lib/textus/step/builtin/json_fetch.rb +0 -18
- data/lib/textus/step/builtin/markdown_links_fetch.rb +0 -20
- data/lib/textus/step/builtin/rss_fetch.rb +0 -26
- data/lib/textus/step/builtin.rb +0 -22
- data/lib/textus/step/catalog.rb +0 -37
- data/lib/textus/step/context.rb +0 -74
- data/lib/textus/step/discovery.rb +0 -24
- data/lib/textus/step/error_log.rb +0 -32
- data/lib/textus/step/event_bus.rb +0 -113
- data/lib/textus/step/fetch.rb +0 -13
- data/lib/textus/step/fire_report.rb +0 -23
- data/lib/textus/step/loader.rb +0 -108
- data/lib/textus/step/observe.rb +0 -31
- data/lib/textus/step/registry_store.rb +0 -66
- data/lib/textus/step/signature.rb +0 -31
- data/lib/textus/step/transform.rb +0 -12
- data/lib/textus/step/validate.rb +0 -11
- data/lib/textus/step.rb +0 -10
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
module Workflow
|
|
3
|
+
module DSL
|
|
4
|
+
Step = Data.define(:name, :callable, :timeout)
|
|
5
|
+
|
|
6
|
+
class Definition
|
|
7
|
+
attr_reader :name, :steps, :match_pattern, :publish_block
|
|
8
|
+
|
|
9
|
+
def initialize(name)
|
|
10
|
+
@name = name
|
|
11
|
+
@steps = []
|
|
12
|
+
@match_pattern = nil
|
|
13
|
+
@publish_block = nil
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def match(pattern)
|
|
17
|
+
@match_pattern = pattern
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def step(name, callable_or_opt = nil, timeout: nil, &block)
|
|
21
|
+
callable = if callable_or_opt.respond_to?(:call)
|
|
22
|
+
callable_or_opt
|
|
23
|
+
elsif block
|
|
24
|
+
block
|
|
25
|
+
else
|
|
26
|
+
raise ArgumentError.new("step :#{name} requires a block or a callable (got neither)")
|
|
27
|
+
end
|
|
28
|
+
t = callable_or_opt.is_a?(Hash) ? callable_or_opt[:timeout] : timeout
|
|
29
|
+
@steps << Step.new(name: name, callable: callable, timeout: t)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def publish(&block)
|
|
33
|
+
@publish_block = block || :default
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def match?(key)
|
|
37
|
+
return false unless @match_pattern
|
|
38
|
+
|
|
39
|
+
Pattern.match?(@match_pattern, key)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
module Workflow
|
|
3
|
+
class Loader
|
|
4
|
+
def self.load_all(root)
|
|
5
|
+
registry = Registry.new
|
|
6
|
+
workflows_dir = File.join(root, "workflows")
|
|
7
|
+
return registry unless File.directory?(workflows_dir)
|
|
8
|
+
|
|
9
|
+
collector = Collector.new(registry)
|
|
10
|
+
Collector.with(collector) do
|
|
11
|
+
Dir.glob(File.join(workflows_dir, "**", "*.rb")).each { |path| load path }
|
|
12
|
+
end
|
|
13
|
+
registry
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
module Workflow
|
|
3
|
+
module Pattern
|
|
4
|
+
def self.match?(pattern, key)
|
|
5
|
+
if pattern.end_with?(".**")
|
|
6
|
+
prefix = pattern.delete_suffix(".**")
|
|
7
|
+
key.start_with?("#{prefix}.")
|
|
8
|
+
elsif pattern.end_with?(".*")
|
|
9
|
+
prefix = pattern.delete_suffix(".*")
|
|
10
|
+
suffix = key.delete_prefix("#{prefix}.")
|
|
11
|
+
key != suffix && !suffix.include?(".")
|
|
12
|
+
else
|
|
13
|
+
key == pattern
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
module Workflow
|
|
3
|
+
class Registry
|
|
4
|
+
def initialize
|
|
5
|
+
@definitions = []
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def register(definition)
|
|
9
|
+
@definitions << definition
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def for(key)
|
|
13
|
+
@definitions.find { |d| d.match?(key) }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def all
|
|
17
|
+
@definitions.dup
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "timeout"
|
|
2
|
+
|
|
3
|
+
module Textus
|
|
4
|
+
module Workflow
|
|
5
|
+
class Runner
|
|
6
|
+
DEFAULT_TIMEOUT = 30
|
|
7
|
+
|
|
8
|
+
def initialize(definition, container:, call:)
|
|
9
|
+
@definition = definition
|
|
10
|
+
@container = container
|
|
11
|
+
@call = call
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run(key)
|
|
15
|
+
ctx = build_context(key)
|
|
16
|
+
data = execute_steps(ctx)
|
|
17
|
+
publish(key, data, ctx)
|
|
18
|
+
data
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def build_context(key)
|
|
24
|
+
res = @container.manifest.resolver.resolve(key)
|
|
25
|
+
Context.new(
|
|
26
|
+
key: key,
|
|
27
|
+
entry: res.entry,
|
|
28
|
+
config: {}.freeze,
|
|
29
|
+
lane: res.entry.lane.to_s,
|
|
30
|
+
container: @container,
|
|
31
|
+
call: @call,
|
|
32
|
+
)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def execute_steps(ctx)
|
|
36
|
+
data = nil
|
|
37
|
+
@definition.steps.each { |step| data = execute_one(step, data, ctx) }
|
|
38
|
+
data
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def execute_one(step, data, ctx)
|
|
42
|
+
timeout = step.timeout || DEFAULT_TIMEOUT
|
|
43
|
+
Timeout.timeout(timeout) { step.callable.call(data, ctx) }
|
|
44
|
+
rescue Timeout::Error => e
|
|
45
|
+
raise StepFailed.new(step.name, e)
|
|
46
|
+
rescue Textus::Error
|
|
47
|
+
raise
|
|
48
|
+
rescue StandardError => e
|
|
49
|
+
raise StepFailed.new(step.name, e)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def publish(key, data, ctx)
|
|
53
|
+
blk = @definition.publish_block
|
|
54
|
+
return blk.call(data, ctx) if blk && blk != :default
|
|
55
|
+
|
|
56
|
+
built_in_publish(key, data, ctx)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def built_in_publish(key, data, ctx)
|
|
60
|
+
normalized = normalize(data, ctx.entry.format)
|
|
61
|
+
Gate::Auth.new(@container).check_action!(action: :converge, actor: @call.role, key: key)
|
|
62
|
+
Envelope::Writer.from(container: @container, call: @call).put(
|
|
63
|
+
key,
|
|
64
|
+
mentry: ctx.entry,
|
|
65
|
+
payload: Envelope::Writer::Payload.new(**normalized),
|
|
66
|
+
)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def normalize(data, format)
|
|
70
|
+
return { meta: {}, body: "", content: nil } if data.nil?
|
|
71
|
+
|
|
72
|
+
data = data.transform_keys(&:to_s) if data.is_a?(Hash)
|
|
73
|
+
case format.to_s
|
|
74
|
+
when "markdown", "text"
|
|
75
|
+
{ meta: data["_meta"] || {}, body: (data["body"] || "").to_s, content: nil }
|
|
76
|
+
when "json", "yaml"
|
|
77
|
+
{ meta: data["_meta"] || {}, body: nil, content: data["content"] || data }
|
|
78
|
+
else
|
|
79
|
+
{ meta: {}, body: data.to_s, content: nil }
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Textus
|
|
2
|
+
module Workflow
|
|
3
|
+
class StepFailed < Textus::Error
|
|
4
|
+
attr_reader :step_name, :cause
|
|
5
|
+
|
|
6
|
+
def initialize(step_name, cause)
|
|
7
|
+
@step_name = step_name
|
|
8
|
+
@cause = cause
|
|
9
|
+
super(:workflow_step_failed, "workflow step '#{step_name}' failed: #{cause.message}")
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class NotFound < Textus::Error
|
|
14
|
+
def initialize(key)
|
|
15
|
+
super(:workflow_not_found, "no workflow matches key '#{key}'; add a .textus/workflows/*.rb file with match: '#{key}'")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.workflow(name, &)
|
|
21
|
+
collector = Workflow::Collector.current
|
|
22
|
+
raise "Textus.workflow called outside Workflow::Loader.load_all context" unless collector
|
|
23
|
+
|
|
24
|
+
defn = Workflow::DSL::Definition.new(name)
|
|
25
|
+
defn.instance_eval(&)
|
|
26
|
+
collector.register(defn)
|
|
27
|
+
end
|
|
28
|
+
end
|
data/lib/textus.rb
CHANGED
|
@@ -9,17 +9,19 @@ loader.inflector.inflect(
|
|
|
9
9
|
"cli" => "CLI",
|
|
10
10
|
"json" => "Json",
|
|
11
11
|
"yaml" => "Yaml",
|
|
12
|
-
"io" => "IO",
|
|
13
12
|
"mcp" => "MCP",
|
|
14
13
|
"mcp_serve" => "MCPServe",
|
|
14
|
+
"dsl" => "DSL",
|
|
15
15
|
)
|
|
16
16
|
loader.ignore(File.expand_path("textus/errors.rb", __dir__))
|
|
17
17
|
loader.ignore(File.expand_path("textus/surfaces/mcp.rb", __dir__))
|
|
18
18
|
loader.ignore(File.expand_path("textus/surfaces/mcp/errors.rb", __dir__))
|
|
19
|
+
loader.ignore(File.expand_path("textus/workflow/errors.rb", __dir__))
|
|
19
20
|
# Scaffold sources copied verbatim into user stores by `textus init`. They are
|
|
20
21
|
# templates for user-owned step classes, not gem constants — Zeitwerk must not
|
|
21
22
|
# manage or eager-load them.
|
|
22
23
|
loader.ignore(File.expand_path("textus/init/templates", __dir__))
|
|
24
|
+
loader.ignore(File.expand_path("textus/produce/acquire", __dir__))
|
|
23
25
|
loader.setup
|
|
24
26
|
loader.eager_load
|
|
25
27
|
|
|
@@ -33,6 +35,7 @@ Textus::Action::VERBS = {
|
|
|
33
35
|
reject: Textus::Action::Reject,
|
|
34
36
|
enqueue: Textus::Action::Enqueue,
|
|
35
37
|
get: Textus::Action::Get,
|
|
38
|
+
ingest: Textus::Action::Ingest,
|
|
36
39
|
list: Textus::Action::List,
|
|
37
40
|
where: Textus::Action::Where,
|
|
38
41
|
uid: Textus::Action::Uid,
|
|
@@ -87,4 +90,12 @@ Textus::Action::VERBS.each_key do |verb|
|
|
|
87
90
|
end
|
|
88
91
|
|
|
89
92
|
module Textus
|
|
93
|
+
def self.workflow(name, &)
|
|
94
|
+
collector = Workflow::Collector.current
|
|
95
|
+
raise "Textus.workflow called outside Workflow::Loader.load_all context" unless collector
|
|
96
|
+
|
|
97
|
+
defn = Workflow::DSL::Definition.new(name)
|
|
98
|
+
defn.instance_eval(&)
|
|
99
|
+
collector.register(defn)
|
|
100
|
+
end
|
|
90
101
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: textus
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.54.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Patrick
|
|
@@ -135,6 +135,7 @@ files:
|
|
|
135
135
|
- lib/textus/action/drain.rb
|
|
136
136
|
- lib/textus/action/enqueue.rb
|
|
137
137
|
- lib/textus/action/get.rb
|
|
138
|
+
- lib/textus/action/ingest.rb
|
|
138
139
|
- lib/textus/action/jobs.rb
|
|
139
140
|
- lib/textus/action/key_delete.rb
|
|
140
141
|
- lib/textus/action/key_delete_prefix.rb
|
|
@@ -144,7 +145,6 @@ files:
|
|
|
144
145
|
- lib/textus/action/propose.rb
|
|
145
146
|
- lib/textus/action/published.rb
|
|
146
147
|
- lib/textus/action/pulse.rb
|
|
147
|
-
- lib/textus/action/pulse/scanner.rb
|
|
148
148
|
- lib/textus/action/put.rb
|
|
149
149
|
- lib/textus/action/rdeps.rb
|
|
150
150
|
- lib/textus/action/reject.rb
|
|
@@ -155,15 +155,6 @@ files:
|
|
|
155
155
|
- lib/textus/action/uid.rb
|
|
156
156
|
- lib/textus/action/where.rb
|
|
157
157
|
- lib/textus/action/write_verb.rb
|
|
158
|
-
- lib/textus/background/job.rb
|
|
159
|
-
- lib/textus/background/job/base.rb
|
|
160
|
-
- lib/textus/background/job/materialize.rb
|
|
161
|
-
- lib/textus/background/job/refresh.rb
|
|
162
|
-
- lib/textus/background/job/sweep.rb
|
|
163
|
-
- lib/textus/background/plan.rb
|
|
164
|
-
- lib/textus/background/planner/plan.rb
|
|
165
|
-
- lib/textus/background/retention/apply.rb
|
|
166
|
-
- lib/textus/background/worker.rb
|
|
167
158
|
- lib/textus/boot.rb
|
|
168
159
|
- lib/textus/call.rb
|
|
169
160
|
- lib/textus/command.rb
|
|
@@ -187,15 +178,14 @@ files:
|
|
|
187
178
|
- lib/textus/doctor/check.rb
|
|
188
179
|
- lib/textus/doctor/check/audit_log.rb
|
|
189
180
|
- lib/textus/doctor/check/generator_drift.rb
|
|
190
|
-
- lib/textus/doctor/check/handler_permit.rb
|
|
191
|
-
- lib/textus/doctor/check/hooks.rb
|
|
192
181
|
- lib/textus/doctor/check/illegal_keys.rb
|
|
193
|
-
- lib/textus/doctor/check/intake_registration.rb
|
|
194
182
|
- lib/textus/doctor/check/manifest_files.rb
|
|
183
|
+
- lib/textus/doctor/check/notebook_sources.rb
|
|
195
184
|
- lib/textus/doctor/check/orphaned_publish_targets.rb
|
|
196
185
|
- lib/textus/doctor/check/proposal_targets.rb
|
|
197
186
|
- lib/textus/doctor/check/protocol_version.rb
|
|
198
187
|
- lib/textus/doctor/check/publish_tree_index_overlap.rb
|
|
188
|
+
- lib/textus/doctor/check/raw_asset_paths.rb
|
|
199
189
|
- lib/textus/doctor/check/rule_ambiguity.rb
|
|
200
190
|
- lib/textus/doctor/check/schema_parse_error.rb
|
|
201
191
|
- lib/textus/doctor/check/schema_violations.rb
|
|
@@ -204,23 +194,28 @@ files:
|
|
|
204
194
|
- lib/textus/doctor/check/templates.rb
|
|
205
195
|
- lib/textus/doctor/check/unowned_schema_fields.rb
|
|
206
196
|
- lib/textus/doctor/validator.rb
|
|
207
|
-
- lib/textus/entry.rb
|
|
208
|
-
- lib/textus/entry/base.rb
|
|
209
|
-
- lib/textus/entry/json.rb
|
|
210
|
-
- lib/textus/entry/markdown.rb
|
|
211
|
-
- lib/textus/entry/text.rb
|
|
212
|
-
- lib/textus/entry/yaml.rb
|
|
213
197
|
- lib/textus/envelope.rb
|
|
214
|
-
- lib/textus/envelope/
|
|
215
|
-
- lib/textus/envelope/
|
|
198
|
+
- lib/textus/envelope/reader.rb
|
|
199
|
+
- lib/textus/envelope/writer.rb
|
|
216
200
|
- lib/textus/errors.rb
|
|
217
201
|
- lib/textus/etag.rb
|
|
218
|
-
- lib/textus/
|
|
202
|
+
- lib/textus/format.rb
|
|
203
|
+
- lib/textus/format/base.rb
|
|
204
|
+
- lib/textus/format/json.rb
|
|
205
|
+
- lib/textus/format/markdown.rb
|
|
206
|
+
- lib/textus/format/text.rb
|
|
207
|
+
- lib/textus/format/yaml.rb
|
|
219
208
|
- lib/textus/gate.rb
|
|
220
209
|
- lib/textus/gate/auth.rb
|
|
221
210
|
- lib/textus/init.rb
|
|
222
|
-
- lib/textus/
|
|
223
|
-
- lib/textus/
|
|
211
|
+
- lib/textus/jobs.rb
|
|
212
|
+
- lib/textus/jobs/base.rb
|
|
213
|
+
- lib/textus/jobs/materialize.rb
|
|
214
|
+
- lib/textus/jobs/plan.rb
|
|
215
|
+
- lib/textus/jobs/planner.rb
|
|
216
|
+
- lib/textus/jobs/retention.rb
|
|
217
|
+
- lib/textus/jobs/sweep.rb
|
|
218
|
+
- lib/textus/jobs/worker.rb
|
|
224
219
|
- lib/textus/key/distance.rb
|
|
225
220
|
- lib/textus/key/grammar.rb
|
|
226
221
|
- lib/textus/key/matching.rb
|
|
@@ -244,12 +239,10 @@ files:
|
|
|
244
239
|
- lib/textus/manifest/entry/publish/to_paths.rb
|
|
245
240
|
- lib/textus/manifest/entry/publish/tree.rb
|
|
246
241
|
- lib/textus/manifest/entry/validators.rb
|
|
247
|
-
- lib/textus/manifest/entry/validators/events.rb
|
|
248
242
|
- lib/textus/manifest/entry/validators/format_matrix.rb
|
|
249
243
|
- lib/textus/manifest/entry/validators/ignore.rb
|
|
250
244
|
- lib/textus/manifest/entry/validators/publish.rb
|
|
251
245
|
- lib/textus/manifest/policy.rb
|
|
252
|
-
- lib/textus/manifest/policy/handler_permit.rb
|
|
253
246
|
- lib/textus/manifest/policy/matcher.rb
|
|
254
247
|
- lib/textus/manifest/policy/publish_target.rb
|
|
255
248
|
- lib/textus/manifest/policy/react.rb
|
|
@@ -261,53 +254,23 @@ files:
|
|
|
261
254
|
- lib/textus/manifest/schema/keys.rb
|
|
262
255
|
- lib/textus/manifest/schema/validator.rb
|
|
263
256
|
- lib/textus/manifest/schema/vocabulary.rb
|
|
264
|
-
- lib/textus/pipeline/acquire/handler.rb
|
|
265
|
-
- lib/textus/pipeline/acquire/intake.rb
|
|
266
|
-
- lib/textus/pipeline/acquire/projection.rb
|
|
267
|
-
- lib/textus/pipeline/acquire/serializer.rb
|
|
268
|
-
- lib/textus/pipeline/acquire/serializer/json.rb
|
|
269
|
-
- lib/textus/pipeline/acquire/serializer/text.rb
|
|
270
|
-
- lib/textus/pipeline/acquire/serializer/yaml.rb
|
|
271
|
-
- lib/textus/pipeline/engine.rb
|
|
272
|
-
- lib/textus/pipeline/render.rb
|
|
273
257
|
- lib/textus/ports/audit_log.rb
|
|
274
|
-
- lib/textus/ports/audit_subscriber.rb
|
|
275
258
|
- lib/textus/ports/build_lock.rb
|
|
276
259
|
- lib/textus/ports/clock.rb
|
|
260
|
+
- lib/textus/ports/job_store.rb
|
|
261
|
+
- lib/textus/ports/job_store/job.rb
|
|
277
262
|
- lib/textus/ports/publisher.rb
|
|
278
|
-
- lib/textus/ports/queue.rb
|
|
279
|
-
- lib/textus/ports/queue/job.rb
|
|
280
263
|
- lib/textus/ports/sentinel_store.rb
|
|
281
264
|
- lib/textus/ports/storage/file_stat.rb
|
|
282
265
|
- lib/textus/ports/storage/file_store.rb
|
|
283
266
|
- lib/textus/ports/watcher_lock.rb
|
|
284
|
-
- lib/textus/
|
|
267
|
+
- lib/textus/produce/engine.rb
|
|
268
|
+
- lib/textus/produce/render.rb
|
|
285
269
|
- lib/textus/role.rb
|
|
286
270
|
- lib/textus/schema.rb
|
|
287
271
|
- lib/textus/schema/tools.rb
|
|
288
272
|
- lib/textus/schemas.rb
|
|
289
273
|
- lib/textus/session.rb
|
|
290
|
-
- lib/textus/step.rb
|
|
291
|
-
- lib/textus/step/base.rb
|
|
292
|
-
- lib/textus/step/builtin.rb
|
|
293
|
-
- lib/textus/step/builtin/csv_fetch.rb
|
|
294
|
-
- lib/textus/step/builtin/ical_events_fetch.rb
|
|
295
|
-
- lib/textus/step/builtin/json_fetch.rb
|
|
296
|
-
- lib/textus/step/builtin/markdown_links_fetch.rb
|
|
297
|
-
- lib/textus/step/builtin/rss_fetch.rb
|
|
298
|
-
- lib/textus/step/catalog.rb
|
|
299
|
-
- lib/textus/step/context.rb
|
|
300
|
-
- lib/textus/step/discovery.rb
|
|
301
|
-
- lib/textus/step/error_log.rb
|
|
302
|
-
- lib/textus/step/event_bus.rb
|
|
303
|
-
- lib/textus/step/fetch.rb
|
|
304
|
-
- lib/textus/step/fire_report.rb
|
|
305
|
-
- lib/textus/step/loader.rb
|
|
306
|
-
- lib/textus/step/observe.rb
|
|
307
|
-
- lib/textus/step/registry_store.rb
|
|
308
|
-
- lib/textus/step/signature.rb
|
|
309
|
-
- lib/textus/step/transform.rb
|
|
310
|
-
- lib/textus/step/validate.rb
|
|
311
274
|
- lib/textus/store.rb
|
|
312
275
|
- lib/textus/surfaces/cli.rb
|
|
313
276
|
- lib/textus/surfaces/cli/group.rb
|
|
@@ -330,6 +293,7 @@ files:
|
|
|
330
293
|
- lib/textus/surfaces/mcp.rb
|
|
331
294
|
- lib/textus/surfaces/mcp/catalog.rb
|
|
332
295
|
- lib/textus/surfaces/mcp/errors.rb
|
|
296
|
+
- lib/textus/surfaces/mcp/routing.rb
|
|
333
297
|
- lib/textus/surfaces/mcp/server.rb
|
|
334
298
|
- lib/textus/surfaces/mcp/session.rb
|
|
335
299
|
- lib/textus/surfaces/mcp/tool_schemas.rb
|
|
@@ -337,6 +301,14 @@ files:
|
|
|
337
301
|
- lib/textus/surfaces/watcher.rb
|
|
338
302
|
- lib/textus/uid.rb
|
|
339
303
|
- lib/textus/version.rb
|
|
304
|
+
- lib/textus/workflow.rb
|
|
305
|
+
- lib/textus/workflow/collector.rb
|
|
306
|
+
- lib/textus/workflow/context.rb
|
|
307
|
+
- lib/textus/workflow/dsl.rb
|
|
308
|
+
- lib/textus/workflow/loader.rb
|
|
309
|
+
- lib/textus/workflow/pattern.rb
|
|
310
|
+
- lib/textus/workflow/registry.rb
|
|
311
|
+
- lib/textus/workflow/runner.rb
|
|
340
312
|
homepage: https://github.com/patrick204nqh/textus
|
|
341
313
|
licenses:
|
|
342
314
|
- MIT
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "time"
|
|
4
|
-
|
|
5
|
-
module Textus
|
|
6
|
-
module Action
|
|
7
|
-
class Pulse
|
|
8
|
-
class Scanner
|
|
9
|
-
def initialize(prefix: nil, lane: nil)
|
|
10
|
-
@prefix = prefix
|
|
11
|
-
@lane = lane
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def call(container:, call:)
|
|
15
|
-
@container = container
|
|
16
|
-
@call = call
|
|
17
|
-
@manifest = container.manifest
|
|
18
|
-
@file_store = container.file_store
|
|
19
|
-
|
|
20
|
-
rows = []
|
|
21
|
-
@manifest.data.entries.each do |mentry|
|
|
22
|
-
next if @prefix && !mentry.key.start_with?(@prefix)
|
|
23
|
-
next if @lane && mentry.lane != @lane
|
|
24
|
-
|
|
25
|
-
rows << row_for(mentry)
|
|
26
|
-
end
|
|
27
|
-
rows
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
|
|
32
|
-
def row_for(mentry)
|
|
33
|
-
envelope = safe_get(mentry.key)
|
|
34
|
-
last = envelope&.meta&.dig("last_fetched_at")
|
|
35
|
-
ttl, action = policy_for(mentry)
|
|
36
|
-
return base_row(mentry, last).merge(status: :no_policy) if ttl.nil?
|
|
37
|
-
|
|
38
|
-
basis = basis_for(mentry)
|
|
39
|
-
expired = expired?(mentry, basis, ttl)
|
|
40
|
-
base_row(mentry, last).merge(
|
|
41
|
-
ttl_seconds: ttl,
|
|
42
|
-
action: action,
|
|
43
|
-
status: expired ? :expired : :fresh,
|
|
44
|
-
next_due_at: basis.nil? ? nil : (basis + ttl).utc.iso8601,
|
|
45
|
-
)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def policy_for(mentry)
|
|
49
|
-
if mentry.intake?
|
|
50
|
-
ttl = mentry.source.ttl_seconds
|
|
51
|
-
return [ttl, :refresh] unless ttl.nil?
|
|
52
|
-
end
|
|
53
|
-
ret = @manifest.rules.for(mentry.key).retention
|
|
54
|
-
return [ret.ttl_seconds, ret.action] unless ret.nil?
|
|
55
|
-
|
|
56
|
-
[nil, nil]
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def basis_for(mentry)
|
|
60
|
-
return evaluator.intake_basis(mentry) if mentry.intake? && mentry.source.ttl_seconds
|
|
61
|
-
|
|
62
|
-
mtime_for(mentry.key)
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
def expired?(mentry, basis, ttl)
|
|
66
|
-
if mentry.intake? && mentry.source.ttl_seconds
|
|
67
|
-
evaluator.verdict(mentry).stale
|
|
68
|
-
else
|
|
69
|
-
basis.nil? || Textus::Core::Retention::Sweep.expired?(ttl_seconds: ttl, mtime: basis, now: @call.now)
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
def evaluator
|
|
74
|
-
@evaluator ||= Textus::Core::Freshness::Evaluator.new(
|
|
75
|
-
manifest: @manifest,
|
|
76
|
-
file_stat: Textus::Ports::Storage::FileStat.new,
|
|
77
|
-
clock: @call,
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def mtime_for(key)
|
|
82
|
-
path = @manifest.resolver.resolve(key).path
|
|
83
|
-
@file_store.exists?(path) ? Textus::Ports::Storage::FileStat.new.mtime(path) : nil
|
|
84
|
-
rescue Textus::Error
|
|
85
|
-
nil
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
def base_row(mentry, last)
|
|
89
|
-
{
|
|
90
|
-
key: mentry.key,
|
|
91
|
-
lane: mentry.lane,
|
|
92
|
-
last_fetched_at: last,
|
|
93
|
-
age_seconds: last ? (@call.now - Time.parse(last)).to_i : nil,
|
|
94
|
-
}
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def safe_get(key)
|
|
98
|
-
res = @manifest.resolver.resolve(key)
|
|
99
|
-
return nil unless @file_store.exists?(res.path)
|
|
100
|
-
|
|
101
|
-
raw = @file_store.read(res.path)
|
|
102
|
-
parsed = Textus::Entry.for_format(res.entry.format).parse(raw, path: res.path)
|
|
103
|
-
Textus::Envelope.build(
|
|
104
|
-
key: key,
|
|
105
|
-
mentry: res.entry,
|
|
106
|
-
path: res.path,
|
|
107
|
-
meta: parsed["_meta"],
|
|
108
|
-
body: parsed["body"],
|
|
109
|
-
etag: Textus::Etag.for_bytes(raw),
|
|
110
|
-
content: parsed["content"],
|
|
111
|
-
)
|
|
112
|
-
rescue Textus::Error
|
|
113
|
-
nil
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
module Background
|
|
5
|
-
module Job
|
|
6
|
-
class Base
|
|
7
|
-
def self.inherited(subclass)
|
|
8
|
-
super
|
|
9
|
-
return unless subclass.name
|
|
10
|
-
|
|
11
|
-
TracePoint.new(:end) do |tp|
|
|
12
|
-
if tp.self == subclass
|
|
13
|
-
Textus::Background::Job.register(subclass)
|
|
14
|
-
tp.disable
|
|
15
|
-
end
|
|
16
|
-
end.enable
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def call(**)
|
|
20
|
-
raise NotImplementedError.new("#{self.class}#call")
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def args = {}
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
module Background
|
|
5
|
-
module Job
|
|
6
|
-
class Materialize < Base
|
|
7
|
-
TYPE = "materialize"
|
|
8
|
-
|
|
9
|
-
def initialize(key:)
|
|
10
|
-
super()
|
|
11
|
-
@key = key
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def args = { key: @key }
|
|
15
|
-
|
|
16
|
-
def call(container:, call:)
|
|
17
|
-
result = Textus::Pipeline::Engine.converge(container: container, call: call, keys: [@key])
|
|
18
|
-
return unless result.is_a?(Hash)
|
|
19
|
-
|
|
20
|
-
Array(result[:failed]).each do |failure|
|
|
21
|
-
container.steps.publish(
|
|
22
|
-
:produce_failed,
|
|
23
|
-
ctx: Textus::Step::Context.for(container: container, call: call),
|
|
24
|
-
keys: [failure["key"]], error: failure["error"]
|
|
25
|
-
)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
module Background
|
|
5
|
-
module Job
|
|
6
|
-
class Refresh < Base
|
|
7
|
-
TYPE = "refresh"
|
|
8
|
-
|
|
9
|
-
def initialize(key:)
|
|
10
|
-
super()
|
|
11
|
-
@key = key
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def args = { key: @key }
|
|
15
|
-
|
|
16
|
-
def call(container:, call:)
|
|
17
|
-
Textus::Pipeline::Engine.converge(container: container, call: call, keys: [@key])
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Textus
|
|
4
|
-
module Background
|
|
5
|
-
module Job
|
|
6
|
-
class Sweep < Base
|
|
7
|
-
REQUIRED_ROLE = Textus::Role::AUTOMATION
|
|
8
|
-
TYPE = "sweep"
|
|
9
|
-
|
|
10
|
-
def initialize(scope: nil, key: nil)
|
|
11
|
-
super()
|
|
12
|
-
@scope = scope || {}
|
|
13
|
-
@key = key
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def args = { scope: @scope, key: @key }.compact
|
|
17
|
-
|
|
18
|
-
def call(container:, call:)
|
|
19
|
-
prefix = @key || (@scope.is_a?(Hash) ? @scope["prefix"] : nil)
|
|
20
|
-
lane = @scope.is_a?(Hash) ? @scope["lane"] : nil
|
|
21
|
-
rows = Textus::Core::Retention::Sweep.new(
|
|
22
|
-
manifest: container.manifest,
|
|
23
|
-
file_stat: Textus::Ports::Storage::FileStat.new,
|
|
24
|
-
clock: Textus::Ports::Clock.new,
|
|
25
|
-
).call(prefix: prefix, lane: lane)
|
|
26
|
-
Textus::Background::Retention::Apply.new(container: container, call: call).call(rows)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|