@reventlessdev/reventless-local 3.0.0-alpha.250 → 3.0.0-alpha.252

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,39 @@
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.252 (2026-09-16)
7
+
8
+ * feat(spec)!: infer the DCB partition key per slice, and drop @partitionTag where inference agrees ([6c10161](https://github.com/ReventlessDev/reventless-core/commit/6c101611d438beea4bc9175ae8ea0ea1f6f7493b))
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ * DcbTag.derivePartitionTag is removed; a boundary whose
13
+ partition cannot be inferred no longer deploys.
14
+
15
+
16
+
17
+ # 3.0.0-alpha.251 (2026-09-11)
18
+
19
+ * feat(spec)!: a secret is a capability, so a test can know it in advance ([71507c6](https://github.com/ReventlessDev/reventless-core/commit/71507c6459b49178746f00c5040cb479d7fdade8))
20
+ * feat(spec)!: making a principal is a capability, so the provider is replaceable ([3e59b9b](https://github.com/ReventlessDev/reventless-core/commit/3e59b9b665cc7e4b6b6f156b63b1b6bd0c6181ca))
21
+ ### Features
22
+
23
+ * **aws:** the first administrator is one command, not the console ([5f1a846](https://github.com/ReventlessDev/reventless-core/commit/5f1a84603b21a9feb691a08a148751c6c6e8eca8))
24
+ * **aws:** the rest of the cast is a file, not a comment ([aa23581](https://github.com/ReventlessDev/reventless-core/commit/aa23581f09948a47fb451ec2ecf2fc2bdd8e6ee6))
25
+
26
+ ### BREAKING CHANGES
27
+
28
+ * Capabilities.t gains a secrets member. A platform that builds
29
+ the record literally no longer compiles until it supplies one;
30
+ Capabilities.none.secrets is the refusing value. Code that spreads
31
+ Capabilities.none is unaffected.
32
+ * Capabilities.t gains an identityProvider member. A platform
33
+ that builds the record literally no longer compiles until it supplies one;
34
+ Capabilities.none.identityProvider is the refusing value to pass while no
35
+ backend exists. Code that spreads Capabilities.none is unaffected.
36
+
37
+
38
+
6
39
  # 3.0.0-alpha.250 (2026-09-09)
7
40
 
8
41
  **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.252",
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": {
@@ -35,18 +35,18 @@
35
35
  "sury": "11.0.0-rc.2",
36
36
  "ws": "^8.18.0",
37
37
  "@reventlessdev/rescript-effect": "0.1.0-alpha.33",
38
- "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.28",
39
38
  "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
40
- "@reventlessdev/rescript-node": "2.0.0-alpha.11",
39
+ "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.28",
41
40
  "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.22",
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"
41
+ "@reventlessdev/rescript-node": "2.0.0-alpha.12",
42
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.21",
43
+ "@reventlessdev/reventless-core": "3.0.0-alpha.263",
44
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.164",
45
+ "@reventlessdev/reventless-gwt": "1.0.0-alpha.210",
46
+ "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.111",
47
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.127",
48
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.136",
49
+ "@reventlessdev/reventless-seed": "1.0.0-alpha.22"
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 */
@@ -256,8 +256,8 @@ let register = (
256
256
  // Called by Dcb_Builder via dcbMutationResolverHook. Registers SDL + resolver
257
257
  // stubs for DCB mutations. Unlike aggregate mutations, DCB commands use tagged
258
258
  // ID field(s) (e.g., itemId, or composite-partition members) — the envelope id
259
- // is derived inside makeGenerateCommand from the command schema, so the resolver
260
- // just forwards args verbatim.
259
+ // is the command's value of its slice's partition key, taken inside
260
+ // makeGenerateCommand, so the resolver just forwards args verbatim.
261
261
 
262
262
  let registerDcb = (
263
263
  ~fieldName: string,
@@ -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
  }
@@ -14,6 +14,7 @@ module Make = (
14
14
  ~publishJsons: Pulumi.Output.t<ReventlessInfra.CommandTopic.publishJsons>,
15
15
  ~tagKeysByEventType: Dict.t<array<string>>=?,
16
16
  ~crossPartitionTagKeys: array<string>=?,
17
+ ~partitionTag: Reventless.DcbTag.derivedPartitionTag=?,
17
18
  ~runtime: ReventlessInfra.RuntimeHints.t=?,
18
19
  ~opts: Pulumi.ComponentResource.options=?,
19
20
  ) => component = Inner.make
@@ -374,7 +374,7 @@ let PlaceMaker = StateChangeSlice_Builder$ReventlessLocal.Make({
374
374
  moduleUrl: moduleUrl$1
375
375
  });
376
376
 
377
- let _placeSlice = PlaceMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined);
377
+ let _placeSlice = PlaceMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined, undefined);
378
378
 
379
379
  let ShipMaker = StateChangeSlice_Builder$ReventlessLocal.Make({
380
380
  name: name$1,
@@ -395,7 +395,7 @@ let ShipMaker = StateChangeSlice_Builder$ReventlessLocal.Make({
395
395
  moduleUrl: moduleUrl$3
396
396
  });
397
397
 
398
- let _shipSlice = ShipMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined);
398
+ let _shipSlice = ShipMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined, undefined);
399
399
 
400
400
  let dcbTopicOutputs = Component$ReventlessInfra.outputs(dcbEventLog).eventTopic;
401
401
 
@@ -236,7 +236,7 @@ let _addProductSlice = AddProductMaker.make(eventLog, publishJsonsOutput, Object
236
236
  "CategoryAdded",
237
237
  ["categoryId"]
238
238
  ]
239
- ]), ["categoryId"], undefined, undefined);
239
+ ]), ["categoryId"], undefined, undefined, undefined);
240
240
 
241
241
  async function dispatch(commandJson, id) {
242
242
  return await publishJsons([{
@@ -179,7 +179,7 @@ async function publishJsons(cmdJsons) {
179
179
 
180
180
  let publishJsonsOutput = Pulumi.output(publishJsons);
181
181
 
182
- let _addItemSlice = AddItemMaker.make(eventLog, publishJsonsOutput, undefined, undefined, undefined, undefined);
182
+ let _addItemSlice = AddItemMaker.make(eventLog, publishJsonsOutput, undefined, undefined, undefined, undefined, undefined);
183
183
 
184
184
  async function dispatch(commandJson, id) {
185
185
  return await publishJsons([{
@@ -235,7 +235,7 @@ let PlaceMaker = StateChangeSlice_Builder$ReventlessLocal.Make({
235
235
  moduleUrl: moduleUrl$1
236
236
  });
237
237
 
238
- let _placeSlice = PlaceMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined);
238
+ let _placeSlice = PlaceMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined, undefined);
239
239
 
240
240
  let dcbTopicOutputs = Component$ReventlessInfra.outputs(dcbEventLog).eventTopic;
241
241
 
@@ -244,7 +244,7 @@ async function publishJsons(cmdJsons) {
244
244
 
245
245
  let publishJsonsOutput = Pulumi.output(publishJsons);
246
246
 
247
- let _addProductSlice = AddProductMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined);
247
+ let _addProductSlice = AddProductMaker.make(dcbEventLog, publishJsonsOutput, undefined, undefined, undefined, undefined, undefined);
248
248
 
249
249
  let dcbTopicOutputs = Component$ReventlessInfra.outputs(dcbEventLog).eventTopic;
250
250