@reventlessdev/reventless-aws 3.0.0-alpha.280 → 3.0.0-alpha.281
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 +7 -0
- package/package.json +6 -6
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +25 -11
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +6 -3
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res +64 -0
- package/tests/Platform_ComponentDefinitions_Lambda_OpsTest.res.mjs +44 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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.281 (2026-08-10)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **admin:** carry Internal queryables on their own manifest field ([f5aa03d](https://github.com/ReventlessDev/reventless-core/commit/f5aa03d60b0b2554d116fe9b3784d30f2c4ef356))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.280 (2026-08-10)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-aws",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.281",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -14,16 +14,16 @@
|
|
|
14
14
|
"uuid": "^13.0.0",
|
|
15
15
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.6",
|
|
16
16
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
17
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
18
17
|
"@reventlessdev/rescript-node": "2.0.0-alpha.3",
|
|
19
18
|
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.69",
|
|
19
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
20
20
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
21
21
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
22
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
23
|
-
"@reventlessdev/reventless-infra": "3.0.0-alpha.
|
|
22
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.223",
|
|
23
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.133",
|
|
24
24
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.30",
|
|
25
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
26
|
-
"@reventlessdev/reventless-spec": "3.0.0-alpha.
|
|
25
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.87",
|
|
26
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.107"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"rescript": "12.3.0",
|
|
@@ -114,19 +114,33 @@ let isPublicQueryable = (q: JSON.t): bool =>
|
|
|
114
114
|
| _ => true
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
-
// Drop Internal ReadModels / StateViewSlices from a persisted structure
|
|
118
|
-
//
|
|
117
|
+
// Drop Internal ReadModels / StateViewSlices from a persisted structure, and
|
|
118
|
+
// carry what was dropped on `internalQueryables` — the complement, so a client
|
|
119
|
+
// can resolve a reference to an Internal view without any enumeration site
|
|
120
|
+
// being able to see it. A missing/non-array field defaults to `[]` (mirrors the
|
|
121
|
+
// JS `?? []`), which also covers a structure persisted before this field
|
|
122
|
+
// existed: the complement is recomputed here, never read off the structure.
|
|
123
|
+
//
|
|
124
|
+
// The `~filter=false` path (Platform_PluginStructures) deliberately does not go
|
|
125
|
+
// through here — it serves Internal components inline and must not grow a
|
|
126
|
+
// duplicate list.
|
|
119
127
|
let filterStructure = (structure: dict<JSON.t>): dict<JSON.t> => {
|
|
120
128
|
let out = Dict.fromArray(structure->Dict.toArray)
|
|
121
|
-
let
|
|
122
|
-
out
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
let arrAt = key =>
|
|
130
|
+
out->Dict.get(key)->Option.flatMap(JSON.Decode.array)->Option.getOr([])
|
|
131
|
+
let readModels = arrAt("readModels")
|
|
132
|
+
let stateViewSlices = arrAt("stateViewSlices")
|
|
133
|
+
out->Dict.set("readModels", readModels->Array.filter(isPublicQueryable)->JSON.Encode.array)
|
|
134
|
+
out->Dict.set(
|
|
135
|
+
"stateViewSlices",
|
|
136
|
+
stateViewSlices->Array.filter(isPublicQueryable)->JSON.Encode.array,
|
|
137
|
+
)
|
|
138
|
+
out->Dict.set(
|
|
139
|
+
"internalQueryables",
|
|
140
|
+
Array.concat(readModels, stateViewSlices)
|
|
141
|
+
->Array.filter(q => !isPublicQueryable(q))
|
|
142
|
+
->JSON.Encode.array,
|
|
143
|
+
)
|
|
130
144
|
out
|
|
131
145
|
}
|
|
132
146
|
|
|
@@ -166,9 +166,12 @@ function isPublicQueryable(q) {
|
|
|
166
166
|
|
|
167
167
|
function filterStructure(structure) {
|
|
168
168
|
let out = Object.fromEntries(Object.entries(structure));
|
|
169
|
-
let
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
let arrAt = key => Stdlib_Option.getOr(Stdlib_Option.flatMap(out[key], Stdlib_JSON.Decode.array), []);
|
|
170
|
+
let readModels = arrAt("readModels");
|
|
171
|
+
let stateViewSlices = arrAt("stateViewSlices");
|
|
172
|
+
out["readModels"] = readModels.filter(isPublicQueryable);
|
|
173
|
+
out["stateViewSlices"] = stateViewSlices.filter(isPublicQueryable);
|
|
174
|
+
out["internalQueryables"] = readModels.concat(stateViewSlices).filter(q => !isPublicQueryable(q));
|
|
172
175
|
return out;
|
|
173
176
|
}
|
|
174
177
|
|
|
@@ -112,3 +112,67 @@ describe("toEntryWith heals a structure persisted before a required list existed
|
|
|
112
112
|
expect(names)->toEqual(["Products"])
|
|
113
113
|
})
|
|
114
114
|
})
|
|
115
|
+
|
|
116
|
+
// The persisted structure is pre-filter, so this handler is a twin of the
|
|
117
|
+
// in-memory `encodePluginStructureEntry` rather than a consequence of it. A shell
|
|
118
|
+
// reading `internalQueryables` against a platform that emits it on only one
|
|
119
|
+
// transport gets working pickers locally and silent text inputs when deployed —
|
|
120
|
+
// which is why the split is asserted here as well as in core.
|
|
121
|
+
describe("toEntryWith carries Internal queryables on the filtered field", () => {
|
|
122
|
+
let mixed = Dict.fromArray([
|
|
123
|
+
(
|
|
124
|
+
"readModels",
|
|
125
|
+
JSON.Encode.array([
|
|
126
|
+
JSON.parseOrThrow(`{"name": "AvailableProducts", "visibility": "Internal"}`),
|
|
127
|
+
JSON.parseOrThrow(`{"name": "Products"}`),
|
|
128
|
+
]),
|
|
129
|
+
),
|
|
130
|
+
(
|
|
131
|
+
"stateViewSlices",
|
|
132
|
+
JSON.Encode.array([
|
|
133
|
+
JSON.parseOrThrow(`{"name": "PendingShipments", "visibility": "Internal"}`),
|
|
134
|
+
]),
|
|
135
|
+
),
|
|
136
|
+
])->JSON.Encode.object
|
|
137
|
+
|
|
138
|
+
let namesAt = (~filter, collection) =>
|
|
139
|
+
entry(~filter, mixed)
|
|
140
|
+
->Option.map(members(_, collection))
|
|
141
|
+
->Option.getOr([])
|
|
142
|
+
->Array.filterMap(m => m->Dict.get("name")->Option.flatMap(JSON.Decode.string))
|
|
143
|
+
|
|
144
|
+
testSync("emits the complement of the filter, fed by both source arrays", () =>
|
|
145
|
+
expect(namesAt(~filter=true, "internalQueryables"))->toEqual([
|
|
146
|
+
"AvailableProducts",
|
|
147
|
+
"PendingShipments",
|
|
148
|
+
])
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
testSync("keeps the surface lists public-only", () => {
|
|
152
|
+
expect(namesAt(~filter=true, "readModels"))->toEqual(["Products"])
|
|
153
|
+
expect(namesAt(~filter=true, "stateViewSlices"))->toEqual([])
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
// Platform_PluginStructures serves Internal components inline; a duplicate list
|
|
157
|
+
// there would be a second answer to a question that already has one.
|
|
158
|
+
testSync("adds nothing to the complete field", () =>
|
|
159
|
+
expect(
|
|
160
|
+
entry(~filter=false, mixed)->Option.flatMap(o => o->Dict.get("internalQueryables")),
|
|
161
|
+
)->toEqual(None)
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
// A structure persisted before the field existed has no key for it, and must not
|
|
165
|
+
// grow one from its own contents — the complement is always recomputed.
|
|
166
|
+
testSync("computes the complement rather than reading it off the structure", () => {
|
|
167
|
+
let stale = Dict.fromArray([
|
|
168
|
+
("internalQueryables", JSON.parseOrThrow(`[{"name": "Stale"}]`)),
|
|
169
|
+
("readModels", JSON.parseOrThrow(`[{"name": "Products"}]`)),
|
|
170
|
+
])->JSON.Encode.object
|
|
171
|
+
let names =
|
|
172
|
+
entry(~filter=true, stale)
|
|
173
|
+
->Option.map(members(_, "internalQueryables"))
|
|
174
|
+
->Option.getOr([])
|
|
175
|
+
->Array.filterMap(m => m->Dict.get("name")->Option.flatMap(JSON.Decode.string))
|
|
176
|
+
expect(names)->toEqual([])
|
|
177
|
+
})
|
|
178
|
+
})
|
|
@@ -64,6 +64,50 @@ globalThis.describe("toEntryWith heals a structure persisted before a required l
|
|
|
64
64
|
});
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
globalThis.describe("toEntryWith carries Internal queryables on the filtered field", () => {
|
|
68
|
+
let mixed = Object.fromEntries([
|
|
69
|
+
[
|
|
70
|
+
"readModels",
|
|
71
|
+
[
|
|
72
|
+
JSON.parse(`{"name": "AvailableProducts", "visibility": "Internal"}`),
|
|
73
|
+
JSON.parse(`{"name": "Products"}`)
|
|
74
|
+
]
|
|
75
|
+
],
|
|
76
|
+
[
|
|
77
|
+
"stateViewSlices",
|
|
78
|
+
[JSON.parse(`{"name": "PendingShipments", "visibility": "Internal"}`)]
|
|
79
|
+
]
|
|
80
|
+
]);
|
|
81
|
+
let namesAt = (filter, collection) => Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.map(entry(filter, mixed), __x => members(__x, collection)), []), m => Stdlib_Option.flatMap(m["name"], Stdlib_JSON.Decode.string));
|
|
82
|
+
globalThis.test("emits the complement of the filter, fed by both source arrays", () => {
|
|
83
|
+
globalThis.expect(namesAt(true, "internalQueryables")).toEqual([
|
|
84
|
+
"AvailableProducts",
|
|
85
|
+
"PendingShipments"
|
|
86
|
+
]);
|
|
87
|
+
});
|
|
88
|
+
globalThis.test("keeps the surface lists public-only", () => {
|
|
89
|
+
globalThis.expect(namesAt(true, "readModels")).toEqual(["Products"]);
|
|
90
|
+
globalThis.expect(namesAt(true, "stateViewSlices")).toEqual([]);
|
|
91
|
+
});
|
|
92
|
+
globalThis.test("adds nothing to the complete field", () => {
|
|
93
|
+
globalThis.expect(Stdlib_Option.flatMap(entry(false, mixed), o => o["internalQueryables"])).toEqual(undefined);
|
|
94
|
+
});
|
|
95
|
+
globalThis.test("computes the complement rather than reading it off the structure", () => {
|
|
96
|
+
let stale = Object.fromEntries([
|
|
97
|
+
[
|
|
98
|
+
"internalQueryables",
|
|
99
|
+
JSON.parse(`[{"name": "Stale"}]`)
|
|
100
|
+
],
|
|
101
|
+
[
|
|
102
|
+
"readModels",
|
|
103
|
+
JSON.parse(`[{"name": "Products"}]`)
|
|
104
|
+
]
|
|
105
|
+
]);
|
|
106
|
+
let names = Stdlib_Array.filterMap(Stdlib_Option.getOr(Stdlib_Option.map(entry(true, stale), __x => members(__x, "internalQueryables")), []), m => Stdlib_Option.flatMap(m["name"], Stdlib_JSON.Decode.string));
|
|
107
|
+
globalThis.expect(names).toEqual([]);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
|
|
67
111
|
export {
|
|
68
112
|
entry,
|
|
69
113
|
members,
|