@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
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
|
|
4
4
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
|
-
import * as Stdlib_String from "@rescript/runtime/lib/es6/Stdlib_String.js";
|
|
7
6
|
import * as Logger$ReventlessCore from "../../util/Logger.res.mjs";
|
|
8
7
|
|
|
9
8
|
let log = Logger$ReventlessCore.fromEnv();
|
|
@@ -226,88 +225,6 @@ function stitchStandalone(fragment) {
|
|
|
226
225
|
return assembleSdl([fragment], false);
|
|
227
226
|
}
|
|
228
227
|
|
|
229
|
-
function isIdentStart(line) {
|
|
230
|
-
let c = line.codePointAt(0);
|
|
231
|
-
if (c !== undefined) {
|
|
232
|
-
if (c >= 65 && c <= 90 || c >= 97 && c <= 122) {
|
|
233
|
-
return true;
|
|
234
|
-
} else {
|
|
235
|
-
return c === 95;
|
|
236
|
-
}
|
|
237
|
-
} else {
|
|
238
|
-
return false;
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
function rootTypeFieldNames(sdl, typeName) {
|
|
243
|
-
let marker = `type ` + typeName;
|
|
244
|
-
let typeStart = Stdlib_String.indexOfOpt(sdl, marker);
|
|
245
|
-
if (typeStart === undefined) {
|
|
246
|
-
return [];
|
|
247
|
-
}
|
|
248
|
-
let rest = sdl.slice(typeStart);
|
|
249
|
-
let match = Stdlib_String.indexOfOpt(rest, "{");
|
|
250
|
-
let match$1 = Stdlib_String.indexOfOpt(rest, "}");
|
|
251
|
-
if (match !== undefined) {
|
|
252
|
-
if (match$1 !== undefined) {
|
|
253
|
-
if (match$1 > match) {
|
|
254
|
-
return Stdlib_Array.filterMap(rest.slice(match + 1 | 0, match$1).split("\n"), line => {
|
|
255
|
-
let trimmed = line.trim();
|
|
256
|
-
if (!(trimmed.length > 0 && isIdentStart(trimmed))) {
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
let name = extractLeadingName(trimmed);
|
|
260
|
-
let name$1 = name.endsWith(":") ? name.slice(0, name.length - 1 | 0) : name;
|
|
261
|
-
if (name$1.length > 0) {
|
|
262
|
-
return name$1;
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
} else {
|
|
266
|
-
return [];
|
|
267
|
-
}
|
|
268
|
-
} else {
|
|
269
|
-
return [];
|
|
270
|
-
}
|
|
271
|
-
} else {
|
|
272
|
-
return [];
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function countRootTypeFields(sdl, typeName) {
|
|
277
|
-
return rootTypeFieldNames(sdl, typeName).length;
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
let rootTypeNames = [
|
|
281
|
-
"Mutation",
|
|
282
|
-
"Query",
|
|
283
|
-
"Subscription"
|
|
284
|
-
];
|
|
285
|
-
|
|
286
|
-
function allRootFieldNames(sdl) {
|
|
287
|
-
return rootTypeNames.flatMap(typeName => rootTypeFieldNames(sdl, typeName));
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function missingRootFields(expectedSdl, liveSdl) {
|
|
291
|
-
let liveSet = new Set(allRootFieldNames(liveSdl));
|
|
292
|
-
return allRootFieldNames(expectedSdl).filter(name => !liveSet.has(name));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function isCatastrophicSchemaShrink(currentSdl, newSdl, threshold) {
|
|
296
|
-
let currentNames = allRootFieldNames(currentSdl);
|
|
297
|
-
let newSet = new Set(allRootFieldNames(newSdl));
|
|
298
|
-
let dropped = currentNames.filter(name => !newSet.has(name));
|
|
299
|
-
if (dropped.length === 0) {
|
|
300
|
-
return false;
|
|
301
|
-
}
|
|
302
|
-
let current = currentNames.length;
|
|
303
|
-
let next = allRootFieldNames(newSdl).length;
|
|
304
|
-
if (current > 0) {
|
|
305
|
-
return next < current * threshold;
|
|
306
|
-
} else {
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
228
|
export {
|
|
312
229
|
log,
|
|
313
230
|
encodeSubscriptionSource,
|
|
@@ -320,12 +237,5 @@ export {
|
|
|
320
237
|
assembleSdl,
|
|
321
238
|
stitch,
|
|
322
239
|
stitchStandalone,
|
|
323
|
-
isIdentStart,
|
|
324
|
-
rootTypeFieldNames,
|
|
325
|
-
countRootTypeFields,
|
|
326
|
-
rootTypeNames,
|
|
327
|
-
allRootFieldNames,
|
|
328
|
-
missingRootFields,
|
|
329
|
-
isCatastrophicSchemaShrink,
|
|
330
240
|
}
|
|
331
241
|
/* log Not a pure module */
|
|
@@ -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 */
|