@reventlessdev/reventless-local 3.0.0-alpha.296 → 3.0.0-alpha.298

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,23 @@
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.298 (2026-10-23)
7
+
8
+ ### Features
9
+
10
+ * **aws:** a new account gets a domain user id, carried as a token claim from a pool-scoped identity store ([ccfc90c](https://github.com/ReventlessDev/reventless-core/commit/ccfc90ced0c211cab23843864a2b4e580001b3ae))
11
+
12
+
13
+ # 3.0.0-alpha.297 (2026-10-22)
14
+
15
+ ### Bug Fixes
16
+
17
+ * **local:** a plugin declaring a capability the local platform does not provision is told so ([0f4f504](https://github.com/ReventlessDev/reventless-core/commit/0f4f504e310fb001a760d738767590e91c8c7195))
18
+ ### Features
19
+
20
+ * **local:** the local platform provisions an identity provider over the accounts file ([1a46867](https://github.com/ReventlessDev/reventless-core/commit/1a46867ac391b3b4a2d6973606d782547190dcb8))
21
+
22
+
6
23
  # 3.0.0-alpha.296 (2026-10-22)
7
24
 
8
25
  ### Features
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-local",
3
- "version": "3.0.0-alpha.296",
3
+ "version": "3.0.0-alpha.298",
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": {
@@ -40,13 +40,13 @@
40
40
  "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.23",
41
41
  "@reventlessdev/rescript-node": "2.0.0-alpha.23",
42
42
  "@reventlessdev/rescript-pulumi-pulumi": "3.0.0-alpha.0",
43
- "@reventlessdev/reventless-core": "3.0.0-alpha.305",
44
- "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.153",
45
- "@reventlessdev/reventless-gwt": "1.0.0-alpha.253",
46
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.169",
47
- "@reventlessdev/reventless-infra": "3.0.0-alpha.202",
48
- "@reventlessdev/reventless-seed": "1.0.0-alpha.36",
49
- "@reventlessdev/reventless-spec": "3.0.0-alpha.174"
43
+ "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.155",
44
+ "@reventlessdev/reventless-core": "3.0.0-alpha.307",
45
+ "@reventlessdev/reventless-gwt": "1.0.0-alpha.255",
46
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.204",
47
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.171",
48
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.175",
49
+ "@reventlessdev/reventless-seed": "1.0.0-alpha.36"
50
50
  },
51
51
  "devDependencies": {
52
52
  "rescript": "12.3.1",
@@ -56,9 +56,13 @@ let make = (~path: option<string>=?, ()): IdentityProvider.t => {
56
56
  | Ok(username) if LocalAuth.Login.credentialsOf(~username)->Option.isSome => Error(Conflict)
57
57
  | Ok(username) =>
58
58
  let password = passwordOf(credential)
59
+ // Minted here, never the username: the id reaches the event log as
60
+ // `meta.user` and every `@owner` field, and a log keyed by login names
61
+ // cannot survive a rename or a move to another provider.
62
+ let userId = ReventlessCore.Auth_UserId.mint()
59
63
  switch AccountsManifest.editFile(
60
64
  ~path=file(),
61
- ~edits=[Append({username, password, groups})],
65
+ ~edits=[Append({username, password, groups, userId})],
62
66
  ) {
63
67
  | Error(message) if message->String.endsWith("already declared in the manifest") =>
64
68
  Error(Conflict)
@@ -67,7 +71,7 @@ let make = (~path: option<string>=?, ()): IdentityProvider.t => {
67
71
  LocalAuth.Login.setCredentials(
68
72
  ~username,
69
73
  ~password,
70
- ~identity=identityOf(~username, ~groups),
74
+ ~identity={...identityOf(~username, ~groups), userId},
71
75
  )
72
76
  Ok({IdentityProvider.providerId: username})
73
77
  }
@@ -3,6 +3,7 @@
3
3
  import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
4
  import * as Primitive_object from "@rescript/runtime/lib/es6/Primitive_object.js";
5
5
  import * as LocalAuth$ReventlessLocal from "../adapter/Auth/LocalAuth.res.mjs";
6
+ import * as Auth_UserId$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_UserId.res.mjs";
6
7
  import * as AccountsManifest$Reventless from "@reventlessdev/reventless-spec/src/types/AccountsManifest.res.mjs";
7
8
  import * as IdentityProvider$Reventless from "@reventlessdev/reventless-spec/src/semantic/IdentityProvider.res.mjs";
8
9
 
@@ -60,16 +61,19 @@ function make(path, param) {
60
61
  };
61
62
  }
62
63
  let password = passwordOf(credential);
64
+ let userId = Auth_UserId$ReventlessCore.mint();
63
65
  let message = AccountsManifest$Reventless.editFile(Stdlib_Option.getOr(path, AccountsManifest$Reventless.defaultPath()), [{
64
66
  TAG: "Append",
65
67
  _0: {
66
68
  username: username,
67
69
  password: password,
68
- groups: groups
70
+ groups: groups,
71
+ userId: userId
69
72
  }
70
73
  }]);
71
74
  if (message.TAG === "Ok") {
72
- LocalAuth$ReventlessLocal.Login.setCredentials(username, password, identityOf(username, groups));
75
+ let newrecord = {...identityOf(username, groups)};
76
+ LocalAuth$ReventlessLocal.Login.setCredentials(username, password, (newrecord.userId = userId, newrecord));
73
77
  return {
74
78
  TAG: "Ok",
75
79
  _0: {
@@ -49,6 +49,8 @@ describe("Capability_IdentityProvider_UserStore", () => {
49
49
  (await LocalAuth.Login.issue(~username="new@example.com", ~password="secret-1"))->Result.isOk,
50
50
  )->toBe(true)
51
51
 
52
+ let mintedNow = LocalAuth.lookupUser("new@example.com")->Option.map(i => i.userId)
53
+
52
54
  restartFrom(file)
53
55
  expect(
54
56
  (await LocalAuth.Login.issue(~username="new@example.com", ~password="secret-1"))->Result.isOk,
@@ -56,6 +58,10 @@ describe("Capability_IdentityProvider_UserStore", () => {
56
58
  expect(LocalAuth.lookupUser("new@example.com")->Option.map(i => i.groups))->toEqual(
57
59
  Some(["Shopper"]),
58
60
  )
61
+ // The domain id is minted, not the login name, and the file carries the
62
+ // same one the running process handed out.
63
+ expect(mintedNow->Option.map(ReventlessCore.Auth_UserId.isMinted))->toEqual(Some(true))
64
+ expect(LocalAuth.lookupUser("new@example.com")->Option.map(i => i.userId))->toEqual(mintedNow)
59
65
  })
60
66
 
61
67
  testPromise("the same contact twice is a Conflict, not a second account", async () => {
@@ -8,6 +8,7 @@ import * as Stdlib_Result from "@rescript/runtime/lib/es6/Stdlib_Result.js";
8
8
  import * as Stdlib_JsError from "@rescript/runtime/lib/es6/Stdlib_JsError.js";
9
9
  import * as LocalAuth$ReventlessLocal from "../../src/adapter/Auth/LocalAuth.res.mjs";
10
10
  import * as UserStore$ReventlessLocal from "../../src/adapter/Auth/UserStore.res.mjs";
11
+ import * as Auth_UserId$ReventlessCore from "@reventlessdev/reventless-core/src/adapter/Auth/Auth_UserId.res.mjs";
11
12
  import * as Capability_IdentityProvider_UserStore$ReventlessLocal from "../../src/capability/Capability_IdentityProvider_UserStore.res.mjs";
12
13
 
13
14
  function email(address) {
@@ -65,9 +66,12 @@ globalThis.describe("Capability_IdentityProvider_UserStore", () => {
65
66
  }
66
67
  });
67
68
  globalThis.expect(Stdlib_Result.isOk(await LocalAuth$ReventlessLocal.Login.issue("new@example.com", "secret-1", undefined))).toBe(true);
69
+ let mintedNow = Stdlib_Option.map(LocalAuth$ReventlessLocal.lookupUser("new@example.com"), i => i.userId);
68
70
  restartFrom(file);
69
71
  globalThis.expect(Stdlib_Result.isOk(await LocalAuth$ReventlessLocal.Login.issue("new@example.com", "secret-1", undefined))).toBe(true);
70
72
  globalThis.expect(Stdlib_Option.map(LocalAuth$ReventlessLocal.lookupUser("new@example.com"), i => i.groups)).toEqual(["Shopper"]);
73
+ globalThis.expect(Stdlib_Option.map(mintedNow, Auth_UserId$ReventlessCore.isMinted)).toEqual(true);
74
+ globalThis.expect(Stdlib_Option.map(LocalAuth$ReventlessLocal.lookupUser("new@example.com"), i => i.userId)).toEqual(mintedNow);
71
75
  });
72
76
  globalThis.test("the same contact twice is a Conflict, not a second account", async () => {
73
77
  resetAll();
@@ -0,0 +1,61 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+ import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
4
+ import * as Capabilities$Reventless from "@reventlessdev/reventless-spec/src/semantic/Capabilities.res.mjs";
5
+ import * as LocalCapabilities$ReventlessLocal from "../../src/adapter/LocalCapabilities.res.mjs";
6
+ import * as Capability_IdentityProvider_UserStore$ReventlessLocal from "../../src/capability/Capability_IdentityProvider_UserStore.res.mjs";
7
+
8
+ let declared = [
9
+ [
10
+ "Geocoding",
11
+ "GeocodeAddress"
12
+ ],
13
+ [
14
+ "Messaging",
15
+ "SendNotification"
16
+ ],
17
+ [
18
+ "IdentityProvider",
19
+ "CreateAccount"
20
+ ]
21
+ ];
22
+
23
+ function reset() {
24
+ LocalCapabilities$ReventlessLocal.registerMessagingSender("");
25
+ LocalCapabilities$ReventlessLocal.registerIdentityProvider(Capabilities$Reventless.none.identityProvider);
26
+ }
27
+
28
+ globalThis.describe("LocalCapabilities.unmetWarning", () => {
29
+ globalThis.test("a bare platform provisions nothing, and the warning names each need once", () => {
30
+ reset();
31
+ globalThis.expect(LocalCapabilities$ReventlessLocal.provisioned()).toEqual([]);
32
+ let message = Stdlib_Option.getOr(LocalCapabilities$ReventlessLocal.unmetWarning(declared), "");
33
+ globalThis.expect(message.includes("Geocoding (GeocodeAddress)")).toBe(true);
34
+ globalThis.expect(message.includes("Capability_Messaging_Log.make()")).toBe(true);
35
+ globalThis.expect(message.includes("Capability_IdentityProvider_UserStore.make()")).toBe(true);
36
+ });
37
+ globalThis.test("a registered provider leaves the warning", () => {
38
+ reset();
39
+ LocalCapabilities$ReventlessLocal.registerMessagingSender("Shop <shop@example.test>");
40
+ LocalCapabilities$ReventlessLocal.registerIdentityProvider(Capability_IdentityProvider_UserStore$ReventlessLocal.make("/nonexistent/users.yaml", undefined));
41
+ globalThis.expect(LocalCapabilities$ReventlessLocal.provisioned()).toEqual([
42
+ "Messaging",
43
+ "IdentityProvider"
44
+ ]);
45
+ let message = Stdlib_Option.getOr(LocalCapabilities$ReventlessLocal.unmetWarning(declared), "");
46
+ globalThis.expect(message.includes("Messaging")).toBe(false);
47
+ globalThis.expect(message.includes("IdentityProvider")).toBe(false);
48
+ globalThis.expect(message.includes("Geocoding")).toBe(true);
49
+ reset();
50
+ });
51
+ globalThis.test("a plugin declaring nothing gets no warning", () => {
52
+ reset();
53
+ globalThis.expect(LocalCapabilities$ReventlessLocal.unmetWarning([])).toEqual(undefined);
54
+ });
55
+ });
56
+
57
+ export {
58
+ declared,
59
+ reset,
60
+ }
61
+ /* Not a pure module */