@reventlessdev/reventless-local 3.0.0-alpha.183 → 3.0.0-alpha.184

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,14 @@
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.184 (2026-07-31)
7
+
8
+ **Note:** Version bump only for package @reventlessdev/reventless-local
9
+
10
+
11
+
12
+
13
+
6
14
  # 3.0.0-alpha.183 (2026-07-30)
7
15
 
8
16
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/reventless-local",
3
- "version": "3.0.0-alpha.183",
3
+ "version": "3.0.0-alpha.184",
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": {
@@ -34,17 +34,18 @@
34
34
  "graphql-yoga": "^5.21.0",
35
35
  "sury": "11.0.0-alpha.4",
36
36
  "ws": "^8.18.0",
37
- "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.25",
38
- "@reventlessdev/rescript-jest": "1.0.0-alpha.9",
39
- "@reventlessdev/rescript-effect": "0.1.0-alpha.31",
40
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.17",
41
- "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.20",
42
- "@reventlessdev/reventless-core": "3.0.0-alpha.195",
43
- "@reventlessdev/reventless-gwt": "1.0.0-alpha.142",
44
- "@reventlessdev/reventless-postgres": "3.0.0-alpha.59",
45
- "@reventlessdev/reventless-spec": "3.0.0-alpha.88",
46
- "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.43",
47
- "@reventlessdev/reventless-infra": "3.0.0-alpha.113"
37
+ "@reventlessdev/rescript-effect": "0.1.0-alpha.32",
38
+ "@reventlessdev/rescript-graphql-yoga": "1.0.0-alpha.26",
39
+ "@reventlessdev/rescript-jest": "1.0.0-alpha.10",
40
+ "@reventlessdev/rescript-node": "2.0.0-alpha.0",
41
+ "@reventlessdev/rescript-mcp-sdk": "1.0.0-alpha.21",
42
+ "@reventlessdev/reventless-core": "3.0.0-alpha.196",
43
+ "@reventlessdev/reventless-gwt": "1.0.0-alpha.143",
44
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.18",
45
+ "@reventlessdev/reventless-graphql-server": "1.0.0-alpha.44",
46
+ "@reventlessdev/reventless-postgres": "3.0.0-alpha.60",
47
+ "@reventlessdev/reventless-infra": "3.0.0-alpha.114",
48
+ "@reventlessdev/reventless-spec": "3.0.0-alpha.89"
48
49
  },
49
50
  "devDependencies": {
50
51
  "rescript": "12.3.0",
package/rescript.json CHANGED
@@ -24,6 +24,7 @@
24
24
  ],
25
25
  "dependencies": [
26
26
  "sury",
27
+ "@reventlessdev/rescript-node",
27
28
  "@reventlessdev/rescript-jest",
28
29
  "@reventlessdev/rescript-effect",
29
30
  "@reventlessdev/rescript-pulumi-pulumi",
@@ -20,21 +20,6 @@
20
20
  // Usage: emit-capabilities <srcDir> [<compositionModule>]
21
21
  // (run from the plugin package, after `rescript build`)
22
22
 
23
- // ── Node ─────────────────────────────────────────────────────────────────────
24
-
25
- @module("node:fs") external existsSync: string => bool = "existsSync"
26
- @module("node:fs") external writeFileSync: (string, string, @as("utf8") _) => unit = "writeFileSync"
27
-
28
- @module("node:path") @variadic external join: array<string> => string = "join"
29
- @module("node:path") @variadic external resolve: array<string> => string = "resolve"
30
- @module("node:path") @val external sep: string = "sep"
31
-
32
- @module("node:url") external pathToFileURL: string => {"href": string} = "pathToFileURL"
33
-
34
- @val @scope("process") external argv: array<string> = "argv"
35
- @val @scope("process") external env: Dict.t<string> = "env"
36
- @val external processExit: int => unit = "process.exit"
37
-
38
23
  // Emitted by ReScript as a literal `import(...)` expression — the composition
39
24
  // root's path is only known at run time, so it cannot be a static binding.
40
25
  @val external dynImport: string => promise<'a> = "import"
@@ -56,11 +41,11 @@ type compositionExports = {"Make": platform => builtPlugin}
56
41
  let compositionModulePath = (~srcDir: string, ~moduleArg: option<string>): string => {
57
42
  let withExtension = name => name->String.endsWith(".mjs") ? name : name ++ ".res.mjs"
58
43
  switch moduleArg {
59
- | None | Some("") => join([srcDir, "Plugin.res.mjs"])
44
+ | None | Some("") => NodePath.join([srcDir, "Plugin.res.mjs"])
60
45
  | Some(name) =>
61
- name->String.includes("/") || name->String.includes(sep)
62
- ? resolve([withExtension(name)])
63
- : join([srcDir, withExtension(name)])
46
+ name->String.includes("/") || name->String.includes(NodePath.sep)
47
+ ? NodePath.resolve([withExtension(name)])
48
+ : NodePath.join([srcDir, withExtension(name)])
64
49
  }
65
50
  }
66
51
 
@@ -68,25 +53,25 @@ let compositionModulePath = (~srcDir: string, ~moduleArg: option<string>): strin
68
53
 
69
54
  let fail = (message: string) => {
70
55
  Console.error("emit-capabilities: " ++ message)
71
- processExit(1)
56
+ NodeProcess.exit(1)
72
57
  }
73
58
 
74
59
  let main = async () => {
75
60
  // The local platform defaults to Debug-level logging; a build step should not.
76
- switch env->Dict.get("LOG_LEVEL") {
61
+ switch NodeProcess.env->Dict.get("LOG_LEVEL") {
77
62
  | Some(_) => ()
78
- | None => env->Dict.set("LOG_LEVEL", "warn")
63
+ | None => NodeProcess.env->Dict.set("LOG_LEVEL", "warn")
79
64
  }
80
65
 
81
- switch argv->Array.get(2) {
66
+ switch NodeProcess.argv->Array.get(2) {
82
67
  | None | Some("") => {
83
68
  Console.error("Usage: emit-capabilities <srcDir> [<compositionModule>]")
84
- processExit(1)
69
+ NodeProcess.exit(1)
85
70
  }
86
71
  | Some(srcDirArg) => {
87
- let srcDir = resolve([srcDirArg])
88
- let modulePath = compositionModulePath(~srcDir, ~moduleArg=argv->Array.get(3))
89
- if !existsSync(modulePath) {
72
+ let srcDir = NodePath.resolve([srcDirArg])
73
+ let modulePath = compositionModulePath(~srcDir, ~moduleArg=NodeProcess.argv->Array.get(3))
74
+ if !NodeFs.existsSync(modulePath) {
90
75
  fail(`${modulePath} not found — run \`rescript build\` first.`)
91
76
  }
92
77
 
@@ -95,11 +80,11 @@ let main = async () => {
95
80
  let platformModule: localPlatformExports = await dynImport("./Platform.res.mjs")
96
81
  let platform = platformModule["Make"]()
97
82
 
98
- let composition: compositionExports = await dynImport(pathToFileURL(modulePath)["href"])
83
+ let composition: compositionExports = await dynImport(NodeUrl.pathToFileURL(modulePath)["href"])
99
84
  let built = composition["Make"](platform)
100
85
 
101
- let manifestPath = join([srcDir, "capabilities.json"])
102
- writeFileSync(
86
+ let manifestPath = NodePath.join([srcDir, "capabilities.json"])
87
+ NodeFs.writeFileSync(
103
88
  manifestPath,
104
89
  Reventless.CapabilityManifest.renderForStructure(built["pluginStructure"]),
105
90
  )
@@ -107,7 +92,7 @@ let main = async () => {
107
92
 
108
93
  // Applying the platform functor wires in-process infrastructure; exit
109
94
  // explicitly so no lingering handle keeps the build step alive.
110
- processExit(0)
95
+ NodeProcess.exit(0)
111
96
  }
112
97
  }
113
98
  }
package/src/Platform.res CHANGED
@@ -821,15 +821,14 @@ module MakeWithConfig = (
821
821
  component->ReventlessCore.Component.operations
822
822
  }
823
823
 
824
- @val external processEnv: dict<string> = "process.env"
825
- let graphqlDebug = processEnv->Dict.get("GRAPHQL_DEBUG")->Option.isSome
824
+ let graphqlDebug = NodeProcess.env->Dict.get("GRAPHQL_DEBUG")->Option.isSome
826
825
 
827
826
  // Per-session server ports for the VS Code local platform runner (features
828
827
  // plan Phase 9). The runner spawns one platform child per app folder; default
829
828
  // ports (4000/4001/3001/3002) collide when several run, so each is overridable
830
829
  // via env. Defaults preserve today's behaviour for all non-runner callers.
831
830
  let resolvePort = (key, fallback) =>
832
- processEnv->Dict.get(key)->Option.flatMap(v => Int.fromString(v))->Option.getOr(fallback)
831
+ NodeProcess.env->Dict.get(key)->Option.flatMap(v => Int.fromString(v))->Option.getOr(fallback)
833
832
  let domainPort = resolvePort("REVENTLESS_DOMAIN_PORT", 4000)
834
833
  let domainMcpPort = resolvePort("REVENTLESS_DOMAIN_MCP_PORT", 3001)
835
834
  let platformPort = resolvePort("REVENTLESS_PLATFORM_PORT", 4001)
@@ -95,24 +95,15 @@ module Login = {
95
95
  // after a backend reload. When unset, falls back to a random per-process
96
96
  // secret — secure for ephemeral test runs but invalidates every previously
97
97
  // issued token on every restart.
98
- type buf
99
- @module("node:crypto") external _randomBytes: int => buf = "randomBytes"
100
- @send external _bufToString: (buf, string) => string = "toString"
101
- type hmac
102
- @module("node:crypto") external _createHmac: (string, string) => hmac = "createHmac"
103
- @send external _update: (hmac, string) => hmac = "update"
104
- @send external _digest: (hmac, string) => string = "digest"
105
-
106
- @val external _processEnv: Dict.t<string> = "process.env"
107
98
 
108
99
  let _secret: ref<option<string>> = ref(None)
109
100
  let _getSecret = (): string =>
110
101
  switch _secret.contents {
111
102
  | Some(s) => s
112
103
  | None =>
113
- let s = switch _processEnv->Dict.get("REVENTLESS_INMEMORY_TOKEN_SECRET") {
104
+ let s = switch NodeProcess.env->Dict.get("REVENTLESS_INMEMORY_TOKEN_SECRET") {
114
105
  | Some(envSecret) if String.length(envSecret) >= 16 => envSecret
115
- | _ => _randomBytes(32)->_bufToString("hex")
106
+ | _ => NodeCrypto.randomBytes(32)->NodeCrypto.bufferToString("hex")
116
107
  }
117
108
  _secret := Some(s)
118
109
  s
@@ -121,7 +112,9 @@ module Login = {
121
112
  let setTokenSecret = (s: string): unit => _secret := Some(s)
122
113
 
123
114
  let _sign = (payload: string): string =>
124
- _createHmac("sha256", _getSecret())->_update(payload)->_digest("base64url")
115
+ NodeCrypto.createHmac("sha256", _getSecret())
116
+ ->NodeCrypto.hmacUpdate(payload)
117
+ ->NodeCrypto.hmacDigest("base64url")
125
118
 
126
119
  // base64url over arbitrary JSON strings: btoa-encode + URL-safe alphabet,
127
120
  // strip `=` padding. Avoids pulling in Buffer for a single-line conversion.
@@ -26,10 +26,6 @@ type entry = {
26
26
  // ── YAML parsing (eemeli/yaml) ──────────────────────────────────────────────
27
27
 
28
28
  @module("yaml") external _yamlParse: string => unknown = "parse"
29
- @module("node:fs") external _readFileSync: (string, string) => string = "readFileSync"
30
- @module("node:fs") external _existsSync: string => bool = "existsSync"
31
- @module("node:path") external _join: (string, string) => string = "join"
32
- @val external _processCwd: unit => string = "process.cwd"
33
29
 
34
30
  let _entriesSchema = S.array(entrySchema)
35
31
 
@@ -46,7 +42,7 @@ let parseString = (yamlText: string): result<array<entry>, string> =>
46
42
  /** Reads + parses a YAML file. */
47
43
  let parseFile = (path: string): result<array<entry>, string> =>
48
44
  try {
49
- let text = _readFileSync(path, "utf8")
45
+ let text = NodeFs.readFileSync(path)
50
46
  parseString(text)
51
47
  } catch {
52
48
  | JsExn(err) => Error(JsExn.message(err)->Option.getOr(`Cannot read ${path}`))
@@ -66,7 +62,7 @@ let _registerEntries = (entries: array<entry>): unit =>
66
62
  LocalAuth.Login.setCredentials(~username, ~password, ~identity=id)
67
63
  })
68
64
 
69
- let _defaultPath = (): string => _join(_processCwd(), ".reventless/users.yaml")
65
+ let _defaultPath = (): string => NodePath.join([NodeProcess.cwd(), ".reventless/users.yaml"])
70
66
 
71
67
  /**
72
68
  * Hydrates the Login store. Returns the resolution path actually used
@@ -106,7 +102,7 @@ let load = (
106
102
  })
107
103
  | None =>
108
104
  let defaultPath = _defaultPath()
109
- if _existsSync(defaultPath) {
105
+ if NodeFs.existsSync(defaultPath) {
110
106
  parseFile(defaultPath)->Result.map(entries => {
111
107
  _registerEntries(entries)
112
108
  DefaultFile(defaultPath)
@@ -106,7 +106,7 @@ function load(users, usersFile, param) {
106
106
  };
107
107
  });
108
108
  }
109
- let defaultPath = Nodepath.join(process.cwd(), ".reventless/users.yaml");
109
+ let defaultPath = _defaultPath();
110
110
  if (Nodefs.existsSync(defaultPath)) {
111
111
  return Stdlib_Result.map(parseFile(defaultPath), entries => {
112
112
  _registerEntries(entries);
@@ -34,13 +34,11 @@ let postgres = async (~connection, ~resetOnStart=false) => {
34
34
  Postgres({pool, initialCount, connection})
35
35
  }
36
36
 
37
- @val external processEnv: dict<string> = "process.env"
38
37
 
39
38
  // Deletes a file if it exists. No-op if the path is missing.
40
- @module("node:fs") external _unlinkSync: string => unit = "unlinkSync"
41
39
 
42
40
  let removeFileIfExists = (path: string) =>
43
- try _unlinkSync(path) catch {
41
+ try NodeFs.unlinkSync(path) catch {
44
42
  | _ => ()
45
43
  }
46
44
 
@@ -50,7 +48,7 @@ let removeFileIfExists = (path: string) =>
50
48
  // memory — explicit Memory selector
51
49
  // Anything else (including unset) yields Memory.
52
50
  let fromEnv = () =>
53
- switch processEnv->Dict.get("REVENTLESS_LOCAL_BACKEND") {
51
+ switch NodeProcess.env->Dict.get("REVENTLESS_LOCAL_BACKEND") {
54
52
  | None => Memory
55
53
  | Some(raw) =>
56
54
  let trimmed = raw->String.trim
@@ -37,7 +37,6 @@ type nodeResponse
37
37
  // mangled by utf8 decoding (setEncoding is deliberately NOT called).
38
38
  @send external _onDataBuf: (nodeRequest, @as("data") _, LocalObjectStore.buffer => unit) => unit = "on"
39
39
 
40
- @module("crypto") external randomUUID: unit => string = "randomUUID"
41
40
 
42
41
  let readBody = (req: nodeRequest, onBody: string => unit): unit => {
43
42
  let buf = ref("")
@@ -155,7 +154,7 @@ let handleUploadPresign = (req: nodeRequest, res: nodeResponse): unit =>
155
154
  }
156
155
  let fileName =
157
156
  parsed->Dict.get("fileName")->Option.flatMap(JSON.Decode.string)->Option.getOr("upload")
158
- let ref = `/${LocalObjectStore.defaultUploadPrefix}/${randomUUID()}/${fileName}`
157
+ let ref = `/${LocalObjectStore.defaultUploadPrefix}/${NodeCrypto.randomUUID()}/${fileName}`
159
158
  _writeJson(
160
159
  res,
161
160
  ~status=200,
@@ -496,8 +495,7 @@ let buildNodeResolver = (): option<(string, resolverFn)> =>
496
495
 
497
496
  // -- Server lifecycle ------------------------------------------------------
498
497
 
499
- @val external processEnv: dict<string> = "process.env"
500
- let debug = processEnv->Dict.get("GRAPHQL_DEBUG")->Option.isSome
498
+ let debug = NodeProcess.env->Dict.get("GRAPHQL_DEBUG")->Option.isSome
501
499
 
502
500
  let activeServer: ref<option<YG.httpServer>> = ref(None)
503
501
  let activeSchema: ref<option<YG.schema>> = ref(None)
@@ -2,11 +2,11 @@
2
2
 
3
3
  import * as S from "sury/src/S.res.mjs";
4
4
  import * as Http from "http";
5
- import * as Crypto from "crypto";
6
5
  import * as Graphql from "graphql";
7
6
  import * as GraphqlYoga from "@reventlessdev/rescript-graphql-yoga/src/GraphqlYoga.res.mjs";
8
7
  import * as Stdlib_Dict from "@rescript/runtime/lib/es6/Stdlib_Dict.js";
9
8
  import * as Stdlib_JSON from "@rescript/runtime/lib/es6/Stdlib_JSON.js";
9
+ import * as Nodecrypto from "node:crypto";
10
10
  import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
11
11
  import * as Stdlib_JsExn from "@rescript/runtime/lib/es6/Stdlib_JsExn.js";
12
12
  import * as GraphqlYoga$1 from "graphql-yoga";
@@ -117,7 +117,7 @@ function handleUploadPresign(req, res) {
117
117
  parsed = {};
118
118
  }
119
119
  let fileName = Stdlib_Option.getOr(Stdlib_Option.flatMap(parsed["fileName"], Stdlib_JSON.Decode.string), "upload");
120
- let ref = `/` + LocalObjectStore$ReventlessLocal.defaultUploadPrefix + `/` + Crypto.randomUUID() + `/` + fileName;
120
+ let ref = `/` + LocalObjectStore$ReventlessLocal.defaultUploadPrefix + `/` + Nodecrypto.randomUUID() + `/` + fileName;
121
121
  _writeJson(res, 200, Object.fromEntries([
122
122
  [
123
123
  "uploadUrl",
@@ -132,8 +132,7 @@ let registerEventHistoryResourcesFromEntries = (
132
132
 
133
133
  let activeServer: ref<option<McpSdk.httpServer>> = ref(None)
134
134
 
135
- @val external processEnv: dict<string> = "process.env"
136
- let debug = processEnv->Dict.get("MCP_DEBUG")->Option.isSome
135
+ let debug = NodeProcess.env->Dict.get("MCP_DEBUG")->Option.isSome
137
136
 
138
137
  // Extract identity from an HTTP IncomingMessage via the X-Identity header.
139
138
  // Falls back to Identity.anonymous when the header is absent or malformed.
@@ -98,10 +98,9 @@ let makeStateChangeDescriptor = (
98
98
  // so the runner's line parser can pick it out of the platform's ANSI log noise on
99
99
  // stdout. The emit is a plain Console.log, so it never perturbs publishEvent's
100
100
  // subscriber-countdown delivery semantics.
101
- @val external tapProcessEnv: dict<string> = "process.env"
102
101
  // Read per publish (not once at import) so the runner can toggle it live and so
103
102
  // hermetic tests can flip it between cases. The cost is one dict lookup per event.
104
- let eventTapEnabled = () => tapProcessEnv->Dict.get("REVENTLESS_EVENT_TAP")->Option.isSome
103
+ let eventTapEnabled = () => NodeProcess.env->Dict.get("REVENTLESS_EVENT_TAP")->Option.isSome
105
104
  let eventTapSeq = ref(0)
106
105
  // Seed the in-memory tap counter from a persistent store's existing event count
107
106
  // so the timeline's #N continues across process restarts / app switches instead
@@ -51,8 +51,7 @@ type t = {
51
51
  printDiagnostics: unit => unit,
52
52
  }
53
53
 
54
- @val external processEnv: dict<string> = "process.env"
55
- let debug = processEnv->Dict.get("MCP_DEBUG")->Option.isSome
54
+ let debug = NodeProcess.env->Dict.get("MCP_DEBUG")->Option.isSome
56
55
 
57
56
  let make = (~label: string="MCP"): t => {
58
57
  let tools: ref<dict<registeredTool>> = ref(Dict.make())
@@ -7,10 +7,6 @@ type statement
7
7
  @module("node:sqlite") @new
8
8
  external openDatabaseSync: string => t = "DatabaseSync"
9
9
 
10
- @module("node:path") external dirname: string => string = "dirname"
11
- type mkdirOpts = {recursive: bool}
12
- @module("node:fs") external mkdirSync: (string, mkdirOpts) => unit = "mkdirSync"
13
-
14
10
  @send external _close: t => unit = "close"
15
11
  @send external _exec: (t, string) => unit = "exec"
16
12
  @send external _prepare: (t, string) => statement = "prepare"
@@ -25,7 +21,7 @@ let _iterateApply: (statement, array<JSON.t>) => Iterator.t<dict<JSON.t>> = %raw
25
21
 
26
22
  let openDb = (~path) => {
27
23
  if path !== ":memory:" {
28
- mkdirSync(dirname(path), {recursive: true})
24
+ NodeFs.mkdirSync(NodePath.dirname(path), {recursive: true})
29
25
  }
30
26
  let db = openDatabaseSync(path)
31
27
  // Connection pragmas. WAL lets readers proceed concurrently with a writer and
@@ -10,13 +10,12 @@
10
10
 
11
11
  open JestGlobals
12
12
 
13
- @val external processEnv: dict<string> = "process.env"
14
13
 
15
14
  let _ = TestRunner.setup()
16
15
  let opts: Pulumi.CustomResourceOptions.t = {}
17
16
 
18
17
  let pgPool =
19
- processEnv
18
+ NodeProcess.env
20
19
  ->Dict.get("PG_URL")
21
20
  ->Option.map(url => ReventlessPostgres.PgDriver.makePool({connectionString: url}))
22
21
 
@@ -17,7 +17,6 @@ let defaultMeta: Reventless.Message.meta = {
17
17
  correlationId: "",
18
18
  }
19
19
 
20
- @val external processEnv: Dict.t<string> = "process.env"
21
20
 
22
21
  type consoleObj = {mutable log: string => unit}
23
22
  @val external console: consoleObj = "console"
@@ -48,7 +47,7 @@ let tapLines = (logs: array<string>): array<string> =>
48
47
 
49
48
  describe("LocalBus event tap (Phase 9)", () => {
50
49
  // Each test owns the env flag; always clear it afterwards so cases don't leak.
51
- let clearEnv = () => processEnv->Dict.delete("REVENTLESS_EVENT_TAP")
50
+ let clearEnv = () => NodeProcess.env->Dict.delete("REVENTLESS_EVENT_TAP")
52
51
 
53
52
  testPromise("tap off → no sentinel line is emitted", async () => {
54
53
  clearEnv()
@@ -60,7 +59,7 @@ describe("LocalBus event tap (Phase 9)", () => {
60
59
  })
61
60
 
62
61
  testPromise("tap on → one well-formed line per published event", async () => {
63
- processEnv->Dict.set("REVENTLESS_EVENT_TAP", "ndjson")
62
+ NodeProcess.env->Dict.set("REVENTLESS_EVENT_TAP", "ndjson")
64
63
  module TestBus = LocalBus.MakeSilent()
65
64
  let logs = await captureLogs(async () => {
66
65
  await TestBus.publishEvent(
@@ -90,7 +89,7 @@ describe("LocalBus event tap (Phase 9)", () => {
90
89
  })
91
90
 
92
91
  testPromise("tap emits even when the topic has no subscribers", async () => {
93
- processEnv->Dict.set("REVENTLESS_EVENT_TAP", "ndjson")
92
+ NodeProcess.env->Dict.set("REVENTLESS_EVENT_TAP", "ndjson")
94
93
  module TestBus = LocalBus.MakeSilent()
95
94
  let logs = await captureLogs(async () => {
96
95
  // No subscribeToEvents call — the tap fires before the subscriber-count gate.
@@ -4,11 +4,6 @@
4
4
 
5
5
  open JestGlobals
6
6
 
7
- @module("node:fs") external _writeFileSync: (string, string, string) => unit = "writeFileSync"
8
- @module("node:fs") external _mkdtempSync: string => string = "mkdtempSync"
9
- @module("node:fs") external _rmSync: (string, {..}) => unit = "rmSync"
10
- @module("node:path") external _join: (string, string) => string = "join"
11
- @module("node:os") external _tmpdir: unit => string = "tmpdir"
12
7
 
13
8
  let resetAll = () => {
14
9
  LocalAuth.resetUsers()
@@ -17,9 +12,9 @@ let resetAll = () => {
17
12
  }
18
13
 
19
14
  let _writeYamlFile = (contents: string): string => {
20
- let dir = _mkdtempSync(_join(_tmpdir(), "reventless-userstore-"))
21
- let path = _join(dir, "users.yaml")
22
- _writeFileSync(path, contents, "utf8")
15
+ let dir = NodeFs.mkdtempSync(NodePath.join([NodeOs.tmpdir(), "reventless-userstore-"]))
16
+ let path = NodePath.join([dir, "users.yaml"])
17
+ NodeFs.writeFileSync(path, contents)
23
18
  path
24
19
  }
25
20
 
@@ -102,7 +97,7 @@ testPromise("load(~usersFile) reads a YAML file from disk", async () => {
102
97
  | Ok(_) => ()
103
98
  | Error(msg) => JsError.throwWithMessage("issue rejected file-loaded credentials: " ++ msg)
104
99
  }
105
- _rmSync(path, {"force": true})
100
+ NodeFs.rmSync(path, {force: true})
106
101
  })
107
102
 
108
103
  testPromise("load(~usersFile) returns Error when the file is missing", async () => {
@@ -152,7 +147,7 @@ testPromise("inline users win over a file path", async () => {
152
147
  | Some(_) => ()
153
148
  | None => JsError.throwWithMessage("inline-user missing from registry")
154
149
  }
155
- _rmSync(path, {"force": true})
150
+ NodeFs.rmSync(path, {force: true})
156
151
  })
157
152
 
158
153
  testPromise("autoLoadOnce is a no-op after an explicit load", async () => {
@@ -10,7 +10,6 @@
10
10
 
11
11
  open JestGlobals
12
12
 
13
- @val external processEnv: dict<string> = "process.env"
14
13
  let opts: Pulumi.CustomResourceOptions.t = {}
15
14
 
16
15
  // Flat stored aggregate event — the exact shape EventLog_Operations.encodeEvent'
@@ -58,7 +57,7 @@ let recorder = () => {
58
57
  (received, handler)
59
58
  }
60
59
 
61
- switch processEnv->Dict.get("PG_URL") {
60
+ switch NodeProcess.env->Dict.get("PG_URL") {
62
61
  | None => testSync("Postgres projection catch-up (skipped — set PG_URL)", () => expect(true)->toBe(true))
63
62
  | Some(url) =>
64
63
  let pool = ReventlessPostgres.PgDriver.makePool({connectionString: url})
@@ -64,23 +64,23 @@ describe("SqliteDriver", () => {
64
64
 
65
65
  testPromise("Backend.fromEnv parses sqlite path and reset flag", async () => {
66
66
  let originalEnv =
67
- Backend.processEnv->Dict.get("REVENTLESS_LOCAL_BACKEND")
67
+ NodeProcess.env->Dict.get("REVENTLESS_LOCAL_BACKEND")
68
68
 
69
- Backend.processEnv->Dict.set("REVENTLESS_LOCAL_BACKEND", "sqlite:./local.db")
69
+ NodeProcess.env->Dict.set("REVENTLESS_LOCAL_BACKEND", "sqlite:./local.db")
70
70
  let parsed = Backend.fromEnv()
71
71
  expect(parsed)->toEqual(Backend.Sqlite({path: "./local.db", resetOnStart: false}))
72
72
 
73
- Backend.processEnv->Dict.set("REVENTLESS_LOCAL_BACKEND", "sqlite:./local.db?reset")
73
+ NodeProcess.env->Dict.set("REVENTLESS_LOCAL_BACKEND", "sqlite:./local.db?reset")
74
74
  let parsedReset = Backend.fromEnv()
75
75
  expect(parsedReset)->toEqual(Backend.Sqlite({path: "./local.db", resetOnStart: true}))
76
76
 
77
- Backend.processEnv->Dict.set("REVENTLESS_LOCAL_BACKEND", "memory")
77
+ NodeProcess.env->Dict.set("REVENTLESS_LOCAL_BACKEND", "memory")
78
78
  let parsedMem = Backend.fromEnv()
79
79
  expect(parsedMem)->toEqual(Backend.Memory)
80
80
 
81
81
  switch originalEnv {
82
- | Some(v) => Backend.processEnv->Dict.set("REVENTLESS_LOCAL_BACKEND", v)
83
- | None => Backend.processEnv->Dict.delete("REVENTLESS_LOCAL_BACKEND")
82
+ | Some(v) => NodeProcess.env->Dict.set("REVENTLESS_LOCAL_BACKEND", v)
83
+ | None => NodeProcess.env->Dict.delete("REVENTLESS_LOCAL_BACKEND")
84
84
  }
85
85
  })
86
86
  })