@rdfc/js-runner 2.0.0-alpha.8 → 2.0.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.
Files changed (59) hide show
  1. package/.idea/LNKD.tech Editor.xml +194 -0
  2. package/.idea/codeStyles/Project.xml +52 -0
  3. package/.idea/codeStyles/codeStyleConfig.xml +5 -0
  4. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  5. package/.idea/js-runner.iml +12 -0
  6. package/{examples/echo/.idea → .idea}/modules.xml +1 -1
  7. package/.idea/vcs.xml +6 -0
  8. package/dist/args.d.ts +4 -0
  9. package/dist/args.js +58 -0
  10. package/dist/connectors/file.d.ts +15 -0
  11. package/dist/connectors/file.js +89 -0
  12. package/dist/connectors/http.d.ts +14 -0
  13. package/dist/connectors/http.js +82 -0
  14. package/dist/connectors/kafka.d.ts +48 -0
  15. package/dist/connectors/kafka.js +68 -0
  16. package/dist/connectors/ws.d.ts +10 -0
  17. package/dist/connectors/ws.js +72 -0
  18. package/dist/connectors.d.ts +73 -0
  19. package/dist/connectors.js +168 -0
  20. package/dist/index.cjs +732 -0
  21. package/dist/index.d.ts +42 -0
  22. package/dist/index.js +83 -0
  23. package/dist/tsconfig.tsbuildinfo +1 -0
  24. package/dist/util.d.ts +71 -0
  25. package/dist/util.js +92 -0
  26. package/lib/client.js +2 -2
  27. package/lib/index.d.ts +1 -0
  28. package/lib/index.js +2 -1
  29. package/lib/logger.d.ts +3 -1
  30. package/lib/logger.js +23 -1
  31. package/lib/reader.js +1 -2
  32. package/lib/runner.js +3 -3
  33. package/lib/testUtils.d.ts +6 -0
  34. package/lib/testUtils.js +92 -2
  35. package/lib/tsconfig.tsbuildinfo +1 -1
  36. package/package.json +3 -3
  37. package/src/client.ts +15 -17
  38. package/src/index.ts +1 -0
  39. package/src/logger.ts +27 -1
  40. package/src/reader.ts +8 -11
  41. package/src/runner.ts +4 -6
  42. package/src/testUtils.ts +122 -3
  43. package/.editorconfig +0 -9
  44. package/__tests__/channels.test.js +0 -117
  45. package/examples/echo/.idea/echo.iml +0 -9
  46. package/examples/echo/.idea/misc.xml +0 -6
  47. package/examples/echo/.idea/vcs.xml +0 -7
  48. package/examples/echo/.swls/config.json +0 -1
  49. package/examples/echo/index.ttl +0 -3
  50. package/examples/echo/minimal.ttl +0 -90
  51. package/examples/echo/shacl.ttl +0 -9
  52. package/examples/echo/shape.ttl +0 -1339
  53. package/examples/echo/src/processors.js +0 -123
  54. package/examples/echo/test.ttl +0 -11
  55. package/examples/echo/untitled:/types/MyType.ttl +0 -0
  56. package/file:/home/silvius/Projects/mumo-pipeline/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +0 -3
  57. package/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +0 -3
  58. package/minimal.ttl +0 -99
  59. package/vite.config.js +0 -11
package/src/reader.ts CHANGED
@@ -11,14 +11,14 @@ import {
11
11
 
12
12
  export type Any =
13
13
  | {
14
- string: string
15
- }
14
+ string: string
15
+ }
16
16
  | {
17
- stream: AsyncGenerator<Uint8Array>
18
- }
17
+ stream: AsyncGenerator<Uint8Array>
18
+ }
19
19
  | {
20
- buffer: Uint8Array
21
- }
20
+ buffer: Uint8Array
21
+ }
22
22
 
23
23
  export interface Reader {
24
24
  readonly uri: string
@@ -55,7 +55,7 @@ class MyIter<T> implements AsyncIterable<T> {
55
55
  }
56
56
 
57
57
  async pushStream(chunks: ClientReadableStream<DataChunk>) {
58
- const stream = (async function*(stream) {
58
+ const stream = (async function* (stream) {
59
59
  for await (const c of stream) {
60
60
  const chunk: DataChunk = c
61
61
  yield chunk.data
@@ -76,9 +76,7 @@ class MyIter<T> implements AsyncIterable<T> {
76
76
  if (item === undefined) break
77
77
  yield item
78
78
  } else {
79
- await new Promise<undefined>(
80
- (resolve) => (this.resolveNext = resolve),
81
- )
79
+ await new Promise<undefined>((resolve) => (this.resolveNext = resolve))
82
80
  }
83
81
  }
84
82
  }
@@ -123,7 +121,6 @@ export class ReaderInstance implements Reader {
123
121
 
124
122
  handleMsg(msg: Message) {
125
123
  this.logger.debug(`${this.uri} handling message`)
126
- console.log(`${this.uri} handling message`)
127
124
  for (const iter of this.iterators) {
128
125
  iter.push(msg.data)
129
126
  }
package/src/runner.ts CHANGED
@@ -79,7 +79,7 @@ export class Runner {
79
79
  transports: [
80
80
  new RpcTransport({
81
81
  entities: [proc.uri, this.uri],
82
- stream: this.client.logStream(() => { }),
82
+ stream: this.client.logStream(() => {}),
83
83
  }),
84
84
  ],
85
85
  })
@@ -121,7 +121,7 @@ export class Runner {
121
121
  await Promise.all(this.processors.map((x) => x.produce()))
122
122
  await Promise.all(this.processorTransforms)
123
123
  } catch (ex: unknown) {
124
- this.logger.error("Start failed: " + JSON.stringify(ex))
124
+ this.logger.error('Start failed: ' + JSON.stringify(ex))
125
125
  }
126
126
  }
127
127
 
@@ -175,7 +175,6 @@ export class Runner {
175
175
 
176
176
  if (msg.pipeline) {
177
177
  try {
178
-
179
178
  // here
180
179
  const quads = new Parser().parse(msg.pipeline)
181
180
  this.shapes = extractShapes(
@@ -190,9 +189,8 @@ export class Runner {
190
189
  },
191
190
  )
192
191
  this.quads = quads
193
- }
194
- catch (ex: unknown) {
195
- this.logger.error("Pipeline failed: " + JSON.stringify(ex))
192
+ } catch (ex: unknown) {
193
+ this.logger.error('Pipeline failed: ' + JSON.stringify(ex))
196
194
  }
197
195
  }
198
196
  }
package/src/testUtils.ts CHANGED
@@ -1,13 +1,18 @@
1
1
  import { DataChunk, RunnerClient } from '@rdfc/proto'
2
- import { ClientReadableStream } from '@grpc/grpc-js'
3
2
  import * as grpc from '@grpc/grpc-js'
4
- import { ClientReadableStreamImpl } from './reexports'
3
+ import { ClientReadableStream } from '@grpc/grpc-js'
4
+ import { ClientReadableStreamImpl, OrchestratorMessage } from './reexports'
5
5
  import { extractShapes } from 'rdf-lens'
6
- import { Parser } from 'n3'
6
+ import { NamedNode, Parser, Writer } from 'n3'
7
7
  import { readFile } from 'fs/promises'
8
8
  import winston, { createLogger } from 'winston'
9
9
  import { WriterInstance } from './writer'
10
10
  import { ReaderInstance } from './reader'
11
+ import { Processor } from './processor'
12
+ import { FullProc, Runner } from './runner'
13
+ import { Quad, Term } from '@rdfjs/types'
14
+ import { createTermNamespace } from '@treecg/types'
15
+ import { expect } from 'vitest'
11
16
 
12
17
  export async function getProcessorShape(
13
18
  baseIRI = process.cwd() + '/node_modules/@rdfc/js-runner/index.ttl',
@@ -75,3 +80,117 @@ export function createReader(iri = uri): ReaderInstance {
75
80
  const reader = new ReaderInstance(iri, client, logger)
76
81
  return reader
77
82
  }
83
+
84
+ const shapeQuads = `
85
+ @prefix rdfc: <https://w3id.org/rdf-connect#>.
86
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
87
+ @prefix sh: <http://www.w3.org/ns/shacl#>.
88
+ [ ] a sh:NodeShape;
89
+ sh:targetClass <JsProcessorShape>;
90
+ sh:property [
91
+ sh:path rdfc:entrypoint;
92
+ sh:name "location";
93
+ sh:minCount 1;
94
+ sh:maxCount 1;
95
+ sh:datatype xsd:string;
96
+ ], [
97
+ sh:path rdfc:file;
98
+ sh:name "file";
99
+ sh:minCount 1;
100
+ sh:maxCount 1;
101
+ sh:datatype xsd:string;
102
+ ], [
103
+ sh:path rdfc:class;
104
+ sh:name "clazz";
105
+ sh:maxCount 1;
106
+ sh:datatype xsd:string;
107
+ ].
108
+ `
109
+ const OWL = createTermNamespace('http://www.w3.org/2002/07/owl#', 'imports')
110
+ const processorShapes = extractShapes(new Parser().parse(shapeQuads))
111
+ const base = 'https://w3id.org/rdf-connect#'
112
+
113
+ export async function importFile(file: string): Promise<Quad[]> {
114
+ const done = new Set<string>()
115
+ const todo = [new URL('file://' + file)]
116
+ const quads: Quad[] = []
117
+
118
+ let item = todo.pop()
119
+ while (item !== undefined) {
120
+ if (done.has(item.toString())) {
121
+ item = todo.pop()
122
+ continue
123
+ }
124
+ done.add(item.toString())
125
+ if (item.protocol !== 'file:') {
126
+ throw 'No supported protocol ' + item.protocol
127
+ }
128
+
129
+ const txt = await readFile(item.pathname, { encoding: 'utf8' })
130
+ const extras = new Parser({ baseIRI: item.toString() }).parse(txt)
131
+
132
+ for (const o of extras
133
+ .filter(
134
+ (x) =>
135
+ x.subject.value === item?.toString() &&
136
+ x.predicate.equals(OWL.imports),
137
+ )
138
+ .map((x) => x.object.value)) {
139
+ todo.push(new URL(o))
140
+ }
141
+ quads.push(...extras)
142
+
143
+ item = todo.pop()
144
+ }
145
+
146
+ return quads
147
+ }
148
+
149
+ export async function getProc<T extends Processor<unknown>>(
150
+ config: string,
151
+ ty: string,
152
+ configLocation: string,
153
+ uri = 'http://example.com/ns#processor',
154
+ ): Promise<FullProc<T>> {
155
+ const configQuads = await importFile(configLocation)
156
+ const procConfig = processorShapes.lenses['JsProcessorShape'].execute({
157
+ id: new NamedNode(base + ty),
158
+ quads: configQuads,
159
+ })
160
+
161
+ const msgs: OrchestratorMessage[] = []
162
+ const write = async (x: OrchestratorMessage) => {
163
+ msgs.push(x)
164
+ }
165
+ const runner = new Runner(
166
+ new TestClient(),
167
+ write,
168
+ 'http://example.com/ns#',
169
+ logger,
170
+ )
171
+ configQuads.push(...new Parser().parse(config))
172
+ await runner.handleOrchMessage({
173
+ pipeline: new Writer().quadsToString(configQuads),
174
+ })
175
+
176
+ const proc = await runner.addProcessor<T>({
177
+ config: JSON.stringify(procConfig),
178
+ arguments: '',
179
+ uri,
180
+ })
181
+
182
+ return proc
183
+ }
184
+
185
+ export async function checkProcDefinition(file: string, n: string) {
186
+ const quads = await importFile(file)
187
+ const procConfig = <{ file: Term; location: string; clazz: string }>(
188
+ processorShapes.lenses['JsProcessorShape'].execute({
189
+ id: new NamedNode(base + n),
190
+ quads: quads,
191
+ })
192
+ )
193
+ expect(procConfig.file, n + ' has file').toBeDefined()
194
+ expect(procConfig.location, n + ' has location').toBeDefined()
195
+ expect(procConfig.clazz, n + ' has clazz').toBeDefined()
196
+ }
package/.editorconfig DELETED
@@ -1,9 +0,0 @@
1
- # top-most EditorConfig file
2
- root = true
3
-
4
- # Unix-style newlines with a newline ending every file
5
- [*]
6
- end_of_line = lf
7
- insert_final_newline = true
8
- indent_style = space
9
- indent_size = 2
@@ -1,117 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __asyncValues = (this && this.__asyncValues) || function (o) {
12
- if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
13
- var m = o[Symbol.asyncIterator], i;
14
- return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
15
- function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
16
- function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
17
- };
18
- Object.defineProperty(exports, "__esModule", { value: true });
19
- const vitest_1 = require("vitest");
20
- const reader_1 = require("../src/reader");
21
- const testUtils_1 = require("../src/testUtils");
22
- const encoder = new TextEncoder();
23
- (0, vitest_1.describe)('Reader', () => __awaiter(void 0, void 0, void 0, function* () {
24
- (0, vitest_1.test)('handles string', () => __awaiter(void 0, void 0, void 0, function* () {
25
- const reader = new reader_1.ReaderInstance(testUtils_1.uri, testUtils_1.client, testUtils_1.logger);
26
- const promise = (0, testUtils_1.one)(reader.strings());
27
- reader.handleMsg({ data: encoder.encode('Hello world'), channel: testUtils_1.uri });
28
- const out = yield promise;
29
- (0, vitest_1.expect)(out).toEqual('Hello world');
30
- }));
31
- (0, vitest_1.test)('handles closed string', () => __awaiter(void 0, void 0, void 0, function* () {
32
- const reader = new reader_1.ReaderInstance(testUtils_1.uri, testUtils_1.client, testUtils_1.logger);
33
- const promise = (0, testUtils_1.one)(reader.strings());
34
- reader.close();
35
- const out = yield promise;
36
- (0, vitest_1.expect)(out).toEqual(undefined);
37
- }));
38
- (0, vitest_1.test)('handles strings', () => __awaiter(void 0, void 0, void 0, function* () {
39
- const reader = new reader_1.ReaderInstance(testUtils_1.uri, testUtils_1.client, testUtils_1.logger);
40
- const msgsPromise = (() => __awaiter(void 0, void 0, void 0, function* () {
41
- var _a, e_1, _b, _c;
42
- const msgs = [];
43
- try {
44
- for (var _d = true, _e = __asyncValues(reader.strings()), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
45
- _c = _f.value;
46
- _d = false;
47
- const msg = _c;
48
- msgs.push(msg);
49
- }
50
- }
51
- catch (e_1_1) { e_1 = { error: e_1_1 }; }
52
- finally {
53
- try {
54
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
55
- }
56
- finally { if (e_1) throw e_1.error; }
57
- }
58
- return msgs;
59
- }))();
60
- reader.handleMsg({ data: encoder.encode('Hello'), channel: testUtils_1.uri });
61
- reader.handleMsg({ data: encoder.encode('World'), channel: testUtils_1.uri });
62
- reader.close();
63
- const msgs = yield msgsPromise;
64
- (0, vitest_1.expect)(msgs).toEqual(['Hello', 'World']);
65
- }));
66
- (0, vitest_1.test)('handles buffer', () => __awaiter(void 0, void 0, void 0, function* () {
67
- const reader = new reader_1.ReaderInstance(testUtils_1.uri, testUtils_1.client, testUtils_1.logger);
68
- const data = encoder.encode('Hello world');
69
- const promise = (0, testUtils_1.one)(reader.buffers());
70
- reader.handleMsg({ data, channel: testUtils_1.uri });
71
- const out = yield promise;
72
- (0, vitest_1.expect)(out).toEqual(data);
73
- }));
74
- (0, vitest_1.test)('handles buffers', () => __awaiter(void 0, void 0, void 0, function* () {
75
- const reader = new reader_1.ReaderInstance(testUtils_1.uri, testUtils_1.client, testUtils_1.logger);
76
- const data = encoder.encode('Hello world');
77
- const promise = (0, testUtils_1.one)(reader.buffers());
78
- reader.handleMsg({ data, channel: testUtils_1.uri });
79
- const out = yield promise;
80
- (0, vitest_1.expect)(out).toEqual(data);
81
- }));
82
- (0, vitest_1.test)('handles stream', () => __awaiter(void 0, void 0, void 0, function* () {
83
- const reader = new reader_1.ReaderInstance(testUtils_1.uri, testUtils_1.client, testUtils_1.logger);
84
- const nextStream = testUtils_1.client.nextStream();
85
- const promise = (0, testUtils_1.one)(reader.streams());
86
- reader.handleStreamingMessage({ id: { id: 5 }, channel: testUtils_1.uri });
87
- const stream = yield nextStream;
88
- stream.push('Hello');
89
- stream.push('World');
90
- console.log(stream.eventNames());
91
- const out = yield promise;
92
- const msgsPromise = (() => __awaiter(void 0, void 0, void 0, function* () {
93
- var _a, e_2, _b, _c;
94
- const msgs = [];
95
- try {
96
- for (var _d = true, _e = __asyncValues(out), _f; _f = yield _e.next(), _a = _f.done, !_a; _d = true) {
97
- _c = _f.value;
98
- _d = false;
99
- const msg = _c;
100
- msgs.push(msg);
101
- }
102
- }
103
- catch (e_2_1) { e_2 = { error: e_2_1 }; }
104
- finally {
105
- try {
106
- if (!_d && !_a && (_b = _e.return)) yield _b.call(_e);
107
- }
108
- finally { if (e_2) throw e_2.error; }
109
- }
110
- return msgs;
111
- }))();
112
- stream.emit('end');
113
- stream.emit('close');
114
- const msgs = yield msgsPromise;
115
- console.log(msgs);
116
- }));
117
- }));
@@ -1,9 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <module type="JAVA_MODULE" version="4">
3
- <component name="NewModuleRootManager" inherit-compiler-output="true">
4
- <exclude-output />
5
- <content url="file://$MODULE_DIR$" />
6
- <orderEntry type="inheritedJdk" />
7
- <orderEntry type="sourceFolder" forTests="false" />
8
- </component>
9
- </module>
@@ -1,6 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="ProjectRootManager" version="2" languageLevel="JDK_23" project-jdk-name="24" project-jdk-type="JavaSDK">
4
- <output url="file://$PROJECT_DIR$/out" />
5
- </component>
6
- </project>
@@ -1,7 +0,0 @@
1
- <?xml version="1.0" encoding="UTF-8"?>
2
- <project version="4">
3
- <component name="VcsDirectoryMappings">
4
- <mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
5
- <mapping directory="$PROJECT_DIR$/node_modules/@rdfc/js-runner" vcs="Git" />
6
- </component>
7
- </project>
@@ -1 +0,0 @@
1
- { "disabled": ["shapes"] }
@@ -1,3 +0,0 @@
1
- @prefix foaf: <http://xmlns.com/foaf/0.1/>.
2
- <#me> a foaf:Person;
3
- foaf:name "".
@@ -1,90 +0,0 @@
1
- @prefix rdfc: <https://w3id.org/rdf-connect#>.
2
- @prefix prov: <http://www.w3.org/ns/prov#>.
3
- @prefix sds: <https://w3id.org/sds#>.
4
- @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
5
- @prefix owl: <http://www.w3.org/2002/07/owl#>.
6
- @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
7
- @prefix sh: <http://www.w3.org/ns/shacl#>.
8
-
9
- ############################################################
10
- # General statements #
11
- ############################################################
12
- # sds:Activity is a prov:Activity
13
- sds:Activity rdfs:subClassOf prov:Activity.
14
-
15
- # rdfc:Processor too
16
- rdfc:Processor rdfs:subClassOf sds:Activity.
17
-
18
- # sds:implementationOf is subClassOf
19
- sds:implementationOf rdfs:subPropertyOf rdfs:subClassOf.
20
-
21
- ############################################################
22
- # Javascript statements #
23
- ############################################################
24
- # specialized for js too
25
- rdfc:jsImplementationOf rdfs:subPropertyOf sds:implementationOf.
26
-
27
- # A node runner, runs things that are rdfc:jsImplementationOf rdfc:Processor (aka, rdfs:subClassOf prov:Activity)
28
- rdfc:NodeRunner a rdfc:Runner;
29
- rdfc:handlesSubjectsOf rdfc:jsImplementationOf;
30
- rdfc:command "npx js-runner".
31
-
32
- # Shape that a Js Processor should fulfil;
33
- [ ] a sh:NodeShape;
34
- # We target it with jsImplementationOf
35
- sh:targetSubjectsOf rdfc:jsImplementationOf;
36
- sh:property [
37
- sh:path rdfc:entrypoint;
38
- sh:name "location";
39
- sh:minCount 1;
40
- sh:maxCount 1;
41
- sh:datatype xsd:string;
42
- ], [
43
- sh:path rdfc:file;
44
- sh:name "file";
45
- sh:minCount 1;
46
- sh:maxCount 1;
47
- sh:datatype xsd:string;
48
- ], [
49
- sh:path rdfc:class;
50
- sh:name "clazz";
51
- sh:maxCount 1;
52
- sh:datatype xsd:string;
53
- ].
54
-
55
- ############################################################
56
- # Processor statements #
57
- ############################################################
58
- rdfc:FooBarProcessor a owl:Class, rdfs:Class;
59
- rdfc:jsImplementationOf rdfc:Processor;
60
- rdfs:label "My Epic FooBar Processor";
61
- rdfs:description "FooBars everything!";
62
- rdfc:entrypoint <./>;
63
- rdfc:file <./lib/processors.js>;
64
- rdfc:class "FooBarProcessor".
65
-
66
- [ ] a sh:NodeShape;
67
- sh:targetClass rdfc:FooBarProcessor;
68
- sh:property [
69
- sh:path rdfc:reader;
70
- sh:name "reader";
71
- sh:minCount 1;
72
- sh:maxCount 1;
73
- sh:class rdfc:Reader;
74
- ].
75
-
76
- ############################################################
77
- # Pipeline statements #
78
- ############################################################
79
- <> a rdfc:Pipeline;
80
- rdfc:consistsOf [
81
- rdfc:instantiates rdfc:NodeRunner;
82
- rdfc:processor <foobar>;
83
- ].
84
-
85
- # <incomingMessages> a rdfc:Reader.
86
- <incomingMessages> a rdfc:Reader.
87
-
88
- <fooBar> a rdfc:FooBarProcessor;
89
- rdfc:reader <incomingMessages>.
90
-
@@ -1,9 +0,0 @@
1
-
2
- @prefix foaf: <http://xmlns.com/foaf/0.1/>.
3
- @prefix sh: <http://www.w3.org/ns/shacl#>.
4
- [] a sh:NodeShape;
5
- sh:targetClass foaf:Person;
6
- sh:property [
7
- sh:path foaf:name;
8
- sh:minCount 1;
9
- ].