@harperfast/harper-pro 5.0.0-beta.2 → 5.0.0-beta.3

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.
Files changed (33) hide show
  1. package/core/integrationTests/README.md +6 -6
  2. package/core/integrationTests/deploy/deploy-from-github.test.ts +2 -2
  3. package/core/integrationTests/deploy/deploy-from-source.test.ts +2 -2
  4. package/core/integrationTests/server/crash-replay.test.ts +3 -3
  5. package/core/integrationTests/server/operation-user-rbac.test.ts +2 -2
  6. package/core/integrationTests/server/operations-server.test.ts +4 -2
  7. package/core/integrationTests/server/storage-reclamation.test.ts +2 -2
  8. package/core/integrationTests/server/thread-management.test.ts +2 -2
  9. package/core/integrationTests/utils/README.md +84 -32
  10. package/core/integrationTests/utils/harperLifecycle.ts +10 -18
  11. package/core/package.json +13 -13
  12. package/core/server/threads/manageThreads.js +3 -1
  13. package/dist/cloneNode/cloneNode.js +3 -7
  14. package/dist/cloneNode/cloneNode.js.map +1 -1
  15. package/dist/core/server/threads/manageThreads.js +3 -1
  16. package/dist/core/server/threads/manageThreads.js.map +1 -1
  17. package/npm-shrinkwrap.json +1109 -982
  18. package/package.json +13 -13
  19. package/studio/web/assets/{index-CzghSAn2.js → index-C1G-Jo6n.js} +2 -2
  20. package/studio/web/assets/{index-CzghSAn2.js.map → index-C1G-Jo6n.js.map} +1 -1
  21. package/studio/web/assets/{index-CWN9Wp5V.js → index-D-CahN0-.js} +2 -2
  22. package/studio/web/assets/{index-CWN9Wp5V.js.map → index-D-CahN0-.js.map} +1 -1
  23. package/studio/web/assets/{index-DMDhGP7N.js → index-DxlZI0PX.js} +5 -5
  24. package/studio/web/assets/{index-DMDhGP7N.js.map → index-DxlZI0PX.js.map} +1 -1
  25. package/studio/web/assets/{index.lazy-C-yDTGUy.js → index.lazy-BUXDDqq9.js} +2 -2
  26. package/studio/web/assets/{index.lazy-C-yDTGUy.js.map → index.lazy-BUXDDqq9.js.map} +1 -1
  27. package/studio/web/assets/{profiler-0fZAOscv.js → profiler-CU93QiSW.js} +2 -2
  28. package/studio/web/assets/{profiler-0fZAOscv.js.map → profiler-CU93QiSW.js.map} +1 -1
  29. package/studio/web/assets/{react-redux-BIxqK8O6.js → react-redux-B8k9Ep7e.js} +2 -2
  30. package/studio/web/assets/{react-redux-BIxqK8O6.js.map → react-redux-B8k9Ep7e.js.map} +1 -1
  31. package/studio/web/assets/{startRecording-Ca3Gf2MY.js → startRecording-DFeBXGk6.js} +2 -2
  32. package/studio/web/assets/{startRecording-Ca3Gf2MY.js.map → startRecording-DFeBXGk6.js.map} +1 -1
  33. package/studio/web/index.html +1 -1
@@ -184,7 +184,7 @@ Integration test utilities are located in the [`integrationTests/utils/`](./util
184
184
 
185
185
  The most commonly used utilities are:
186
186
 
187
- - **`setupHarper(context)`** - Sets up a complete Harper instance for testing. Use in `before()` hooks.
187
+ - **`startHarper(context)`** - Sets up a complete Harper instance for testing. Use in `before()` hooks.
188
188
  - **`teardownHarper(context)`** - Tears down a Harper instance and cleans up resources. Use in `after()` hooks.
189
189
  - **`ContextWithHarper`** - TypeScript interface for test context with Harper instance details.
190
190
  - **`targz(dirPath)`** - Compresses a directory into a base64-encoded tar.gz string for application deployment.
@@ -193,11 +193,11 @@ The most commonly used utilities are:
193
193
 
194
194
  ```ts
195
195
  import { suite, test, before, after } from 'node:test';
196
- import { setupHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.mts';
196
+ import { startHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.ts';
197
197
 
198
198
  suite('test suite', (ctx: ContextWithHarper) => {
199
199
  before(async () => {
200
- await setupHarper(ctx);
200
+ await startHarper(ctx);
201
201
  });
202
202
 
203
203
  after(async () => {
@@ -227,12 +227,12 @@ Copy and paste the following content to get started:
227
227
  */
228
228
  import { suite, test, before, after } from 'node:test';
229
229
  import { strictEqual } from 'node:assert/strict';
230
- // Note: adjust the relative path accordingly (e.g., '../utils/harperLifecycle.mts')
231
- import { setupHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.mts';
230
+ // Note: adjust the relative path accordingly (e.g., '../utils/harperLifecycle.ts')
231
+ import { startHarper, teardownHarper, type ContextWithHarper } from './utils/harperLifecycle.ts';
232
232
 
233
233
  suite('short description of tests', (ctx: ContextWithHarper) => {
234
234
  before(async () => {
235
- await setupHarper(ctx);
235
+ await startHarper(ctx);
236
236
  });
237
237
 
238
238
  after(async () => {
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { suite, test, before, after } from 'node:test';
7
7
  import { deepStrictEqual, ok, strictEqual } from 'node:assert/strict';
8
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
8
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
9
9
  import { join } from 'node:path';
10
10
  import { existsSync } from 'node:fs';
11
11
  import { readFile } from 'node:fs/promises';
@@ -13,7 +13,7 @@ import { parse } from 'yaml';
13
13
 
14
14
  suite('GitHub application deployment', (ctx: ContextWithHarper) => {
15
15
  before(async () => {
16
- await setupHarper(ctx);
16
+ await startHarper(ctx);
17
17
  });
18
18
 
19
19
  after(async () => {
@@ -12,12 +12,12 @@ import { join } from 'node:path';
12
12
  import { existsSync } from 'node:fs';
13
13
  import { setTimeout as sleep } from 'node:timers/promises';
14
14
 
15
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
15
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
16
16
  import { targz } from '../utils/targz.ts';
17
17
 
18
18
  suite('Local application deployment', (ctx: ContextWithHarper) => {
19
19
  before(async () => {
20
- await setupHarper(ctx);
20
+ await startHarper(ctx);
21
21
  });
22
22
 
23
23
  after(async () => {
@@ -3,12 +3,12 @@
3
3
  * database, so replay needs to work for harper to startup.
4
4
  */
5
5
  import { suite, test, before, after } from 'node:test';
6
- import { setupHarper, teardownHarper, type ContextWithHarper, startHarper } from '../utils/harperLifecycle.ts';
6
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
7
7
  import { equal } from 'node:assert';
8
8
 
9
9
  suite('Transaction log replay on crash', (ctx: ContextWithHarper) => {
10
10
  before(async () => {
11
- await setupHarper(ctx, {
11
+ await startHarper(ctx, {
12
12
  config: {},
13
13
  env: {
14
14
  HARPER_NO_FLUSH_ON_EXIT: true, // specifically don't flush, we are testing restart/replay and simulating a crash
@@ -28,7 +28,7 @@ suite('Transaction log replay on crash', (ctx: ContextWithHarper) => {
28
28
  await startHarper(ctx);
29
29
  let response = await sendOperation(ctx.harper, {
30
30
  operation: 'list_roles',
31
- authorization: ctx.admin,
31
+ authorization: ctx.harper.admin,
32
32
  });
33
33
  equal(response.length, 1);
34
34
  equal(response[0].role, 'super_user');
@@ -11,7 +11,7 @@
11
11
  import { suite, test, before, after } from 'node:test';
12
12
  import { strictEqual, ok } from 'node:assert/strict';
13
13
 
14
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
14
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
15
15
 
16
16
  const DATABASE = 'test_db';
17
17
  const TABLE = 'dogs';
@@ -35,7 +35,7 @@ const STANDARD_USER_PASS = 'Test1234!';
35
35
 
36
36
  suite('operations RBAC', (ctx: ContextWithHarper) => {
37
37
  before(async () => {
38
- await setupHarper(ctx, { config: {}, env: {} });
38
+ await startHarper(ctx, { config: {}, env: {} });
39
39
 
40
40
  const adminAuth = `Basic ${Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64')}`;
41
41
 
@@ -12,11 +12,11 @@ import { ok, strictEqual } from 'node:assert/strict';
12
12
  import { pack, unpack } from 'msgpackr';
13
13
  import { encode, decode } from 'cbor-x';
14
14
 
15
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
15
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
16
16
 
17
17
  suite('Operations Server', (ctx: ContextWithHarper) => {
18
18
  before(async () => {
19
- await setupHarper(ctx, { config: {}, env: {} });
19
+ await startHarper(ctx, { config: {}, env: {} });
20
20
  });
21
21
 
22
22
  after(async () => {
@@ -95,6 +95,7 @@ suite('Operations Server', (ctx: ContextWithHarper) => {
95
95
  'Accept': 'application/json',
96
96
  'Authorization': `Basic ${Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64')}`,
97
97
  },
98
+ // @ts-expect-error - Need to update fetch types to support `Buffer<ArrayBufferLike>`
98
99
  body: pack({ operation: 'describe_all' }),
99
100
  });
100
101
  strictEqual(response.status, 200);
@@ -140,6 +141,7 @@ suite('Operations Server', (ctx: ContextWithHarper) => {
140
141
  'Accept': 'application/json',
141
142
  'Authorization': `Basic ${Buffer.from(`${ctx.harper.admin.username}:${ctx.harper.admin.password}`).toString('base64')}`,
142
143
  },
144
+ // @ts-expect-error - Need to update fetch types to support `Buffer<ArrayBufferLike>`
143
145
  body: encode({ operation: 'describe_all' }),
144
146
  });
145
147
  strictEqual(response.status, 200);
@@ -20,7 +20,7 @@ import { suite, test, before, after } from 'node:test';
20
20
  import { ok, strictEqual } from 'node:assert/strict';
21
21
  import { setTimeout as sleep } from 'node:timers/promises';
22
22
 
23
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
23
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
24
24
 
25
25
  const TEST_DATABASE = 'test';
26
26
  const TEST_TABLE = 'reclaim';
@@ -29,7 +29,7 @@ suite('Storage reclamation', (ctx: ContextWithHarper) => {
29
29
  before(async () => {
30
30
  // Set a very high reclamation threshold (99%) so reclamation triggers immediately
31
31
  // and a short interval (1 second) for faster test execution
32
- await setupHarper(ctx, {
32
+ await startHarper(ctx, {
33
33
  config: {
34
34
  STORAGE_RECLAMATION_THRESHOLD: 0.99,
35
35
  STORAGE_RECLAMATION_INTERVAL: '1s',
@@ -8,11 +8,11 @@
8
8
  import { suite, test, before, after } from 'node:test';
9
9
  import { strictEqual } from 'node:assert/strict';
10
10
 
11
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
11
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
12
12
 
13
13
  suite('Thread Management', (ctx: ContextWithHarper) => {
14
14
  before(async () => {
15
- await setupHarper(ctx, { config: {}, env: {} });
15
+ await startHarper(ctx, { config: {}, env: {} });
16
16
  });
17
17
 
18
18
  after(async () => {
@@ -4,26 +4,41 @@ This directory contains utility functions and modules for Harper integration tes
4
4
 
5
5
  ## Table of Contents
6
6
 
7
- - [Harper Lifecycle Management](#harper-lifecycle-management)
8
- - [Loopback Address Pool](#loopback-address-pool)
9
- - [Compression Utilities](#compression-utilities)
7
+ - [Integration Test Utilities](#integration-test-utilities)
8
+ - [Table of Contents](#table-of-contents)
9
+ - [Harper Lifecycle Management](#harper-lifecycle-management)
10
+ - [`startHarper(context, options?): Promise<ContextWithHarper>`](#startharpercontext-options-promisecontextwithharper)
11
+ - [`StartHarperOptions`](#startharperoptions)
12
+ - [`killHarper(context): Promise<void>`](#killharpercontext-promisevoid)
13
+ - [`teardownHarper(context): Promise<void>`](#teardownharpercontext-promisevoid)
14
+ - [`ContextWithHarper`](#contextwithharper)
15
+ - [Loopback Address Pool](#loopback-address-pool)
16
+ - [`validateLoopbackAddressPool(): Promise<ValidationResult>`](#validateloopbackaddresspool-promisevalidationresult)
17
+ - [`getNextAvailableLoopbackAddress(): Promise<string>`](#getnextavailableloopbackaddress-promisestring)
18
+ - [`releaseLoopbackAddress(address: string): Promise<void>`](#releaseloopbackaddressaddress-string-promisevoid)
19
+ - [`releaseAllLoopbackAddressesForCurrentProcess(): Promise<void>`](#releaseallloopbackaddressesforcurrentprocess-promisevoid)
20
+ - [Compression Utilities](#compression-utilities)
21
+ - [`targz(dirPath: string): Promise<string>`](#targzdirpath-string-promisestring)
22
+ - [Scripts](#scripts)
23
+ - [`scripts/setup-loopback.sh`](#scriptssetup-loopbacksh)
24
+ - [`scripts/run.mts`](#scriptsrunmts)
10
25
 
11
26
  ---
12
27
 
13
28
  ## Harper Lifecycle Management
14
29
 
15
- **Module:** [`harperLifecycle.mts`](./harperLifecycle.mts)
30
+ **Module:** [`harperLifecycle.ts`](./harperLifecycle.ts)
16
31
 
17
32
  Provides functions for managing Harper instances during integration tests, including installation, startup, and teardown.
18
33
 
19
- ### `setupHarper(context, options?): Promise<ContextWithHarper>`
34
+ ### `startHarper(context, options?): Promise<ContextWithHarper>`
20
35
 
21
36
  Sets up a complete Harper instance for testing.
22
37
 
23
38
  **Parameters:**
24
39
 
25
40
  - `context` - [`ContextWithHarper`](#contextwithharper) - The test context object
26
- - `options` - [`SetupHarperOptions`](#setupharperoptions) (optional) - Configuration options for the setup process
41
+ - `options` - [`StartHarperOptions`](#startharperoptions) (optional) - Configuration options for the setup process
27
42
 
28
43
  **Returns:** `Promise<ContextWithHarper>` - The context with the `harper` property populated
29
44
 
@@ -31,8 +46,8 @@ Sets up a complete Harper instance for testing.
31
46
 
32
47
  This method should be used in the `before()` lifecycle hook for a test suite. It performs the following steps:
33
48
 
34
- 1. Creates a Harper instance in a temporary directory
35
- 2. Assigns a unique loopback address from the loopback address pool
49
+ 1. Creates a Harper instance in a temporary directory (reuses `ctx.harper.installDir` if already set)
50
+ 2. Assigns a unique loopback address from the loopback address pool (reuses `ctx.harper.hostname` if already set)
36
51
  3. Starts Harper with test configuration (which self-installs)
37
52
  4. Waits for Harper to be fully started, waiting for the startup message to appear in stdout
38
53
  5. Populates the `context.harper` object with connection details
@@ -43,12 +58,12 @@ This method should be used in the `before()` lifecycle hook for a test suite. It
43
58
 
44
59
  ```ts
45
60
  import { suite, test, before, after } from 'node:test';
46
- import { setupHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.mts';
61
+ import { startHarper, teardownHarper, type ContextWithHarper } from '../utils/harperLifecycle.ts';
47
62
 
48
63
  // Default setup
49
64
  suite('My test suite', (ctx: ContextWithHarper) => {
50
65
  before(async () => {
51
- await setupHarper(ctx);
66
+ await startHarper(ctx);
52
67
  });
53
68
 
54
69
  after(async () => {
@@ -64,12 +79,12 @@ suite('My test suite', (ctx: ContextWithHarper) => {
64
79
 
65
80
  ---
66
81
 
67
- ### `SetupHarperOptions`
82
+ ### `StartHarperOptions`
68
83
 
69
- Configuration options for `setupHarper()`.
84
+ Configuration options for `startHarper()`.
70
85
 
71
86
  ```typescript
72
- export interface SetupHarperOptions {
87
+ export interface StartHarperOptions {
73
88
  startupTimeoutMs?: number;
74
89
  config: any;
75
90
  env: any;
@@ -78,13 +93,44 @@ export interface SetupHarperOptions {
78
93
 
79
94
  **Properties:**
80
95
 
81
- - **`config`** - `object` (optional) - Additional configuration options to pass to the Harper CLI.
82
- - **`env`** - `object` (optional) - Additional environment variables to set when starting Harper.
96
+ - **`config`** - `object` - Additional configuration options to pass to the Harper CLI.
97
+ - **`env`** - `object` - Additional environment variables to set when starting Harper.
83
98
  - **`startupTimeoutMs`** - `number` (optional) - Timeout in milliseconds to wait for Harper to start. Defaults to 30000, or the value of the `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` environment variable if set.
84
99
 
85
100
  **Environment Variables:**
86
101
 
87
- - `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` - Sets the default startup delay for all tests when `startupTimeoutMs` is not explicitly provided
102
+ - `HARPER_INTEGRATION_TEST_STARTUP_TIMEOUT_MS` - Sets the default startup timeout for all tests when `startupTimeoutMs` is not explicitly provided
103
+ - `HARPER_INTEGRATION_TEST_INSTALL_PARENT_DIR` - Override the parent directory for Harper installation directories (defaults to the OS temp directory)
104
+ - `HARPER_INTEGRATION_TEST_INSTALL_SCRIPT` - Override the path to the Harper CLI script (defaults to `dist/bin/harper.js` relative to the repo root)
105
+ - `HARPER_INTEGRATION_TEST_LOG_DIR` - When set, stdout/stderr logs and Harper's `hdb.log` are written to per-suite subdirectories here; logs are deleted automatically on successful exit and retained on failure
106
+
107
+ ---
108
+
109
+ ### `killHarper(context): Promise<void>`
110
+
111
+ Kills the running Harper process. Does **not** release the loopback address or remove the installation directory.
112
+
113
+ **Parameters:**
114
+
115
+ - `context` - [`ContextWithHarper`](#contextwithharper) - The test context with a running Harper instance
116
+
117
+ **Returns:** `Promise<void>`
118
+
119
+ **Description:**
120
+
121
+ Sends `SIGTERM` to the Harper process and waits for it to exit. If the process does not exit within 200ms, `SIGKILL` is sent.
122
+
123
+ This is useful for testing Harper restart/crash scenarios. After calling `killHarper()`, call `startHarper()` to restart the instance in the same directory with the same loopback address.
124
+
125
+ **Example:**
126
+
127
+ ```ts
128
+ test('recovers after restart', async () => {
129
+ await killHarper(ctx);
130
+ await startHarper(ctx);
131
+ // Harper is running again on the same address
132
+ });
133
+ ```
88
134
 
89
135
  ---
90
136
 
@@ -100,7 +146,7 @@ Tears down a Harper instance and cleans up all resources.
100
146
 
101
147
  **Description:**
102
148
 
103
- This method should be used in the `after()` lifecycle hook in conjunction with `setupHarper()` and `before()`. It performs the following cleanup steps:
149
+ This method should be used in the `after()` lifecycle hook in conjunction with `startHarper()` and `before()`. It performs the following cleanup steps:
104
150
 
105
151
  1. Stops the Harper instance
106
152
  2. Releases the loopback address back to the pool
@@ -111,7 +157,7 @@ This method should be used in the `after()` lifecycle hook in conjunction with `
111
157
  ```ts
112
158
  suite('My test suite', (ctx: ContextWithHarper) => {
113
159
  before(async () => {
114
- await setupHarper(ctx);
160
+ await startHarper(ctx);
115
161
  });
116
162
 
117
163
  after(async () => {
@@ -131,23 +177,27 @@ TypeScript interface that extends `SuiteContext` and `TestContext` from Node.js
131
177
  **Interface Definition:**
132
178
 
133
179
  ```typescript
134
- interface ContextWithHarper extends SuiteContext, TestContext {
135
- harper: {
136
- installDir: string;
137
- admin: {
138
- username: string;
139
- password: string;
140
- };
141
- httpURL: string;
142
- operationsAPIURL: string;
143
- loopbackAddress: string;
180
+ export interface HarperContext {
181
+ installDir: string;
182
+ admin: {
183
+ username: string;
184
+ password: string;
144
185
  };
186
+ httpURL: string;
187
+ operationsAPIURL: string;
188
+ hostname: string;
189
+ process: ChildProcess;
190
+ logDir?: string;
191
+ }
192
+
193
+ export interface ContextWithHarper extends SuiteContext, TestContext {
194
+ harper: HarperContext;
145
195
  }
146
196
  ```
147
197
 
148
198
  **Properties:**
149
199
 
150
- - **`harper`** - `object` - The Harper instance details
200
+ - **`harper`** - `HarperContext` - The Harper instance details
151
201
  - **`installDir`** - `string` - The absolute path to the Harper installation directory
152
202
  - **`admin`** - `object` - Admin credentials
153
203
  - **`username`** - `string` - The Harper Admin Username (default: `'admin'`)
@@ -155,6 +205,8 @@ interface ContextWithHarper extends SuiteContext, TestContext {
155
205
  - **`httpURL`** - `string` - The HTTP URL for the Harper instance (e.g., `'http://127.0.0.2:9926'`)
156
206
  - **`operationsAPIURL`** - `string` - The Operations API URL (e.g., `'http://127.0.0.2:9925'`)
157
207
  - **`hostname`** - `string` - The assigned loopback IP address (e.g., `'127.0.0.2'`)
208
+ - **`process`** - `ChildProcess` - The Node.js child process handle for the running Harper instance
209
+ - **`logDir`** - `string | undefined` - Absolute path to the per-suite log directory; only set when `HARPER_INTEGRATION_TEST_LOG_DIR` is configured
158
210
 
159
211
  **Example Usage:**
160
212
 
@@ -175,7 +227,7 @@ test('authenticate with admin credentials', async () => {
175
227
 
176
228
  ## Loopback Address Pool
177
229
 
178
- **Module:** [`loopbackAddressPool.mts`](./loopbackAddressPool.mts)
230
+ **Module:** [`loopbackAddressPool.ts`](./loopbackAddressPool.ts)
179
231
 
180
232
  Manages a pool of loopback addresses for concurrent test execution. This allows multiple Harper instances to run simultaneously on different loopback addresses without port conflicts.
181
233
 
@@ -227,7 +279,7 @@ If no addresses are available, the function waits and retries until one becomes
227
279
  **Pool file location:** `${tmpdir()}/harper-integration-test-loopback-pool.json`
228
280
  **Lock file location:** `${tmpdir()}/harper-integration-test-loopback-pool.lock`
229
281
 
230
- **Note:** This is automatically called by `setupHarper()`. You typically don't need to call this directly unless you're implementing custom test infrastructure.
282
+ **Note:** This is automatically called by `startHarper()`. You typically don't need to call this directly unless you're implementing custom test infrastructure.
231
283
 
232
284
  ---
233
285
 
@@ -285,7 +337,7 @@ try {
285
337
 
286
338
  ## Compression Utilities
287
339
 
288
- **Module:** [`targz.mts`](./targz.mts)
340
+ **Module:** [`targz.ts`](./targz.ts)
289
341
 
290
342
  Provides utilities for compressing directories into tar.gz archives.
291
343
 
@@ -18,7 +18,7 @@ const LOG_DIR = process.env.HARPER_INTEGRATION_TEST_LOG_DIR;
18
18
  /**
19
19
  * Options for setting up a Harper instance.
20
20
  */
21
- export interface SetupHarperOptions {
21
+ export interface StartHarperOptions {
22
22
  /**
23
23
  * Timeout in milliseconds to wait for Harper to start.
24
24
  * @default 30000
@@ -59,8 +59,8 @@ export interface HarperContext {
59
59
  /**
60
60
  * Test context interface with Harper instance details.
61
61
  *
62
- * This interface is populated by `setupHarper()` and contains all necessary
63
- * information to interact with the test Harper instance.
62
+ * This interface is populated by `startHarper()` and contains
63
+ * all necessary information to interact with the test Harper instance.
64
64
  */
65
65
  export interface ContextWithHarper extends SuiteContext, TestContext {
66
66
  harper: HarperContext;
@@ -184,6 +184,10 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
184
184
  * This function performs installation, startup, and waits for Harper to be ready.
185
185
  * Always call `teardownHarper()` in the `after()` hook to clean up resources.
186
186
  *
187
+ * If `ctx.harper.installDir` or `ctx.harper.hostname` are already set they are
188
+ * reused rather than creating new ones — making this safe to call after
189
+ * `killHarper()` to restart an existing instance.
190
+ *
187
191
  * @param ctx - The test context to populate with Harper instance details
188
192
  * @param options - Optional configuration for the setup process
189
193
  * @returns The context with the `harper` property populated
@@ -192,7 +196,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
192
196
  * ```ts
193
197
  * suite('My tests', (ctx: ContextWithHarper) => {
194
198
  * before(async () => {
195
- * await setupHarper(ctx);
199
+ * await startHarper(ctx);
196
200
  * });
197
201
  *
198
202
  * after(async () => {
@@ -206,19 +210,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
206
210
  * });
207
211
  * ```
208
212
  */
209
- export async function setupHarper(ctx: ContextWithHarper, options?: SetupHarperOptions): Promise<ContextWithHarper> {
210
- return startHarper(ctx, options);
211
- }
212
-
213
- /**
214
- * Starts a Harper instance that has been installed.
215
- *
216
- * This is a lower-level function called by `setupHarper()`.
217
- * Most tests should use `setupHarper()` instead.
218
- *
219
- * @param ctx - The test context with Harper installation details
220
- */
221
- export async function startHarper(ctx: ContextWithHarper, options?: SetupHarperOptions): Promise<ContextWithHarper> {
213
+ export async function startHarper(ctx: ContextWithHarper, options?: StartHarperOptions): Promise<ContextWithHarper> {
222
214
  // Create a directory for this Harper installation
223
215
  // Use the system temp directory by default, or a custom parent directory if specified
224
216
  const installDirPrefix = join(
@@ -322,7 +314,7 @@ export async function killHarper(ctx: ContextWithHarper): Promise<void> {
322
314
  * ```ts
323
315
  * suite('My tests', (ctx: ContextWithHarper) => {
324
316
  * before(async () => {
325
- * await setupHarper(ctx);
317
+ * await startHarper(ctx);
326
318
  * });
327
319
  *
328
320
  * after(async () => {
package/core/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "harper",
3
3
  "description": "Harper is an open-source Node.js performance platform that unifies database, cache, application, and messaging layers into one in-memory process.",
4
- "version": "5.0.0-beta.1",
4
+ "version": "5.0.0-beta.3",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://harper.fast",
7
7
  "bugs": {
@@ -133,7 +133,7 @@
133
133
  "globals": "^16.5.0",
134
134
  "intercept-stdout": "0.1.2",
135
135
  "mkcert": "^3.2.0",
136
- "mocha": "^11.7.4",
136
+ "mocha": "^11.7.5",
137
137
  "mqtt": "~4.3.8",
138
138
  "oxlint": "^1.31.0",
139
139
  "prettier": "~3.7.3",
@@ -144,18 +144,18 @@
144
144
  "tsx": "^4.20.6",
145
145
  "typescript": "^5.8.2",
146
146
  "typescript-eslint": "^8.45.0",
147
- "undici": "^7.16.0",
147
+ "undici": "^7.24.4",
148
148
  "why-is-node-still-running": "^1.0.0"
149
149
  },
150
150
  "dependencies": {
151
- "@aws-sdk/client-s3": "3.964.0",
151
+ "@aws-sdk/client-s3": "^3.1012.0",
152
152
  "@aws-sdk/lib-storage": "3.964.0",
153
153
  "@datadog/pprof": "^5.11.1",
154
154
  "@endo/static-module-record": "^1.1.2",
155
- "@fastify/autoload": "5.10.0",
156
- "@fastify/compress": "~6.5.0",
157
- "@fastify/cors": "~9.0.1",
158
- "@fastify/static": "~7.0.4",
155
+ "@fastify/autoload": "^6.3.1",
156
+ "@fastify/compress": "^8.3.1",
157
+ "@fastify/cors": "^11.2.0",
158
+ "@fastify/static": "^9.0.0",
159
159
  "@harperfast/extended-iterable": "^1.0.1",
160
160
  "@harperfast/rocksdb-js": "^0.1.12",
161
161
  "@turf/area": "6.5.0",
@@ -179,8 +179,8 @@
179
179
  "dotenv": "^16.4.7",
180
180
  "easy-ocsp": "1.2.2",
181
181
  "fast-glob": "3.3.3",
182
- "fastify": "~4.29.0",
183
- "fastify-plugin": "~4.5.1",
182
+ "fastify": "^5.8.2",
183
+ "fastify-plugin": "^5.1.0",
184
184
  "fs-extra": "11.3.3",
185
185
  "graphql": "^16.10.0",
186
186
  "graphql-http": "^1.22.4",
@@ -194,7 +194,7 @@
194
194
  "jsonata": "1.8.7",
195
195
  "jsonwebtoken": "9.0.3",
196
196
  "lmdb": "3.5.2",
197
- "lodash": "4.17.21",
197
+ "lodash": "^4.17.23",
198
198
  "mathjs": "11.12.0",
199
199
  "micromatch": "^4.0.8",
200
200
  "minimist": "1.2.8",
@@ -222,8 +222,8 @@
222
222
  "ses": "^1.14.0",
223
223
  "stream-chain": "2.2.5",
224
224
  "stream-json": "1.9.1",
225
- "systeminformation": "5.27.11",
226
- "tar-fs": "3.0.9",
225
+ "systeminformation": "^5.31.4",
226
+ "tar-fs": "^3.1.2",
227
227
  "ulidx": "0.5.0",
228
228
  "uuid": "11.1.0",
229
229
  "validate.js": "0.13.1",
@@ -339,7 +339,9 @@ function onMessageByType(type, listener) {
339
339
  listeners.push(listener);
340
340
  if (messagesQueuedByType.has(type)) {
341
341
  for (let message of messagesQueuedByType.get(type)) {
342
- listener(message);
342
+ // enqueue in next event turn; messages always come as events, and trying to do this synchronously can be
343
+ // problematic for getting mixed up with module loading
344
+ setImmediate(() => listener(message));
343
345
  }
344
346
  messagesQueuedByType.delete(type);
345
347
  }
@@ -214,9 +214,6 @@ async function cloneNode() {
214
214
  if (freshClone || !systemExists) {
215
215
  await installHarper();
216
216
  }
217
- // Start Harper to prepare for clone operations
218
- const { main } = await import('../core/bin/run.js');
219
- await main();
220
217
  logger.initLogSettings();
221
218
  harperLogger = logger.loggerWithTag('cloneNode');
222
219
  // Get the config from the leader and write it to the existing local config file, excluding any parameters that should not be cloned
@@ -241,10 +238,9 @@ async function cloneNode() {
241
238
  await databases.system.hdb_user.delete({ username: 'clone-temp-admin' });
242
239
  }
243
240
  }
244
- // Restarting workers to ensure new configuration it loaded.
245
- log('Restarting workers to apply new configuration');
246
- const { restartWorkers } = await import('../core/server/threads/manageThreads.js');
247
- await restartWorkers();
241
+ // Start Harper to prepare for clone operations
242
+ const { main } = await import('../core/bin/run.js');
243
+ await main();
248
244
  // Dynamically importing setNode because it was causing early usage of rootpath var install before it was initialized.
249
245
  const { setNode } = await import('../replication/setNode.js');
250
246
  // Set node will set up replication between this node and the leader,