eyeling 1.24.29 → 1.24.31
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.
- package/dist/browser/eyeling.browser.js +283 -4
- package/examples/input/rdf-message-flow.trig +56 -105
- package/examples/odrl-benefits.n3 +221 -0
- package/examples/output/odrl-benefits.md +29 -0
- package/examples/output/rdf-message-flow.md +3 -3
- package/examples/rdf-message-flow.n3 +70 -49
- package/eyeling.js +283 -4
- package/lib/lexer.js +283 -4
- package/package.json +1 -1
- package/test/api.test.js +58 -0
- package/test/playground.test.js +23 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# ======================================================================================
|
|
2
|
+
# odrl-benefits.n3
|
|
3
|
+
#
|
|
4
|
+
# A very small example showing why ODRL policies are useful.
|
|
5
|
+
#
|
|
6
|
+
# The same machine-readable policy can:
|
|
7
|
+
# - permit a good use case (medical research),
|
|
8
|
+
# - block an unwanted use case (marketing),
|
|
9
|
+
# - attach an operational duty (delete the data after use), and
|
|
10
|
+
# - produce an explainable audit trail.
|
|
11
|
+
# ======================================================================================
|
|
12
|
+
|
|
13
|
+
@prefix : <https://example.org/odrl-benefits#> .
|
|
14
|
+
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
|
|
15
|
+
@prefix dct: <http://purl.org/dc/terms/> .
|
|
16
|
+
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
|
|
17
|
+
@prefix string:<http://www.w3.org/2000/10/swap/string#> .
|
|
18
|
+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
|
19
|
+
|
|
20
|
+
# -------------------
|
|
21
|
+
# Parties and assets
|
|
22
|
+
# -------------------
|
|
23
|
+
|
|
24
|
+
:ResearchLab dct:title "Example Research Lab" .
|
|
25
|
+
:HealthDataset dct:title "Pseudonymised health dataset" .
|
|
26
|
+
:MedicalResearch dct:title "medical research" .
|
|
27
|
+
:Marketing dct:title "marketing" .
|
|
28
|
+
:Allowed dct:title "Allowed" .
|
|
29
|
+
:Blocked dct:title "Blocked" .
|
|
30
|
+
|
|
31
|
+
# -----------------------------
|
|
32
|
+
# One compact ODRL policy
|
|
33
|
+
# -----------------------------
|
|
34
|
+
|
|
35
|
+
:Policy a odrl:Policy ;
|
|
36
|
+
dct:title "Health-data sharing policy" ;
|
|
37
|
+
odrl:permission :PermitResearchUse ;
|
|
38
|
+
odrl:prohibition :ProhibitMarketingUse .
|
|
39
|
+
|
|
40
|
+
:PermitResearchUse a odrl:Permission ;
|
|
41
|
+
odrl:assignee :ResearchLab ;
|
|
42
|
+
odrl:target :HealthDataset ;
|
|
43
|
+
odrl:action odrl:use ;
|
|
44
|
+
odrl:constraint [
|
|
45
|
+
odrl:leftOperand odrl:purpose ;
|
|
46
|
+
odrl:operator odrl:eq ;
|
|
47
|
+
odrl:rightOperand :MedicalResearch
|
|
48
|
+
] ;
|
|
49
|
+
odrl:duty :DeleteAfterUse .
|
|
50
|
+
|
|
51
|
+
:DeleteAfterUse a odrl:Duty ;
|
|
52
|
+
odrl:action odrl:delete ;
|
|
53
|
+
odrl:target :HealthDataset ;
|
|
54
|
+
odrl:constraint [
|
|
55
|
+
odrl:leftOperand odrl:dateTime ;
|
|
56
|
+
odrl:operator odrl:lteq ;
|
|
57
|
+
odrl:rightOperand "2026-12-31"^^xsd:date
|
|
58
|
+
] .
|
|
59
|
+
|
|
60
|
+
:ProhibitMarketingUse a odrl:Prohibition ;
|
|
61
|
+
odrl:assignee :ResearchLab ;
|
|
62
|
+
odrl:target :HealthDataset ;
|
|
63
|
+
odrl:action odrl:use ;
|
|
64
|
+
odrl:constraint [
|
|
65
|
+
odrl:leftOperand odrl:purpose ;
|
|
66
|
+
odrl:operator odrl:eq ;
|
|
67
|
+
odrl:rightOperand :Marketing
|
|
68
|
+
] .
|
|
69
|
+
|
|
70
|
+
# -----------------
|
|
71
|
+
# Two access requests
|
|
72
|
+
# -----------------
|
|
73
|
+
|
|
74
|
+
:RequestResearch a :Request ;
|
|
75
|
+
dct:title "Use the dataset for medical research" ;
|
|
76
|
+
odrl:assignee :ResearchLab ;
|
|
77
|
+
odrl:target :HealthDataset ;
|
|
78
|
+
odrl:action odrl:use ;
|
|
79
|
+
:purpose :MedicalResearch .
|
|
80
|
+
|
|
81
|
+
:RequestMarketing a :Request ;
|
|
82
|
+
dct:title "Use the dataset for marketing" ;
|
|
83
|
+
odrl:assignee :ResearchLab ;
|
|
84
|
+
odrl:target :HealthDataset ;
|
|
85
|
+
odrl:action odrl:use ;
|
|
86
|
+
:purpose :Marketing .
|
|
87
|
+
|
|
88
|
+
# ---------------------------
|
|
89
|
+
# Minimal policy evaluation
|
|
90
|
+
# ---------------------------
|
|
91
|
+
|
|
92
|
+
# Benefit 1: permissions are machine-readable.
|
|
93
|
+
{
|
|
94
|
+
?request a :Request ;
|
|
95
|
+
odrl:assignee ?party ;
|
|
96
|
+
odrl:target ?asset ;
|
|
97
|
+
odrl:action ?action ;
|
|
98
|
+
:purpose ?purpose .
|
|
99
|
+
|
|
100
|
+
:Policy odrl:permission ?permission .
|
|
101
|
+
?permission odrl:assignee ?party ;
|
|
102
|
+
odrl:target ?asset ;
|
|
103
|
+
odrl:action ?action ;
|
|
104
|
+
odrl:constraint [
|
|
105
|
+
odrl:leftOperand odrl:purpose ;
|
|
106
|
+
odrl:operator odrl:eq ;
|
|
107
|
+
odrl:rightOperand ?purpose
|
|
108
|
+
] .
|
|
109
|
+
}
|
|
110
|
+
=>
|
|
111
|
+
{
|
|
112
|
+
?request :decision :Allowed ;
|
|
113
|
+
:benefit :MachineReadablePermission ;
|
|
114
|
+
:why "A matching ODRL permission grants this purpose-specific use." .
|
|
115
|
+
} .
|
|
116
|
+
|
|
117
|
+
# Benefit 2: prohibitions make unwanted use cases explicit.
|
|
118
|
+
{
|
|
119
|
+
?request a :Request ;
|
|
120
|
+
odrl:assignee ?party ;
|
|
121
|
+
odrl:target ?asset ;
|
|
122
|
+
odrl:action ?action ;
|
|
123
|
+
:purpose ?purpose .
|
|
124
|
+
|
|
125
|
+
:Policy odrl:prohibition ?prohibition .
|
|
126
|
+
?prohibition odrl:assignee ?party ;
|
|
127
|
+
odrl:target ?asset ;
|
|
128
|
+
odrl:action ?action ;
|
|
129
|
+
odrl:constraint [
|
|
130
|
+
odrl:leftOperand odrl:purpose ;
|
|
131
|
+
odrl:operator odrl:eq ;
|
|
132
|
+
odrl:rightOperand ?purpose
|
|
133
|
+
] .
|
|
134
|
+
}
|
|
135
|
+
=>
|
|
136
|
+
{
|
|
137
|
+
?request :decision :Blocked ;
|
|
138
|
+
:benefit :PurposeLimitation ;
|
|
139
|
+
:why "A matching ODRL prohibition blocks this purpose-specific use." .
|
|
140
|
+
} .
|
|
141
|
+
|
|
142
|
+
# Benefit 3: permitted use can carry operational duties.
|
|
143
|
+
{
|
|
144
|
+
?request :decision :Allowed ;
|
|
145
|
+
odrl:assignee ?party ;
|
|
146
|
+
odrl:target ?asset ;
|
|
147
|
+
odrl:action ?action ;
|
|
148
|
+
:purpose ?purpose .
|
|
149
|
+
|
|
150
|
+
:Policy odrl:permission ?permission .
|
|
151
|
+
?permission odrl:assignee ?party ;
|
|
152
|
+
odrl:target ?asset ;
|
|
153
|
+
odrl:action ?action ;
|
|
154
|
+
odrl:constraint [
|
|
155
|
+
odrl:leftOperand odrl:purpose ;
|
|
156
|
+
odrl:operator odrl:eq ;
|
|
157
|
+
odrl:rightOperand ?purpose
|
|
158
|
+
] ;
|
|
159
|
+
odrl:duty ?duty .
|
|
160
|
+
|
|
161
|
+
?duty odrl:action odrl:delete ;
|
|
162
|
+
odrl:constraint [
|
|
163
|
+
odrl:leftOperand odrl:dateTime ;
|
|
164
|
+
odrl:operator odrl:lteq ;
|
|
165
|
+
odrl:rightOperand ?date
|
|
166
|
+
] .
|
|
167
|
+
}
|
|
168
|
+
=>
|
|
169
|
+
{
|
|
170
|
+
?request :duty "Delete the dataset after the approved research use." ;
|
|
171
|
+
:deleteBy ?date ;
|
|
172
|
+
:benefit :OperationalDuty .
|
|
173
|
+
} .
|
|
174
|
+
|
|
175
|
+
# ----------------------------
|
|
176
|
+
# Markdown report output
|
|
177
|
+
# ----------------------------
|
|
178
|
+
|
|
179
|
+
:out01 log:outputString "# odrl-benefits\n\n## Source files\n\n- [N3 rules](../odrl-benefits.n3)\n\n" .
|
|
180
|
+
:out02 log:outputString "## Why ODRL helps\n\n" .
|
|
181
|
+
:out03 log:outputString "This example uses one small ODRL policy to make data-use decisions explicit, machine-checkable, and auditable.\n\n" .
|
|
182
|
+
:out04 log:outputString "### Policy in plain language\n\n" .
|
|
183
|
+
:out05 log:outputString "- The research lab **may use** the pseudonymised health dataset for **medical research**.\n" .
|
|
184
|
+
:out06 log:outputString "- The research lab **must delete** the dataset by `2026-12-31`.\n" .
|
|
185
|
+
:out07 log:outputString "- The research lab **must not use** the dataset for **marketing**.\n\n" .
|
|
186
|
+
:out08 log:outputString "### Evaluated requests\n\n" .
|
|
187
|
+
:out09 log:outputString "| Request | Decision | Explanation |\n" .
|
|
188
|
+
:out10 log:outputString "| --- | --- | --- |\n" .
|
|
189
|
+
|
|
190
|
+
{
|
|
191
|
+
:RequestResearch dct:title ?title ;
|
|
192
|
+
:decision ?decision ;
|
|
193
|
+
:why ?why ;
|
|
194
|
+
:deleteBy ?date .
|
|
195
|
+
?decision dct:title ?decisionLabel .
|
|
196
|
+
|
|
197
|
+
("| %s | `%s` | %s Duty: delete by `%s`. |\n" ?title ?decisionLabel ?why ?date) string:format ?line .
|
|
198
|
+
}
|
|
199
|
+
=>
|
|
200
|
+
{
|
|
201
|
+
:out11 log:outputString ?line .
|
|
202
|
+
} .
|
|
203
|
+
|
|
204
|
+
{
|
|
205
|
+
:RequestMarketing dct:title ?title ;
|
|
206
|
+
:decision ?decision ;
|
|
207
|
+
:why ?why .
|
|
208
|
+
?decision dct:title ?decisionLabel .
|
|
209
|
+
|
|
210
|
+
("| %s | `%s` | %s |\n" ?title ?decisionLabel ?why) string:format ?line .
|
|
211
|
+
}
|
|
212
|
+
=>
|
|
213
|
+
{
|
|
214
|
+
:out12 log:outputString ?line .
|
|
215
|
+
} .
|
|
216
|
+
|
|
217
|
+
:out13 log:outputString "\n### Benefits illustrated\n\n" .
|
|
218
|
+
:out14 log:outputString "1. **Clarity:** the policy says who may do what, to which asset, and for which purpose.\n" .
|
|
219
|
+
:out15 log:outputString "2. **Automation:** requests can be allowed or blocked by rules instead of manual reading.\n" .
|
|
220
|
+
:out16 log:outputString "3. **Purpose limitation:** research use and marketing use are treated differently.\n" .
|
|
221
|
+
:out17 log:outputString "4. **Accountability:** duties such as deletion are part of the same policy and can be audited.\n" .
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# odrl-benefits
|
|
2
|
+
|
|
3
|
+
## Source files
|
|
4
|
+
|
|
5
|
+
- [N3 rules](../odrl-benefits.n3)
|
|
6
|
+
|
|
7
|
+
## Why ODRL helps
|
|
8
|
+
|
|
9
|
+
This example uses one small ODRL policy to make data-use decisions explicit, machine-checkable, and auditable.
|
|
10
|
+
|
|
11
|
+
### Policy in plain language
|
|
12
|
+
|
|
13
|
+
- The research lab **may use** the pseudonymised health dataset for **medical research**.
|
|
14
|
+
- The research lab **must delete** the dataset by `2026-12-31`.
|
|
15
|
+
- The research lab **must not use** the dataset for **marketing**.
|
|
16
|
+
|
|
17
|
+
### Evaluated requests
|
|
18
|
+
|
|
19
|
+
| Request | Decision | Explanation |
|
|
20
|
+
| --- | --- | --- |
|
|
21
|
+
| Use the dataset for medical research | `Allowed` | A matching ODRL permission grants this purpose-specific use. Duty: delete by `2026-12-31`. |
|
|
22
|
+
| Use the dataset for marketing | `Blocked` | A matching ODRL prohibition blocks this purpose-specific use. |
|
|
23
|
+
|
|
24
|
+
### Benefits illustrated
|
|
25
|
+
|
|
26
|
+
1. **Clarity:** the policy says who may do what, to which asset, and for which purpose.
|
|
27
|
+
2. **Automation:** requests can be allowed or blocked by rules instead of manual reading.
|
|
28
|
+
3. **Purpose limitation:** research use and marketing use are treated differently.
|
|
29
|
+
4. **Accountability:** duties such as deletion are part of the same policy and can be audited.
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
## Source files
|
|
4
4
|
|
|
5
5
|
- [N3 rules](../rdf-message-flow.n3)
|
|
6
|
-
- [Input
|
|
6
|
+
- [Input RDF Message Log](../input/rdf-message-flow.trig)
|
|
7
7
|
|
|
8
8
|
## Answer
|
|
9
|
-
Continuous RDF Message flow accepted: 5 ordered message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was 26, so results 21 and 22 were archived, the heartbeat kept the stream alive, and results 28 and 29 were emitted as alerts.
|
|
9
|
+
Continuous RDF Message flow accepted: 5 ordered parser-replayed message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was 26, so results 21 and 22 were archived, the empty heartbeat kept the stream alive, and results 28 and 29 were emitted as alerts.
|
|
10
10
|
|
|
11
11
|
## Explanation
|
|
12
|
-
The input is a
|
|
12
|
+
The input sidecar is now a true RDF Message Log using VERSION \"1.2-messages\" and MESSAGE delimiters. Eyeling parses the log internally into an eymsg: replay view with ordered envelopes, offsets, next-envelope links, and one payload graph per non-empty message. Only the first replayed envelope starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its eymsg:nextEnvelope. Observation payloads are inspected with log:includes inside their own message formula, and the delimiter-only third message is replayed as an empty heartbeat. This keeps message boundaries in the parser/runtime instead of modeling them by hand in the TriG sidecar.
|
|
@@ -9,28 +9,23 @@
|
|
|
9
9
|
# Motivation
|
|
10
10
|
# ----------
|
|
11
11
|
# The RDF Messages draft describes an RDF Message as an RDF dataset interpreted
|
|
12
|
-
# atomically,
|
|
13
|
-
#
|
|
14
|
-
# example keeps the same message-level discipline while staying in ordinary
|
|
15
|
-
# N3/TriG that Eyeling can reason over today.
|
|
12
|
+
# atomically, an RDF Message Stream as an ordered sequence of messages, and an
|
|
13
|
+
# RDF Message Log as a static replayable representation of that stream.
|
|
16
14
|
#
|
|
17
|
-
# The companion
|
|
15
|
+
# The companion sidecar is now a true RDF Message Log: it uses
|
|
16
|
+
# VERSION "1.2-messages" and MESSAGE delimiters. In RDF compatibility mode,
|
|
17
|
+
# Eyeling parses those delimiters internally and materializes a replay view using
|
|
18
|
+
# the eymsg: vocabulary: a stream resource, ordered envelopes, offsets,
|
|
19
|
+
# next-envelope links, and one named payload graph per non-empty message.
|
|
18
20
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
# The rules below deliberately do not merge all payload graphs into one global
|
|
25
|
-
# graph. Instead, each observation is checked with log:includes inside the named
|
|
26
|
-
# payload formula. The empty heartbeat has no payload graph but still advances
|
|
27
|
-
# through the same pipeline. The next message is released only after the current
|
|
28
|
-
# one reaches the sink, making the example a small back-pressure / flow-control
|
|
29
|
-
# story rather than a batch merge of all input triples.
|
|
21
|
+
# The rules below therefore no longer invent envelope facts in the TriG file.
|
|
22
|
+
# They consume the replay view that Eyeling exposes, inspect each payload with
|
|
23
|
+
# log:includes inside its own message formula, and let the empty heartbeat flow
|
|
24
|
+
# through without a payload graph.
|
|
30
25
|
|
|
31
26
|
@prefix : <https://eyereasoner.github.io/eyeling/examples/rdf-message-flow#>.
|
|
32
27
|
@prefix flow: <https://eyereasoner.github.io/eyeling/examples/rdf-message-flow/vocab#>.
|
|
33
|
-
@prefix
|
|
28
|
+
@prefix eymsg: <https://eyereasoner.github.io/eyeling/vocab/message#>.
|
|
34
29
|
@prefix sosa: <http://www.w3.org/ns/sosa/>.
|
|
35
30
|
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
|
|
36
31
|
@prefix math: <http://www.w3.org/2000/10/swap/math#>.
|
|
@@ -38,29 +33,49 @@
|
|
|
38
33
|
@prefix log: <http://www.w3.org/2000/10/swap/log#>.
|
|
39
34
|
@prefix string: <http://www.w3.org/2000/10/swap/string#>.
|
|
40
35
|
|
|
36
|
+
# Eyeling's parser-level replay exposes the first envelope; that starts the
|
|
37
|
+
# consumer-visible flow. Later envelopes are released only by the sink rule.
|
|
38
|
+
{
|
|
39
|
+
?Stream a eymsg:RDFMessageStream;
|
|
40
|
+
eymsg:firstEnvelope ?Envelope.
|
|
41
|
+
} => {
|
|
42
|
+
?Envelope :atStage :ingest.
|
|
43
|
+
}.
|
|
44
|
+
|
|
45
|
+
# Stream configuration is carried by the first RDF Message payload, not by a
|
|
46
|
+
# hand-written sidecar envelope. The rule lifts only the threshold needed by the
|
|
47
|
+
# routing rules into the ordinary reasoning context.
|
|
48
|
+
{
|
|
49
|
+
?Envelope eymsg:payloadGraph ?Payload.
|
|
50
|
+
?Payload log:nameOf ?PayloadContext.
|
|
51
|
+
?PayloadContext log:includes { :temperatureFlow :highThreshold ?Threshold. }.
|
|
52
|
+
} => {
|
|
53
|
+
:temperatureFlow :highThreshold ?Threshold.
|
|
54
|
+
}.
|
|
55
|
+
|
|
41
56
|
# Stage 1: once an envelope has entered the stream, validate it.
|
|
42
57
|
{ ?Envelope :atStage :ingest. } => {
|
|
43
58
|
?Envelope :atStage :validate.
|
|
44
59
|
}.
|
|
45
60
|
|
|
46
|
-
# Stage 2: validation records that the
|
|
47
|
-
# boundary before the payload is interpreted.
|
|
61
|
+
# Stage 2: validation records that the parser gave the reasoner an explicit
|
|
62
|
+
# RDF Message boundary before the payload is interpreted.
|
|
48
63
|
{
|
|
49
64
|
?Envelope :atStage :validate;
|
|
50
|
-
a
|
|
51
|
-
|
|
65
|
+
a eymsg:MessageEnvelope;
|
|
66
|
+
eymsg:offset ?Offset.
|
|
52
67
|
} => {
|
|
53
68
|
?Envelope flow:boundaryExplicit true.
|
|
54
69
|
?Envelope :atStage :interpret.
|
|
55
70
|
}.
|
|
56
71
|
|
|
57
|
-
# Stage 3a: observation payloads are inspected inside their own
|
|
58
|
-
# This
|
|
72
|
+
# Stage 3a: observation payloads are inspected inside their own replayed message
|
|
73
|
+
# graph. This keeps the RDF Messages default discipline: messages are atomic
|
|
74
|
+
# datasets and are not silently merged.
|
|
59
75
|
{
|
|
60
76
|
?Envelope :atStage :interpret;
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
flow:payloadGraph ?Payload.
|
|
77
|
+
eymsg:payloadKind eymsg:nonEmpty;
|
|
78
|
+
eymsg:payloadGraph ?Payload.
|
|
64
79
|
?Payload log:nameOf ?PayloadContext.
|
|
65
80
|
?PayloadContext log:includes { ?Observation sosa:hasSimpleResult ?Result. }.
|
|
66
81
|
} => {
|
|
@@ -69,10 +84,10 @@
|
|
|
69
84
|
}.
|
|
70
85
|
|
|
71
86
|
# Stage 3b: empty heartbeats contain no quads, but RDF Messages explicitly allow
|
|
72
|
-
# empty messages, so the envelope still moves through the flow.
|
|
87
|
+
# empty messages, so the parser-level envelope still moves through the flow.
|
|
73
88
|
{
|
|
74
89
|
?Envelope :atStage :interpret;
|
|
75
|
-
|
|
90
|
+
eymsg:payloadKind eymsg:empty.
|
|
76
91
|
} => {
|
|
77
92
|
?Envelope flow:emptyMessageAllowed true.
|
|
78
93
|
?Envelope :atStage :route.
|
|
@@ -112,46 +127,52 @@
|
|
|
112
127
|
:heartbeatSink :received ?Envelope.
|
|
113
128
|
}.
|
|
114
129
|
|
|
115
|
-
# Continuous-flow rule: reaching the sink releases the next envelope
|
|
116
|
-
#
|
|
117
|
-
#
|
|
130
|
+
# Continuous-flow rule: reaching the sink releases the next replayed envelope
|
|
131
|
+
# into ingress. The ordering link comes from Eyeling's internal MESSAGE replay,
|
|
132
|
+
# not from the sidecar data.
|
|
118
133
|
{
|
|
119
134
|
?Envelope :atStage :sink;
|
|
120
|
-
|
|
121
|
-
?Next a
|
|
135
|
+
eymsg:nextEnvelope ?Next.
|
|
136
|
+
?Next a eymsg:MessageEnvelope.
|
|
122
137
|
} => {
|
|
123
138
|
?Envelope :releases ?Next.
|
|
124
139
|
?Next :atStage :ingest.
|
|
125
140
|
}.
|
|
126
141
|
|
|
127
|
-
# The
|
|
128
|
-
# through ingest, validation, interpretation, routing, and sink while
|
|
129
|
-
#
|
|
142
|
+
# The verdict is emitted only after all five parser-replayed envelopes have
|
|
143
|
+
# flowed through ingest, validation, interpretation, routing, and sink while
|
|
144
|
+
# preserving message boundaries.
|
|
130
145
|
{
|
|
131
|
-
|
|
132
|
-
:
|
|
146
|
+
?Stream a eymsg:RDFMessageStream;
|
|
147
|
+
eymsg:orderedEnvelopes ?Envelopes;
|
|
148
|
+
eymsg:firstEnvelope ?M1.
|
|
133
149
|
?Envelopes list:length ?Count.
|
|
134
|
-
:
|
|
150
|
+
:temperatureFlow :highThreshold ?Threshold.
|
|
151
|
+
?M1 :atStage :sink;
|
|
135
152
|
flow:payloadResult ?FirstResult;
|
|
136
153
|
:route :archiveSink;
|
|
137
|
-
:
|
|
138
|
-
|
|
154
|
+
eymsg:nextEnvelope ?M2;
|
|
155
|
+
:releases ?M2.
|
|
156
|
+
?M2 :atStage :sink;
|
|
139
157
|
flow:payloadResult ?SecondResult;
|
|
140
158
|
:route :archiveSink;
|
|
141
|
-
:
|
|
142
|
-
|
|
159
|
+
eymsg:nextEnvelope ?M3;
|
|
160
|
+
:releases ?M3.
|
|
161
|
+
?M3 :atStage :sink;
|
|
143
162
|
flow:emptyMessageAllowed true;
|
|
144
163
|
:route :heartbeatSink;
|
|
145
|
-
:
|
|
146
|
-
|
|
164
|
+
eymsg:nextEnvelope ?M4;
|
|
165
|
+
:releases ?M4.
|
|
166
|
+
?M4 :atStage :sink;
|
|
147
167
|
flow:payloadResult ?FourthResult;
|
|
148
168
|
:route :alertSink;
|
|
149
|
-
:
|
|
150
|
-
|
|
169
|
+
eymsg:nextEnvelope ?M5;
|
|
170
|
+
:releases ?M5.
|
|
171
|
+
?M5 :atStage :sink;
|
|
151
172
|
flow:payloadResult ?FifthResult;
|
|
152
173
|
:route :alertSink.
|
|
153
|
-
("# rdf-message-flow\n\n## Source files\n\n- [N3 rules](../rdf-message-flow.n3)\n- [Input
|
|
174
|
+
("# rdf-message-flow\n\n## Source files\n\n- [N3 rules](../rdf-message-flow.n3)\n- [Input RDF Message Log](../input/rdf-message-flow.trig)\n\n## Answer\nContinuous RDF Message flow accepted: %d ordered parser-replayed message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was %d, so results %s and %s were archived, the empty heartbeat kept the stream alive, and results %s and %s were emitted as alerts.\n\n## Explanation\nThe input sidecar is now a true RDF Message Log using VERSION \\\"1.2-messages\\\" and MESSAGE delimiters. Eyeling parses the log internally into an eymsg: replay view with ordered envelopes, offsets, next-envelope links, and one payload graph per non-empty message. Only the first replayed envelope starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its eymsg:nextEnvelope. Observation payloads are inspected with log:includes inside their own message formula, and the delimiter-only third message is replayed as an empty heartbeat. This keeps message boundaries in the parser/runtime instead of modeling them by hand in the TriG sidecar." ?Count ?Threshold ?FirstResult ?SecondResult ?FourthResult ?FifthResult) string:format ?Block.
|
|
154
175
|
} => {
|
|
155
176
|
:rdfMessageFlowExample log:outputString ?Block.
|
|
156
|
-
:rdfMessageFlowExample :demonstrates :ContinuousFlow, :BackPressureRelease, :AtomicMessageContext, :HeartbeatInFlow, :ThresholdRouting.
|
|
177
|
+
:rdfMessageFlowExample :demonstrates :ContinuousFlow, :BackPressureRelease, :AtomicMessageContext, :HeartbeatInFlow, :ThresholdRouting, :RDFMessageLogReplay.
|
|
157
178
|
}.
|