@reventlessdev/reventless-core 3.0.0-alpha.178 → 3.0.0-alpha.180

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,20 @@
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.180 (2026-07-24)
7
+
8
+ ### Features
9
+
10
+ * **core:** Monitoring seam announces owning plugin/platform to backends ([79dd49a](https://github.com/ReventlessDev/reventless-core/commit/79dd49ae29feb95ed88c836cd8827c8e68978889))
11
+
12
+
13
+ # 3.0.0-alpha.179 (2026-07-23)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **aws:** round-trip the DynamoDB Scan cursor in full-list connections ([0e21008](https://github.com/ReventlessDev/reventless-core/commit/0e2100860767a153a5c2f50a88fd6b501787c7f4))
18
+
19
+
6
20
  # 3.0.0-alpha.178 (2026-07-22)
7
21
 
8
22
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-core",
3
- "version": "3.0.0-alpha.178",
3
+ "version": "3.0.0-alpha.180",
4
4
  "description": "Core package for Reventless framework",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -29,16 +29,16 @@
29
29
  "sury": "11.0.0-alpha.4",
30
30
  "uuid": "^13.0.0",
31
31
  "@reventlessdev/rescript-effect": "0.1.0-alpha.30",
32
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
32
33
  "@reventlessdev/rescript-fast-csv": "1.2.0-alpha.14",
33
34
  "@reventlessdev/rescript-hash-object": "1.2.0-alpha.13",
34
- "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
35
35
  "@reventlessdev/rescript-node-streams": "1.1.0-alpha.13",
36
- "@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
37
36
  "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
38
- "@reventlessdev/rescript-uuid": "1.1.0-alpha.17",
37
+ "@reventlessdev/rescript-ssh2": "1.1.0-alpha.14",
39
38
  "@reventlessdev/reventless-interop": "3.0.0-alpha.29",
40
39
  "@reventlessdev/reventless-infra": "3.0.0-alpha.104",
41
- "@reventlessdev/reventless-spec": "3.0.0-alpha.80"
40
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.80",
41
+ "@reventlessdev/rescript-uuid": "1.1.0-alpha.17"
42
42
  },
43
43
  "devDependencies": {
44
44
  "rescript": "12.3.0",
@@ -58,18 +58,26 @@ FunctionName or a PrometheusRule deployment selector, but unusable as a logical
58
58
  resource name). Because `onProvisioned` fires during resource creation inside the
59
59
  deploy program, a Pulumi-based backend can create monitoring resources in the
60
60
  same stack, parented naturally.
61
+
62
+ `~plugin` / `~platform` are the OWNING plugin and platform of the unit, taken from the ambient
63
+ `ResourceAttribution` context the plugin builder publishes during construction (the same source
64
+ `AWS_Tags` uses). Both are `None` for substrate provisioned outside any plugin construct — the
65
+ correct answer for platform-scope resources. A backend that names or routes its monitoring resources
66
+ per plugin needs this: `~name` is only the component, and two plugins can own like-named components.
61
67
  */
62
68
  module type Backend = {
63
69
  let onProvisioned: (
64
70
  ~kind: unitKind,
65
71
  ~name: string,
66
72
  ~component: ReventlessInfra.Adapter.resource,
73
+ ~plugin: option<string>,
74
+ ~platform: option<string>,
67
75
  ) => unit
68
76
  }
69
77
 
70
78
  /** Default backend: does nothing. Active unless an extension calls `use`. */
71
79
  module Noop: Backend = {
72
- let onProvisioned = (~kind as _, ~name as _, ~component as _) => ()
80
+ let onProvisioned = (~kind as _, ~name as _, ~component as _, ~plugin as _, ~platform as _) => ()
73
81
  }
74
82
 
75
83
  let backend: ref<module(Backend)> = ref(module(Noop: Backend))
@@ -92,5 +100,9 @@ let notify = (
92
100
  ~component: ReventlessInfra.Adapter.resource,
93
101
  ) => {
94
102
  module B = unpack(backend.contents)
95
- B.onProvisioned(~kind, ~name, ~component)
103
+ // The owning plugin/platform come from the ambient context the plugin builder publishes
104
+ // (ResourceAttribution.enter) — populated exactly when this fires, so the call sites need not
105
+ // thread the names. `None` outside a plugin construct (platform substrate).
106
+ let {ResourceAttribution.plugin: plugin, platform} = ResourceAttribution.current.contents
107
+ B.onProvisioned(~kind, ~name, ~component, ~plugin, ~platform)
96
108
  }
@@ -1,7 +1,8 @@
1
1
  // Generated by ReScript, PLEASE EDIT WITH CARE
2
2
 
3
+ import * as ResourceAttribution$ReventlessCore from "../../ResourceAttribution.res.mjs";
3
4
 
4
- function onProvisioned(param, param$1, param$2) {
5
+ function onProvisioned(param, param$1, param$2, param$3, param$4) {
5
6
 
6
7
  }
7
8
 
@@ -19,7 +20,8 @@ function use(b) {
19
20
 
20
21
  function notify(kind, name, component) {
21
22
  let B = backend.contents;
22
- B.onProvisioned(kind, name, component);
23
+ let match = ResourceAttribution$ReventlessCore.current.contents;
24
+ B.onProvisioned(kind, name, component, match.plugin, match.platform);
23
25
  }
24
26
 
25
27
  export {
@@ -251,7 +251,7 @@ let validateScanSortAlignment = (
251
251
  None
252
252
  } else {
253
253
  Some(
254
- `Read model "${readModelName}": @scanSort field "${field}" is not the sort key of any table or GSI. Sort requests for this field will be evaluated as a JS-runtime per-page sort over a full Scan — expensive in production. Add it as the sort key of an index, or accept the per-page-sort caveat.`,
254
+ `Read model "${readModelName}": @scanSort field "${field}" is not the sort key of any table or GSI. Sort requests Scan the whole table and sort per-page, so results past the first page are NOT globally ordered — and the Scan is expensive in production. Promote the field to an index sort key for correct, cheap ordering, or accept per-page ordering.`,
255
255
  )
256
256
  }
257
257
  )
@@ -221,7 +221,7 @@ function validateScanSortAlignment(schema, readModelName, knownSortFields) {
221
221
  if (knownSortFields.includes(field)) {
222
222
  return;
223
223
  } else {
224
- return `Read model "` + readModelName + `": @scanSort field "` + field + `" is not the sort key of any table or GSI. Sort requests for this field will be evaluated as a JS-runtime per-page sort over a full Scan — expensive in production. Add it as the sort key of an index, or accept the per-page-sort caveat.`;
224
+ return `Read model "` + readModelName + `": @scanSort field "` + field + `" is not the sort key of any table or GSI. Sort requests Scan the whole table and sort per-page, so results past the first page are NOT globally ordered — and the Scan is expensive in production. Promote the field to an index sort key for correct, cheap ordering, or accept per-page ordering.`;
225
225
  }
226
226
  });
227
227
  } else {
@@ -15,7 +15,8 @@ describe("Monitoring", () => {
15
15
  testSync("use registers a backend that receives every notify, role and name", () => {
16
16
  let recorded: array<(Monitoring.unitKind, string)> = []
17
17
  module Recorder: Monitoring.Backend = {
18
- let onProvisioned = (~kind, ~name, ~component as _) => recorded->Array.push((kind, name))
18
+ let onProvisioned = (~kind, ~name, ~component as _, ~plugin as _, ~platform as _) =>
19
+ recorded->Array.push((kind, name))
19
20
  }
20
21
  Monitoring.use(module(Recorder: Monitoring.Backend))
21
22
 
@@ -38,7 +39,8 @@ describe("Monitoring", () => {
38
39
  testSync("notify forwards the static name and component resource to the backend", () => {
39
40
  let seen: ref<option<(string, ReventlessInfra.Adapter.resource)>> = ref(None)
40
41
  module Capture: Monitoring.Backend = {
41
- let onProvisioned = (~kind as _, ~name, ~component) => seen := Some((name, component))
42
+ let onProvisioned = (~kind as _, ~name, ~component, ~plugin as _, ~platform as _) =>
43
+ seen := Some((name, component))
42
44
  }
43
45
  Monitoring.use(module(Capture: Monitoring.Backend))
44
46
 
@@ -50,4 +52,32 @@ describe("Monitoring", () => {
50
52
 
51
53
  Monitoring.use(module(Monitoring.Noop))
52
54
  })
55
+
56
+ testSync("notify delivers the ambient plugin/platform inside a construct scope, None outside", () => {
57
+ let seen: array<(option<string>, option<string>)> = []
58
+ module OwnerCapture: Monitoring.Backend = {
59
+ let onProvisioned = (~kind as _, ~name as _, ~component as _, ~plugin, ~platform) =>
60
+ seen->Array.push((plugin, platform))
61
+ }
62
+ Monitoring.use(module(OwnerCapture: Monitoring.Backend))
63
+
64
+ // Outside any plugin construct: platform substrate → both None.
65
+ Monitoring.notify(~kind=CommandHandler, ~name="ApiRouter", ~component=stubResource)
66
+
67
+ // Inside a construct scope (what Plugin_Builder.construct establishes): both delivered.
68
+ let prev = ResourceAttribution.enter(~platform="online-shop", ~plugin="Ordering")
69
+ Monitoring.notify(~kind=CommandHandler, ~name="AllAggregatesCmdHandler", ~component=stubResource)
70
+ ResourceAttribution.restore(prev)
71
+
72
+ // After restore: back to None.
73
+ Monitoring.notify(~kind=Projection, ~name="AllStateViewSlices", ~component=stubResource)
74
+
75
+ expect(seen)->toEqual([
76
+ (None, None),
77
+ (Some("Ordering"), Some("online-shop")),
78
+ (None, None),
79
+ ])
80
+
81
+ Monitoring.use(module(Monitoring.Noop))
82
+ })
53
83
  })