@harperfast/harper-pro 5.0.4 → 5.0.6

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 (120) hide show
  1. package/core/AGENTS.md +107 -0
  2. package/core/CLAUDE.md +1 -0
  3. package/core/bin/status.js +2 -2
  4. package/core/bin/stop.js +5 -6
  5. package/core/components/EntryHandler.ts +4 -2
  6. package/core/components/Scope.ts +1 -1
  7. package/core/components/componentLoader.ts +11 -4
  8. package/core/components/requestRestart.ts +17 -2
  9. package/core/dataLayer/harperBridge/TableSizeObject.ts +35 -0
  10. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.ts +24 -0
  11. package/core/package-lock.json +1043 -112
  12. package/core/resources/DatabaseTransaction.ts +8 -3
  13. package/core/resources/Table.ts +12 -17
  14. package/core/resources/databases.ts +2 -2
  15. package/core/resources/graphql.ts +163 -165
  16. package/core/resources/indexes/HierarchicalNavigableSmallWorld.ts +14 -3
  17. package/core/resources/indexes/vector.ts +17 -0
  18. package/core/resources/loadEnv.ts +20 -16
  19. package/core/resources/login.ts +4 -3
  20. package/core/resources/roles.ts +60 -65
  21. package/core/security/auth.ts +15 -14
  22. package/core/security/jsLoader.ts +27 -9
  23. package/core/security/keys.js +1 -1
  24. package/core/server/REST.ts +10 -11
  25. package/core/server/fastifyRoutes.ts +30 -29
  26. package/core/server/graphqlQuerying.ts +4 -3
  27. package/core/server/http.ts +175 -1
  28. package/core/server/mqtt.ts +8 -2
  29. package/core/server/serverHelpers/serverUtilities.ts +2 -5
  30. package/core/server/threads/threadServer.js +30 -2
  31. package/core/server/throttle.ts +18 -0
  32. package/core/utility/environment/environmentManager.js +10 -4
  33. package/core/utility/environment/systemInformation.ts +698 -0
  34. package/core/utility/hdbTerms.ts +1 -0
  35. package/core/utility/operation_authorization.js +2 -5
  36. package/dist/core/bin/status.js +2 -2
  37. package/dist/core/bin/status.js.map +1 -1
  38. package/dist/core/bin/stop.js +5 -5
  39. package/dist/core/bin/stop.js.map +1 -1
  40. package/dist/core/components/EntryHandler.js +4 -2
  41. package/dist/core/components/EntryHandler.js.map +1 -1
  42. package/dist/core/components/Scope.js +1 -1
  43. package/dist/core/components/Scope.js.map +1 -1
  44. package/dist/core/components/componentLoader.js +11 -3
  45. package/dist/core/components/componentLoader.js.map +1 -1
  46. package/dist/core/components/requestRestart.js +12 -1
  47. package/dist/core/components/requestRestart.js.map +1 -1
  48. package/dist/core/dataLayer/harperBridge/TableSizeObject.js +32 -0
  49. package/dist/core/dataLayer/harperBridge/TableSizeObject.js.map +1 -0
  50. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.js +18 -19
  51. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.js.map +1 -1
  52. package/dist/core/resources/DatabaseTransaction.js +6 -1
  53. package/dist/core/resources/DatabaseTransaction.js.map +1 -1
  54. package/dist/core/resources/Table.js +14 -18
  55. package/dist/core/resources/Table.js.map +1 -1
  56. package/dist/core/resources/databases.js +2 -1
  57. package/dist/core/resources/databases.js.map +1 -1
  58. package/dist/core/resources/graphql.js +176 -176
  59. package/dist/core/resources/graphql.js.map +1 -1
  60. package/dist/core/resources/indexes/HierarchicalNavigableSmallWorld.js +14 -2
  61. package/dist/core/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  62. package/dist/core/resources/indexes/vector.js +14 -0
  63. package/dist/core/resources/indexes/vector.js.map +1 -1
  64. package/dist/core/resources/loadEnv.js +20 -17
  65. package/dist/core/resources/loadEnv.js.map +1 -1
  66. package/dist/core/resources/login.js +4 -4
  67. package/dist/core/resources/login.js.map +1 -1
  68. package/dist/core/resources/roles.js +64 -68
  69. package/dist/core/resources/roles.js.map +1 -1
  70. package/dist/core/security/auth.js +17 -15
  71. package/dist/core/security/auth.js.map +1 -1
  72. package/dist/core/security/jsLoader.js +29 -9
  73. package/dist/core/security/jsLoader.js.map +1 -1
  74. package/dist/core/security/keys.js +1 -1
  75. package/dist/core/server/REST.js +11 -11
  76. package/dist/core/server/REST.js.map +1 -1
  77. package/dist/core/server/fastifyRoutes.js +30 -29
  78. package/dist/core/server/fastifyRoutes.js.map +1 -1
  79. package/dist/core/server/graphqlQuerying.js +5 -4
  80. package/dist/core/server/graphqlQuerying.js.map +1 -1
  81. package/dist/core/server/http.js +179 -0
  82. package/dist/core/server/http.js.map +1 -1
  83. package/dist/core/server/mqtt.js +5 -3
  84. package/dist/core/server/mqtt.js.map +1 -1
  85. package/dist/core/server/serverHelpers/serverUtilities.js +2 -2
  86. package/dist/core/server/serverHelpers/serverUtilities.js.map +1 -1
  87. package/dist/core/server/threads/threadServer.js +26 -2
  88. package/dist/core/server/threads/threadServer.js.map +1 -1
  89. package/dist/core/server/throttle.js +17 -0
  90. package/dist/core/server/throttle.js.map +1 -1
  91. package/dist/core/utility/environment/environmentManager.js +9 -4
  92. package/dist/core/utility/environment/environmentManager.js.map +1 -1
  93. package/dist/core/utility/environment/systemInformation.js +359 -219
  94. package/dist/core/utility/environment/systemInformation.js.map +1 -1
  95. package/dist/core/utility/hdbTerms.js +1 -0
  96. package/dist/core/utility/hdbTerms.js.map +1 -1
  97. package/dist/core/utility/operation_authorization.js +2 -2
  98. package/dist/core/utility/operation_authorization.js.map +1 -1
  99. package/dist/licensing/usageLicensing.js +1 -1
  100. package/dist/licensing/usageLicensing.js.map +1 -1
  101. package/dist/replication/replicationConnection.js +1 -0
  102. package/dist/replication/replicationConnection.js.map +1 -1
  103. package/dist/replication/setNode.js +1 -1
  104. package/dist/replication/setNode.js.map +1 -1
  105. package/dist/replication/subscriptionManager.js +1 -0
  106. package/dist/replication/subscriptionManager.js.map +1 -1
  107. package/licensing/usageLicensing.ts +1 -1
  108. package/npm-shrinkwrap.json +1880 -891
  109. package/package.json +14 -13
  110. package/replication/replicationConnection.ts +1 -0
  111. package/replication/setNode.ts +1 -1
  112. package/replication/subscriptionManager.ts +1 -0
  113. package/studio/web/assets/{index-CjeZNBFc.js → index-qbLPhOzw.js} +2 -2
  114. package/studio/web/assets/{index-CjeZNBFc.js.map → index-qbLPhOzw.js.map} +1 -1
  115. package/studio/web/index.html +1 -1
  116. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/TableSizeObject.js +0 -25
  117. package/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/lmdbGetTableSize.js +0 -34
  118. package/core/utility/environment/systemInformation.js +0 -355
  119. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/TableSizeObject.js +0 -24
  120. package/dist/core/dataLayer/harperBridge/lmdbBridge/lmdbUtility/TableSizeObject.js.map +0 -1
package/core/AGENTS.md ADDED
@@ -0,0 +1,107 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance when working with code in this repository.
4
+
5
+ ---
6
+
7
+ ## What This Is
8
+
9
+ Harper is a Node.js unified development platform that fuses a document database (RocksDB-backed), in-memory cache, application runtime, and messaging broker (WebSockets, MQTT, NATS) into a single in-process runtime. This directory is the open-source core (`harper` npm package, Apache-2.0), which is the base for the enterprise `harper-pro` wrapper above it.
10
+
11
+ ---
12
+
13
+ ## Commands
14
+
15
+ ```bash
16
+ # Build
17
+ npm run build # TypeScript → dist/ via tsconfig.build.json
18
+ npm run build:watch # Incremental watch build
19
+
20
+ # Lint / Format
21
+ npm run lint # oxlint (warnings = errors)
22
+ npm run lint:fix # Auto-fix
23
+ npm run format:write # Prettier
24
+
25
+ # Test — run specific suites
26
+ npm run test:unit # All unit tests (mocha)
27
+ npm run test:unit:main # Core unit tests (excludes apiTests, lmdb, resources)
28
+ npm run test:unit:resources # Resource layer tests
29
+ npm run test:unit:server # Server layer tests
30
+ npm run test:unit:dataLayer # Data layer tests
31
+ npm run test:unit:components # Component/plugin system tests
32
+ npm run test:unit:security # Security tests
33
+ npm run test:unit:apitests # API tests (stops running server first)
34
+ npm run test:unit:lmdb # LMDB storage engine tests
35
+ npm run test:integration # Full integration test suite
36
+ ```
37
+
38
+ Run a single test file directly:
39
+
40
+ ```bash
41
+ npx mocha unitTests/resources/mytest.js
42
+ ```
43
+
44
+ TypeScript is stripped at runtime via `--conditions=typestrip` (Node.js native type stripping) — no compilation required for development. Use `npm run test:unit:typestrip` to run tests with this mode.
45
+
46
+ ---
47
+
48
+ ## Architecture
49
+
50
+ ### Layers (top to bottom)
51
+
52
+ **Components** (`components/`)
53
+ The plugin/application loader. Applications export a `handleApplication(scope)` function. `Scope` is the primary object passed to apps; it exposes:
54
+
55
+ - `scope.options` — `OptionsWatcher` for live-reloaded YAML config
56
+ - `scope.resources` — access to database tables and registered resources
57
+ - `scope.server` — the HTTP server handle
58
+
59
+ Files within a component are discovered via micromatch glob patterns and automatically mapped to URL paths.
60
+
61
+ **Server** (`server/`)
62
+ Two HTTP stacks coexist:
63
+
64
+ - **Native layer** (`server/http.ts`) — direct socket handling for HTTP/1.1, HTTPS, HTTP/2, and WebSockets in one path; highest performance
65
+ - **Fastify layer** (`server/fastifyRoutes.ts`) — used for legacy custom functions; wraps Fastify with autoload
66
+
67
+ All inbound protocols (REST, GraphQL, MQTT, NATS, WebSockets) eventually resolve to the same **Resource interface**.
68
+
69
+ **Resources** (`resources/`)
70
+ The universal abstraction. Everything that can be queried or mutated — database tables, caches, message topics, custom endpoints — extends `Resource` (`resources/Resource.ts`).
71
+
72
+ Static methods (`Resource.get`, `Resource.put`, `Resource.post`, `Resource.delete`, `Resource.patch`, `Resource.subscribe`) are the entry points and are automatically wrapped with `transactional()` for transaction management. Override instance methods (`get`, `put`, etc.) for custom behavior.
73
+
74
+ `Table.ts` is the database table implementation (~177KB) — the most complex file in the codebase.
75
+
76
+ **Data Layer** (`dataLayer/`)
77
+ Legacy translation modules plus SQL translation (`sqlTranslator/`) via AlaSQL; these should be avoided. The storage engine is selectable via `HARPER_STORAGE_ENGINE=lmdb`.
78
+
79
+ **Configuration** (`config/`)
80
+ YAML-based. `configUtils.js` parses config; `RootConfigWatcher.ts` enables hot reload. Environment variables override YAML values.
81
+
82
+ **Utility** (`utility/`)
83
+ Logging, error types, helpers, async utilities.
84
+
85
+ ---
86
+
87
+ ## Key Patterns
88
+
89
+ **`transactional()` wrapper** — All static Resource methods go through this. It ensures async operations run inside a database transaction. Use `contextStorage` (AsyncLocalStorage) to access the current transaction context without passing it explicitly.
90
+
91
+ **Resource discovery** — A component's config file maps glob patterns to URL paths. Files matching a pattern become routable resources automatically; no explicit route registration is needed.
92
+
93
+ **Lazy loading** — GraphQL, secure sandboxing, and tarball extraction are imported on demand. Do not add top-level imports for these modules.
94
+
95
+ **TypeScript + type stripping** — Source files are `.ts` but Node.js runs them directly via type stripping in development. The `dist/` directory is the compiled production artifact. Both `.ts` and legacy `.js` files coexist; new code should be `.ts`.
96
+
97
+ **Minimal dependencies** — `dependencies.md` documents the rationale for every dependency. Adding a new dependency requires justification; implementing something ourselves is often preferred.
98
+
99
+ ---
100
+
101
+ ## Non-Obvious Constraints
102
+
103
+ - `Resource` static methods must stay wrapped with `transactional()` — removing this breaks transaction isolation.
104
+ - Worker threads (`server/threads/`) receive `workerData.noServerStart = true` to prevent recursive server startup; never start the server inside a worker.
105
+ - `contextStorage` (AsyncLocalStorage) carries per-request context (user, transaction) across async boundaries — this is how authorization and transactions work without explicit parameter threading.
106
+ - Tests under `unitTests/apiTests/` require the server to be stopped first (`node ./dist/bin/harper.js stop`) — `test:unit:apitests` does this automatically.
107
+ - `@export` annotation on a schema class auto-generates a REST API for that table — this is the primary developer-facing API.
package/core/CLAUDE.md ADDED
@@ -0,0 +1 @@
1
+ Please see AGENTS.md for guidance on this project.
@@ -6,7 +6,7 @@ const YAML = require('yaml');
6
6
 
7
7
  const hdbTerms = require('../utility/hdbTerms.ts');
8
8
  const hdbLog = require('../utility/logging/harper_logger.js');
9
- const sysInfo = require('../utility/environment/systemInformation.js');
9
+ const systemInformation = require('../utility/environment/systemInformation.ts');
10
10
  const envMgr = require('../utility/environment/environmentManager.js');
11
11
  const installation = require('../utility/installation.ts');
12
12
  envMgr.initSync();
@@ -51,7 +51,7 @@ async function status() {
51
51
  }
52
52
 
53
53
  // Check the saved pid against any running hdb processes
54
- const hdbSysInfo = await sysInfo.getHDBProcessInfo();
54
+ const hdbSysInfo = await systemInformation.getHDBProcessInfo();
55
55
  for (const proc of hdbSysInfo.core) {
56
56
  if (proc.pid === hdbPid) {
57
57
  status.harperdb.status = STATUSES.RUNNING;
package/core/bin/stop.js CHANGED
@@ -4,7 +4,7 @@ const hdbLogger = require('../utility/logging/harper_logger.js');
4
4
  const util = require('util');
5
5
  const childProcess = require('child_process');
6
6
  const exec = util.promisify(childProcess.exec);
7
- const sysInfo = require('../utility/environment/systemInformation.js');
7
+ const systemInformation = require('../utility/environment/systemInformation.ts');
8
8
 
9
9
  const STOP_MSG = 'Stopping Harper Pro.';
10
10
 
@@ -14,9 +14,8 @@ async function stop() {
14
14
  console.log(STOP_MSG);
15
15
  hdbLogger.notify(STOP_MSG);
16
16
 
17
- const processes = await sysInfo.getHDBProcessInfo();
18
-
19
- processes.core.forEach((p) => {
20
- exec(`kill ${p.pid}`);
21
- });
17
+ const processes = await systemInformation.getHDBProcessInfo();
18
+ for (const { pid } of processes.core) {
19
+ exec(`kill ${pid}`);
20
+ }
22
21
  }
@@ -189,12 +189,14 @@ export class EntryHandler extends EventEmitter<EntryHandlerEventMap> {
189
189
  .watch(this.#component.commonPatternBase, {
190
190
  cwd: this.#component.directory,
191
191
  persistent: false,
192
+ followSymlinks: false,
192
193
  ignored: (path) => {
193
194
  const normalizedPath = path.replace(/\\/g, '/');
194
195
  const normalizedBases = allowedBases.map((base) => base.replace(/\\/g, '/'));
195
196
  return (
196
- normalizedPath !== this.#component.directory.replace(/\\/g, '/') &&
197
- normalizedBases.every((base) => !normalizedPath.startsWith(base))
197
+ normalizedPath.includes('/node_modules') ||
198
+ (normalizedPath !== this.#component.directory.replace(/\\/g, '/') &&
199
+ normalizedBases.every((base) => !normalizedPath.startsWith(base)))
198
200
  );
199
201
  },
200
202
  })
@@ -63,7 +63,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
63
63
  this.#pluginName = pluginName;
64
64
  this.#directory = directory;
65
65
  this.#configFilePath = configFilePath;
66
- this.#logger = logger || loggerWithTag(this.#appName);
66
+ this.#logger = loggerWithTag(this.#appName);
67
67
 
68
68
  this.databaseEvents = databaseEventsEmitter;
69
69
  this.applicationScope = applicationScope;
@@ -17,7 +17,8 @@ import * as staticFiles from '../server/static.ts';
17
17
  import * as loadEnv from '../resources/loadEnv.ts';
18
18
  import harperLogger from '../utility/logging/harper_logger.js';
19
19
  import * as dataLoader from '../resources/dataLoader.ts';
20
- import { watchDir, getWorkerIndex } from '../server/threads/manageThreads.js';
20
+ import { restartWorkers, getWorkerIndex } from '../server/threads/manageThreads.js';
21
+ import { resetRestartNeeded, subscribeToRestartRequests } from './requestRestart.ts';
21
22
  import { scopedImport } from '../security/jsLoader.ts';
22
23
  import { server } from '../server/Server.ts';
23
24
  import { Resources } from '../resources/Resources.ts';
@@ -515,10 +516,16 @@ export async function loadComponent(
515
516
  }
516
517
 
517
518
  compName = parentCompName;
518
- // Auto restart threads on changes to any app folder. TODO: Make this configurable
519
519
  if (isMainThread && !watchesSetup && autoReload) {
520
- watchDir(componentDirectory, async () => {
521
- return loadComponentDirectories(); // return the promise
520
+ let debounceTimer: ReturnType<typeof setTimeout> | null = null;
521
+ subscribeToRestartRequests(() => {
522
+ if (debounceTimer) clearTimeout(debounceTimer);
523
+ debounceTimer = setTimeout(async () => {
524
+ debounceTimer = null;
525
+ resetRestartNeeded();
526
+ await loadComponentDirectories();
527
+ restartWorkers();
528
+ }, 500);
522
529
  });
523
530
  }
524
531
  if ((config.extensionModule || config.pluginModule) && (!isMainThread || config.runOnMainThread)) {
@@ -1,11 +1,19 @@
1
1
  import { Status } from '../server/status/index.ts';
2
2
 
3
- let restartArrayBuffer: ArrayBuffer;
3
+ interface NotifyingArrayBuffer extends ArrayBuffer {
4
+ notify(): void;
5
+ cancel(): void;
6
+ }
7
+
8
+ let restartArrayBuffer: NotifyingArrayBuffer;
4
9
  let restartNeededArray: Uint8Array;
10
+ let onRestartRequestedCallback: (() => void) | null = null;
5
11
 
6
12
  function ensureInitialized() {
7
13
  if (!restartArrayBuffer) {
8
- restartArrayBuffer = Status.primaryStore.getUserSharedBuffer('restart-needed', new ArrayBuffer(1));
14
+ restartArrayBuffer = Status.primaryStore.getUserSharedBuffer('restart-needed', new ArrayBuffer(1), {
15
+ callback: () => onRestartRequestedCallback?.(),
16
+ }) as NotifyingArrayBuffer;
9
17
  restartNeededArray = new Uint8Array(restartArrayBuffer);
10
18
  }
11
19
  }
@@ -13,6 +21,7 @@ function ensureInitialized() {
13
21
  export function requestRestart() {
14
22
  ensureInitialized();
15
23
  restartNeededArray[0] = 1;
24
+ restartArrayBuffer.notify();
16
25
  }
17
26
 
18
27
  export function restartNeeded() {
@@ -24,3 +33,9 @@ export function resetRestartNeeded() {
24
33
  ensureInitialized();
25
34
  restartNeededArray[0] = 0;
26
35
  }
36
+
37
+ export function subscribeToRestartRequests(callback: () => void) {
38
+ ensureInitialized();
39
+ if (onRestartRequestedCallback) throw new Error('A restart-request subscriber is already registered');
40
+ onRestartRequestedCallback = callback;
41
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Represents the table size entry for a RocksDB or LMDB table.
3
+ */
4
+ export class TableSizeObject {
5
+ schema: string;
6
+ table: string;
7
+ tableSize: number;
8
+ recordCount: number;
9
+ transactionLogSize: number;
10
+ transactionLogRecordCount?: number;
11
+
12
+ /**
13
+ * @param schema - The schema of the table
14
+ * @param table - The name of the table
15
+ * @param tableSize - The data size of the table in bytes
16
+ * @param recordCount - The number of entries in the table
17
+ * @param transactionLogSize - The number of entries in the transaction log
18
+ * @param transactionLogRecordCount - The data size of the transaction log in bytes
19
+ */
20
+ constructor(
21
+ schema: string,
22
+ table: string,
23
+ tableSize: number = 0,
24
+ recordCount: number = 0,
25
+ transactionLogSize: number = 0,
26
+ transactionLogRecordCount?: number
27
+ ) {
28
+ this.schema = schema;
29
+ this.table = table;
30
+ this.tableSize = tableSize;
31
+ this.recordCount = recordCount;
32
+ this.transactionLogSize = transactionLogSize;
33
+ this.transactionLogRecordCount = transactionLogRecordCount;
34
+ }
35
+ }
@@ -0,0 +1,24 @@
1
+ import { TableSizeObject } from '../../TableSizeObject.ts';
2
+ import logger from '../../../../utility/logging/harper_logger.js';
3
+ import type { Table } from '../../../../resources/databases.ts';
4
+
5
+ /**
6
+ * Calculates the number of entries & data size in bytes for a table & its transaction log
7
+ * @param table
8
+ * @returns {TableSizeObject}
9
+ */
10
+ export function lmdbGetTableSize(table: Table) {
11
+ const tableStats = new TableSizeObject(table.databaseName, table.tableName);
12
+ try {
13
+ const dbiStat = table.primaryStore.getStats();
14
+
15
+ //get the txn log record count
16
+ const txnDbiStat = table.auditStore?.getStats();
17
+
18
+ tableStats.recordCount = dbiStat.entryCount;
19
+ tableStats.transactionLogRecordCount = txnDbiStat.entryCount;
20
+ } catch (e) {
21
+ logger.warn(`unable to stat table dbi due to ${e}`);
22
+ }
23
+ return tableStats;
24
+ }