@reventlessdev/reventless-infra 3.0.0-alpha.152 → 3.0.0-alpha.154
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 +21 -0
- package/package.json +3 -3
- package/src/components/Api.res +64 -25
- package/src/components/Api.res.mjs +37 -15
- package/src/types/ExtensionMapping.res +3 -0
- package/src/types/ExtensionMapping.res.mjs +9 -1
- package/src/types/Platform.res +21 -0
- package/src/types/PluginExtensionPointSpec.res.mjs +12 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,27 @@
|
|
|
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.154 (2026-09-01)
|
|
7
|
+
|
|
8
|
+
**Note:** Version bump only for package @reventlessdev/reventless-infra
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# 3.0.0-alpha.153 (2026-09-01)
|
|
15
|
+
|
|
16
|
+
### Bug Fixes
|
|
17
|
+
|
|
18
|
+
* **api:** [@no](https://github.com/no)Api was lost when a command variant was spliced into another type ([00403fb](https://github.com/ReventlessDev/reventless-core/commit/00403fb4b8873a68a69558829e598339a5012833))
|
|
19
|
+
* **capabilities:** a plugin's geocoding need was declared nowhere and failed silently ([67917dd](https://github.com/ReventlessDev/reventless-core/commit/67917dd504b43fa78b7c6a51644c9eae656b7f6b))
|
|
20
|
+
### Features
|
|
21
|
+
|
|
22
|
+
* **capabilities:** a plugin can send a message without naming a provider ([b001a1e](https://github.com/ReventlessDev/reventless-core/commit/b001a1e9361a0f4d0affe228cb4c08c38a5a995e))
|
|
23
|
+
* **spec:** a command declares its lifecycle edge as a value ([40eee9f](https://github.com/ReventlessDev/reventless-core/commit/40eee9f7723dc05e418be680528f01967d074da4))
|
|
24
|
+
* **spec:** a graft leaves a trace the deployed plugin can read ([c08ff6c](https://github.com/ReventlessDev/reventless-core/commit/c08ff6c0f6177d58603e7ae1e5cec392d9bac16a))
|
|
25
|
+
|
|
26
|
+
|
|
6
27
|
# 3.0.0-alpha.152 (2026-08-27)
|
|
7
28
|
|
|
8
29
|
* feat(spec)!: reflect the command direction across a port, both halves ([f2fe258](https://github.com/ReventlessDev/reventless-core/commit/f2fe258d195b74f4a61488edee305665341020ea))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-infra",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.154",
|
|
4
4
|
"description": "Infrastructure types for Reventless framework",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"jest": {
|
|
@@ -17,9 +17,9 @@
|
|
|
17
17
|
"sury-ppx": "11.0.0-rc.2",
|
|
18
18
|
"uuid": "^13.0.0",
|
|
19
19
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.32",
|
|
20
|
-
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
21
20
|
"@reventlessdev/rescript-uuid": "2.0.0-alpha.0",
|
|
22
|
-
"@reventlessdev/
|
|
21
|
+
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.19",
|
|
22
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.126"
|
|
23
23
|
},
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"rescript": "12.3.0",
|
package/src/components/Api.res
CHANGED
|
@@ -7,37 +7,76 @@ let noApiId: S.Metadata.Id.t<bool> = S.Metadata.Id.make(~namespace="api", ~name=
|
|
|
7
7
|
let noApiVariantsId: S.Metadata.Id.t<Set.t<string>> =
|
|
8
8
|
S.Metadata.Id.make(~namespace="api", ~name="noApiVariants")
|
|
9
9
|
|
|
10
|
-
/**
|
|
11
|
-
|
|
12
|
-
let allowedStatesId: S.Metadata.Id.t<dict<array<string>>> =
|
|
13
|
-
S.Metadata.Id.make(~namespace="api", ~name="allowedStates")
|
|
10
|
+
/** The same exclusion, carried by the excluded *member* rather than by the union
|
|
11
|
+
it sits in — so it survives a variant spread.
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
13
|
+
A spread splices members: the generated code concatenates the source union's
|
|
14
|
+
`anyOf` into the target's. Metadata on the source *union* is not part of that,
|
|
15
|
+
so an exclusion recorded only there vanishes on the way into the host, and the
|
|
16
|
+
host publishes a command its author marked internal. A mark on the member
|
|
17
|
+
travels, because the member is what is spliced — the host's union holds the
|
|
18
|
+
same schema object. */
|
|
19
|
+
let noApiVariantId: S.Metadata.Id.t<bool> =
|
|
20
|
+
S.Metadata.Id.make(~namespace="api", ~name="noApiVariant")
|
|
19
21
|
|
|
20
22
|
/** PPX helper: attaches the noApi flag to a command schema. Called by generated code. */
|
|
21
23
|
let markNoApi = (schema: S.t<'a>): S.t<'a> =>
|
|
22
24
|
schema->S.Metadata.set(~id=noApiId, true)
|
|
23
25
|
|
|
24
|
-
/**
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
26
|
+
/** The constructor a union member stands for: the `TAG` literal of a
|
|
27
|
+
payload-bearing variant, or the bare literal a payload-less one compiles to. */
|
|
28
|
+
let variantNameOf = (member: S.t<unknown>): option<string> =>
|
|
29
|
+
switch member {
|
|
30
|
+
| Object({properties}) =>
|
|
31
|
+
properties
|
|
32
|
+
->Dict.get("TAG")
|
|
33
|
+
->Option.flatMap(tag =>
|
|
34
|
+
switch tag {
|
|
35
|
+
| String({const: ?Some(name)}) => Some(name)
|
|
36
|
+
| _ => None
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
| String({const: ?Some(name)}) => Some(name)
|
|
40
|
+
| _ => None
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
PPX helper: attaches variant-level exclusions to a command schema. Called by
|
|
45
|
+
generated code.
|
|
46
|
+
|
|
47
|
+
Recorded twice, on purpose. The set on the union is what every reader has always
|
|
48
|
+
consulted; the per-member mark is what survives being spliced into another type.
|
|
49
|
+
Writing only the second would break any reader holding a union whose members it
|
|
50
|
+
did not build, and writing only the first is the defect this exists to close.
|
|
51
|
+
|
|
52
|
+
The union is rebuilt from the marked members because sury metadata is immutable —
|
|
53
|
+
setting it returns a new schema, so the members the original union holds would
|
|
54
|
+
otherwise still be the unmarked ones. Sound here because the PPX calls this
|
|
55
|
+
*first*, directly on the freshly built union, before any other metadata is
|
|
56
|
+
attached: there is nothing on the parent yet to lose. A schema that is not a
|
|
57
|
+
union, or that names no excluded variant, is left structurally untouched.
|
|
58
|
+
*/
|
|
59
|
+
let markNoApiVariants = (schema: S.t<'a>, variants: array<string>): S.t<'a> => {
|
|
60
|
+
let excluded = Set.fromArray(variants)
|
|
61
|
+
let marked = switch (variants, schema->S.castToUnknown) {
|
|
62
|
+
| ([], _) => schema
|
|
63
|
+
| (_, AnyOf({anyOf})) =>
|
|
64
|
+
anyOf
|
|
65
|
+
->Array.map(member =>
|
|
66
|
+
switch variantNameOf(member) {
|
|
67
|
+
| Some(name) if excluded->Set.has(name) => member->S.Metadata.set(~id=noApiVariantId, true)
|
|
68
|
+
| _ => member
|
|
69
|
+
}
|
|
70
|
+
)
|
|
71
|
+
->S.union
|
|
72
|
+
// The rebuilt union describes the same values as the one handed in — same
|
|
73
|
+
// members, same order, only metadata added. `S.union` cannot express that in
|
|
74
|
+
// the type, so the equivalence is carried here rather than by the compiler.
|
|
75
|
+
->(Obj.magic: S.t<unknown> => S.t<'a>)
|
|
76
|
+
| (_, _) => schema
|
|
77
|
+
}
|
|
78
|
+
marked->S.Metadata.set(~id=noApiVariantsId, excluded)
|
|
79
|
+
}
|
|
41
80
|
|
|
42
81
|
/** Type alias for schema fragments (re-exports Plugin.apiSchemaFragment). */
|
|
43
82
|
type schemaFragment = Reventless.Plugin.apiSchemaFragment
|
|
@@ -1,39 +1,61 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Sury from "sury";
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
4
5
|
|
|
5
6
|
let noApiId = Sury.$Metadata_Id_make("api", "noApi");
|
|
6
7
|
|
|
7
8
|
let noApiVariantsId = Sury.$Metadata_Id_make("api", "noApiVariants");
|
|
8
9
|
|
|
9
|
-
let
|
|
10
|
-
|
|
11
|
-
let targetStateId = Sury.$Metadata_Id_make("api", "targetState");
|
|
10
|
+
let noApiVariantId = Sury.$Metadata_Id_make("api", "noApiVariant");
|
|
12
11
|
|
|
13
12
|
function markNoApi(schema) {
|
|
14
13
|
return Sury.$Metadata_set(schema, noApiId, true);
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
function
|
|
18
|
-
|
|
16
|
+
function variantNameOf(member) {
|
|
17
|
+
switch (member.type) {
|
|
18
|
+
case "string" :
|
|
19
|
+
let name = member.const;
|
|
20
|
+
if (name !== undefined) {
|
|
21
|
+
return name;
|
|
22
|
+
} else {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
case "object" :
|
|
26
|
+
return Stdlib_Option.flatMap(member.properties["TAG"], tag => {
|
|
27
|
+
if (tag.type !== "string") {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
let name = tag.const;
|
|
31
|
+
if (name !== undefined) {
|
|
32
|
+
return name;
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
default:
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
19
38
|
}
|
|
20
39
|
|
|
21
|
-
function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
40
|
+
function markNoApiVariants(schema, variants) {
|
|
41
|
+
let excluded = new Set(variants);
|
|
42
|
+
let marked = variants.length !== 0 && schema.type === "anyOf" ? Sury.union(schema.anyOf.map(member => {
|
|
43
|
+
let name = variantNameOf(member);
|
|
44
|
+
if (name !== undefined && excluded.has(name)) {
|
|
45
|
+
return Sury.$Metadata_set(member, noApiVariantId, true);
|
|
46
|
+
} else {
|
|
47
|
+
return member;
|
|
48
|
+
}
|
|
49
|
+
})) : schema;
|
|
50
|
+
return Sury.$Metadata_set(marked, noApiVariantsId, excluded);
|
|
27
51
|
}
|
|
28
52
|
|
|
29
53
|
export {
|
|
30
54
|
noApiId,
|
|
31
55
|
noApiVariantsId,
|
|
32
|
-
|
|
33
|
-
targetStateId,
|
|
56
|
+
noApiVariantId,
|
|
34
57
|
markNoApi,
|
|
58
|
+
variantNameOf,
|
|
35
59
|
markNoApiVariants,
|
|
36
|
-
markAllowedStates,
|
|
37
|
-
markTargetState,
|
|
38
60
|
}
|
|
39
61
|
/* noApiId Not a pure module */
|
|
@@ -147,6 +147,9 @@ module NoDelegate = {
|
|
|
147
147
|
let moduleUrl: string = %raw(`import.meta.url`)
|
|
148
148
|
let commandAuthorization = (_: command): Reventless.Authorization.permission =>
|
|
149
149
|
AllowAuthenticated
|
|
150
|
+
type lifecycleState = unit
|
|
151
|
+
let commandTransition = (_: command): Reventless.Transition.t<lifecycleState> => Unrestricted
|
|
152
|
+
let traits: array<Reventless.Trait.t> = []
|
|
150
153
|
}
|
|
151
154
|
|
|
152
155
|
|
|
@@ -32,6 +32,12 @@ function commandAuthorization() {
|
|
|
32
32
|
return "AllowAuthenticated";
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
function commandTransition() {
|
|
36
|
+
return "Unrestricted";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let traits = [];
|
|
40
|
+
|
|
35
41
|
let NoDelegate = {
|
|
36
42
|
name: "NoDelegate",
|
|
37
43
|
Id: Id,
|
|
@@ -39,7 +45,9 @@ let NoDelegate = {
|
|
|
39
45
|
errorSchema: errorSchema,
|
|
40
46
|
commandSchema: commandSchema,
|
|
41
47
|
moduleUrl: moduleUrl,
|
|
42
|
-
commandAuthorization: commandAuthorization
|
|
48
|
+
commandAuthorization: commandAuthorization,
|
|
49
|
+
commandTransition: commandTransition,
|
|
50
|
+
traits: traits
|
|
43
51
|
};
|
|
44
52
|
|
|
45
53
|
function Make(MappingImpl) {
|
package/src/types/Platform.res
CHANGED
|
@@ -109,6 +109,7 @@ store, and reaching another plugin's store is the qualified form.
|
|
|
109
109
|
type capability =
|
|
110
110
|
| ObjectStore({plugin: string, store: string})
|
|
111
111
|
| Geocoding
|
|
112
|
+
| Messaging
|
|
112
113
|
|
|
113
114
|
// A provisioned geocoding place index, as returned by the framework's geocoding
|
|
114
115
|
// capability helper. A record rather than a bare name so the handle can grow
|
|
@@ -181,6 +182,20 @@ type bakedManifest = {
|
|
|
181
182
|
|
|
182
183
|
type geocoderIndex = {indexName: Pulumi.Input.t<string>}
|
|
183
184
|
|
|
185
|
+
/**
|
|
186
|
+
A provisioned messaging sender, as returned by the framework's messaging
|
|
187
|
+
capability helper.
|
|
188
|
+
|
|
189
|
+
`emailSender` is the verified address messages are sent *from* — a provider-side
|
|
190
|
+
identity, not something an application picks per message, which is why it is a
|
|
191
|
+
deploy-time handle rather than a field on `Messaging.message`. Absent when the
|
|
192
|
+
deployment provisions no email channel.
|
|
193
|
+
|
|
194
|
+
The channel set is derived from which senders are present rather than declared
|
|
195
|
+
beside them, so a handle carrying no sender at all cannot claim a channel.
|
|
196
|
+
*/
|
|
197
|
+
type messagingSender = {emailSender?: Pulumi.Input.t<string>}
|
|
198
|
+
|
|
184
199
|
// Options for the shell's map view mode. Written to config.json flat, beside
|
|
185
200
|
// `viewModes`, because that is where the shell reads them; carried on the arm
|
|
186
201
|
// here so a style set with the map off is unrepresentable.
|
|
@@ -426,6 +441,12 @@ module type T = {
|
|
|
426
441
|
// on the platform API — and exports the index name for the unattended slice
|
|
427
442
|
// path. Unset ⇒ no resolver, no export. In-memory platforms ignore this.
|
|
428
443
|
geocoderPlaceIndex?: geocoderIndex,
|
|
444
|
+
// Optional messaging sender. When set, the deploy exports the verified
|
|
445
|
+
// sender address for the unattended slice path and grants those Lambdas the
|
|
446
|
+
// right to send from it. No client door — a browser has no business sending
|
|
447
|
+
// from the deployment's identity. Unset ⇒ no export, no grant. In-memory
|
|
448
|
+
// platforms ignore this.
|
|
449
|
+
messagingSender?: messagingSender,
|
|
429
450
|
// Optional object store for direct-to-store uploads. When set, the deploy
|
|
430
451
|
// provisions a presign service against it, threads that service's URL into
|
|
431
452
|
// config.json as `uploadEndpoint`, and serves the store read-only from the
|
|
@@ -175,6 +175,14 @@ let requiredStoreDeclarationSchema = Plugin$Reventless.requiredStoreDeclarationS
|
|
|
175
175
|
|
|
176
176
|
let requiredStoreDeclarationArrayOptionSchema = Plugin$Reventless.requiredStoreDeclarationArrayOptionSchema;
|
|
177
177
|
|
|
178
|
+
let requiredCapabilityDeclarationSchema = Plugin$Reventless.requiredCapabilityDeclarationSchema;
|
|
179
|
+
|
|
180
|
+
let requiredCapabilityDeclarationArrayOptionSchema = Plugin$Reventless.requiredCapabilityDeclarationArrayOptionSchema;
|
|
181
|
+
|
|
182
|
+
let traitDeclarationSchema = Plugin$Reventless.traitDeclarationSchema;
|
|
183
|
+
|
|
184
|
+
let traitDeclarationArrayOptionSchema = Plugin$Reventless.traitDeclarationArrayOptionSchema;
|
|
185
|
+
|
|
178
186
|
let pluginStructureSchema = Plugin$Reventless.pluginStructureSchema;
|
|
179
187
|
|
|
180
188
|
let pluginStructureOffloadSchema = Plugin$Reventless.pluginStructureOffloadSchema;
|
|
@@ -225,6 +233,10 @@ export {
|
|
|
225
233
|
extensionPointDefArrayOptionSchema,
|
|
226
234
|
requiredStoreDeclarationSchema,
|
|
227
235
|
requiredStoreDeclarationArrayOptionSchema,
|
|
236
|
+
requiredCapabilityDeclarationSchema,
|
|
237
|
+
requiredCapabilityDeclarationArrayOptionSchema,
|
|
238
|
+
traitDeclarationSchema,
|
|
239
|
+
traitDeclarationArrayOptionSchema,
|
|
228
240
|
pluginStructureSchema,
|
|
229
241
|
pluginStructureOffloadSchema,
|
|
230
242
|
pluginDefinitionSchema,
|