@reventlessdev/reventless-local 3.0.0-alpha.250 → 3.0.0-alpha.251
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 +22 -0
- package/package.json +10 -10
- package/src/adapter/Auth/LocalAuth.res +1 -1
- package/src/adapter/Auth/LocalAuth.res.mjs +2 -1
- package/src/adapter/Auth/UserStore.res +8 -33
- package/src/adapter/Auth/UserStore.res.mjs +11 -67
- package/src/adapter/LocalCapabilities.res +6 -0
- package/src/adapter/LocalCapabilities.res.mjs +4 -1
- package/src/adapter/PlatformGraphQL_Server.res +1 -1
- package/src/adapter/PlatformGraphQL_Server.res.mjs +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,28 @@
|
|
|
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.251 (2026-09-11)
|
|
7
|
+
|
|
8
|
+
* feat(spec)!: a secret is a capability, so a test can know it in advance ([71507c6](https://github.com/ReventlessDev/reventless-core/commit/71507c6459b49178746f00c5040cb479d7fdade8))
|
|
9
|
+
* feat(spec)!: making a principal is a capability, so the provider is replaceable ([3e59b9b](https://github.com/ReventlessDev/reventless-core/commit/3e59b9b665cc7e4b6b6f156b63b1b6bd0c6181ca))
|
|
10
|
+
### Features
|
|
11
|
+
|
|
12
|
+
* **aws:** the first administrator is one command, not the console ([5f1a846](https://github.com/ReventlessDev/reventless-core/commit/5f1a84603b21a9feb691a08a148751c6c6e8eca8))
|
|
13
|
+
* **aws:** the rest of the cast is a file, not a comment ([aa23581](https://github.com/ReventlessDev/reventless-core/commit/aa23581f09948a47fb451ec2ecf2fc2bdd8e6ee6))
|
|
14
|
+
|
|
15
|
+
### BREAKING CHANGES
|
|
16
|
+
|
|
17
|
+
* Capabilities.t gains a secrets member. A platform that builds
|
|
18
|
+
the record literally no longer compiles until it supplies one;
|
|
19
|
+
Capabilities.none.secrets is the refusing value. Code that spreads
|
|
20
|
+
Capabilities.none is unaffected.
|
|
21
|
+
* Capabilities.t gains an identityProvider member. A platform
|
|
22
|
+
that builds the record literally no longer compiles until it supplies one;
|
|
23
|
+
Capabilities.none.identityProvider is the refusing value to pass while no
|
|
24
|
+
backend exists. Code that spreads Capabilities.none is unaffected.
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
6
28
|
# 3.0.0-alpha.250 (2026-09-09)
|
|
7
29
|
|
|
8
30
|
**Note:** Version bump only for package @reventlessdev/reventless-local
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reventlessdev/reventless-local",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.251",
|
|
4
4
|
"description": "Local platform for Reventless (in-memory or SQLite backend, for development and testing without AWS)",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"bin": {
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
"ws": "^8.18.0",
|
|
37
37
|
"@reventlessdev/rescript-effect": "0.1.0-alpha.33",
|
|
38
38
|
"@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.28",
|
|
39
|
-
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
40
|
-
"@reventlessdev/rescript-node": "2.0.0-alpha.11",
|
|
41
39
|
"@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.22",
|
|
40
|
+
"@reventlessdev/rescript-node": "2.0.0-alpha.11",
|
|
41
|
+
"@reventlessdev/rescript-jest": "1.0.0-alpha.10",
|
|
42
42
|
"@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.20",
|
|
43
|
-
"@reventlessdev/reventless-core": "3.0.0-alpha.
|
|
44
|
-
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.
|
|
45
|
-
"@reventlessdev/reventless-
|
|
46
|
-
"@reventlessdev/reventless-
|
|
47
|
-
"@reventlessdev/reventless-postgres": "3.0.0-alpha.
|
|
48
|
-
"@reventlessdev/reventless-
|
|
49
|
-
"@reventlessdev/reventless-
|
|
43
|
+
"@reventlessdev/reventless-core": "3.0.0-alpha.262",
|
|
44
|
+
"@reventlessdev/reventless-graphql-server": "1.0.0-alpha.110",
|
|
45
|
+
"@reventlessdev/reventless-gwt": "1.0.0-alpha.209",
|
|
46
|
+
"@reventlessdev/reventless-infra": "3.0.0-alpha.163",
|
|
47
|
+
"@reventlessdev/reventless-postgres": "3.0.0-alpha.126",
|
|
48
|
+
"@reventlessdev/reventless-spec": "3.0.0-alpha.135",
|
|
49
|
+
"@reventlessdev/reventless-seed": "1.0.0-alpha.21"
|
|
50
50
|
},
|
|
51
51
|
"devDependencies": {
|
|
52
52
|
"rescript": "12.3.0",
|
|
@@ -8,6 +8,7 @@ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
|
8
8
|
import * as Pulumi from "@pulumi/pulumi";
|
|
9
9
|
import * as Identity$Reventless from "@reventlessdev/reventless-spec/src/types/Identity.res.mjs";
|
|
10
10
|
import * as Util_Sury$Reventless from "@reventlessdev/reventless-spec/src/util/Util_Sury.res.mjs";
|
|
11
|
+
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
11
12
|
import * as Auth_ActiveRole$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_ActiveRole.res.mjs";
|
|
12
13
|
|
|
13
14
|
let defaultUser_groups = ["User"];
|
|
@@ -20,7 +21,7 @@ let defaultUser = {
|
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
let adminUser_groups = [
|
|
23
|
-
|
|
24
|
+
AdminGroup$Reventless.name,
|
|
24
25
|
"User"
|
|
25
26
|
];
|
|
26
27
|
|
|
@@ -14,40 +14,15 @@ open Reventless
|
|
|
14
14
|
/**
|
|
15
15
|
* One YAML entry. `groups` is required (use `[]` for an unprivileged user);
|
|
16
16
|
* `userId` defaults to the username when omitted.
|
|
17
|
+
*
|
|
18
|
+
* The shape belongs to [AccountsManifest] rather than to this adapter: the AWS
|
|
19
|
+
* provisioning tool writes the same file, and two definitions of one file format
|
|
20
|
+
* is how a manifest starts working on one platform and failing on the other.
|
|
17
21
|
*/
|
|
18
|
-
|
|
19
|
-
type entry = {
|
|
20
|
-
username: string,
|
|
21
|
-
password: string,
|
|
22
|
-
groups: array<string>,
|
|
23
|
-
userId?: string,
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// ── YAML parsing (eemeli/yaml) ──────────────────────────────────────────────
|
|
27
|
-
|
|
28
|
-
@module("yaml") external _yamlParse: string => unknown = "parse"
|
|
29
|
-
|
|
30
|
-
let _entriesSchema = S.array(entrySchema)
|
|
22
|
+
type entry = AccountsManifest.entry
|
|
31
23
|
|
|
32
|
-
|
|
33
|
-
let
|
|
34
|
-
try {
|
|
35
|
-
let json = _yamlParse(yamlText)->Obj.magic
|
|
36
|
-
Ok(S.parseOrThrow(json, ~to=_entriesSchema))
|
|
37
|
-
} catch {
|
|
38
|
-
| JsExn(err) => Error(JsExn.message(err)->Option.getOr("YAML parse error"))
|
|
39
|
-
| _ => Error("YAML parse error")
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** Reads + parses a YAML file. */
|
|
43
|
-
let parseFile = (path: string): result<array<entry>, string> =>
|
|
44
|
-
try {
|
|
45
|
-
let text = NodeFs.readFileSync(path)
|
|
46
|
-
parseString(text)
|
|
47
|
-
} catch {
|
|
48
|
-
| JsExn(err) => Error(JsExn.message(err)->Option.getOr(`Cannot read ${path}`))
|
|
49
|
-
| _ => Error(`Cannot read ${path}`)
|
|
50
|
-
}
|
|
24
|
+
let parseString = AccountsManifest.parseString
|
|
25
|
+
let parseFile = AccountsManifest.parseFile
|
|
51
26
|
|
|
52
27
|
// ── Resolution & loading ────────────────────────────────────────────────────
|
|
53
28
|
|
|
@@ -62,7 +37,7 @@ let _registerEntries = (entries: array<entry>): unit =>
|
|
|
62
37
|
LocalAuth.Login.setCredentials(~username, ~password, ~identity=id)
|
|
63
38
|
})
|
|
64
39
|
|
|
65
|
-
let _defaultPath =
|
|
40
|
+
let _defaultPath = AccountsManifest.defaultPath
|
|
66
41
|
|
|
67
42
|
/**
|
|
68
43
|
* Hydrates the Login store. Returns the resolution path actually used
|
|
@@ -1,66 +1,10 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
-
import * as S from "sury/src/S.res.mjs";
|
|
4
|
-
import * as Sury from "sury";
|
|
5
|
-
import * as Yaml from "yaml";
|
|
6
3
|
import * as Nodefs from "node:fs";
|
|
7
|
-
import * as Nodepath from "node:path";
|
|
8
|
-
import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
|
|
9
4
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
10
5
|
import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
|
|
11
|
-
import * as Primitive_exceptions from "@rescript/runtime/lib/es6/Primitive_exceptions.js";
|
|
12
6
|
import * as LocalAuth$ReventlessLocal from "./LocalAuth.res.mjs";
|
|
13
|
-
|
|
14
|
-
let entrySchema = Sury.$schema(s => ({
|
|
15
|
-
username: s.m(Sury.string),
|
|
16
|
-
password: s.m(Sury.string),
|
|
17
|
-
groups: s.m(Sury.array(Sury.string)),
|
|
18
|
-
userId: s.m(Sury.$option(Sury.string))
|
|
19
|
-
}));
|
|
20
|
-
|
|
21
|
-
let _entriesSchema = Sury.array(entrySchema);
|
|
22
|
-
|
|
23
|
-
function parseString(yamlText) {
|
|
24
|
-
try {
|
|
25
|
-
let json = Yaml.parse(yamlText);
|
|
26
|
-
return {
|
|
27
|
-
TAG: "Ok",
|
|
28
|
-
_0: S.parseOrThrow(json, _entriesSchema)
|
|
29
|
-
};
|
|
30
|
-
} catch (raw_err) {
|
|
31
|
-
let err = Primitive_exceptions.internalToException(raw_err);
|
|
32
|
-
if (err.RE_EXN_ID === "JsExn") {
|
|
33
|
-
return {
|
|
34
|
-
TAG: "Error",
|
|
35
|
-
_0: Stdlib_Option.getOr(Stdlib_JsExn.message(err._1), "YAML parse error")
|
|
36
|
-
};
|
|
37
|
-
} else {
|
|
38
|
-
return {
|
|
39
|
-
TAG: "Error",
|
|
40
|
-
_0: "YAML parse error"
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function parseFile(path) {
|
|
47
|
-
try {
|
|
48
|
-
return parseString(Nodefs.readFileSync(path, "utf8"));
|
|
49
|
-
} catch (raw_err) {
|
|
50
|
-
let err = Primitive_exceptions.internalToException(raw_err);
|
|
51
|
-
if (err.RE_EXN_ID === "JsExn") {
|
|
52
|
-
return {
|
|
53
|
-
TAG: "Error",
|
|
54
|
-
_0: Stdlib_Option.getOr(Stdlib_JsExn.message(err._1), `Cannot read ` + path)
|
|
55
|
-
};
|
|
56
|
-
} else {
|
|
57
|
-
return {
|
|
58
|
-
TAG: "Error",
|
|
59
|
-
_0: `Cannot read ` + path
|
|
60
|
-
};
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
7
|
+
import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
|
|
64
8
|
|
|
65
9
|
function _registerEntries(entries) {
|
|
66
10
|
entries.forEach(param => {
|
|
@@ -77,10 +21,6 @@ function _registerEntries(entries) {
|
|
|
77
21
|
});
|
|
78
22
|
}
|
|
79
23
|
|
|
80
|
-
function _defaultPath() {
|
|
81
|
-
return Nodepath.join(process.cwd(), ".reventless/users.yaml");
|
|
82
|
-
}
|
|
83
|
-
|
|
84
24
|
let resolved = {
|
|
85
25
|
contents: false
|
|
86
26
|
};
|
|
@@ -99,7 +39,7 @@ function load(users, usersFile, param) {
|
|
|
99
39
|
};
|
|
100
40
|
}
|
|
101
41
|
if (usersFile !== undefined) {
|
|
102
|
-
return Stdlib_Result.map(parseFile(usersFile), entries => {
|
|
42
|
+
return Stdlib_Result.map(AccountsManifest$Reventless.parseFile(usersFile), entries => {
|
|
103
43
|
_registerEntries(entries);
|
|
104
44
|
return {
|
|
105
45
|
TAG: "UsersFile",
|
|
@@ -107,9 +47,9 @@ function load(users, usersFile, param) {
|
|
|
107
47
|
};
|
|
108
48
|
});
|
|
109
49
|
}
|
|
110
|
-
let defaultPath =
|
|
50
|
+
let defaultPath = AccountsManifest$Reventless.defaultPath();
|
|
111
51
|
if (Nodefs.existsSync(defaultPath)) {
|
|
112
|
-
return Stdlib_Result.map(parseFile(defaultPath), entries => {
|
|
52
|
+
return Stdlib_Result.map(AccountsManifest$Reventless.parseFile(defaultPath), entries => {
|
|
113
53
|
_registerEntries(entries);
|
|
114
54
|
return {
|
|
115
55
|
TAG: "DefaultFile",
|
|
@@ -132,9 +72,13 @@ function autoLoadOnce() {
|
|
|
132
72
|
}
|
|
133
73
|
}
|
|
134
74
|
|
|
75
|
+
let parseString = AccountsManifest$Reventless.parseString;
|
|
76
|
+
|
|
77
|
+
let parseFile = AccountsManifest$Reventless.parseFile;
|
|
78
|
+
|
|
79
|
+
let _defaultPath = AccountsManifest$Reventless.defaultPath;
|
|
80
|
+
|
|
135
81
|
export {
|
|
136
|
-
entrySchema,
|
|
137
|
-
_entriesSchema,
|
|
138
82
|
parseString,
|
|
139
83
|
parseFile,
|
|
140
84
|
_registerEntries,
|
|
@@ -144,4 +88,4 @@ export {
|
|
|
144
88
|
load,
|
|
145
89
|
autoLoadOnce,
|
|
146
90
|
}
|
|
147
|
-
/*
|
|
91
|
+
/* node:fs Not a pure module */
|
|
@@ -27,4 +27,10 @@ let registerMessagingSender = (sender: string) => messagingSenderRef := sender
|
|
|
27
27
|
let capabilities = (): Reventless.Capabilities.t => {
|
|
28
28
|
geocode: Reventless.Capabilities.none.geocode,
|
|
29
29
|
messaging: ReventlessCore.Messaging_Log_Backend.provider(~sender=messagingSenderRef.contents),
|
|
30
|
+
// Refusing rather than absent: the seam exists so a deployment can be asked
|
|
31
|
+
// what it provisions. Nothing writes back to the local user store yet, and
|
|
32
|
+
// answering `Unavailable` leaves the work queued and visible instead of
|
|
33
|
+
// recording a verdict against a person who has done nothing wrong.
|
|
34
|
+
identityProvider: Reventless.Capabilities.none.identityProvider,
|
|
35
|
+
secrets: ReventlessCore.Secrets_Node_Backend.provider,
|
|
30
36
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
|
|
4
|
+
import * as Secrets_Node_Backend$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Secrets/Secrets_Node_Backend.res.mjs";
|
|
4
5
|
import * as Messaging_Log_Backend$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Messaging/Messaging_Log_Backend.res.mjs";
|
|
5
6
|
|
|
6
7
|
let messagingSenderRef = {
|
|
@@ -14,7 +15,9 @@ function registerMessagingSender(sender) {
|
|
|
14
15
|
function capabilities() {
|
|
15
16
|
return {
|
|
16
17
|
geocode: Capabilities$Reventless.none.geocode,
|
|
17
|
-
messaging: Messaging_Log_Backend$ReventlessCore.provider(messagingSenderRef.contents)
|
|
18
|
+
messaging: Messaging_Log_Backend$ReventlessCore.provider(messagingSenderRef.contents),
|
|
19
|
+
identityProvider: Capabilities$Reventless.none.identityProvider,
|
|
20
|
+
secrets: Secrets_Node_Backend$ReventlessCore.provider
|
|
18
21
|
};
|
|
19
22
|
}
|
|
20
23
|
|
|
@@ -41,7 +41,7 @@ let wrapAdmin = (resolvers: dict<ReventlessGraphqlServer.GraphQL_ServerInstance.
|
|
|
41
41
|
resolvers
|
|
42
42
|
->Dict.toArray
|
|
43
43
|
->Array.forEach(((k, v)) =>
|
|
44
|
-
wrapped->Dict.set(k, Auth_GraphqlContext.requireGroup(~group=
|
|
44
|
+
wrapped->Dict.set(k, Auth_GraphqlContext.requireGroup(~group=Reventless.AdminGroup.name, v))
|
|
45
45
|
)
|
|
46
46
|
wrapped
|
|
47
47
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
|
+
import * as AdminGroup$Reventless from "@reventlessdev/reventless-spec/src/types/AdminGroup.res.mjs";
|
|
3
4
|
import * as Auth_GraphqlContext$ReventlessLocal from "./Auth/Auth_GraphqlContext.res.mjs";
|
|
4
5
|
import * as GraphQL_ServerInstance$ReventlessGraphqlServer from "@reventlessdev/reventless-graphql-server/src/GraphQL_ServerInstance.res.mjs";
|
|
5
6
|
|
|
@@ -8,7 +9,7 @@ let instance = GraphQL_ServerInstance$ReventlessGraphqlServer.make("GraphQL:Plat
|
|
|
8
9
|
function wrapAdmin(resolvers) {
|
|
9
10
|
let wrapped = {};
|
|
10
11
|
Object.entries(resolvers).forEach(param => {
|
|
11
|
-
wrapped[param[0]] = Auth_GraphqlContext$ReventlessLocal.requireGroup(
|
|
12
|
+
wrapped[param[0]] = Auth_GraphqlContext$ReventlessLocal.requireGroup(AdminGroup$Reventless.name, param[1]);
|
|
12
13
|
});
|
|
13
14
|
return wrapped;
|
|
14
15
|
}
|