@rdfc/js-runner 2.0.0 → 3.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.
- package/.editorconfig +9 -0
- package/.github/renovate.json +3 -0
- package/README.md +127 -3
- package/__tests__/channels.test.ts +131 -74
- package/__tests__/echoProcessor.test.ts +131 -0
- package/__tests__/testProcessor.test.ts +69 -0
- package/eslint.config.mjs +1 -1
- package/examples/echo/.idea/echo.iml +9 -0
- package/examples/echo/.idea/misc.xml +6 -0
- package/{.idea → examples/echo/.idea}/modules.xml +1 -1
- package/examples/echo/.idea/vcs.xml +7 -0
- package/examples/echo/.swls/config.json +1 -0
- package/examples/echo/index.ttl +3 -0
- package/examples/echo/minimal.ttl +90 -0
- package/examples/echo/shacl.ttl +9 -0
- package/examples/echo/shape.ttl +1339 -0
- package/examples/echo/test.ttl +11 -0
- package/examples/echo/untitled:/types/MyType.ttl +0 -0
- package/file:/home/silvius/Projects/mumo-pipeline/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
- package/index.ttl +3 -31
- package/ldes/http_3A_2F_2Fdata.mumo.be_2Fstreams_2Fnodes_2Fdefault/root/index.trig +3 -0
- 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 +10 -5
- 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/minimal.ttl +99 -0
- package/package.json +3 -3
- package/src/client.ts +8 -11
- package/src/logger.ts +3 -3
- package/src/reader.ts +207 -29
- package/src/runner.ts +128 -65
- package/src/testUtils/duplex.ts +112 -0
- package/src/testUtils/index.ts +430 -0
- package/src/writer.ts +106 -16
- package/.idea/LNKD.tech Editor.xml +0 -194
- package/.idea/codeStyles/Project.xml +0 -52
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/js-runner.iml +0 -12
- package/.idea/vcs.xml +0 -6
- package/dist/args.d.ts +0 -4
- package/dist/args.js +0 -58
- package/dist/connectors/file.d.ts +0 -15
- package/dist/connectors/file.js +0 -89
- package/dist/connectors/http.d.ts +0 -14
- package/dist/connectors/http.js +0 -82
- package/dist/connectors/kafka.d.ts +0 -48
- package/dist/connectors/kafka.js +0 -68
- package/dist/connectors/ws.d.ts +0 -10
- package/dist/connectors/ws.js +0 -72
- package/dist/connectors.d.ts +0 -73
- package/dist/connectors.js +0 -168
- package/dist/index.cjs +0 -732
- package/dist/index.d.ts +0 -42
- package/dist/index.js +0 -83
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/util.d.ts +0 -71
- package/dist/util.js +0 -92
- package/src/jsonld.ts +0 -220
- package/src/testUtils.ts +0 -196
package/lib/writer.js
CHANGED
|
@@ -2,14 +2,23 @@ import { promisify } from 'util';
|
|
|
2
2
|
const encoder = new TextEncoder();
|
|
3
3
|
export class WriterInstance {
|
|
4
4
|
uri;
|
|
5
|
+
localSequenceNumber = 1;
|
|
5
6
|
client;
|
|
6
|
-
|
|
7
|
+
notifyOrchestrator;
|
|
7
8
|
logger;
|
|
8
|
-
|
|
9
|
+
awaitingProcessed = [];
|
|
10
|
+
openStreams = 0;
|
|
11
|
+
shouldClose = [];
|
|
12
|
+
runnerId;
|
|
13
|
+
constructor(uri, client, notifyOrchestrator, runnerId, logger) {
|
|
9
14
|
this.client = client;
|
|
10
|
-
this.
|
|
15
|
+
this.notifyOrchestrator = notifyOrchestrator;
|
|
11
16
|
this.uri = uri;
|
|
12
17
|
this.logger = logger;
|
|
18
|
+
this.runnerId = runnerId;
|
|
19
|
+
}
|
|
20
|
+
awaitProcessed() {
|
|
21
|
+
return new Promise((res) => this.awaitingProcessed.push(res));
|
|
13
22
|
}
|
|
14
23
|
async any(any) {
|
|
15
24
|
if ('stream' in any) {
|
|
@@ -24,34 +33,78 @@ export class WriterInstance {
|
|
|
24
33
|
}
|
|
25
34
|
async buffer(buffer) {
|
|
26
35
|
this.logger.debug(`${this.uri} sends buffer ${buffer.length} bytes`);
|
|
27
|
-
|
|
36
|
+
const localSequenceNumber = this.localSequenceNumber++;
|
|
37
|
+
const handledPromise = this.awaitProcessed();
|
|
38
|
+
await this.notifyOrchestrator({
|
|
39
|
+
msg: { data: buffer, channel: this.uri, localSequenceNumber },
|
|
40
|
+
});
|
|
41
|
+
await handledPromise;
|
|
28
42
|
}
|
|
29
43
|
async stream(buffer, transform) {
|
|
44
|
+
this.openStreams += 1;
|
|
30
45
|
const t = transform || ((x) => x);
|
|
31
46
|
const stream = this.client.sendStreamMessage();
|
|
47
|
+
const handledPromise = this.awaitProcessed();
|
|
48
|
+
const writeStreamMessageChunk = promisify(stream.write.bind(stream));
|
|
49
|
+
const localSequenceNumber = this.localSequenceNumber++;
|
|
50
|
+
await writeStreamMessageChunk({
|
|
51
|
+
id: {
|
|
52
|
+
channel: this.uri,
|
|
53
|
+
localSequenceNumber,
|
|
54
|
+
runner: this.runnerId,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
32
57
|
const id = await new Promise((res) => stream.once('data', res));
|
|
33
|
-
this.logger.debug(`${this.uri} streams message with id ${
|
|
34
|
-
await this.write({ streamMsg: { id, channel: this.uri } });
|
|
35
|
-
const write = promisify(stream.write.bind(stream));
|
|
58
|
+
this.logger.debug(`${this.uri} streams message with id ${JSON.stringify(id)}`);
|
|
36
59
|
for await (const msg of buffer) {
|
|
37
|
-
|
|
60
|
+
const processedPromise = new Promise((res) => stream.once('data', res));
|
|
61
|
+
await writeStreamMessageChunk({ data: { data: t(msg) } });
|
|
62
|
+
await processedPromise;
|
|
38
63
|
}
|
|
39
|
-
this.logger.debug(`${this.uri} is done streaming message with id ${id.id}`);
|
|
40
64
|
stream.end();
|
|
65
|
+
await handledPromise;
|
|
66
|
+
this.openStreams -= 1;
|
|
67
|
+
if (this.shouldClose.length > 0)
|
|
68
|
+
await this.close();
|
|
41
69
|
}
|
|
42
70
|
async string(msg) {
|
|
43
71
|
this.logger.debug(`${this.uri} sends string ${msg.length} characters`);
|
|
44
|
-
|
|
45
|
-
|
|
72
|
+
const localSequenceNumber = this.localSequenceNumber++;
|
|
73
|
+
const handledPromise = this.awaitProcessed();
|
|
74
|
+
await this.notifyOrchestrator({
|
|
75
|
+
msg: {
|
|
76
|
+
data: encoder.encode(msg),
|
|
77
|
+
channel: this.uri,
|
|
78
|
+
localSequenceNumber,
|
|
79
|
+
},
|
|
46
80
|
});
|
|
81
|
+
await handledPromise;
|
|
47
82
|
}
|
|
48
83
|
async close(issued = false) {
|
|
84
|
+
if (this.openStreams !== 0) {
|
|
85
|
+
await new Promise((resolve) => this.shouldClose.push(resolve));
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
49
88
|
this.logger.debug(`${this.uri} closes stream`);
|
|
50
89
|
if (!issued) {
|
|
51
|
-
await this.
|
|
90
|
+
await this.notifyOrchestrator({
|
|
52
91
|
close: { channel: this.uri },
|
|
53
92
|
});
|
|
54
93
|
}
|
|
94
|
+
let resolve = this.shouldClose.pop();
|
|
95
|
+
while (resolve) {
|
|
96
|
+
resolve();
|
|
97
|
+
resolve = this.shouldClose.pop();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
handled() {
|
|
101
|
+
if (this.awaitingProcessed.length > 0) {
|
|
102
|
+
this.awaitingProcessed.shift()();
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
this.logger.error('Expected to be waiting for a message to be processed, but this is not the case ' +
|
|
106
|
+
this.uri);
|
|
107
|
+
}
|
|
55
108
|
}
|
|
56
109
|
}
|
|
57
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dyaXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sTUFBTSxDQUFBO0FBbUJoQyxNQUFNLE9BQU8sR0FBRyxJQUFJLFdBQVcsRUFBRSxDQUFBO0FBQ2pDLE1BQU0sT0FBTyxjQUFjO0lBQ2hCLEdBQUcsQ0FBUTtJQUNILE1BQU0sQ0FBYztJQUNwQixLQUFLLENBQVU7SUFDZixNQUFNLENBQVE7SUFFL0IsWUFDRSxHQUFXLEVBQ1gsTUFBb0IsRUFDcEIsS0FBZSxFQUNmLE1BQWM7UUFFZCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtRQUNwQixJQUFJLENBQUMsS0FBSyxHQUFHLEtBQUssQ0FBQTtRQUNsQixJQUFJLENBQUMsR0FBRyxHQUFHLEdBQUcsQ0FBQTtRQUNkLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFBO0lBQ3RCLENBQUM7SUFDRCxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQVE7UUFDaEIsSUFBSSxRQUFRLElBQUksR0FBRyxFQUFFLENBQUM7WUFDcEIsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUMvQixDQUFDO1FBQ0QsSUFBSSxRQUFRLElBQUksR0FBRyxFQUFFLENBQUM7WUFDcEIsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUMvQixDQUFDO1FBQ0QsSUFBSSxRQUFRLElBQUksR0FBRyxFQUFFLENBQUM7WUFDcEIsTUFBTSxJQUFJLENBQUMsTUFBTSxDQUFDLEdBQUcsQ0FBQyxNQUFNLENBQUMsQ0FBQTtRQUMvQixDQUFDO0lBQ0gsQ0FBQztJQUVELEtBQUssQ0FBQyxNQUFNLENBQUMsTUFBa0I7UUFDN0IsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxpQkFBaUIsTUFBTSxDQUFDLE1BQU0sUUFBUSxDQUFDLENBQUE7UUFDcEUsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsR0FBRyxFQUFFLEVBQUUsSUFBSSxFQUFFLE1BQU0sRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQTtJQUNoRSxDQUFDO0lBRUQsS0FBSyxDQUFDLE1BQU0sQ0FDVixNQUF3QixFQUN4QixTQUFnQztRQUVoQyxNQUFNLENBQUMsR0FBRyxTQUFTLElBQUksQ0FBQyxDQUFDLENBQVUsRUFBRSxFQUFFLENBQWEsQ0FBQyxDQUFDLENBQUE7UUFDdEQsTUFBTSxNQUFNLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQyxpQkFBaUIsRUFBRSxDQUFBO1FBQzlDLE1BQU0sRUFBRSxHQUFPLE1BQU0sSUFBSSxPQUFPLENBQUMsQ0FBQyxHQUFHLEVBQUUsRUFBRSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsQ0FBQyxDQUFDLENBQUE7UUFDbkUsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyw0QkFBNEIsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUE7UUFDakUsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDLEVBQUUsU0FBUyxFQUFFLEVBQUUsRUFBRSxFQUFFLE9BQU8sRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFBO1FBRTFELE1BQU0sS0FBSyxHQUFHLFNBQVMsQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQyxDQUFBO1FBQ2xELElBQUksS0FBSyxFQUFFLE1BQU0sR0FBRyxJQUFJLE1BQU0sRUFBRSxDQUFDO1lBQy9CLE1BQU0sS0FBSyxDQUFDLEVBQUUsSUFBSSxFQUFFLENBQUMsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDLENBQUE7UUFDL0IsQ0FBQztRQUVELElBQUksQ0FBQyxNQUFNLENBQUMsS0FBSyxDQUFDLEdBQUcsSUFBSSxDQUFDLEdBQUcsc0NBQXNDLEVBQUUsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFBO1FBQzNFLE1BQU0sQ0FBQyxHQUFHLEVBQUUsQ0FBQTtJQUNkLENBQUM7SUFFRCxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQVc7UUFDdEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxpQkFBaUIsR0FBRyxDQUFDLE1BQU0sYUFBYSxDQUFDLENBQUE7UUFDdEUsTUFBTSxJQUFJLENBQUMsS0FBSyxDQUFDO1lBQ2YsR0FBRyxFQUFFLEVBQUUsSUFBSSxFQUFFLE9BQU8sQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLEVBQUUsT0FBTyxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7U0FDdEQsQ0FBQyxDQUFBO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxLQUFLLENBQUMsTUFBTSxHQUFHLEtBQUs7UUFDeEIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxJQUFJLENBQUMsR0FBRyxnQkFBZ0IsQ0FBQyxDQUFBO1FBQzlDLElBQUksQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUNaLE1BQU0sSUFBSSxDQUFDLEtBQUssQ0FBQztnQkFDZixLQUFLLEVBQUUsRUFBRSxPQUFPLEVBQUUsSUFBSSxDQUFDLEdBQUcsRUFBRTthQUM3QixDQUFDLENBQUE7UUFDSixDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgSWQsIE9yY2hlc3RyYXRvck1lc3NhZ2UsIFJ1bm5lckNsaWVudCB9IGZyb20gJ0ByZGZjL3Byb3RvJ1xuaW1wb3J0IHsgcHJvbWlzaWZ5IH0gZnJvbSAndXRpbCdcbmltcG9ydCB7IExvZ2dlciB9IGZyb20gJ3dpbnN0b24nXG5pbXBvcnQgeyBBbnkgfSBmcm9tICcuL3JlYWRlcidcblxudHlwZSBXcml0YWJsZSA9IChtc2c6IE9yY2hlc3RyYXRvck1lc3NhZ2UpID0+IFByb21pc2U8dW5rbm93bj5cbmV4cG9ydCBpbnRlcmZhY2UgV3JpdGVyIHtcbiAgcmVhZG9ubHkgdXJpOiBzdHJpbmdcbiAgYnVmZmVyKGJ1ZmZlcjogVWludDhBcnJheSk6IFByb21pc2U8dm9pZD5cblxuICBzdHJlYW0oYnVmZmVyOiBBc3luY0l0ZXJhYmxlPFVpbnQ4QXJyYXk+KTogUHJvbWlzZTx2b2lkPlxuICBzdHJlYW08VD4oXG4gICAgYnVmZmVyOiBBc3luY0l0ZXJhYmxlPFQ+LFxuICAgIHRyYW5mb3JtOiAoeDogVCkgPT4gVWludDhBcnJheSxcbiAgKTogUHJvbWlzZTx2b2lkPlxuXG4gIHN0cmluZyhidWZmZXI6IHN0cmluZyk6IFByb21pc2U8dm9pZD5cbiAgYW55KGFueTogQW55KTogUHJvbWlzZTx2b2lkPlxuICBjbG9zZSgpOiBQcm9taXNlPHZvaWQ+XG59XG5jb25zdCBlbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKClcbmV4cG9ydCBjbGFzcyBXcml0ZXJJbnN0YW5jZSBpbXBsZW1lbnRzIFdyaXRlciB7XG4gIHJlYWRvbmx5IHVyaTogc3RyaW5nXG4gIHByaXZhdGUgcmVhZG9ubHkgY2xpZW50OiBSdW5uZXJDbGllbnRcbiAgcHJpdmF0ZSByZWFkb25seSB3cml0ZTogV3JpdGFibGVcbiAgcHJpdmF0ZSByZWFkb25seSBsb2dnZXI6IExvZ2dlclxuXG4gIGNvbnN0cnVjdG9yKFxuICAgIHVyaTogc3RyaW5nLFxuICAgIGNsaWVudDogUnVubmVyQ2xpZW50LFxuICAgIHdyaXRlOiBXcml0YWJsZSxcbiAgICBsb2dnZXI6IExvZ2dlcixcbiAgKSB7XG4gICAgdGhpcy5jbGllbnQgPSBjbGllbnRcbiAgICB0aGlzLndyaXRlID0gd3JpdGVcbiAgICB0aGlzLnVyaSA9IHVyaVxuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyXG4gIH1cbiAgYXN5bmMgYW55KGFueTogQW55KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgaWYgKCdzdHJlYW0nIGluIGFueSkge1xuICAgICAgYXdhaXQgdGhpcy5zdHJlYW0oYW55LnN0cmVhbSlcbiAgICB9XG4gICAgaWYgKCdidWZmZXInIGluIGFueSkge1xuICAgICAgYXdhaXQgdGhpcy5idWZmZXIoYW55LmJ1ZmZlcilcbiAgICB9XG4gICAgaWYgKCdzdHJpbmcnIGluIGFueSkge1xuICAgICAgYXdhaXQgdGhpcy5zdHJpbmcoYW55LnN0cmluZylcbiAgICB9XG4gIH1cblxuICBhc3luYyBidWZmZXIoYnVmZmVyOiBVaW50OEFycmF5KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5sb2dnZXIuZGVidWcoYCR7dGhpcy51cml9IHNlbmRzIGJ1ZmZlciAke2J1ZmZlci5sZW5ndGh9IGJ5dGVzYClcbiAgICBhd2FpdCB0aGlzLndyaXRlKHsgbXNnOiB7IGRhdGE6IGJ1ZmZlciwgY2hhbm5lbDogdGhpcy51cmkgfSB9KVxuICB9XG5cbiAgYXN5bmMgc3RyZWFtPFQgPSBVaW50OEFycmF5PihcbiAgICBidWZmZXI6IEFzeW5jSXRlcmFibGU8VD4sXG4gICAgdHJhbnNmb3JtPzogKHg6IFQpID0+IFVpbnQ4QXJyYXksXG4gICkge1xuICAgIGNvbnN0IHQgPSB0cmFuc2Zvcm0gfHwgKCh4OiB1bmtub3duKSA9PiA8VWludDhBcnJheT54KVxuICAgIGNvbnN0IHN0cmVhbSA9IHRoaXMuY2xpZW50LnNlbmRTdHJlYW1NZXNzYWdlKClcbiAgICBjb25zdCBpZDogSWQgPSBhd2FpdCBuZXcgUHJvbWlzZSgocmVzKSA9PiBzdHJlYW0ub25jZSgnZGF0YScsIHJlcykpXG4gICAgdGhpcy5sb2dnZXIuZGVidWcoYCR7dGhpcy51cml9IHN0cmVhbXMgbWVzc2FnZSB3aXRoIGlkICR7aWQuaWR9YClcbiAgICBhd2FpdCB0aGlzLndyaXRlKHsgc3RyZWFtTXNnOiB7IGlkLCBjaGFubmVsOiB0aGlzLnVyaSB9IH0pXG5cbiAgICBjb25zdCB3cml0ZSA9IHByb21pc2lmeShzdHJlYW0ud3JpdGUuYmluZChzdHJlYW0pKVxuICAgIGZvciBhd2FpdCAoY29uc3QgbXNnIG9mIGJ1ZmZlcikge1xuICAgICAgYXdhaXQgd3JpdGUoeyBkYXRhOiB0KG1zZykgfSlcbiAgICB9XG5cbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0aGlzLnVyaX0gaXMgZG9uZSBzdHJlYW1pbmcgbWVzc2FnZSB3aXRoIGlkICR7aWQuaWR9YClcbiAgICBzdHJlYW0uZW5kKClcbiAgfVxuXG4gIGFzeW5jIHN0cmluZyhtc2c6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3RoaXMudXJpfSBzZW5kcyBzdHJpbmcgJHttc2cubGVuZ3RofSBjaGFyYWN0ZXJzYClcbiAgICBhd2FpdCB0aGlzLndyaXRlKHtcbiAgICAgIG1zZzogeyBkYXRhOiBlbmNvZGVyLmVuY29kZShtc2cpLCBjaGFubmVsOiB0aGlzLnVyaSB9LFxuICAgIH0pXG4gIH1cblxuICBhc3luYyBjbG9zZShpc3N1ZWQgPSBmYWxzZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3RoaXMudXJpfSBjbG9zZXMgc3RyZWFtYClcbiAgICBpZiAoIWlzc3VlZCkge1xuICAgICAgYXdhaXQgdGhpcy53cml0ZSh7XG4gICAgICAgIGNsb3NlOiB7IGNoYW5uZWw6IHRoaXMudXJpIH0sXG4gICAgICB9KVxuICAgIH1cbiAgfVxufVxuIl19
|
|
110
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid3JpdGVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3dyaXRlci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFDQSxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0sTUFBTSxDQUFBO0FBbUJoQyxNQUFNLE9BQU8sR0FBRyxJQUFJLFdBQVcsRUFBRSxDQUFBO0FBQ2pDLE1BQU0sT0FBTyxjQUFjO0lBQ2hCLEdBQUcsQ0FBUTtJQUNwQixtQkFBbUIsR0FBVyxDQUFDLENBQUE7SUFDZCxNQUFNLENBQWM7SUFDcEIsa0JBQWtCLENBQVU7SUFDNUIsTUFBTSxDQUFRO0lBRXZCLGlCQUFpQixHQUFzQixFQUFFLENBQUE7SUFFekMsV0FBVyxHQUFXLENBQUMsQ0FBQTtJQUN2QixXQUFXLEdBQXNCLEVBQUUsQ0FBQTtJQUUxQixRQUFRLENBQVE7SUFFakMsWUFDRSxHQUFXLEVBQ1gsTUFBb0IsRUFDcEIsa0JBQTRCLEVBQzVCLFFBQWdCLEVBQ2hCLE1BQWM7UUFFZCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtRQUNwQixJQUFJLENBQUMsa0JBQWtCLEdBQUcsa0JBQWtCLENBQUE7UUFDNUMsSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLENBQUE7UUFDZCxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQTtRQUNwQixJQUFJLENBQUMsUUFBUSxHQUFHLFFBQVEsQ0FBQTtJQUMxQixDQUFDO0lBRU8sY0FBYztRQUNwQixPQUFPLElBQUksT0FBTyxDQUFDLENBQUMsR0FBRyxFQUFFLEVBQUUsQ0FBQyxJQUFJLENBQUMsaUJBQWlCLENBQUMsSUFBSSxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUE7SUFDL0QsQ0FBQztJQUVELEtBQUssQ0FBQyxHQUFHLENBQUMsR0FBUTtRQUNoQixJQUFJLFFBQVEsSUFBSSxHQUFHLEVBQUUsQ0FBQztZQUNwQixNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsR0FBRyxDQUFDLE1BQU0sQ0FBQyxDQUFBO1FB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R0FBRyxFQUFFLENBQUE7UUFDcEMsT0FBTyxPQUFPLEVBQUUsQ0FBQztZQUNmLE9BQU8sRUFBRSxDQUFBO1lBQ1QsT0FBTyxHQUFHLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxFQUFFLENBQUE7UUFDbEMsQ0FBQztJQUNILENBQUM7SUFLRCxPQUFPO1FBQ0wsSUFBSSxJQUFJLENBQUMsaUJBQWlCLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1lBQ3RDLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxLQUFLLEVBQUcsRUFBRSxDQUFBO1FBQ25DLENBQUM7YUFBTSxDQUFDO1lBQ04sSUFBSSxDQUFDLE1BQU0sQ0FBQyxLQUFLLENBQ2YsaUZBQWlGO2dCQUMvRSxJQUFJLENBQUMsR0FBRyxDQUNYLENBQUE7UUFDSCxDQUFDO0lBQ0gsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRnJvbVJ1bm5lciwgUnVubmVyQ2xpZW50IH0gZnJvbSAnQHJkZmMvcHJvdG8nXG5pbXBvcnQgeyBwcm9taXNpZnkgfSBmcm9tICd1dGlsJ1xuaW1wb3J0IHsgTG9nZ2VyIH0gZnJvbSAnd2luc3RvbidcbmltcG9ydCB7IEFueSB9IGZyb20gJy4vcmVhZGVyJ1xuXG50eXBlIFdyaXRhYmxlID0gKG1zZzogRnJvbVJ1bm5lcikgPT4gUHJvbWlzZTx1bmtub3duPlxuZXhwb3J0IGludGVyZmFjZSBXcml0ZXIge1xuICByZWFkb25seSB1cmk6IHN0cmluZ1xuICBidWZmZXIoYnVmZmVyOiBVaW50OEFycmF5KTogUHJvbWlzZTx2b2lkPlxuXG4gIHN0cmVhbShidWZmZXI6IEFzeW5jSXRlcmFibGU8VWludDhBcnJheT4pOiBQcm9taXNlPHZvaWQ+XG4gIHN0cmVhbTxUPihcbiAgICBidWZmZXI6IEFzeW5jSXRlcmFibGU8VD4sXG4gICAgdHJhbnNmb3JtOiAoeDogVCkgPT4gVWludDhBcnJheSxcbiAgKTogUHJvbWlzZTx2b2lkPlxuXG4gIHN0cmluZyhidWZmZXI6IHN0cmluZyk6IFByb21pc2U8dm9pZD5cbiAgYW55KGFueTogQW55KTogUHJvbWlzZTx2b2lkPlxuICBjbG9zZSgpOiBQcm9taXNlPHZvaWQ+XG59XG5jb25zdCBlbmNvZGVyID0gbmV3IFRleHRFbmNvZGVyKClcbmV4cG9ydCBjbGFzcyBXcml0ZXJJbnN0YW5jZSBpbXBsZW1lbnRzIFdyaXRlciB7XG4gIHJlYWRvbmx5IHVyaTogc3RyaW5nXG4gIGxvY2FsU2VxdWVuY2VOdW1iZXI6IG51bWJlciA9IDFcbiAgcHJpdmF0ZSByZWFkb25seSBjbGllbnQ6IFJ1bm5lckNsaWVudFxuICBwcml2YXRlIHJlYWRvbmx5IG5vdGlmeU9yY2hlc3RyYXRvcjogV3JpdGFibGVcbiAgcHJpdmF0ZSByZWFkb25seSBsb2dnZXI6IExvZ2dlclxuXG4gIHByaXZhdGUgYXdhaXRpbmdQcm9jZXNzZWQ6IEFycmF5PCgpID0+IHZvaWQ+ID0gW11cblxuICBwcml2YXRlIG9wZW5TdHJlYW1zOiBudW1iZXIgPSAwXG4gIHByaXZhdGUgc2hvdWxkQ2xvc2U6IEFycmF5PCgpID0+IHZvaWQ+ID0gW11cblxuICBwcml2YXRlIHJlYWRvbmx5IHJ1bm5lcklkOiBzdHJpbmdcblxuICBjb25zdHJ1Y3RvcihcbiAgICB1cmk6IHN0cmluZyxcbiAgICBjbGllbnQ6IFJ1bm5lckNsaWVudCxcbiAgICBub3RpZnlPcmNoZXN0cmF0b3I6IFdyaXRhYmxlLFxuICAgIHJ1bm5lcklkOiBzdHJpbmcsXG4gICAgbG9nZ2VyOiBMb2dnZXIsXG4gICkge1xuICAgIHRoaXMuY2xpZW50ID0gY2xpZW50XG4gICAgdGhpcy5ub3RpZnlPcmNoZXN0cmF0b3IgPSBub3RpZnlPcmNoZXN0cmF0b3JcbiAgICB0aGlzLnVyaSA9IHVyaVxuICAgIHRoaXMubG9nZ2VyID0gbG9nZ2VyXG4gICAgdGhpcy5ydW5uZXJJZCA9IHJ1bm5lcklkXG4gIH1cblxuICBwcml2YXRlIGF3YWl0UHJvY2Vzc2VkKCk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzKSA9PiB0aGlzLmF3YWl0aW5nUHJvY2Vzc2VkLnB1c2gocmVzKSlcbiAgfVxuXG4gIGFzeW5jIGFueShhbnk6IEFueSk6IFByb21pc2U8dm9pZD4ge1xuICAgIGlmICgnc3RyZWFtJyBpbiBhbnkpIHtcbiAgICAgIGF3YWl0IHRoaXMuc3RyZWFtKGFueS5zdHJlYW0pXG4gICAgfVxuICAgIGlmICgnYnVmZmVyJyBpbiBhbnkpIHtcbiAgICAgIGF3YWl0IHRoaXMuYnVmZmVyKGFueS5idWZmZXIpXG4gICAgfVxuICAgIGlmICgnc3RyaW5nJyBpbiBhbnkpIHtcbiAgICAgIGF3YWl0IHRoaXMuc3RyaW5nKGFueS5zdHJpbmcpXG4gICAgfVxuICB9XG5cbiAgYXN5bmMgYnVmZmVyKGJ1ZmZlcjogVWludDhBcnJheSk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3RoaXMudXJpfSBzZW5kcyBidWZmZXIgJHtidWZmZXIubGVuZ3RofSBieXRlc2ApXG4gICAgY29uc3QgbG9jYWxTZXF1ZW5jZU51bWJlciA9IHRoaXMubG9jYWxTZXF1ZW5jZU51bWJlcisrXG4gICAgY29uc3QgaGFuZGxlZFByb21pc2UgPSB0aGlzLmF3YWl0UHJvY2Vzc2VkKClcblxuICAgIGF3YWl0IHRoaXMubm90aWZ5T3JjaGVzdHJhdG9yKHtcbiAgICAgIG1zZzogeyBkYXRhOiBidWZmZXIsIGNoYW5uZWw6IHRoaXMudXJpLCBsb2NhbFNlcXVlbmNlTnVtYmVyIH0sXG4gICAgfSlcbiAgICBhd2FpdCBoYW5kbGVkUHJvbWlzZVxuICB9XG5cbiAgYXN5bmMgc3RyZWFtPFQgPSBVaW50OEFycmF5PihcbiAgICBidWZmZXI6IEFzeW5jSXRlcmFibGU8VD4sXG4gICAgdHJhbnNmb3JtPzogKHg6IFQpID0+IFVpbnQ4QXJyYXksXG4gICkge1xuICAgIHRoaXMub3BlblN0cmVhbXMgKz0gMVxuICAgIGNvbnN0IHQgPSB0cmFuc2Zvcm0gfHwgKCh4OiB1bmtub3duKSA9PiA8VWludDhBcnJheT54KVxuICAgIGNvbnN0IHN0cmVhbSA9IHRoaXMuY2xpZW50LnNlbmRTdHJlYW1NZXNzYWdlKClcblxuICAgIGNvbnN0IGhhbmRsZWRQcm9taXNlID0gdGhpcy5hd2FpdFByb2Nlc3NlZCgpXG4gICAgY29uc3Qgd3JpdGVTdHJlYW1NZXNzYWdlQ2h1bmsgPSBwcm9taXNpZnkoc3RyZWFtLndyaXRlLmJpbmQoc3RyZWFtKSlcbiAgICBjb25zdCBsb2NhbFNlcXVlbmNlTnVtYmVyID0gdGhpcy5sb2NhbFNlcXVlbmNlTnVtYmVyKytcbiAgICBhd2FpdCB3cml0ZVN0cmVhbU1lc3NhZ2VDaHVuayh7XG4gICAgICBpZDoge1xuICAgICAgICBjaGFubmVsOiB0aGlzLnVyaSxcbiAgICAgICAgbG9jYWxTZXF1ZW5jZU51bWJlcixcbiAgICAgICAgcnVubmVyOiB0aGlzLnJ1bm5lcklkLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgY29uc3QgaWQgPSBhd2FpdCBuZXcgUHJvbWlzZSgocmVzKSA9PiBzdHJlYW0ub25jZSgnZGF0YScsIHJlcykpXG5cbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhcbiAgICAgIGAke3RoaXMudXJpfSBzdHJlYW1zIG1lc3NhZ2Ugd2l0aCBpZCAke0pTT04uc3RyaW5naWZ5KGlkKX1gLFxuICAgIClcblxuICAgIGZvciBhd2FpdCAoY29uc3QgbXNnIG9mIGJ1ZmZlcikge1xuICAgICAgY29uc3QgcHJvY2Vzc2VkUHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXMpID0+IHN0cmVhbS5vbmNlKCdkYXRhJywgcmVzKSlcbiAgICAgIGF3YWl0IHdyaXRlU3RyZWFtTWVzc2FnZUNodW5rKHsgZGF0YTogeyBkYXRhOiB0KG1zZykgfSB9KVxuICAgICAgLy8gQXdhaXQgYSBtZXNzYWdlIG9uIHRoZSBzdHJlYW0sIGluZGljYXRpbmcgdGhhdCB0aGUgY2h1bmsgaGFzIGJlZW4gcHJvY2Vzc2VkXG4gICAgICBhd2FpdCBwcm9jZXNzZWRQcm9taXNlXG4gICAgfVxuXG4gICAgc3RyZWFtLmVuZCgpXG5cbiAgICBhd2FpdCBoYW5kbGVkUHJvbWlzZVxuXG4gICAgdGhpcy5vcGVuU3RyZWFtcyAtPSAxXG5cbiAgICBpZiAodGhpcy5zaG91bGRDbG9zZS5sZW5ndGggPiAwKSBhd2FpdCB0aGlzLmNsb3NlKClcbiAgfVxuXG4gIGFzeW5jIHN0cmluZyhtc2c6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMubG9nZ2VyLmRlYnVnKGAke3RoaXMudXJpfSBzZW5kcyBzdHJpbmcgJHttc2cubGVuZ3RofSBjaGFyYWN0ZXJzYClcbiAgICBjb25zdCBsb2NhbFNlcXVlbmNlTnVtYmVyID0gdGhpcy5sb2NhbFNlcXVlbmNlTnVtYmVyKytcbiAgICBjb25zdCBoYW5kbGVkUHJvbWlzZSA9IHRoaXMuYXdhaXRQcm9jZXNzZWQoKVxuXG4gICAgYXdhaXQgdGhpcy5ub3RpZnlPcmNoZXN0cmF0b3Ioe1xuICAgICAgbXNnOiB7XG4gICAgICAgIGRhdGE6IGVuY29kZXIuZW5jb2RlKG1zZyksXG4gICAgICAgIGNoYW5uZWw6IHRoaXMudXJpLFxuICAgICAgICBsb2NhbFNlcXVlbmNlTnVtYmVyLFxuICAgICAgfSxcbiAgICB9KVxuXG4gICAgYXdhaXQgaGFuZGxlZFByb21pc2VcbiAgfVxuXG4gIC8qKlxuICAgKiBHcmFjZWZ1bGx5IGNsb3NlcyB0aGlzIGNoYW5uZWwuXG4gICAqXG4gICAqIEJlaGF2aW9yOlxuICAgKiAtIElmIHRoZXJlIGFyZSBzdGlsbCBhY3RpdmUgc3RyZWFtcywgY2xvc2luZyBpcyBkZWZlcnJlZCB1bnRpbCB0aGV5IGNvbXBsZXRlLlxuICAgKiAtIElmIG11bHRpcGxlIGNhbGxlcnMgaW52b2tlIGBjbG9zZSgpYCB3aGlsZSB3YWl0aW5nLCB0aGVpciBQcm9taXNlcyBhcmUgcXVldWVkIGFuZFxuICAgKiAgIHJlc29sdmVkIG9uY2UgdGhlIGNoYW5uZWwgYWN0dWFsbHkgY2xvc2VzLlxuICAgKiAtIElmIHRoaXMgc2lkZSBpbml0aWF0ZWQgdGhlIGNsb3NlIChgaXNzdWVkID0gZmFsc2VgKSwgYSBjbG9zZSBtZXNzYWdlIGlzIHNlbnQgdG8gdGhlIHJlbW90ZS5cbiAgICpcbiAgICogQHBhcmFtIGlzc3VlZCAtIElmIHRydWUsIGluZGljYXRlcyB0aGUgY2xvc2UgcmVxdWVzdCBvcmlnaW5hdGVkIHJlbW90ZWx5XG4gICAqL1xuICBhc3luYyBjbG9zZShpc3N1ZWQgPSBmYWxzZSk6IFByb21pc2U8dm9pZD4ge1xuICAgIC8vIENhc2UgMTogQWN0aXZlIHN0cmVhbXMgc3RpbGwgcnVubmluZyDihpIgd2FpdCB1bnRpbCB0aGV5IGZpbmlzaFxuICAgIGlmICh0aGlzLm9wZW5TdHJlYW1zICE9PSAwKSB7XG4gICAgICBhd2FpdCBuZXcgUHJvbWlzZTx2b2lkPigocmVzb2x2ZSkgPT4gdGhpcy5zaG91bGRDbG9zZS5wdXNoKHJlc29sdmUpKVxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gQ2FzZSAyOiBObyBhY3RpdmUgc3RyZWFtcyDihpIgcGVyZm9ybSBhY3R1YWwgY2xvc2VcbiAgICB0aGlzLmxvZ2dlci5kZWJ1ZyhgJHt0aGlzLnVyaX0gY2xvc2VzIHN0cmVhbWApXG4gICAgaWYgKCFpc3N1ZWQpIHtcbiAgICAgIGF3YWl0IHRoaXMubm90aWZ5T3JjaGVzdHJhdG9yKHtcbiAgICAgICAgY2xvc2U6IHsgY2hhbm5lbDogdGhpcy51cmkgfSxcbiAgICAgIH0pXG4gICAgfVxuXG4gICAgbGV0IHJlc29sdmUgPSB0aGlzLnNob3VsZENsb3NlLnBvcCgpXG4gICAgd2hpbGUgKHJlc29sdmUpIHtcbiAgICAgIHJlc29sdmUoKVxuICAgICAgcmVzb2x2ZSA9IHRoaXMuc2hvdWxkQ2xvc2UucG9wKClcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQSBtZXNzYWdlIGlzIGhhbmRsZWQsIGxldCdzIG5vdGlmeSB0aGUgZmlmbyB7QGxpbmsgYXdhaXRQcm9jZXNzZWR9XG4gICAqL1xuICBoYW5kbGVkKCk6IHZvaWQge1xuICAgIGlmICh0aGlzLmF3YWl0aW5nUHJvY2Vzc2VkLmxlbmd0aCA+IDApIHtcbiAgICAgIHRoaXMuYXdhaXRpbmdQcm9jZXNzZWQuc2hpZnQoKSEoKVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGlzLmxvZ2dlci5lcnJvcihcbiAgICAgICAgJ0V4cGVjdGVkIHRvIGJlIHdhaXRpbmcgZm9yIGEgbWVzc2FnZSB0byBiZSBwcm9jZXNzZWQsIGJ1dCB0aGlzIGlzIG5vdCB0aGUgY2FzZSAnICtcbiAgICAgICAgICB0aGlzLnVyaSxcbiAgICAgIClcbiAgICB9XG4gIH1cbn1cbiJdfQ==
|
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": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
4
|
"main": "lib/index.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@eslint/js": "^9.21.0",
|
|
33
33
|
"@rdfjs/types": "^2.0.1",
|
|
34
|
+
"@grpc/grpc-js": "^1.12.6",
|
|
34
35
|
"@types/jest": "^29.5.14",
|
|
35
36
|
"@types/jsonld": "^1.5.15",
|
|
36
37
|
"@types/n3": "^1.21.1",
|
|
@@ -53,8 +54,7 @@
|
|
|
53
54
|
"vitest": "^3.2.4"
|
|
54
55
|
},
|
|
55
56
|
"dependencies": {
|
|
56
|
-
"@
|
|
57
|
-
"@rdfc/proto": "^0.1.2-alpha.1",
|
|
57
|
+
"@rdfc/proto": "^0.1.2",
|
|
58
58
|
"@treecg/types": "^0.4.6",
|
|
59
59
|
"jsonld": "^8.3.3",
|
|
60
60
|
"jsonld-streaming-parser": "^5.0.0",
|
package/src/client.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import * as grpc from '@grpc/grpc-js'
|
|
2
2
|
import { promisify } from 'util'
|
|
3
|
-
import { RunnerClient,
|
|
4
|
-
import
|
|
3
|
+
import { RunnerClient, ToRunner } from '@rdfc/proto'
|
|
4
|
+
import { createLogger } from 'winston'
|
|
5
5
|
import { RpcTransport } from './logger'
|
|
6
6
|
import { Runner } from './runner'
|
|
7
7
|
|
|
8
8
|
export async function start(addr: string, uri: string) {
|
|
9
9
|
const client = new RunnerClient(addr, grpc.credentials.createInsecure())
|
|
10
10
|
|
|
11
|
-
const logger =
|
|
11
|
+
const logger = createLogger({
|
|
12
12
|
transports: [
|
|
13
13
|
new RpcTransport({
|
|
14
14
|
entities: [uri, 'cli'],
|
|
@@ -25,25 +25,22 @@ export async function start(addr: string, uri: string) {
|
|
|
25
25
|
|
|
26
26
|
await writable({ identify: { uri } })
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
;(async () => {
|
|
28
|
+
/* eslint-disable no-async-promise-executor */
|
|
29
|
+
await new Promise(async (res) => {
|
|
31
30
|
for await (const chunk of stream) {
|
|
32
|
-
const msg:
|
|
31
|
+
const msg: ToRunner = chunk
|
|
33
32
|
if (msg.proc) {
|
|
34
33
|
await runner.addProcessor(msg.proc)
|
|
35
34
|
}
|
|
36
35
|
if (msg.start) {
|
|
37
|
-
runner.start().then(
|
|
36
|
+
runner.start().then(res)
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
await runner.handleOrchMessage(msg)
|
|
41
40
|
}
|
|
42
41
|
|
|
43
42
|
logger.error('Stream ended')
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
await processorsEnded
|
|
43
|
+
})
|
|
47
44
|
|
|
48
45
|
logger.info('All processors are finished')
|
|
49
46
|
stream.end()
|
package/src/logger.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { createLogger, LogEntry, Logger } from 'winston'
|
|
2
2
|
import Transport from 'winston-transport'
|
|
3
3
|
|
|
4
4
|
import * as grpc from '@grpc/grpc-js'
|
|
@@ -21,7 +21,7 @@ export class RpcTransport extends Transport {
|
|
|
21
21
|
this.aliases = opts.aliases || []
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
log(info:
|
|
24
|
+
log(info: LogEntry, callback: () => void) {
|
|
25
25
|
if (!this.stream.closed) {
|
|
26
26
|
this.stream.write(
|
|
27
27
|
{
|
|
@@ -55,7 +55,7 @@ export function extendLogger(baseLogger: Logger, newEntity: string): Logger {
|
|
|
55
55
|
return t
|
|
56
56
|
})
|
|
57
57
|
|
|
58
|
-
return
|
|
58
|
+
return createLogger({
|
|
59
59
|
level: baseLogger.level,
|
|
60
60
|
format: baseLogger.format,
|
|
61
61
|
defaultMeta: baseLogger.defaultMeta,
|
package/src/reader.ts
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { ClientReadableStream } from '@grpc/grpc-js'
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
DataChunk,
|
|
4
|
+
ReceivingMessage,
|
|
5
|
+
ReceivingStreamMessage,
|
|
6
|
+
RunnerClient,
|
|
7
|
+
} from '@rdfc/proto'
|
|
8
|
+
import { Logger } from 'winston'
|
|
4
9
|
import {
|
|
5
10
|
AnyConvertor,
|
|
6
11
|
Convertor,
|
|
@@ -8,6 +13,8 @@ import {
|
|
|
8
13
|
StreamConvertor,
|
|
9
14
|
StringConvertor,
|
|
10
15
|
} from './convertor'
|
|
16
|
+
import { Writable } from './runner'
|
|
17
|
+
import { promisify } from 'util'
|
|
11
18
|
|
|
12
19
|
export type Any =
|
|
13
20
|
| {
|
|
@@ -28,41 +35,47 @@ export interface Reader {
|
|
|
28
35
|
anys(): AsyncIterable<Any>
|
|
29
36
|
}
|
|
30
37
|
|
|
38
|
+
type Todo<T> = {
|
|
39
|
+
item: T
|
|
40
|
+
onComplete: () => void
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
class MyIter<T> implements AsyncIterable<T> {
|
|
32
44
|
private convertor: Convertor<T>
|
|
33
|
-
private queue:
|
|
45
|
+
private queue: Todo<T | undefined>[] = []
|
|
34
46
|
private resolveNext: ((value: undefined) => void) | null = null
|
|
35
47
|
|
|
36
48
|
constructor(convertor: Convertor<T>) {
|
|
37
49
|
this.convertor = convertor
|
|
38
50
|
}
|
|
39
51
|
|
|
40
|
-
push(buffer: Uint8Array) {
|
|
52
|
+
push(buffer: Uint8Array, onComplete: () => void) {
|
|
41
53
|
const item = this.convertor.from(buffer)
|
|
42
|
-
this.queue.push(item)
|
|
54
|
+
this.queue.push({ item, onComplete })
|
|
43
55
|
if (this.resolveNext) {
|
|
44
56
|
this.resolveNext(undefined)
|
|
45
57
|
this.resolveNext = null
|
|
46
58
|
}
|
|
47
59
|
}
|
|
48
60
|
|
|
49
|
-
close() {
|
|
50
|
-
this.queue.push(undefined)
|
|
61
|
+
close(onComplete: () => void) {
|
|
62
|
+
this.queue.push({ item: undefined, onComplete })
|
|
51
63
|
if (this.resolveNext) {
|
|
52
64
|
this.resolveNext(undefined)
|
|
53
65
|
this.resolveNext = null
|
|
54
66
|
}
|
|
55
67
|
}
|
|
56
68
|
|
|
57
|
-
async pushStream(chunks:
|
|
69
|
+
async pushStream(chunks: AsyncIterable<DataChunk>, onComplete: () => void) {
|
|
70
|
+
// This is an async generator that transforms DataChunks to Buffers
|
|
58
71
|
const stream = (async function* (stream) {
|
|
59
|
-
for await (const
|
|
60
|
-
const chunk: DataChunk = c
|
|
72
|
+
for await (const chunk of stream) {
|
|
61
73
|
yield chunk.data
|
|
62
74
|
}
|
|
63
75
|
})(chunks)
|
|
76
|
+
|
|
64
77
|
const item = await this.convertor.fromStream(stream)
|
|
65
|
-
this.queue.push(item)
|
|
78
|
+
this.queue.push({ item, onComplete })
|
|
66
79
|
if (this.resolveNext) {
|
|
67
80
|
this.resolveNext(undefined)
|
|
68
81
|
this.resolveNext = null
|
|
@@ -72,9 +85,15 @@ class MyIter<T> implements AsyncIterable<T> {
|
|
|
72
85
|
async *[Symbol.asyncIterator]() {
|
|
73
86
|
while (true) {
|
|
74
87
|
if (this.queue.length > 0) {
|
|
75
|
-
const item = this.queue.shift()!
|
|
76
|
-
if (item === undefined)
|
|
88
|
+
const { item, onComplete } = this.queue.shift()!
|
|
89
|
+
if (item === undefined) {
|
|
90
|
+
onComplete()
|
|
91
|
+
break
|
|
92
|
+
}
|
|
77
93
|
yield item
|
|
94
|
+
// Note: execution pauses at `yield` until the consumer calls `.next()` again.
|
|
95
|
+
// We call onComplete *after* resuming, so the producer knows the item was actually consumed.
|
|
96
|
+
onComplete()
|
|
78
97
|
} else {
|
|
79
98
|
await new Promise<undefined>((resolve) => (this.resolveNext = resolve))
|
|
80
99
|
}
|
|
@@ -85,58 +104,217 @@ class MyIter<T> implements AsyncIterable<T> {
|
|
|
85
104
|
export class ReaderInstance implements Reader {
|
|
86
105
|
private client: RunnerClient
|
|
87
106
|
readonly uri: string
|
|
88
|
-
private logger:
|
|
107
|
+
private logger: Logger
|
|
108
|
+
private readonly notifyOrchestrator: Writable
|
|
89
109
|
|
|
90
|
-
private
|
|
110
|
+
private consumers: MyIter<unknown>[] = []
|
|
91
111
|
|
|
92
|
-
constructor(
|
|
112
|
+
constructor(
|
|
113
|
+
uri: string,
|
|
114
|
+
client: RunnerClient,
|
|
115
|
+
notifyOrchestrator: Writable,
|
|
116
|
+
logger: Logger,
|
|
117
|
+
) {
|
|
93
118
|
this.uri = uri
|
|
94
119
|
this.client = client
|
|
95
120
|
this.logger = logger
|
|
121
|
+
this.notifyOrchestrator = notifyOrchestrator
|
|
96
122
|
}
|
|
97
123
|
|
|
98
124
|
anys(): AsyncIterable<Any> {
|
|
99
125
|
const iter = new MyIter(AnyConvertor)
|
|
100
|
-
this.
|
|
126
|
+
this.consumers.push(iter)
|
|
101
127
|
return iter
|
|
102
128
|
}
|
|
103
129
|
|
|
104
130
|
strings(): AsyncIterable<string> {
|
|
105
131
|
const iter = new MyIter(StringConvertor)
|
|
106
|
-
this.
|
|
132
|
+
this.consumers.push(iter)
|
|
107
133
|
return iter
|
|
108
134
|
}
|
|
109
135
|
|
|
110
136
|
buffers(): AsyncIterable<Uint8Array> {
|
|
111
137
|
const iter = new MyIter(NoConvertor)
|
|
112
|
-
this.
|
|
138
|
+
this.consumers.push(iter)
|
|
113
139
|
return iter
|
|
114
140
|
}
|
|
115
141
|
|
|
116
142
|
streams(): AsyncIterable<AsyncGenerator<Uint8Array>> {
|
|
117
143
|
const iter = new MyIter(StreamConvertor)
|
|
118
|
-
this.
|
|
144
|
+
this.consumers.push(iter)
|
|
119
145
|
return iter
|
|
120
146
|
}
|
|
121
147
|
|
|
122
|
-
handleMsg(msg:
|
|
148
|
+
handleMsg(msg: ReceivingMessage) {
|
|
123
149
|
this.logger.debug(`${this.uri} handling message`)
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
|
|
151
|
+
const promises = []
|
|
152
|
+
for (const iter of this.consumers) {
|
|
153
|
+
promises.push(new Promise((res) => iter.push(msg.data, () => res(null))))
|
|
126
154
|
}
|
|
155
|
+
|
|
156
|
+
Promise.all(promises).then(() =>
|
|
157
|
+
this.notifyOrchestrator({
|
|
158
|
+
processed: {
|
|
159
|
+
globalSequenceNumber: msg.globalSequenceNumber,
|
|
160
|
+
channel: this.uri,
|
|
161
|
+
},
|
|
162
|
+
}),
|
|
163
|
+
)
|
|
127
164
|
}
|
|
128
165
|
|
|
129
166
|
close() {
|
|
130
|
-
for (const iter of this.
|
|
131
|
-
iter.close()
|
|
167
|
+
for (const iter of this.consumers) {
|
|
168
|
+
iter.close(() => {})
|
|
132
169
|
}
|
|
133
170
|
}
|
|
134
171
|
|
|
135
|
-
|
|
172
|
+
// There is a stream message available for this reader
|
|
173
|
+
async handleStreamingMessage({
|
|
174
|
+
channel,
|
|
175
|
+
globalSequenceNumber,
|
|
176
|
+
}: ReceivingStreamMessage) {
|
|
136
177
|
this.logger.debug(`${this.uri} handling streaming message`)
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
178
|
+
|
|
179
|
+
const chunks = this.client.receiveStreamMessage()
|
|
180
|
+
const writeControlMessage = promisify(chunks.write.bind(chunks))
|
|
181
|
+
const consumersConsumed = []
|
|
182
|
+
|
|
183
|
+
// After each chunk is handled by all consumer, emit a processed message
|
|
184
|
+
let idx = 0
|
|
185
|
+
const messageIterators = fanoutStream(
|
|
186
|
+
chunks,
|
|
187
|
+
this.consumers.length,
|
|
188
|
+
async () => {
|
|
189
|
+
await writeControlMessage({ streamSequenceNumber: idx++ })
|
|
190
|
+
},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
for (const consumer of this.consumers) {
|
|
194
|
+
consumersConsumed.push(
|
|
195
|
+
new Promise((res) =>
|
|
196
|
+
consumer.pushStream(messageIterators.pop()!, () => res(null)),
|
|
197
|
+
),
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
await writeControlMessage({ globalSequenceNumber })
|
|
202
|
+
|
|
203
|
+
Promise.all(consumersConsumed).then(() => {
|
|
204
|
+
console.log('Writing processed for streaming message')
|
|
205
|
+
this.notifyOrchestrator({ processed: { globalSequenceNumber, channel } })
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Helper function to tee a stream `numConsumers` times
|
|
212
|
+
* When each tee'd stream has handled a chunk, call {@link onAllHandled}
|
|
213
|
+
*/
|
|
214
|
+
function fanoutStream<T>(
|
|
215
|
+
stream: ClientReadableStream<T>,
|
|
216
|
+
numConsumers: number,
|
|
217
|
+
onAllHandled: () => void | Promise<void>,
|
|
218
|
+
): AsyncIterable<T>[] {
|
|
219
|
+
type Waiter = (value: IteratorResult<T>) => void
|
|
220
|
+
|
|
221
|
+
let ended = false
|
|
222
|
+
const buffer: T[] = []
|
|
223
|
+
const pending: Waiter[] = []
|
|
224
|
+
let activeConsumers = numConsumers
|
|
225
|
+
|
|
226
|
+
// consumer bookkeeping
|
|
227
|
+
let awaitingAck = 0
|
|
228
|
+
|
|
229
|
+
function pushChunk(chunk: T) {
|
|
230
|
+
buffer.push(chunk)
|
|
231
|
+
flush()
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function flush() {
|
|
235
|
+
while (buffer.length > 0 && pending.length > 0) {
|
|
236
|
+
const chunk = buffer[0] // keep until all consumers ack
|
|
237
|
+
const waiter = pending.shift()!
|
|
238
|
+
waiter({ value: chunk, done: false })
|
|
239
|
+
awaitingAck++
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function end() {
|
|
244
|
+
ended = true
|
|
245
|
+
while (pending.length > 0) {
|
|
246
|
+
const waiter = pending.shift()!
|
|
247
|
+
waiter({ value: undefined, done: true })
|
|
140
248
|
}
|
|
141
249
|
}
|
|
250
|
+
|
|
251
|
+
stream.on('data', (chunk: T) => {
|
|
252
|
+
pushChunk(chunk)
|
|
253
|
+
})
|
|
254
|
+
|
|
255
|
+
stream.on('end', () => {
|
|
256
|
+
end()
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
stream.on('error', (err) => {
|
|
260
|
+
while (pending.length > 0) {
|
|
261
|
+
const waiter = pending.shift()!
|
|
262
|
+
waiter({ value: undefined, done: true })
|
|
263
|
+
}
|
|
264
|
+
throw err
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
function makeIterable(): AsyncIterable<T> {
|
|
268
|
+
return {
|
|
269
|
+
[Symbol.asyncIterator]() {
|
|
270
|
+
return {
|
|
271
|
+
next(): Promise<IteratorResult<T>> {
|
|
272
|
+
if (buffer.length > 0) {
|
|
273
|
+
const chunk = buffer[0]
|
|
274
|
+
awaitingAck++
|
|
275
|
+
return Promise.resolve({ value: chunk, done: false })
|
|
276
|
+
}
|
|
277
|
+
if (ended) {
|
|
278
|
+
return Promise.resolve({ value: undefined, done: true })
|
|
279
|
+
}
|
|
280
|
+
return new Promise((resolve) => {
|
|
281
|
+
pending.push(resolve)
|
|
282
|
+
})
|
|
283
|
+
},
|
|
284
|
+
async return() {
|
|
285
|
+
activeConsumers--
|
|
286
|
+
if (activeConsumers === 0) {
|
|
287
|
+
end()
|
|
288
|
+
}
|
|
289
|
+
return { value: undefined, done: true }
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function ack() {
|
|
297
|
+
awaitingAck--
|
|
298
|
+
if (awaitingAck === 0) {
|
|
299
|
+
// all consumers done with the current chunk
|
|
300
|
+
buffer.shift() // drop it
|
|
301
|
+
await onAllHandled()
|
|
302
|
+
flush() // continue with next chunk
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// wrap consumer so they *must* call ack() after processing
|
|
307
|
+
function wrap(iterable: AsyncIterable<T>): AsyncIterable<T> {
|
|
308
|
+
return {
|
|
309
|
+
async *[Symbol.asyncIterator]() {
|
|
310
|
+
for await (const item of iterable) {
|
|
311
|
+
yield item
|
|
312
|
+
await ack()
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const rawIterables = Array.from({ length: numConsumers }, makeIterable)
|
|
319
|
+
return rawIterables.map(wrap)
|
|
142
320
|
}
|