@reventlessdev/reventless-core 3.0.0-alpha.167 → 3.0.0-alpha.168
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 +12 -0
- package/package.json +5 -5
- package/src/Projection.res +11 -6
- package/src/Projection.res.mjs +27 -25
- package/src/components/Dcb/Dcb_Builder.res +38 -21
- package/src/components/Dcb/Dcb_Builder.res.mjs +13 -4
- package/src/components/ReadModel/ReadModel_Callback.res +1 -1
- package/src/components/ReadModel/ReadModel_Callback.res.mjs +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Builder.res +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Builder.res.mjs +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res +1 -1
- package/src/components/StateViewSlice/StateViewSlice_Callback.res.mjs +1 -1
- package/src/plugin/component/Plugin_Builder.res +5 -7
- package/src/plugin/component/Plugin_Builder.res.mjs +3 -6
- package/src/util/EffectLogger.res +5 -3
- package/src/util/EffectLogger.res.mjs +3 -10
- package/src/util/Logger.res +41 -11
- package/src/util/Logger.res.mjs +36 -10
- package/tests/api/GraphQL_StitcherTest.res.mjs +0 -168
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
# 3.0.0-alpha.168 (2026-07-14)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
* **aws:** create EC publishToAggregates SendMessage grants — cross-plugin extension→DCB publishes no longer AccessDenied ([c2126df](https://github.com/ReventlessDev/reventless-core/commit/c2126df0b9af4a79a980f2668ffc18a7760ef4c3))
|
|
11
|
+
* **aws:** create heartbeat SendMessage grant at top level; document the pulumi.all side-channel gotcha ([6ffa06a](https://github.com/ReventlessDev/reventless-core/commit/6ffa06ac87070af4027ba5753ac7c2cb216a76d0))
|
|
12
|
+
* show debug logs on the local platform (both launch paths) ([81837d7](https://github.com/ReventlessDev/reventless-core/commit/81837d7e02abe1065a722b43b195adef2c836794))
|
|
13
|
+
### Features
|
|
14
|
+
|
|
15
|
+
* **core:** thread ~comp through Projection.handleAction for attributed action logging ([fa32d93](https://github.com/ReventlessDev/reventless-core/commit/fa32d93dedabea1b1f655f01a4ea769a5908b143))
|
|
16
|
+
|
|
17
|
+
|
|
6
18
|
# 3.0.0-alpha.167 (2026-07-14)
|
|
7
19
|
|
|
8
20
|
**Note:** Version bump only for package @reventlessdev/reventless-core
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-core",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.168",
|
|
4
4
|
"description": "Core package for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -28,16 +28,16 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"sury": "11.0.0-alpha.4",
|
|
30
30
|
"uuid": "^13.0.0",
|
|
31
|
-
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.12",
|
|
32
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.27",
|
|
32
|
+
"@reventlessdev/rescript-fast-csv": "1.2.0-alpha.12",
|
|
33
33
|
"@reventlessdev/rescript-hash-object": "1.2.0-alpha.11",
|
|
34
|
-
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.11",
|
|
35
34
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.7",
|
|
36
|
-
"@reventlessdev/rescript-
|
|
35
|
+
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.11",
|
|
37
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.15",
|
|
37
|
+
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.12",
|
|
38
38
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.15",
|
|
39
|
-
"@reventlessdev/reventless-interop": "3.0.0-alpha.26",
|
|
40
39
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.99",
|
|
40
|
+
"@reventlessdev/reventless-interop": "3.0.0-alpha.26",
|
|
41
41
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.76"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
package/src/Projection.res
CHANGED
|
@@ -133,10 +133,16 @@ let statesToString: array<'a> => string = states =>
|
|
|
133
133
|
states->Array.map(stateToString)->Array.joinUnsafe(", ")
|
|
134
134
|
|
|
135
135
|
let handleAction = async (
|
|
136
|
+
~comp="Projection",
|
|
136
137
|
action,
|
|
137
138
|
{QueryDb.loadStream: loadStream, save, saveBatch, delete, deleteBatch} as operations,
|
|
138
139
|
subIdConfig,
|
|
139
140
|
) => {
|
|
141
|
+
// Log each applied action at Debug with the full state it creates/changes,
|
|
142
|
+
// attributed to the owning component. Debug-lazy so the state is only
|
|
143
|
+
// serialized when debug logging is enabled; shadows the module-level
|
|
144
|
+
// `logAction` to carry the threaded `~comp` and the `handling action:` prefix.
|
|
145
|
+
let logAction = makeStr => log.debugLazy(~comp, () => `handling action: ${makeStr()}`)
|
|
140
146
|
let loadAtMost = (n, id) =>
|
|
141
147
|
loadStream(id)
|
|
142
148
|
->Stream.take(n)
|
|
@@ -210,7 +216,6 @@ let handleAction = async (
|
|
|
210
216
|
| Error(err) => Error(err)
|
|
211
217
|
}
|
|
212
218
|
| UpdateWithDefault(id, default, update) =>
|
|
213
|
-
logAction(() => `UpdateWithDefault(${id}, loading ...`)
|
|
214
219
|
switch await loadAtMost(2, id) {
|
|
215
220
|
| Ok(states) =>
|
|
216
221
|
switch states {
|
|
@@ -424,19 +429,19 @@ let optimizeActions = actions => {
|
|
|
424
429
|
})
|
|
425
430
|
}
|
|
426
431
|
|
|
427
|
-
let handleActions = async (actions, operations, subIdConfig) => {
|
|
432
|
+
let handleActions = async (~comp="Projection", actions, operations, subIdConfig) => {
|
|
428
433
|
let handleActionsForId = async (actions, id) => {
|
|
429
434
|
let actionCount = actions->Array.length
|
|
430
435
|
if actionCount > 1 {
|
|
431
436
|
log.debug(
|
|
432
|
-
~comp
|
|
437
|
+
~comp,
|
|
433
438
|
`handleActions: optimizing ${actionCount->Int.toString} actions for id=${id}`,
|
|
434
439
|
)
|
|
435
440
|
}
|
|
436
441
|
|
|
437
442
|
let optimizedActions = optimizeActions(actions)
|
|
438
443
|
let optimizedActionCount = optimizedActions->Array.length
|
|
439
|
-
log.
|
|
444
|
+
log.debug(~comp, `handleActions: id=${id} actions=${optimizedActionCount->Int.toString}`)
|
|
440
445
|
|
|
441
446
|
// FIXME: handle errors!
|
|
442
447
|
await optimizedActions->Array.reduce(Ok()->Promise.resolve, async (p, action) => {
|
|
@@ -444,11 +449,11 @@ let handleActions = async (actions, operations, subIdConfig) => {
|
|
|
444
449
|
| Ok() => ()
|
|
445
450
|
| Error(err) =>
|
|
446
451
|
log.error(
|
|
447
|
-
~comp
|
|
452
|
+
~comp,
|
|
448
453
|
`storage error: ${err->Message.encode(ReventlessInfra.QueryDb.storageErrorSchema)->JSON.stringify}`,
|
|
449
454
|
)
|
|
450
455
|
}
|
|
451
|
-
await action->handleAction(operations, subIdConfig)
|
|
456
|
+
await action->handleAction(~comp, operations, subIdConfig)
|
|
452
457
|
})
|
|
453
458
|
}
|
|
454
459
|
|
package/src/Projection.res.mjs
CHANGED
|
@@ -202,10 +202,12 @@ function statesToString(states) {
|
|
|
202
202
|
return states.map(stateToString).join(", ");
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
async function handleAction(action, operations, subIdConfig) {
|
|
205
|
+
async function handleAction($staropt$star, action, operations, subIdConfig) {
|
|
206
206
|
let saveBatch = operations.saveBatch;
|
|
207
207
|
let save = operations.save;
|
|
208
208
|
let loadStream = operations.loadStream;
|
|
209
|
+
let comp = $staropt$star !== undefined ? $staropt$star : "Projection";
|
|
210
|
+
let logAction = makeStr => log.debugLazy(comp, () => `handling action: ` + makeStr());
|
|
209
211
|
let loadAtMost = (n, id) => Effect.runPromise(Effect.catchAll(Effect.map(Stream.runCollect(Stream$1.take(loadStream(id), n)), states => ({
|
|
210
212
|
TAG: "Ok",
|
|
211
213
|
_0: states
|
|
@@ -221,7 +223,7 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
221
223
|
_0: e
|
|
222
224
|
})));
|
|
223
225
|
if (typeof action !== "object") {
|
|
224
|
-
|
|
226
|
+
logAction(() => "Ignore");
|
|
225
227
|
return {
|
|
226
228
|
TAG: "Ok",
|
|
227
229
|
_0: undefined
|
|
@@ -231,7 +233,7 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
231
233
|
case "Create" :
|
|
232
234
|
let state = action._1;
|
|
233
235
|
let id = action._0;
|
|
234
|
-
|
|
236
|
+
logAction(() => `Create(` + id + `, ` + stateToString(state) + `)`);
|
|
235
237
|
return await save(id, state, "Init", undefined);
|
|
236
238
|
case "CreateMany" :
|
|
237
239
|
let batch = action._0.map(param => [
|
|
@@ -239,7 +241,7 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
239
241
|
param[1],
|
|
240
242
|
undefined
|
|
241
243
|
]);
|
|
242
|
-
|
|
244
|
+
logAction(() => `CreateMany(` + batch.map(param => `(` + param[0] + `,` + stateToString(param[1]) + `)`).join(", ") + `)`);
|
|
243
245
|
return await saveBatch(batch);
|
|
244
246
|
case "Update" :
|
|
245
247
|
let id$1 = action._0;
|
|
@@ -254,13 +256,13 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
254
256
|
let len = states$1.length;
|
|
255
257
|
if (len !== 1) {
|
|
256
258
|
if (len !== 0) {
|
|
257
|
-
|
|
259
|
+
logAction(() => `Update Error: Multiple oldStates for ` + id$1 + `)`);
|
|
258
260
|
return {
|
|
259
261
|
TAG: "Error",
|
|
260
262
|
_0: "StaleState"
|
|
261
263
|
};
|
|
262
264
|
} else {
|
|
263
|
-
|
|
265
|
+
logAction(() => `Update Error: No oldState for ` + id$1 + `)`);
|
|
264
266
|
return {
|
|
265
267
|
TAG: "Error",
|
|
266
268
|
_0: "StaleState"
|
|
@@ -269,35 +271,34 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
269
271
|
}
|
|
270
272
|
let oldState = states$1[0];
|
|
271
273
|
let newState = action._1(oldState);
|
|
272
|
-
|
|
274
|
+
logAction(() => `Update(` + id$1 + `, ` + stateToString(oldState) + ` => ` + stateToString(newState) + `)`);
|
|
273
275
|
return await save(id$1, newState, "Overwrite", undefined);
|
|
274
276
|
case "UpdateWithDefault" :
|
|
275
277
|
let $$default = action._1;
|
|
276
278
|
let id$2 = action._0;
|
|
277
|
-
log.debugLazy("Projection", () => `UpdateWithDefault(` + id$2 + `, loading ...`);
|
|
278
279
|
let states$2 = await loadAtMost(2, id$2);
|
|
279
280
|
if (states$2.TAG === "Ok") {
|
|
280
281
|
let states$3 = states$2._0;
|
|
281
282
|
let len$1 = states$3.length;
|
|
282
283
|
if (len$1 !== 1) {
|
|
283
284
|
if (len$1 !== 0) {
|
|
284
|
-
|
|
285
|
+
logAction(() => `UpdateWithDefault Error: Multiple oldStates for ` + id$2 + `)`);
|
|
285
286
|
return {
|
|
286
287
|
TAG: "Error",
|
|
287
288
|
_0: "StaleState"
|
|
288
289
|
};
|
|
289
290
|
} else {
|
|
290
|
-
|
|
291
|
+
logAction(() => `UpdateWithDefault(` + id$2 + `, default: ` + stateToString($$default) + `)`);
|
|
291
292
|
return await save(id$2, $$default, "Init", undefined);
|
|
292
293
|
}
|
|
293
294
|
}
|
|
294
295
|
let oldState$1 = states$3[0];
|
|
295
296
|
let newState$1 = action._2(oldState$1);
|
|
296
|
-
|
|
297
|
+
logAction(() => `UpdateWithDefault(` + id$2 + `, ` + stateToString(oldState$1) + ` => ` + stateToString(newState$1) + `)`);
|
|
297
298
|
return await save(id$2, newState$1, "Overwrite", undefined);
|
|
298
299
|
}
|
|
299
300
|
let err = states$2._0;
|
|
300
|
-
|
|
301
|
+
logAction(() => `UpdateWithDefault Error: Couldn't load oldState(s) for ` + id$2 + `: ` + QueryDb$ReventlessCore.storageErrorToString(err) + `)`);
|
|
301
302
|
return {
|
|
302
303
|
TAG: "Error",
|
|
303
304
|
_0: err
|
|
@@ -305,7 +306,7 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
305
306
|
case "Set" :
|
|
306
307
|
let state$1 = action._1;
|
|
307
308
|
let id$3 = action._0;
|
|
308
|
-
|
|
309
|
+
logAction(() => `Set(` + id$3 + `, ` + stateToString(state$1) + `)`);
|
|
309
310
|
return await save(id$3, state$1, "Any", undefined);
|
|
310
311
|
case "SetMany" :
|
|
311
312
|
let set = action._1;
|
|
@@ -314,15 +315,15 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
314
315
|
set(id),
|
|
315
316
|
undefined
|
|
316
317
|
]);
|
|
317
|
-
|
|
318
|
+
logAction(() => `SetMany(` + batch$1.map(param => `(` + param[0] + `,` + stateToString(param[1]) + `)`).join(", ") + `)`);
|
|
318
319
|
return await saveBatch(batch$1);
|
|
319
320
|
case "Delete" :
|
|
320
321
|
let id$4 = action._0;
|
|
321
|
-
|
|
322
|
+
logAction(() => `Delete(` + id$4 + `)`);
|
|
322
323
|
return await operations.delete(id$4, undefined);
|
|
323
324
|
case "DeleteMany" :
|
|
324
325
|
let ids = action._0;
|
|
325
|
-
|
|
326
|
+
logAction(() => `DeleteMany(` + ids.join(", ") + `)`);
|
|
326
327
|
return await operations.deleteBatch(ids.map(id => [
|
|
327
328
|
id,
|
|
328
329
|
undefined
|
|
@@ -330,7 +331,7 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
330
331
|
case "CreateMultiState" :
|
|
331
332
|
let states$4 = action._1;
|
|
332
333
|
let id$5 = action._0;
|
|
333
|
-
|
|
334
|
+
logAction(() => `CreateMultiState(` + id$5 + `, ` + states$4.map(stateToString).join(", ") + `)`);
|
|
334
335
|
let len$2 = states$4.length;
|
|
335
336
|
if (len$2 !== 1) {
|
|
336
337
|
if (len$2 === 0) {
|
|
@@ -359,19 +360,19 @@ async function handleAction(action, operations, subIdConfig) {
|
|
|
359
360
|
}
|
|
360
361
|
} else if (subIdConfig !== undefined) {
|
|
361
362
|
let err$1 = match._0;
|
|
362
|
-
|
|
363
|
+
logAction(() => `UpdateMultiState Error: Couldn't load states for ` + id$6 + `: ` + QueryDb$ReventlessCore.storageErrorToString(err$1) + `)`);
|
|
363
364
|
return {
|
|
364
365
|
TAG: "Error",
|
|
365
366
|
_0: err$1
|
|
366
367
|
};
|
|
367
368
|
}
|
|
368
|
-
|
|
369
|
+
logAction(() => "UpdateMultiState Error: Missing SubIdConfig !");
|
|
369
370
|
return {
|
|
370
371
|
TAG: "Error",
|
|
371
372
|
_0: "MissingSubIdConfig"
|
|
372
373
|
};
|
|
373
374
|
default:
|
|
374
|
-
|
|
375
|
+
logAction(() => "Error: projection action not supported");
|
|
375
376
|
return {
|
|
376
377
|
TAG: "Error",
|
|
377
378
|
_0: {
|
|
@@ -668,24 +669,25 @@ function optimizeActions(actions) {
|
|
|
668
669
|
});
|
|
669
670
|
}
|
|
670
671
|
|
|
671
|
-
async function handleActions(actions, operations, subIdConfig) {
|
|
672
|
+
async function handleActions(compOpt, actions, operations, subIdConfig) {
|
|
673
|
+
let comp = compOpt !== undefined ? compOpt : "Projection";
|
|
672
674
|
let handleActionsForId = async (actions, id) => {
|
|
673
675
|
let actionCount = actions.length;
|
|
674
676
|
if (actionCount > 1) {
|
|
675
|
-
log.debug(
|
|
677
|
+
log.debug(comp, undefined, `handleActions: optimizing ` + actionCount.toString() + ` actions for id=` + id);
|
|
676
678
|
}
|
|
677
679
|
let optimizedActions = optimizeActions(actions);
|
|
678
680
|
let optimizedActionCount = optimizedActions.length;
|
|
679
|
-
log.
|
|
681
|
+
log.debug(comp, undefined, `handleActions: id=` + id + ` actions=` + optimizedActionCount.toString());
|
|
680
682
|
return await Stdlib_Array.reduce(optimizedActions, Promise.resolve({
|
|
681
683
|
TAG: "Ok",
|
|
682
684
|
_0: undefined
|
|
683
685
|
}), async (p, action) => {
|
|
684
686
|
let err = await p;
|
|
685
687
|
if (err.TAG !== "Ok") {
|
|
686
|
-
log.error(
|
|
688
|
+
log.error(comp, undefined, `storage error: ` + JSON.stringify(Message$ReventlessCore.encode(err._0, QueryDb$ReventlessInfra.storageErrorSchema)));
|
|
687
689
|
}
|
|
688
|
-
return await handleAction(action, operations, subIdConfig);
|
|
690
|
+
return await handleAction(comp, action, operations, subIdConfig);
|
|
689
691
|
});
|
|
690
692
|
};
|
|
691
693
|
let results = await Promise.all(groupActionsById(actions).map(param => handleActionsForId(param[1], param[0])));
|
|
@@ -35,8 +35,15 @@ type dcbResult = {
|
|
|
35
35
|
dcbPublishJsons: option<Pulumi.Output.t<CommandTopic.publishJsons>>,
|
|
36
36
|
// SQS URL of the DCB command topic — shared by all StateChangeSlices in this
|
|
37
37
|
// plugin. Surfaced so the bundled Plugin EventCollector Lambda can dispatch
|
|
38
|
-
// PublishStateChangeSliceCommand actions from user extensions to the right
|
|
39
|
-
|
|
38
|
+
// PublishStateChangeSliceCommand actions from user extensions to the right
|
|
39
|
+
// queue. Resolves to "" when the plugin has no DCB slices — deliberately NOT
|
|
40
|
+
// option<Pulumi.Output.t<string>> (that combination is a repo-wide
|
|
41
|
+
// anti-pattern; it minted the nested-option sentinel that broke the
|
|
42
|
+
// collector's SendMessage grant). The Output is gated on the topic's
|
|
43
|
+
// `Component.operations`, so it resolves only after the topic finished
|
|
44
|
+
// constructing — see the construction site and
|
|
45
|
+
// docs/analysis/ec-publish-to-aggregates-grant-broken.md.
|
|
46
|
+
dcbCommandTopicQueueUrl: Pulumi.Output.t<string>,
|
|
40
47
|
mutationEntries: array<ReventlessInfra.Api.mutationSchemaEntry>,
|
|
41
48
|
queryEntries: array<ReventlessInfra.Api.querySchemaEntry>,
|
|
42
49
|
eventLogEntries: array<ReventlessInfra.Api.eventLogSchemaEntry>,
|
|
@@ -51,7 +58,7 @@ let emptyResult: dcbResult = {
|
|
|
51
58
|
inboundTranslationSlicesOutputs: Dict.make(),
|
|
52
59
|
dcbRuntimeSetup: None,
|
|
53
60
|
dcbPublishJsons: None,
|
|
54
|
-
dcbCommandTopicQueueUrl:
|
|
61
|
+
dcbCommandTopicQueueUrl: Pulumi.Output.make(""),
|
|
55
62
|
mutationEntries: [],
|
|
56
63
|
queryEntries: [],
|
|
57
64
|
eventLogEntries: [],
|
|
@@ -956,24 +963,34 @@ module Make = (
|
|
|
956
963
|
Sc.Spec.eventSchema->S.castToUnknown
|
|
957
964
|
)
|
|
958
965
|
|
|
959
|
-
//
|
|
960
|
-
//
|
|
961
|
-
//
|
|
962
|
-
//
|
|
963
|
-
// .id
|
|
964
|
-
//
|
|
965
|
-
//
|
|
966
|
-
//
|
|
967
|
-
//
|
|
968
|
-
//
|
|
969
|
-
//
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
966
|
+
// Derive the DCB command topic's queue URL *after* the component's
|
|
967
|
+
// construct has stored its real resources. This reads `Component.outputs`
|
|
968
|
+
// as a synchronous side channel inside a callback gated on a DIFFERENT
|
|
969
|
+
// output (`Component.operations`, which resolves only post-construct) —
|
|
970
|
+
// reading `resources[0].id` at build time instead captured the SQS
|
|
971
|
+
// Queue's not-yet-resolved `.id`, which degenerated to the
|
|
972
|
+
// `{BS_PRIVATE_NESTED_SOME_NONE: 0}` nested-option sentinel and silently
|
|
973
|
+
// broke the Plugin EventCollector's sqs:SendMessage grant (cross-plugin
|
|
974
|
+
// extension → DCB-slice publishes → IAM AccessDenied).
|
|
975
|
+
//
|
|
976
|
+
// CONSUMER CONSTRAINT: because the value comes from that gated
|
|
977
|
+
// side-channel read (not from the `operations` payload itself), it must
|
|
978
|
+
// be resolved with a direct `.apply`, NOT batched through
|
|
979
|
+
// `Pulumi.Output.all`/`all2` — `all` invokes the callback at a different
|
|
980
|
+
// point in the graph where the side-channel read still yields the
|
|
981
|
+
// sentinel (verified on both preview and up). The EC grant builder
|
|
982
|
+
// therefore resolves it per-target. See
|
|
983
|
+
// docs/analysis/ec-publish-to-aggregates-grant-broken.md.
|
|
984
|
+
let dcbCommandTopicQueueUrl =
|
|
985
|
+
dcbCommandTopic
|
|
986
|
+
->Component.operations
|
|
987
|
+
->Pulumi.Output.flatMap(_ops => {
|
|
988
|
+
let outputs: CommandTopic.outputs = dcbCommandTopic->Component.outputs->Obj.magic
|
|
989
|
+
switch outputs.resources->Array.get(0) {
|
|
990
|
+
| Some(r) => r.id
|
|
991
|
+
| None => Pulumi.Output.make("")
|
|
992
|
+
}
|
|
993
|
+
})
|
|
977
994
|
{
|
|
978
995
|
dcbEventLogOutputs: Some(dcbEventLog->Component.outputs),
|
|
979
996
|
stateChangeSlicesOutputs: Dict.fromArray(
|
|
@@ -4,6 +4,7 @@ import * as S from "sury/src/S.res.mjs";
|
|
|
4
4
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
5
5
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
6
6
|
import * as Id$Reventless from "@reventlessdev/reventless-spec/src/types/Id.res.mjs";
|
|
7
|
+
import * as Output$Pulumi from "@reventlessdev/rescript-pulumi-pulumi/src/Output.res.mjs";
|
|
7
8
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
8
9
|
import * as Effect from "effect/Effect";
|
|
9
10
|
import * as Pulumi from "@pulumi/pulumi";
|
|
@@ -55,6 +56,8 @@ let emptyResult_outboundTranslationSlicesOutputs = {};
|
|
|
55
56
|
|
|
56
57
|
let emptyResult_inboundTranslationSlicesOutputs = {};
|
|
57
58
|
|
|
59
|
+
let emptyResult_dcbCommandTopicQueueUrl = Pulumi.output("");
|
|
60
|
+
|
|
58
61
|
let emptyResult_mutationEntries = [];
|
|
59
62
|
|
|
60
63
|
let emptyResult_queryEntries = [];
|
|
@@ -70,7 +73,7 @@ let emptyResult = {
|
|
|
70
73
|
inboundTranslationSlicesOutputs: emptyResult_inboundTranslationSlicesOutputs,
|
|
71
74
|
dcbRuntimeSetup: undefined,
|
|
72
75
|
dcbPublishJsons: undefined,
|
|
73
|
-
dcbCommandTopicQueueUrl:
|
|
76
|
+
dcbCommandTopicQueueUrl: emptyResult_dcbCommandTopicQueueUrl,
|
|
74
77
|
mutationEntries: emptyResult_mutationEntries,
|
|
75
78
|
queryEntries: emptyResult_queryEntries,
|
|
76
79
|
eventLogEntries: emptyResult_eventLogEntries
|
|
@@ -544,9 +547,15 @@ function Make(DcbEventLogStorage) {
|
|
|
544
547
|
};
|
|
545
548
|
});
|
|
546
549
|
let allProducedSchemas = stateChangeSlices.map(Sc => Sc.Spec.eventSchema);
|
|
547
|
-
let
|
|
548
|
-
|
|
549
|
-
|
|
550
|
+
let dcbCommandTopicQueueUrl = Output$Pulumi.flatMap(Component$ReventlessCore.operations(dcbCommandTopic), _ops => {
|
|
551
|
+
let outputs = Component$ReventlessCore.outputs(dcbCommandTopic);
|
|
552
|
+
let r = outputs.resources[0];
|
|
553
|
+
if (r !== undefined) {
|
|
554
|
+
return r.id;
|
|
555
|
+
} else {
|
|
556
|
+
return Pulumi.output("");
|
|
557
|
+
}
|
|
558
|
+
});
|
|
550
559
|
return {
|
|
551
560
|
dcbEventLogOutputs: Component$ReventlessCore.outputs(dcbEventLog),
|
|
552
561
|
stateChangeSlicesOutputs: Object.fromEntries(Object.entries(stateChangeSlicesOutputs).concat(Object.entries(asyncStateChangeSlicesOutputs))),
|
|
@@ -39,7 +39,7 @@ module Make = (
|
|
|
39
39
|
_ => {
|
|
40
40
|
let action = Projection.rewriteAction(action, ReadModelSpec.stateSchema)
|
|
41
41
|
Effect.promise(
|
|
42
|
-
() => Projection.handleAction(action, Spec.operations, ReadModelSpec.subIdConfig),
|
|
42
|
+
() => Projection.handleAction(~comp, action, Spec.operations, ReadModelSpec.subIdConfig),
|
|
43
43
|
)->Effect.map(_ => ())
|
|
44
44
|
},
|
|
45
45
|
)
|
|
@@ -31,7 +31,7 @@ function Make(ReadModelSpec) {
|
|
|
31
31
|
return actions;
|
|
32
32
|
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => {
|
|
33
33
|
let action$1 = Projection$ReventlessCore.rewriteAction(action, ReadModelSpec.stateSchema);
|
|
34
|
-
return Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(action$1, Spec.operations, ReadModelSpec.subIdConfig)), param => {});
|
|
34
|
+
return Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(comp, action$1, Spec.operations, ReadModelSpec.subIdConfig)), param => {});
|
|
35
35
|
}));
|
|
36
36
|
});
|
|
37
37
|
return {
|
|
@@ -130,7 +130,7 @@ module Make = (
|
|
|
130
130
|
->Array.reduce(Effect.succeed(), (acc, action) =>
|
|
131
131
|
acc->Effect.flatMap(_ =>
|
|
132
132
|
Effect.promise(() =>
|
|
133
|
-
FrameworkProjection.handleAction(action, projectionOps, Spec.subIdConfig)
|
|
133
|
+
FrameworkProjection.handleAction(~comp, action, projectionOps, Spec.subIdConfig)
|
|
134
134
|
)->Effect.map(_ => ())
|
|
135
135
|
)
|
|
136
136
|
)
|
|
@@ -94,7 +94,7 @@ function Make(RuntimeEnvironment) {
|
|
|
94
94
|
let fieldsStr = f === "" ? "" : `({` + f + `})`;
|
|
95
95
|
Effect.runSync(EffectLogger$ReventlessCore.logInfo(comp, dataDict, `handling event ` + idxStr + `/` + total + `: ` + LogFormat$ReventlessCore.bold(eventType) + fieldsStr + ` ` + actionsStr));
|
|
96
96
|
return actions;
|
|
97
|
-
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(action, projectionOps, Spec.subIdConfig)), param => {})));
|
|
97
|
+
}).flat(), Effect.succeed(), (acc, action) => Effect.flatMap(acc, () => Effect.map(Effect.promise(() => Projection$ReventlessCore.handleAction(comp, action, projectionOps, Spec.subIdConfig)), param => {})));
|
|
98
98
|
});
|
|
99
99
|
let handler = SpecificEventCollector.makeHandler(ec, jsonEventsHandler);
|
|
100
100
|
let resources = Component$ReventlessCore.outputs(queryDb).resources;
|
|
@@ -59,6 +59,6 @@ module Make = (
|
|
|
59
59
|
`skipped=${skipped->Int.toString} events (decode mismatch)`,
|
|
60
60
|
)->Effect.runSync
|
|
61
61
|
}
|
|
62
|
-
await FrameworkProjection.handleActions(allActions, queryDbOps, Spec.subIdConfig)
|
|
62
|
+
await FrameworkProjection.handleActions(~comp, allActions, queryDbOps, Spec.subIdConfig)
|
|
63
63
|
}
|
|
64
64
|
}
|
|
@@ -38,7 +38,7 @@ function Make(Spec) {
|
|
|
38
38
|
if (skipped > 0) {
|
|
39
39
|
Effect.runSync(EffectLogger$ReventlessCore.logWarn(comp, undefined, `skipped=` + skipped.toString() + ` events (decode mismatch)`));
|
|
40
40
|
}
|
|
41
|
-
return await Projection$ReventlessCore.handleActions(allActions, queryDbOps, Spec.subIdConfig);
|
|
41
|
+
return await Projection$ReventlessCore.handleActions(comp, allActions, queryDbOps, Spec.subIdConfig);
|
|
42
42
|
};
|
|
43
43
|
return {
|
|
44
44
|
Spec: Spec,
|
|
@@ -776,13 +776,11 @@ module Make = (
|
|
|
776
776
|
// All DCB StateChangeSlices share the same dcb command topic — map
|
|
777
777
|
// every slice name to that single URL so PublishStateChangeSliceCommand
|
|
778
778
|
// routes correctly regardless of which slice the extension targets.
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
| None => ()
|
|
785
|
-
}
|
|
779
|
+
// (No-op when the plugin has no DCB slices: the list is empty and the
|
|
780
|
+
// URL Output resolves to "".)
|
|
781
|
+
stateChangeSlices->Array.forEach((module(Sc: StateChangeSlice.T)) =>
|
|
782
|
+
aggregateQueueUrls->Dict.set(Sc.Spec.name, dcbResult.dcbCommandTopicQueueUrl)
|
|
783
|
+
)
|
|
786
784
|
|
|
787
785
|
// Per-RM EventCollector SQS URL — populated only for plugins that
|
|
788
786
|
// expose RMs the bundled handler may enqueue events into directly.
|
|
@@ -510,12 +510,9 @@ function Make(Spec) {
|
|
|
510
510
|
return;
|
|
511
511
|
}
|
|
512
512
|
});
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
aggregateQueueUrls[Sc.Spec.name] = url;
|
|
517
|
-
});
|
|
518
|
-
}
|
|
513
|
+
stateChangeSlices.forEach(Sc => {
|
|
514
|
+
aggregateQueueUrls[Sc.Spec.name] = dcbResult.dcbCommandTopicQueueUrl;
|
|
515
|
+
});
|
|
519
516
|
let readModelQueueUrls = {};
|
|
520
517
|
Object.entries(readModelsOutputs).forEach(param => {
|
|
521
518
|
let urlOutput = Output$Pulumi.flatMap(param[1].eventCollector, ecOutputs => {
|
|
@@ -68,8 +68,10 @@ external _withMinimumLogLevel: (Effect.t<'a, 'e, 'r>, effectLogLevel) => Effect.
|
|
|
68
68
|
|
|
69
69
|
// Minimum level used when LOG_LEVEL is unset. Framework default is Info; a platform
|
|
70
70
|
// may lower it (e.g. the in-memory dev platform sets Debug) via setDefaultMinLevel.
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
// The ref lives in Logger (single source of truth) so both the record logger
|
|
72
|
+
// (Logger.fromEnv) and this Effect logger share one platform default; re-exported
|
|
73
|
+
// here so existing `EffectLogger.setDefaultMinLevel` callers keep working.
|
|
74
|
+
let setDefaultMinLevel = Logger.setDefaultMinLevel
|
|
73
75
|
|
|
74
76
|
// Resolve the active minimum: explicit LOG_LEVEL wins, else the platform default.
|
|
75
77
|
let _effectMin = (): effectLogLevel =>
|
|
@@ -80,7 +82,7 @@ let _effectMin = (): effectLogLevel =>
|
|
|
80
82
|
| Some("warn") => _llWarning
|
|
81
83
|
| Some("error") => _llError
|
|
82
84
|
| _ =>
|
|
83
|
-
switch
|
|
85
|
+
switch Logger.defaultMinLevel.contents {
|
|
84
86
|
| Logger.Debug => _llDebug
|
|
85
87
|
| Logger.Info => _llInfo
|
|
86
88
|
| Logger.Warn => _llWarning
|
|
@@ -26,14 +26,6 @@ function _messageToString(msg) {
|
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
let _defaultMin = {
|
|
30
|
-
contents: "Info"
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
function setDefaultMinLevel(l) {
|
|
34
|
-
_defaultMin.contents = l;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
29
|
function _effectMin() {
|
|
38
30
|
let match = process.env.LOG_LEVEL;
|
|
39
31
|
if (match !== undefined) {
|
|
@@ -50,7 +42,7 @@ function _effectMin() {
|
|
|
50
42
|
return LogLevel.Warning;
|
|
51
43
|
}
|
|
52
44
|
}
|
|
53
|
-
let match$1 =
|
|
45
|
+
let match$1 = Logger$ReventlessCore.defaultMinLevel.contents;
|
|
54
46
|
switch (match$1) {
|
|
55
47
|
case "Debug" :
|
|
56
48
|
return LogLevel.Debug;
|
|
@@ -155,9 +147,10 @@ function logDebug(comp, detail, msg) {
|
|
|
155
147
|
return Logger.withMinimumLogLevel(_annotate(Effect.logDebug(msg), comp, detail), _effectMin());
|
|
156
148
|
}
|
|
157
149
|
|
|
150
|
+
let setDefaultMinLevel = Logger$ReventlessCore.setDefaultMinLevel;
|
|
151
|
+
|
|
158
152
|
export {
|
|
159
153
|
_messageToString,
|
|
160
|
-
_defaultMin,
|
|
161
154
|
setDefaultMinLevel,
|
|
162
155
|
_effectMin,
|
|
163
156
|
ordinalToLevel,
|
package/src/util/Logger.res
CHANGED
|
@@ -165,8 +165,18 @@ let emit = (
|
|
|
165
165
|
|
|
166
166
|
// ─── Logger.t (record-based API) ─────────────────────────────────────────────
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
// Platform-tunable default minimum level, used when LOG_LEVEL is unset. Lives
|
|
169
|
+
// here (not in EffectLogger) so it is the single source of truth for BOTH logger
|
|
170
|
+
// paths — the record logger below and EffectLogger, which re-exports
|
|
171
|
+
// `setDefaultMinLevel`. A platform may lower it (e.g. reventless-local sets
|
|
172
|
+
// Debug); the framework default is Info.
|
|
173
|
+
let defaultMinLevel = ref(Info)
|
|
174
|
+
let setDefaultMinLevel = (l: level) => defaultMinLevel := l
|
|
175
|
+
|
|
176
|
+
// Build a logger from a per-call predicate. `shouldLog` is evaluated on every
|
|
177
|
+
// emit, so a logger created at module-init time still reflects a platform
|
|
178
|
+
// default set later (and a live LOG_LEVEL) instead of baking the level in once.
|
|
179
|
+
let makeLoggerWith = (shouldLog: level => bool): t => {
|
|
170
180
|
let log = (level, ~comp=?, ~data=?, msg) =>
|
|
171
181
|
if shouldLog(level) {
|
|
172
182
|
let dataStr = data->Option.map(d => ` ${d->JSON.stringify}`)->Option.getOr("")
|
|
@@ -184,6 +194,11 @@ let makeLogger = (~minLevel=Info): t => {
|
|
|
184
194
|
}
|
|
185
195
|
}
|
|
186
196
|
|
|
197
|
+
// Fixed-level logger — the level is baked at creation. Used by tests that assert
|
|
198
|
+
// a specific minimum regardless of env/platform default.
|
|
199
|
+
let makeLogger = (~minLevel=Info): t =>
|
|
200
|
+
makeLoggerWith(l => levelToInt(l) >= levelToInt(minLevel))
|
|
201
|
+
|
|
187
202
|
let silent: t = {
|
|
188
203
|
debug: (~comp as _=?, ~data as _=?, _) => (),
|
|
189
204
|
info: (~comp as _=?, ~data as _=?, _) => (),
|
|
@@ -192,15 +207,30 @@ let silent: t = {
|
|
|
192
207
|
debugLazy: (~comp as _=?, _) => (),
|
|
193
208
|
}
|
|
194
209
|
|
|
195
|
-
//
|
|
196
|
-
//
|
|
210
|
+
// Active minimum level: explicit LOG_LEVEL wins, else the platform default.
|
|
211
|
+
// None ⇒ silent (suppress everything). Re-read per call — LOG_LEVEL via the
|
|
212
|
+
// process.env external, the platform default via `defaultMinLevel`.
|
|
213
|
+
let effectiveMin = (): option<level> =>
|
|
214
|
+
switch _logLevel {
|
|
215
|
+
| Some("silent") => None
|
|
216
|
+
| Some("debug") => Some(Debug)
|
|
217
|
+
| Some("info") => Some(Info)
|
|
218
|
+
| Some("warn") => Some(Warn)
|
|
219
|
+
| Some("error") => Some(Error)
|
|
220
|
+
| Some(_) => Some(Info) // unrecognized value ⇒ Info (unchanged behavior)
|
|
221
|
+
| None => Some(defaultMinLevel.contents)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Reads LOG_LEVEL from process.env on every call and honors the platform default
|
|
225
|
+
// when unset. Safe to call at module top-level: the returned logger evaluates the
|
|
226
|
+
// level lazily, so a platform lowering the default afterwards (e.g.
|
|
227
|
+
// reventless-local → Debug) takes effect for loggers already constructed.
|
|
197
228
|
// LOG_LEVEL=silent disables all output (used by the reventless-gwt CLI so
|
|
198
229
|
// framework diagnostics don't interleave with NDJSON / TAP / JUnit streams).
|
|
199
230
|
let fromEnv = (): t =>
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}
|
|
231
|
+
makeLoggerWith(l =>
|
|
232
|
+
switch effectiveMin() {
|
|
233
|
+
| None => false
|
|
234
|
+
| Some(min) => levelToInt(l) >= levelToInt(min)
|
|
235
|
+
}
|
|
236
|
+
)
|
package/src/util/Logger.res.mjs
CHANGED
|
@@ -142,9 +142,15 @@ function emit(level, comp, plugin, detail, annotations, msg) {
|
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
145
|
+
let defaultMinLevel = {
|
|
146
|
+
contents: "Info"
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
function setDefaultMinLevel(l) {
|
|
150
|
+
defaultMinLevel.contents = l;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function makeLoggerWith(shouldLog) {
|
|
148
154
|
let log = (level, comp, data, msg) => {
|
|
149
155
|
if (!shouldLog(level)) {
|
|
150
156
|
return;
|
|
@@ -165,6 +171,11 @@ function makeLogger(minLevelOpt) {
|
|
|
165
171
|
};
|
|
166
172
|
}
|
|
167
173
|
|
|
174
|
+
function makeLogger(minLevelOpt) {
|
|
175
|
+
let minLevel = minLevelOpt !== undefined ? minLevelOpt : "Info";
|
|
176
|
+
return makeLoggerWith(l => levelToInt(l) >= levelToInt(minLevel));
|
|
177
|
+
}
|
|
178
|
+
|
|
168
179
|
function silent_debug(param, param$1, param$2) {
|
|
169
180
|
|
|
170
181
|
}
|
|
@@ -193,25 +204,36 @@ let silent = {
|
|
|
193
204
|
debugLazy: silent_debugLazy
|
|
194
205
|
};
|
|
195
206
|
|
|
196
|
-
function
|
|
207
|
+
function effectiveMin() {
|
|
197
208
|
let match = process.env.LOG_LEVEL;
|
|
198
209
|
if (match === undefined) {
|
|
199
|
-
return
|
|
210
|
+
return defaultMinLevel.contents;
|
|
200
211
|
}
|
|
201
212
|
switch (match) {
|
|
202
213
|
case "debug" :
|
|
203
|
-
return
|
|
214
|
+
return "Debug";
|
|
204
215
|
case "error" :
|
|
205
|
-
return
|
|
216
|
+
return "Error";
|
|
206
217
|
case "silent" :
|
|
207
|
-
return
|
|
218
|
+
return;
|
|
208
219
|
case "warn" :
|
|
209
|
-
return
|
|
220
|
+
return "Warn";
|
|
210
221
|
default:
|
|
211
|
-
return
|
|
222
|
+
return "Info";
|
|
212
223
|
}
|
|
213
224
|
}
|
|
214
225
|
|
|
226
|
+
function fromEnv() {
|
|
227
|
+
return makeLoggerWith(l => {
|
|
228
|
+
let min = effectiveMin();
|
|
229
|
+
if (min !== undefined) {
|
|
230
|
+
return levelToInt(l) >= levelToInt(min);
|
|
231
|
+
} else {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
|
|
215
237
|
let currentPluginName = LogPrefix$Reventless.currentPluginName;
|
|
216
238
|
|
|
217
239
|
let registerComponentPlugin = LogPrefix$Reventless.registerComponentPlugin;
|
|
@@ -241,8 +263,12 @@ export {
|
|
|
241
263
|
fmtPlainPrefix,
|
|
242
264
|
resolvePlugin,
|
|
243
265
|
emit,
|
|
266
|
+
defaultMinLevel,
|
|
267
|
+
setDefaultMinLevel,
|
|
268
|
+
makeLoggerWith,
|
|
244
269
|
makeLogger,
|
|
245
270
|
silent,
|
|
271
|
+
effectiveMin,
|
|
246
272
|
fromEnv,
|
|
247
273
|
}
|
|
248
274
|
/* s Not a pure module */
|
|
@@ -3,169 +3,6 @@
|
|
|
3
3
|
import * as AdminApi$ReventlessCore from "../../src/admin/AdminApi.res.mjs";
|
|
4
4
|
import * as GraphQL_Stitcher$ReventlessCore from "../../src/components/Api/GraphQL_Stitcher.res.mjs";
|
|
5
5
|
|
|
6
|
-
let fullSchemaSdl = `type Node {
|
|
7
|
-
id: ID!
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
type Query {
|
|
11
|
-
node(id: ID!): Node
|
|
12
|
-
Platform_plugins: [Plugin]
|
|
13
|
-
Catalog_products: [Product]
|
|
14
|
-
Ordering_orders: [Order]
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
type Mutation {
|
|
18
|
-
Platform_connectPlugin(input: ConnectInput): ConnectResult
|
|
19
|
-
Catalog_addProduct(input: AddInput): Product
|
|
20
|
-
Catalog_updateProduct(input: UpdateInput): Product
|
|
21
|
-
Ordering_placeOrder(input: OrderInput): Order
|
|
22
|
-
}`;
|
|
23
|
-
|
|
24
|
-
let collapsedSchemaSdl = `type Node {
|
|
25
|
-
id: ID!
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
type Query {
|
|
29
|
-
node(id: ID!): Node
|
|
30
|
-
Platform_plugins: [Plugin]
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
type Mutation {
|
|
34
|
-
Platform_connectPlugin(input: ConnectInput): ConnectResult
|
|
35
|
-
}`;
|
|
36
|
-
|
|
37
|
-
let authAnnotatedSdl = `type Mutation {
|
|
38
|
-
Platform_connectPlugin(input: ConnectInput): ConnectResult
|
|
39
|
-
@aws_auth(cognito_groups: ["Admin"])
|
|
40
|
-
Catalog_addProduct(input: AddInput): Product
|
|
41
|
-
@aws_auth(cognito_groups: ["User"])
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
type Query {
|
|
45
|
-
node(id: ID!): Node @aws_auth(cognito_groups: ["Admin"])
|
|
46
|
-
}`;
|
|
47
|
-
|
|
48
|
-
globalThis.describe("GraphQL_Stitcher.countRootTypeFields", () => {
|
|
49
|
-
globalThis.test("counts Mutation fields", () => {
|
|
50
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(fullSchemaSdl, "Mutation")).toBe(4);
|
|
51
|
-
});
|
|
52
|
-
globalThis.test("counts Query fields", () => {
|
|
53
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(fullSchemaSdl, "Query")).toBe(4);
|
|
54
|
-
});
|
|
55
|
-
globalThis.test("ignores @aws_auth directive lines, counts each Mutation field once", () => {
|
|
56
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(authAnnotatedSdl, "Mutation")).toBe(2);
|
|
57
|
-
});
|
|
58
|
-
globalThis.test("ignores @aws_auth directive lines, counts each Query field once", () => {
|
|
59
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(authAnnotatedSdl, "Query")).toBe(1);
|
|
60
|
-
});
|
|
61
|
-
globalThis.test("missing type returns 0", () => {
|
|
62
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(fullSchemaSdl, "Subscription")).toBe(0);
|
|
63
|
-
});
|
|
64
|
-
globalThis.test("empty SDL returns 0", () => {
|
|
65
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields("", "Mutation")).toBe(0);
|
|
66
|
-
});
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
globalThis.describe("GraphQL_Stitcher.isCatastrophicSchemaShrink", () => {
|
|
70
|
-
globalThis.test("rejects the all-disconnected collapse (8 → 3 root fields, < 50%)", () => {
|
|
71
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.isCatastrophicSchemaShrink(fullSchemaSdl, collapsedSchemaSdl, 0.5)).toBe(true);
|
|
72
|
-
});
|
|
73
|
-
globalThis.test("allows an intentional small removal above threshold (8 → 7)", () => {
|
|
74
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.isCatastrophicSchemaShrink(fullSchemaSdl, `type Query {
|
|
75
|
-
node(id: ID!): Node
|
|
76
|
-
Platform_plugins: [Plugin]
|
|
77
|
-
Catalog_products: [Product]
|
|
78
|
-
Ordering_orders: [Order]
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
type Mutation {
|
|
82
|
-
Platform_connectPlugin(input: ConnectInput): ConnectResult
|
|
83
|
-
Catalog_addProduct(input: AddInput): Product
|
|
84
|
-
Ordering_placeOrder(input: OrderInput): Order
|
|
85
|
-
}`, 0.5)).toBe(false);
|
|
86
|
-
});
|
|
87
|
-
globalThis.test("allows the first push when there is no live schema to protect", () => {
|
|
88
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.isCatastrophicSchemaShrink("", fullSchemaSdl, 0.5)).toBe(false);
|
|
89
|
-
});
|
|
90
|
-
globalThis.test("allows an unchanged re-push (same SDL)", () => {
|
|
91
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.isCatastrophicSchemaShrink(fullSchemaSdl, fullSchemaSdl, 0.5)).toBe(false);
|
|
92
|
-
});
|
|
93
|
-
globalThis.test("allows a purely additive push even below threshold cardinality", () => {
|
|
94
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.isCatastrophicSchemaShrink(collapsedSchemaSdl, fullSchemaSdl, 0.5)).toBe(false);
|
|
95
|
-
});
|
|
96
|
-
globalThis.test("includes Subscription fields when judging a shrink", () => {
|
|
97
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.isCatastrophicSchemaShrink(`type Query {
|
|
98
|
-
node(id: ID!): Node
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
type Mutation {
|
|
102
|
-
Ordering_placeOrder(input: OrderInput): Order
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
type Subscription {
|
|
106
|
-
onOrdering_Customer_Register: Customer
|
|
107
|
-
onOrdering_Customer_UpdateAddress: Customer
|
|
108
|
-
onCatalog_Product_Added: Product
|
|
109
|
-
}`, `type Query {
|
|
110
|
-
node(id: ID!): Node
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
type Mutation {
|
|
114
|
-
Ordering_placeOrder(input: OrderInput): Order
|
|
115
|
-
}`, 0.5)).toBe(true);
|
|
116
|
-
});
|
|
117
|
-
});
|
|
118
|
-
|
|
119
|
-
globalThis.describe("GraphQL_Stitcher.rootTypeFieldNames", () => {
|
|
120
|
-
globalThis.test("lists Mutation field names", () => {
|
|
121
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.rootTypeFieldNames(fullSchemaSdl, "Mutation")).toEqual([
|
|
122
|
-
"Platform_connectPlugin",
|
|
123
|
-
"Catalog_addProduct",
|
|
124
|
-
"Catalog_updateProduct",
|
|
125
|
-
"Ordering_placeOrder"
|
|
126
|
-
]);
|
|
127
|
-
});
|
|
128
|
-
globalThis.test("strips args and @aws_auth directive lines", () => {
|
|
129
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.rootTypeFieldNames(authAnnotatedSdl, "Mutation")).toEqual([
|
|
130
|
-
"Platform_connectPlugin",
|
|
131
|
-
"Catalog_addProduct"
|
|
132
|
-
]);
|
|
133
|
-
});
|
|
134
|
-
globalThis.test("missing type returns []", () => {
|
|
135
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.rootTypeFieldNames(fullSchemaSdl, "Subscription")).toEqual([]);
|
|
136
|
-
});
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
globalThis.describe("GraphQL_Stitcher.missingRootFields", () => {
|
|
140
|
-
globalThis.test("reports plugin fields absent from an admin-base clobber", () => {
|
|
141
|
-
let missing = GraphQL_Stitcher$ReventlessCore.missingRootFields(fullSchemaSdl, collapsedSchemaSdl);
|
|
142
|
-
globalThis.expect(missing.includes("Catalog_addProduct")).toBe(true);
|
|
143
|
-
globalThis.expect(missing.includes("Ordering_placeOrder")).toBe(true);
|
|
144
|
-
globalThis.expect(missing.includes("Ordering_orders")).toBe(true);
|
|
145
|
-
});
|
|
146
|
-
globalThis.test("empty when live schema is a superset (intact)", () => {
|
|
147
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.missingRootFields(fullSchemaSdl, fullSchemaSdl)).toEqual([]);
|
|
148
|
-
});
|
|
149
|
-
globalThis.test("detects an equal-cardinality field swap the count test misses", () => {
|
|
150
|
-
let swapped = `type Query {
|
|
151
|
-
node(id: ID!): Node
|
|
152
|
-
Platform_plugins: [Plugin]
|
|
153
|
-
Catalog_products: [Product]
|
|
154
|
-
Ordering_orders: [Order]
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
type Mutation {
|
|
158
|
-
Platform_connectPlugin(input: ConnectInput): ConnectResult
|
|
159
|
-
Catalog_addProduct(input: AddInput): Product
|
|
160
|
-
Catalog_updateProduct(input: UpdateInput): Product
|
|
161
|
-
Foreign_strayField(input: OrderInput): Order
|
|
162
|
-
}`;
|
|
163
|
-
globalThis.expect(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(swapped, "Mutation")).toBe(GraphQL_Stitcher$ReventlessCore.countRootTypeFields(fullSchemaSdl, "Mutation"));
|
|
164
|
-
let missing = GraphQL_Stitcher$ReventlessCore.missingRootFields(fullSchemaSdl, swapped);
|
|
165
|
-
globalThis.expect(missing.includes("Ordering_placeOrder")).toBe(true);
|
|
166
|
-
});
|
|
167
|
-
});
|
|
168
|
-
|
|
169
6
|
globalThis.describe("GraphQL_Stitcher.subscriptionSources", () => {
|
|
170
7
|
globalThis.test("encode/decode round-trips the subscription→mutation source mapping", () => {
|
|
171
8
|
let fragment = GraphQL_Stitcher$ReventlessCore.encode({
|
|
@@ -272,9 +109,4 @@ globalThis.describe("GraphQL_Stitcher.stitchStandalone", () => {
|
|
|
272
109
|
});
|
|
273
110
|
});
|
|
274
111
|
|
|
275
|
-
export {
|
|
276
|
-
fullSchemaSdl,
|
|
277
|
-
collapsedSchemaSdl,
|
|
278
|
-
authAnnotatedSdl,
|
|
279
|
-
}
|
|
280
112
|
/* Not a pure module */
|