@rdfc/js-runner 2.0.0 → 3.0.1
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/.editorconfig +9 -0
- package/.github/renovate.json +3 -0
- package/.idea/copilot.data.migration.agent.xml +6 -0
- package/.idea/copilot.data.migration.ask.xml +6 -0
- package/.idea/copilot.data.migration.ask2agent.xml +6 -0
- package/.idea/copilot.data.migration.edit.xml +6 -0
- package/README.md +127 -3
- package/__tests__/channels.test.ts +131 -74
- package/__tests__/echoProcessor.test.ts +132 -0
- package/__tests__/testProcessor.test.ts +69 -0
- package/eslint.config.mjs +1 -1
- package/index.ttl +3 -31
- package/lib/client.js +6 -9
- package/lib/logger.d.ts +2 -2
- package/lib/logger.js +3 -3
- package/lib/reader.d.ts +8 -6
- package/lib/reader.js +135 -25
- package/lib/runner.d.ts +11 -6
- package/lib/runner.js +86 -46
- package/lib/testUtils/duplex.d.ts +25 -0
- package/lib/testUtils/duplex.js +70 -0
- package/lib/testUtils/index.d.ts +51 -0
- package/lib/testUtils/index.js +243 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/writer.d.ts +12 -5
- package/lib/writer.js +66 -13
- package/package.json +22 -22
- package/src/client.ts +8 -11
- package/src/logger.ts +3 -3
- package/src/reader.ts +214 -36
- package/src/runner.ts +129 -67
- package/src/testUtils/duplex.ts +112 -0
- package/src/testUtils/index.ts +430 -0
- package/src/writer.ts +106 -16
- package/lib/jsonld.d.ts +0 -17
- package/lib/jsonld.js +0 -135
- package/lib/testUtils.d.ts +0 -24
- package/lib/testUtils.js +0 -150
- package/src/jsonld.ts +0 -220
- package/src/testUtils.ts +0 -196
package/src/jsonld.ts
DELETED
|
@@ -1,220 +0,0 @@
|
|
|
1
|
-
import { OrchestratorMessage, RunnerClient } from '@rdfc/proto'
|
|
2
|
-
import { createNamespace, createUriAndTermNamespace } from '@treecg/types'
|
|
3
|
-
import { ReaderInstance } from './reader'
|
|
4
|
-
import { WriterInstance } from './writer'
|
|
5
|
-
import { Logger } from 'winston'
|
|
6
|
-
import { JsonLdParser } from 'jsonld-streaming-parser'
|
|
7
|
-
import { pred, ShaclPath } from 'rdf-lens'
|
|
8
|
-
import { Quad, Term } from '@rdfjs/types'
|
|
9
|
-
import { NamedNode } from 'n3'
|
|
10
|
-
|
|
11
|
-
export type RunnerItems = {
|
|
12
|
-
readers: { [uri: string]: ReaderInstance[] }
|
|
13
|
-
writers: { [uri: string]: WriterInstance[] }
|
|
14
|
-
client: RunnerClient
|
|
15
|
-
write: Writable
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const RDFL = createUriAndTermNamespace(
|
|
19
|
-
'https://w3id.org/rdf-lens/ontology#',
|
|
20
|
-
'CBD',
|
|
21
|
-
'Path',
|
|
22
|
-
'PathLens',
|
|
23
|
-
'Context',
|
|
24
|
-
'TypedExtract',
|
|
25
|
-
'EnvVariable',
|
|
26
|
-
'envKey',
|
|
27
|
-
'envDefault',
|
|
28
|
-
'datatype',
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
type Writable = (msg: OrchestratorMessage) => Promise<unknown>
|
|
32
|
-
const RDFC = createNamespace(
|
|
33
|
-
'https://w3id.org/rdf-connect#',
|
|
34
|
-
(x) => x,
|
|
35
|
-
'Reader',
|
|
36
|
-
'Writer',
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
function as_string_array(obj: unknown): string[] {
|
|
40
|
-
const out = Array.isArray(obj) ? obj : [obj]
|
|
41
|
-
return out.filter((x) => typeof x === 'string')
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
function cbdToQuads(value: unknown) {
|
|
45
|
-
const quads: Quad[] = []
|
|
46
|
-
const parser = new JsonLdParser()
|
|
47
|
-
const promise = new Promise<{ value: Quad[] }>((res, rej) =>
|
|
48
|
-
parser
|
|
49
|
-
.on('end', () => res({ value: quads }))
|
|
50
|
-
.on('error', (e) => rej(e))
|
|
51
|
-
.on('data', (q) => {
|
|
52
|
-
quads.push(q)
|
|
53
|
-
}),
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
if (value instanceof Object && '@type' in value) {
|
|
57
|
-
delete value['@type']
|
|
58
|
-
}
|
|
59
|
-
parser.write(JSON.stringify(value))
|
|
60
|
-
parser.end()
|
|
61
|
-
return promise
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
async function cbdToPath(value: unknown): Promise<{ value: unknown }> {
|
|
65
|
-
const qs = (
|
|
66
|
-
await cbdToQuads({
|
|
67
|
-
'@id': 'http://example.com/ns#me1234',
|
|
68
|
-
'http://example.com/ns#innerPred': value,
|
|
69
|
-
})
|
|
70
|
-
).value
|
|
71
|
-
|
|
72
|
-
const path = pred(new NamedNode('http://example.com/ns#innerPred'))
|
|
73
|
-
.one()
|
|
74
|
-
.then(ShaclPath)
|
|
75
|
-
.execute({
|
|
76
|
-
quads: qs,
|
|
77
|
-
id: new NamedNode('http://example.com/ns#me1234'),
|
|
78
|
-
})
|
|
79
|
-
return { value: path }
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
type Cont = { quads: Quad[]; id: Term }
|
|
83
|
-
async function jsonldToQuads(value: unknown): Promise<Cont> {
|
|
84
|
-
const qs = (
|
|
85
|
-
await cbdToQuads({
|
|
86
|
-
'@id': 'http://example.com/ns#me1234',
|
|
87
|
-
'http://example.com/ns#innerPred': value,
|
|
88
|
-
})
|
|
89
|
-
).value
|
|
90
|
-
|
|
91
|
-
const idx = qs.findIndex(
|
|
92
|
-
(x) =>
|
|
93
|
-
x.predicate.equals(new NamedNode('http://example.com/ns#innerPred')) &&
|
|
94
|
-
x.subject.equals(new NamedNode('http://example.com/ns#me1234')),
|
|
95
|
-
)
|
|
96
|
-
if (idx < 0) throw 'This cannot happen'
|
|
97
|
-
const id = qs[idx].object
|
|
98
|
-
|
|
99
|
-
qs.splice(idx, 1)
|
|
100
|
-
|
|
101
|
-
return {
|
|
102
|
-
quads: qs,
|
|
103
|
-
id,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
108
|
-
function revive(
|
|
109
|
-
_key: string,
|
|
110
|
-
value: any,
|
|
111
|
-
logger: Logger,
|
|
112
|
-
promises: Promise<unknown>[],
|
|
113
|
-
runnerItems: RunnerItems,
|
|
114
|
-
): any {
|
|
115
|
-
if (typeof value === 'object') {
|
|
116
|
-
const types = as_string_array(value['@type'] || [])
|
|
117
|
-
const ids = as_string_array(value['@id'] || [])[0] || ''
|
|
118
|
-
|
|
119
|
-
if (types.includes(RDFC.Reader)) {
|
|
120
|
-
if (runnerItems.readers[ids] === undefined) {
|
|
121
|
-
runnerItems.readers[ids] = []
|
|
122
|
-
}
|
|
123
|
-
const reader = new ReaderInstance(ids, runnerItems.client, logger)
|
|
124
|
-
runnerItems.readers[ids].push(reader)
|
|
125
|
-
return reader
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
if (types.includes(RDFC.Writer)) {
|
|
129
|
-
if (runnerItems.writers[ids] === undefined) {
|
|
130
|
-
runnerItems.writers[ids] = []
|
|
131
|
-
}
|
|
132
|
-
const writer = new WriterInstance(
|
|
133
|
-
ids,
|
|
134
|
-
runnerItems.client,
|
|
135
|
-
runnerItems.write,
|
|
136
|
-
logger,
|
|
137
|
-
)
|
|
138
|
-
runnerItems.writers[ids].push(writer)
|
|
139
|
-
return writer
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (types.includes(RDFL.CBD)) {
|
|
143
|
-
return cbdToQuads(value)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
if (types.includes(RDFL.Path)) {
|
|
147
|
-
return cbdToPath(value)
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
Object.entries(value).forEach(([k, v]) => {
|
|
152
|
-
if (v instanceof Promise) {
|
|
153
|
-
logger.info('Found promise')
|
|
154
|
-
promises.push(
|
|
155
|
-
v.then((x) => {
|
|
156
|
-
logger.info('Setting field ' + k)
|
|
157
|
-
value[k] = x.value
|
|
158
|
-
}),
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
return value
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
type Arg = {
|
|
167
|
-
[id: string]: any
|
|
168
|
-
}
|
|
169
|
-
function expandArgs(args: Arg, cont: Cont, logger: Logger) {
|
|
170
|
-
const context = args['@context']
|
|
171
|
-
if (!context) return
|
|
172
|
-
|
|
173
|
-
for (const key of Object.keys(args)) {
|
|
174
|
-
if (key == '@context') continue
|
|
175
|
-
const ctxObj = context[key]
|
|
176
|
-
if (!ctxObj) {
|
|
177
|
-
logger.debug("Didn't find ctx things for " + key)
|
|
178
|
-
continue
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
logger.debug(
|
|
182
|
-
'Did find ctx things for ' +
|
|
183
|
-
key +
|
|
184
|
-
' ' +
|
|
185
|
-
JSON.stringify(ctxObj) +
|
|
186
|
-
': ' +
|
|
187
|
-
JSON.stringify(
|
|
188
|
-
Object.entries(args[key]).map(([k, v]) => {
|
|
189
|
-
let x = 'object'
|
|
190
|
-
try {
|
|
191
|
-
x = JSON.stringify(v)
|
|
192
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
193
|
-
} catch (_e: unknown) {
|
|
194
|
-
// default is set
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return k + ': ' + x
|
|
198
|
-
}),
|
|
199
|
-
),
|
|
200
|
-
)
|
|
201
|
-
|
|
202
|
-
expandArgs(args[key], cont, logger)
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
export async function parse_jsonld(
|
|
207
|
-
args: string,
|
|
208
|
-
logger: Logger,
|
|
209
|
-
items: RunnerItems,
|
|
210
|
-
): Promise<unknown> {
|
|
211
|
-
const promises: Promise<unknown>[] = []
|
|
212
|
-
logger.info(args)
|
|
213
|
-
const cont = await jsonldToQuads(JSON.parse(args))
|
|
214
|
-
const out = JSON.parse(args, (k, v) => revive(k, v, logger, promises, items))
|
|
215
|
-
|
|
216
|
-
await Promise.all(promises)
|
|
217
|
-
|
|
218
|
-
expandArgs(out, cont, logger)
|
|
219
|
-
return out
|
|
220
|
-
}
|
package/src/testUtils.ts
DELETED
|
@@ -1,196 +0,0 @@
|
|
|
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
|
-
}
|