@reventlessdev/reventless-local 3.0.0-alpha.316 → 3.0.0-alpha.318
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/CHANGELOG.md +16 -0
- package/package.json +11 -11
- package/src/LocalPlatformRegistry.res +19 -0
- package/src/LocalPlatformRegistry.res.mjs +19 -1
- package/src/Platform.res +24 -2
- package/src/Platform.res.mjs +22 -2
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res +38 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_InMemory.res.mjs +29 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res +87 -0
- package/src/adapter/DcbEventLog/DcbEventLogStorage_Sqlite.res.mjs +76 -0
- package/src/adapter/DcbEventLog/LocalDcbEventLogStorage.res +8 -1
- package/src/adapter/DcbEventLog/LocalDcbEventLogStorage.res.mjs +17 -4
- package/src/adapter/EventLog/EventLogStorage_InMemory.res +49 -2
- package/src/adapter/EventLog/EventLogStorage_InMemory.res.mjs +57 -21
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res +85 -0
- package/src/adapter/EventLog/EventLogStorage_Sqlite.res.mjs +50 -0
- package/src/adapter/EventLog/LocalEventLogStorage.res +3 -0
- package/src/adapter/EventLog/LocalEventLogStorage.res.mjs +6 -2
- package/src/adapter/LocalBus.res +32 -5
- package/src/adapter/LocalBus.res.mjs +27 -7
- package/src/adapter/LocalEventLogRead.res +477 -0
- package/src/adapter/LocalEventLogRead.res.mjs +507 -0
- package/src/adapter/LocalEventTap.res +18 -80
- package/src/adapter/LocalEventTap.res.mjs +19 -108
- package/src/adapter/LocalFeed.res +433 -0
- package/src/adapter/LocalFeed.res.mjs +419 -0
- package/src/adapter/LocalLogFeed.res +217 -0
- package/src/adapter/LocalLogFeed.res.mjs +295 -0
- package/src/adapter/LocalPgEventLogRead.res +85 -0
- package/src/adapter/LocalPgEventLogRead.res.mjs +91 -0
- package/src/adapter/LocalPlatformObserve.res +456 -0
- package/src/adapter/LocalPlatformObserve.res.mjs +505 -0
- package/src/adapter/LocalTapLines.res +147 -0
- package/src/adapter/LocalTapLines.res.mjs +229 -0
- package/src/adapter/PlatformGraphQL_Server.res +36 -3
- package/src/adapter/PlatformGraphQL_Server.res.mjs +15 -1
- package/src/reexport/CommandTopic.res.mjs +5 -2
- package/tests/adapter/GraphQLWsFixtures.mjs +46 -0
- package/tests/adapter/LocalEventLogReadTest.res +539 -0
- package/tests/adapter/LocalEventLogReadTest.res.mjs +466 -0
- package/tests/adapter/LocalFeedTest.res +314 -0
- package/tests/adapter/LocalFeedTest.res.mjs +329 -0
- package/tests/adapter/LocalPlatformObserveTest.res +165 -0
- package/tests/adapter/LocalPlatformObserveTest.res.mjs +183 -0
- package/tests/adapter/LocalTapLinesTest.res +182 -0
- package/tests/adapter/LocalTapLinesTest.res.mjs +242 -0
- package/tests/adapter/PlatformSubscriptionsTest.res +123 -0
- package/tests/adapter/PlatformSubscriptionsTest.res.mjs +132 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res +80 -0
- package/tests/components/automationslice/AutomationSliceCallbackTest.res.mjs +81 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res +17 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockFixtures.res.mjs +24 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res +30 -0
- package/tests/components/automationslice/AutomationSliceSelfDeadlockTest.res.mjs +28 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res +58 -0
- package/tests/components/outboundtranslationslice/OutboundTranslationSliceCallbackTest.res.mjs +71 -19
- package/tests/components/outboundtranslationslice/OutboundTranslationSlicePiiTest.res.mjs +7 -7
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
|
+
import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
|
|
5
|
+
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
6
|
+
import * as LogRedaction$Reventless from "@reventlessdev/reventless-spec/src/util/LogRedaction.res.mjs";
|
|
7
|
+
import * as ViewChange$ReventlessCore from "@reventlessdev/reventless-core/src/ViewChange.res.mjs";
|
|
8
|
+
import * as LocalLogFeed$ReventlessLocal from "./LocalLogFeed.res.mjs";
|
|
9
|
+
import * as LocalEventTap$ReventlessLocal from "./LocalEventTap.res.mjs";
|
|
10
|
+
import * as CommandTopic_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/components/CommandTopic/CommandTopic_Helpers.res.mjs";
|
|
11
|
+
|
|
12
|
+
function str(prim) {
|
|
13
|
+
return prim;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function now() {
|
|
17
|
+
return new Date().toISOString();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function lineOf(fields) {
|
|
21
|
+
return JSON.stringify(Object.fromEntries(fields));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function optional(name, value) {
|
|
25
|
+
return Stdlib_Option.mapOr(value, [], v => [[
|
|
26
|
+
name,
|
|
27
|
+
v
|
|
28
|
+
]]);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function compOf(r) {
|
|
32
|
+
return `CommandTopic(` + r.component + `)`;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function pluginOf(r) {
|
|
36
|
+
return Logger$ReventlessCore.resolvePlugin(compOf(r), undefined);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function outcomeLine(r) {
|
|
40
|
+
let plugin = pluginOf(r);
|
|
41
|
+
let head = [
|
|
42
|
+
[
|
|
43
|
+
"kind",
|
|
44
|
+
"commandOutcome"
|
|
45
|
+
],
|
|
46
|
+
[
|
|
47
|
+
"component",
|
|
48
|
+
r.component
|
|
49
|
+
]
|
|
50
|
+
].concat(optional("plugin", plugin)).concat([[
|
|
51
|
+
"msgId",
|
|
52
|
+
r.reference
|
|
53
|
+
]]).concat(optional("correlationId", r.correlationId));
|
|
54
|
+
let match = r.outcome;
|
|
55
|
+
let body;
|
|
56
|
+
body = match.TAG === "OutcomeAccepted" ? [[
|
|
57
|
+
"outcome",
|
|
58
|
+
"accepted"
|
|
59
|
+
]].concat(optional("entityId", match.entityId)).concat([[
|
|
60
|
+
"eventCount",
|
|
61
|
+
match.eventCount
|
|
62
|
+
]]) : [
|
|
63
|
+
[
|
|
64
|
+
"outcome",
|
|
65
|
+
"rejected"
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
"errorCode",
|
|
69
|
+
match.errorCode
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
"cause",
|
|
73
|
+
CommandTopic_Helpers$ReventlessCore.refusalCauseToGraphQL(match.cause)
|
|
74
|
+
]
|
|
75
|
+
];
|
|
76
|
+
return lineOf(head.concat(body).concat([[
|
|
77
|
+
"ts",
|
|
78
|
+
new Date().toISOString()
|
|
79
|
+
]]));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function rejectionRecord(r) {
|
|
83
|
+
let match = r.outcome;
|
|
84
|
+
if (match.TAG === "OutcomeAccepted") {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
let cause = match.cause;
|
|
88
|
+
let errorDetail = match.errorDetail;
|
|
89
|
+
let level;
|
|
90
|
+
switch (cause) {
|
|
91
|
+
case "InfrastructureFailure" :
|
|
92
|
+
level = "Error";
|
|
93
|
+
break;
|
|
94
|
+
case "DomainRejection" :
|
|
95
|
+
case "AccessRefusal" :
|
|
96
|
+
level = "Warn";
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
let detailJson;
|
|
100
|
+
if (errorDetail === "") {
|
|
101
|
+
detailJson = [];
|
|
102
|
+
} else {
|
|
103
|
+
let parsed;
|
|
104
|
+
try {
|
|
105
|
+
parsed = JSON.parse(errorDetail);
|
|
106
|
+
} catch (exn) {
|
|
107
|
+
parsed = errorDetail;
|
|
108
|
+
}
|
|
109
|
+
detailJson = [[
|
|
110
|
+
"errorDetail",
|
|
111
|
+
Logger$ReventlessCore.truncateDetail(LogRedaction$Reventless.redactJson(parsed))
|
|
112
|
+
]];
|
|
113
|
+
}
|
|
114
|
+
let annotations = Object.fromEntries([[
|
|
115
|
+
"msgId",
|
|
116
|
+
r.reference
|
|
117
|
+
]]);
|
|
118
|
+
Stdlib_Option.forEach(r.correlationId, id => {
|
|
119
|
+
annotations["correlationId"] = id;
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
time: new Date().toISOString(),
|
|
123
|
+
level: level,
|
|
124
|
+
message: "command rejected",
|
|
125
|
+
service: Logger$ReventlessCore.serviceName(),
|
|
126
|
+
plugin: pluginOf(r),
|
|
127
|
+
comp: compOf(r),
|
|
128
|
+
annotations: annotations,
|
|
129
|
+
detail: Object.fromEntries([
|
|
130
|
+
[
|
|
131
|
+
"component",
|
|
132
|
+
r.component
|
|
133
|
+
],
|
|
134
|
+
[
|
|
135
|
+
"errorCode",
|
|
136
|
+
match.errorCode
|
|
137
|
+
],
|
|
138
|
+
[
|
|
139
|
+
"cause",
|
|
140
|
+
CommandTopic_Helpers$ReventlessCore.refusalCauseToGraphQL(cause)
|
|
141
|
+
]
|
|
142
|
+
].concat(detailJson))
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function onOutcome(r) {
|
|
147
|
+
Stdlib_Option.forEach(rejectionRecord(r), LocalLogFeed$ReventlessLocal.keepProblem);
|
|
148
|
+
if (LocalEventTap$ReventlessLocal.hasReaders()) {
|
|
149
|
+
return LocalEventTap$ReventlessLocal.publish({
|
|
150
|
+
line: outcomeLine(r),
|
|
151
|
+
facets: {
|
|
152
|
+
plugin: pluginOf(r),
|
|
153
|
+
correlationId: r.correlationId
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
function viewChangeLine(c) {
|
|
160
|
+
let side = (name, value) => Stdlib_Option.mapOr(value, [], v => [[
|
|
161
|
+
name,
|
|
162
|
+
Logger$ReventlessCore.truncateDetail(LogRedaction$Reventless.redactJson(v))
|
|
163
|
+
]]);
|
|
164
|
+
return lineOf([[
|
|
165
|
+
"kind",
|
|
166
|
+
"viewChange"
|
|
167
|
+
]].concat(ViewChange$ReventlessCore.summaryFields(c)).concat(optional("correlationId", c.correlationId)).concat(side("before", c.before)).concat(side("after", c.after)).concat([[
|
|
168
|
+
"ts",
|
|
169
|
+
new Date().toISOString()
|
|
170
|
+
]]));
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function onViewChange(c) {
|
|
174
|
+
if (LocalEventTap$ReventlessLocal.hasReaders()) {
|
|
175
|
+
return LocalEventTap$ReventlessLocal.publish({
|
|
176
|
+
line: viewChangeLine(c),
|
|
177
|
+
facets: {
|
|
178
|
+
plugin: Logger$ReventlessCore.resolvePlugin(`View(` + c.view + `)`, undefined),
|
|
179
|
+
correlationId: c.correlationId
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
let removals = {
|
|
186
|
+
contents: []
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
function install() {
|
|
190
|
+
if (Primitive_object.equal(removals.contents, [])) {
|
|
191
|
+
removals.contents = [
|
|
192
|
+
CommandTopic_Helpers$ReventlessCore.registerCommandOutcome(onOutcome),
|
|
193
|
+
ViewChange$ReventlessCore.register(onViewChange)
|
|
194
|
+
];
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function resetForTests() {
|
|
200
|
+
removals.contents.forEach(remove => remove());
|
|
201
|
+
removals.contents = [];
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
let Logger;
|
|
205
|
+
|
|
206
|
+
let Outcome;
|
|
207
|
+
|
|
208
|
+
let ViewChange;
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
Logger,
|
|
212
|
+
Outcome,
|
|
213
|
+
ViewChange,
|
|
214
|
+
str,
|
|
215
|
+
now,
|
|
216
|
+
lineOf,
|
|
217
|
+
optional,
|
|
218
|
+
compOf,
|
|
219
|
+
pluginOf,
|
|
220
|
+
outcomeLine,
|
|
221
|
+
rejectionRecord,
|
|
222
|
+
onOutcome,
|
|
223
|
+
viewChangeLine,
|
|
224
|
+
onViewChange,
|
|
225
|
+
removals,
|
|
226
|
+
install,
|
|
227
|
+
resetForTests,
|
|
228
|
+
}
|
|
229
|
+
/* Logger-ReventlessCore Not a pure module */
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
// Does NOT include Relay-specific features (encodeGlobalId, node resolver)
|
|
9
9
|
// since admin queries are not Relay-paginated.
|
|
10
10
|
//
|
|
11
|
-
// Authorization: every query AND
|
|
12
|
-
// wrapped with `Auth_GraphqlContext.requireGroup(~group="Admin")` so
|
|
11
|
+
// Authorization: every query, mutation AND subscription resolver registered
|
|
12
|
+
// here is wrapped with `Auth_GraphqlContext.requireGroup(~group="Admin")` so
|
|
13
13
|
// non-Admin identities are refused before the underlying resolver runs.
|
|
14
14
|
// Mirrors AppSync's `@aws_cognito_user_pools(cognito_groups: ["Admin"])`
|
|
15
15
|
// directive that gates Platform_* fields in the AWS adapter.
|
|
@@ -46,11 +46,44 @@ let wrapAdmin = (resolvers: dict<ReventlessGraphqlServer.GraphQL_ServerInstance.
|
|
|
46
46
|
wrapped
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
// A subscription field's resolver is an object: `subscribe` starts the stream,
|
|
50
|
+
// `resolve` maps each payload. The group check goes on `subscribe`, so a caller
|
|
51
|
+
// without the group is refused before anything is streamed to it.
|
|
52
|
+
type subscriptionResolver = {
|
|
53
|
+
subscribe: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn,
|
|
54
|
+
resolve?: JSON.t => JSON.t,
|
|
55
|
+
}
|
|
56
|
+
external asSubscription: ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn => subscriptionResolver =
|
|
57
|
+
"%identity"
|
|
58
|
+
external ofSubscription: subscriptionResolver => ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn =
|
|
59
|
+
"%identity"
|
|
60
|
+
|
|
61
|
+
let wrapAdminSubscriptions = (
|
|
62
|
+
resolvers: dict<ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn>,
|
|
63
|
+
): dict<ReventlessGraphqlServer.GraphQL_ServerInstance.resolverFn> =>
|
|
64
|
+
resolvers
|
|
65
|
+
->Dict.toArray
|
|
66
|
+
->Array.map(((k, v)) => {
|
|
67
|
+
let sub = asSubscription(v)
|
|
68
|
+
(
|
|
69
|
+
k,
|
|
70
|
+
ofSubscription({
|
|
71
|
+
...sub,
|
|
72
|
+
subscribe: Auth_GraphqlContext.requireGroup(
|
|
73
|
+
~group=Reventless.Role.adminGroup(),
|
|
74
|
+
sub.subscribe,
|
|
75
|
+
),
|
|
76
|
+
}),
|
|
77
|
+
)
|
|
78
|
+
})
|
|
79
|
+
->Dict.fromArray
|
|
80
|
+
|
|
49
81
|
let registerMutations = (~sdlFields, ~resolvers) =>
|
|
50
82
|
instance.registerMutations(~sdlFields, ~resolvers=wrapAdmin(resolvers))
|
|
51
83
|
let registerQueries = (~sdlFields, ~resolvers) =>
|
|
52
84
|
instance.registerQueries(~sdlFields, ~resolvers=wrapAdmin(resolvers))
|
|
53
|
-
let registerSubscriptions =
|
|
85
|
+
let registerSubscriptions = (~sdlFields, ~resolvers) =>
|
|
86
|
+
instance.registerSubscriptions(~sdlFields, ~resolvers=wrapAdminSubscriptions(resolvers))
|
|
54
87
|
let registerTypes = instance.registerTypes
|
|
55
88
|
// Not admin-wrapped: a field resolver is only reached through a parent field
|
|
56
89
|
// that already met the group check, so wrapping would re-refuse a caller the
|
|
@@ -14,6 +14,17 @@ function wrapAdmin(resolvers) {
|
|
|
14
14
|
return wrapped;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
function wrapAdminSubscriptions(resolvers) {
|
|
18
|
+
return Object.fromEntries(Object.entries(resolvers).map(param => {
|
|
19
|
+
let v = param[1];
|
|
20
|
+
let newrecord = {...v};
|
|
21
|
+
return [
|
|
22
|
+
param[0],
|
|
23
|
+
(newrecord.subscribe = Auth_GraphqlContext$ReventlessLocal.requireGroup(Role$Reventless.adminGroup(), v.subscribe), newrecord)
|
|
24
|
+
];
|
|
25
|
+
}));
|
|
26
|
+
}
|
|
27
|
+
|
|
17
28
|
function registerMutations(sdlFields, resolvers) {
|
|
18
29
|
instance.registerMutations(sdlFields, wrapAdmin(resolvers));
|
|
19
30
|
}
|
|
@@ -22,7 +33,9 @@ function registerQueries(sdlFields, resolvers) {
|
|
|
22
33
|
instance.registerQueries(sdlFields, wrapAdmin(resolvers));
|
|
23
34
|
}
|
|
24
35
|
|
|
25
|
-
|
|
36
|
+
function registerSubscriptions(sdlFields, resolvers) {
|
|
37
|
+
instance.registerSubscriptions(sdlFields, wrapAdminSubscriptions(resolvers));
|
|
38
|
+
}
|
|
26
39
|
|
|
27
40
|
let registerTypes = instance.registerTypes;
|
|
28
41
|
|
|
@@ -69,6 +82,7 @@ let asInterface = {
|
|
|
69
82
|
export {
|
|
70
83
|
instance,
|
|
71
84
|
wrapAdmin,
|
|
85
|
+
wrapAdminSubscriptions,
|
|
72
86
|
registerMutations,
|
|
73
87
|
registerQueries,
|
|
74
88
|
registerSubscriptions,
|
|
@@ -8,7 +8,9 @@ let NotPublishedToChannel = CommandTopic$ReventlessCore.NotPublishedToChannel;
|
|
|
8
8
|
|
|
9
9
|
let refusalCauseToGraphQL = CommandTopic$ReventlessCore.refusalCauseToGraphQL;
|
|
10
10
|
|
|
11
|
-
let
|
|
11
|
+
let commandOutcomeHooks = CommandTopic$ReventlessCore.commandOutcomeHooks;
|
|
12
|
+
|
|
13
|
+
let nextOutcomeHookId = CommandTopic$ReventlessCore.nextOutcomeHookId;
|
|
12
14
|
|
|
13
15
|
let registerCommandOutcome = CommandTopic$ReventlessCore.registerCommandOutcome;
|
|
14
16
|
|
|
@@ -50,7 +52,8 @@ export {
|
|
|
50
52
|
componentType,
|
|
51
53
|
NotPublishedToChannel,
|
|
52
54
|
refusalCauseToGraphQL,
|
|
53
|
-
|
|
55
|
+
commandOutcomeHooks,
|
|
56
|
+
nextOutcomeHookId,
|
|
54
57
|
registerCommandOutcome,
|
|
55
58
|
clearCommandOutcome,
|
|
56
59
|
fireCommandOutcome,
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// A graphql-ws client for the subscription tests, here rather than in an
|
|
2
|
+
// `Obj.magic` inside the test, per the repo convention that untyped reflection
|
|
3
|
+
// lives in a companion `.mjs`.
|
|
4
|
+
import { createClient } from 'graphql-ws'
|
|
5
|
+
import WebSocket from 'ws'
|
|
6
|
+
|
|
7
|
+
/** Subscribes, collects values until `count` arrive (or an error or `timeoutMs`),
|
|
8
|
+
calling `onStarted` once the server acknowledged the connection. Resolves
|
|
9
|
+
`{ values, error }`; `error` is the first error message, if any. */
|
|
10
|
+
export const collect = (url, query, connectionParams, count, timeoutMs, onStarted) =>
|
|
11
|
+
new Promise(resolve => {
|
|
12
|
+
const values = []
|
|
13
|
+
let settled = false
|
|
14
|
+
const client = createClient({
|
|
15
|
+
url,
|
|
16
|
+
webSocketImpl: WebSocket,
|
|
17
|
+
connectionParams,
|
|
18
|
+
lazy: false,
|
|
19
|
+
retryAttempts: 0,
|
|
20
|
+
on: { connected: () => onStarted() },
|
|
21
|
+
})
|
|
22
|
+
const finish = error => {
|
|
23
|
+
if (settled) return
|
|
24
|
+
settled = true
|
|
25
|
+
clearTimeout(timer)
|
|
26
|
+
dispose()
|
|
27
|
+
client.dispose()
|
|
28
|
+
resolve({ values, error })
|
|
29
|
+
}
|
|
30
|
+
const timer = setTimeout(() => finish(undefined), timeoutMs)
|
|
31
|
+
const dispose = client.subscribe(
|
|
32
|
+
{ query },
|
|
33
|
+
{
|
|
34
|
+
next: msg => {
|
|
35
|
+
if (msg.errors && msg.errors.length > 0) return finish(String(msg.errors[0].message))
|
|
36
|
+
values.push(Object.values(msg.data)[0])
|
|
37
|
+
if (values.length >= count) finish(undefined)
|
|
38
|
+
},
|
|
39
|
+
error: err => {
|
|
40
|
+
const m = Array.isArray(err) ? err[0]?.message : err?.message ?? err?.reason ?? String(err)
|
|
41
|
+
finish(String(m))
|
|
42
|
+
},
|
|
43
|
+
complete: () => finish(undefined),
|
|
44
|
+
},
|
|
45
|
+
)
|
|
46
|
+
})
|