@reventlessdev/reventless-core 3.0.0-alpha.166 → 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 +20 -0
- package/package.json +3 -3
- package/src/Projection.res +11 -6
- package/src/Projection.res.mjs +27 -25
- package/src/admin/AdminApi.res +9 -62
- package/src/admin/AdminApi.res.mjs +6 -28
- package/src/components/Api/GraphQL_Stitcher.res +4 -112
- package/src/components/Api/GraphQL_Stitcher.res.mjs +0 -90
- 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 +0 -198
- package/tests/api/GraphQL_StitcherTest.res.mjs +0 -168
- package/src/admin/ApiFragmentRegistry/ApiFragmentRegistryBehavior.res +0 -108
- package/src/admin/ApiFragmentRegistry/ApiFragmentRegistryBehavior.res.mjs +0 -190
- package/src/admin/ApiFragmentRegistry/ApiFragmentRegistrySpec.res +0 -71
- package/src/admin/ApiFragmentRegistry/ApiFragmentRegistrySpec.res.mjs +0 -92
- package/src/admin/ApiFragmentRegistry/ApiFragmentsProjection.res +0 -63
- package/src/admin/ApiFragmentRegistry/ApiFragmentsProjection.res.mjs +0 -108
- package/src/admin/ApiFragmentRegistry/ApiFragmentsReadModelSpec.res +0 -23
- package/src/admin/ApiFragmentRegistry/ApiFragmentsReadModelSpec.res.mjs +0 -43
- package/src/admin/Platform_ApiFragmentsApi.res +0 -32
- package/src/admin/Platform_ApiFragmentsApi.res.mjs +0 -52
- package/src/components/Api/Api_Builder.res +0 -40
- package/src/components/Api/Api_Builder.res.mjs +0 -33
- package/src/components/Api/Api_Operations.res +0 -13
- package/src/components/Api/Api_Operations.res.mjs +0 -12
- package/src/components/Api/GraphQL_PushPlanner.res +0 -65
- package/src/components/Api/GraphQL_PushPlanner.res.mjs +0 -46
- package/tests/admin/Platform_ApiFragmentsApiTest.res +0 -66
- package/tests/admin/Platform_ApiFragmentsApiTest.res.mjs +0 -68
- package/tests/api/GraphQL_PushPlannerTest.res +0 -62
- package/tests/api/GraphQL_PushPlannerTest.res.mjs +0 -80
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,26 @@
|
|
|
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
|
+
|
|
18
|
+
# 3.0.0-alpha.167 (2026-07-14)
|
|
19
|
+
|
|
20
|
+
**Note:** Version bump only for package @reventlessdev/reventless-core
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
6
26
|
# 3.0.0-alpha.166 (2026-07-14)
|
|
7
27
|
|
|
8
28
|
### Features
|
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": {
|
|
@@ -31,12 +31,12 @@
|
|
|
31
31
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.27",
|
|
32
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",
|
|
35
|
+
"@reventlessdev/rescript-node-streams": "1.1.0-alpha.11",
|
|
36
36
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.15",
|
|
37
37
|
"@reventlessdev/rescript-ssh2": "1.1.0-alpha.12",
|
|
38
38
|
"@reventlessdev/rescript-uuid": "1.1.0-alpha.15",
|
|
39
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
39
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.99",
|
|
40
40
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.26",
|
|
41
41
|
"@reventlessdev/reventless-spec": "3.0.0-alpha.76"
|
|
42
42
|
},
|
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])));
|
package/src/admin/AdminApi.res
CHANGED
|
@@ -9,46 +9,14 @@ let cloneMutationEntry: mutationSchemaEntry = {
|
|
|
9
9
|
description: "Clone the system to a specific point in time",
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
// ApiFragmentRegistry admin mutations — the real GraphQL surface of the ApiFragmentRegistry
|
|
13
|
-
// singleton aggregate: the plugin / standalone-service deploy calls
|
|
14
|
-
// `Platform_ApiFragmentRegistry_RegisterApiFragment` / `..._DeregisterApiFragment` as a SigV4
|
|
15
|
-
// system caller. Derived from the aggregate's `commandSchema` exactly like the Plugin aggregate's
|
|
16
|
-
// entries (`PluginBaseFragment.pluginAggregateMutationEntries`) — one `Platform_<Spec>_<Command>`
|
|
17
|
-
// field per non-`@noApi` variant (`RecordApiFragmentPush` is `@noApi`, filtered out). The matching
|
|
18
|
-
// resolver wiring is fired by `Plugin_Helpers.registerAdminAggregateMutations` over the same
|
|
19
|
-
// `~aggregates` list in `Platform_Admin.construct`, so the SDL field and the auto-bound resolver
|
|
20
|
-
// carry byte-identical names.
|
|
21
|
-
let apiFragmentRegistryMutationEntries: array<mutationSchemaEntry> = {
|
|
22
|
-
let commandSchema = ApiFragmentRegistrySpec.commandSchema->S.castToUnknown
|
|
23
|
-
let constructorNames = Reventless.DcbTag.extractAllVariantNames(ApiFragmentRegistrySpec.commandSchema)
|
|
24
|
-
let filteredConstructorNames = ApiNoApiHelpers.filterNoApiVariants(constructorNames, commandSchema)
|
|
25
|
-
let fieldNames =
|
|
26
|
-
filteredConstructorNames->Array.map(cname =>
|
|
27
|
-
Api_Naming.adminField(~name=ApiFragmentRegistrySpec.name ++ "_" ++ cname)
|
|
28
|
-
)
|
|
29
|
-
if fieldNames->Array.length === 0 {
|
|
30
|
-
[]
|
|
31
|
-
} else {
|
|
32
|
-
[
|
|
33
|
-
{
|
|
34
|
-
ReventlessInfra.Api.fieldNames,
|
|
35
|
-
commandSchema,
|
|
36
|
-
},
|
|
37
|
-
]
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
12
|
// Aggregate-derived admin mutations come from `PluginBaseFragment.pluginAggregateMutationEntries`
|
|
42
|
-
// (Plugin aggregate)
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
//
|
|
13
|
+
// (Plugin aggregate) so they end up in the admin base document. The corresponding resolver wiring
|
|
14
|
+
// is fired in parallel by `Plugin_Helpers.registerAdminAggregateMutations` from
|
|
15
|
+
// `Platform_Admin.construct` over the same `~aggregates` list.
|
|
16
|
+
// (The ApiFragmentRegistry aggregate's entries were retired with the merged-API cutover —
|
|
17
|
+
// schema composition is push-free, so no deploy-time register/deregister surface exists.)
|
|
47
18
|
let mutationEntries = (~cloner: bool) => {
|
|
48
|
-
let base =
|
|
49
|
-
PluginBaseFragment.pluginAggregateMutationEntries,
|
|
50
|
-
apiFragmentRegistryMutationEntries,
|
|
51
|
-
)
|
|
19
|
+
let base = PluginBaseFragment.pluginAggregateMutationEntries
|
|
52
20
|
if cloner {
|
|
53
21
|
Array.concat(base, [cloneMutationEntry])
|
|
54
22
|
} else {
|
|
@@ -58,17 +26,6 @@ let mutationEntries = (~cloner: bool) => {
|
|
|
58
26
|
|
|
59
27
|
let queryEntries = PluginBaseFragment.queryEntries
|
|
60
28
|
|
|
61
|
-
// Admin fields a deploy-time system caller (machine credentials — SigV4/IAM on AWS) invokes: the
|
|
62
|
-
// two ApiFragmentRegistry mutations (the deploy's register/deregister write path) and
|
|
63
|
-
// `Platform_ApiFragments` (the deploy waiter's push-status poll). Provider-neutral by design — the
|
|
64
|
-
// AWS adapter threads these into `injectAwsAuthAll`'s `~iamFieldNames` so only these fields carry
|
|
65
|
-
// the dual-auth (`@aws_cognito_user_pools @aws_iam`) directive; every other admin field stays
|
|
66
|
-
// Cognito-only. Derived from the same mutation entries so the names never drift from the SDL.
|
|
67
|
-
let systemCallerFieldNames =
|
|
68
|
-
apiFragmentRegistryMutationEntries
|
|
69
|
-
->Array.flatMap(e => e.fieldNames)
|
|
70
|
-
->Array.concat([Platform_ApiFragmentsApi.queryFieldName])
|
|
71
|
-
|
|
72
29
|
// UIFragment lifecycle Source C subscriptions.
|
|
73
30
|
// Mutations are called by the backend when UIFragment events occur; the
|
|
74
31
|
// subscription→mutation fan-in is carried as neutral `subscriptionSource`
|
|
@@ -132,35 +89,25 @@ let baseFragment = (~cloner: bool) => {
|
|
|
132
89
|
// matching SDL fields, AppSync's CreateResolver fails with "Type not found".
|
|
133
90
|
let (pluginAggregateSubscriptionFields, pluginAggregateSubscriptionSources) =
|
|
134
91
|
Plugin_SubscriptionSchema.sourceCFields(~mutationEntries=PluginBaseFragment.pluginAggregateMutationEntries)
|
|
135
|
-
// The ApiFragmentRegistry aggregate's mutations go through the same CommandGenerator
|
|
136
|
-
// auto-flow, which creates an `on<field>` subscription resolver per mutation. On the
|
|
137
|
-
// static-push platform (AWS) those resolvers orphan unless the fields are declared in the
|
|
138
|
-
// pushed baseFragment — so generate them here too, symmetric with the Plugin aggregate.
|
|
139
|
-
let (apiFragmentAggregateSubscriptionFields, apiFragmentAggregateSubscriptionSources) =
|
|
140
|
-
Plugin_SubscriptionSchema.sourceCFields(~mutationEntries=apiFragmentRegistryMutationEntries)
|
|
141
92
|
GraphQL_Stitcher.encode({
|
|
142
93
|
types: parts.types
|
|
143
94
|
->Array.concat(uiFragmentSubscriptionTypes)
|
|
144
95
|
->Array.concat(pluginStatusSubscriptionTypes)
|
|
145
96
|
->Array.concat(Platform_ComponentDefinitionsApi.sdlTypes)
|
|
146
|
-
->Array.concat(Platform_UIFragmentsApi.sdlTypes)
|
|
147
|
-
->Array.concat(Platform_ApiFragmentsApi.sdlTypes),
|
|
97
|
+
->Array.concat(Platform_UIFragmentsApi.sdlTypes),
|
|
148
98
|
queries: Array.concat(
|
|
149
99
|
parts.queries,
|
|
150
100
|
[
|
|
151
101
|
Platform_ComponentDefinitionsApi.sdlQueryField,
|
|
152
102
|
Platform_UIFragmentsApi.sdlQueryField,
|
|
153
|
-
Platform_ApiFragmentsApi.sdlQueryField,
|
|
154
103
|
],
|
|
155
104
|
),
|
|
156
105
|
mutations: parts.mutations
|
|
157
106
|
->Array.concat(uiFragmentMutationFields)
|
|
158
107
|
->Array.concat(pluginStatusMutationFields),
|
|
159
108
|
subscriptions: [uiFragmentSubscriptionField, pluginStatusSubscriptionField]
|
|
160
|
-
->Array.concat(pluginAggregateSubscriptionFields)
|
|
161
|
-
->Array.concat(apiFragmentAggregateSubscriptionFields),
|
|
109
|
+
->Array.concat(pluginAggregateSubscriptionFields),
|
|
162
110
|
subscriptionSources: [uiFragmentSubscriptionSource, pluginStatusSubscriptionSource]
|
|
163
|
-
->Array.concat(pluginAggregateSubscriptionSources)
|
|
164
|
-
->Array.concat(apiFragmentAggregateSubscriptionSources),
|
|
111
|
+
->Array.concat(pluginAggregateSubscriptionSources),
|
|
165
112
|
})
|
|
166
113
|
}
|
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as S from "sury/src/S.res.mjs";
|
|
4
|
-
import * as DcbTag$Reventless from "@reventlessdev/reventless-spec/src/components/DcbTag.res.mjs";
|
|
5
4
|
import * as Api_Naming$ReventlessCore from "../components/Api/Api_Naming.res.mjs";
|
|
6
|
-
import * as ApiNoApiHelpers$ReventlessCore from "../components/Api/ApiNoApiHelpers.res.mjs";
|
|
7
5
|
import * as GraphQL_Stitcher$ReventlessCore from "../components/Api/GraphQL_Stitcher.res.mjs";
|
|
8
6
|
import * as PluginBaseFragment$ReventlessCore from "../plugin/api/PluginBaseFragment.res.mjs";
|
|
9
|
-
import * as ApiFragmentRegistrySpec$ReventlessCore from "./ApiFragmentRegistry/ApiFragmentRegistrySpec.res.mjs";
|
|
10
7
|
import * as Platform_UIFragmentsApi$ReventlessCore from "./Platform_UIFragmentsApi.res.mjs";
|
|
11
|
-
import * as Platform_ApiFragmentsApi$ReventlessCore from "./Platform_ApiFragmentsApi.res.mjs";
|
|
12
8
|
import * as GraphQL_FragmentGenerator$ReventlessCore from "../components/Api/GraphQL_FragmentGenerator.res.mjs";
|
|
13
9
|
import * as Plugin_SubscriptionSchema$ReventlessCore from "../plugin/component/Plugin_SubscriptionSchema.res.mjs";
|
|
14
10
|
import * as Platform_ComponentDefinitionsApi$ReventlessCore from "./Platform_ComponentDefinitionsApi.res.mjs";
|
|
@@ -27,28 +23,14 @@ let cloneMutationEntry = {
|
|
|
27
23
|
description: cloneMutationEntry_description
|
|
28
24
|
};
|
|
29
25
|
|
|
30
|
-
let constructorNames = DcbTag$Reventless.extractAllVariantNames(ApiFragmentRegistrySpec$ReventlessCore.commandSchema);
|
|
31
|
-
|
|
32
|
-
let filteredConstructorNames = ApiNoApiHelpers$ReventlessCore.filterNoApiVariants(constructorNames, ApiFragmentRegistrySpec$ReventlessCore.commandSchema);
|
|
33
|
-
|
|
34
|
-
let fieldNames = filteredConstructorNames.map(cname => Api_Naming$ReventlessCore.adminField(ApiFragmentRegistrySpec$ReventlessCore.name + "_" + cname));
|
|
35
|
-
|
|
36
|
-
let apiFragmentRegistryMutationEntries = fieldNames.length === 0 ? [] : [{
|
|
37
|
-
fieldNames: fieldNames,
|
|
38
|
-
commandSchema: ApiFragmentRegistrySpec$ReventlessCore.commandSchema
|
|
39
|
-
}];
|
|
40
|
-
|
|
41
26
|
function mutationEntries(cloner) {
|
|
42
|
-
let base = PluginBaseFragment$ReventlessCore.pluginAggregateMutationEntries.concat(apiFragmentRegistryMutationEntries);
|
|
43
27
|
if (cloner) {
|
|
44
|
-
return
|
|
28
|
+
return PluginBaseFragment$ReventlessCore.pluginAggregateMutationEntries.concat([cloneMutationEntry]);
|
|
45
29
|
} else {
|
|
46
|
-
return
|
|
30
|
+
return PluginBaseFragment$ReventlessCore.pluginAggregateMutationEntries;
|
|
47
31
|
}
|
|
48
32
|
}
|
|
49
33
|
|
|
50
|
-
let systemCallerFieldNames = apiFragmentRegistryMutationEntries.flatMap(e => e.fieldNames).concat([Platform_ApiFragmentsApi$ReventlessCore.queryFieldName]);
|
|
51
|
-
|
|
52
34
|
let uiFragmentSubscriptionTypes = [
|
|
53
35
|
`enum UIFragmentChangeKind {\n Registered\n Updated\n Deregistered\n}`,
|
|
54
36
|
`type UIFragmentChangeEvent {\n pluginId: ID!\n changeKind: UIFragmentChangeKind!\n manifest: String\n}`
|
|
@@ -93,23 +75,21 @@ function baseFragment(cloner) {
|
|
|
93
75
|
let base = GraphQL_FragmentGenerator$ReventlessCore.generate(mutationEntries(cloner), PluginBaseFragment$ReventlessCore.queryEntries);
|
|
94
76
|
let parts = GraphQL_Stitcher$ReventlessCore.decode(base);
|
|
95
77
|
let match = Plugin_SubscriptionSchema$ReventlessCore.sourceCFields(PluginBaseFragment$ReventlessCore.pluginAggregateMutationEntries);
|
|
96
|
-
let match$1 = Plugin_SubscriptionSchema$ReventlessCore.sourceCFields(apiFragmentRegistryMutationEntries);
|
|
97
78
|
return GraphQL_Stitcher$ReventlessCore.encode({
|
|
98
|
-
types: parts.types.concat(uiFragmentSubscriptionTypes).concat(pluginStatusSubscriptionTypes).concat(Platform_ComponentDefinitionsApi$ReventlessCore.sdlTypes).concat(Platform_UIFragmentsApi$ReventlessCore.sdlTypes)
|
|
79
|
+
types: parts.types.concat(uiFragmentSubscriptionTypes).concat(pluginStatusSubscriptionTypes).concat(Platform_ComponentDefinitionsApi$ReventlessCore.sdlTypes).concat(Platform_UIFragmentsApi$ReventlessCore.sdlTypes),
|
|
99
80
|
mutations: parts.mutations.concat(uiFragmentMutationFields).concat(pluginStatusMutationFields),
|
|
100
81
|
queries: parts.queries.concat([
|
|
101
82
|
Platform_ComponentDefinitionsApi$ReventlessCore.sdlQueryField,
|
|
102
|
-
Platform_UIFragmentsApi$ReventlessCore.sdlQueryField
|
|
103
|
-
Platform_ApiFragmentsApi$ReventlessCore.sdlQueryField
|
|
83
|
+
Platform_UIFragmentsApi$ReventlessCore.sdlQueryField
|
|
104
84
|
]),
|
|
105
85
|
subscriptions: [
|
|
106
86
|
uiFragmentSubscriptionField,
|
|
107
87
|
pluginStatusSubscriptionField
|
|
108
|
-
].concat(match[0])
|
|
88
|
+
].concat(match[0]),
|
|
109
89
|
subscriptionSources: [
|
|
110
90
|
uiFragmentSubscriptionSource,
|
|
111
91
|
pluginStatusSubscriptionSource
|
|
112
|
-
].concat(match[1])
|
|
92
|
+
].concat(match[1])
|
|
113
93
|
});
|
|
114
94
|
}
|
|
115
95
|
|
|
@@ -118,10 +98,8 @@ let queryEntries = PluginBaseFragment$ReventlessCore.queryEntries;
|
|
|
118
98
|
export {
|
|
119
99
|
cloneArgsSchema,
|
|
120
100
|
cloneMutationEntry,
|
|
121
|
-
apiFragmentRegistryMutationEntries,
|
|
122
101
|
mutationEntries,
|
|
123
102
|
queryEntries,
|
|
124
|
-
systemCallerFieldNames,
|
|
125
103
|
uiFragmentSubscriptionTypes,
|
|
126
104
|
uiFragmentMutationFields,
|
|
127
105
|
uiFragmentSubscriptionField,
|
|
@@ -317,115 +317,7 @@ document carries it (via `stitch`).
|
|
|
317
317
|
let stitchStandalone = (~fragment: Reventless.Plugin.apiSchemaFragment): string =>
|
|
318
318
|
assembleSdl(~fragments=[fragment], ~includeGlobalNodeQuery=false)
|
|
319
319
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
| None => false
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
/**
|
|
328
|
-
List the field NAMES defined inside a root object type (`Mutation` / `Query` /
|
|
329
|
-
`Subscription`) of a full SDL string.
|
|
330
|
-
|
|
331
|
-
This is the identity-aware primitive underlying every schema-clobber guard:
|
|
332
|
-
`countRootTypeFields` is just its length, and the deploy-time repair / shrink
|
|
333
|
-
guards compare the returned NAME SETS rather than cardinalities so they catch
|
|
334
|
-
field *swaps* and equal-cardinality drift that a count comparison misses.
|
|
335
|
-
|
|
336
|
-
Heuristic but adequate: root operation fields are flat single-line declarations
|
|
337
|
-
(`name(args): T` or `name: T`), so we slice the `type <typeName> { … }` block at
|
|
338
|
-
its first `{`/`}` and read the leading identifier of each indented line that
|
|
339
|
-
begins with an identifier character, ignoring directive-only lines (`@…`),
|
|
340
|
-
comments (`#…`) and braces.
|
|
341
|
-
*/
|
|
342
|
-
let rootTypeFieldNames = (~sdl: string, ~typeName: string): array<string> => {
|
|
343
|
-
let marker = `type ${typeName}`
|
|
344
|
-
switch sdl->String.indexOfOpt(marker) {
|
|
345
|
-
| None => []
|
|
346
|
-
| Some(typeStart) =>
|
|
347
|
-
let rest = sdl->String.slice(~start=typeStart)
|
|
348
|
-
switch (rest->String.indexOfOpt("{"), rest->String.indexOfOpt("}")) {
|
|
349
|
-
| (Some(openIdx), Some(closeIdx)) if closeIdx > openIdx =>
|
|
350
|
-
rest
|
|
351
|
-
->String.slice(~start=openIdx + 1, ~end=closeIdx)
|
|
352
|
-
->String.split("\n")
|
|
353
|
-
->Array.filterMap(line => {
|
|
354
|
-
let trimmed = line->String.trim
|
|
355
|
-
if trimmed->String.length > 0 && isIdentStart(trimmed) {
|
|
356
|
-
// `name(args): T` → extractLeadingName strips the arg list; `name: T`
|
|
357
|
-
// (no args) leaves a trailing colon on the token — drop it so a field
|
|
358
|
-
// reads the same whether or not it takes arguments.
|
|
359
|
-
let name = extractLeadingName(trimmed)
|
|
360
|
-
let name = name->String.endsWith(":")
|
|
361
|
-
? name->String.slice(~start=0, ~end=name->String.length - 1)
|
|
362
|
-
: name
|
|
363
|
-
name->String.length > 0 ? Some(name) : None
|
|
364
|
-
} else {
|
|
365
|
-
None
|
|
366
|
-
}
|
|
367
|
-
})
|
|
368
|
-
| _ => []
|
|
369
|
-
}
|
|
370
|
-
}
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
/**
|
|
374
|
-
Count the field definitions inside a root object type (`Mutation` / `Query`) of a
|
|
375
|
-
full SDL string. Length of `rootTypeFieldNames`.
|
|
376
|
-
*/
|
|
377
|
-
let countRootTypeFields = (~sdl: string, ~typeName: string): int =>
|
|
378
|
-
rootTypeFieldNames(~sdl, ~typeName)->Array.length
|
|
379
|
-
|
|
380
|
-
// The three GraphQL root operation types every clobber guard reasons over.
|
|
381
|
-
let rootTypeNames = ["Mutation", "Query", "Subscription"]
|
|
382
|
-
|
|
383
|
-
/**
|
|
384
|
-
Union of every root-operation field name across `Mutation` + `Query` +
|
|
385
|
-
`Subscription`. The identity set the deploy-time repair and shrink guards diff.
|
|
386
|
-
*/
|
|
387
|
-
let allRootFieldNames = (~sdl: string): array<string> =>
|
|
388
|
-
rootTypeNames->Array.flatMap(typeName => rootTypeFieldNames(~sdl, ~typeName))
|
|
389
|
-
|
|
390
|
-
/**
|
|
391
|
-
Root-operation field names present in `expectedSdl` but MISSING from `liveSdl`
|
|
392
|
-
(across Mutation + Query + Subscription). Non-empty ⇒ the live schema is not a
|
|
393
|
-
superset of what we expect and a repair push is warranted — this heals field
|
|
394
|
-
*swaps* and equal-cardinality drift that a bare count comparison would skip.
|
|
395
|
-
*/
|
|
396
|
-
let missingRootFields = (~expectedSdl: string, ~liveSdl: string): array<string> => {
|
|
397
|
-
let liveSet = allRootFieldNames(~sdl=liveSdl)->Set.fromArray
|
|
398
|
-
allRootFieldNames(~sdl=expectedSdl)->Array.filter(name => !(liveSet->Set.has(name)))
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
Decide whether replacing the live schema (`currentSdl`) with `newSdl` would drop
|
|
403
|
-
root-operation fields the pushing stack does not carry — i.e. an almost-certainly
|
|
404
|
-
transient/incomplete stitch rather than an intentional removal.
|
|
405
|
-
|
|
406
|
-
Identity-based (across `Mutation` + `Query` + `Subscription`):
|
|
407
|
-
- When `newSdl`'s field-name set is a SUPERSET of the live set — nothing is
|
|
408
|
-
dropped — it is purely additive (or unchanged) and never a clobber, so this
|
|
409
|
-
returns `false` even when the pushing stack is legitimately adding its own
|
|
410
|
-
fields.
|
|
411
|
-
- Otherwise it falls back to the cardinality threshold on the union of the three
|
|
412
|
-
root types: `true` only when the new total drops below `threshold` × the
|
|
413
|
-
current total. A small intentional removal (above threshold) is allowed.
|
|
414
|
-
|
|
415
|
-
When the current schema has no countable root fields (e.g. first deploy, or
|
|
416
|
-
introspection was unavailable so `currentSdl` is empty) it returns `false` so the
|
|
417
|
-
initial push always proceeds.
|
|
418
|
-
*/
|
|
419
|
-
let isCatastrophicSchemaShrink = (~currentSdl: string, ~newSdl: string, ~threshold: float): bool => {
|
|
420
|
-
let currentNames = allRootFieldNames(~sdl=currentSdl)
|
|
421
|
-
let newSet = allRootFieldNames(~sdl=newSdl)->Set.fromArray
|
|
422
|
-
let dropped = currentNames->Array.filter(name => !(newSet->Set.has(name)))
|
|
423
|
-
if dropped->Array.length == 0 {
|
|
424
|
-
// Additive or unchanged push — the new schema keeps every live field.
|
|
425
|
-
false
|
|
426
|
-
} else {
|
|
427
|
-
let current = currentNames->Array.length
|
|
428
|
-
let next = allRootFieldNames(~sdl=newSdl)->Array.length
|
|
429
|
-
current > 0 && next->Int.toFloat < current->Int.toFloat *. threshold
|
|
430
|
-
}
|
|
431
|
-
}
|
|
320
|
+
// (The schema-clobber guard family — rootTypeFieldNames / countRootTypeFields /
|
|
321
|
+
// missingRootFields / isCatastrophicSchemaShrink — was retired with the push
|
|
322
|
+
// machinery: under merged-API composition every source API is a single writer,
|
|
323
|
+
// so no whole-replace push exists to guard.)
|