@rdfc/js-runner 2.0.0-alpha.1 → 2.0.0-alpha.11

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 (46) hide show
  1. package/.editorconfig +9 -0
  2. package/__tests__/channels.test.ts +1 -38
  3. package/examples/echo/.idea/echo.iml +9 -0
  4. package/examples/echo/.idea/misc.xml +6 -0
  5. package/examples/echo/.idea/modules.xml +8 -0
  6. package/examples/echo/.idea/vcs.xml +7 -0
  7. package/examples/echo/.swls/config.json +1 -0
  8. package/examples/echo/index.ttl +3 -0
  9. package/examples/echo/minimal.ttl +90 -0
  10. package/examples/echo/pipeline.ttl +22 -4
  11. package/examples/echo/processors.ttl +5 -7
  12. package/examples/echo/shacl.ttl +9 -0
  13. package/examples/echo/shape.ttl +1339 -0
  14. package/examples/echo/src/processors.ts +3 -4
  15. package/examples/echo/test.ttl +9 -12
  16. package/file:/home/silvius/Projects/mumo-pipeline/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
  17. package/index.ttl +29 -21
  18. package/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
  19. package/lib/client.js +2 -2
  20. package/lib/index.d.ts +3 -0
  21. package/lib/index.js +4 -1
  22. package/lib/jsonld.js +4 -3
  23. package/lib/logger.d.ts +3 -1
  24. package/lib/logger.js +23 -1
  25. package/lib/reader.d.ts +2 -1
  26. package/lib/reader.js +7 -16
  27. package/lib/reexports.d.ts +3 -0
  28. package/lib/reexports.js +4 -0
  29. package/lib/runner.d.ts +4 -3
  30. package/lib/runner.js +30 -20
  31. package/lib/testUtils.d.ts +24 -0
  32. package/lib/testUtils.js +150 -0
  33. package/lib/tsconfig.tsbuildinfo +1 -1
  34. package/lib/writer.d.ts +2 -1
  35. package/lib/writer.js +1 -1
  36. package/minimal.ttl +99 -0
  37. package/package.json +3 -3
  38. package/src/client.ts +15 -15
  39. package/src/index.ts +4 -0
  40. package/src/jsonld.ts +5 -2
  41. package/src/logger.ts +27 -1
  42. package/src/reader.ts +9 -15
  43. package/src/reexports.ts +6 -0
  44. package/src/runner.ts +51 -40
  45. package/src/testUtils.ts +196 -0
  46. package/src/writer.ts +2 -1
package/minimal.ttl ADDED
@@ -0,0 +1,99 @@
1
+ @prefix prov: <http://www.w3.org/ns/prov#>.
2
+ @prefix sds: <https://w3id.org/sds#>.
3
+ @prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
4
+ @prefix owl: <http://www.w3.org/2002/07/owl#>.
5
+ @prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
6
+ @prefix sh: <http://www.w3.org/ns/shacl#>.
7
+ @prefix rdfc: <https://w3id.org/rdf-connect#>.
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
+ # This shouldn't be necessary, should work with sh:targetSubjectsOf
33
+ # rdfc:processor_definition <JsProcessorShape>.
34
+ #
35
+ # Shape that a Js Processor should fulfil;
36
+ [ ] a sh:NodeShape;
37
+ # This shouldn't be necessary,should work with sh:targetSubjectsOf and this isn't a real Class
38
+ sh:targetClass <JsProcessorShape>;
39
+ # We target it with jsImplementationOf
40
+ sh:targetSubjectsOf rdfc:jsImplementationOf;
41
+ sh:property [
42
+ sh:path rdfc:entrypoint;
43
+ sh:name "location";
44
+ sh:minCount 1;
45
+ sh:maxCount 1;
46
+ sh:datatype xsd:iri;
47
+ ], [
48
+ sh:path rdfc:file;
49
+ sh:name "file";
50
+ sh:minCount 1;
51
+ sh:maxCount 1;
52
+ sh:datatype xsd:iri;
53
+ ], [
54
+ sh:path rdfc:class;
55
+ sh:name "clazz";
56
+ sh:maxCount 1;
57
+ sh:datatype xsd:string;
58
+ ].
59
+
60
+ ############################################################
61
+ # Processor statements #
62
+ ############################################################
63
+ rdfc:FooBarProcessor a owl:Class;
64
+ rdfs:label "My Epic FooBar Processor";
65
+ rdfs:description "FooBars everything!";
66
+ rdfc:jsImplementationOf rdfc:Processor;
67
+ rdfc:entrypoint <./>;
68
+ rdfc:file <./lib/processors.js>;
69
+ rdfc:class "FooBarProcessor".
70
+
71
+ [ ] a sh:NodeShape;
72
+ sh:targetClass rdfc:FooBarProcessor;
73
+ sh:property [
74
+ sh:path rdfc:reader;
75
+ sh:name "reader";
76
+ sh:minCount 1;
77
+ sh:maxCount 1;
78
+ sh:class rdfc:Reader;
79
+ ], [
80
+ sh:path rdfc:writer;
81
+ sh:name "writer";
82
+ sh:maxCount 1;
83
+ sh:class rdfc:Writer;
84
+ ].
85
+
86
+ ############################################################
87
+ # Pipeline statements #
88
+ ############################################################
89
+ <> a rdfc:Pipeline;
90
+ rdfc:consistsOf [
91
+ rdfc:instantiates rdfc:NodeRunner;
92
+ rdfc:processor <foobar>;
93
+ ].
94
+
95
+ <incomingMessages> a rdfc:Reader, rdfc:Writer.
96
+ <foobar> a rdfc:FooBarProcessor;
97
+ rdfc:reader <incomingMessages>;
98
+ rdfc:writer <incomingMessages>.
99
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rdfc/js-runner",
3
- "version": "2.0.0-alpha.1",
3
+ "version": "2.0.0-alpha.11",
4
4
  "main": "lib/index.js",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -37,7 +37,7 @@
37
37
  "@types/node": "^22.13.5",
38
38
  "@typescript-eslint/eslint-plugin": "^8.25.0",
39
39
  "@typescript-eslint/parser": "^8.25.0",
40
- "@vitest/coverage-v8": "^3.0.7",
40
+ "@vitest/coverage-v8": "^3.2.4",
41
41
  "eslint": "^9.21.0",
42
42
  "eslint-config-prettier": "^10.0.1",
43
43
  "eslint-plugin-prettier": "^5.2.3",
@@ -50,7 +50,7 @@
50
50
  "tsc-alias": "^1.8.10",
51
51
  "typescript": "^5.7.3",
52
52
  "typescript-eslint": "^8.25.0",
53
- "vitest": "^3.0.7"
53
+ "vitest": "^3.2.4"
54
54
  },
55
55
  "dependencies": {
56
56
  "@grpc/grpc-js": "^1.12.6",
package/src/client.ts CHANGED
@@ -12,7 +12,7 @@ export async function start(addr: string, uri: string) {
12
12
  transports: [
13
13
  new RpcTransport({
14
14
  entities: [uri, 'cli'],
15
- stream: client.logStream(() => {}),
15
+ stream: client.logStream(() => { }),
16
16
  }),
17
17
  ],
18
18
  })
@@ -27,26 +27,26 @@ export async function start(addr: string, uri: string) {
27
27
 
28
28
  let processorsEnd!: (v: unknown) => unknown
29
29
  const processorsEnded = new Promise((res) => (processorsEnd = res))
30
- ;(async () => {
31
- for await (const chunk of stream) {
32
- const msg: RunnerMessage = chunk
33
- if (msg.proc) {
34
- await runner.addProcessor(msg.proc)
30
+ ; (async () => {
31
+ for await (const chunk of stream) {
32
+ const msg: RunnerMessage = chunk
33
+ if (msg.proc) {
34
+ await runner.addProcessor(msg.proc)
35
+ }
36
+ if (msg.start) {
37
+ runner.start().then(processorsEnd)
38
+ }
39
+
40
+ await runner.handleOrchMessage(msg)
35
41
  }
36
- if (msg.start) {
37
- runner.start().then(processorsEnd)
38
- }
39
-
40
- await runner.handleOrchMessage(msg)
41
- }
42
42
 
43
- logger.error('Stream ended')
44
- })()
43
+ logger.error('Stream ended')
44
+ })()
45
45
 
46
46
  await processorsEnded
47
47
 
48
48
  logger.info('All processors are finished')
49
49
  stream.end()
50
50
  client.close()
51
- process.exit(0)
51
+ setTimeout(() => process.exit(0), 500)
52
52
  }
package/src/index.ts CHANGED
@@ -2,3 +2,7 @@ export * from './client'
2
2
  export * from './writer'
3
3
  export * from './reader'
4
4
  export * from './processor'
5
+ export * from './runner'
6
+ export * from './logger'
7
+
8
+ export * as reexports from './reexports'
package/src/jsonld.ts CHANGED
@@ -30,7 +30,7 @@ const RDFL = createUriAndTermNamespace(
30
30
 
31
31
  type Writable = (msg: OrchestratorMessage) => Promise<unknown>
32
32
  const RDFC = createNamespace(
33
- 'https://w3id.org/rdf-connect/ontology#',
33
+ 'https://w3id.org/rdf-connect#',
34
34
  (x) => x,
35
35
  'Reader',
36
36
  'Writer',
@@ -189,7 +189,10 @@ function expandArgs(args: Arg, cont: Cont, logger: Logger) {
189
189
  let x = 'object'
190
190
  try {
191
191
  x = JSON.stringify(v)
192
- } catch (e) {}
192
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
193
+ } catch (_e: unknown) {
194
+ // default is set
195
+ }
193
196
 
194
197
  return k + ': ' + x
195
198
  }),
package/src/logger.ts CHANGED
@@ -1,12 +1,14 @@
1
- import winston from 'winston'
1
+ import winston, { Logger } from 'winston'
2
2
  import Transport from 'winston-transport'
3
3
 
4
4
  import * as grpc from '@grpc/grpc-js'
5
5
  import { LogMessage } from '@rdfc/proto'
6
+
6
7
  export class RpcTransport extends Transport {
7
8
  private readonly stream: grpc.ClientWritableStream<LogMessage>
8
9
  private readonly entities: string[]
9
10
  private readonly aliases: string[]
11
+
10
12
  constructor(opts: {
11
13
  stream: grpc.ClientWritableStream<LogMessage>
12
14
  entities: string[]
@@ -35,4 +37,28 @@ export class RpcTransport extends Transport {
35
37
  callback()
36
38
  }
37
39
  }
40
+
41
+ withEntity(entity: string): RpcTransport {
42
+ return new RpcTransport({
43
+ stream: this.stream,
44
+ entities: [...this.entities, entity],
45
+ aliases: this.aliases,
46
+ })
47
+ }
48
+ }
49
+
50
+ export function extendLogger(baseLogger: Logger, newEntity: string): Logger {
51
+ const newTransports = baseLogger.transports.map((t) => {
52
+ if (t instanceof RpcTransport) {
53
+ return t.withEntity(newEntity)
54
+ }
55
+ return t
56
+ })
57
+
58
+ return winston.createLogger({
59
+ level: baseLogger.level,
60
+ format: baseLogger.format,
61
+ defaultMeta: baseLogger.defaultMeta,
62
+ transports: newTransports,
63
+ })
38
64
  }
package/src/reader.ts CHANGED
@@ -21,6 +21,7 @@ export type Any =
21
21
  }
22
22
 
23
23
  export interface Reader {
24
+ readonly uri: string
24
25
  strings(): AsyncIterable<string>
25
26
  streams(): AsyncIterable<AsyncGenerator<Uint8Array>>
26
27
  buffers(): AsyncIterable<Uint8Array>
@@ -30,7 +31,7 @@ export interface Reader {
30
31
  class MyIter<T> implements AsyncIterable<T> {
31
32
  private convertor: Convertor<T>
32
33
  private queue: (T | undefined)[] = []
33
- private resolveNext: ((value: T | undefined) => void) | null = null
34
+ private resolveNext: ((value: undefined) => void) | null = null
34
35
 
35
36
  constructor(convertor: Convertor<T>) {
36
37
  this.convertor = convertor
@@ -38,20 +39,18 @@ class MyIter<T> implements AsyncIterable<T> {
38
39
 
39
40
  push(buffer: Uint8Array) {
40
41
  const item = this.convertor.from(buffer)
42
+ this.queue.push(item)
41
43
  if (this.resolveNext) {
42
- this.resolveNext(item)
44
+ this.resolveNext(undefined)
43
45
  this.resolveNext = null
44
- } else {
45
- this.queue.push(item)
46
46
  }
47
47
  }
48
48
 
49
49
  close() {
50
+ this.queue.push(undefined)
50
51
  if (this.resolveNext) {
51
52
  this.resolveNext(undefined)
52
53
  this.resolveNext = null
53
- } else {
54
- this.queue.push(undefined)
55
54
  }
56
55
  }
57
56
 
@@ -63,11 +62,10 @@ class MyIter<T> implements AsyncIterable<T> {
63
62
  }
64
63
  })(chunks)
65
64
  const item = await this.convertor.fromStream(stream)
65
+ this.queue.push(item)
66
66
  if (this.resolveNext) {
67
- this.resolveNext(item)
67
+ this.resolveNext(undefined)
68
68
  this.resolveNext = null
69
- } else {
70
- this.queue.push(item)
71
69
  }
72
70
  }
73
71
 
@@ -78,11 +76,7 @@ class MyIter<T> implements AsyncIterable<T> {
78
76
  if (item === undefined) break
79
77
  yield item
80
78
  } else {
81
- const item = await new Promise<T | undefined>(
82
- (resolve) => (this.resolveNext = resolve),
83
- )
84
- if (item === undefined) break
85
- yield item
79
+ await new Promise<undefined>((resolve) => (this.resolveNext = resolve))
86
80
  }
87
81
  }
88
82
  }
@@ -90,7 +84,7 @@ class MyIter<T> implements AsyncIterable<T> {
90
84
 
91
85
  export class ReaderInstance implements Reader {
92
86
  private client: RunnerClient
93
- private uri: string
87
+ readonly uri: string
94
88
  private logger: winston.Logger
95
89
 
96
90
  private iterators: MyIter<unknown>[] = []
@@ -0,0 +1,6 @@
1
+ export * from '@rdfc/proto'
2
+ export {
3
+ ClientReadableStreamImpl,
4
+ ClientReadableStream,
5
+ } from '@grpc/grpc-js/build/src/call.js'
6
+ export * as grpc from '@grpc/grpc-js'
package/src/runner.ts CHANGED
@@ -11,7 +11,6 @@ import { Logger } from 'winston'
11
11
 
12
12
  import winston from 'winston'
13
13
  import { RpcTransport } from './logger'
14
- import { parse_jsonld } from './jsonld'
15
14
  import { Cont, empty, extractShapes, Shapes } from 'rdf-lens'
16
15
  import { NamedNode, Parser } from 'n3'
17
16
  import { createNamespace, createUriAndTermNamespace, RDF } from '@treecg/types'
@@ -31,13 +30,13 @@ const RDFL = createUriAndTermNamespace(
31
30
  )
32
31
 
33
32
  const RDFC = createNamespace(
34
- 'https://w3id.org/rdf-connect/ontology#',
33
+ 'https://w3id.org/rdf-connect#',
35
34
  (x) => x,
36
35
  'Reader',
37
36
  'Writer',
38
37
  )
39
38
 
40
- type Writable = (msg: OrchestratorMessage) => Promise<unknown>
39
+ export type Writable = (msg: OrchestratorMessage) => Promise<unknown>
41
40
 
42
41
  type ProcessorConfig = {
43
42
  location: string
@@ -45,6 +44,8 @@ type ProcessorConfig = {
45
44
  clazz?: string
46
45
  }
47
46
 
47
+ export type FullProc<C extends Proc<unknown>> =
48
+ C extends Proc<infer T> ? T & C : unknown
48
49
  export class Runner {
49
50
  private readonly readers: { [uri: string]: ReaderInstance[] } = {}
50
51
  private readonly writers: { [uri: string]: WriterInstance[] } = {}
@@ -71,69 +72,72 @@ export class Runner {
71
72
  this.logger = logger
72
73
  }
73
74
 
74
- async addProcessor(proc: Processor) {
75
+ async addProcessor<P extends Proc<unknown>>(
76
+ proc: Processor,
77
+ ): Promise<FullProc<P>> {
75
78
  const procLogger = winston.createLogger({
76
79
  transports: [
77
80
  new RpcTransport({
78
81
  entities: [proc.uri, this.uri],
79
- stream: this.client.logStream(() => { }),
82
+ stream: this.client.logStream(() => {}),
80
83
  }),
81
84
  ],
82
85
  })
83
86
 
84
- const ty = JSON.stringify(this.quads.filter(x => x.subject.value === proc.uri && x.predicate.equals(RDF.terms.type)).map(x => x.object.value))
85
- this.logger.info("parsing " + proc.uri + " type " + ty)
87
+ const ty = JSON.stringify(
88
+ this.quads
89
+ .filter(
90
+ (x) =>
91
+ x.subject.value === proc.uri && x.predicate.equals(RDF.terms.type),
92
+ )
93
+ .map((x) => x.object.value),
94
+ )
95
+ this.logger.info('parsing ' + proc.uri + ' type ' + ty)
86
96
  const args = this.shapes.lenses[RDFL.TypedExtract].execute({
87
97
  id: new NamedNode(proc.uri),
88
98
  quads: this.quads,
89
99
  })
90
- // this.logger.info('Parsed ' + JSON.stringify(args))
91
-
92
- // const args = await parse_jsonld(proc.arguments, procLogger, {
93
- // client: this.client,
94
- // write: this.write,
95
- // readers: this.readers,
96
- // writers: this.writers,
97
- // })
98
100
 
99
101
  const config: ProcessorConfig = JSON.parse(proc.config)
100
- const url = new URL(config.location)
101
- process.chdir(url.pathname)
102
+ // const url = new URL(config.location)
103
+ // process.chdir(url.pathname)
102
104
  const jsProgram = await import(config.file)
103
105
  const clazz = jsProgram[config.clazz || 'default']
104
106
  const instance: Proc<unknown> = new clazz(args, procLogger)
105
107
  await instance.init()
106
108
 
107
- this.logger.info("inited " + proc.uri + " type " + ty)
108
- await this.write({ init: { uri: proc.uri } })
109
+ this.logger.info('inited ' + proc.uri + ' type ' + ty)
109
110
 
110
111
  this.processors.push(instance)
111
112
  this.processorTransforms.push(instance.transform())
113
+
114
+ await this.write({ init: { uri: proc.uri } })
115
+
116
+ return <FullProc<P>>instance
112
117
  }
113
118
 
114
119
  async start() {
115
- await Promise.all(this.processors.map((x) => x.produce()))
116
- await Promise.all(this.processorTransforms)
120
+ try {
121
+ await Promise.all(this.processors.map((x) => x.produce()))
122
+ await Promise.all(this.processorTransforms)
123
+ } catch (ex: unknown) {
124
+ this.logger.error('Start failed: ' + JSON.stringify(ex))
125
+ }
117
126
  }
118
127
 
119
128
  createWriter(uri: Term): Writer {
120
- const ids = uri.value;
129
+ const ids = uri.value
121
130
 
122
131
  if (this.writers[ids] === undefined) {
123
132
  this.writers[ids] = []
124
133
  }
125
- const writer = new WriterInstance(
126
- ids,
127
- this.client,
128
- this.write,
129
- this.logger,
130
- )
134
+ const writer = new WriterInstance(ids, this.client, this.write, this.logger)
131
135
  this.writers[ids].push(writer)
132
136
  return writer
133
137
  }
134
138
 
135
139
  createReader(uri: Term): Reader {
136
- const ids = uri.value;
140
+ const ids = uri.value
137
141
 
138
142
  if (this.readers[ids] === undefined) {
139
143
  this.readers[ids] = []
@@ -170,17 +174,24 @@ export class Runner {
170
174
  }
171
175
 
172
176
  if (msg.pipeline) {
173
- // here
174
- const quads = new Parser().parse(msg.pipeline);
175
- this.shapes = extractShapes(quads, {
176
- [RDFC.Reader]: (x: Cont) => this.createReader(x.id),
177
- [RDFC.Writer]: (x: Cont) => this.createWriter(x.id)
178
- }, {
179
- [RDFC.Reader]: empty<Cont>(),
180
- [RDFC.Writer]: empty<Cont>()
181
- });
182
- this.quads = quads;
183
- this.logger.info("extracted shapes " + JSON.stringify(Object.keys(this.shapes.lenses)));
177
+ try {
178
+ // here
179
+ const quads = new Parser().parse(msg.pipeline)
180
+ this.shapes = extractShapes(
181
+ quads,
182
+ {
183
+ [RDFC.Reader]: (x: Cont) => this.createReader(x.id),
184
+ [RDFC.Writer]: (x: Cont) => this.createWriter(x.id),
185
+ },
186
+ {
187
+ [RDFC.Reader]: empty<Cont>(),
188
+ [RDFC.Writer]: empty<Cont>(),
189
+ },
190
+ )
191
+ this.quads = quads
192
+ } catch (ex: unknown) {
193
+ this.logger.error('Pipeline failed: ' + JSON.stringify(ex))
194
+ }
184
195
  }
185
196
  }
186
197
  }
@@ -0,0 +1,196 @@
1
+ import { DataChunk, RunnerClient } from '@rdfc/proto'
2
+ import * as grpc from '@grpc/grpc-js'
3
+ import { ClientReadableStream } from '@grpc/grpc-js'
4
+ import { ClientReadableStreamImpl, OrchestratorMessage } from './reexports'
5
+ import { extractShapes } from 'rdf-lens'
6
+ import { NamedNode, Parser, Writer } from 'n3'
7
+ import { readFile } from 'fs/promises'
8
+ import winston, { createLogger } from 'winston'
9
+ import { WriterInstance } from './writer'
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'
16
+
17
+ export async function getProcessorShape(
18
+ baseIRI = process.cwd() + '/node_modules/@rdfc/js-runner/index.ttl',
19
+ ) {
20
+ const configFile = await readFile(baseIRI, { encoding: 'utf8' })
21
+ const configQuads = new Parser().parse(configFile)
22
+ const shapes = extractShapes(configQuads)
23
+
24
+ return shapes
25
+ }
26
+
27
+ export class TestClient extends RunnerClient {
28
+ next: (stream: ClientReadableStream<DataChunk>) => unknown
29
+
30
+ constructor() {
31
+ super('localhost:5400', grpc.credentials.createInsecure())
32
+ }
33
+
34
+ nextStream(): Promise<ClientReadableStream<DataChunk>> {
35
+ return new Promise((res) => (this.next = res))
36
+ }
37
+
38
+ receiveStreamMessage(): ClientReadableStream<DataChunk> {
39
+ const stream = new ClientReadableStreamImpl<DataChunk>((data: Buffer) => {
40
+ return { data }
41
+ })
42
+ this.next(stream)
43
+ return stream
44
+ }
45
+ }
46
+
47
+ export async function one<T>(iter: AsyncIterable<T>): Promise<T | undefined> {
48
+ for await (const item of iter) {
49
+ return item
50
+ }
51
+ }
52
+
53
+ export const client = new TestClient()
54
+ export const uri = 'someUri'
55
+ export const logger = createLogger({
56
+ transports: new winston.transports.Console({
57
+ level: process.env['DEBUG'] || 'info',
58
+ }),
59
+ })
60
+
61
+ export function createWriter(iri = uri): [WriterInstance, ReaderInstance] {
62
+ const reader = createReader(iri)
63
+ const writeStream = new WriterInstance(
64
+ iri,
65
+ client,
66
+ async (msg) => {
67
+ if (msg.msg) {
68
+ reader.handleMsg(msg.msg)
69
+ }
70
+ if (msg.close) {
71
+ reader.close()
72
+ }
73
+ },
74
+ logger,
75
+ )
76
+ return [writeStream, reader]
77
+ }
78
+
79
+ export function createReader(iri = uri): ReaderInstance {
80
+ const reader = new ReaderInstance(iri, client, logger)
81
+ return reader
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
+ }