@harperfast/harper-pro 5.0.0-beta.1 → 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 (84) hide show
  1. package/core/.github/workflows/integration-tests.yml +7 -5
  2. package/core/components/componentLoader.ts +1 -1
  3. package/core/integrationTests/README.md +6 -6
  4. package/core/integrationTests/deploy/deploy-from-github.test.ts +2 -2
  5. package/core/integrationTests/deploy/deploy-from-source.test.ts +2 -2
  6. package/core/integrationTests/server/crash-replay.test.ts +3 -3
  7. package/core/integrationTests/server/operation-user-rbac.test.ts +2 -2
  8. package/core/integrationTests/server/operations-server.test.ts +4 -2
  9. package/core/integrationTests/server/storage-reclamation.test.ts +2 -2
  10. package/core/integrationTests/server/thread-management.test.ts +2 -2
  11. package/core/integrationTests/utils/README.md +84 -32
  12. package/core/integrationTests/utils/harperLifecycle.ts +19 -23
  13. package/core/package.json +15 -14
  14. package/core/resources/ResourceInterface.ts +1 -1
  15. package/core/resources/Table.ts +9 -5
  16. package/core/resources/search.ts +1 -1
  17. package/core/security/jsLoader.ts +23 -6
  18. package/core/security/keys.js +7 -7
  19. package/core/server/threads/manageThreads.js +3 -1
  20. package/core/unitTests/components/fixtures/testJSWithDeps/child-dir/typestrip.ts +2 -0
  21. package/core/unitTests/components/fixtures/testJSWithDeps/resources.js +3 -0
  22. package/core/unitTests/components/globalIsolation.test.js +1 -0
  23. package/core/unitTests/resources/query.test.js +16 -1
  24. package/core/utility/lmdb/commonUtility.js +21 -10
  25. package/dist/core/components/componentLoader.js +1 -1
  26. package/dist/core/components/componentLoader.js.map +1 -1
  27. package/dist/core/resources/Table.js +8 -3
  28. package/dist/core/resources/Table.js.map +1 -1
  29. package/dist/core/resources/search.js +1 -1
  30. package/dist/core/resources/search.js.map +1 -1
  31. package/dist/core/security/jsLoader.js +22 -7
  32. package/dist/core/security/jsLoader.js.map +1 -1
  33. package/dist/core/security/keys.js +7 -7
  34. package/dist/core/security/keys.js.map +1 -1
  35. package/dist/core/server/threads/manageThreads.js +3 -1
  36. package/dist/core/server/threads/manageThreads.js.map +1 -1
  37. package/dist/core/utility/lmdb/commonUtility.js +20 -13
  38. package/dist/core/utility/lmdb/commonUtility.js.map +1 -1
  39. package/dist/licensing/usageLicensing.js.map +1 -1
  40. package/dist/replication/knownNodes.js +5 -37
  41. package/dist/replication/knownNodes.js.map +1 -1
  42. package/dist/replication/nodeIdMapping.js +2 -35
  43. package/dist/replication/nodeIdMapping.js.map +1 -1
  44. package/dist/replication/replicationConnection.js +6 -3
  45. package/dist/replication/replicationConnection.js.map +1 -1
  46. package/dist/replication/replicator.js +3 -2
  47. package/dist/replication/replicator.js.map +1 -1
  48. package/dist/replication/setNode.js +1 -1
  49. package/dist/replication/setNode.js.map +1 -1
  50. package/dist/security/certificate.js.map +1 -1
  51. package/licensing/usageLicensing.ts +3 -2
  52. package/npm-shrinkwrap.json +1165 -1027
  53. package/package.json +15 -14
  54. package/replication/knownNodes.ts +3 -2
  55. package/replication/nodeIdMapping.ts +1 -1
  56. package/replication/replicationConnection.ts +17 -5
  57. package/replication/replicator.ts +7 -2
  58. package/replication/setNode.ts +1 -1
  59. package/security/certificate.ts +2 -1
  60. package/studio/web/assets/{index-C-GXfcup.js → index-C1G-Jo6n.js} +2 -2
  61. package/studio/web/assets/{index-C-GXfcup.js.map → index-C1G-Jo6n.js.map} +1 -1
  62. package/studio/web/assets/{index-PFlNdimM.js → index-D-CahN0-.js} +2 -2
  63. package/studio/web/assets/{index-PFlNdimM.js.map → index-D-CahN0-.js.map} +1 -1
  64. package/studio/web/assets/{index-BTgXJX9d.js → index-DxlZI0PX.js} +5 -5
  65. package/studio/web/assets/{index-BTgXJX9d.js.map → index-DxlZI0PX.js.map} +1 -1
  66. package/studio/web/assets/{index.lazy-C3TJZJ4o.js → index.lazy-BUXDDqq9.js} +2 -2
  67. package/studio/web/assets/{index.lazy-C3TJZJ4o.js.map → index.lazy-BUXDDqq9.js.map} +1 -1
  68. package/studio/web/assets/{profiler-DotzgiCJ.js → profiler-CU93QiSW.js} +2 -2
  69. package/studio/web/assets/{profiler-DotzgiCJ.js.map → profiler-CU93QiSW.js.map} +1 -1
  70. package/studio/web/assets/{react-redux-VxUEx_mU.js → react-redux-B8k9Ep7e.js} +2 -2
  71. package/studio/web/assets/{react-redux-VxUEx_mU.js.map → react-redux-B8k9Ep7e.js.map} +1 -1
  72. package/studio/web/assets/{startRecording-B_9J9Csd.js → startRecording-DFeBXGk6.js} +2 -2
  73. package/studio/web/assets/{startRecording-B_9J9Csd.js.map → startRecording-DFeBXGk6.js.map} +1 -1
  74. package/studio/web/index.html +1 -1
  75. package/core/resources/ResourceInterfaceV2.ts +0 -53
  76. package/core/resources/ResourceV2.ts +0 -67
  77. package/core/v1.d.ts +0 -39
  78. package/core/v1.js +0 -41
  79. package/core/v2.d.ts +0 -39
  80. package/core/v2.js +0 -41
  81. package/dist/core/resources/ResourceInterfaceV2.js +0 -3
  82. package/dist/core/resources/ResourceInterfaceV2.js.map +0 -1
  83. package/dist/core/resources/ResourceV2.js +0 -27
  84. package/dist/core/resources/ResourceV2.js.map +0 -1
@@ -112,9 +112,10 @@ jobs:
112
112
  THREADS_DEBUG: false
113
113
  NODE_HOSTNAME: 'localhost'
114
114
  run: |
115
- node ./dist/bin/harper.js install
115
+ mkdir -p /tmp/hdb/log
116
+ node ./dist/bin/harper.js install > /tmp/hdb/log/install-stdout.log 2> /tmp/hdb/log/install-stderr.log
116
117
  sleep 10
117
- node ./dist/bin/harper.js start
118
+ node ./dist/bin/harper.js start > /tmp/hdb/log/start-stdout.log 2> /tmp/hdb/log/start-stderr.log &
118
119
  sleep 10
119
120
 
120
121
  - name: Run API Tests
@@ -125,12 +126,13 @@ jobs:
125
126
  run: npm run test:integration:api-tests
126
127
 
127
128
  - name: Upload Harper logs
128
- if: steps.run-api-tests.outcome == 'failure'
129
+ if: failure()
129
130
  uses: actions/upload-artifact@v7
130
131
  with:
131
132
  name: harper-integration-api-test-logs-node-${{ matrix.node-version }}
132
- path: /tmp/hdb/log/hdb.log
133
- retention-days: 2
133
+ path: /tmp/hdb/log/
134
+ retention-days: 3
135
+ if-no-files-found: ignore
134
136
 
135
137
  run-integration-tests:
136
138
  name: Integration Tests ${{matrix.shard}}/4 (Node.js v${{ matrix.node-version }})
@@ -494,7 +494,7 @@ export async function loadComponent(
494
494
  return loadComponentDirectories(); // return the promise
495
495
  });
496
496
  }
497
- if (config.extensionModule || config.pluginModule) {
497
+ if ((config.extensionModule || config.pluginModule) && (!isMainThread || config.runOnMainThread)) {
498
498
  const extensionModule = await scopedImport(
499
499
  join(componentDirectory, config.extensionModule || config.pluginModule),
500
500
  applicationScope
@@ -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;
@@ -112,9 +112,13 @@ interface RunHarperCommandOptions {
112
112
  */
113
113
  function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCommandOptions): Promise<ChildProcess> {
114
114
  const harperScript = getHarperScript();
115
- const proc = spawn('node', ['--trace-warnings', harperScript, ...args], {
116
- env: { ...process.env, ...env },
117
- });
115
+ const proc = spawn(
116
+ 'node',
117
+ ['--trace-warnings', '--force-node-api-uncaught-exceptions-policy=true', harperScript, ...args],
118
+ {
119
+ env: { ...process.env, ...env },
120
+ }
121
+ );
118
122
 
119
123
  let stdoutStream: WriteStream | undefined;
120
124
  let stderrStream: WriteStream | undefined;
@@ -156,12 +160,12 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
156
160
  proc.on('error', (error) => {
157
161
  reject(error);
158
162
  });
159
- proc.on('exit', (statusCode) => {
163
+ proc.on('exit', (statusCode, signal) => {
160
164
  clearTimeout(timer);
161
165
  if (statusCode === 0) {
162
166
  resolve(proc);
163
167
  } else {
164
- let errorMessage = `Harper process failed with exit code ${statusCode}`;
168
+ let errorMessage = `Harper process failed with exit code/signal ${statusCode ?? signal}`;
165
169
  stderrStream?.write(errorMessage);
166
170
  if (stderr) {
167
171
  errorMessage += `\n\nstderr:\n${stderr}`;
@@ -180,6 +184,10 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
180
184
  * This function performs installation, startup, and waits for Harper to be ready.
181
185
  * Always call `teardownHarper()` in the `after()` hook to clean up resources.
182
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
+ *
183
191
  * @param ctx - The test context to populate with Harper instance details
184
192
  * @param options - Optional configuration for the setup process
185
193
  * @returns The context with the `harper` property populated
@@ -188,7 +196,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
188
196
  * ```ts
189
197
  * suite('My tests', (ctx: ContextWithHarper) => {
190
198
  * before(async () => {
191
- * await setupHarper(ctx);
199
+ * await startHarper(ctx);
192
200
  * });
193
201
  *
194
202
  * after(async () => {
@@ -202,19 +210,7 @@ function runHarperCommand({ args, env, completionMessage, logDir }: RunHarperCom
202
210
  * });
203
211
  * ```
204
212
  */
205
- export async function setupHarper(ctx: ContextWithHarper, options?: SetupHarperOptions): Promise<ContextWithHarper> {
206
- return startHarper(ctx, options);
207
- }
208
-
209
- /**
210
- * Starts a Harper instance that has been installed.
211
- *
212
- * This is a lower-level function called by `setupHarper()`.
213
- * Most tests should use `setupHarper()` instead.
214
- *
215
- * @param ctx - The test context with Harper installation details
216
- */
217
- export async function startHarper(ctx: ContextWithHarper, options?: SetupHarperOptions): Promise<ContextWithHarper> {
213
+ export async function startHarper(ctx: ContextWithHarper, options?: StartHarperOptions): Promise<ContextWithHarper> {
218
214
  // Create a directory for this Harper installation
219
215
  // Use the system temp directory by default, or a custom parent directory if specified
220
216
  const installDirPrefix = join(
@@ -318,7 +314,7 @@ export async function killHarper(ctx: ContextWithHarper): Promise<void> {
318
314
  * ```ts
319
315
  * suite('My tests', (ctx: ContextWithHarper) => {
320
316
  * before(async () => {
321
- * await setupHarper(ctx);
317
+ * await startHarper(ctx);
322
318
  * });
323
319
  *
324
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,20 +144,20 @@
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
- "@harperfast/rocksdb-js": "^0.1.11",
160
+ "@harperfast/rocksdb-js": "^0.1.12",
161
161
  "@turf/area": "6.5.0",
162
162
  "@turf/boolean-contains": "6.5.0",
163
163
  "@turf/boolean-disjoint": "6.5.0",
@@ -168,6 +168,7 @@
168
168
  "@turf/helpers": "6.5.0",
169
169
  "@turf/length": "6.5.0",
170
170
  "alasql": "4.6.6",
171
+ "amaro": "^1.1.8",
171
172
  "argon2": "0.44.0",
172
173
  "asn1js": "3.0.7",
173
174
  "cbor-x": "1.6.4",
@@ -178,8 +179,8 @@
178
179
  "dotenv": "^16.4.7",
179
180
  "easy-ocsp": "1.2.2",
180
181
  "fast-glob": "3.3.3",
181
- "fastify": "~4.29.0",
182
- "fastify-plugin": "~4.5.1",
182
+ "fastify": "^5.8.2",
183
+ "fastify-plugin": "^5.1.0",
183
184
  "fs-extra": "11.3.3",
184
185
  "graphql": "^16.10.0",
185
186
  "graphql-http": "^1.22.4",
@@ -193,7 +194,7 @@
193
194
  "jsonata": "1.8.7",
194
195
  "jsonwebtoken": "9.0.3",
195
196
  "lmdb": "3.5.2",
196
- "lodash": "4.17.21",
197
+ "lodash": "^4.17.23",
197
198
  "mathjs": "11.12.0",
198
199
  "micromatch": "^4.0.8",
199
200
  "minimist": "1.2.8",
@@ -221,8 +222,8 @@
221
222
  "ses": "^1.14.0",
222
223
  "stream-chain": "2.2.5",
223
224
  "stream-json": "1.9.1",
224
- "systeminformation": "5.27.11",
225
- "tar-fs": "3.0.9",
225
+ "systeminformation": "^5.31.4",
226
+ "tar-fs": "^3.1.2",
226
227
  "ulidx": "0.5.0",
227
228
  "uuid": "11.1.0",
228
229
  "validate.js": "0.13.1",
@@ -30,7 +30,7 @@ export interface ResourceInterface<Record extends object = any>
30
30
  allowUpdate(user: User, record: Promise<Record & RecordObject>, context: Context): boolean | Promise<boolean>;
31
31
  put?(
32
32
  record: Record & RecordObject,
33
- target: RequestTargetOrId
33
+ target?: RequestTargetOrId
34
34
  ): void | (Record & Partial<RecordObject>) | Promise<void | (Record & Partial<RecordObject>)>;
35
35
  patch?(
36
36
  record: Partial<Record & RecordObject>,