@sentio/runtime 1.37.0-rc.5 → 1.37.0-rc.7

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 (54) hide show
  1. package/lib/chain-config.d.ts +6 -0
  2. package/lib/chain-config.js +3 -0
  3. package/lib/chain-config.js.map +1 -0
  4. package/lib/endpoints.d.ts +8 -0
  5. package/lib/endpoints.js +15 -0
  6. package/lib/endpoints.js.map +1 -0
  7. package/lib/full-service.d.ts +276 -0
  8. package/lib/full-service.js +47 -0
  9. package/lib/full-service.js.map +1 -0
  10. package/lib/gen/{src/google → google}/protobuf/timestamp.d.ts +1 -1
  11. package/lib/gen/{src/google → google}/protobuf/timestamp.js +4 -4
  12. package/lib/gen/google/protobuf/timestamp.js.map +1 -0
  13. package/lib/index.d.ts +7 -0
  14. package/lib/index.js +24 -0
  15. package/lib/index.js.map +1 -0
  16. package/lib/loader.d.ts +5 -0
  17. package/lib/loader.js +30 -0
  18. package/lib/loader.js.map +1 -0
  19. package/lib/plugin.d.ts +19 -0
  20. package/lib/plugin.js +50 -0
  21. package/lib/plugin.js.map +1 -0
  22. package/lib/processor-runner.d.ts +2 -0
  23. package/lib/processor-runner.js +104 -0
  24. package/lib/processor-runner.js.map +1 -0
  25. package/lib/provider.d.ts +6 -0
  26. package/lib/provider.js +67 -0
  27. package/lib/provider.js.map +1 -0
  28. package/lib/service.d.ts +20 -0
  29. package/lib/service.js +120 -0
  30. package/lib/service.js.map +1 -0
  31. package/lib/service.test.d.ts +2 -0
  32. package/lib/service.test.js.map +1 -0
  33. package/lib/state-storage.test.d.ts +1 -0
  34. package/lib/state-storage.test.js.map +1 -0
  35. package/lib/state.d.ts +22 -0
  36. package/lib/state.js +68 -0
  37. package/lib/state.js.map +1 -0
  38. package/lib/utils.d.ts +4 -0
  39. package/lib/utils.js +22 -0
  40. package/lib/utils.js.map +1 -0
  41. package/package.json +4 -5
  42. package/src/chain-config.ts +6 -0
  43. package/src/endpoints.ts +14 -0
  44. package/src/full-service.ts +61 -0
  45. package/src/gen/{src/google → google}/protobuf/timestamp.ts +45 -40
  46. package/src/index.ts +7 -0
  47. package/src/loader.ts +24 -0
  48. package/src/plugin.ts +53 -0
  49. package/src/processor-runner.ts +97 -0
  50. package/src/provider.ts +75 -0
  51. package/src/service.ts +145 -0
  52. package/src/state.ts +75 -0
  53. package/src/utils.ts +20 -0
  54. package/lib/gen/src/google/protobuf/timestamp.js.map +0 -1
@@ -0,0 +1,61 @@
1
+ import { CallContext } from 'nice-grpc'
2
+
3
+ // Different than the simple one which
4
+ import {
5
+ DataBinding,
6
+ HandlerType,
7
+ ProcessBindingsRequest,
8
+ ProcessConfigRequest,
9
+ ProcessorServiceImplementation,
10
+ StartRequest,
11
+ } from './gen/processor/protos/processor'
12
+
13
+ import { Empty } from '@sentio/protos/lib/google/protobuf/empty'
14
+
15
+ export class FullProcessorServiceImpl implements ProcessorServiceImplementation {
16
+ constructor(instance: ProcessorServiceImplementation) {
17
+ this.instance = instance
18
+ }
19
+
20
+ instance: ProcessorServiceImplementation
21
+
22
+ async getConfig(request: ProcessConfigRequest, context: CallContext) {
23
+ return this.instance.getConfig(request, context)
24
+ }
25
+
26
+ async start(request: StartRequest, context: CallContext) {
27
+ return this.instance.start(request, context)
28
+ }
29
+
30
+ async stop(request: Empty, context: CallContext) {
31
+ return this.instance.stop(request, context)
32
+ }
33
+
34
+ async processBindings(request: ProcessBindingsRequest, options: CallContext) {
35
+ for (const binding of request.bindings) {
36
+ this.adjustDataBinding(binding)
37
+ }
38
+ return this.instance.processBindings(request, options)
39
+ }
40
+
41
+ async *processBindingsStream(requests: AsyncIterable<DataBinding>, context: CallContext) {
42
+ throw new Error('Not Implemented for streaming')
43
+ // y this.instance.processBindingsStream(requests, context)
44
+ }
45
+
46
+ protected adjustDataBinding(dataBinding: DataBinding): void {
47
+ switch (dataBinding.handlerType) {
48
+ case HandlerType.UNKNOWN:
49
+ if (dataBinding.data?.ethBlock) {
50
+ if (dataBinding.data.raw.length === 0) {
51
+ // This is actually not needed in current system, just as initla test propose, move to test only
52
+ // when this is stable
53
+ dataBinding.data.raw = new TextEncoder().encode(JSON.stringify(dataBinding.data.ethBlock.block))
54
+ }
55
+ }
56
+ break
57
+ default:
58
+ break
59
+ }
60
+ }
61
+ }
@@ -1,86 +1,91 @@
1
1
  /* eslint-disable */
2
- import Long from "long";
3
- import _m0 from "protobufjs/minimal";
2
+ import Long from 'long'
3
+ import _m0 from 'protobufjs/minimal'
4
4
 
5
5
  export interface Timestamp {
6
- seconds: bigint;
7
- nanos: number;
6
+ seconds: bigint
7
+ nanos: number
8
8
  }
9
9
 
10
10
  function createBaseTimestamp(): Timestamp {
11
- return { seconds: BigInt("0"), nanos: 0 };
11
+ return { seconds: BigInt('0'), nanos: 0 }
12
12
  }
13
13
 
14
14
  export const Timestamp = {
15
15
  encode(message: Timestamp, writer: _m0.Writer = _m0.Writer.create()): _m0.Writer {
16
- if (message.seconds !== BigInt("0")) {
17
- writer.uint32(8).int64(message.seconds.toString());
16
+ if (message.seconds !== BigInt('0')) {
17
+ writer.uint32(8).int64(message.seconds.toString())
18
18
  }
19
19
  if (message.nanos !== 0) {
20
- writer.uint32(16).int32(message.nanos);
20
+ writer.uint32(16).int32(message.nanos)
21
21
  }
22
- return writer;
22
+ return writer
23
23
  },
24
24
 
25
25
  decode(input: _m0.Reader | Uint8Array, length?: number): Timestamp {
26
- const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
27
- let end = length === undefined ? reader.len : reader.pos + length;
28
- const message = createBaseTimestamp();
26
+ const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input)
27
+ let end = length === undefined ? reader.len : reader.pos + length
28
+ const message = createBaseTimestamp()
29
29
  while (reader.pos < end) {
30
- const tag = reader.uint32();
30
+ const tag = reader.uint32()
31
31
  switch (tag >>> 3) {
32
32
  case 1:
33
- message.seconds = longToBigint(reader.int64() as Long);
34
- break;
33
+ message.seconds = longToBigint(reader.int64() as Long)
34
+ break
35
35
  case 2:
36
- message.nanos = reader.int32();
37
- break;
36
+ message.nanos = reader.int32()
37
+ break
38
38
  default:
39
- reader.skipType(tag & 7);
40
- break;
39
+ reader.skipType(tag & 7)
40
+ break
41
41
  }
42
42
  }
43
- return message;
43
+ return message
44
44
  },
45
45
 
46
46
  fromJSON(object: any): Timestamp {
47
47
  return {
48
- seconds: isSet(object.seconds) ? BigInt(object.seconds) : BigInt("0"),
48
+ seconds: isSet(object.seconds) ? BigInt(object.seconds) : BigInt('0'),
49
49
  nanos: isSet(object.nanos) ? Number(object.nanos) : 0,
50
- };
50
+ }
51
51
  },
52
52
 
53
53
  toJSON(message: Timestamp): unknown {
54
- const obj: any = {};
55
- message.seconds !== undefined && (obj.seconds = message.seconds.toString());
56
- message.nanos !== undefined && (obj.nanos = Math.round(message.nanos));
57
- return obj;
54
+ const obj: any = {}
55
+ message.seconds !== undefined && (obj.seconds = message.seconds.toString())
56
+ message.nanos !== undefined && (obj.nanos = Math.round(message.nanos))
57
+ return obj
58
58
  },
59
59
 
60
60
  fromPartial(object: DeepPartial<Timestamp>): Timestamp {
61
- const message = createBaseTimestamp();
62
- message.seconds = object.seconds ?? BigInt("0");
63
- message.nanos = object.nanos ?? 0;
64
- return message;
61
+ const message = createBaseTimestamp()
62
+ message.seconds = object.seconds ?? BigInt('0')
63
+ message.nanos = object.nanos ?? 0
64
+ return message
65
65
  },
66
- };
66
+ }
67
67
 
68
- type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
68
+ type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined
69
69
 
70
- type DeepPartial<T> = T extends Builtin ? T
71
- : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>>
72
- : T extends {} ? { [K in keyof T]?: DeepPartial<T[K]> }
73
- : Partial<T>;
70
+ type DeepPartial<T> = T extends Builtin
71
+ ? T
72
+ : T extends Array<infer U>
73
+ ? Array<DeepPartial<U>>
74
+ : T extends ReadonlyArray<infer U>
75
+ ? ReadonlyArray<DeepPartial<U>>
76
+ : T extends {}
77
+ ? { [K in keyof T]?: DeepPartial<T[K]> }
78
+ : Partial<T>
74
79
 
75
80
  function longToBigint(long: Long) {
76
- return BigInt(long.toString());
81
+ return BigInt(long.toString())
77
82
  }
78
83
 
79
84
  if (_m0.util.Long !== Long) {
80
- _m0.util.Long = Long as any;
81
- _m0.configure();
85
+ _m0.util.Long = Long as any
86
+ _m0.configure()
82
87
  }
83
88
 
84
89
  function isSet(value: any): boolean {
85
- return value !== null && value !== undefined;
90
+ return value !== null && value !== undefined
86
91
  }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export * from './provider'
2
+ export * from './plugin'
3
+ export * from './state'
4
+ export * from './utils'
5
+ export * from './endpoints'
6
+ export * from './chain-config'
7
+ export * from './service'
package/src/loader.ts ADDED
@@ -0,0 +1,24 @@
1
+ export function load(name: string): { module: any; name: string; path: string } | undefined {
2
+ const req = eval('require')
3
+
4
+ try {
5
+ let path: string
6
+ try {
7
+ path = req.resolve(name, { paths: [process.cwd()] })
8
+ } catch {
9
+ path = req.resolve(name)
10
+ }
11
+
12
+ const module = { module: req(path), name, path }
13
+ console.log('Processor Load successfully')
14
+ return module
15
+ } catch (err) {
16
+ if (err instanceof Error && err.message.startsWith(`Cannot find module '${name}'`)) {
17
+ // this error is expected
18
+ console.log("Couldn't load (expected): ", name)
19
+ return undefined
20
+ } else {
21
+ throw err
22
+ }
23
+ }
24
+ }
package/src/plugin.ts ADDED
@@ -0,0 +1,53 @@
1
+ import { DataBinding, HandlerType, ProcessConfigResponse, ProcessResult, StartRequest } from '@sentio/protos'
2
+
3
+ export abstract class Plugin {
4
+ name: string
5
+ supportedHandlers: HandlerType[] = []
6
+
7
+ configure(config: ProcessConfigResponse) {}
8
+ start(start: StartRequest) {}
9
+ stateDiff(config: ProcessConfigResponse): boolean {
10
+ return false
11
+ }
12
+ async processBinding(request: DataBinding): Promise<ProcessResult> {
13
+ return ProcessResult.fromPartial({})
14
+ }
15
+ }
16
+
17
+ export class PluginManager {
18
+ static INSTANCE = new PluginManager()
19
+
20
+ plugins: Plugin[] = []
21
+ typesToPlugin = new Map<HandlerType, Plugin>()
22
+
23
+ register(plugin: Plugin) {
24
+ this.plugins.push(plugin)
25
+ for (const handlerType of plugin.supportedHandlers) {
26
+ const exsited = this.typesToPlugin.get(handlerType)
27
+ if (exsited) {
28
+ throw new Error(`Duplicate plugin for ${handlerType}: ${exsited.name} and ${plugin.name}`)
29
+ }
30
+ this.typesToPlugin.set(handlerType, plugin)
31
+ }
32
+ }
33
+
34
+ configure(config: ProcessConfigResponse) {
35
+ this.plugins.forEach((plugin) => plugin.configure(config))
36
+ }
37
+
38
+ start(start: StartRequest) {
39
+ this.plugins.forEach((plugin) => plugin.start(start))
40
+ }
41
+
42
+ stateDiff(config: ProcessConfigResponse): boolean {
43
+ return this.plugins.some((plugin) => plugin.stateDiff(config))
44
+ }
45
+
46
+ processBinding(request: DataBinding): Promise<ProcessResult> {
47
+ const plugin = this.typesToPlugin.get(request.handlerType)
48
+ if (!plugin) {
49
+ throw new Error(`No plugin for ${request.handlerType}`)
50
+ }
51
+ return plugin.processBinding(request)
52
+ }
53
+ }
@@ -0,0 +1,97 @@
1
+ #!/usr/bin/env node
2
+
3
+ import path from 'path'
4
+ import fs from 'fs-extra'
5
+ import * as util from 'util'
6
+
7
+ import commandLineArgs from 'command-line-args'
8
+ import { createServer } from 'nice-grpc'
9
+ import { createLogger, transports, format } from 'winston'
10
+ import { CompressionAlgorithms } from '@grpc/grpc-js/build/src/compression-algorithms'
11
+
12
+ import { ProcessorDefinition } from '@sentio/protos'
13
+ import { ProcessorServiceImpl } from './service'
14
+ import { setProvider } from './provider'
15
+ import { State } from './state'
16
+ import { Endpoints } from './endpoints'
17
+
18
+ import { load } from './loader'
19
+ import { FullProcessorServiceImpl } from './full-service'
20
+
21
+ State.reset()
22
+ Endpoints.reset()
23
+
24
+ const optionDefinitions = [
25
+ { name: 'target', type: String, defaultOption: true },
26
+ { name: 'port', alias: 'p', type: String, defaultValue: '4000' },
27
+ { name: 'concurrency', type: Number, defaultValue: 4 },
28
+ { name: 'use-chainserver', type: Boolean, defaultValue: false },
29
+ {
30
+ name: 'chains-config',
31
+ alias: 'c',
32
+ type: String,
33
+ defaultValue: 'chains-config.json',
34
+ },
35
+ { name: 'chainquery-server', type: String, defaultValue: '' },
36
+ { name: 'pricefeed-server', type: String, defaultValue: '' },
37
+ { name: 'log-format', type: String, defaultValue: 'console' },
38
+ { name: 'debug', type: Boolean, defaultValue: false },
39
+ ]
40
+
41
+ const options = commandLineArgs(optionDefinitions, { partial: true })
42
+
43
+ if (options['log-format'] === 'json') {
44
+ const utilFormatter = {
45
+ transform: (info: any) => {
46
+ const args = info[Symbol.for('splat')]
47
+ if (args) {
48
+ info.message = util.format(info.message, ...args)
49
+ }
50
+ return info
51
+ },
52
+ }
53
+ const logger = createLogger({
54
+ format: format.combine(
55
+ format.timestamp({ format: 'YYYY-MM-DDTHH:mm:ssZ' }),
56
+ utilFormatter,
57
+ format.errors({ stack: true }),
58
+ format.json()
59
+ ),
60
+ transports: [new transports.Console()],
61
+ })
62
+
63
+ console.log = (...args) => logger.info.call(logger, ...args)
64
+ console.info = (...args) => logger.info.call(logger, ...args)
65
+ console.warn = (...args) => logger.warn.call(logger, ...args)
66
+ console.error = (...args) => logger.error.call(logger, ...args)
67
+ console.debug = (...args) => logger.debug.call(logger, ...args)
68
+ }
69
+ if (options.debug) {
70
+ console.log('Starting with', options.target)
71
+ }
72
+
73
+ const fullPath = path.resolve(options['chains-config'])
74
+ const chainsConfig = fs.readJsonSync(fullPath)
75
+
76
+ setProvider(chainsConfig, options.concurrency, options['use-chainserver'])
77
+ Endpoints.INSTANCE.chainQueryAPI = options['chainquery-server']
78
+ Endpoints.INSTANCE.priceFeedAPI = options['pricefeed-server']
79
+
80
+ if (options.debug) {
81
+ console.log('Starting Server', options)
82
+ }
83
+
84
+ const server = createServer({
85
+ 'grpc.max_send_message_length': 128 * 1024 * 1024,
86
+ 'grpc.max_receive_message_length': 128 * 1024 * 1024,
87
+ 'grpc.default_compression_algorithm': CompressionAlgorithms.gzip,
88
+ })
89
+
90
+ const baseService = new ProcessorServiceImpl(() => load(options.target), server.shutdown)
91
+ const service = new FullProcessorServiceImpl(baseService)
92
+
93
+ server.add(ProcessorDefinition, service)
94
+
95
+ server.listen('0.0.0.0:' + options.port)
96
+
97
+ console.log('Processor Server Started')
@@ -0,0 +1,75 @@
1
+ import { getNetwork, Provider, StaticJsonRpcProvider } from '@ethersproject/providers'
2
+ import { Networkish } from '@ethersproject/networks'
3
+ import PQueue from 'p-queue'
4
+ import { ConnectionInfo } from '@ethersproject/web'
5
+ import { ChainConfig } from './chain-config'
6
+ import { Endpoints } from './endpoints'
7
+
8
+ export const DummyProvider = new StaticJsonRpcProvider(undefined, 1)
9
+
10
+ export function getProvider(networkish?: Networkish): Provider {
11
+ if (!networkish) {
12
+ networkish = 1
13
+ }
14
+ const network = getNetwork(networkish)
15
+
16
+ if (!Endpoints.INSTANCE.providers) {
17
+ throw Error('Provider not set')
18
+ }
19
+ const value = Endpoints.INSTANCE.providers.get(network.chainId)
20
+ if (value === undefined) {
21
+ throw Error(
22
+ 'Provider not found for chain ' +
23
+ network.chainId +
24
+ ', configured chains: ' +
25
+ [...Endpoints.INSTANCE.providers.keys()].join(' ')
26
+ )
27
+ }
28
+ return value
29
+ }
30
+
31
+ export function setProvider(config: Record<string, ChainConfig>, concurrency = 4, useChainServer = false) {
32
+ Endpoints.INSTANCE.providers = new Map<number, Provider>()
33
+
34
+ for (const chainIdStr in config) {
35
+ if (isNaN(Number.parseInt(chainIdStr))) {
36
+ continue
37
+ }
38
+
39
+ const chainConfig = config[chainIdStr]
40
+ const chainId = Number(chainIdStr)
41
+
42
+ // let providers: StaticJsonRpcProvider[] = []
43
+ // for (const http of chainConfig.Https) {
44
+ // providers.push(new StaticJsonRpcProvider(http, chainId))
45
+ // }
46
+ // random shuffle
47
+ // providers = providers.sort(() => Math.random() - 0.5)
48
+
49
+ // const provider = new FallbackProvider(providers)
50
+
51
+ let rpcAddress = ''
52
+ if (useChainServer && chainConfig.ChainServer) {
53
+ rpcAddress = chainConfig.ChainServer
54
+ } else {
55
+ const idx = Math.floor(Math.random() * chainConfig.Https.length)
56
+ rpcAddress = chainConfig.Https[idx]
57
+ }
58
+
59
+ const provider = new QueuedStaticJsonRpcProvider(rpcAddress, chainId, concurrency)
60
+ Endpoints.INSTANCE.providers.set(chainId, provider)
61
+ }
62
+ }
63
+
64
+ class QueuedStaticJsonRpcProvider extends StaticJsonRpcProvider {
65
+ executor: PQueue
66
+
67
+ constructor(url: ConnectionInfo | string, network: Networkish, concurrency: number) {
68
+ super(url, network)
69
+ this.executor = new PQueue({ concurrency: concurrency })
70
+ }
71
+
72
+ send(method: string, params: Array<any>): Promise<any> {
73
+ return this.executor.add(() => super.send(method, params))
74
+ }
75
+ }
package/src/service.ts ADDED
@@ -0,0 +1,145 @@
1
+ import { CallContext, ServerError, Status } from 'nice-grpc'
2
+
3
+ import {
4
+ DataBinding,
5
+ HandlerType,
6
+ ProcessBindingResponse,
7
+ ProcessBindingsRequest,
8
+ ProcessConfigRequest,
9
+ ProcessConfigResponse,
10
+ ProcessorServiceImplementation,
11
+ ProcessResult,
12
+ StartRequest,
13
+ } from '@sentio/protos'
14
+
15
+ import { Empty } from '@sentio/protos/lib/google/protobuf/empty'
16
+
17
+ import { PluginManager } from './plugin'
18
+ import { errorString, mergeProcessResults } from './utils'
19
+ ;(BigInt.prototype as any).toJSON = function () {
20
+ return this.toString()
21
+ }
22
+
23
+ export class ProcessorServiceImpl implements ProcessorServiceImplementation {
24
+ private started = false
25
+ private processorConfig: ProcessConfigResponse
26
+
27
+ private readonly loader: () => void
28
+
29
+ private readonly shutdownHandler?: () => void
30
+
31
+ constructor(loader: () => void, shutdownHandler?: () => void) {
32
+ this.loader = loader
33
+ this.shutdownHandler = shutdownHandler
34
+ }
35
+
36
+ async getConfig(request: ProcessConfigRequest, context: CallContext): Promise<ProcessConfigResponse> {
37
+ if (!this.started) {
38
+ throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
39
+ }
40
+ if (!this.processorConfig) {
41
+ throw new ServerError(Status.INTERNAL, 'Process config empty.')
42
+ }
43
+ return this.processorConfig
44
+ }
45
+
46
+ async configure() {
47
+ this.processorConfig = ProcessConfigResponse.fromPartial({})
48
+ PluginManager.INSTANCE.configure(this.processorConfig)
49
+ }
50
+
51
+ async start(request: StartRequest, context: CallContext): Promise<Empty> {
52
+ if (this.started) {
53
+ return {}
54
+ }
55
+
56
+ try {
57
+ for (const plugin of [
58
+ '@sentio/sdk/lib/core/core-plugin',
59
+ '@sentio/sdk/lib/core/eth-plugin',
60
+ '@sentio/sdk/lib/core/sui-plugin',
61
+ '@sentio/sdk-aptos/lib/aptos-plugin',
62
+ '@sentio/sdk-solana/lib/solana-plugin',
63
+ ]) {
64
+ try {
65
+ require(plugin)
66
+ } catch (e) {
67
+ console.error('Failed to load plugin: ', plugin)
68
+ }
69
+ }
70
+
71
+ this.loader()
72
+ } catch (e) {
73
+ throw new ServerError(Status.INVALID_ARGUMENT, 'Failed to load processor: ' + errorString(e))
74
+ }
75
+
76
+ PluginManager.INSTANCE.start(request)
77
+
78
+ try {
79
+ await this.configure()
80
+ } catch (e) {
81
+ throw new ServerError(Status.INTERNAL, 'Failed to start processor : ' + errorString(e))
82
+ }
83
+ this.started = true
84
+ return {}
85
+ }
86
+
87
+ async stop(request: Empty, context: CallContext): Promise<Empty> {
88
+ console.log('Server Shutting down in 5 seconds')
89
+ if (this.shutdownHandler) {
90
+ setTimeout(this.shutdownHandler, 5000)
91
+ }
92
+ return {}
93
+ }
94
+
95
+ async processBindings(request: ProcessBindingsRequest, options?: CallContext): Promise<ProcessBindingResponse> {
96
+ if (!this.started) {
97
+ throw new ServerError(Status.UNAVAILABLE, 'Service Not started.')
98
+ }
99
+
100
+ const promises = request.bindings.map((binding) => this.processBinding(binding))
101
+ const result = mergeProcessResults(await Promise.all(promises))
102
+
103
+ let updated = false
104
+ if (PluginManager.INSTANCE.stateDiff(this.processorConfig)) {
105
+ await this.configure()
106
+ updated = true
107
+ }
108
+
109
+ return {
110
+ result,
111
+ configUpdated: updated,
112
+ }
113
+ }
114
+
115
+ async processBinding(request: DataBinding, options?: CallContext): Promise<ProcessResult> {
116
+ const result = await PluginManager.INSTANCE.processBinding(request)
117
+ recordRuntimeInfo(result, request.handlerType)
118
+ return result
119
+ }
120
+
121
+ async *processBindingsStream(requests: AsyncIterable<DataBinding>, context: CallContext) {
122
+ for await (const request of requests) {
123
+ const result = await this.processBinding(request)
124
+ let updated = false
125
+ if (PluginManager.INSTANCE.stateDiff(this.processorConfig)) {
126
+ await this.configure()
127
+ updated = true
128
+ }
129
+ yield {
130
+ result,
131
+ configUpdated: updated,
132
+ }
133
+ }
134
+ }
135
+ }
136
+
137
+ function recordRuntimeInfo(results: ProcessResult, handlerType: HandlerType) {
138
+ for (const list of [results.gauges, results.counters, results.logs, results.events, results.exports]) {
139
+ list.forEach((e) => {
140
+ e.runtimeInfo = {
141
+ from: handlerType,
142
+ }
143
+ })
144
+ }
145
+ }
package/src/state.ts ADDED
@@ -0,0 +1,75 @@
1
+ export class State {
2
+ stateMap = new Map<string, any>()
3
+
4
+ static INSTANCE = new State()
5
+
6
+ static reset() {
7
+ State.INSTANCE = new State()
8
+ }
9
+ }
10
+
11
+ export abstract class StateStorage<T> {
12
+ // TODO learn how to define single instance for all subclasses
13
+
14
+ protected constructor() {
15
+ //
16
+ }
17
+
18
+ abstract initValue(): T
19
+
20
+ key(): string {
21
+ return this.constructor.name
22
+ }
23
+
24
+ getOrRegister(): T {
25
+ let metricState: T = State.INSTANCE.stateMap.get(this.key())
26
+ if (!metricState) {
27
+ metricState = this.initValue()
28
+ State.INSTANCE.stateMap.set(this.key(), metricState)
29
+ }
30
+ return metricState
31
+ }
32
+ }
33
+
34
+ export abstract class MapStateStorage<T> extends StateStorage<Map<string, T>> {
35
+ initValue() {
36
+ return new Map<string, T>()
37
+ }
38
+
39
+ getValue(key: string): T | undefined {
40
+ const m = this.getOrRegister()
41
+ return m.get(key)
42
+ }
43
+
44
+ getValues(): T[] {
45
+ const m = this.getOrRegister()
46
+ return Array.from(m.values())
47
+ }
48
+
49
+ getOrSetValue(key: string, value: T): T {
50
+ const m = this.getOrRegister()
51
+ const oldValue = m.get(key)
52
+ if (oldValue) {
53
+ console.warn(key, 'has been registered twice, use the previous one')
54
+ return oldValue
55
+ }
56
+ m.set(key, value)
57
+ return value
58
+ }
59
+ }
60
+
61
+ export abstract class ListStateStorage<T> extends StateStorage<T[]> {
62
+ initValue() {
63
+ return []
64
+ }
65
+
66
+ getValues(): T[] {
67
+ return this.getOrRegister()
68
+ }
69
+
70
+ addValue(value: T): T {
71
+ const m = this.getOrRegister()
72
+ m.push(value)
73
+ return value
74
+ }
75
+ }
package/src/utils.ts ADDED
@@ -0,0 +1,20 @@
1
+ import { ProcessResult } from '@sentio/protos'
2
+
3
+ export function mergeProcessResults(results: ProcessResult[]): ProcessResult {
4
+ const res = ProcessResult.fromPartial({})
5
+
6
+ for (const r of results) {
7
+ res.counters = res.counters.concat(r.counters)
8
+ res.gauges = res.gauges.concat(r.gauges)
9
+ res.logs = res.logs.concat(r.logs)
10
+ res.events = res.events.concat(r.events)
11
+ res.exports = res.exports.concat(r.exports)
12
+ }
13
+ return res
14
+ }
15
+
16
+ export function errorString(e: Error): string {
17
+ return e.stack || e.message
18
+ }
19
+
20
+ export const USER_PROCESSOR = 'user_processor'