@conduitplatform/module-tools 0.16.26 → 0.17.0-alpha.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/LICENSE +21 -0
- package/dist/index.cjs +2059 -1775
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +490 -0
- package/dist/index.d.ts +490 -7
- package/dist/index.esm.js +2033 -1746
- package/dist/index.esm.js.map +1 -1
- package/dist/module.proto +25 -0
- package/package.json +27 -31
- package/dist/ManagedModule.d.ts +0 -80
- package/dist/classes/ConduitActiveSchema.d.ts +0 -19
- package/dist/classes/ConduitServiceModule.d.ts +0 -27
- package/dist/classes/ConfigController.d.ts +0 -7
- package/dist/classes/GrpcServer.d.ts +0 -23
- package/dist/classes/ModuleError.d.ts +0 -5
- package/dist/classes/index.d.ts +0 -4
- package/dist/helpers/GrpcHelper.d.ts +0 -8
- package/dist/helpers/GrpcToParsedRouterRequest.d.ts +0 -2
- package/dist/helpers/TypeHelpers.d.ts +0 -70
- package/dist/helpers/index.d.ts +0 -4
- package/dist/helpers/wrapGrpcFunctions.d.ts +0 -5
- package/dist/interfaces/ConduitService.d.ts +0 -8
- package/dist/interfaces/ModuleLifecycleStage.d.ts +0 -7
- package/dist/interfaces/index.d.ts +0 -2
- package/dist/logging/index.d.ts +0 -16
- package/dist/metrics/MetricsServer.d.ts +0 -11
- package/dist/metrics/clientMiddleware.d.ts +0 -2
- package/dist/metrics/config/defaults.d.ts +0 -91
- package/dist/metrics/index.d.ts +0 -23
- package/dist/routing/ProxyRouteBuilder.d.ts +0 -21
- package/dist/routing/RouteBuilder.d.ts +0 -23
- package/dist/routing/RoutingController.d.ts +0 -8
- package/dist/routing/RoutingManager.d.ts +0 -35
- package/dist/routing/RoutingUtilities.d.ts +0 -7
- package/dist/routing/index.d.ts +0 -5
- package/dist/routing/wrapRouterFunctions.d.ts +0 -13
- package/dist/utilities/convictConfigParser.d.ts +0 -2
- package/dist/utilities/index.d.ts +0 -3
- package/dist/utilities/initializeSdk.d.ts +0 -2
- package/dist/utilities/linearBackoffTimeout.d.ts +0 -9
- package/dist/utilities/merge.d.ts +0 -1
package/dist/index.esm.js
CHANGED
|
@@ -1,1828 +1,2115 @@
|
|
|
1
|
-
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
import { EventEmitter } from 'events';
|
|
8
|
-
import { fileURLToPath } from 'node:url';
|
|
9
|
-
import convict from 'convict';
|
|
10
|
-
import winston, { format } from 'winston';
|
|
11
|
-
import { get, createServer as createServer$1 } from 'http';
|
|
12
|
-
import LokiTransport from 'winston-loki';
|
|
13
|
-
import { clearTimeout as clearTimeout$1 } from 'timers';
|
|
14
|
-
import { Registry, collectDefaultMetrics, Counter, Summary, Histogram, Gauge, exponentialBuckets } from 'prom-client';
|
|
15
|
-
import { isAbortError } from 'abort-controller-x';
|
|
16
|
-
import { Status, ClientError } from 'nice-grpc-common';
|
|
1
|
+
// ../../node_modules/.pnpm/tsup@8.5.1_jiti@2.6.1_postcss@8.5.8_typescript@6.0.2_yaml@2.8.2/node_modules/tsup/assets/esm_shims.js
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
var getFilename = () => fileURLToPath(import.meta.url);
|
|
5
|
+
var getDirname = () => path.dirname(getFilename());
|
|
6
|
+
var __dirname = /* @__PURE__ */ getDirname();
|
|
17
7
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
8
|
+
// src/classes/ConduitActiveSchema.ts
|
|
9
|
+
import {
|
|
10
|
+
ConduitSchema
|
|
11
|
+
} from "@conduitplatform/grpc-sdk";
|
|
12
|
+
var ConduitActiveSchema = class extends ConduitSchema {
|
|
13
|
+
dbInstance;
|
|
14
|
+
constructor(dbInstance, name, fields, modelOptions, collectionName) {
|
|
15
|
+
super(name, fields ?? {}, modelOptions, collectionName);
|
|
16
|
+
this.dbInstance = dbInstance;
|
|
17
|
+
}
|
|
18
|
+
findOne(query, options) {
|
|
19
|
+
return this.dbInstance.findOne(this.name, query, options);
|
|
20
|
+
}
|
|
21
|
+
findMany(query, options) {
|
|
22
|
+
return this.dbInstance.findMany(this.name, query, options);
|
|
23
|
+
}
|
|
24
|
+
create(query, options) {
|
|
25
|
+
return this.dbInstance.create(this.name, query, options);
|
|
26
|
+
}
|
|
27
|
+
createMany(query, options) {
|
|
28
|
+
return this.dbInstance.createMany(this.name, query, options);
|
|
29
|
+
}
|
|
30
|
+
findByIdAndUpdate(id, document, options) {
|
|
31
|
+
return this.dbInstance.findByIdAndUpdate(this.name, id, document, options);
|
|
32
|
+
}
|
|
33
|
+
findByIdAndReplace(id, document, options) {
|
|
34
|
+
return this.dbInstance.findByIdAndReplace(this.name, id, document, options);
|
|
35
|
+
}
|
|
36
|
+
replaceOne(filterQuery, query, options) {
|
|
37
|
+
return this.dbInstance.replaceOne(this.name, filterQuery, query, options);
|
|
38
|
+
}
|
|
39
|
+
updateOne(filterQuery, query, options) {
|
|
40
|
+
return this.dbInstance.updateOne(this.name, filterQuery, query, options);
|
|
41
|
+
}
|
|
42
|
+
updateMany(filterQuery, query, options) {
|
|
43
|
+
return this.dbInstance.updateMany(this.name, filterQuery, query, options);
|
|
44
|
+
}
|
|
45
|
+
deleteOne(query, options) {
|
|
46
|
+
return this.dbInstance.deleteOne(this.name, query, options);
|
|
47
|
+
}
|
|
48
|
+
deleteMany(query, options) {
|
|
49
|
+
return this.dbInstance.deleteMany(this.name, query, options);
|
|
50
|
+
}
|
|
51
|
+
countDocuments(query, options) {
|
|
52
|
+
return this.dbInstance.countDocuments(this.name, query, options);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
61
55
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
class
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
56
|
+
// src/helpers/TypeHelpers.ts
|
|
57
|
+
import {
|
|
58
|
+
TYPE
|
|
59
|
+
} from "@conduitplatform/grpc-sdk";
|
|
60
|
+
var ConduitStringConstructor = class {
|
|
61
|
+
// private to disallow creating other instances of this type
|
|
62
|
+
constructor() {
|
|
63
|
+
}
|
|
64
|
+
static get Optional() {
|
|
65
|
+
return TYPE.String;
|
|
66
|
+
}
|
|
67
|
+
static get Required() {
|
|
68
|
+
return { type: TYPE.String, required: true };
|
|
69
|
+
}
|
|
70
|
+
static RequiredWith(rules) {
|
|
71
|
+
return { type: TYPE.String, required: true, validate: rules };
|
|
72
|
+
}
|
|
73
|
+
static OptionalWith(rules) {
|
|
74
|
+
return { type: TYPE.String, required: false, validate: rules };
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var ConduitString = ConduitStringConstructor;
|
|
78
|
+
var ConduitNumberConstructor = class {
|
|
79
|
+
// private to disallow creating other instances of this type
|
|
80
|
+
constructor() {
|
|
81
|
+
}
|
|
82
|
+
static get Optional() {
|
|
83
|
+
return TYPE.Number;
|
|
84
|
+
}
|
|
85
|
+
static get Required() {
|
|
86
|
+
return { type: TYPE.Number, required: true };
|
|
87
|
+
}
|
|
88
|
+
static RequiredWith(rules) {
|
|
89
|
+
return { type: TYPE.Number, required: true, validate: rules };
|
|
90
|
+
}
|
|
91
|
+
static OptionalWith(rules) {
|
|
92
|
+
return { type: TYPE.Number, required: false, validate: rules };
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
var ConduitNumber = ConduitNumberConstructor;
|
|
96
|
+
var ConduitBooleanConstructor = class {
|
|
97
|
+
// private to disallow creating other instances of this type
|
|
98
|
+
constructor() {
|
|
99
|
+
}
|
|
100
|
+
static get Optional() {
|
|
101
|
+
return TYPE.Boolean;
|
|
102
|
+
}
|
|
103
|
+
static get Required() {
|
|
104
|
+
return { type: TYPE.Boolean, required: true };
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
var ConduitBoolean = ConduitBooleanConstructor;
|
|
108
|
+
var ConduitDateConstructor = class {
|
|
109
|
+
// private to disallow creating other instances of this type
|
|
110
|
+
constructor() {
|
|
111
|
+
}
|
|
112
|
+
static get Optional() {
|
|
113
|
+
return TYPE.Date;
|
|
114
|
+
}
|
|
115
|
+
static get Required() {
|
|
116
|
+
return { type: TYPE.Date, required: true };
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
var ConduitDate = ConduitDateConstructor;
|
|
120
|
+
var ConduitObjectIdConstructor = class {
|
|
121
|
+
// private to disallow creating other instances of this type
|
|
122
|
+
constructor() {
|
|
123
|
+
}
|
|
124
|
+
static get Optional() {
|
|
125
|
+
return TYPE.ObjectId;
|
|
126
|
+
}
|
|
127
|
+
static get Required() {
|
|
128
|
+
return { type: TYPE.ObjectId, required: true };
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
var ConduitObjectId = ConduitObjectIdConstructor;
|
|
132
|
+
var ConduitJSONConstructor = class {
|
|
133
|
+
// private to disallow creating other instances of this type
|
|
134
|
+
constructor() {
|
|
135
|
+
}
|
|
136
|
+
static get Optional() {
|
|
137
|
+
return TYPE.JSON;
|
|
138
|
+
}
|
|
139
|
+
static get Required() {
|
|
140
|
+
return { type: TYPE.JSON, required: true };
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var ConduitJson = ConduitJSONConstructor;
|
|
144
|
+
var ConduitRelationConstructor = class {
|
|
145
|
+
// private to disallow creating other instances of this type
|
|
146
|
+
constructor() {
|
|
147
|
+
}
|
|
148
|
+
static Optional(model) {
|
|
149
|
+
return { type: TYPE.Relation, model, required: false };
|
|
150
|
+
}
|
|
151
|
+
static Required(model) {
|
|
152
|
+
return { type: TYPE.Relation, model, required: true };
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var ConduitRelation = ConduitRelationConstructor;
|
|
139
156
|
|
|
157
|
+
// src/helpers/GrpcHelper.ts
|
|
158
|
+
import {
|
|
159
|
+
loadPackageDefinition,
|
|
160
|
+
Server,
|
|
161
|
+
ServerCredentials
|
|
162
|
+
} from "@grpc/grpc-js";
|
|
163
|
+
import protoLoader from "@grpc/proto-loader";
|
|
140
164
|
function createServer(port) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
port,
|
|
154
|
-
});
|
|
155
|
-
}
|
|
165
|
+
const grpcServer = new Server({
|
|
166
|
+
"grpc.max_receive_message_length": 1024 * 1024 * 100,
|
|
167
|
+
"grpc.max_send_message_length": 1024 * 1024 * 100
|
|
168
|
+
});
|
|
169
|
+
return new Promise((resolve, reject) => {
|
|
170
|
+
grpcServer.bindAsync(port, ServerCredentials.createInsecure(), (err, port2) => {
|
|
171
|
+
if (err) {
|
|
172
|
+
reject(err);
|
|
173
|
+
} else {
|
|
174
|
+
resolve({
|
|
175
|
+
server: grpcServer,
|
|
176
|
+
port: port2
|
|
156
177
|
});
|
|
178
|
+
}
|
|
157
179
|
});
|
|
180
|
+
});
|
|
158
181
|
}
|
|
159
182
|
function addServiceToServer(server, protoPath, descriptorObject, functions) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
183
|
+
const packageDefinition = protoLoader.loadSync(protoPath, {
|
|
184
|
+
keepCase: true,
|
|
185
|
+
longs: String,
|
|
186
|
+
enums: String,
|
|
187
|
+
defaults: true,
|
|
188
|
+
oneofs: true
|
|
189
|
+
});
|
|
190
|
+
const protoDescriptor = loadPackageDefinition(packageDefinition);
|
|
191
|
+
const objs = descriptorObject.split(".");
|
|
192
|
+
let descObj = protoDescriptor;
|
|
193
|
+
objs.forEach((r) => {
|
|
194
|
+
descObj = descObj[r];
|
|
195
|
+
});
|
|
196
|
+
server.addService(descObj.service, functions);
|
|
174
197
|
}
|
|
175
198
|
|
|
199
|
+
// src/helpers/wrapGrpcFunctions.ts
|
|
200
|
+
import { createVerifier } from "fast-jwt";
|
|
201
|
+
import { status } from "@grpc/grpc-js";
|
|
202
|
+
import { ConduitGrpcSdk } from "@conduitplatform/grpc-sdk";
|
|
176
203
|
function wrapGrpcFunctions(functions, postponeRestart, noAuth = false) {
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
204
|
+
const grpcKey = process.env.GRPC_KEY;
|
|
205
|
+
const wrappedFunctions = {};
|
|
206
|
+
Object.keys(functions).forEach((name) => {
|
|
207
|
+
wrappedFunctions[name] = (call, callback) => {
|
|
208
|
+
ConduitGrpcSdk.Metrics?.increment("internal_grpc_requests_total");
|
|
209
|
+
postponeRestart();
|
|
210
|
+
if (grpcKey && !noAuth) {
|
|
211
|
+
const verify = createVerifier({ key: grpcKey });
|
|
212
|
+
const grpcToken = call.metadata.get("grpc-token")[0];
|
|
213
|
+
if (!grpcToken) {
|
|
214
|
+
return throwError(
|
|
215
|
+
callback,
|
|
216
|
+
"No gRPC protection token provided",
|
|
217
|
+
status.PERMISSION_DENIED
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
try {
|
|
221
|
+
const jwt = verify(grpcToken);
|
|
222
|
+
call.metadata.set("module-name", jwt.moduleName);
|
|
223
|
+
} catch {
|
|
224
|
+
return throwError(
|
|
225
|
+
callback,
|
|
226
|
+
"Failed to verify gRPC protection token. GRPC_KEY value differs",
|
|
227
|
+
status.PERMISSION_DENIED
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
let invoked;
|
|
232
|
+
try {
|
|
233
|
+
invoked = functions[name](call, callback);
|
|
234
|
+
} catch (error) {
|
|
235
|
+
return throwError(callback, error.message);
|
|
236
|
+
}
|
|
237
|
+
if (typeof invoked?.then === "function") {
|
|
238
|
+
invoked.then().catch((error) => {
|
|
239
|
+
return throwError(callback, error.message);
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
});
|
|
244
|
+
return wrappedFunctions;
|
|
212
245
|
}
|
|
213
246
|
function throwError(callback, message, code = status.INTERNAL) {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
247
|
+
if (callback) {
|
|
248
|
+
callback({ code, message });
|
|
249
|
+
}
|
|
218
250
|
}
|
|
219
251
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
252
|
+
// src/helpers/GrpcToParsedRouterRequest.ts
|
|
253
|
+
function createParsedRouterRequest(params, urlParams, queryParams, bodyParams, path5, headers, context, cookies) {
|
|
254
|
+
return {
|
|
255
|
+
request: {
|
|
256
|
+
params,
|
|
257
|
+
urlParams: urlParams ?? {},
|
|
258
|
+
queryParams: queryParams ?? {},
|
|
259
|
+
bodyParams: bodyParams ?? {},
|
|
260
|
+
path: path5 ?? "",
|
|
261
|
+
headers,
|
|
262
|
+
context: context ?? {},
|
|
263
|
+
cookies: cookies ?? {}
|
|
264
|
+
}
|
|
265
|
+
};
|
|
233
266
|
}
|
|
234
267
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
else {
|
|
286
|
-
if (!this.grpcServer) {
|
|
287
|
-
await this.wait(1000);
|
|
288
|
-
}
|
|
289
|
-
addServiceToServer(this.grpcServer, protoPath, protoDescription, functions);
|
|
290
|
-
return this;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
async wait(time) {
|
|
295
|
-
return new Promise((resolve, reject) => {
|
|
296
|
-
const timeout = setTimeout(() => {
|
|
297
|
-
clearTimeout(timeout);
|
|
298
|
-
resolve();
|
|
299
|
-
}, time);
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
async refresh() {
|
|
303
|
-
if (this.started) {
|
|
304
|
-
this.started = false;
|
|
305
|
-
if (this._useForce) {
|
|
306
|
-
this.grpcServer.forceShutdown();
|
|
307
|
-
}
|
|
308
|
-
else {
|
|
309
|
-
//gracefully shutdown so that there are no service disruption
|
|
310
|
-
await new Promise(resolve => this.grpcServer.tryShutdown(() => resolve()));
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
await this.createNewServer();
|
|
314
|
-
this._services.forEach(service => {
|
|
315
|
-
addServiceToServer(this.grpcServer, service.protoPath, service.protoDescription, service.functions);
|
|
316
|
-
});
|
|
317
|
-
if (!this.started && this.startedOnce) {
|
|
318
|
-
this.grpcServer.start();
|
|
319
|
-
this.started = true;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
scheduleRefresh() {
|
|
323
|
-
if (this.scheduledRestart) {
|
|
324
|
-
clearTimeout(this.scheduledRestart);
|
|
325
|
-
}
|
|
326
|
-
const self = this;
|
|
327
|
-
this.scheduledRestart = setTimeout(async () => {
|
|
328
|
-
ConduitGrpcSdk.Logger.log('Begin refresh');
|
|
329
|
-
await self.refresh();
|
|
330
|
-
ConduitGrpcSdk.Logger.log('Refresh complete');
|
|
331
|
-
clearTimeout(self.scheduledRestart);
|
|
332
|
-
self.scheduledRestart = undefined;
|
|
333
|
-
self._postponeRequests = 0;
|
|
334
|
-
}, 2000);
|
|
335
|
-
}
|
|
336
|
-
start() {
|
|
337
|
-
if (this.started) {
|
|
338
|
-
ConduitGrpcSdk.Logger.error('gRPC server is already running!');
|
|
339
|
-
return;
|
|
340
|
-
}
|
|
341
|
-
else if (!this.started && this.startedOnce) {
|
|
342
|
-
ConduitGrpcSdk.Logger.error('gRPC server is down for refresh!');
|
|
343
|
-
}
|
|
344
|
-
this.started = true;
|
|
345
|
-
this.startedOnce = true;
|
|
346
|
-
this.grpcServer?.start();
|
|
347
|
-
}
|
|
348
|
-
postponeRestart() {
|
|
349
|
-
if (!this.scheduledRestart || this._postponeRequests > 5)
|
|
350
|
-
return;
|
|
351
|
-
this._postponeRequests++;
|
|
268
|
+
// src/classes/GrpcServer.ts
|
|
269
|
+
import { isNil } from "lodash-es";
|
|
270
|
+
import { ConduitGrpcSdk as ConduitGrpcSdk2 } from "@conduitplatform/grpc-sdk";
|
|
271
|
+
var GrpcServer = class {
|
|
272
|
+
grpcServer;
|
|
273
|
+
started = false;
|
|
274
|
+
startedOnce = false;
|
|
275
|
+
_serviceNames = [];
|
|
276
|
+
scheduledRestart;
|
|
277
|
+
_postponeRequests = 0;
|
|
278
|
+
_useForce;
|
|
279
|
+
_services = [];
|
|
280
|
+
constructor(port) {
|
|
281
|
+
this._url = `0.0.0.0:${port ?? "5000"}`;
|
|
282
|
+
}
|
|
283
|
+
async waitForReady() {
|
|
284
|
+
while (!isNil(this.scheduledRestart)) {
|
|
285
|
+
await this.wait(200);
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
_url;
|
|
289
|
+
get url() {
|
|
290
|
+
return this._url;
|
|
291
|
+
}
|
|
292
|
+
async createNewServer(useForce = false) {
|
|
293
|
+
if (isNil(this._useForce)) {
|
|
294
|
+
this._useForce = useForce;
|
|
295
|
+
}
|
|
296
|
+
const serverResult = await createServer(this._url);
|
|
297
|
+
this.grpcServer = serverResult.server;
|
|
298
|
+
this._url = this._url.split(":")[0] + ":" + serverResult.port.toString();
|
|
299
|
+
return serverResult.port;
|
|
300
|
+
}
|
|
301
|
+
async addService(protoPath, protoDescription, functions, noAuth = false) {
|
|
302
|
+
functions = wrapGrpcFunctions(functions, this.postponeRestart.bind(this), noAuth);
|
|
303
|
+
if (this._serviceNames.indexOf(protoDescription) !== -1) {
|
|
304
|
+
ConduitGrpcSdk2.Logger.log("Service already exists, performing replace");
|
|
305
|
+
this._services[this._serviceNames.indexOf(protoDescription)] = {
|
|
306
|
+
protoPath,
|
|
307
|
+
protoDescription,
|
|
308
|
+
functions
|
|
309
|
+
};
|
|
310
|
+
this.scheduleRefresh();
|
|
311
|
+
return this;
|
|
312
|
+
} else {
|
|
313
|
+
this._services.push({ protoPath, protoDescription, functions });
|
|
314
|
+
this._serviceNames.push(protoDescription);
|
|
315
|
+
if (this.started) {
|
|
316
|
+
ConduitGrpcSdk2.Logger.log("Server already started, scheduling refresh..");
|
|
352
317
|
this.scheduleRefresh();
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
class RouteBuilder {
|
|
357
|
-
manager;
|
|
358
|
-
_options;
|
|
359
|
-
_returns;
|
|
360
|
-
_handler;
|
|
361
|
-
constructor(manager) {
|
|
362
|
-
this.manager = manager;
|
|
363
|
-
this._options = {};
|
|
364
|
-
}
|
|
365
|
-
method(action) {
|
|
366
|
-
this._options.action = action;
|
|
367
|
-
return this;
|
|
368
|
-
}
|
|
369
|
-
name(name) {
|
|
370
|
-
this._options.name = name;
|
|
371
318
|
return this;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
return this;
|
|
376
|
-
}
|
|
377
|
-
cacheControl(cache) {
|
|
378
|
-
this._options.cacheControl = cache;
|
|
379
|
-
return this;
|
|
380
|
-
}
|
|
381
|
-
middleware(middleware, allowDuplicates = false) {
|
|
382
|
-
if (!Array.isArray(middleware)) {
|
|
383
|
-
middleware = [middleware];
|
|
384
|
-
}
|
|
385
|
-
if (this._options.middlewares !== undefined &&
|
|
386
|
-
this._options.middlewares?.length !== 0) {
|
|
387
|
-
if (allowDuplicates) {
|
|
388
|
-
this._options.middlewares = this._options.middlewares?.concat(middleware);
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
// add to existing middlewares and filter out potential duplicates
|
|
392
|
-
this._options.middlewares = this._options.middlewares?.concat(middleware.filter(mid => this._options.middlewares?.indexOf(mid) === -1));
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
this._options.middlewares = middleware;
|
|
319
|
+
} else {
|
|
320
|
+
if (!this.grpcServer) {
|
|
321
|
+
await this.wait(1e3);
|
|
397
322
|
}
|
|
323
|
+
addServiceToServer(this.grpcServer, protoPath, protoDescription, functions);
|
|
398
324
|
return this;
|
|
325
|
+
}
|
|
399
326
|
}
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
this.
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
this.
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
}
|
|
327
|
+
}
|
|
328
|
+
async wait(time) {
|
|
329
|
+
return new Promise((resolve, reject) => {
|
|
330
|
+
const timeout = setTimeout(() => {
|
|
331
|
+
clearTimeout(timeout);
|
|
332
|
+
resolve();
|
|
333
|
+
}, time);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
async refresh() {
|
|
337
|
+
if (this.started) {
|
|
338
|
+
this.started = false;
|
|
339
|
+
if (this._useForce) {
|
|
340
|
+
this.grpcServer.forceShutdown();
|
|
341
|
+
} else {
|
|
342
|
+
await new Promise((resolve) => this.grpcServer.tryShutdown(() => resolve()));
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
await this.createNewServer();
|
|
346
|
+
this._services.forEach((service) => {
|
|
347
|
+
addServiceToServer(
|
|
348
|
+
this.grpcServer,
|
|
349
|
+
service.protoPath,
|
|
350
|
+
service.protoDescription,
|
|
351
|
+
service.functions
|
|
352
|
+
);
|
|
353
|
+
});
|
|
354
|
+
if (!this.started && this.startedOnce) {
|
|
355
|
+
this.grpcServer.start();
|
|
356
|
+
this.started = true;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
scheduleRefresh() {
|
|
360
|
+
if (this.scheduledRestart) {
|
|
361
|
+
clearTimeout(this.scheduledRestart);
|
|
362
|
+
}
|
|
363
|
+
const self = this;
|
|
364
|
+
this.scheduledRestart = setTimeout(async () => {
|
|
365
|
+
ConduitGrpcSdk2.Logger.log("Begin refresh");
|
|
366
|
+
await self.refresh();
|
|
367
|
+
ConduitGrpcSdk2.Logger.log("Refresh complete");
|
|
368
|
+
clearTimeout(self.scheduledRestart);
|
|
369
|
+
self.scheduledRestart = void 0;
|
|
370
|
+
self._postponeRequests = 0;
|
|
371
|
+
}, 2e3);
|
|
372
|
+
}
|
|
373
|
+
start() {
|
|
374
|
+
if (this.started) {
|
|
375
|
+
ConduitGrpcSdk2.Logger.error("gRPC server is already running!");
|
|
376
|
+
return;
|
|
377
|
+
} else if (!this.started && this.startedOnce) {
|
|
378
|
+
ConduitGrpcSdk2.Logger.error("gRPC server is down for refresh!");
|
|
379
|
+
}
|
|
380
|
+
this.started = true;
|
|
381
|
+
this.startedOnce = true;
|
|
382
|
+
this.grpcServer?.start();
|
|
383
|
+
}
|
|
384
|
+
postponeRestart() {
|
|
385
|
+
if (!this.scheduledRestart || this._postponeRequests > 5) return;
|
|
386
|
+
this._postponeRequests++;
|
|
387
|
+
this.scheduleRefresh();
|
|
388
|
+
}
|
|
389
|
+
};
|
|
457
390
|
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
391
|
+
// src/classes/ConduitServiceModule.ts
|
|
392
|
+
import path2 from "path";
|
|
393
|
+
import { EventEmitter } from "events";
|
|
394
|
+
import { camelCase } from "lodash-es";
|
|
395
|
+
import { status as status3 } from "@grpc/grpc-js";
|
|
396
|
+
import {
|
|
397
|
+
ConduitGrpcSdk as ConduitGrpcSdk4,
|
|
398
|
+
HealthCheckResponse_ServingStatus,
|
|
399
|
+
HealthCheckStatus
|
|
400
|
+
} from "@conduitplatform/grpc-sdk";
|
|
401
|
+
|
|
402
|
+
// src/routing/RouteBuilder.ts
|
|
403
|
+
import {
|
|
404
|
+
ConduitRouteReturnDefinition
|
|
405
|
+
} from "@conduitplatform/grpc-sdk";
|
|
406
|
+
var RouteBuilder = class {
|
|
407
|
+
constructor(manager) {
|
|
408
|
+
this.manager = manager;
|
|
409
|
+
this._options = {};
|
|
410
|
+
}
|
|
411
|
+
_options;
|
|
412
|
+
_returns;
|
|
413
|
+
_handler;
|
|
414
|
+
method(action) {
|
|
415
|
+
this._options.action = action;
|
|
416
|
+
return this;
|
|
417
|
+
}
|
|
418
|
+
name(name) {
|
|
419
|
+
this._options.name = name;
|
|
420
|
+
return this;
|
|
421
|
+
}
|
|
422
|
+
description(description) {
|
|
423
|
+
this._options.description = description;
|
|
424
|
+
return this;
|
|
425
|
+
}
|
|
426
|
+
cacheControl(cache) {
|
|
427
|
+
this._options.cacheControl = cache;
|
|
428
|
+
return this;
|
|
429
|
+
}
|
|
430
|
+
middleware(middleware, allowDuplicates = false) {
|
|
431
|
+
if (!Array.isArray(middleware)) {
|
|
432
|
+
middleware = [middleware];
|
|
433
|
+
}
|
|
434
|
+
if (this._options.middlewares !== void 0 && this._options.middlewares?.length !== 0) {
|
|
435
|
+
if (allowDuplicates) {
|
|
436
|
+
this._options.middlewares = this._options.middlewares?.concat(middleware);
|
|
437
|
+
} else {
|
|
438
|
+
this._options.middlewares = this._options.middlewares?.concat(
|
|
439
|
+
middleware.filter((mid) => this._options.middlewares?.indexOf(mid) === -1)
|
|
440
|
+
);
|
|
441
|
+
}
|
|
442
|
+
} else {
|
|
443
|
+
this._options.middlewares = middleware;
|
|
444
|
+
}
|
|
445
|
+
return this;
|
|
446
|
+
}
|
|
447
|
+
path(path5) {
|
|
448
|
+
this._options.path = path5;
|
|
449
|
+
return this;
|
|
450
|
+
}
|
|
451
|
+
queryParams(params) {
|
|
452
|
+
this._options.queryParams = params;
|
|
453
|
+
return this;
|
|
454
|
+
}
|
|
455
|
+
urlParams(params) {
|
|
456
|
+
this._options.urlParams = params;
|
|
457
|
+
return this;
|
|
458
|
+
}
|
|
459
|
+
bodyParams(params) {
|
|
460
|
+
this._options.bodyParams = params;
|
|
461
|
+
return this;
|
|
462
|
+
}
|
|
463
|
+
strictParams(strict) {
|
|
464
|
+
this._options.strictParams = strict;
|
|
465
|
+
return this;
|
|
466
|
+
}
|
|
467
|
+
rateLimit(maxRequests, resetInterval) {
|
|
468
|
+
this._options.rateLimit = { maxRequests, resetInterval };
|
|
469
|
+
return this;
|
|
470
|
+
}
|
|
471
|
+
return(name, fields) {
|
|
472
|
+
this._returns = new ConduitRouteReturnDefinition(name, fields);
|
|
473
|
+
return this;
|
|
474
|
+
}
|
|
475
|
+
handler(fn) {
|
|
476
|
+
this._handler = fn;
|
|
477
|
+
return this;
|
|
478
|
+
}
|
|
479
|
+
add() {
|
|
480
|
+
if (!this.manager) throw new Error("Builder not setup with manager");
|
|
481
|
+
if (!this._options) throw new Error("Cannot build route without options");
|
|
482
|
+
if (!this._options.action) throw new Error("Cannot build route without action");
|
|
483
|
+
if (!this._options.path) throw new Error("Cannot build route without action");
|
|
484
|
+
if (!this._returns) throw new Error("Cannot build route without return");
|
|
485
|
+
if (!this._handler) throw new Error("Cannot build route without handler");
|
|
486
|
+
this.manager.route(this._options, this._returns, this._handler);
|
|
487
|
+
}
|
|
488
|
+
build() {
|
|
489
|
+
if (!this._options) throw new Error("Cannot build route without options");
|
|
490
|
+
if (!this._options.action) throw new Error("Cannot build route without action");
|
|
491
|
+
if (!this._options.path) throw new Error("Cannot build route without action");
|
|
492
|
+
if (!this._returns) throw new Error("Cannot build route without return");
|
|
493
|
+
if (!this._handler) throw new Error("Cannot build route without handler");
|
|
494
|
+
return {
|
|
495
|
+
input: this._options,
|
|
496
|
+
returnType: this._returns,
|
|
497
|
+
handler: this._handler
|
|
498
|
+
};
|
|
499
|
+
}
|
|
493
500
|
};
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
501
|
+
|
|
502
|
+
// src/routing/wrapRouterFunctions.ts
|
|
503
|
+
import {
|
|
504
|
+
ConduitGrpcSdk as ConduitGrpcSdk3
|
|
505
|
+
} from "@conduitplatform/grpc-sdk";
|
|
506
|
+
import { status as status2 } from "@grpc/grpc-js";
|
|
507
|
+
var mapGrpcStatusToHttp = (code) => {
|
|
508
|
+
if (!code) return "200 (OK)";
|
|
509
|
+
switch (code) {
|
|
510
|
+
case status2.OK:
|
|
511
|
+
return `200 (OK)`;
|
|
512
|
+
case status2.CANCELLED:
|
|
513
|
+
return `499 (Cancelled)`;
|
|
514
|
+
case status2.INVALID_ARGUMENT:
|
|
515
|
+
return `400 (Invalid Argument)`;
|
|
516
|
+
case status2.DEADLINE_EXCEEDED:
|
|
517
|
+
return `504 (Deadline Exceeded)`;
|
|
518
|
+
case status2.NOT_FOUND:
|
|
519
|
+
return `404 (Not Found)`;
|
|
520
|
+
case status2.ALREADY_EXISTS:
|
|
521
|
+
return `409 (Already Exists)`;
|
|
522
|
+
case status2.PERMISSION_DENIED:
|
|
523
|
+
return `403 (Permission Denied)`;
|
|
524
|
+
case status2.UNAUTHENTICATED:
|
|
525
|
+
return `401 (Unauthenticated)`;
|
|
526
|
+
case status2.OUT_OF_RANGE:
|
|
527
|
+
return `416 (Out of Range)`;
|
|
528
|
+
case status2.FAILED_PRECONDITION:
|
|
529
|
+
return `412 (Failed Precondition)`;
|
|
530
|
+
case status2.ABORTED:
|
|
531
|
+
return `409 (Aborted)`;
|
|
532
|
+
case status2.INTERNAL:
|
|
533
|
+
return `416 (Internal)`;
|
|
534
|
+
case status2.UNAVAILABLE:
|
|
535
|
+
return `503 (Service Unavailable)`;
|
|
536
|
+
case status2.DATA_LOSS:
|
|
537
|
+
return `500 (Data Loss)`;
|
|
538
|
+
default:
|
|
539
|
+
return `500 (Unknown)`;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
function generateLog(routerType, routerRequest, requestReceive, call, status6) {
|
|
543
|
+
let log = "";
|
|
544
|
+
const latency = Date.now() - requestReceive;
|
|
545
|
+
if (routerRequest) {
|
|
546
|
+
log += `(${routerType}) Request: ${call.request.path}`;
|
|
547
|
+
} else {
|
|
548
|
+
const socketReq = call.request;
|
|
549
|
+
const socketLabel = socketReq.socketId ?? socketReq.socket;
|
|
550
|
+
log += `(${routerType}) Socket: ${socketReq.event} socket: ${socketLabel}`;
|
|
551
|
+
}
|
|
552
|
+
log += ` ${mapGrpcStatusToHttp(status6) ?? "200"} ${latency}ms`;
|
|
553
|
+
ConduitGrpcSdk3.Logger.log(log);
|
|
554
|
+
ConduitGrpcSdk3.Metrics?.set("grpc_request_latency_seconds", latency / 1e3);
|
|
555
|
+
ConduitGrpcSdk3.Metrics?.set(
|
|
556
|
+
`${routerType}_grpc_request_latency_seconds`,
|
|
557
|
+
latency / 1e3
|
|
558
|
+
);
|
|
559
|
+
const successStatus = !status6 || status6.toString().startsWith("2");
|
|
560
|
+
ConduitGrpcSdk3.Metrics?.increment(`${routerType}_grpc_response_statuses_total`, 1, {
|
|
561
|
+
success: successStatus ? "true" : "false"
|
|
562
|
+
});
|
|
513
563
|
}
|
|
514
564
|
function parseRequestData(data) {
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
}
|
|
565
|
+
if (data && data.length !== 0) {
|
|
566
|
+
return JSON.parse(data);
|
|
567
|
+
} else {
|
|
568
|
+
return {};
|
|
569
|
+
}
|
|
521
570
|
}
|
|
522
571
|
function parseResponseData(r, routerRequest, requestReceive, call, callback, responded, routerType) {
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
572
|
+
if (responded.did) return;
|
|
573
|
+
responded.did = true;
|
|
574
|
+
if (!r) {
|
|
575
|
+
callback({
|
|
576
|
+
code: status2.INTERNAL,
|
|
577
|
+
message: "Handler did not return a response"
|
|
578
|
+
});
|
|
579
|
+
}
|
|
580
|
+
if (routerRequest) {
|
|
581
|
+
if (typeof r === "string") {
|
|
582
|
+
callback(null, { result: r });
|
|
583
|
+
} else {
|
|
584
|
+
let respObject;
|
|
585
|
+
if (r.removeCookies || r.setCookies) {
|
|
586
|
+
respObject = {
|
|
587
|
+
removeCookies: r.removeCookies,
|
|
588
|
+
setCookies: r.setCookies
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
if (r.result || r.redirect) {
|
|
592
|
+
callback(null, {
|
|
593
|
+
...respObject,
|
|
594
|
+
redirect: r.redirect ?? void 0,
|
|
595
|
+
result: r.result ? typeof r.result === "string" ? r.result : JSON.stringify(r.result) : void 0
|
|
530
596
|
});
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
removeCookies: r.removeCookies,
|
|
541
|
-
setCookies: r.setCookies,
|
|
542
|
-
};
|
|
543
|
-
}
|
|
544
|
-
if (r.result || r.redirect) {
|
|
545
|
-
callback(null, {
|
|
546
|
-
...respObject,
|
|
547
|
-
redirect: r.redirect ?? undefined,
|
|
548
|
-
result: r.result
|
|
549
|
-
? typeof r.result === 'string'
|
|
550
|
-
? r.result
|
|
551
|
-
: JSON.stringify(r.result)
|
|
552
|
-
: undefined,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
callback(null, { ...respObject, result: JSON.stringify(r) });
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
else {
|
|
561
|
-
if (r.hasOwnProperty('data'))
|
|
562
|
-
r.data = JSON.stringify(r.data);
|
|
563
|
-
callback(null, r);
|
|
564
|
-
}
|
|
565
|
-
generateLog(routerType, routerRequest, requestReceive, call, undefined);
|
|
597
|
+
} else {
|
|
598
|
+
callback(null, { ...respObject, result: JSON.stringify(r) });
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
} else {
|
|
602
|
+
if (r.hasOwnProperty("data")) r.data = JSON.stringify(r.data);
|
|
603
|
+
callback(null, r);
|
|
604
|
+
}
|
|
605
|
+
generateLog(routerType, routerRequest, requestReceive, call, void 0);
|
|
566
606
|
}
|
|
567
607
|
function wrapRouterGrpcFunction(fun, routerType) {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
+
return (call, callback) => {
|
|
609
|
+
const requestReceive = Date.now();
|
|
610
|
+
let routerRequest = true;
|
|
611
|
+
ConduitGrpcSdk3.Metrics?.increment(`${routerType}_grpc_requests_total`);
|
|
612
|
+
try {
|
|
613
|
+
call.request.context = parseRequestData(call.request.context);
|
|
614
|
+
call.request.params = parseRequestData(call.request.params);
|
|
615
|
+
call.request.cookies = parseRequestData(call.request.cookies);
|
|
616
|
+
call.request.urlParams = parseRequestData(call.request.urlParams);
|
|
617
|
+
call.request.queryParams = parseRequestData(call.request.queryParams);
|
|
618
|
+
call.request.bodyParams = parseRequestData(call.request.bodyParams);
|
|
619
|
+
routerRequest = !call.request.hasOwnProperty("event");
|
|
620
|
+
if (routerRequest) {
|
|
621
|
+
call.request.headers = parseRequestData(call.request.headers);
|
|
622
|
+
}
|
|
623
|
+
} catch (e) {
|
|
624
|
+
ConduitGrpcSdk3.Metrics?.increment(`${routerType}_grpc_errors_total`);
|
|
625
|
+
generateLog(routerType, routerRequest, requestReceive, call, status2.INTERNAL);
|
|
626
|
+
ConduitGrpcSdk3.Logger.error(e.message ?? "Something went wrong");
|
|
627
|
+
return callback({
|
|
628
|
+
code: status2.INTERNAL,
|
|
629
|
+
message: e.message ?? "Something went wrong"
|
|
630
|
+
});
|
|
631
|
+
}
|
|
632
|
+
const responded = { did: false };
|
|
633
|
+
fun(call, (r) => {
|
|
634
|
+
parseResponseData(
|
|
635
|
+
r,
|
|
636
|
+
routerRequest,
|
|
637
|
+
requestReceive,
|
|
638
|
+
call,
|
|
639
|
+
callback,
|
|
640
|
+
responded,
|
|
641
|
+
routerType
|
|
642
|
+
);
|
|
643
|
+
}).then(
|
|
644
|
+
(r) => parseResponseData(
|
|
645
|
+
r,
|
|
646
|
+
routerRequest,
|
|
647
|
+
requestReceive,
|
|
648
|
+
call,
|
|
649
|
+
callback,
|
|
650
|
+
responded,
|
|
651
|
+
routerType
|
|
652
|
+
)
|
|
653
|
+
).catch((error) => {
|
|
654
|
+
ConduitGrpcSdk3.Metrics?.increment(`${routerType}_grpc_errors_total`);
|
|
655
|
+
generateLog(
|
|
656
|
+
routerType,
|
|
657
|
+
routerRequest,
|
|
658
|
+
requestReceive,
|
|
659
|
+
call,
|
|
660
|
+
error.code ?? status2.INTERNAL
|
|
661
|
+
);
|
|
662
|
+
ConduitGrpcSdk3.Logger.error(
|
|
663
|
+
error.debugLogInfo ?? error.message ?? "Something went wrong"
|
|
664
|
+
);
|
|
665
|
+
callback({
|
|
666
|
+
code: error.code ?? status2.INTERNAL,
|
|
667
|
+
message: error.details ?? error.message ?? "Something went wrong"
|
|
668
|
+
});
|
|
669
|
+
});
|
|
670
|
+
};
|
|
608
671
|
}
|
|
609
672
|
|
|
673
|
+
// src/routing/RoutingUtilities.ts
|
|
610
674
|
function wrapFunctionsAsync(functions, routerType) {
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
675
|
+
const modifiedFunctions = {};
|
|
676
|
+
Object.keys(functions).forEach((key) => {
|
|
677
|
+
modifiedFunctions[key] = wrapRouterGrpcFunction(functions[key], routerType);
|
|
678
|
+
});
|
|
679
|
+
return modifiedFunctions;
|
|
616
680
|
}
|
|
617
681
|
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
}
|
|
682
|
+
// src/routing/RoutingManager.ts
|
|
683
|
+
import {
|
|
684
|
+
Admin,
|
|
685
|
+
ConduitRouteActions as ConduitRouteActions2
|
|
686
|
+
} from "@conduitplatform/grpc-sdk";
|
|
687
|
+
var RoutingManager = class _RoutingManager {
|
|
688
|
+
constructor(_router, _server) {
|
|
689
|
+
this._router = _router;
|
|
690
|
+
this._server = _server;
|
|
691
|
+
if (_router instanceof Admin) {
|
|
692
|
+
this.isAdmin = true;
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
_moduleRoutes = {};
|
|
696
|
+
static ClientController;
|
|
697
|
+
static AdminController;
|
|
698
|
+
_routeHandlers = {};
|
|
699
|
+
isAdmin = false;
|
|
700
|
+
get(path5) {
|
|
701
|
+
return new RouteBuilder(this).method(ConduitRouteActions2.GET).path(path5);
|
|
702
|
+
}
|
|
703
|
+
post(path5) {
|
|
704
|
+
return new RouteBuilder(this).method(ConduitRouteActions2.POST).path(path5);
|
|
705
|
+
}
|
|
706
|
+
delete(path5) {
|
|
707
|
+
return new RouteBuilder(this).method(ConduitRouteActions2.DELETE).path(path5);
|
|
708
|
+
}
|
|
709
|
+
update(path5) {
|
|
710
|
+
return new RouteBuilder(this).method(ConduitRouteActions2.UPDATE).path(path5);
|
|
711
|
+
}
|
|
712
|
+
patch(path5) {
|
|
713
|
+
return new RouteBuilder(this).method(ConduitRouteActions2.PATCH).path(path5);
|
|
714
|
+
}
|
|
715
|
+
clear() {
|
|
716
|
+
this._moduleRoutes = {};
|
|
717
|
+
this._routeHandlers = {};
|
|
718
|
+
}
|
|
719
|
+
middleware(input, handler) {
|
|
720
|
+
const routeObject = this.parseRouteObject({
|
|
721
|
+
options: input,
|
|
722
|
+
grpcFunction: input.name
|
|
723
|
+
});
|
|
724
|
+
this._moduleRoutes[routeObject.grpcFunction] = routeObject;
|
|
725
|
+
this._routeHandlers[routeObject.grpcFunction] = handler;
|
|
726
|
+
}
|
|
727
|
+
route(input, type, handler) {
|
|
728
|
+
const routeObject = this.parseRouteObject({
|
|
729
|
+
options: input,
|
|
730
|
+
returns: {
|
|
731
|
+
name: type.name,
|
|
732
|
+
fields: JSON.stringify(type.fields)
|
|
733
|
+
},
|
|
734
|
+
grpcFunction: this.generateGrpcName(input)
|
|
735
|
+
});
|
|
736
|
+
this._moduleRoutes[routeObject.grpcFunction] = routeObject;
|
|
737
|
+
this._routeHandlers[routeObject.grpcFunction] = handler;
|
|
738
|
+
}
|
|
739
|
+
socket(input, events) {
|
|
740
|
+
const eventsObj = {};
|
|
741
|
+
const routeObject = this.parseRouteObject({
|
|
742
|
+
options: input,
|
|
743
|
+
events: ""
|
|
744
|
+
});
|
|
745
|
+
let primary;
|
|
746
|
+
Object.keys(events).forEach((eventName) => {
|
|
747
|
+
if (!primary) primary = eventName;
|
|
748
|
+
const event = events[eventName];
|
|
749
|
+
eventsObj[eventName] = {
|
|
750
|
+
grpcFunction: eventName,
|
|
751
|
+
params: JSON.stringify(event.params),
|
|
752
|
+
returns: {
|
|
753
|
+
name: event.returnType?.name ?? "",
|
|
754
|
+
fields: JSON.stringify(event.returnType?.fields)
|
|
755
|
+
}
|
|
756
|
+
};
|
|
757
|
+
this._routeHandlers[eventName] = event.handler;
|
|
758
|
+
});
|
|
759
|
+
routeObject.events = JSON.stringify(eventsObj);
|
|
760
|
+
this._moduleRoutes[primary] = routeObject;
|
|
761
|
+
}
|
|
762
|
+
async registerRoutes() {
|
|
763
|
+
if (Object.keys(this._routeHandlers).length === 0) return;
|
|
764
|
+
const modifiedFunctions = wrapFunctionsAsync(this._routeHandlers, this.isAdmin ? "admin" : "client");
|
|
765
|
+
if (this.isAdmin) {
|
|
766
|
+
_RoutingManager.AdminController.setRoutes(modifiedFunctions);
|
|
767
|
+
} else {
|
|
768
|
+
_RoutingManager.ClientController.setRoutes(modifiedFunctions);
|
|
769
|
+
}
|
|
770
|
+
const paths = Object.values(this._moduleRoutes);
|
|
771
|
+
return this._router.register(paths);
|
|
772
|
+
}
|
|
773
|
+
generateGrpcName(options) {
|
|
774
|
+
if (options.name) {
|
|
775
|
+
return options.name.charAt(0).toUpperCase() + options.name.slice(1);
|
|
776
|
+
} else {
|
|
777
|
+
const name = options.action.charAt(0) + options.action.slice(1).toLowerCase() + this.extractNameFromPath(options.path);
|
|
778
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
extractNameFromPath(path5) {
|
|
782
|
+
path5 = path5.replace(/[-:]/g, "/");
|
|
783
|
+
return path5.split("/").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
784
|
+
}
|
|
785
|
+
parseRouteObject(routeObject) {
|
|
786
|
+
if (!routeObject.options.middlewares) {
|
|
787
|
+
routeObject.options.middlewares = [];
|
|
788
|
+
}
|
|
789
|
+
for (const option in routeObject.options) {
|
|
790
|
+
if (!routeObject.options.hasOwnProperty(option)) continue;
|
|
791
|
+
if (option === "middlewares" || option === "errors") continue;
|
|
792
|
+
if (typeof routeObject.options[option] === "string" || routeObject.options[option] instanceof String)
|
|
793
|
+
continue;
|
|
794
|
+
if (typeof routeObject.options[option] === "boolean" || routeObject.options[option] instanceof Boolean)
|
|
795
|
+
continue;
|
|
796
|
+
routeObject.options[option] = JSON.stringify(routeObject.options[option]);
|
|
797
|
+
}
|
|
798
|
+
return routeObject;
|
|
799
|
+
}
|
|
800
|
+
};
|
|
692
801
|
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
802
|
+
// src/classes/ConduitServiceModule.ts
|
|
803
|
+
var ConduitServiceModule = class {
|
|
804
|
+
_moduleName;
|
|
805
|
+
_serviceName;
|
|
806
|
+
_address;
|
|
807
|
+
// external address:port of service (LoadBalancer)
|
|
808
|
+
grpcServer;
|
|
809
|
+
events = new EventEmitter();
|
|
810
|
+
_serviceHealthState = HealthCheckStatus.SERVING;
|
|
811
|
+
// default for health-agnostic modules
|
|
812
|
+
constructor(moduleName) {
|
|
813
|
+
this._moduleName = camelCase(moduleName);
|
|
814
|
+
}
|
|
815
|
+
_registered = false;
|
|
816
|
+
set registered(registered) {
|
|
817
|
+
this._registered = registered;
|
|
818
|
+
}
|
|
819
|
+
_port;
|
|
820
|
+
// port to bring up gRPC service
|
|
821
|
+
get port() {
|
|
822
|
+
return this._port;
|
|
823
|
+
}
|
|
824
|
+
_grpcSdk;
|
|
825
|
+
get grpcSdk() {
|
|
826
|
+
if (!this._grpcSdk) throw new Error("grpcSdk not defined yet");
|
|
827
|
+
return this._grpcSdk;
|
|
828
|
+
}
|
|
829
|
+
set grpcSdk(grpcSdk) {
|
|
830
|
+
if (this._grpcSdk) throw new Error("grpcSdk already defined");
|
|
831
|
+
this._grpcSdk = grpcSdk;
|
|
832
|
+
}
|
|
833
|
+
get healthState() {
|
|
834
|
+
return this._serviceHealthState;
|
|
835
|
+
}
|
|
836
|
+
updateHealth(state, init = false) {
|
|
837
|
+
if (state === HealthCheckStatus.UNKNOWN && !init || state === HealthCheckStatus.SERVICE_UNKNOWN) {
|
|
838
|
+
throw new Error(
|
|
839
|
+
`Cannot explicitly set gRPC health state to ${HealthCheckStatus[state]}`
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
if (!init) {
|
|
843
|
+
ConduitGrpcSdk4.Metrics?.set(
|
|
844
|
+
"module_health_state",
|
|
845
|
+
state === HealthCheckStatus.SERVING ? 1 : 0
|
|
846
|
+
);
|
|
847
|
+
}
|
|
848
|
+
if (init) {
|
|
849
|
+
this._serviceHealthState = state;
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
if (this._serviceHealthState !== state) {
|
|
853
|
+
this._serviceHealthState = state;
|
|
854
|
+
if (this._registered) {
|
|
855
|
+
this.events.emit(`grpc-health-change:${this._serviceName}`, state);
|
|
856
|
+
this._grpcSdk?.config.moduleHealthProbe(this._moduleName, this._address);
|
|
857
|
+
}
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
healthCheck(call, callback) {
|
|
861
|
+
const service = call.request.service.substring(call.request.service.indexOf(".") + 1);
|
|
862
|
+
if (service && service !== this._serviceName) {
|
|
863
|
+
callback(null, {
|
|
864
|
+
status: HealthCheckStatus.SERVICE_UNKNOWN
|
|
865
|
+
});
|
|
866
|
+
} else {
|
|
867
|
+
if (!this._registered) {
|
|
868
|
+
return callback(null, {
|
|
869
|
+
status: HealthCheckResponse_ServingStatus.UNKNOWN
|
|
736
870
|
});
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
871
|
+
}
|
|
872
|
+
callback(null, {
|
|
873
|
+
status: this._serviceHealthState
|
|
874
|
+
});
|
|
875
|
+
}
|
|
876
|
+
}
|
|
877
|
+
healthWatch(call) {
|
|
878
|
+
const service = call.request.service.substring(call.request.service.indexOf(".") + 1);
|
|
879
|
+
if (service && service !== this._serviceName) {
|
|
880
|
+
call.write({
|
|
881
|
+
status: HealthCheckStatus.SERVICE_UNKNOWN
|
|
882
|
+
});
|
|
883
|
+
} else {
|
|
884
|
+
this.events.on(
|
|
885
|
+
`grpc-health-change:${this._serviceName}`,
|
|
886
|
+
(status6) => {
|
|
887
|
+
call.write({
|
|
888
|
+
status: status6
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
/** Override to declare which resource types this module can export/import. Default: none. */
|
|
895
|
+
getExportableResources() {
|
|
896
|
+
return [];
|
|
897
|
+
}
|
|
898
|
+
/** Override to export resources. resourceTypes empty = export all. Default: empty. */
|
|
899
|
+
async exportResources(_resourceTypes) {
|
|
900
|
+
return {};
|
|
901
|
+
}
|
|
902
|
+
/** Override to import resources. Default: no-op. */
|
|
903
|
+
async importResources(_resources) {
|
|
904
|
+
return {};
|
|
905
|
+
}
|
|
906
|
+
getExportableResourcesHandler(_call, callback) {
|
|
907
|
+
try {
|
|
908
|
+
const resources = this.getExportableResources();
|
|
909
|
+
callback(null, { resources: JSON.stringify(resources) });
|
|
910
|
+
} catch (e) {
|
|
911
|
+
callback({ code: status3.INTERNAL, message: e.message });
|
|
751
912
|
}
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
returns: {
|
|
774
|
-
name: event.returnType?.name ?? '',
|
|
775
|
-
fields: JSON.stringify(event.returnType?.fields),
|
|
776
|
-
},
|
|
777
|
-
};
|
|
778
|
-
this._routeHandlers[eventName] = event.handler;
|
|
913
|
+
}
|
|
914
|
+
async exportResourcesHandler(call, callback) {
|
|
915
|
+
try {
|
|
916
|
+
const resourceTypes = call.request.resourceTypes ?? [];
|
|
917
|
+
const data = await this.exportResources(resourceTypes);
|
|
918
|
+
callback(null, { data: JSON.stringify(data) });
|
|
919
|
+
} catch (e) {
|
|
920
|
+
callback({ code: status3.INTERNAL, message: e.message });
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
async importResourcesHandler(call, callback) {
|
|
924
|
+
try {
|
|
925
|
+
const data = JSON.parse(call.request.data);
|
|
926
|
+
const result = await this.importResources(data);
|
|
927
|
+
callback(null, { result: JSON.stringify(result) });
|
|
928
|
+
} catch (e) {
|
|
929
|
+
const err = e;
|
|
930
|
+
if (err instanceof SyntaxError) {
|
|
931
|
+
callback({
|
|
932
|
+
code: status3.INVALID_ARGUMENT,
|
|
933
|
+
message: "Invalid JSON in import data"
|
|
779
934
|
});
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
935
|
+
} else {
|
|
936
|
+
callback({ code: status3.INTERNAL, message: err.message });
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
async addHealthCheckService() {
|
|
941
|
+
await this.grpcServer.addService(
|
|
942
|
+
path2.resolve(__dirname, "./grpc_health_check.proto"),
|
|
943
|
+
"grpc.health.v1.Health",
|
|
944
|
+
{
|
|
945
|
+
Check: this.healthCheck.bind(this),
|
|
946
|
+
Watch: this.healthWatch.bind(this)
|
|
947
|
+
},
|
|
948
|
+
true
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
async addModuleService() {
|
|
952
|
+
await this.grpcServer.addService(
|
|
953
|
+
path2.resolve(__dirname, "./module.proto"),
|
|
954
|
+
"conduit.module.v1.ConduitModule",
|
|
955
|
+
{
|
|
956
|
+
SetConfig: this.setConfig.bind(this),
|
|
957
|
+
GetExportableResources: this.getExportableResourcesHandler.bind(this),
|
|
958
|
+
ExportResources: this.exportResourcesHandler.bind(this),
|
|
959
|
+
ImportResources: this.importResourcesHandler.bind(this)
|
|
960
|
+
}
|
|
961
|
+
);
|
|
962
|
+
await this.grpcServer.addService(
|
|
963
|
+
path2.resolve(__dirname, "./module.proto"),
|
|
964
|
+
"conduit.module.v1.ClientRouter",
|
|
965
|
+
{
|
|
966
|
+
Route: RoutingManager.ClientController.handleRequest.bind(
|
|
967
|
+
RoutingManager.ClientController
|
|
968
|
+
),
|
|
969
|
+
SocketRoute: RoutingManager.ClientController.handleRequest.bind(
|
|
970
|
+
RoutingManager.ClientController
|
|
971
|
+
)
|
|
972
|
+
}
|
|
973
|
+
);
|
|
974
|
+
await this.grpcServer.addService(
|
|
975
|
+
path2.resolve(__dirname, "./module.proto"),
|
|
976
|
+
"conduit.module.v1.AdminRouter",
|
|
977
|
+
{
|
|
978
|
+
Route: RoutingManager.AdminController.handleRequest.bind(
|
|
979
|
+
RoutingManager.AdminController
|
|
980
|
+
),
|
|
981
|
+
SocketRoute: RoutingManager.AdminController.handleRequest.bind(
|
|
982
|
+
RoutingManager.AdminController
|
|
983
|
+
)
|
|
984
|
+
}
|
|
985
|
+
);
|
|
986
|
+
}
|
|
987
|
+
};
|
|
831
988
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
get port() {
|
|
850
|
-
return this._port;
|
|
851
|
-
}
|
|
852
|
-
_grpcSdk;
|
|
853
|
-
get grpcSdk() {
|
|
854
|
-
if (!this._grpcSdk)
|
|
855
|
-
throw new Error('grpcSdk not defined yet');
|
|
856
|
-
return this._grpcSdk;
|
|
857
|
-
}
|
|
858
|
-
set grpcSdk(grpcSdk) {
|
|
859
|
-
if (this._grpcSdk)
|
|
860
|
-
throw new Error('grpcSdk already defined');
|
|
861
|
-
this._grpcSdk = grpcSdk;
|
|
862
|
-
}
|
|
863
|
-
get healthState() {
|
|
864
|
-
return this._serviceHealthState;
|
|
865
|
-
}
|
|
866
|
-
updateHealth(state, init = false) {
|
|
867
|
-
if ((state === HealthCheckStatus.UNKNOWN && !init) ||
|
|
868
|
-
state === HealthCheckStatus.SERVICE_UNKNOWN) {
|
|
869
|
-
throw new Error(`Cannot explicitly set gRPC health state to ${HealthCheckStatus[state]}`);
|
|
870
|
-
}
|
|
871
|
-
if (!init) {
|
|
872
|
-
ConduitGrpcSdk.Metrics?.set('module_health_state', state === HealthCheckStatus.SERVING ? 1 : 0);
|
|
873
|
-
}
|
|
874
|
-
if (init) {
|
|
875
|
-
this._serviceHealthState = state;
|
|
876
|
-
return;
|
|
877
|
-
}
|
|
878
|
-
if (this._serviceHealthState !== state) {
|
|
879
|
-
this._serviceHealthState = state;
|
|
880
|
-
// do not emit health updates until registered
|
|
881
|
-
if (this._registered) {
|
|
882
|
-
this.events.emit(`grpc-health-change:${this._serviceName}`, state);
|
|
883
|
-
this._grpcSdk?.config.moduleHealthProbe(this._moduleName, this._address);
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
healthCheck(call, callback) {
|
|
888
|
-
const service = call.request.service.substring(call.request.service.indexOf('.') + 1);
|
|
889
|
-
if (service && service !== this._serviceName) {
|
|
890
|
-
callback(null, {
|
|
891
|
-
status: HealthCheckStatus.SERVICE_UNKNOWN,
|
|
892
|
-
});
|
|
893
|
-
}
|
|
894
|
-
else {
|
|
895
|
-
if (!this._registered) {
|
|
896
|
-
return callback(null, {
|
|
897
|
-
status: HealthCheckResponse_ServingStatus.UNKNOWN,
|
|
898
|
-
});
|
|
899
|
-
}
|
|
900
|
-
callback(null, {
|
|
901
|
-
status: this._serviceHealthState,
|
|
902
|
-
});
|
|
903
|
-
}
|
|
904
|
-
}
|
|
905
|
-
healthWatch(call) {
|
|
906
|
-
const service = call.request.service.substring(call.request.service.indexOf('.') + 1);
|
|
907
|
-
if (service && service !== this._serviceName) {
|
|
908
|
-
call.write({
|
|
909
|
-
status: HealthCheckStatus.SERVICE_UNKNOWN,
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
else {
|
|
913
|
-
this.events.on(`grpc-health-change:${this._serviceName}`, (status) => {
|
|
914
|
-
call.write({
|
|
915
|
-
status: status,
|
|
916
|
-
});
|
|
917
|
-
});
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
async addHealthCheckService() {
|
|
921
|
-
await this.grpcServer.addService(path.resolve(__dirname$1, './grpc_health_check.proto'), 'grpc.health.v1.Health', {
|
|
922
|
-
Check: this.healthCheck.bind(this),
|
|
923
|
-
Watch: this.healthWatch.bind(this),
|
|
924
|
-
}, true);
|
|
925
|
-
}
|
|
926
|
-
async addModuleService() {
|
|
927
|
-
await this.grpcServer.addService(path.resolve(__dirname$1, './module.proto'), 'conduit.module.v1.ConduitModule', {
|
|
928
|
-
SetConfig: this.setConfig.bind(this),
|
|
929
|
-
});
|
|
930
|
-
await this.grpcServer.addService(path.resolve(__dirname$1, './module.proto'), 'conduit.module.v1.ClientRouter', {
|
|
931
|
-
Route: RoutingManager.ClientController.handleRequest.bind(RoutingManager.ClientController),
|
|
932
|
-
SocketRoute: RoutingManager.ClientController.handleRequest.bind(RoutingManager.ClientController),
|
|
933
|
-
});
|
|
934
|
-
await this.grpcServer.addService(path.resolve(__dirname$1, './module.proto'), 'conduit.module.v1.AdminRouter', {
|
|
935
|
-
Route: RoutingManager.AdminController.handleRequest.bind(RoutingManager.AdminController),
|
|
936
|
-
SocketRoute: RoutingManager.AdminController.handleRequest.bind(RoutingManager.AdminController),
|
|
937
|
-
});
|
|
938
|
-
}
|
|
939
|
-
}
|
|
989
|
+
// src/classes/ConfigController.ts
|
|
990
|
+
var ConfigController = class _ConfigController {
|
|
991
|
+
static instance;
|
|
992
|
+
_config;
|
|
993
|
+
get config() {
|
|
994
|
+
return Object.assign({}, this._config);
|
|
995
|
+
}
|
|
996
|
+
set config(config) {
|
|
997
|
+
this._config = config;
|
|
998
|
+
}
|
|
999
|
+
static getInstance() {
|
|
1000
|
+
if (!_ConfigController.instance) {
|
|
1001
|
+
_ConfigController.instance = new _ConfigController();
|
|
1002
|
+
}
|
|
1003
|
+
return _ConfigController.instance;
|
|
1004
|
+
}
|
|
1005
|
+
};
|
|
940
1006
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
// return copy of config and not the original object;
|
|
946
|
-
return Object.assign({}, this._config);
|
|
947
|
-
}
|
|
948
|
-
set config(config) {
|
|
949
|
-
this._config = config;
|
|
950
|
-
}
|
|
951
|
-
static getInstance() {
|
|
952
|
-
if (!ConfigController.instance) {
|
|
953
|
-
ConfigController.instance = new ConfigController();
|
|
954
|
-
}
|
|
955
|
-
return ConfigController.instance;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
1007
|
+
// src/ManagedModule.ts
|
|
1008
|
+
import { kebabCase } from "lodash-es";
|
|
1009
|
+
import { status as status5 } from "@grpc/grpc-js";
|
|
1010
|
+
import convict from "convict";
|
|
958
1011
|
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
1012
|
+
// src/interfaces/ModuleLifecycleStage.ts
|
|
1013
|
+
var ModuleLifecycleStage = /* @__PURE__ */ ((ModuleLifecycleStage2) => {
|
|
1014
|
+
ModuleLifecycleStage2["CREATE_GRPC"] = "creat-grpc-server";
|
|
1015
|
+
ModuleLifecycleStage2["PRE_SERVER_START"] = "pre-grpc-start";
|
|
1016
|
+
ModuleLifecycleStage2["SERVER_STARTED"] = "grpc-server-started";
|
|
1017
|
+
ModuleLifecycleStage2["PRE_REGISTER"] = "pre-register";
|
|
1018
|
+
ModuleLifecycleStage2["POST_REGISTER"] = "post-register";
|
|
1019
|
+
return ModuleLifecycleStage2;
|
|
1020
|
+
})(ModuleLifecycleStage || {});
|
|
967
1021
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1022
|
+
// src/ManagedModule.ts
|
|
1023
|
+
import {
|
|
1024
|
+
ConduitGrpcSdk as ConduitGrpcSdk8
|
|
1025
|
+
} from "@conduitplatform/grpc-sdk";
|
|
1026
|
+
|
|
1027
|
+
// src/utilities/initializeSdk.ts
|
|
1028
|
+
import {
|
|
1029
|
+
ConduitGrpcSdk as ConduitGrpcSdk7,
|
|
1030
|
+
HealthCheckStatus as HealthCheckStatus2
|
|
1031
|
+
} from "@conduitplatform/grpc-sdk";
|
|
1032
|
+
|
|
1033
|
+
// src/logging/index.ts
|
|
1034
|
+
import {
|
|
1035
|
+
ConduitGrpcSdk as ConduitGrpcSdk5
|
|
1036
|
+
} from "@conduitplatform/grpc-sdk";
|
|
1037
|
+
import winston, { format } from "winston";
|
|
1038
|
+
import { isEmpty } from "lodash-es";
|
|
1039
|
+
import { get } from "http";
|
|
1040
|
+
import LokiTransport from "winston-loki";
|
|
1041
|
+
|
|
1042
|
+
// src/utilities/linearBackoffTimeout.ts
|
|
1043
|
+
import { clearTimeout as clearTimeout2 } from "timers";
|
|
1044
|
+
function linearBackoffTimeout(onTry, delay, reps, onFailure) {
|
|
1045
|
+
const nextRep = () => reps === void 0 || --reps > 0;
|
|
1046
|
+
let stopSignal = false;
|
|
1047
|
+
const stop = () => {
|
|
1048
|
+
stopSignal = true;
|
|
1049
|
+
};
|
|
1050
|
+
let timeout;
|
|
1051
|
+
const invoker = async () => {
|
|
1052
|
+
if (stopSignal) {
|
|
1053
|
+
return timeout ? clearTimeout2(timeout) : null;
|
|
1054
|
+
}
|
|
1055
|
+
delay = Math.floor(delay * 2);
|
|
1056
|
+
if (delay > 0 && nextRep()) {
|
|
1057
|
+
timeout = setTimeout(invoker, delay);
|
|
1058
|
+
onTry(stop);
|
|
1059
|
+
} else {
|
|
1060
|
+
timeout = null;
|
|
1061
|
+
onFailure && onFailure();
|
|
1062
|
+
}
|
|
1063
|
+
};
|
|
1064
|
+
timeout = setTimeout(invoker, delay);
|
|
1065
|
+
return {
|
|
1066
|
+
clear: () => {
|
|
1067
|
+
if (timeout) {
|
|
1068
|
+
clearTimeout2(timeout);
|
|
1069
|
+
timeout = null;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
};
|
|
1073
|
+
}
|
|
1074
|
+
async function linearBackoffTimeoutAsync(onTry, delay, reps, onFailure, startNow = false) {
|
|
1075
|
+
const nextRep = () => reps === void 0 || --reps > 0;
|
|
1076
|
+
const invoker = async () => {
|
|
1077
|
+
delay = Math.floor(delay * 2);
|
|
1078
|
+
if (delay > 0 && nextRep()) {
|
|
1079
|
+
if (await onTry()) setTimeout(invoker, delay);
|
|
1080
|
+
} else {
|
|
1081
|
+
onFailure && await onFailure();
|
|
1082
|
+
}
|
|
1083
|
+
};
|
|
1084
|
+
setTimeout(invoker, startNow ? 0 : delay);
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
// src/logging/index.ts
|
|
1088
|
+
var processMeta = (meta) => {
|
|
1089
|
+
if (Array.isArray(meta)) {
|
|
1090
|
+
return meta.reduce(
|
|
1091
|
+
(message, meta2) => `${message}
|
|
1092
|
+
${JSON.stringify(meta2, null, 2)}`,
|
|
1093
|
+
""
|
|
1094
|
+
);
|
|
1095
|
+
} else {
|
|
1096
|
+
return JSON.stringify(meta, null, 2);
|
|
1097
|
+
}
|
|
975
1098
|
};
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1099
|
+
var createFormat = (logMeta = true) => {
|
|
1100
|
+
return format.combine(
|
|
1101
|
+
format.printf((info) => {
|
|
1102
|
+
if (info.stack) {
|
|
1103
|
+
return `[${info.timestamp}] [${info.level.toUpperCase()}]: ${info.message} ${info.stack}`;
|
|
1104
|
+
}
|
|
1105
|
+
if (logMeta) {
|
|
1106
|
+
return `[${info.timestamp}] [${info.level.toUpperCase()}]: ${info.message} ${!isEmpty(info.meta) ? processMeta(info.meta) : ""}`;
|
|
1107
|
+
} else {
|
|
1108
|
+
return `[${info.timestamp}] [${info.level.toUpperCase()}]: ${info.message}`;
|
|
1109
|
+
}
|
|
1110
|
+
})
|
|
1111
|
+
);
|
|
989
1112
|
};
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1113
|
+
var defaultTransport = new winston.transports.Console({
|
|
1114
|
+
level: "debug",
|
|
1115
|
+
format: format.combine(
|
|
1116
|
+
format.prettyPrint(),
|
|
1117
|
+
format.errors({ stack: true }),
|
|
1118
|
+
format.timestamp({
|
|
1119
|
+
format: "YYYY-MM-DD HH:mm:ss"
|
|
1120
|
+
}),
|
|
1121
|
+
createFormat(false),
|
|
1122
|
+
format.colorize({
|
|
1123
|
+
all: true
|
|
1124
|
+
})
|
|
1125
|
+
)
|
|
997
1126
|
});
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1127
|
+
var ConduitLogger = class {
|
|
1128
|
+
_winston;
|
|
1129
|
+
constructor(transports) {
|
|
1130
|
+
this._winston = winston.createLogger({
|
|
1131
|
+
format: format.combine(
|
|
1132
|
+
format.prettyPrint(),
|
|
1133
|
+
format.errors({ stack: true }),
|
|
1134
|
+
format.timestamp({
|
|
1135
|
+
format: "YYYY-MM-DD HH:mm:ss"
|
|
1136
|
+
}),
|
|
1137
|
+
createFormat()
|
|
1138
|
+
),
|
|
1139
|
+
transports: transports ? [...transports, defaultTransport] : [defaultTransport]
|
|
1140
|
+
});
|
|
1141
|
+
}
|
|
1142
|
+
get winston() {
|
|
1143
|
+
return this._winston;
|
|
1144
|
+
}
|
|
1145
|
+
addTransport(transport) {
|
|
1146
|
+
this._winston.add(transport);
|
|
1147
|
+
}
|
|
1148
|
+
log(message, level = "info") {
|
|
1149
|
+
return this._winston.log(level, message);
|
|
1150
|
+
}
|
|
1151
|
+
logObject(object, message = "", level = "info") {
|
|
1152
|
+
return this._winston.log(level, message, object);
|
|
1153
|
+
}
|
|
1154
|
+
info(message) {
|
|
1155
|
+
return this._winston.info(message);
|
|
1156
|
+
}
|
|
1157
|
+
warn(message) {
|
|
1158
|
+
return this._winston.warn(message);
|
|
1159
|
+
}
|
|
1160
|
+
error(message, error) {
|
|
1161
|
+
if (typeof message === "object") {
|
|
1162
|
+
return this._winston.error(message);
|
|
1163
|
+
}
|
|
1164
|
+
return this._winston.error(message, error);
|
|
1165
|
+
}
|
|
1166
|
+
http(message) {
|
|
1167
|
+
return this._winston.http(message);
|
|
1168
|
+
}
|
|
1169
|
+
verbose(message) {
|
|
1170
|
+
return this._winston.verbose(message);
|
|
1171
|
+
}
|
|
1172
|
+
};
|
|
1039
1173
|
async function lokiReadyCheck(lokiUrl) {
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
}).on('error', err => {
|
|
1051
|
-
reject(err.message);
|
|
1052
|
-
});
|
|
1174
|
+
return new Promise((resolve, reject) => {
|
|
1175
|
+
const data = [];
|
|
1176
|
+
get(`${lokiUrl}/ready`, (r) => {
|
|
1177
|
+
r.on("data", (chunk) => data.push(chunk));
|
|
1178
|
+
r.on("end", () => {
|
|
1179
|
+
if (Buffer.concat(data).toString() === "ready\n") resolve();
|
|
1180
|
+
else reject(false);
|
|
1181
|
+
});
|
|
1182
|
+
}).on("error", (err) => {
|
|
1183
|
+
reject(err.message);
|
|
1053
1184
|
});
|
|
1185
|
+
});
|
|
1054
1186
|
}
|
|
1055
1187
|
async function setupLoki(module, instance) {
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
}
|
|
1188
|
+
let lokiUrl = process.env.LOKI_URL;
|
|
1189
|
+
if (lokiUrl && lokiUrl !== "") {
|
|
1190
|
+
if (lokiUrl.endsWith("/")) lokiUrl = lokiUrl.slice(0, -1);
|
|
1191
|
+
const onTry = async () => {
|
|
1192
|
+
return await lokiReadyCheck(lokiUrl).then(() => {
|
|
1193
|
+
ConduitGrpcSdk5.Logger.addTransport(
|
|
1194
|
+
new LokiTransport({
|
|
1195
|
+
level: "debug",
|
|
1196
|
+
host: lokiUrl,
|
|
1197
|
+
batching: false,
|
|
1198
|
+
replaceTimestamp: true,
|
|
1199
|
+
labels: {
|
|
1200
|
+
module,
|
|
1201
|
+
instance,
|
|
1202
|
+
...process.env.CONDUIT_NAMESPACE && {
|
|
1203
|
+
namespace: process.env.CONDUIT_NAMESPACE
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
})
|
|
1207
|
+
);
|
|
1208
|
+
return false;
|
|
1209
|
+
}).catch(() => true);
|
|
1210
|
+
};
|
|
1211
|
+
const onFailure = () => {
|
|
1212
|
+
ConduitGrpcSdk5.Logger.error(`Failed to connect to Loki on '${lokiUrl}'`);
|
|
1213
|
+
};
|
|
1214
|
+
await linearBackoffTimeoutAsync(onTry, 250, 15, onFailure, true);
|
|
1215
|
+
}
|
|
1085
1216
|
}
|
|
1086
1217
|
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
server.listen(port, () => {
|
|
1101
|
-
ConduitGrpcSdk.Logger.info(`Metrics server listening on: ${url}`);
|
|
1102
|
-
});
|
|
1103
|
-
return server;
|
|
1104
|
-
}
|
|
1105
|
-
async requestHandler(req, res) {
|
|
1106
|
-
if (req.url === '/' && req.method === 'GET') {
|
|
1107
|
-
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
1108
|
-
res.end(`Conduit Metrics for module: ${this.moduleName}, instance: ${this.instance}`);
|
|
1109
|
-
}
|
|
1110
|
-
else if (req.url === '/metrics' && req.method === 'GET') {
|
|
1111
|
-
const metrics = await this.Registry.metrics();
|
|
1112
|
-
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
|
1113
|
-
res.end(metrics);
|
|
1114
|
-
}
|
|
1115
|
-
else {
|
|
1116
|
-
res.writeHead(404, { 'Content-Type': 'text/plain' });
|
|
1117
|
-
res.end('Not Found');
|
|
1118
|
-
}
|
|
1119
|
-
}
|
|
1120
|
-
getHttpPort() {
|
|
1121
|
-
const value = process.env['METRICS_PORT'];
|
|
1122
|
-
if (!value) {
|
|
1123
|
-
ConduitGrpcSdk.Logger.error(`Metrics HTTP port not set`);
|
|
1124
|
-
process.exit(-1);
|
|
1125
|
-
}
|
|
1126
|
-
const port = parseInt(value, 10);
|
|
1127
|
-
if (isNaN(port) || port < 1 || port > 65535) {
|
|
1128
|
-
ConduitGrpcSdk.Logger.error(`Invalid HTTP port value: ${port}`);
|
|
1129
|
-
process.exit(-1);
|
|
1130
|
-
}
|
|
1131
|
-
return port;
|
|
1132
|
-
}
|
|
1133
|
-
}
|
|
1218
|
+
// src/utilities/initializeSdk.ts
|
|
1219
|
+
import winston2 from "winston";
|
|
1220
|
+
import path3 from "path";
|
|
1221
|
+
|
|
1222
|
+
// src/metrics/index.ts
|
|
1223
|
+
import {
|
|
1224
|
+
collectDefaultMetrics,
|
|
1225
|
+
Counter,
|
|
1226
|
+
Gauge,
|
|
1227
|
+
Histogram,
|
|
1228
|
+
Registry,
|
|
1229
|
+
Summary
|
|
1230
|
+
} from "prom-client";
|
|
1134
1231
|
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
}
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
}
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
adminGrpcRequestLatency: {
|
|
1186
|
-
type: MetricType.Gauge,
|
|
1187
|
-
config: {
|
|
1188
|
-
name: 'admin_grpc_request_latency_seconds',
|
|
1189
|
-
help: 'Tracks the latency of admin grpc requests in seconds',
|
|
1190
|
-
},
|
|
1191
|
-
},
|
|
1192
|
-
clientGrpcRequestLatency: {
|
|
1193
|
-
type: MetricType.Gauge,
|
|
1194
|
-
config: {
|
|
1195
|
-
name: 'client_grpc_request_latency_seconds',
|
|
1196
|
-
help: 'Tracks the latency of client grpc requests in seconds',
|
|
1197
|
-
},
|
|
1198
|
-
},
|
|
1199
|
-
grpcResponseStatuses: {
|
|
1200
|
-
type: MetricType.Counter,
|
|
1201
|
-
config: {
|
|
1202
|
-
name: 'grpc_response_statuses_total',
|
|
1203
|
-
help: 'Tracks the total number of grpc response codes',
|
|
1204
|
-
labelNames: ['success'],
|
|
1205
|
-
},
|
|
1206
|
-
},
|
|
1207
|
-
adminGrpcResponseStatuses: {
|
|
1208
|
-
type: MetricType.Counter,
|
|
1209
|
-
config: {
|
|
1210
|
-
name: 'admin_grpc_response_statuses_total',
|
|
1211
|
-
help: 'Tracks the total number of admin grpc response codes',
|
|
1212
|
-
labelNames: ['success'],
|
|
1213
|
-
},
|
|
1214
|
-
},
|
|
1215
|
-
clientGrpcResponseStatuses: {
|
|
1216
|
-
type: MetricType.Counter,
|
|
1217
|
-
config: {
|
|
1218
|
-
name: 'client_grpc_response_statuses_total',
|
|
1219
|
-
help: 'Tracks the total number of client grpc response codes',
|
|
1220
|
-
labelNames: ['success'],
|
|
1221
|
-
},
|
|
1222
|
-
},
|
|
1232
|
+
// src/metrics/MetricsServer.ts
|
|
1233
|
+
import { createServer as createServer2 } from "http";
|
|
1234
|
+
import { isNaN } from "lodash-es";
|
|
1235
|
+
import { ConduitGrpcSdk as ConduitGrpcSdk6 } from "@conduitplatform/grpc-sdk";
|
|
1236
|
+
var MetricsServer = class {
|
|
1237
|
+
moduleName;
|
|
1238
|
+
instance;
|
|
1239
|
+
Registry;
|
|
1240
|
+
constructor(moduleName, instance, registry2) {
|
|
1241
|
+
this.moduleName = moduleName;
|
|
1242
|
+
this.instance = instance;
|
|
1243
|
+
this.Registry = registry2;
|
|
1244
|
+
}
|
|
1245
|
+
initialize() {
|
|
1246
|
+
const port = this.getHttpPort();
|
|
1247
|
+
const url = "0.0.0.0:" + port.toString();
|
|
1248
|
+
const server = createServer2(this.requestHandler.bind(this));
|
|
1249
|
+
server.listen(port, () => {
|
|
1250
|
+
ConduitGrpcSdk6.Logger.info(`Metrics server listening on: ${url}`);
|
|
1251
|
+
});
|
|
1252
|
+
return server;
|
|
1253
|
+
}
|
|
1254
|
+
async requestHandler(req, res) {
|
|
1255
|
+
if (req.url === "/" && req.method === "GET") {
|
|
1256
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
1257
|
+
res.end(
|
|
1258
|
+
`Conduit Metrics for module: ${this.moduleName}, instance: ${this.instance}`
|
|
1259
|
+
);
|
|
1260
|
+
} else if (req.url === "/metrics" && req.method === "GET") {
|
|
1261
|
+
const metrics = await this.Registry.metrics();
|
|
1262
|
+
res.writeHead(200, { "Content-Type": "text/plain" });
|
|
1263
|
+
res.end(metrics);
|
|
1264
|
+
} else {
|
|
1265
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
1266
|
+
res.end("Not Found");
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
getHttpPort() {
|
|
1270
|
+
const value = process.env["METRICS_PORT"];
|
|
1271
|
+
if (!value) {
|
|
1272
|
+
ConduitGrpcSdk6.Logger.error(`Metrics HTTP port not set`);
|
|
1273
|
+
process.exit(-1);
|
|
1274
|
+
}
|
|
1275
|
+
const port = parseInt(value, 10);
|
|
1276
|
+
if (isNaN(port) || port < 1 || port > 65535) {
|
|
1277
|
+
ConduitGrpcSdk6.Logger.error(`Invalid HTTP port value: ${port}`);
|
|
1278
|
+
process.exit(-1);
|
|
1279
|
+
}
|
|
1280
|
+
return port;
|
|
1281
|
+
}
|
|
1223
1282
|
};
|
|
1224
1283
|
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
throw new Error(`Metric ${metric} is not an incrementable metric`);
|
|
1317
|
-
}
|
|
1318
|
-
return labels
|
|
1319
|
-
? metricInstance.labels({ ...labels }).inc(increment)
|
|
1320
|
-
: metricInstance.inc(increment);
|
|
1321
|
-
}
|
|
1322
|
-
decrement(metric, decrement = 1, labels) {
|
|
1323
|
-
const metricInstance = this.getMetric(metric);
|
|
1324
|
-
if (!(metricInstance instanceof Gauge)) {
|
|
1325
|
-
throw new Error(`Metric ${metric} is not a decrementable metric`);
|
|
1326
|
-
}
|
|
1327
|
-
return labels
|
|
1328
|
-
? metricInstance.labels({ ...labels }).dec(decrement)
|
|
1329
|
-
: metricInstance.dec(decrement);
|
|
1330
|
-
}
|
|
1331
|
-
set(metric, value, labels) {
|
|
1332
|
-
const metricInstance = this.getMetric(metric);
|
|
1333
|
-
if (!(metricInstance instanceof Gauge)) {
|
|
1334
|
-
throw new Error(`Metric ${metric} is not a Gauge`);
|
|
1335
|
-
}
|
|
1336
|
-
return labels
|
|
1337
|
-
? metricInstance.labels({ ...labels }).set(value)
|
|
1338
|
-
: metricInstance.set(value);
|
|
1339
|
-
}
|
|
1340
|
-
observe(metric, value, labels) {
|
|
1341
|
-
const metricInstance = this.getMetric(metric);
|
|
1342
|
-
if (!(metricInstance instanceof Histogram) && !(metricInstance instanceof Summary)) {
|
|
1343
|
-
throw new Error(`Metric ${metric} is not a Histogram`);
|
|
1344
|
-
}
|
|
1345
|
-
return labels
|
|
1346
|
-
? metricInstance.labels({ ...labels }).observe(value)
|
|
1347
|
-
: metricInstance.observe(value);
|
|
1348
|
-
}
|
|
1349
|
-
addPrefix(metric) {
|
|
1350
|
-
if (!metric.includes(METRICS_PREFIX)) {
|
|
1351
|
-
return `${METRICS_PREFIX}${metric}`;
|
|
1352
|
-
}
|
|
1353
|
-
return metric;
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1284
|
+
// src/metrics/config/defaults.ts
|
|
1285
|
+
import { MetricType } from "@conduitplatform/grpc-sdk";
|
|
1286
|
+
var defaults_default = {
|
|
1287
|
+
internalGrpcRequests: {
|
|
1288
|
+
type: MetricType.Counter,
|
|
1289
|
+
config: {
|
|
1290
|
+
name: "internal_grpc_requests_total",
|
|
1291
|
+
help: "Tracks the total number of Conduit's internal gRPC requests"
|
|
1292
|
+
}
|
|
1293
|
+
},
|
|
1294
|
+
clientGrpcRequests: {
|
|
1295
|
+
type: MetricType.Counter,
|
|
1296
|
+
config: {
|
|
1297
|
+
name: "client_grpc_requests_total",
|
|
1298
|
+
help: "Tracks the total number of the client gRPC requests"
|
|
1299
|
+
}
|
|
1300
|
+
},
|
|
1301
|
+
adminGrpcRequests: {
|
|
1302
|
+
type: MetricType.Counter,
|
|
1303
|
+
config: {
|
|
1304
|
+
name: "admin_grpc_requests_total",
|
|
1305
|
+
help: "Tracks the total number of the admin gRPC requests"
|
|
1306
|
+
}
|
|
1307
|
+
},
|
|
1308
|
+
clientGrpcErrors: {
|
|
1309
|
+
type: MetricType.Counter,
|
|
1310
|
+
config: {
|
|
1311
|
+
name: "client_grpc_errors_total",
|
|
1312
|
+
help: "Tracks the total number of client grpc errors"
|
|
1313
|
+
}
|
|
1314
|
+
},
|
|
1315
|
+
adminGrpcErrors: {
|
|
1316
|
+
type: MetricType.Counter,
|
|
1317
|
+
config: {
|
|
1318
|
+
name: "admin_grpc_errors_total",
|
|
1319
|
+
help: "Tracks the total number of admin grpc errors"
|
|
1320
|
+
}
|
|
1321
|
+
},
|
|
1322
|
+
moduleHealthState: {
|
|
1323
|
+
type: MetricType.Gauge,
|
|
1324
|
+
config: {
|
|
1325
|
+
name: "module_health_state",
|
|
1326
|
+
help: "Tracks the health state of the module"
|
|
1327
|
+
}
|
|
1328
|
+
},
|
|
1329
|
+
grpcRequestLatency: {
|
|
1330
|
+
type: MetricType.Gauge,
|
|
1331
|
+
config: {
|
|
1332
|
+
name: "grpc_request_latency_seconds",
|
|
1333
|
+
help: "Tracks the latency of grpc requests in seconds"
|
|
1334
|
+
}
|
|
1335
|
+
},
|
|
1336
|
+
adminGrpcRequestLatency: {
|
|
1337
|
+
type: MetricType.Gauge,
|
|
1338
|
+
config: {
|
|
1339
|
+
name: "admin_grpc_request_latency_seconds",
|
|
1340
|
+
help: "Tracks the latency of admin grpc requests in seconds"
|
|
1341
|
+
}
|
|
1342
|
+
},
|
|
1343
|
+
clientGrpcRequestLatency: {
|
|
1344
|
+
type: MetricType.Gauge,
|
|
1345
|
+
config: {
|
|
1346
|
+
name: "client_grpc_request_latency_seconds",
|
|
1347
|
+
help: "Tracks the latency of client grpc requests in seconds"
|
|
1348
|
+
}
|
|
1349
|
+
},
|
|
1350
|
+
grpcResponseStatuses: {
|
|
1351
|
+
type: MetricType.Counter,
|
|
1352
|
+
config: {
|
|
1353
|
+
name: "grpc_response_statuses_total",
|
|
1354
|
+
help: "Tracks the total number of grpc response codes",
|
|
1355
|
+
labelNames: ["success"]
|
|
1356
|
+
}
|
|
1357
|
+
},
|
|
1358
|
+
adminGrpcResponseStatuses: {
|
|
1359
|
+
type: MetricType.Counter,
|
|
1360
|
+
config: {
|
|
1361
|
+
name: "admin_grpc_response_statuses_total",
|
|
1362
|
+
help: "Tracks the total number of admin grpc response codes",
|
|
1363
|
+
labelNames: ["success"]
|
|
1364
|
+
}
|
|
1365
|
+
},
|
|
1366
|
+
clientGrpcResponseStatuses: {
|
|
1367
|
+
type: MetricType.Counter,
|
|
1368
|
+
config: {
|
|
1369
|
+
name: "client_grpc_response_statuses_total",
|
|
1370
|
+
help: "Tracks the total number of client grpc response codes",
|
|
1371
|
+
labelNames: ["success"]
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
};
|
|
1356
1375
|
|
|
1357
|
-
//
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1376
|
+
// src/metrics/index.ts
|
|
1377
|
+
import { MetricType as MetricType2 } from "@conduitplatform/grpc-sdk";
|
|
1378
|
+
var METRICS_PREFIX = "cnd_";
|
|
1379
|
+
var ConduitMetrics = class {
|
|
1380
|
+
moduleName;
|
|
1381
|
+
instance;
|
|
1382
|
+
_registry;
|
|
1383
|
+
_httpServer;
|
|
1384
|
+
constructor(moduleName, instance) {
|
|
1385
|
+
this.moduleName = moduleName;
|
|
1386
|
+
this.instance = instance;
|
|
1387
|
+
this._registry = new Registry();
|
|
1388
|
+
this._registry.setDefaultLabels({
|
|
1389
|
+
module_name: this.moduleName,
|
|
1390
|
+
module_instance: this.instance
|
|
1391
|
+
});
|
|
1392
|
+
this.collectDefaultMetrics();
|
|
1393
|
+
this._httpServer = new MetricsServer(moduleName, instance, this._registry);
|
|
1394
|
+
this._httpServer.initialize();
|
|
1395
|
+
}
|
|
1396
|
+
get registry() {
|
|
1397
|
+
return this._registry;
|
|
1398
|
+
}
|
|
1399
|
+
initializeDefaultMetrics() {
|
|
1400
|
+
for (const metric of Object.values(defaults_default)) {
|
|
1401
|
+
this.registerMetric(metric.type, metric.config);
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
registerMetric(type, config) {
|
|
1405
|
+
if (this.getMetric(config.name)) return;
|
|
1406
|
+
const metricConfig = JSON.parse(JSON.stringify(config));
|
|
1407
|
+
metricConfig.name = this.addPrefix(config.name);
|
|
1408
|
+
switch (type) {
|
|
1409
|
+
case MetricType2.Counter:
|
|
1410
|
+
this.createCounter(metricConfig);
|
|
1411
|
+
break;
|
|
1412
|
+
case MetricType2.Gauge:
|
|
1413
|
+
this.createGauge(metricConfig);
|
|
1414
|
+
break;
|
|
1415
|
+
case MetricType2.Histogram:
|
|
1416
|
+
this.createHistogram(metricConfig);
|
|
1417
|
+
break;
|
|
1418
|
+
case MetricType2.Summary:
|
|
1419
|
+
this.createSummary(metricConfig);
|
|
1420
|
+
break;
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
collectDefaultMetrics() {
|
|
1424
|
+
collectDefaultMetrics({
|
|
1425
|
+
register: this._registry,
|
|
1426
|
+
prefix: METRICS_PREFIX,
|
|
1427
|
+
// probably not needed
|
|
1428
|
+
labels: {
|
|
1429
|
+
module_name: this.moduleName,
|
|
1430
|
+
instance_instance: this.instance
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1434
|
+
createCounter(config) {
|
|
1435
|
+
return new Counter({
|
|
1436
|
+
...config,
|
|
1437
|
+
name: this.addPrefix(config.name),
|
|
1438
|
+
registers: [this._registry]
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
createSummary(config) {
|
|
1442
|
+
return new Summary({
|
|
1443
|
+
...config,
|
|
1444
|
+
name: this.addPrefix(config.name),
|
|
1445
|
+
registers: [this._registry]
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
createHistogram(config) {
|
|
1449
|
+
return new Histogram({
|
|
1450
|
+
...config,
|
|
1451
|
+
name: this.addPrefix(config.name),
|
|
1452
|
+
registers: [this._registry]
|
|
1453
|
+
});
|
|
1454
|
+
}
|
|
1455
|
+
createGauge(config) {
|
|
1456
|
+
return new Gauge({
|
|
1457
|
+
...config,
|
|
1458
|
+
name: this.addPrefix(config.name),
|
|
1459
|
+
registers: [this._registry]
|
|
1460
|
+
});
|
|
1461
|
+
}
|
|
1462
|
+
getMetric(name) {
|
|
1463
|
+
return this._registry.getSingleMetric(this.addPrefix(name));
|
|
1464
|
+
}
|
|
1465
|
+
increment(metric, increment = 1, labels) {
|
|
1466
|
+
const metricInstance = this.getMetric(metric);
|
|
1467
|
+
if (!(metricInstance instanceof Counter) && !(metricInstance instanceof Gauge)) {
|
|
1468
|
+
throw new Error(`Metric ${metric} is not an incrementable metric`);
|
|
1469
|
+
}
|
|
1470
|
+
return labels ? metricInstance.labels({ ...labels }).inc(increment) : metricInstance.inc(increment);
|
|
1471
|
+
}
|
|
1472
|
+
decrement(metric, decrement = 1, labels) {
|
|
1473
|
+
const metricInstance = this.getMetric(metric);
|
|
1474
|
+
if (!(metricInstance instanceof Gauge)) {
|
|
1475
|
+
throw new Error(`Metric ${metric} is not a decrementable metric`);
|
|
1476
|
+
}
|
|
1477
|
+
return labels ? metricInstance.labels({ ...labels }).dec(decrement) : metricInstance.dec(decrement);
|
|
1478
|
+
}
|
|
1479
|
+
set(metric, value, labels) {
|
|
1480
|
+
const metricInstance = this.getMetric(metric);
|
|
1481
|
+
if (!(metricInstance instanceof Gauge)) {
|
|
1482
|
+
throw new Error(`Metric ${metric} is not a Gauge`);
|
|
1483
|
+
}
|
|
1484
|
+
return labels ? metricInstance.labels({ ...labels }).set(value) : metricInstance.set(value);
|
|
1485
|
+
}
|
|
1486
|
+
observe(metric, value, labels) {
|
|
1487
|
+
const metricInstance = this.getMetric(metric);
|
|
1488
|
+
if (!(metricInstance instanceof Histogram) && !(metricInstance instanceof Summary)) {
|
|
1489
|
+
throw new Error(`Metric ${metric} is not a Histogram`);
|
|
1490
|
+
}
|
|
1491
|
+
return labels ? metricInstance.labels({ ...labels }).observe(value) : metricInstance.observe(value);
|
|
1492
|
+
}
|
|
1493
|
+
addPrefix(metric) {
|
|
1494
|
+
if (!metric.includes(METRICS_PREFIX)) {
|
|
1495
|
+
return `${METRICS_PREFIX}${metric}`;
|
|
1496
|
+
}
|
|
1497
|
+
return metric;
|
|
1498
|
+
}
|
|
1499
|
+
};
|
|
1500
|
+
|
|
1501
|
+
// src/metrics/clientMiddleware.ts
|
|
1502
|
+
import { isAbortError } from "abort-controller-x";
|
|
1503
|
+
import {
|
|
1504
|
+
ClientError,
|
|
1505
|
+
Status
|
|
1506
|
+
} from "nice-grpc-common";
|
|
1507
|
+
import { Counter as Counter2, exponentialBuckets, Histogram as Histogram2, Registry as Registry2 } from "prom-client";
|
|
1508
|
+
var registry = new Registry2();
|
|
1509
|
+
var typeLabel = "grpc_type";
|
|
1510
|
+
var serviceLabel = "grpc_service";
|
|
1511
|
+
var methodLabel = "grpc_method";
|
|
1512
|
+
var pathLabel = "grpc_path";
|
|
1513
|
+
var codeLabel = "grpc_code";
|
|
1514
|
+
var latencySecondsBuckets = exponentialBuckets(1e-3, 4, 12);
|
|
1368
1515
|
function getLabels(method) {
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
return {
|
|
1379
|
-
[typeLabel]: callType,
|
|
1380
|
-
[serviceLabel]: serviceName,
|
|
1381
|
-
[methodLabel]: methodName,
|
|
1382
|
-
[pathLabel]: path,
|
|
1383
|
-
};
|
|
1516
|
+
const callType = method.requestStream ? method.responseStream ? "bidi_stream" : "client_stream" : method.responseStream ? "server_stream" : "unary";
|
|
1517
|
+
const { path: path5 } = method;
|
|
1518
|
+
const [serviceName, methodName] = path5.split("/").slice(1);
|
|
1519
|
+
return {
|
|
1520
|
+
[typeLabel]: callType,
|
|
1521
|
+
[serviceLabel]: serviceName,
|
|
1522
|
+
[methodLabel]: methodName,
|
|
1523
|
+
[pathLabel]: path5
|
|
1524
|
+
};
|
|
1384
1525
|
}
|
|
1385
1526
|
async function* incrementStreamMessagesCounter(iterable, counter) {
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1527
|
+
for await (const item of iterable) {
|
|
1528
|
+
counter.inc();
|
|
1529
|
+
yield item;
|
|
1530
|
+
}
|
|
1390
1531
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1532
|
+
var clientStartedMetric = new Counter2({
|
|
1533
|
+
registers: [registry],
|
|
1534
|
+
name: "cnd_grpc_client_started_total",
|
|
1535
|
+
help: "Total number of RPCs started on the client.",
|
|
1536
|
+
labelNames: [typeLabel, serviceLabel, methodLabel, pathLabel]
|
|
1396
1537
|
});
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1538
|
+
var clientHandledMetric = new Counter2({
|
|
1539
|
+
registers: [registry],
|
|
1540
|
+
name: "cnd_grpc_client_handled_total",
|
|
1541
|
+
help: "Total number of RPCs completed on the client, regardless of success or failure.",
|
|
1542
|
+
labelNames: [typeLabel, serviceLabel, methodLabel, pathLabel, codeLabel]
|
|
1402
1543
|
});
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1544
|
+
var clientStreamMsgReceivedMetric = new Counter2({
|
|
1545
|
+
registers: [registry],
|
|
1546
|
+
name: "cnd_grpc_client_msg_received_total",
|
|
1547
|
+
help: "Total number of RPC stream messages received by the client.",
|
|
1548
|
+
labelNames: [typeLabel, serviceLabel, methodLabel, pathLabel]
|
|
1408
1549
|
});
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1550
|
+
var clientStreamMsgSentMetric = new Counter2({
|
|
1551
|
+
registers: [registry],
|
|
1552
|
+
name: "cnd_grpc_client_msg_sent_total",
|
|
1553
|
+
help: "Total number of gRPC stream messages sent by the client.",
|
|
1554
|
+
labelNames: [typeLabel, serviceLabel, methodLabel, pathLabel]
|
|
1414
1555
|
});
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1556
|
+
var clientHandlingSecondsMetric = new Histogram2({
|
|
1557
|
+
registers: [registry],
|
|
1558
|
+
name: "grpc_client_handling_seconds",
|
|
1559
|
+
help: "Histogram of response latency (seconds) of the gRPC until it is finished by the application.",
|
|
1560
|
+
labelNames: [typeLabel, serviceLabel, methodLabel, pathLabel, codeLabel],
|
|
1561
|
+
buckets: latencySecondsBuckets
|
|
1421
1562
|
});
|
|
1422
1563
|
function clientMiddleware() {
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1564
|
+
return async function* prometheusClientMiddlewareGenerator(call, options) {
|
|
1565
|
+
const labels = getLabels(call.method);
|
|
1566
|
+
clientStartedMetric.inc(labels);
|
|
1567
|
+
const stopTimer = clientHandlingSecondsMetric.startTimer(labels);
|
|
1568
|
+
let settled = false;
|
|
1569
|
+
let status6 = Status.OK;
|
|
1570
|
+
try {
|
|
1571
|
+
let request;
|
|
1572
|
+
if (!call.requestStream) {
|
|
1573
|
+
request = call.request;
|
|
1574
|
+
} else {
|
|
1575
|
+
request = incrementStreamMessagesCounter(
|
|
1576
|
+
call.request,
|
|
1577
|
+
clientStreamMsgSentMetric.labels(labels)
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
if (!call.responseStream) {
|
|
1581
|
+
const response = yield* call.next(request, options);
|
|
1582
|
+
settled = true;
|
|
1583
|
+
return response;
|
|
1584
|
+
} else {
|
|
1585
|
+
yield* incrementStreamMessagesCounter(
|
|
1586
|
+
call.next(request, options),
|
|
1587
|
+
clientStreamMsgReceivedMetric.labels(labels)
|
|
1588
|
+
);
|
|
1589
|
+
settled = true;
|
|
1590
|
+
return;
|
|
1591
|
+
}
|
|
1592
|
+
} catch (err) {
|
|
1593
|
+
settled = true;
|
|
1594
|
+
if (err instanceof ClientError) {
|
|
1595
|
+
status6 = err.code;
|
|
1596
|
+
} else if (isAbortError(err)) {
|
|
1597
|
+
status6 = Status.ABORTED;
|
|
1598
|
+
} else {
|
|
1599
|
+
status6 = Status.UNKNOWN;
|
|
1600
|
+
}
|
|
1601
|
+
throw err;
|
|
1602
|
+
} finally {
|
|
1603
|
+
if (!settled) {
|
|
1604
|
+
status6 = Status.CANCELLED;
|
|
1605
|
+
}
|
|
1606
|
+
stopTimer({ [codeLabel]: Status[status6] });
|
|
1607
|
+
clientHandledMetric.inc({
|
|
1608
|
+
...labels,
|
|
1609
|
+
[codeLabel]: Status[status6]
|
|
1610
|
+
});
|
|
1611
|
+
}
|
|
1612
|
+
};
|
|
1472
1613
|
}
|
|
1473
1614
|
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1615
|
+
// src/utilities/initializeSdk.ts
|
|
1616
|
+
var initializeSdk = (coreUrl, moduleName, watchModules = true, serviceHealthStatusGetter = () => HealthCheckStatus2.SERVING) => {
|
|
1617
|
+
let logger;
|
|
1618
|
+
try {
|
|
1619
|
+
logger = new ConduitLogger([
|
|
1620
|
+
new winston2.transports.File({
|
|
1621
|
+
filename: path3.join(__dirname, ".logs/combined.log"),
|
|
1622
|
+
level: "info"
|
|
1623
|
+
}),
|
|
1624
|
+
new winston2.transports.File({
|
|
1625
|
+
filename: path3.join(__dirname, ".logs/errors.log"),
|
|
1626
|
+
level: "error"
|
|
1627
|
+
})
|
|
1628
|
+
]);
|
|
1629
|
+
} catch (e) {
|
|
1630
|
+
logger = new ConduitLogger();
|
|
1631
|
+
}
|
|
1632
|
+
const grpcSdk = new ConduitGrpcSdk7(
|
|
1633
|
+
coreUrl,
|
|
1634
|
+
moduleName,
|
|
1635
|
+
watchModules,
|
|
1636
|
+
logger,
|
|
1637
|
+
serviceHealthStatusGetter
|
|
1638
|
+
);
|
|
1639
|
+
setupLoki(grpcSdk.name, grpcSdk.instance).then();
|
|
1640
|
+
if (process.env.METRICS_PORT) {
|
|
1641
|
+
ConduitGrpcSdk7.Metrics = new ConduitMetrics(grpcSdk.name, grpcSdk.instance);
|
|
1642
|
+
grpcSdk.addMiddleware(clientMiddleware());
|
|
1643
|
+
}
|
|
1644
|
+
return grpcSdk;
|
|
1500
1645
|
};
|
|
1501
1646
|
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
stopSignal = true;
|
|
1512
|
-
};
|
|
1513
|
-
let timeout;
|
|
1514
|
-
const invoker = async () => {
|
|
1515
|
-
if (stopSignal) {
|
|
1516
|
-
return timeout ? clearTimeout$1(timeout) : null;
|
|
1517
|
-
}
|
|
1518
|
-
delay = Math.floor(delay * 2);
|
|
1519
|
-
if (delay > 0 && nextRep()) {
|
|
1520
|
-
timeout = setTimeout(invoker, delay);
|
|
1521
|
-
onTry(stop);
|
|
1522
|
-
}
|
|
1523
|
-
else {
|
|
1524
|
-
timeout = null;
|
|
1525
|
-
onFailure && onFailure();
|
|
1526
|
-
}
|
|
1527
|
-
};
|
|
1528
|
-
timeout = setTimeout(invoker, delay);
|
|
1529
|
-
return {
|
|
1530
|
-
clear: () => {
|
|
1531
|
-
if (timeout) {
|
|
1532
|
-
clearTimeout$1(timeout);
|
|
1533
|
-
timeout = null;
|
|
1534
|
-
}
|
|
1535
|
-
},
|
|
1536
|
-
};
|
|
1647
|
+
// src/utilities/merge.ts
|
|
1648
|
+
import { mergeWith as _mergeWith } from "lodash-es";
|
|
1649
|
+
function merge(objA, objB) {
|
|
1650
|
+
const customizer = (objValue, srcValue) => {
|
|
1651
|
+
if (Array.isArray(objValue)) {
|
|
1652
|
+
return srcValue;
|
|
1653
|
+
}
|
|
1654
|
+
};
|
|
1655
|
+
return _mergeWith({}, objA, objB, customizer);
|
|
1537
1656
|
}
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
*/
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1657
|
+
|
|
1658
|
+
// src/utilities/exportHelpers.ts
|
|
1659
|
+
var DEFAULT_STRIP_FIELDS = ["_id", "createdAt", "updatedAt", "__v"];
|
|
1660
|
+
function stripInternalFields(obj, additionalFields = []) {
|
|
1661
|
+
const strip = /* @__PURE__ */ new Set([...DEFAULT_STRIP_FIELDS, ...additionalFields]);
|
|
1662
|
+
const out = {};
|
|
1663
|
+
for (const [k, v] of Object.entries(obj)) {
|
|
1664
|
+
if (!strip.has(k)) out[k] = v;
|
|
1665
|
+
}
|
|
1666
|
+
return out;
|
|
1667
|
+
}
|
|
1668
|
+
function sanitizeDocumentsForExport(documents, options = {}) {
|
|
1669
|
+
const exclude = options.excludeFields ?? [];
|
|
1670
|
+
return documents.map((doc) => stripInternalFields(doc, exclude));
|
|
1671
|
+
}
|
|
1672
|
+
async function exportModel(adapter, options = {}) {
|
|
1673
|
+
const { query, select, excludeFields, ...rest } = options;
|
|
1674
|
+
const docs = await adapter.findMany(query ?? {}, select ? { select } : {});
|
|
1675
|
+
return sanitizeDocumentsForExport(docs, { excludeFields, ...rest });
|
|
1676
|
+
}
|
|
1677
|
+
async function importModel(adapter, records, options = {}) {
|
|
1678
|
+
const entry = { created: 0, updated: 0, failed: 0, errors: [] };
|
|
1679
|
+
const keyField = adapter.keyField;
|
|
1680
|
+
for (const record of records) {
|
|
1681
|
+
const key = record[keyField];
|
|
1682
|
+
if (key == null || key === "") {
|
|
1683
|
+
entry.failed += 1;
|
|
1684
|
+
entry.errors.push(`Missing or empty key "${keyField}" in record`);
|
|
1685
|
+
continue;
|
|
1686
|
+
}
|
|
1687
|
+
try {
|
|
1688
|
+
const existing = await adapter.findOne({ [keyField]: key });
|
|
1689
|
+
const sanitized = stripInternalFields(record, options.excludeFields ?? []);
|
|
1690
|
+
if (existing) {
|
|
1691
|
+
await adapter.updateOne({ [keyField]: key }, sanitized);
|
|
1692
|
+
entry.updated += 1;
|
|
1693
|
+
} else {
|
|
1694
|
+
await adapter.create(sanitized);
|
|
1695
|
+
entry.created += 1;
|
|
1696
|
+
}
|
|
1697
|
+
} catch (err) {
|
|
1698
|
+
entry.failed += 1;
|
|
1699
|
+
entry.errors.push(`${String(key)}: ${err.message}`);
|
|
1700
|
+
}
|
|
1701
|
+
}
|
|
1702
|
+
return entry;
|
|
1556
1703
|
}
|
|
1557
1704
|
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1705
|
+
// src/utilities/conduitPeers.ts
|
|
1706
|
+
import { existsSync, readFileSync } from "fs";
|
|
1707
|
+
import path4 from "path";
|
|
1708
|
+
function readConduitPeersManifest(moduleRootDir = process.cwd()) {
|
|
1709
|
+
const pkgPath = path4.join(moduleRootDir, "package.json");
|
|
1710
|
+
if (!existsSync(pkgPath)) return null;
|
|
1711
|
+
try {
|
|
1712
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
1713
|
+
if (!pkg.conduit?.peers) return null;
|
|
1714
|
+
return pkg.conduit;
|
|
1715
|
+
} catch (e) {
|
|
1716
|
+
console.warn(`[conduitPeers] Failed to parse ${pkgPath}: ${e.message}`);
|
|
1717
|
+
return null;
|
|
1718
|
+
}
|
|
1566
1719
|
}
|
|
1567
1720
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1721
|
+
// src/utilities/convictConfigParser.ts
|
|
1722
|
+
var convictConfigParser = (config) => {
|
|
1723
|
+
if (typeof config === "object") {
|
|
1724
|
+
Object.keys(config).forEach((key) => {
|
|
1725
|
+
if (key === "_cvtProperties") {
|
|
1726
|
+
config = convictConfigParser(config._cvtProperties);
|
|
1727
|
+
} else {
|
|
1728
|
+
config[key] = convictConfigParser(config[key]);
|
|
1729
|
+
}
|
|
1730
|
+
});
|
|
1731
|
+
}
|
|
1732
|
+
return config;
|
|
1580
1733
|
};
|
|
1581
1734
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
}
|
|
1735
|
+
// src/routing/RoutingController.ts
|
|
1736
|
+
import { status as status4 } from "@grpc/grpc-js";
|
|
1737
|
+
var RoutingController = class {
|
|
1738
|
+
_routeHandlers = {};
|
|
1739
|
+
handleRequest(call, callback) {
|
|
1740
|
+
if (this._routeHandlers[call.request.functionName]) {
|
|
1741
|
+
this._routeHandlers[call.request.functionName](call, callback);
|
|
1742
|
+
} else {
|
|
1743
|
+
callback({
|
|
1744
|
+
code: status4.NOT_FOUND,
|
|
1745
|
+
message: "Not found"
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
setRoutes(routes) {
|
|
1750
|
+
this._routeHandlers = routes;
|
|
1751
|
+
}
|
|
1752
|
+
};
|
|
1599
1753
|
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
return this.
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
}
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1754
|
+
// src/ManagedModule.ts
|
|
1755
|
+
var ManagedModule = class extends ConduitServiceModule {
|
|
1756
|
+
config;
|
|
1757
|
+
configOverride = false;
|
|
1758
|
+
service;
|
|
1759
|
+
serviceAddress;
|
|
1760
|
+
servicePort;
|
|
1761
|
+
_moduleState;
|
|
1762
|
+
_declaredPeerWatchDisposers = [];
|
|
1763
|
+
_cachedPeersManifest;
|
|
1764
|
+
_lastPeerBatchStates = {};
|
|
1765
|
+
_peerManifestRoot;
|
|
1766
|
+
_configInitialized = false;
|
|
1767
|
+
_pendingConfigUpdates = [];
|
|
1768
|
+
_processingConfig = false;
|
|
1769
|
+
constructor(moduleName, peerManifestRoot) {
|
|
1770
|
+
super(moduleName);
|
|
1771
|
+
this._peerManifestRoot = peerManifestRoot;
|
|
1772
|
+
this._moduleState = "creat-grpc-server" /* CREATE_GRPC */;
|
|
1773
|
+
if (!process.env.CONDUIT_SERVER) {
|
|
1774
|
+
throw new Error("CONDUIT_SERVER is undefined, specify Conduit server URL");
|
|
1775
|
+
}
|
|
1776
|
+
this.servicePort = process.env.GRPC_PORT ?? "5000";
|
|
1777
|
+
this.serviceAddress = // @compat (v0.15): SERVICE_IP -> SERVICE_URL
|
|
1778
|
+
process.env.SERVICE_URL || process.env.SERVICE_IP || "0.0.0.0:" + this.servicePort;
|
|
1779
|
+
try {
|
|
1780
|
+
this.grpcSdk = initializeSdk(process.env.CONDUIT_SERVER, this.name, true, () => {
|
|
1781
|
+
return this.healthState;
|
|
1782
|
+
});
|
|
1783
|
+
} catch (e) {
|
|
1784
|
+
throw new Error("Failed to initialize grpcSdk");
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
get name() {
|
|
1788
|
+
return this._moduleName;
|
|
1789
|
+
}
|
|
1790
|
+
get address() {
|
|
1791
|
+
return this._address;
|
|
1792
|
+
}
|
|
1793
|
+
async start() {
|
|
1794
|
+
await this.grpcSdk.initialize();
|
|
1795
|
+
this.initialize(this.serviceAddress, this.servicePort);
|
|
1796
|
+
try {
|
|
1797
|
+
await this.preRegisterLifecycle();
|
|
1798
|
+
await this.grpcSdk.config.registerModule(this.name, this.address, this.healthState);
|
|
1799
|
+
} catch (err) {
|
|
1800
|
+
ConduitGrpcSdk8.Logger.error("Failed to initialize server");
|
|
1801
|
+
ConduitGrpcSdk8.Logger.error(err);
|
|
1802
|
+
process.exit(-1);
|
|
1803
|
+
}
|
|
1804
|
+
await this.postRegisterLifecycle().catch((err) => {
|
|
1805
|
+
ConduitGrpcSdk8.Logger.error("Failed to activate module");
|
|
1806
|
+
ConduitGrpcSdk8.Logger.error(err);
|
|
1807
|
+
process.exit(-1);
|
|
1808
|
+
});
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1811
|
+
* This is the first function triggered on module spin up.
|
|
1812
|
+
* @param address {string} external address:port of service (LoadBalancer)
|
|
1813
|
+
* @param port {string} port to bring up gRPC service
|
|
1814
|
+
*/
|
|
1815
|
+
initialize(address, port) {
|
|
1816
|
+
this._address = address;
|
|
1817
|
+
this._port = port;
|
|
1818
|
+
if (this.configSchema) {
|
|
1819
|
+
this.config = convict(this.configSchema);
|
|
1820
|
+
}
|
|
1821
|
+
}
|
|
1822
|
+
/**
|
|
1823
|
+
* This is triggered before spinning up the Grpc Server
|
|
1824
|
+
* of the module. On this stage, the redis bus and state have
|
|
1825
|
+
* not yet been initialized, and only communications with Conduit core
|
|
1826
|
+
* have been established.
|
|
1827
|
+
*/
|
|
1828
|
+
async preServerStart() {
|
|
1829
|
+
}
|
|
1830
|
+
/**
|
|
1831
|
+
* This is triggered right after Grpc Server start.
|
|
1832
|
+
* At this stage the core module will communicate with your service
|
|
1833
|
+
* for health checks, and the Bus and State management mechanics
|
|
1834
|
+
* are online and available.
|
|
1835
|
+
*/
|
|
1836
|
+
async onServerStart() {
|
|
1837
|
+
}
|
|
1838
|
+
/**
|
|
1839
|
+
* Triggers right before registering the module with Conduit Core.
|
|
1840
|
+
*/
|
|
1841
|
+
async preRegister() {
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Triggers when module has been registered with Conduit Core
|
|
1845
|
+
* This is the step where you should initialize your
|
|
1846
|
+
* module's general routing (admin or client) and any other operation,
|
|
1847
|
+
* that would require other modules being able to reach you
|
|
1848
|
+
*/
|
|
1849
|
+
async onRegister() {
|
|
1850
|
+
}
|
|
1851
|
+
/**
|
|
1852
|
+
* This is triggered when the module receives new configuration.
|
|
1853
|
+
* At this step the configuration has not been checked yet,
|
|
1854
|
+
* and has only been parsed.
|
|
1855
|
+
* @param config
|
|
1856
|
+
*/
|
|
1857
|
+
async preConfig(config) {
|
|
1858
|
+
return config;
|
|
1859
|
+
}
|
|
1860
|
+
/**
|
|
1861
|
+
* This is triggered when new configuration has been applied.
|
|
1862
|
+
* This happens either when changes are being made on the configuration,
|
|
1863
|
+
* or on module start up, where the config is recovered from the database.
|
|
1864
|
+
* In either case the configuration is now available through the
|
|
1865
|
+
* Config Controller of the sdk.
|
|
1866
|
+
*/
|
|
1867
|
+
async onConfig() {
|
|
1868
|
+
}
|
|
1869
|
+
/**
|
|
1870
|
+
* Registers common and module-specific metric types.
|
|
1871
|
+
*/
|
|
1872
|
+
async registerMetrics() {
|
|
1873
|
+
if (ConduitGrpcSdk8.Metrics) {
|
|
1874
|
+
ConduitGrpcSdk8.Metrics.initializeDefaultMetrics();
|
|
1875
|
+
if (this.metricsSchema) {
|
|
1876
|
+
Object.values(this.metricsSchema).forEach((metric) => {
|
|
1877
|
+
ConduitGrpcSdk8.Metrics.registerMetric(metric.type, metric.config);
|
|
1648
1878
|
});
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
this.
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
}
|
|
1746
|
-
let config = JSON.parse(call.request.newConfig);
|
|
1747
|
-
config = merge(this.config.getProperties(), config);
|
|
1748
|
-
config = await this.preConfig(config);
|
|
1749
|
-
const previousConfig = this.config.getProperties();
|
|
1750
|
-
try {
|
|
1751
|
-
this.config.load(config).validate({
|
|
1752
|
-
allowed: 'warn',
|
|
1753
|
-
});
|
|
1754
|
-
config = this.config.getProperties();
|
|
1755
|
-
callback(null, { updatedConfig: JSON.stringify(config) });
|
|
1756
|
-
}
|
|
1757
|
-
catch (e) {
|
|
1758
|
-
this.config = convict(this.configSchema);
|
|
1759
|
-
this.config.load(previousConfig);
|
|
1760
|
-
return callback({
|
|
1761
|
-
code: status.INVALID_ARGUMENT,
|
|
1762
|
-
message: 'Invalid configuration values: ' + e.message,
|
|
1763
|
-
});
|
|
1764
|
-
}
|
|
1765
|
-
ConfigController.getInstance().config = config;
|
|
1766
|
-
await this.onConfig();
|
|
1767
|
-
this.grpcSdk.bus?.publish(kebabCase(this.name) + ':config:update', JSON.stringify(config));
|
|
1768
|
-
}
|
|
1769
|
-
catch (e) {
|
|
1770
|
-
return callback({ code: status.INTERNAL, message: e.message });
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
/** Used to update the module's configuration on initial Redis/DB reconciliation. */
|
|
1774
|
-
async handleConfigSyncUpdate() {
|
|
1775
|
-
if (!this.config)
|
|
1776
|
-
return;
|
|
1777
|
-
this.grpcSdk.bus.subscribe(`${kebabCase(this.name)}:config:update`, async (message) => {
|
|
1778
|
-
ConfigController.getInstance().config = await this.preConfig(JSON.parse(message));
|
|
1779
|
-
await this.onConfig();
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
/**
|
|
1883
|
+
* Initializes metric startup values.
|
|
1884
|
+
* Implemented by individual modules.
|
|
1885
|
+
*/
|
|
1886
|
+
async initializeMetrics() {
|
|
1887
|
+
}
|
|
1888
|
+
getPeerManifestRoot() {
|
|
1889
|
+
return this._peerManifestRoot ?? process.cwd();
|
|
1890
|
+
}
|
|
1891
|
+
loadConduitPeersManifest() {
|
|
1892
|
+
if (this._cachedPeersManifest === void 0) {
|
|
1893
|
+
this._cachedPeersManifest = readConduitPeersManifest(this.getPeerManifestRoot());
|
|
1894
|
+
}
|
|
1895
|
+
return this._cachedPeersManifest;
|
|
1896
|
+
}
|
|
1897
|
+
validateDeclaredPeerNames(names) {
|
|
1898
|
+
const known = new Set(this.grpcSdk.getKnownModuleNames());
|
|
1899
|
+
for (const n of names) {
|
|
1900
|
+
if (!known.has(n)) {
|
|
1901
|
+
ConduitGrpcSdk8.Logger.warn(
|
|
1902
|
+
`Declared Conduit peer "${n}" is not a known built-in module (see ConduitGrpcSdk.getKnownModuleNames).`
|
|
1903
|
+
);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
async awaitPeersFromManifest() {
|
|
1908
|
+
const manifest = this.loadConduitPeersManifest();
|
|
1909
|
+
const peers = manifest?.peers?.await;
|
|
1910
|
+
if (!peers?.length) return;
|
|
1911
|
+
this.validateDeclaredPeerNames(peers);
|
|
1912
|
+
await Promise.all(peers.map((m) => this.grpcSdk.awaitPeer(m)));
|
|
1913
|
+
}
|
|
1914
|
+
registerDeclaredPeerWatches() {
|
|
1915
|
+
this.disposeDeclaredPeerWatches();
|
|
1916
|
+
const manifest = this.loadConduitPeersManifest();
|
|
1917
|
+
const watch = manifest?.peers?.watch;
|
|
1918
|
+
if (!watch?.length) return;
|
|
1919
|
+
this.validateDeclaredPeerNames(watch.map((w) => w.module));
|
|
1920
|
+
const specs = watch.map((entry) => ({
|
|
1921
|
+
module: entry.module,
|
|
1922
|
+
edge: entry.edge ?? "rising"
|
|
1923
|
+
}));
|
|
1924
|
+
const dispose = this.grpcSdk.watchPeers(
|
|
1925
|
+
specs,
|
|
1926
|
+
(statuses) => {
|
|
1927
|
+
for (const [mod, s] of Object.entries(statuses)) {
|
|
1928
|
+
if (this._lastPeerBatchStates[mod] !== s) {
|
|
1929
|
+
this.onDeclaredPeerConnectionUpdate(mod, s);
|
|
1930
|
+
this._lastPeerBatchStates[mod] = s;
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
},
|
|
1934
|
+
{ debounceMs: 100, syncInitialState: true }
|
|
1935
|
+
);
|
|
1936
|
+
this._declaredPeerWatchDisposers.push(dispose);
|
|
1937
|
+
}
|
|
1938
|
+
disposeDeclaredPeerWatches() {
|
|
1939
|
+
for (const d of this._declaredPeerWatchDisposers) {
|
|
1940
|
+
d();
|
|
1941
|
+
}
|
|
1942
|
+
this._declaredPeerWatchDisposers = [];
|
|
1943
|
+
this._lastPeerBatchStates = {};
|
|
1944
|
+
}
|
|
1945
|
+
onDeclaredPeerConnectionUpdate(_moduleName, _serving) {
|
|
1946
|
+
}
|
|
1947
|
+
async createGrpcServer() {
|
|
1948
|
+
this.grpcServer = new GrpcServer(this._port);
|
|
1949
|
+
this._port = (await this.grpcServer.createNewServer()).toString();
|
|
1950
|
+
}
|
|
1951
|
+
async startGrpcServer() {
|
|
1952
|
+
if (this.service) {
|
|
1953
|
+
this._serviceName = this.service.protoDescription.substring(
|
|
1954
|
+
this.service.protoDescription.indexOf(".") + 1
|
|
1955
|
+
);
|
|
1956
|
+
await this.grpcServer.addService(
|
|
1957
|
+
this.service.protoPath,
|
|
1958
|
+
this.service.protoDescription,
|
|
1959
|
+
this.service.functions
|
|
1960
|
+
);
|
|
1961
|
+
}
|
|
1962
|
+
RoutingManager.ClientController = new RoutingController();
|
|
1963
|
+
RoutingManager.AdminController = new RoutingController();
|
|
1964
|
+
await this.addHealthCheckService();
|
|
1965
|
+
await this.addModuleService();
|
|
1966
|
+
await this.grpcServer.start();
|
|
1967
|
+
ConduitGrpcSdk8.Logger.log("gRPC server listening on " + this._port);
|
|
1968
|
+
}
|
|
1969
|
+
async setConfig(call, callback) {
|
|
1970
|
+
try {
|
|
1971
|
+
if (!this.config) {
|
|
1972
|
+
return callback({
|
|
1973
|
+
code: status5.INVALID_ARGUMENT,
|
|
1974
|
+
message: "Module is not configurable"
|
|
1780
1975
|
});
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
this.
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1976
|
+
}
|
|
1977
|
+
let config = JSON.parse(call.request.newConfig);
|
|
1978
|
+
config = merge(this.config.getProperties(), config);
|
|
1979
|
+
config = await this.preConfig(config);
|
|
1980
|
+
const previousConfig = this.config.getProperties();
|
|
1981
|
+
try {
|
|
1982
|
+
this.config.load(config).validate({
|
|
1983
|
+
allowed: "warn"
|
|
1984
|
+
});
|
|
1985
|
+
config = this.config.getProperties();
|
|
1986
|
+
callback(null, { updatedConfig: JSON.stringify(config) });
|
|
1987
|
+
} catch (e) {
|
|
1988
|
+
this.config = convict(this.configSchema);
|
|
1989
|
+
this.config.load(previousConfig);
|
|
1990
|
+
return callback({
|
|
1991
|
+
code: status5.INVALID_ARGUMENT,
|
|
1992
|
+
message: "Invalid configuration values: " + e.message
|
|
1993
|
+
});
|
|
1994
|
+
}
|
|
1995
|
+
const configJson = JSON.stringify(config);
|
|
1996
|
+
this._pendingConfigUpdates.push(configJson);
|
|
1997
|
+
await this._drainConfigQueue();
|
|
1998
|
+
this.grpcSdk.bus?.publish(kebabCase(this.name) + ":config:update", configJson);
|
|
1999
|
+
} catch (e) {
|
|
2000
|
+
return callback({ code: status5.INTERNAL, message: e.message });
|
|
2001
|
+
}
|
|
2002
|
+
}
|
|
2003
|
+
/** Used to update the module's configuration on initial Redis/DB reconciliation. */
|
|
2004
|
+
async handleConfigSyncUpdate() {
|
|
2005
|
+
if (!this.config) return;
|
|
2006
|
+
this.grpcSdk.bus.subscribe(
|
|
2007
|
+
`${kebabCase(this.name)}:config:update`,
|
|
2008
|
+
async (message) => {
|
|
2009
|
+
this._pendingConfigUpdates.push(message);
|
|
2010
|
+
await this._drainConfigQueue();
|
|
2011
|
+
}
|
|
2012
|
+
);
|
|
2013
|
+
}
|
|
2014
|
+
/** Serializes config updates; waits for initial lifecycle before draining. */
|
|
2015
|
+
async _drainConfigQueue() {
|
|
2016
|
+
if (!this._configInitialized || this._processingConfig) return;
|
|
2017
|
+
this._processingConfig = true;
|
|
2018
|
+
try {
|
|
2019
|
+
while (this._pendingConfigUpdates.length > 0) {
|
|
2020
|
+
const message = this._pendingConfigUpdates.shift();
|
|
2021
|
+
ConfigController.getInstance().config = await this.preConfig(JSON.parse(message));
|
|
2022
|
+
await this.onConfig();
|
|
2023
|
+
}
|
|
2024
|
+
} finally {
|
|
2025
|
+
this._processingConfig = false;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
async preRegisterLifecycle() {
|
|
2029
|
+
await this.createGrpcServer();
|
|
2030
|
+
this._moduleState = "pre-grpc-start" /* PRE_SERVER_START */;
|
|
2031
|
+
await this.preServerStart();
|
|
2032
|
+
await this.grpcSdk.initializeEventBus();
|
|
2033
|
+
await this.handleConfigSyncUpdate();
|
|
2034
|
+
await this.registerMetrics();
|
|
2035
|
+
await this.startGrpcServer();
|
|
2036
|
+
this._moduleState = "grpc-server-started" /* SERVER_STARTED */;
|
|
2037
|
+
await this.onServerStart();
|
|
2038
|
+
await this.initializeMetrics();
|
|
2039
|
+
this._moduleState = "pre-register" /* PRE_REGISTER */;
|
|
2040
|
+
await this.preRegister();
|
|
2041
|
+
}
|
|
2042
|
+
async postRegisterLifecycle() {
|
|
2043
|
+
this._moduleState = "post-register" /* POST_REGISTER */;
|
|
2044
|
+
this.registered = true;
|
|
2045
|
+
await this.onRegister();
|
|
2046
|
+
if (this.config) {
|
|
2047
|
+
const configSchema = this.config.getSchema();
|
|
2048
|
+
let config = this.config.getProperties();
|
|
2049
|
+
config = await this.preConfig(config);
|
|
2050
|
+
config = await this.grpcSdk.config.configure(
|
|
2051
|
+
config,
|
|
2052
|
+
convictConfigParser(configSchema),
|
|
2053
|
+
this.configOverride
|
|
2054
|
+
);
|
|
2055
|
+
ConfigController.getInstance();
|
|
2056
|
+
if (config) ConfigController.getInstance().config = config;
|
|
2057
|
+
if (!config || config.active || !config.hasOwnProperty("active"))
|
|
2058
|
+
await this.onConfig();
|
|
2059
|
+
}
|
|
2060
|
+
this._configInitialized = true;
|
|
2061
|
+
await this._drainConfigQueue();
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
1826
2064
|
|
|
1827
|
-
|
|
1828
|
-
|
|
2065
|
+
// src/classes/ModuleError.ts
|
|
2066
|
+
import { GrpcError } from "@conduitplatform/grpc-sdk";
|
|
2067
|
+
var ModuleError = class extends GrpcError {
|
|
2068
|
+
debugLogInfo;
|
|
2069
|
+
constructor(errorDefinition, debugLogInfo) {
|
|
2070
|
+
const { grpcCode, conduitCode, message } = errorDefinition;
|
|
2071
|
+
super(
|
|
2072
|
+
grpcCode,
|
|
2073
|
+
JSON.stringify({
|
|
2074
|
+
message,
|
|
2075
|
+
conduitCode
|
|
2076
|
+
})
|
|
2077
|
+
);
|
|
2078
|
+
if (debugLogInfo) this.debugLogInfo = debugLogInfo;
|
|
2079
|
+
}
|
|
2080
|
+
};
|
|
2081
|
+
export {
|
|
2082
|
+
ConduitActiveSchema,
|
|
2083
|
+
ConduitBoolean,
|
|
2084
|
+
ConduitDate,
|
|
2085
|
+
ConduitJson,
|
|
2086
|
+
ConduitNumber,
|
|
2087
|
+
ConduitObjectId,
|
|
2088
|
+
ConduitRelation,
|
|
2089
|
+
ConduitServiceModule,
|
|
2090
|
+
ConduitString,
|
|
2091
|
+
ConfigController,
|
|
2092
|
+
DEFAULT_STRIP_FIELDS,
|
|
2093
|
+
GrpcServer,
|
|
2094
|
+
ManagedModule,
|
|
2095
|
+
ModuleError,
|
|
2096
|
+
ModuleLifecycleStage,
|
|
2097
|
+
RouteBuilder,
|
|
2098
|
+
RoutingManager,
|
|
2099
|
+
addServiceToServer,
|
|
2100
|
+
createParsedRouterRequest,
|
|
2101
|
+
createServer,
|
|
2102
|
+
exportModel,
|
|
2103
|
+
importModel,
|
|
2104
|
+
initializeSdk,
|
|
2105
|
+
linearBackoffTimeout,
|
|
2106
|
+
linearBackoffTimeoutAsync,
|
|
2107
|
+
merge,
|
|
2108
|
+
readConduitPeersManifest,
|
|
2109
|
+
sanitizeDocumentsForExport,
|
|
2110
|
+
stripInternalFields,
|
|
2111
|
+
wrapFunctionsAsync,
|
|
2112
|
+
wrapGrpcFunctions,
|
|
2113
|
+
wrapRouterGrpcFunction
|
|
2114
|
+
};
|
|
2115
|
+
//# sourceMappingURL=index.esm.js.map
|