@reventlessdev/reventless-local 3.0.0-alpha.173 → 3.0.0-alpha.175
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 +15 -0
- package/package.json +9 -9
- package/src/Platform.res +19 -6
- package/src/Platform.res.mjs +10 -0
- package/src/adapter/Api/LocalEvents_Server.res +324 -0
- package/src/adapter/Api/LocalEvents_Server.res.mjs +341 -0
- package/src/adapter/DomainGraphQL_Server.res +23 -0
- package/src/adapter/DomainGraphQL_Server.res.mjs +36 -14
- package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res +352 -0
- package/src/adapter/EventHistory/EventHistoryResolvers_GraphQL.res.mjs +362 -0
- package/src/adapter/LocalBus.res +12 -1
- package/src/adapter/LocalBus.res.mjs +36 -0
- package/tests/adapter/EventHistoryResolverTest.res +208 -0
- package/tests/adapter/EventHistoryResolverTest.res.mjs +286 -0
- package/tests/adapter/LocalEvents_ServerTest.res +200 -0
- package/tests/adapter/LocalEvents_ServerTest.res.mjs +147 -0
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
|
+
|
|
3
|
+
import * as Ws from "ws";
|
|
4
|
+
import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
|
|
5
|
+
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
6
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
7
|
+
import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/Logger.res.mjs";
|
|
8
|
+
import * as LocalAuth$ReventlessLocal from "../Auth/LocalAuth.res.mjs";
|
|
9
|
+
|
|
10
|
+
let log = Logger$ReventlessCore.fromEnv();
|
|
11
|
+
|
|
12
|
+
function pathSegment(s) {
|
|
13
|
+
return s.replace(/[^A-Za-z0-9-]/g, "-");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let clientChannelPrefix = "/client/";
|
|
17
|
+
|
|
18
|
+
function channelMatches(subscription, channel) {
|
|
19
|
+
if (!subscription.endsWith("/*")) {
|
|
20
|
+
return subscription === channel;
|
|
21
|
+
}
|
|
22
|
+
let prefix = subscription.slice(0, subscription.length - 1 | 0);
|
|
23
|
+
return channel.startsWith(prefix);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let connections = {
|
|
27
|
+
contents: []
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
let nextConnectionId = {
|
|
31
|
+
contents: 0
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function addConnection(send) {
|
|
35
|
+
nextConnectionId.contents = nextConnectionId.contents + 1 | 0;
|
|
36
|
+
let conn_id = nextConnectionId.contents;
|
|
37
|
+
let conn_subscriptions = {};
|
|
38
|
+
let conn = {
|
|
39
|
+
id: conn_id,
|
|
40
|
+
send: send,
|
|
41
|
+
subscriptions: conn_subscriptions
|
|
42
|
+
};
|
|
43
|
+
connections.contents.push(conn);
|
|
44
|
+
return conn;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function removeConnection(conn) {
|
|
48
|
+
connections.contents = connections.contents.filter(c => c.id !== conn.id);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function resetConnections() {
|
|
52
|
+
connections.contents = [];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function frame(fields) {
|
|
56
|
+
return JSON.stringify(Object.fromEntries(fields));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
let connectionAckFrame = frame([
|
|
60
|
+
[
|
|
61
|
+
"type",
|
|
62
|
+
"connection_ack"
|
|
63
|
+
],
|
|
64
|
+
[
|
|
65
|
+
"connectionTimeoutMs",
|
|
66
|
+
300000
|
|
67
|
+
]
|
|
68
|
+
]);
|
|
69
|
+
|
|
70
|
+
function dataFrame(subscriptionId, event) {
|
|
71
|
+
return frame([
|
|
72
|
+
[
|
|
73
|
+
"type",
|
|
74
|
+
"data"
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
"id",
|
|
78
|
+
subscriptionId
|
|
79
|
+
],
|
|
80
|
+
[
|
|
81
|
+
"event",
|
|
82
|
+
event
|
|
83
|
+
]
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function broadcast(channel, event) {
|
|
88
|
+
connections.contents.forEach(conn => {
|
|
89
|
+
Object.entries(conn.subscriptions).forEach(param => {
|
|
90
|
+
if (channelMatches(param[1], channel)) {
|
|
91
|
+
return conn.send(dataFrame(param[0], event));
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function broadcastStateChange(name, descriptor) {
|
|
98
|
+
let entityKey = Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(descriptor), o => o["id"]), Stdlib_JSON.Decode.string), "");
|
|
99
|
+
if (entityKey === "") {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
let channel = `/default/` + pathSegment(name) + `/` + pathSegment(entityKey);
|
|
103
|
+
broadcast(channel, JSON.stringify(descriptor));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function decodeStringField(json, field) {
|
|
107
|
+
return Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(json), o => o[field]), Stdlib_JSON.Decode.string);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function handleFrame(conn, text) {
|
|
111
|
+
let parsed;
|
|
112
|
+
try {
|
|
113
|
+
parsed = JSON.parse(text);
|
|
114
|
+
} catch (exn) {
|
|
115
|
+
parsed = undefined;
|
|
116
|
+
}
|
|
117
|
+
if (parsed === undefined) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
let match = decodeStringField(parsed, "type");
|
|
121
|
+
if (match === undefined) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
switch (match) {
|
|
125
|
+
case "connection_init" :
|
|
126
|
+
return conn.send(connectionAckFrame);
|
|
127
|
+
case "subscribe" :
|
|
128
|
+
let match$1 = decodeStringField(parsed, "id");
|
|
129
|
+
let match$2 = decodeStringField(parsed, "channel");
|
|
130
|
+
if (match$1 !== undefined && match$2 !== undefined) {
|
|
131
|
+
conn.subscriptions[match$1] = match$2;
|
|
132
|
+
return conn.send(frame([
|
|
133
|
+
[
|
|
134
|
+
"type",
|
|
135
|
+
"subscribe_success"
|
|
136
|
+
],
|
|
137
|
+
[
|
|
138
|
+
"id",
|
|
139
|
+
match$1
|
|
140
|
+
]
|
|
141
|
+
]));
|
|
142
|
+
} else {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
case "unsubscribe" :
|
|
146
|
+
let id = decodeStringField(parsed, "id");
|
|
147
|
+
if (id !== undefined) {
|
|
148
|
+
Stdlib_Dict.$$delete(conn.subscriptions, id);
|
|
149
|
+
return conn.send(frame([
|
|
150
|
+
[
|
|
151
|
+
"type",
|
|
152
|
+
"unsubscribe_success"
|
|
153
|
+
],
|
|
154
|
+
[
|
|
155
|
+
"id",
|
|
156
|
+
id
|
|
157
|
+
]
|
|
158
|
+
]));
|
|
159
|
+
} else {
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
default:
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function stripBearer(token) {
|
|
168
|
+
if (token.startsWith("Bearer ")) {
|
|
169
|
+
return token.slice(7, token.length).trim();
|
|
170
|
+
} else {
|
|
171
|
+
return token.trim();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function tokenIsInvalid(token) {
|
|
176
|
+
if (token !== undefined) {
|
|
177
|
+
return Stdlib_Option.isNone(LocalAuth$ReventlessLocal.Login.verifyAndDecode(stripBearer(token)));
|
|
178
|
+
} else {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function authFromSubprotocolHeader(headerValue) {
|
|
184
|
+
return Stdlib_Option.flatMap(headerValue.split(",").map(prim => prim.trim()).find(p => p.startsWith("header-")), p => {
|
|
185
|
+
let blob = p.slice(7, p.length);
|
|
186
|
+
try {
|
|
187
|
+
return decodeStringField(JSON.parse(Buffer.from(blob, "base64url").toString("utf8")), "Authorization");
|
|
188
|
+
} catch (exn) {
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function jsonError(message) {
|
|
195
|
+
return Object.fromEntries([[
|
|
196
|
+
"error",
|
|
197
|
+
message
|
|
198
|
+
]]);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function handlePublish(authorization, body) {
|
|
202
|
+
if (tokenIsInvalid(authorization)) {
|
|
203
|
+
return [
|
|
204
|
+
401,
|
|
205
|
+
jsonError("Invalid token")
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
let parsed;
|
|
209
|
+
try {
|
|
210
|
+
parsed = JSON.parse(body);
|
|
211
|
+
} catch (exn) {
|
|
212
|
+
parsed = undefined;
|
|
213
|
+
}
|
|
214
|
+
if (parsed === undefined) {
|
|
215
|
+
return [
|
|
216
|
+
400,
|
|
217
|
+
jsonError("Invalid JSON body")
|
|
218
|
+
];
|
|
219
|
+
}
|
|
220
|
+
let channel = decodeStringField(parsed, "channel");
|
|
221
|
+
let events = Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(parsed), o => o["events"]), Stdlib_JSON.Decode.array);
|
|
222
|
+
if (channel === undefined) {
|
|
223
|
+
return [
|
|
224
|
+
400,
|
|
225
|
+
jsonError("Body must carry `channel` and a non-empty `events` array")
|
|
226
|
+
];
|
|
227
|
+
}
|
|
228
|
+
if (events === undefined) {
|
|
229
|
+
return [
|
|
230
|
+
400,
|
|
231
|
+
jsonError("Body must carry `channel` and a non-empty `events` array")
|
|
232
|
+
];
|
|
233
|
+
}
|
|
234
|
+
if (events.length === 0) {
|
|
235
|
+
return [
|
|
236
|
+
400,
|
|
237
|
+
jsonError("Body must carry `channel` and a non-empty `events` array")
|
|
238
|
+
];
|
|
239
|
+
}
|
|
240
|
+
if (!channel.startsWith(clientChannelPrefix)) {
|
|
241
|
+
return [
|
|
242
|
+
403,
|
|
243
|
+
jsonError(`Clients may only publish under ` + clientChannelPrefix)
|
|
244
|
+
];
|
|
245
|
+
}
|
|
246
|
+
let successful = [];
|
|
247
|
+
let failed = [];
|
|
248
|
+
events.forEach((event, index) => {
|
|
249
|
+
let entry = Object.fromEntries([[
|
|
250
|
+
"index",
|
|
251
|
+
index
|
|
252
|
+
]]);
|
|
253
|
+
let s = Stdlib_JSON.Decode.string(event);
|
|
254
|
+
if (s !== undefined) {
|
|
255
|
+
let valid;
|
|
256
|
+
try {
|
|
257
|
+
JSON.parse(s);
|
|
258
|
+
valid = true;
|
|
259
|
+
} catch (exn) {
|
|
260
|
+
valid = false;
|
|
261
|
+
}
|
|
262
|
+
if (valid) {
|
|
263
|
+
broadcast(channel, s);
|
|
264
|
+
successful.push(entry);
|
|
265
|
+
} else {
|
|
266
|
+
failed.push(entry);
|
|
267
|
+
}
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
failed.push(entry);
|
|
271
|
+
});
|
|
272
|
+
return [
|
|
273
|
+
200,
|
|
274
|
+
Object.fromEntries([
|
|
275
|
+
[
|
|
276
|
+
"successful",
|
|
277
|
+
successful
|
|
278
|
+
],
|
|
279
|
+
[
|
|
280
|
+
"failed",
|
|
281
|
+
failed
|
|
282
|
+
]
|
|
283
|
+
])
|
|
284
|
+
];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
let realtimePath = "/events/realtime";
|
|
288
|
+
|
|
289
|
+
let subprotocol = "aws-appsync-event-ws";
|
|
290
|
+
|
|
291
|
+
function attach(server, port) {
|
|
292
|
+
let wss = new Ws.WebSocketServer({
|
|
293
|
+
server: server,
|
|
294
|
+
path: realtimePath,
|
|
295
|
+
handleProtocols: (_protocols, _req) => subprotocol
|
|
296
|
+
});
|
|
297
|
+
wss.on("connection", (ws, req) => {
|
|
298
|
+
let auth = Stdlib_Option.flatMap(req.headers["sec-websocket-protocol"], authFromSubprotocolHeader);
|
|
299
|
+
if (tokenIsInvalid(auth)) {
|
|
300
|
+
ws.close(4401, "Invalid token");
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
let conn = addConnection(f => {
|
|
304
|
+
ws.send(f);
|
|
305
|
+
});
|
|
306
|
+
ws.on("message", data => handleFrame(conn, data.toString("utf8")));
|
|
307
|
+
ws.on("close", () => removeConnection(conn));
|
|
308
|
+
});
|
|
309
|
+
log.info("Events:Local", undefined, `events transport on ws://localhost:` + port.toString() + realtimePath + ` (publish: POST /events)`);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let YG;
|
|
313
|
+
|
|
314
|
+
export {
|
|
315
|
+
YG,
|
|
316
|
+
log,
|
|
317
|
+
pathSegment,
|
|
318
|
+
clientChannelPrefix,
|
|
319
|
+
channelMatches,
|
|
320
|
+
connections,
|
|
321
|
+
nextConnectionId,
|
|
322
|
+
addConnection,
|
|
323
|
+
removeConnection,
|
|
324
|
+
resetConnections,
|
|
325
|
+
frame,
|
|
326
|
+
connectionAckFrame,
|
|
327
|
+
dataFrame,
|
|
328
|
+
broadcast,
|
|
329
|
+
broadcastStateChange,
|
|
330
|
+
decodeStringField,
|
|
331
|
+
handleFrame,
|
|
332
|
+
stripBearer,
|
|
333
|
+
tokenIsInvalid,
|
|
334
|
+
authFromSubprotocolHeader,
|
|
335
|
+
jsonError,
|
|
336
|
+
handlePublish,
|
|
337
|
+
realtimePath,
|
|
338
|
+
subprotocol,
|
|
339
|
+
attach,
|
|
340
|
+
}
|
|
341
|
+
/* log Not a pure module */
|
|
@@ -234,6 +234,26 @@ let _dispatch = (req: nodeRequest, res: nodeResponse, yoga: YG.yoga, getSdl: uni
|
|
|
234
234
|
handleLogout(req, res)
|
|
235
235
|
} else if path == uploadPresignPath && req.method == "POST" {
|
|
236
236
|
handleUploadPresign(req, res)
|
|
237
|
+
} else if path == "/events" && req.method == "POST" {
|
|
238
|
+
// Local AppSync Events publish endpoint (`/client/**` only). The
|
|
239
|
+
// Authorization header carries the token raw (AWS Cognito-publish shape),
|
|
240
|
+
// so it bypasses `_isInvalidBearer` above and is verified inside.
|
|
241
|
+
let authorization = req.headers->Dict.get("authorization")
|
|
242
|
+
readBody(req, body => {
|
|
243
|
+
let (status, json) = LocalEvents_Server.handlePublish(~authorization, ~body)
|
|
244
|
+
_writeJson(res, ~status, json)
|
|
245
|
+
})
|
|
246
|
+
} else if path == "/events" && req.method == "OPTIONS" {
|
|
247
|
+
// Preflight for the cross-origin browser publish (Authorization header).
|
|
248
|
+
res->writeHead(
|
|
249
|
+
204,
|
|
250
|
+
{
|
|
251
|
+
"Access-Control-Allow-Origin": "*",
|
|
252
|
+
"Access-Control-Allow-Methods": "POST,OPTIONS",
|
|
253
|
+
"Access-Control-Allow-Headers": "*",
|
|
254
|
+
},
|
|
255
|
+
)
|
|
256
|
+
res->endEmpty
|
|
237
257
|
} else if req.method == "OPTIONS" && LocalObjectStore.servedKey(path)->Option.isSome {
|
|
238
258
|
// Preflight for the cross-origin browser PUT to a served-object path.
|
|
239
259
|
res->writeHead(204, _corsWriteHeaders)
|
|
@@ -692,6 +712,9 @@ let start = (~port: int=4000, ~contextFactory as _: option<YG.contextFactory>=?,
|
|
|
692
712
|
server->YG.listen(port, () =>
|
|
693
713
|
log.info(~comp="GraphQL:Domain", `listening on http://localhost:${port->Int.toString}/graphql (SDL: /sdl)`)
|
|
694
714
|
)
|
|
715
|
+
// Local AppSync Events transport (subscribe WS). Attached here so every
|
|
716
|
+
// start mode (split, unified, replay) carries it.
|
|
717
|
+
LocalEvents_Server.attach(~server, ~port)
|
|
695
718
|
activeServer.contents = Some(server)
|
|
696
719
|
}
|
|
697
720
|
|
|
@@ -19,6 +19,7 @@ import * as Logger$ReventlessCore from "@reventlessdev/reventless-core/src/util/
|
|
|
19
19
|
import * as LocalAuth$ReventlessLocal from "./Auth/LocalAuth.res.mjs";
|
|
20
20
|
import * as GraphQL_Stitcher$ReventlessCore from "@reventlessdev/reventless-core/src/components/Api/GraphQL_Stitcher.res.mjs";
|
|
21
21
|
import * as LocalObjectStore$ReventlessLocal from "./LocalObjectStore.res.mjs";
|
|
22
|
+
import * as LocalEvents_Server$ReventlessLocal from "./Api/LocalEvents_Server.res.mjs";
|
|
22
23
|
import * as Auth_GraphqlContext$ReventlessLocal from "./Auth/Auth_GraphqlContext.res.mjs";
|
|
23
24
|
|
|
24
25
|
let log = Logger$ReventlessCore.fromEnv();
|
|
@@ -175,30 +176,50 @@ function _dispatch(req, res, yoga, getSdl) {
|
|
|
175
176
|
"error",
|
|
176
177
|
"Invalid bearer token"
|
|
177
178
|
]]));
|
|
178
|
-
}
|
|
179
|
+
}
|
|
180
|
+
if (path === "/sdl") {
|
|
179
181
|
res.writeHead(200, {
|
|
180
182
|
"Content-Type": "text/plain",
|
|
181
183
|
"Access-Control-Allow-Origin": "*"
|
|
182
184
|
});
|
|
183
185
|
res.end(getSdl());
|
|
184
186
|
return;
|
|
185
|
-
}
|
|
187
|
+
}
|
|
188
|
+
if (path === "/__inmemory/login" && req.method === "POST") {
|
|
186
189
|
return handleLogin(req, res);
|
|
187
|
-
}
|
|
190
|
+
}
|
|
191
|
+
if (path === "/__inmemory/logout" && req.method === "POST") {
|
|
188
192
|
return handleLogout(req, res);
|
|
189
|
-
}
|
|
193
|
+
}
|
|
194
|
+
if (path === uploadPresignPath && req.method === "POST") {
|
|
190
195
|
return handleUploadPresign(req, res);
|
|
191
|
-
} else if (req.method === "OPTIONS" && Stdlib_Option.isSome(LocalObjectStore$ReventlessLocal.servedKey(path))) {
|
|
192
|
-
res.writeHead(204, _corsWriteHeaders);
|
|
193
|
-
res.end(null);
|
|
194
|
-
return;
|
|
195
|
-
} else if (req.method === "PUT" && Stdlib_Option.isSome(LocalObjectStore$ReventlessLocal.servedKey(path))) {
|
|
196
|
-
return handleObjectPut(req, res, Stdlib_Option.getOr(LocalObjectStore$ReventlessLocal.servedKey(path), ""));
|
|
197
|
-
} else if (req.method === "GET" && Stdlib_Option.isSome(LocalObjectStore$ReventlessLocal.servedKey(path))) {
|
|
198
|
-
return handleObjectGet(res, Stdlib_Option.getOr(LocalObjectStore$ReventlessLocal.servedKey(path), ""));
|
|
199
|
-
} else {
|
|
200
|
-
return yoga(req, res);
|
|
201
196
|
}
|
|
197
|
+
if (path !== "/events" || req.method !== "POST") {
|
|
198
|
+
if (path === "/events" && req.method === "OPTIONS") {
|
|
199
|
+
res.writeHead(204, {
|
|
200
|
+
"Access-Control-Allow-Origin": "*",
|
|
201
|
+
"Access-Control-Allow-Methods": "POST,OPTIONS",
|
|
202
|
+
"Access-Control-Allow-Headers": "*"
|
|
203
|
+
});
|
|
204
|
+
res.end(null);
|
|
205
|
+
return;
|
|
206
|
+
} else if (req.method === "OPTIONS" && Stdlib_Option.isSome(LocalObjectStore$ReventlessLocal.servedKey(path))) {
|
|
207
|
+
res.writeHead(204, _corsWriteHeaders);
|
|
208
|
+
res.end(null);
|
|
209
|
+
return;
|
|
210
|
+
} else if (req.method === "PUT" && Stdlib_Option.isSome(LocalObjectStore$ReventlessLocal.servedKey(path))) {
|
|
211
|
+
return handleObjectPut(req, res, Stdlib_Option.getOr(LocalObjectStore$ReventlessLocal.servedKey(path), ""));
|
|
212
|
+
} else if (req.method === "GET" && Stdlib_Option.isSome(LocalObjectStore$ReventlessLocal.servedKey(path))) {
|
|
213
|
+
return handleObjectGet(res, Stdlib_Option.getOr(LocalObjectStore$ReventlessLocal.servedKey(path), ""));
|
|
214
|
+
} else {
|
|
215
|
+
return yoga(req, res);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
let authorization = req.headers["authorization"];
|
|
219
|
+
readBody(req, body => {
|
|
220
|
+
let match = LocalEvents_Server$ReventlessLocal.handlePublish(authorization, body);
|
|
221
|
+
_writeJson(res, match[0], match[1]);
|
|
222
|
+
});
|
|
202
223
|
}
|
|
203
224
|
|
|
204
225
|
function encodeGlobalId(typeName, localId) {
|
|
@@ -572,6 +593,7 @@ function start(portOpt, param, param$1) {
|
|
|
572
593
|
Stdlib_JsError.throwWithMessage(`DomainGraphQL_Server could not bind port ` + port.toString() + `: ` + detail);
|
|
573
594
|
});
|
|
574
595
|
server.listen(port, () => log.info("GraphQL:Domain", undefined, `listening on http://localhost:` + port.toString() + `/graphql (SDL: /sdl)`));
|
|
596
|
+
LocalEvents_Server$ReventlessLocal.attach(server, port);
|
|
575
597
|
activeServer.contents = Primitive_option.some(server);
|
|
576
598
|
}
|
|
577
599
|
|