@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 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.167",
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-ssh2": "1.1.0-alpha.12",
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": {
@@ -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="Projection",
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.info(~comp="Projection", `handleActions: id=${id} actions=${optimizedActionCount->Int.toString}`)
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="Projection",
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
 
@@ -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
- log.debugLazy("Projection", () => "Ignore");
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
- log.debugLazy("Projection", () => `Create(` + id + `, ` + stateToString(state) + `)`);
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
- log.debugLazy("Projection", () => `CreateMany(` + batch.map(param => `(` + param[0] + `,` + stateToString(param[1]) + `)`).join(", ") + `)`);
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
- log.debugLazy("Projection", () => `Update Error: Multiple oldStates for ` + id$1 + `)`);
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
- log.debugLazy("Projection", () => `Update Error: No oldState for ` + id$1 + `)`);
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
- log.debugLazy("Projection", () => `Update(` + id$1 + `, ` + stateToString(oldState) + ` => ` + stateToString(newState) + `)`);
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
- log.debugLazy("Projection", () => `UpdateWithDefault Error: Multiple oldStates for ` + id$2 + `)`);
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
- log.debugLazy("Projection", () => `UpdateWithDefault(` + id$2 + `, default: ` + stateToString($$default) + `)`);
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
- log.debugLazy("Projection", () => `UpdateWithDefault(` + id$2 + `, ` + stateToString(oldState$1) + ` => ` + stateToString(newState$1) + `)`);
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
- log.debugLazy("Projection", () => `UpdateWithDefault Error: Couldn't load oldState(s) for ` + id$2 + `: ` + QueryDb$ReventlessCore.storageErrorToString(err) + `)`);
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
- log.debugLazy("Projection", () => `Set(` + id$3 + `, ` + stateToString(state$1) + `)`);
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
- log.debugLazy("Projection", () => `SetMany(` + batch$1.map(param => `(` + param[0] + `,` + stateToString(param[1]) + `)`).join(", ") + `)`);
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
- log.debugLazy("Projection", () => `Delete(` + id$4 + `)`);
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
- log.debugLazy("Projection", () => `DeleteMany(` + ids.join(", ") + `)`);
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
- log.debugLazy("Projection", () => `CreateMultiState(` + id$5 + `, ` + states$4.map(stateToString).join(", ") + `)`);
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
- log.debugLazy("Projection", () => `UpdateMultiState Error: Couldn't load states for ` + id$6 + `: ` + QueryDb$ReventlessCore.storageErrorToString(err$1) + `)`);
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
- log.debugLazy("Projection", () => "UpdateMultiState Error: Missing SubIdConfig !");
369
+ logAction(() => "UpdateMultiState Error: Missing SubIdConfig !");
369
370
  return {
370
371
  TAG: "Error",
371
372
  _0: "MissingSubIdConfig"
372
373
  };
373
374
  default:
374
- log.debugLazy("Projection", () => "Error: projection action not supported");
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("Projection", undefined, `handleActions: optimizing ` + actionCount.toString() + ` actions for id=` + id);
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.info("Projection", undefined, `handleActions: id=` + id + ` actions=` + optimizedActionCount.toString());
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("Projection", undefined, `storage error: ` + JSON.stringify(Message$ReventlessCore.encode(err._0, QueryDb$ReventlessInfra.storageErrorSchema)));
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 queue.
39
- dcbCommandTopicQueueUrl: option<Pulumi.Output.t<string>>,
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: None,
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
- // NB: don't use `Option.map(r => r.id)` here — Stdlib_Option.map has a
960
- // generic `'a => 'b` signature, so it wraps the lambda's return value
961
- // via Caml_option.some(...). The static type of r.id is
962
- // Pulumi.Output.t<string>, but a Pulumi Output is a JS object whose
963
- // .id getter can resolve to undefined during early CustomResource
964
- // construction; Caml_option.some(undefined) returns the nested-
965
- // option sentinel `{BS_PRIVATE_NESTED_SOME_NONE: 0}`, which then
966
- // flows through publishToAggregates into Lambda env vars and
967
- // Pulumi's type checker rejects it (expected string, got object).
968
- // The explicit switch keeps r.id's static type (non-option), so the
969
- // `Some(r.id)` construction is identity-compiled — no wrapping.
970
- let dcbCommandTopicQueueUrl = {
971
- let outputs: CommandTopic.outputs = dcbCommandTopic->Component.outputs->Obj.magic
972
- switch outputs.resources->Array.get(0) {
973
- | Some(r) => Some(r.id)
974
- | None => None
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: undefined,
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 outputs = Component$ReventlessCore.outputs(dcbCommandTopic);
548
- let r = outputs.resources[0];
549
- let dcbCommandTopicQueueUrl = r !== undefined ? r.id : undefined;
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
- switch dcbResult.dcbCommandTopicQueueUrl {
780
- | Some(url) =>
781
- stateChangeSlices->Array.forEach((module(Sc: StateChangeSlice.T)) =>
782
- aggregateQueueUrls->Dict.set(Sc.Spec.name, url)
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
- let url = dcbResult.dcbCommandTopicQueueUrl;
514
- if (url !== undefined) {
515
- stateChangeSlices.forEach(Sc => {
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
- let _defaultMin = ref(Logger.Info)
72
- let setDefaultMinLevel = (l: Logger.level) => _defaultMin := l
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 _defaultMin.contents {
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 = _defaultMin.contents;
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,
@@ -165,8 +165,18 @@ let emit = (
165
165
 
166
166
  // ─── Logger.t (record-based API) ─────────────────────────────────────────────
167
167
 
168
- let makeLogger = (~minLevel=Info): t => {
169
- let shouldLog = l => levelToInt(l) >= levelToInt(minLevel)
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
- // Reads LOG_LEVEL from process.env and returns a configured logger.
196
- // Call once at module top-level or startup — not inside hot loops.
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
- switch _logLevel {
201
- | Some("silent") => silent
202
- | Some("debug") => makeLogger(~minLevel=Debug)
203
- | Some("warn") => makeLogger(~minLevel=Warn)
204
- | Some("error") => makeLogger(~minLevel=Error)
205
- | _ => makeLogger(~minLevel=Info)
206
- }
231
+ makeLoggerWith(l =>
232
+ switch effectiveMin() {
233
+ | None => false
234
+ | Some(min) => levelToInt(l) >= levelToInt(min)
235
+ }
236
+ )
@@ -142,9 +142,15 @@ function emit(level, comp, plugin, detail, annotations, msg) {
142
142
  }
143
143
  }
144
144
 
145
- function makeLogger(minLevelOpt) {
146
- let minLevel = minLevelOpt !== undefined ? minLevelOpt : "Info";
147
- let shouldLog = l => levelToInt(l) >= levelToInt(minLevel);
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 fromEnv() {
207
+ function effectiveMin() {
197
208
  let match = process.env.LOG_LEVEL;
198
209
  if (match === undefined) {
199
- return makeLogger("Info");
210
+ return defaultMinLevel.contents;
200
211
  }
201
212
  switch (match) {
202
213
  case "debug" :
203
- return makeLogger("Debug");
214
+ return "Debug";
204
215
  case "error" :
205
- return makeLogger("Error");
216
+ return "Error";
206
217
  case "silent" :
207
- return silent;
218
+ return;
208
219
  case "warn" :
209
- return makeLogger("Warn");
220
+ return "Warn";
210
221
  default:
211
- return makeLogger("Info");
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 */