@rdfc/js-runner 1.0.0 → 2.0.0-alpha.2
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/.husky/pre-commit +6 -0
- package/.prettierrc +4 -0
- package/README.md +3 -38
- package/__tests__/channels.test.ts +133 -0
- package/bin/runner.js +8 -0
- package/eslint.config.mjs +21 -0
- package/examples/echo/package-lock.json +80 -0
- package/examples/echo/package.json +18 -0
- package/examples/echo/pipeline.ttl +30 -0
- package/examples/echo/processors.ttl +84 -0
- package/examples/echo/src/processors.ts +75 -0
- package/examples/echo/test.ttl +14 -0
- package/examples/echo/tsconfig.json +114 -0
- package/examples/echo/untitled:/types/MyType.ttl +0 -0
- package/index.ttl +63 -0
- package/jest.config.js +2 -0
- package/lib/client.d.ts +1 -0
- package/lib/client.js +43 -0
- package/lib/convertor.d.ts +9 -0
- package/lib/convertor.js +51 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +5 -0
- package/lib/jsonld.d.ts +17 -0
- package/lib/jsonld.js +134 -0
- package/lib/logger.d.ts +15 -0
- package/lib/logger.js +27 -0
- package/lib/processor.d.ts +19 -0
- package/lib/processor.js +13 -0
- package/lib/reader.d.ts +29 -0
- package/lib/reader.js +110 -0
- package/lib/runner.d.ts +25 -0
- package/lib/runner.js +111 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/util_processors.d.ts +11 -0
- package/lib/util_processors.js +13 -0
- package/lib/writer.d.ts +25 -0
- package/lib/writer.js +57 -0
- package/package.json +49 -51
- package/src/client.ts +52 -0
- package/src/convertor.ts +59 -0
- package/src/index.ts +4 -0
- package/src/jsonld.ts +217 -0
- package/src/logger.ts +38 -0
- package/src/processor.ts +39 -0
- package/src/reader.ts +148 -0
- package/src/runner.ts +186 -0
- package/src/util_processors.ts +20 -0
- package/src/writer.ts +89 -0
- package/tsconfig.json +33 -0
- package/vite.config.ts +10 -0
- package/LICENSE +0 -21
- package/bin/js-runner.js +0 -4
- package/channels/file.ttl +0 -37
- package/channels/http.ttl +0 -59
- package/channels/kafka.ttl +0 -98
- package/channels/ws.ttl +0 -33
- package/dist/args.d.ts +0 -4
- package/dist/args.js +0 -59
- 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 -64
- package/dist/connectors/ws.d.ts +0 -10
- package/dist/connectors/ws.js +0 -69
- package/dist/connectors.d.ts +0 -54
- package/dist/connectors.js +0 -145
- package/dist/index.cjs +0 -677
- package/dist/index.d.ts +0 -33
- package/dist/index.js +0 -74
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/dist/util.d.ts +0 -43
- package/dist/util.js +0 -75
- package/ontology.ttl +0 -169
- package/processor/echo.ttl +0 -38
- package/processor/resc.ttl +0 -34
- package/processor/send.ttl +0 -40
- package/processor/test.js +0 -35
package/src/client.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import * as grpc from '@grpc/grpc-js'
|
|
2
|
+
import { promisify } from 'util'
|
|
3
|
+
import { RunnerClient, RunnerMessage } from '@rdfc/proto'
|
|
4
|
+
import winston from 'winston'
|
|
5
|
+
import { RpcTransport } from './logger'
|
|
6
|
+
import { Runner } from './runner'
|
|
7
|
+
|
|
8
|
+
export async function start(addr: string, uri: string) {
|
|
9
|
+
const client = new RunnerClient(addr, grpc.credentials.createInsecure())
|
|
10
|
+
|
|
11
|
+
const logger = winston.createLogger({
|
|
12
|
+
transports: [
|
|
13
|
+
new RpcTransport({
|
|
14
|
+
entities: [uri, 'cli'],
|
|
15
|
+
stream: client.logStream(() => {}),
|
|
16
|
+
}),
|
|
17
|
+
],
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
const stream = client.connect()
|
|
21
|
+
|
|
22
|
+
logger.info('Connected with server ' + addr)
|
|
23
|
+
const writable = promisify(stream.write.bind(stream))
|
|
24
|
+
const runner = new Runner(client, writable, uri, logger)
|
|
25
|
+
|
|
26
|
+
await writable({ identify: { uri } })
|
|
27
|
+
|
|
28
|
+
let processorsEnd!: (v: unknown) => unknown
|
|
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)
|
|
35
|
+
}
|
|
36
|
+
if (msg.start) {
|
|
37
|
+
runner.start().then(processorsEnd)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await runner.handleOrchMessage(msg)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
logger.error('Stream ended')
|
|
44
|
+
})()
|
|
45
|
+
|
|
46
|
+
await processorsEnded
|
|
47
|
+
|
|
48
|
+
logger.info('All processors are finished')
|
|
49
|
+
stream.end()
|
|
50
|
+
client.close()
|
|
51
|
+
process.exit(0)
|
|
52
|
+
}
|
package/src/convertor.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Any } from './reader'
|
|
2
|
+
|
|
3
|
+
const decoder = new TextDecoder()
|
|
4
|
+
export interface Convertor<T> {
|
|
5
|
+
from(buffer: Uint8Array): T
|
|
6
|
+
fromStream(stream: AsyncIterable<Uint8Array>): Promise<T>
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export const AnyConvertor: Convertor<Any> = {
|
|
10
|
+
from: function (buffer: Uint8Array): Any {
|
|
11
|
+
return {
|
|
12
|
+
buffer,
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
fromStream: async function (inp: AsyncIterable<Uint8Array>): Promise<Any> {
|
|
16
|
+
const stream = (async function* () {
|
|
17
|
+
yield* inp
|
|
18
|
+
})()
|
|
19
|
+
return { stream }
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const StringConvertor: Convertor<string> = {
|
|
24
|
+
from(buffer) {
|
|
25
|
+
return decoder.decode(buffer)
|
|
26
|
+
},
|
|
27
|
+
async fromStream(stream) {
|
|
28
|
+
const chunks: Uint8Array[] = []
|
|
29
|
+
for await (const chunk of stream) {
|
|
30
|
+
chunks.push(chunk)
|
|
31
|
+
}
|
|
32
|
+
return decoder.decode(Buffer.concat(chunks))
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
export const StreamConvertor: Convertor<AsyncGenerator<Uint8Array>> = {
|
|
36
|
+
from(buffer) {
|
|
37
|
+
return (async function* () {
|
|
38
|
+
yield buffer
|
|
39
|
+
})()
|
|
40
|
+
},
|
|
41
|
+
|
|
42
|
+
async fromStream(stream) {
|
|
43
|
+
return (async function* () {
|
|
44
|
+
yield* stream
|
|
45
|
+
})()
|
|
46
|
+
},
|
|
47
|
+
}
|
|
48
|
+
export const NoConvertor: Convertor<Uint8Array> = {
|
|
49
|
+
from(buffer) {
|
|
50
|
+
return buffer
|
|
51
|
+
},
|
|
52
|
+
async fromStream(stream) {
|
|
53
|
+
const chunks: Uint8Array[] = []
|
|
54
|
+
for await (const chunk of stream) {
|
|
55
|
+
chunks.push(chunk)
|
|
56
|
+
}
|
|
57
|
+
return Buffer.concat(chunks)
|
|
58
|
+
},
|
|
59
|
+
}
|
package/src/index.ts
ADDED
package/src/jsonld.ts
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
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/ontology#',
|
|
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
|
+
} catch (e) {}
|
|
193
|
+
|
|
194
|
+
return k + ': ' + x
|
|
195
|
+
}),
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
expandArgs(args[key], cont, logger)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export async function parse_jsonld(
|
|
204
|
+
args: string,
|
|
205
|
+
logger: Logger,
|
|
206
|
+
items: RunnerItems,
|
|
207
|
+
): Promise<unknown> {
|
|
208
|
+
const promises: Promise<unknown>[] = []
|
|
209
|
+
logger.info(args)
|
|
210
|
+
const cont = await jsonldToQuads(JSON.parse(args))
|
|
211
|
+
const out = JSON.parse(args, (k, v) => revive(k, v, logger, promises, items))
|
|
212
|
+
|
|
213
|
+
await Promise.all(promises)
|
|
214
|
+
|
|
215
|
+
expandArgs(out, cont, logger)
|
|
216
|
+
return out
|
|
217
|
+
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import winston from 'winston'
|
|
2
|
+
import Transport from 'winston-transport'
|
|
3
|
+
|
|
4
|
+
import * as grpc from '@grpc/grpc-js'
|
|
5
|
+
import { LogMessage } from '@rdfc/proto'
|
|
6
|
+
export class RpcTransport extends Transport {
|
|
7
|
+
private readonly stream: grpc.ClientWritableStream<LogMessage>
|
|
8
|
+
private readonly entities: string[]
|
|
9
|
+
private readonly aliases: string[]
|
|
10
|
+
constructor(opts: {
|
|
11
|
+
stream: grpc.ClientWritableStream<LogMessage>
|
|
12
|
+
entities: string[]
|
|
13
|
+
aliases?: string[]
|
|
14
|
+
}) {
|
|
15
|
+
super({ level: 'debug' })
|
|
16
|
+
|
|
17
|
+
this.stream = opts.stream
|
|
18
|
+
this.entities = opts.entities
|
|
19
|
+
this.aliases = opts.aliases || []
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
log(info: winston.LogEntry, callback: () => void) {
|
|
23
|
+
if (!this.stream.closed) {
|
|
24
|
+
this.stream.write(
|
|
25
|
+
{
|
|
26
|
+
msg: info.message,
|
|
27
|
+
level: info.level,
|
|
28
|
+
entities: this.entities,
|
|
29
|
+
aliases: this.aliases,
|
|
30
|
+
},
|
|
31
|
+
callback,
|
|
32
|
+
)
|
|
33
|
+
} else {
|
|
34
|
+
console.log('Output stream closed')
|
|
35
|
+
callback()
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
package/src/processor.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Logger } from 'winston'
|
|
2
|
+
import { Reader } from './reader'
|
|
3
|
+
import { Writer } from './writer'
|
|
4
|
+
|
|
5
|
+
export type Primitive = string | number | Writer | Reader | ProcessorArgs
|
|
6
|
+
|
|
7
|
+
export type ProcessorArgs = {
|
|
8
|
+
[id: string]: Primitive | Primitive[]
|
|
9
|
+
}
|
|
10
|
+
export type BGetter<T> = {
|
|
11
|
+
[K in keyof T]: T[K]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export abstract class Processor<T> {
|
|
15
|
+
protected readonly args: T // Store args safely
|
|
16
|
+
protected readonly logger: Logger
|
|
17
|
+
|
|
18
|
+
constructor(args: T, logger: Logger) {
|
|
19
|
+
Object.assign(this, args)
|
|
20
|
+
this.args = args
|
|
21
|
+
this.logger = logger
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected get<K extends keyof T>(key: K): T[K] {
|
|
25
|
+
return this.args[key]
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// This is the first function that is called (and awaited), when creating a processor
|
|
29
|
+
// This is the perfect location to start things like database connections
|
|
30
|
+
abstract init(this: T & this): Promise<void>
|
|
31
|
+
|
|
32
|
+
// Function to start reading channels
|
|
33
|
+
// This function is called for each processor before _produce_ is called
|
|
34
|
+
abstract transform(this: T & this): Promise<void>
|
|
35
|
+
|
|
36
|
+
// Function to start the production of data, starting the pipeline
|
|
37
|
+
// This function is called after all processors are completely setup
|
|
38
|
+
abstract produce(this: T & this): Promise<void>
|
|
39
|
+
}
|
package/src/reader.ts
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { ClientReadableStream } from '@grpc/grpc-js'
|
|
2
|
+
import { DataChunk, Message, RunnerClient, StreamMessage } from '@rdfc/proto'
|
|
3
|
+
import winston from 'winston'
|
|
4
|
+
import {
|
|
5
|
+
AnyConvertor,
|
|
6
|
+
Convertor,
|
|
7
|
+
NoConvertor,
|
|
8
|
+
StreamConvertor,
|
|
9
|
+
StringConvertor,
|
|
10
|
+
} from './convertor'
|
|
11
|
+
|
|
12
|
+
export type Any =
|
|
13
|
+
| {
|
|
14
|
+
string: string
|
|
15
|
+
}
|
|
16
|
+
| {
|
|
17
|
+
stream: AsyncGenerator<Uint8Array>
|
|
18
|
+
}
|
|
19
|
+
| {
|
|
20
|
+
buffer: Uint8Array
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface Reader {
|
|
24
|
+
strings(): AsyncIterable<string>
|
|
25
|
+
streams(): AsyncIterable<AsyncGenerator<Uint8Array>>
|
|
26
|
+
buffers(): AsyncIterable<Uint8Array>
|
|
27
|
+
anys(): AsyncIterable<Any>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class MyIter<T> implements AsyncIterable<T> {
|
|
31
|
+
private convertor: Convertor<T>
|
|
32
|
+
private queue: (T | undefined)[] = []
|
|
33
|
+
private resolveNext: ((value: T | undefined) => void) | null = null
|
|
34
|
+
|
|
35
|
+
constructor(convertor: Convertor<T>) {
|
|
36
|
+
this.convertor = convertor
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
push(buffer: Uint8Array) {
|
|
40
|
+
const item = this.convertor.from(buffer)
|
|
41
|
+
if (this.resolveNext) {
|
|
42
|
+
this.resolveNext(item)
|
|
43
|
+
this.resolveNext = null
|
|
44
|
+
} else {
|
|
45
|
+
this.queue.push(item)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
close() {
|
|
50
|
+
if (this.resolveNext) {
|
|
51
|
+
this.resolveNext(undefined)
|
|
52
|
+
this.resolveNext = null
|
|
53
|
+
} else {
|
|
54
|
+
this.queue.push(undefined)
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async pushStream(chunks: ClientReadableStream<DataChunk>) {
|
|
59
|
+
const stream = (async function* (stream) {
|
|
60
|
+
for await (const c of stream) {
|
|
61
|
+
const chunk: DataChunk = c
|
|
62
|
+
yield chunk.data
|
|
63
|
+
}
|
|
64
|
+
})(chunks)
|
|
65
|
+
const item = await this.convertor.fromStream(stream)
|
|
66
|
+
if (this.resolveNext) {
|
|
67
|
+
this.resolveNext(item)
|
|
68
|
+
this.resolveNext = null
|
|
69
|
+
} else {
|
|
70
|
+
this.queue.push(item)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async *[Symbol.asyncIterator]() {
|
|
75
|
+
while (true) {
|
|
76
|
+
if (this.queue.length > 0) {
|
|
77
|
+
const item = this.queue.shift()!
|
|
78
|
+
if (item === undefined) break
|
|
79
|
+
yield item
|
|
80
|
+
} else {
|
|
81
|
+
const item = await new Promise<T | undefined>(
|
|
82
|
+
(resolve) => (this.resolveNext = resolve),
|
|
83
|
+
)
|
|
84
|
+
if (item === undefined) break
|
|
85
|
+
yield item
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export class ReaderInstance implements Reader {
|
|
92
|
+
private client: RunnerClient
|
|
93
|
+
private uri: string
|
|
94
|
+
private logger: winston.Logger
|
|
95
|
+
|
|
96
|
+
private iterators: MyIter<unknown>[] = []
|
|
97
|
+
|
|
98
|
+
constructor(uri: string, client: RunnerClient, logger: winston.Logger) {
|
|
99
|
+
this.uri = uri
|
|
100
|
+
this.client = client
|
|
101
|
+
this.logger = logger
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
anys(): AsyncIterable<Any> {
|
|
105
|
+
const iter = new MyIter(AnyConvertor)
|
|
106
|
+
this.iterators.push(iter)
|
|
107
|
+
return iter
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
strings(): AsyncIterable<string> {
|
|
111
|
+
const iter = new MyIter(StringConvertor)
|
|
112
|
+
this.iterators.push(iter)
|
|
113
|
+
return iter
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
buffers(): AsyncIterable<Uint8Array> {
|
|
117
|
+
const iter = new MyIter(NoConvertor)
|
|
118
|
+
this.iterators.push(iter)
|
|
119
|
+
return iter
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
streams(): AsyncIterable<AsyncGenerator<Uint8Array>> {
|
|
123
|
+
const iter = new MyIter(StreamConvertor)
|
|
124
|
+
this.iterators.push(iter)
|
|
125
|
+
return iter
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
handleMsg(msg: Message) {
|
|
129
|
+
this.logger.debug(`${this.uri} handling message`)
|
|
130
|
+
for (const iter of this.iterators) {
|
|
131
|
+
iter.push(msg.data)
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
close() {
|
|
136
|
+
for (const iter of this.iterators) {
|
|
137
|
+
iter.close()
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
handleStreamingMessage(msg: StreamMessage) {
|
|
142
|
+
this.logger.debug(`${this.uri} handling streaming message`)
|
|
143
|
+
const chunks = this.client.receiveStreamMessage(msg.id!)
|
|
144
|
+
for (const iter of this.iterators) {
|
|
145
|
+
iter.pushStream(chunks)
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|