eyeling 1.24.30 → 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.
@@ -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, and an RDF Message Stream as an ordered sequence of such messages.
13
- # It also defines RDF Message Logs with explicit MESSAGE delimiters. This Eyeling
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 TriG file therefore separates two layers:
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
- # 1. example-local envelope facts in the default graph, such as order,
20
- # processing stage, and payload graph; and
21
- # 2. one named graph per non-empty message payload, treated here as the RDF
22
- # dataset/message that must be inspected atomically.
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 prov: <http://www.w3.org/ns/prov#>.
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 envelope keeps an explicit message
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 flow:MessageEnvelope;
51
- flow:offset ?Offset.
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 payload graph.
58
- # This models the RDF Messages idea that messages are separate atomic datasets.
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
- flow:payloadKind :observation;
62
- flow:expectedResult ?Result;
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
- flow:payloadKind :heartbeat.
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 into ingress.
116
- # This mirrors a consumer-visible ordered stream: later messages are not processed
117
- # until the earlier message has completed the pipeline.
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
- flow:nextEnvelope ?Next.
121
- ?Next a flow:MessageEnvelope.
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 Eyeling verdict is emitted only after all five envelopes have flowed
128
- # through ingest, validation, interpretation, routing, and sink while preserving
129
- # their message boundaries.
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
- :temperatureFlow flow:orderedEnvelopes ?Envelopes;
132
- :highThreshold ?Threshold.
146
+ ?Stream a eymsg:RDFMessageStream;
147
+ eymsg:orderedEnvelopes ?Envelopes;
148
+ eymsg:firstEnvelope ?M1.
133
149
  ?Envelopes list:length ?Count.
134
- :m001 :atStage :sink;
150
+ :temperatureFlow :highThreshold ?Threshold.
151
+ ?M1 :atStage :sink;
135
152
  flow:payloadResult ?FirstResult;
136
153
  :route :archiveSink;
137
- :releases :m002.
138
- :m002 :atStage :sink;
154
+ eymsg:nextEnvelope ?M2;
155
+ :releases ?M2.
156
+ ?M2 :atStage :sink;
139
157
  flow:payloadResult ?SecondResult;
140
158
  :route :archiveSink;
141
- :releases :m003.
142
- :m003 :atStage :sink;
159
+ eymsg:nextEnvelope ?M3;
160
+ :releases ?M3.
161
+ ?M3 :atStage :sink;
143
162
  flow:emptyMessageAllowed true;
144
163
  :route :heartbeatSink;
145
- :releases :m004.
146
- :m004 :atStage :sink;
164
+ eymsg:nextEnvelope ?M4;
165
+ :releases ?M4.
166
+ ?M4 :atStage :sink;
147
167
  flow:payloadResult ?FourthResult;
148
168
  :route :alertSink;
149
- :releases :m005.
150
- :m005 :atStage :sink;
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 TriG](../input/rdf-message-flow.trig)\n\n## Answer\nContinuous RDF Message flow accepted: %d ordered message envelopes moved through the ingest → validate → interpret → route → sink pipeline. The threshold was %d, so results %s and %s were archived, the heartbeat kept the stream alive, and results %s and %s were emitted as alerts.\n\n## Explanation\nThe input is a single runnable example split across an N3 rule file and a TriG sidecar. The TriG file uses example-local envelope facts for stream order and processing state, while each named payload graph is treated as an atomic RDF Message dataset. Only :m001 starts at ingress; each envelope must reach :sink before the continuous-flow rule releases its flow:nextEnvelope. Observation payloads are inspected with log:includes inside their own payload formula, and the empty heartbeat advances without a payload graph. This keeps message boundaries visible to the reasoner instead of merging all payload triples into one global graph." ?Count ?Threshold ?FirstResult ?SecondResult ?FourthResult ?FifthResult) string:format ?Block.
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
  }.
package/eyeling.js CHANGED
@@ -9769,6 +9769,24 @@ function stripQuotes(lex) {
9769
9769
  // This keeps all downstream parsing/reasoning N3-only.
9770
9770
  const LOG_NAME_OF_IRI = '<http://www.w3.org/2000/10/swap/log#nameOf>';
9771
9771
  const RDF_REIFIES_IRI = '<http://www.w3.org/1999/02/22-rdf-syntax-ns#reifies>';
9772
+ const RDF_TYPE_IRI = '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>';
9773
+ const XSD_INTEGER_IRI = '<http://www.w3.org/2001/XMLSchema#integer>';
9774
+ const EYMSG_NS = 'https://eyereasoner.github.io/eyeling/vocab/message#';
9775
+ const EYMSG = Object.freeze({
9776
+ RDFMessageStream: `<${EYMSG_NS}RDFMessageStream>`,
9777
+ MessageEnvelope: `<${EYMSG_NS}MessageEnvelope>`,
9778
+ envelope: `<${EYMSG_NS}envelope>`,
9779
+ firstEnvelope: `<${EYMSG_NS}firstEnvelope>`,
9780
+ lastEnvelope: `<${EYMSG_NS}lastEnvelope>`,
9781
+ orderedEnvelopes: `<${EYMSG_NS}orderedEnvelopes>`,
9782
+ messageCount: `<${EYMSG_NS}messageCount>`,
9783
+ offset: `<${EYMSG_NS}offset>`,
9784
+ nextEnvelope: `<${EYMSG_NS}nextEnvelope>`,
9785
+ payloadGraph: `<${EYMSG_NS}payloadGraph>`,
9786
+ payloadKind: `<${EYMSG_NS}payloadKind>`,
9787
+ empty: `<${EYMSG_NS}empty>`,
9788
+ nonEmpty: `<${EYMSG_NS}nonEmpty>`,
9789
+ });
9772
9790
 
9773
9791
  function normalizeRdfCompatibility(inputText) {
9774
9792
  let text = String(inputText ?? '');
@@ -9779,10 +9797,11 @@ function normalizeRdfCompatibility(inputText) {
9779
9797
  // plausible top-level TriG named graph block.
9780
9798
  const hasTripleTerms = text.includes('<<');
9781
9799
  const hasVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/im.test(text);
9800
+ const hasMessageVersionDirective = /^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)-messages\1\s*\.?\s*(?:#.*)?$/im.test(text);
9782
9801
  const hasNamedGraphCandidate = /(?:^|[.\r\n])\s*(?:GRAPH\s+)?(?:<[^>\r\n]*>|_:[A-Za-z][A-Za-z0-9_-]*|[A-Za-z][A-Za-z0-9_-]*:[^\s{};,.()[\]]*)\s*\{/m.test(text);
9783
9802
  const hasAnnotationSyntax = /(?:^|\s)~\s*(?:<|_:[A-Za-z]|[A-Za-z][A-Za-z0-9_-]*:|\{\|)|\{\|/.test(text);
9784
9803
 
9785
- if (!hasTripleTerms && !hasVersionDirective && !hasNamedGraphCandidate && !hasAnnotationSyntax) return text;
9804
+ if (!hasTripleTerms && !hasVersionDirective && !hasMessageVersionDirective && !hasNamedGraphCandidate && !hasAnnotationSyntax) return text;
9786
9805
 
9787
9806
  function isWordChar(ch) {
9788
9807
  return ch != null && /[A-Za-z0-9_:-]/.test(ch);
@@ -10136,7 +10155,7 @@ function normalizeRdfCompatibility(inputText) {
10136
10155
  }
10137
10156
 
10138
10157
  function stripVersionDirectives(s) {
10139
- return s.replace(/^\s*(?:@version|VERSION)\s+(["'])1\.2\1\s*\.?\s*(?:#.*)?$/gim, '');
10158
+ return s.replace(/^\s*(?:@version|VERSION)\s+(["'])(?:1\.1|1\.2|1\.2-basic)(?:-messages)?\1\s*\.?\s*(?:#.*)?$/gim, '');
10140
10159
  }
10141
10160
 
10142
10161
  function skipWsAndComments(s, at) {
@@ -10290,10 +10309,270 @@ function normalizeRdfCompatibility(inputText) {
10290
10309
  return out;
10291
10310
  }
10292
10311
 
10312
+
10313
+ function isOnlyWhitespaceAndComments(s) {
10314
+ return skipWsAndComments(s, 0) >= s.length;
10315
+ }
10316
+
10317
+
10318
+ function skipOldStyleDirective(s, at) {
10319
+ let i = at;
10320
+ while (i < s.length) {
10321
+ const ch = s[i];
10322
+ if (ch === '"' || ch === "'") {
10323
+ i = readStringAt(s, i).end;
10324
+ continue;
10325
+ }
10326
+ if (ch === '<') {
10327
+ i = readIriAt(s, i).end;
10328
+ continue;
10329
+ }
10330
+ if (ch === '#') {
10331
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
10332
+ continue;
10333
+ }
10334
+ i += 1;
10335
+ if (ch === '.') return i;
10336
+ }
10337
+ return i;
10338
+ }
10339
+
10340
+ function stripDirectivesAndCommentsForEmptiness(s) {
10341
+ let out = '';
10342
+ let i = 0;
10343
+ let statementStart = true;
10344
+ while (i < s.length) {
10345
+ const ch = s[i];
10346
+ if (ch === '"' || ch === "'") {
10347
+ const str = readStringAt(s, i);
10348
+ out += str.text;
10349
+ i = str.end;
10350
+ statementStart = false;
10351
+ continue;
10352
+ }
10353
+ if (ch === '<') {
10354
+ const iri = readIriAt(s, i);
10355
+ out += iri.text;
10356
+ i = iri.end;
10357
+ statementStart = false;
10358
+ continue;
10359
+ }
10360
+ if (ch === '#') {
10361
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
10362
+ statementStart = true;
10363
+ continue;
10364
+ }
10365
+ if (statementStart) {
10366
+ const start = skipWsAndComments(s, i);
10367
+ out += s.slice(i, start);
10368
+ i = start;
10369
+ if (startsWordAt(s, 'PREFIX', i) || startsWordAt(s, 'BASE', i) || startsWordAt(s, 'VERSION', i)) {
10370
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
10371
+ statementStart = true;
10372
+ continue;
10373
+ }
10374
+ if (s[i] === '@') {
10375
+ const lower = s.slice(i, i + 9).toLowerCase();
10376
+ if (lower.startsWith('@prefix') || lower.startsWith('@base') || lower.startsWith('@version')) {
10377
+ i = skipOldStyleDirective(s, i);
10378
+ statementStart = true;
10379
+ continue;
10380
+ }
10381
+ }
10382
+ }
10383
+ out += ch;
10384
+ if (ch === '.' || ch === '}' || ch === '\n' || ch === '\r') statementStart = true;
10385
+ else if (!/\s/.test(ch)) statementStart = false;
10386
+ i += 1;
10387
+ }
10388
+ return out;
10389
+ }
10390
+
10391
+ function simpleHashText(s) {
10392
+ let h = 0x811c9dc5;
10393
+ for (let i = 0; i < s.length; i += 1) {
10394
+ h ^= s.charCodeAt(i);
10395
+ h = Math.imul(h, 0x01000193) >>> 0;
10396
+ }
10397
+ return h.toString(16).padStart(8, '0');
10398
+ }
10399
+
10400
+ function rewriteMessageBlankLabels(s, messageIndex) {
10401
+ let out = '';
10402
+ let i = 0;
10403
+ const prefix = `_:eyeling_m${String(messageIndex).padStart(3, '0')}_`;
10404
+ while (i < s.length) {
10405
+ const ch = s[i];
10406
+ if (ch === '"' || ch === "'") {
10407
+ const str = readStringAt(s, i);
10408
+ out += str.text;
10409
+ i = str.end;
10410
+ continue;
10411
+ }
10412
+ if (ch === '<' && !s.startsWith('<<', i)) {
10413
+ const iri = readIriAt(s, i);
10414
+ out += iri.text;
10415
+ i = iri.end;
10416
+ continue;
10417
+ }
10418
+ if (ch === '#') {
10419
+ while (i < s.length) {
10420
+ const c = s[i++];
10421
+ out += c;
10422
+ if (c === '\n' || c === '\r') break;
10423
+ }
10424
+ continue;
10425
+ }
10426
+ if (s.startsWith('_:', i)) {
10427
+ let j = i + 2;
10428
+ while (j < s.length && !/\s/.test(s[j]) && !'{}[](),;.'.includes(s[j])) j += 1;
10429
+ const label = s.slice(i + 2, j);
10430
+ if (label) {
10431
+ out += prefix + label.replace(/[^A-Za-z0-9_]/g, '_');
10432
+ i = j;
10433
+ continue;
10434
+ }
10435
+ }
10436
+ out += ch;
10437
+ i += 1;
10438
+ }
10439
+ return out;
10440
+ }
10441
+
10442
+ function findMessageDirectiveAt(s, at) {
10443
+ if (startsWordAt(s, 'MESSAGE', at)) return { start: at, end: at + 'MESSAGE'.length };
10444
+ if (s.slice(at, at + 8).toLowerCase() === '@message' && !isWordChar(s[at + 8])) {
10445
+ let end = at + 8;
10446
+ end = skipWsAndComments(s, end);
10447
+ if (s[end] === '.') end += 1;
10448
+ return { start: at, end };
10449
+ }
10450
+ return null;
10451
+ }
10452
+
10453
+ function splitRdfMessageLog(s) {
10454
+ const chunks = [];
10455
+ let i = 0;
10456
+ let start = 0;
10457
+ let braceDepth = 0;
10458
+ let bracketDepth = 0;
10459
+ let parenDepth = 0;
10460
+ let statementStart = true;
10461
+ let sawDelimiter = false;
10462
+
10463
+ while (i < s.length) {
10464
+ const ch = s[i];
10465
+ if (ch === '"' || ch === "'") {
10466
+ i = readStringAt(s, i).end;
10467
+ statementStart = false;
10468
+ continue;
10469
+ }
10470
+ if (ch === '<' && !s.startsWith('<<', i)) {
10471
+ i = readIriAt(s, i).end;
10472
+ statementStart = false;
10473
+ continue;
10474
+ }
10475
+ if (ch === '#') {
10476
+ while (i < s.length && s[i] !== '\n' && s[i] !== '\r') i += 1;
10477
+ statementStart = true;
10478
+ continue;
10479
+ }
10480
+ if (statementStart && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) {
10481
+ const termStart = skipWsAndComments(s, i);
10482
+ const msg = findMessageDirectiveAt(s, termStart);
10483
+ if (msg) {
10484
+ chunks.push(s.slice(start, termStart));
10485
+ start = msg.end;
10486
+ i = msg.end;
10487
+ statementStart = true;
10488
+ sawDelimiter = true;
10489
+ continue;
10490
+ }
10491
+ if (termStart !== i) {
10492
+ i = termStart;
10493
+ continue;
10494
+ }
10495
+ }
10496
+ if (ch === '{') braceDepth += 1;
10497
+ else if (ch === '}' && braceDepth > 0) braceDepth -= 1;
10498
+ else if (ch === '[') bracketDepth += 1;
10499
+ else if (ch === ']' && bracketDepth > 0) bracketDepth -= 1;
10500
+ else if (ch === '(') parenDepth += 1;
10501
+ else if (ch === ')' && parenDepth > 0) parenDepth -= 1;
10502
+
10503
+ if (ch === '.' && braceDepth === 0 && bracketDepth === 0 && parenDepth === 0) statementStart = true;
10504
+ else if (ch === '\n' || ch === '\r') statementStart = true;
10505
+ else if (!/\s/.test(ch)) statementStart = false;
10506
+ i += 1;
10507
+ }
10508
+
10509
+ const tail = s.slice(start);
10510
+ if (!sawDelimiter || !isOnlyWhitespaceAndComments(tail)) chunks.push(tail);
10511
+ return chunks;
10512
+ }
10513
+
10514
+ function normalizeMessageChunk(chunk, messageIndex) {
10515
+ let body = String(chunk || '');
10516
+ if (hasTripleTerms || body.includes('<<')) body = convertTripleTerms(body);
10517
+ if (hasAnnotationSyntax || /(?:^|\s)~\s*(?:<|_:[A-Za-z]|[A-Za-z][A-Za-z0-9_-]*:|\{\|)|\{\|/.test(body)) {
10518
+ body = convertAnnotations(body);
10519
+ }
10520
+ body = normalizeNamedGraphs(body);
10521
+ body = rewriteMessageBlankLabels(body, messageIndex);
10522
+ return body.trim();
10523
+ }
10524
+
10525
+ function messageChunkHasRdf(body) {
10526
+ return !isOnlyWhitespaceAndComments(stripDirectivesAndCommentsForEmptiness(body));
10527
+ }
10528
+
10529
+ function normalizeRdfMessageLog(s) {
10530
+ const withoutVersion = stripVersionDirectives(s);
10531
+ const chunks = splitRdfMessageLog(withoutVersion);
10532
+ const hash = simpleHashText(s);
10533
+ const base = `urn:eyeling:message-log:${hash}`;
10534
+ const stream = `<${base}#stream>`;
10535
+ const envelopeIris = chunks.map((unused, idx) => `<${base}#m${String(idx + 1).padStart(3, '0')}>`);
10536
+ const payloadIris = chunks.map((unused, idx) => `<${base}#m${String(idx + 1).padStart(3, '0')}/payload>`);
10537
+ const out = [];
10538
+
10539
+ out.push(`${stream} ${RDF_TYPE_IRI} ${EYMSG.RDFMessageStream} .`);
10540
+ out.push(`${stream} ${EYMSG.messageCount} "${chunks.length}"^^${XSD_INTEGER_IRI} .`);
10541
+ if (envelopeIris.length) {
10542
+ out.push(`${stream} ${EYMSG.orderedEnvelopes} (${envelopeIris.join(' ')}) .`);
10543
+ out.push(`${stream} ${EYMSG.firstEnvelope} ${envelopeIris[0]} .`);
10544
+ out.push(`${stream} ${EYMSG.lastEnvelope} ${envelopeIris[envelopeIris.length - 1]} .`);
10545
+ }
10546
+
10547
+ for (let idx = 0; idx < chunks.length; idx += 1) {
10548
+ const n = idx + 1;
10549
+ const envelope = envelopeIris[idx];
10550
+ const payload = payloadIris[idx];
10551
+ const body = normalizeMessageChunk(chunks[idx], n);
10552
+ const hasBody = messageChunkHasRdf(body);
10553
+
10554
+ out.push(`${stream} ${EYMSG.envelope} ${envelope} .`);
10555
+ out.push(`${envelope} ${RDF_TYPE_IRI} ${EYMSG.MessageEnvelope} .`);
10556
+ out.push(`${envelope} ${EYMSG.offset} "${n}"^^${XSD_INTEGER_IRI} .`);
10557
+ out.push(`${envelope} ${EYMSG.payloadKind} ${hasBody ? EYMSG.nonEmpty : EYMSG.empty} .`);
10558
+ if (idx + 1 < envelopeIris.length) out.push(`${envelope} ${EYMSG.nextEnvelope} ${envelopeIris[idx + 1]} .`);
10559
+ if (hasBody) {
10560
+ out.push(`${envelope} ${EYMSG.payloadGraph} ${payload} .`);
10561
+ out.push(`${payload} ${LOG_NAME_OF_IRI} {`);
10562
+ out.push(body);
10563
+ out.push(`} .`);
10564
+ }
10565
+ }
10566
+
10567
+ return out.join('\n') + '\n';
10568
+ }
10569
+
10570
+ if (hasMessageVersionDirective) return normalizeRdfMessageLog(text);
10571
+
10293
10572
  if (hasTripleTerms) text = convertTripleTerms(text);
10294
10573
  if (hasAnnotationSyntax) text = convertAnnotations(text);
10295
- if (hasVersionDirective) text = stripVersionDirectives(text);
10296
- if (hasVersionDirective || hasNamedGraphCandidate) text = normalizeNamedGraphs(text);
10574
+ if (hasVersionDirective || hasMessageVersionDirective) text = stripVersionDirectives(text);
10575
+ if (hasVersionDirective || hasMessageVersionDirective || hasNamedGraphCandidate) text = normalizeNamedGraphs(text);
10297
10576
  return text;
10298
10577
  }
10299
10578