@reventlessdev/reventless-aws 3.0.0-alpha.266 → 3.0.0-alpha.267
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/Platform.res +1 -0
- package/src/Platform.res.mjs +2 -2
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res +36 -0
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda.res.mjs +14 -1
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res +37 -6
- package/src/adapter/Api/Platform_ComponentDefinitions_Lambda_Ops.res.mjs +19 -6
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.267 (2026-08-04)
|
|
7
|
+
|
|
8
|
+
### Features
|
|
9
|
+
|
|
10
|
+
* **admin:** serve the complete plugin structure to developer tooling ([e99cda0](https://github.com/ReventlessDev/reventless-core/commit/e99cda02a2f862b36bcfdb018a0fb34bb7426a2b))
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
# 3.0.0-alpha.266 (2026-08-04)
|
|
7
14
|
|
|
8
15
|
### Features
|
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.267",
|
|
4
4
|
"description": "AWS adapters for Reventless",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"dependencies": {
|
|
@@ -13,16 +13,16 @@
|
|
|
13
13
|
"uuid": "^13.0.0",
|
|
14
14
|
"@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.3",
|
|
15
15
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
16
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.1",
|
|
17
16
|
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
18
17
|
"@reventlessdev/rescript-pulumi-aws": "2.4.0-alpha.65",
|
|
18
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.1",
|
|
19
19
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
|
|
20
|
-
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
21
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.211",
|
|
22
20
|
"@reventlessdev/reventless-infra": "3.0.0-alpha.126",
|
|
23
|
-
"@reventlessdev/reventless-
|
|
21
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.212",
|
|
24
22
|
"@reventlessdev/reventless-interop": "3.0.0-alpha.30",
|
|
25
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
23
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.76",
|
|
24
|
+
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
25
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.100"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"rescript": "12.3.0",
|
package/src/Platform.res
CHANGED
package/src/Platform.res.mjs
CHANGED
|
@@ -861,7 +861,7 @@ function MakeWithConfig(Config) {
|
|
|
861
861
|
Pulumi$Pulumi.$$export("offloadBucket", offloadBucketName);
|
|
862
862
|
if (pluginReadModelTableName !== undefined) {
|
|
863
863
|
AggregateRuntime_Builder_Single$ReventlessAws.setPluginReadModelTable(pluginReadModelTableName);
|
|
864
|
-
Platform_ComponentDefinitions_Lambda$ReventlessAws.make(platformApi, pluginReadModelTableName, offloadBucketName, {});
|
|
864
|
+
Platform_ComponentDefinitions_Lambda$ReventlessAws.make(platformApi, pluginReadModelTableName, offloadBucketName, admin.adminSchemaPushed, {});
|
|
865
865
|
}
|
|
866
866
|
let rm = admin.stateViewSlicesOutputs["UiFragments"];
|
|
867
867
|
if (rm !== undefined) {
|
|
@@ -2096,7 +2096,7 @@ function Make($star) {
|
|
|
2096
2096
|
Pulumi$Pulumi.$$export("offloadBucket", offloadBucketName);
|
|
2097
2097
|
if (pluginReadModelTableName !== undefined) {
|
|
2098
2098
|
AggregateRuntime_Builder_Single$ReventlessAws.setPluginReadModelTable(pluginReadModelTableName);
|
|
2099
|
-
Platform_ComponentDefinitions_Lambda$ReventlessAws.make(platformApi, pluginReadModelTableName, offloadBucketName, {});
|
|
2099
|
+
Platform_ComponentDefinitions_Lambda$ReventlessAws.make(platformApi, pluginReadModelTableName, offloadBucketName, admin.adminSchemaPushed, {});
|
|
2100
2100
|
}
|
|
2101
2101
|
let rm = admin.stateViewSlicesOutputs["UiFragments"];
|
|
2102
2102
|
if (rm !== undefined) {
|
|
@@ -24,10 +24,29 @@ export function response(ctx) {
|
|
|
24
24
|
}
|
|
25
25
|
`->Pulumi.Input.make
|
|
26
26
|
|
|
27
|
+
// `Platform_PluginStructures` — the same scan, unfiltered. The `complete` flag is
|
|
28
|
+
// the whole difference; see the handler's `toEntryWith`.
|
|
29
|
+
let completeResolverCode = `
|
|
30
|
+
import { util } from '@aws-appsync/utils';
|
|
31
|
+
export function request(ctx) {
|
|
32
|
+
return { operation: 'Invoke', payload: { complete: true } };
|
|
33
|
+
}
|
|
34
|
+
export function response(ctx) {
|
|
35
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
36
|
+
return ctx.result;
|
|
37
|
+
}
|
|
38
|
+
`->Pulumi.Input.make
|
|
39
|
+
|
|
27
40
|
let make = (
|
|
28
41
|
~api: Pulumi.Output.t<AppSync.GraphQLApi.t>,
|
|
29
42
|
~pluginReadModelTableName: Pulumi.Output.t<string>,
|
|
30
43
|
~offloadBucketName: Pulumi.Output.t<string>,
|
|
44
|
+
// Resolves once the admin schema push is ACTIVE (Platform_Admin.adminSchemaPushed).
|
|
45
|
+
// Only the `Platform_PluginStructures` resolver is gated on it: that field is new,
|
|
46
|
+
// so CreateResolver would otherwise race StartSchemaCreation on the deploy that
|
|
47
|
+
// first ships it. `Platform_ComponentDefinitions` predates the gate and is left
|
|
48
|
+
// ungated so this change adds no dependency edge to an already-deployed resource.
|
|
49
|
+
~schemaReady: Pulumi.Output.t<unit>,
|
|
31
50
|
~opts: Pulumi.ComponentResource.options,
|
|
32
51
|
) => {
|
|
33
52
|
let opts = opts->ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions
|
|
@@ -215,4 +234,21 @@ let make = (
|
|
|
215
234
|
~code=resolverCode,
|
|
216
235
|
~opts,
|
|
217
236
|
)
|
|
237
|
+
|
|
238
|
+
// Second field, same DataSource and same Lambda — the developer-tooling read of
|
|
239
|
+
// the identical scan. Sharing the data source is what keeps the two answers from
|
|
240
|
+
// drifting: there is one place that decides what a deployed plugin's structure is.
|
|
241
|
+
// Created only after the schema carrying `Platform_PluginStructures` is ACTIVE.
|
|
242
|
+
let _structuresResolver =
|
|
243
|
+
schemaReady->Pulumi.Output.apply(() =>
|
|
244
|
+
AppSync_Resolver_Native.makeUnitJsResolver(
|
|
245
|
+
~name=name ++ "StructuresResolver",
|
|
246
|
+
~api,
|
|
247
|
+
~dataSourceName=dataSource.name->Pulumi.Output.asInput,
|
|
248
|
+
~type_="Query"->Pulumi.Input.make,
|
|
249
|
+
~field="Platform_PluginStructures"->Pulumi.Input.make,
|
|
250
|
+
~code=completeResolverCode,
|
|
251
|
+
~opts,
|
|
252
|
+
)
|
|
253
|
+
)
|
|
218
254
|
}
|
|
@@ -27,7 +27,18 @@ export function response(ctx) {
|
|
|
27
27
|
}
|
|
28
28
|
`;
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
let completeResolverCode = `
|
|
31
|
+
import { util } from '@aws-appsync/utils';
|
|
32
|
+
export function request(ctx) {
|
|
33
|
+
return { operation: 'Invoke', payload: { complete: true } };
|
|
34
|
+
}
|
|
35
|
+
export function response(ctx) {
|
|
36
|
+
if (ctx.error) util.error(ctx.error.message, ctx.error.type);
|
|
37
|
+
return ctx.result;
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
function make(api, pluginReadModelTableName, offloadBucketName, schemaReady, opts) {
|
|
31
42
|
let opts$1 = Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions(opts);
|
|
32
43
|
let name = "PlatformUIDefinitions";
|
|
33
44
|
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name + "Lambda", Pulumi.output(AWS$ReventlessAws.Lambda.principal), AWS_Tags$ReventlessAws.make(name + "Lambda", "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
@@ -131,10 +142,12 @@ function make(api, pluginReadModelTableName, offloadBucketName, opts) {
|
|
|
131
142
|
serviceRoleArn: dataSourceRole.arn
|
|
132
143
|
}, opts$1);
|
|
133
144
|
AppSync_Resolver_Native$ReventlessAws.makeUnitJsResolver(name + "Resolver", api, dataSource.name, "Query", "Platform_ComponentDefinitions", resolverCode, opts$1);
|
|
145
|
+
schemaReady.apply(() => AppSync_Resolver_Native$ReventlessAws.makeUnitJsResolver(name + "StructuresResolver", api, dataSource.name, "Query", "Platform_PluginStructures", completeResolverCode, opts$1));
|
|
134
146
|
}
|
|
135
147
|
|
|
136
148
|
export {
|
|
137
149
|
resolverCode,
|
|
150
|
+
completeResolverCode,
|
|
138
151
|
make,
|
|
139
152
|
}
|
|
140
153
|
/* @pulumi/aws Not a pure module */
|
|
@@ -43,11 +43,21 @@ let filterStructure = (structure: JSON.t): option<dict<JSON.t>> =>
|
|
|
43
43
|
Some(out)
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
-
// `{pluginId: name, ...
|
|
47
|
-
//
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
// `{pluginId: name, ...structure}` — pluginId first so the structure's fields win
|
|
47
|
+
// on the (never-expected) key collision, matching the JS spread. Drops a row with
|
|
48
|
+
// no decodable structure (former `!item.structure`).
|
|
49
|
+
//
|
|
50
|
+
// `~filter` is what separates the two fields this handler serves: AutoUI
|
|
51
|
+
// (`Platform_ComponentDefinitions`) gets the public-queryable subset, developer
|
|
52
|
+
// tooling (`Platform_PluginStructures`) gets the structure exactly as persisted,
|
|
53
|
+
// including Internal components and the producer-side `extensionPoints` that the
|
|
54
|
+
// AutoUI entry type does not declare.
|
|
55
|
+
let toEntryWith = (~filter: bool, item: dict<JSON.t>, ~name: string): option<JSON.t> => {
|
|
56
|
+
let structure =
|
|
57
|
+
item
|
|
58
|
+
->Dict.get("structure")
|
|
59
|
+
->Option.flatMap(s => filter ? filterStructure(s) : s->JSON.Decode.object)
|
|
60
|
+
switch structure {
|
|
51
61
|
| None => None
|
|
52
62
|
| Some(structureObj) =>
|
|
53
63
|
let entry = Dict.make()
|
|
@@ -55,10 +65,17 @@ let toEntry = (item: dict<JSON.t>, ~name: string): option<JSON.t> =>
|
|
|
55
65
|
structureObj->Dict.toArray->Array.forEach(((k, v)) => entry->Dict.set(k, v))
|
|
56
66
|
Some(JSON.Encode.object(entry))
|
|
57
67
|
}
|
|
68
|
+
}
|
|
69
|
+
|
|
58
70
|
|
|
59
71
|
// The built-in Platform_Admin entry, injected at deploy time as the
|
|
60
72
|
// ADMIN_ENTRY_JSON env var (the admin never Connects to itself, so its structure
|
|
61
73
|
// never enters the Plugin read model).
|
|
74
|
+
//
|
|
75
|
+
// Shared by both fields. The admin structure has no Internal components and no
|
|
76
|
+
// extension points, so its filtered and complete encodings carry the same
|
|
77
|
+
// components; the structure-level fields the complete entry adds are absent from
|
|
78
|
+
// this JSON and resolve to null, which is what `extensionPoints: None` means.
|
|
62
79
|
let adminEntry: option<JSON.t> =
|
|
63
80
|
switch NodeProcess.env->Dict.get("ADMIN_ENTRY_JSON") {
|
|
64
81
|
| Some(s) if s != "" => Some(JSON.parseOrThrow(s))
|
|
@@ -94,7 +111,21 @@ let resolveStructure = (
|
|
|
94
111
|
}
|
|
95
112
|
}
|
|
96
113
|
|
|
97
|
-
|
|
114
|
+
// One Lambda serves both fields — the scan, the offload resolution and the
|
|
115
|
+
// latest-version-per-plugin collapse are identical work, and duplicating them into
|
|
116
|
+
// a second function would mean a second cold start and a second chance for the two
|
|
117
|
+
// to disagree about what "the deployed structure" is. The resolver for
|
|
118
|
+
// `Platform_PluginStructures` invokes with `{complete: true}`.
|
|
119
|
+
let isComplete = (event: JSON.t): bool =>
|
|
120
|
+
event
|
|
121
|
+
->JSON.Decode.object
|
|
122
|
+
->Option.flatMap(o => o->Dict.get("complete"))
|
|
123
|
+
->Option.flatMap(JSON.Decode.bool)
|
|
124
|
+
->Option.getOr(false)
|
|
125
|
+
|
|
126
|
+
let handler = async (event: JSON.t): array<JSON.t> => {
|
|
127
|
+
let complete = isComplete(event)
|
|
128
|
+
let toEntry = (item, ~name) => toEntryWith(~filter=!complete, item, ~name)
|
|
98
129
|
let admin = adminEntry->Option.mapOr([], e => [e])
|
|
99
130
|
switch NodeProcess.env->Dict.get("PLUGIN_RM_TABLE") {
|
|
100
131
|
| None | Some("") =>
|
|
@@ -31,14 +31,20 @@ function filterStructure(structure) {
|
|
|
31
31
|
return out;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function
|
|
35
|
-
let
|
|
36
|
-
|
|
34
|
+
function toEntryWith(filter, item, name) {
|
|
35
|
+
let structure = Stdlib_Option.flatMap(item["structure"], s => {
|
|
36
|
+
if (filter) {
|
|
37
|
+
return filterStructure(s);
|
|
38
|
+
} else {
|
|
39
|
+
return Stdlib_JSON.Decode.object(s);
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
if (structure === undefined) {
|
|
37
43
|
return;
|
|
38
44
|
}
|
|
39
45
|
let entry = {};
|
|
40
46
|
entry["pluginId"] = name;
|
|
41
|
-
Object.entries(
|
|
47
|
+
Object.entries(structure).forEach(param => {
|
|
42
48
|
entry[param[0]] = param[1];
|
|
43
49
|
});
|
|
44
50
|
return entry;
|
|
@@ -65,7 +71,13 @@ function resolveStructure(fetch, item) {
|
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
|
|
68
|
-
|
|
74
|
+
function isComplete(event) {
|
|
75
|
+
return Stdlib_Option.getOr(Stdlib_Option.flatMap(Stdlib_Option.flatMap(Stdlib_JSON.Decode.object(event), o => o["complete"]), Stdlib_JSON.Decode.bool), false);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function handler(event) {
|
|
79
|
+
let complete = isComplete(event);
|
|
80
|
+
let toEntry = (item, name) => toEntryWith(!complete, item, name);
|
|
69
81
|
let admin = Stdlib_Option.mapOr(adminEntry, [], e => [e]);
|
|
70
82
|
let table = process.env["PLUGIN_RM_TABLE"];
|
|
71
83
|
if (table !== undefined) {
|
|
@@ -94,9 +106,10 @@ export {
|
|
|
94
106
|
str,
|
|
95
107
|
isPublicQueryable,
|
|
96
108
|
filterStructure,
|
|
97
|
-
|
|
109
|
+
toEntryWith,
|
|
98
110
|
adminEntry,
|
|
99
111
|
resolveStructure,
|
|
112
|
+
isComplete,
|
|
100
113
|
handler,
|
|
101
114
|
}
|
|
102
115
|
/* s Not a pure module */
|