@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 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.250",
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.261",
44
- "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.109",
45
- "@reventlessdev/reventless-infra": "3.0.0-alpha.162",
46
- "@reventlessdev/reventless-seed": "1.0.0-alpha.21",
47
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.125",
48
- "@reventlessdev/reventless-gwt": "1.0.0-alpha.208",
49
- "@reventlessdev/reventless-spec": "3.0.0-alpha.134"
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",
@@ -24,7 +24,7 @@ let defaultUser: Identity.t = {
24
24
  let adminUser: Identity.t = {
25
25
  userId: "local-admin",
26
26
  username: "admin",
27
- groups: ["Admin", "User"],
27
+ groups: [AdminGroup.name, "User"],
28
28
  provider: InMemory,
29
29
  }
30
30
 
@@ -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
- "Admin",
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
- @schema
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
- /** Parses a YAML document string into entries (best-effort). */
33
- let parseString = (yamlText: string): result<array<entry>, string> =>
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 = (): string => NodePath.join([NodeProcess.cwd(), ".reventless/users.yaml"])
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 = _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
- /* entrySchema Not a pure module */
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="Admin", v))
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("Admin", param[1]);
12
+ wrapped[param[0]] = Auth_GraphqlContext$ReventlessLocal.requireGroup(AdminGroup$Reventless.name, param[1]);
12
13
  });
13
14
  return wrapped;
14
15
  }