Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
fa8fde1
Add startup-phase lifecycle and runtime path helpers
kriszyp May 16, 2026
1f4522a
Convert CJS .js source modules to ESM .ts
kriszyp May 16, 2026
25f4c86
Wire type-strip entry point and defer module-load side effects
kriszyp May 16, 2026
72d70c5
Fix subcommand module resolution for CJS dist mode
kriszyp May 16, 2026
ded6937
Address claude/review feedback and unit-test regression
kriszyp May 17, 2026
c01c4df
Fix remaining typestrip require()s and lazy-init ErrorResource
kriszyp May 17, 2026
31b0ef6
Preserve module-scope variable names for rewire-using unit tests
kriszyp May 17, 2026
a793905
Fix unit-test regressions from CJS-to-ESM conversion
kriszyp May 17, 2026
17e6399
Alias remaining imports for rewire-using tests (PropertiesReader, ins…
kriszyp May 17, 2026
04ec5f9
Alias applyRuntimeEnvConfig import + update its test
kriszyp May 17, 2026
9162181
Lazy-access harperBridge in dataLayer; fix __dirname use in jobRunner
kriszyp May 17, 2026
1bedb95
Convert remaining top-level require() calls in LMDB/launch files to i…
kriszyp May 17, 2026
5f90d5f
Drop unused JSDoc type require() in lmdbSearchByConditions
kriszyp May 17, 2026
6afa9e5
Unwrap CJS double-default in signalling onStartup preload
kriszyp May 17, 2026
ad0f82f
Expose alaSQLExtension members as named exports
kriszyp May 17, 2026
a555b9f
Update readLog test rewire suffix after configUtils .js->.ts move
kriszyp May 17, 2026
f3a3abf
Convert remaining ESM-file require() calls flagged by claude/review
kriszyp May 17, 2026
5e455b8
Make jsLoader spawn-wrapper env reads lazy
kriszyp May 17, 2026
b91fcbc
Fall back to defaultConfig in jsLoader spawn allow-list
kriszyp May 17, 2026
bac7238
Hardcode pre-config spawn allow-list default
kriszyp May 17, 2026
0fde1f7
Drain startup hooks before HTTP-test server start
kriszyp May 17, 2026
b38004d
Stop subcommand env init and config-load resilience
kriszyp May 17, 2026
9322d6c
Wait for whenComponentsLoaded before api-test requests
kriszyp May 17, 2026
811f76c
Resolve whenComponentsLoaded only after ports are bound
kriszyp May 17, 2026
4af6fb9
Fix componentsLoadedResolve passing namespace instead of chain
kriszyp May 17, 2026
6902c9a
Remove whenComponentsLoaded await in api-test setup
kriszyp May 17, 2026
30ac85a
Restore initSync() at top of databases.ts
kriszyp May 17, 2026
52200f6
Make startServers() return the loaded promise
kriszyp May 17, 2026
126d863
Drain startup before server.getUser override in api-test setup
kriszyp May 17, 2026
e5826e2
Make onSocket sync to preserve mTLS test compatibility
kriszyp May 17, 2026
3ff7c69
Restore initSync() module-load calls (cycle-tolerant)
kriszyp May 17, 2026
0f27e90
Graceful worker shutdown in create.test.js to avoid rocksdb-js segfault
kriszyp May 17, 2026
0432e9e
Lazy dataLoader logger + lazy componentLoader in operations.ts
kriszyp May 17, 2026
be74100
Move startup-hook drain into run.ts main() after initialize()
kriszyp May 17, 2026
761cdf5
Lazy-Proxy CertificateVerificationSource for stub-friendly construction
kriszyp May 17, 2026
30e1fd9
Restore startServers() fire-and-forget; drop dead return path
kriszyp May 17, 2026
e6516f6
Resolve apitests/CRL conflict on startServers awaiting
kriszyp May 17, 2026
00e5af9
Lazy-Proxy CertificateRevocationListSource (matches CertificateVerifi…
kriszyp May 17, 2026
c2c8c6a
Register ITC ack listener at module load, not via setImmediate
kriszyp May 17, 2026
b551bb5
Revert "Register ITC ack listener at module load, not via setImmediate"
kriszyp May 18, 2026
5c4a454
Preload serverHandlers before ack-listener registration in itc.ts
kriszyp May 18, 2026
ad08e8c
Ack ITC events immediately, run handler in background
kriszyp May 18, 2026
078f6f5
Revert "Ack ITC events immediately, run handler in background"
kriszyp May 18, 2026
e3c4ec6
Register ITC ack listener at module load with eager handlers preload
kriszyp May 18, 2026
8575a36
Unwrap CJS-double-default when resolving serverItcHandlers
kriszyp May 18, 2026
59dec88
Ack ITC events immediately, run schema handler in background
kriszyp May 18, 2026
82a0db8
Restore setImmediate-wrapped listener; correctly unwrap handlers name…
kriszyp May 18, 2026
bd03224
Skip DEFAULT_CONFIG fallback for non-root components with empty config
kriszyp May 18, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/scheduled_tasks.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"sessionId":"5206baae-267d-4d02-bedd-55b8d6b8cf05","pid":3219805,"procStart":"41160224","acquiredAt":1778799452286}
14 changes: 8 additions & 6 deletions bin/cliOperations.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
'use strict';

import * as envMgr from '../utility/environment/environmentManager.ts';
envMgr.initSync();
import * as terms from '../utility/hdbTerms.ts';
import { httpRequest } from '../utility/common_utils.ts';
import * as path from 'path';
import * as fs from 'fs-extra';
import fs from 'fs-extra';
import * as YAML from 'yaml';
import { packageDirectory } from '../components/packageComponent.ts';
import { encode } from 'cbor-x';
import { getHdbPid } from '../utility/processManagement/processManagement.js';
import { initConfig, getConfigPath } from '../config/configUtils.js';

import { getHdbPid } from '../utility/processManagement/processManagement.ts';
import { initConfig, getConfigPath } from '../config/configUtils.ts';
try {
envMgr.initSync();

Check failure on line 13 in bin/cliOperations.ts

View workflow job for this annotation

GitHub Actions / Unit Test (Node.js v24)

Cannot find name 'envMgr'.
} catch {
/* tolerate ESM cycle TDZ; bin entry will re-call later */
}
const OP_ALIASES = { deploy: 'deploy_component', package: 'package_component' };

export { cliOperations, buildRequest };
Expand Down
5 changes: 3 additions & 2 deletions bin/copyDb.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { getDatabases, getDefaultCompression, resetDatabases } from '../resources/databases.ts';
import { open, asBinary } from 'lmdb';
import { join } from 'path';
import { move, remove } from 'fs-extra';
import _fs_extra from 'fs-extra';
const { move, remove } = _fs_extra;
import { existsSync, mkdirSync } from 'node:fs';
import { get } from '../utility/environment/environmentManager.ts';
import OpenEnvironmentObject from '../utility/lmdb/OpenEnvironmentObject.ts';
Expand All @@ -10,7 +11,7 @@ import { INTERNAL_DBIS_NAME, AUDIT_STORE_NAME } from '../utility/lmdb/terms.ts';
import { CONFIG_PARAMS, DATABASES_DIR_NAME } from '../utility/hdbTerms.ts';
import { AUDIT_STORE_OPTIONS } from '../resources/auditStore.ts';
import { describeSchema } from '../dataLayer/schemaDescribe.ts';
import { updateConfigValue } from '../config/configUtils.js';
import { updateConfigValue } from '../config/configUtils.ts';
import * as hdbLogger from '../utility/logging/harper_logger.ts';
import { RocksDatabase, type RocksDatabaseOptions } from '@harperfast/rocksdb-js';
import { RocksIndexStore } from '../resources/RocksIndexStore.ts';
Expand Down
86 changes: 66 additions & 20 deletions bin/harper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import logger from '../utility/logging/harper_logger.ts';
import * as cliOperations from './cliOperations.ts';
import { packageJson } from '../utility/packageUtils.js';
import checkNode from '../launchServiceScripts/utility/checkNodeVersion.js';
import * as hdbTerms from '../utility/hdbTerms.ts';
Expand Down Expand Up @@ -38,6 +37,36 @@ version - Print the version
deploy - Deploy the application locally or remotely with target=<remote url>
`;

/**
* Initialize the environment manager. Call before dynamically importing the
* subcommand module so any module-load reads of `env.get(…)` see a populated
* configuration. Side-effectful initialization is deferred to `onStartup(…)`
* hooks; call `runServerStartup()` once the subcommand module has finished
* loading to drain them.
*/
async function initEnv() {
const env = await import('../utility/environment/environmentManager.ts');
env.initSync();
}

async function runServerStartup() {
const lifecycle = await import('../utility/lifecycle.ts');
await lifecycle.runStartup();
}

/**
* Resolve a default-exported function from a dynamically-imported module that
* may have been loaded from a .ts source (ESM: `mod.default` is the function)
* or a tsc-compiled .js (CJS-wrapped: `mod.default` is the CJS exports object
* and `mod.default.default` is the function).
*/
function getDefaultExport(mod: any): any {
if (typeof mod === 'function') return mod;
if (typeof mod.default === 'function') return mod.default;
if (typeof mod.default?.default === 'function') return mod.default.default;
return mod.default ?? mod;
}

async function harper() {
let nodeResults = checkNode();

Expand All @@ -61,34 +90,41 @@ async function harper() {
switch (service) {
case SERVICE_ACTIONS_ENUM.HELP:
return HELP;
case SERVICE_ACTIONS_ENUM.START:
return require('./run').launch();
case SERVICE_ACTIONS_ENUM.INSTALL:
return (require('./install').default || require('./install'))();
case SERVICE_ACTIONS_ENUM.STOP:
return (require('./stop').default || require('./stop'))().then(() => {
case SERVICE_ACTIONS_ENUM.START: {
await initEnv();
const mod = await import('./run.ts');
// runStartup() is drained inside run.ts main() after initialize()
// completes, so server.X singletons and auth's table() resolution
// see a populated env / installed hdbBasePath.
return mod.launch();
}
case SERVICE_ACTIONS_ENUM.INSTALL: {
return getDefaultExport(await import('./install.ts'))();
}
case SERVICE_ACTIONS_ENUM.STOP: {
await initEnv();
return getDefaultExport(await import('./stop.ts'))().then(() => {
process.exit(0);
});
}
case SERVICE_ACTIONS_ENUM.RESTART:
return require('./restart').restart({});
return (await import('./restart.ts')).restart({});
case SERVICE_ACTIONS_ENUM.VERSION:
return packageJson.version;
case SERVICE_ACTIONS_ENUM.UPGRADE:
logger.setLogLevel(hdbTerms.LOG_LEVELS.INFO);
// The require is here to better control the flow of imports when this module is called.
return require('./upgrade.js')
.upgrade(null)
.then(() => 'Your instance of Harper is up to date!');
case SERVICE_ACTIONS_ENUM.STATUS:
return (require('./status').default || require('./status'))();
return (await import('./upgrade.ts')).upgrade(null).then(() => 'Your instance of Harper is up to date!');
case SERVICE_ACTIONS_ENUM.STATUS: {
return getDefaultExport(await import('./status.ts'))();
}
case SERVICE_ACTIONS_ENUM.RENEWCERTS:
return require('../security/keys')
return (await import('../security/keys.ts'))
.renewSelfSigned()
.then(() => 'Successfully renewed self-signed certificates');
case SERVICE_ACTIONS_ENUM.COPYDB: {
let sourceDb = process.argv[3];
let targetDbPath = process.argv[4];
return require('./copyDb').copyDb(sourceDb, targetDbPath);
return (await import('./copyDb.ts')).copyDb(sourceDb, targetDbPath);
}
case SERVICE_ACTIONS_ENUM.DEV:
process.env.DEV_MODE = 'true';
Expand Down Expand Up @@ -127,17 +163,27 @@ async function harper() {
}
}
// fall through
case undefined: // run harperdb in the foreground in standard mode
return require('./run').main();
default:
case undefined: {
// run harperdb in the foreground in standard mode.
// runStartup() is drained inside run.ts main() after initialize().
await initEnv();
const mod = await import('./run.ts');
return mod.main();
}
default: {
const cliOperations = await import('./cliOperations.ts');
const cliApiOp = cliOperations.buildRequest();
logger.trace('calling cli operations with:', cliApiOp);
await cliOperations.cliOperations(cliApiOp);
return;
}
}
}
export { harper };
if (require.main === module) {
// In CJS (dist), `module` is defined and we check the entry. In ESM (typestrip),
// `module` is undefined; this file is only run directly as a CLI, so treat as main.
const isEntry = typeof module === 'undefined' || require.main === module;
if (isEntry) {
harper()
.then((message) => {
if (message) {
Expand Down
13 changes: 8 additions & 5 deletions bin/restart.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
import { isMainThread, parentPort } from 'worker_threads';
import * as hdbTerms from '../utility/hdbTerms.ts';
import hdbLogger from '../utility/logging/harper_logger.ts';
import * as processMan from '../utility/processManagement/processManagement.js';
import * as processMan from '../utility/processManagement/processManagement.ts';
import { compactOnStart } from './copyDb.ts';
import { restartWorkers, onMessageByType, shutdownWorkersNow } from '../server/threads/manageThreads.js';
import { restartWorkers, onMessageByType, shutdownWorkersNow } from '../server/threads/manageThreads.ts';
import { handleHDBError, hdbErrors } from '../utility/errors/hdbError.ts';
const { HTTP_STATUS_CODES } = hdbErrors;
import * as envMgr from '../utility/environment/environmentManager.ts';
import * as path from 'node:path';
import { unlinkSync } from 'node:fs';
import { getThisNodeName } from '../server/nodeName.ts';
envMgr.initSync();

try {
envMgr.initSync();
} catch {
/* tolerate ESM cycle TDZ; bin entry will re-call later */
}
const RESTART_RESPONSE = `Restarting Harper. This may take up to ${hdbTerms.RESTART_TIMEOUT_MS / 1000} seconds.`;
const INVALID_SERVICE_ERR = 'Invalid service';

Expand Down Expand Up @@ -65,8 +68,8 @@
// connections and hanging. So we need to explicitly close down all the workers and then start the new process
// and shut down.
hdbLogger.debug('Shutdown workers');
await shutdownWorkersNow();

Check failure on line 71 in bin/restart.ts

View workflow job for this annotation

GitHub Actions / Unit Test (Node.js v24)

Expected 1 arguments, but got 0.
const { closeServers } = require('../server/threads/threadServer.js');
const { closeServers } = await import('../server/threads/threadServer.ts');
await closeServers();
await processMan.cleanupChildrenProcesses(false);
// remove pid file so it doesn't trip up the launch
Expand Down
27 changes: 18 additions & 9 deletions bin/run.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,27 @@
'use strict';

import * as env from '../utility/environment/environmentManager.ts';
env.initSync();

try {
env.initSync();
} catch {
/* tolerate ESM cycle TDZ; bin entry will re-call later */
}
// This unused restart import is here so that main thread loads ITC event listener defined in restart file. Do not remove.
import './restart.ts';
import * as terms from '../utility/hdbTerms.ts';
const { CONFIG_PARAMS } = terms;
import hdbLogger from '../utility/logging/harper_logger.ts';
import * as fs from 'fs-extra';
import fs from 'fs-extra';
import * as path from 'path';
import checkJwtTokens from '../utility/install/checkJWTTokensExist.js';
import checkJwtTokens from '../utility/install/checkJWTTokensExist.ts';
import { install } from '../utility/install/installer.ts';
import chalk from 'chalk';
import { packageJson } from '../utility/packageUtils.js';
import * as hdbUtils from '../utility/common_utils.ts';
import * as installation from '../utility/installation.ts';
import * as configUtils from '../config/configUtils.js';
import * as configUtils from '../config/configUtils.ts';
import assignCMDENVVariables from '../utility/assignCmdEnvVariables.ts';
import * as upgrade from './upgrade.js';
import * as upgrade from './upgrade.ts';
import { compactOnStart, migrateOnStart } from './copyDb.ts';
import minimist from 'minimist';
import * as keys from '../security/keys.ts';
Expand All @@ -27,7 +30,7 @@ import * as hdbInfoController from '../dataLayer/hdbInfoController.ts';
import { isReadOnlyMode } from '../resources/databases.ts';
import { getThisNodeName } from '../server/nodeName.ts';
import * as hdbTerms from '../utility/hdbTerms.ts';
import { getHdbPid, isProcessRunning } from '../utility/processManagement/processManagement.js';
import { getHdbPid, isProcessRunning } from '../utility/processManagement/processManagement.ts';
import { PACKAGE_ROOT } from '../utility/packageUtils.js';

let pmUtils;
Expand Down Expand Up @@ -108,7 +111,7 @@ async function initialize(calledByInstall = false, calledByMain = false) {

// If HARPER_SET_CONFIG is present, filter out any config keys that are set in it
// to prevent individual env vars from overriding explicit runtime configuration
const { filterArgsAgainstRuntimeConfig } = require('../config/harperConfigEnvVars');
const { filterArgsAgainstRuntimeConfig } = await import('../config/harperConfigEnvVars.ts');
parsedArgs = filterArgsAgainstRuntimeConfig(parsedArgs);

if (!hdbUtils.isEmpty(parsedArgs) && !hdbUtils.isEmptyOrZeroLength(Object.keys(parsedArgs))) {
Expand Down Expand Up @@ -199,6 +202,12 @@ async function main(calledByInstall = false) {
}
await initialize(calledByInstall, true);

// Drain onStartup hooks after install/initialize so server.X singletons
// (server.http, server.getUser, etc.) and auth.ts's onStartup table()
// call see the populated env / installed hdbBasePath.
const lifecycle = await import('../utility/lifecycle.ts');
await lifecycle.runStartup();

if (env.get(terms.CONFIG_PARAMS.STORAGE_COMPACTONSTART)) await compactOnStart();
if (env.get(terms.CONFIG_PARAMS.STORAGE_MIGRATEONSTART)) await migrateOnStart();

Expand Down Expand Up @@ -233,7 +242,7 @@ function started() {
async function launch(exit = true) {
skipExitListeners = !exit;
try {
if (pmUtils === undefined) pmUtils = require('../utility/processManagement/processManagement.js');
if (pmUtils === undefined) pmUtils = await import('../utility/processManagement/processManagement.ts');
hdbLogger.debug('initializing processManagement...');
await initialize();
hdbLogger.debug('Starting new main process');
Expand Down
9 changes: 6 additions & 3 deletions bin/status.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import * as fs from 'fs-extra';
import fs from 'fs-extra';
import * as path from 'path';
import * as YAML from 'yaml';

Expand All @@ -9,8 +9,11 @@ import hdbLog from '../utility/logging/harper_logger.ts';
import * as systemInformation from '../utility/environment/systemInformation.ts';
import * as envMgr from '../utility/environment/environmentManager.ts';
import * as installation from '../utility/installation.ts';
envMgr.initSync();

try {
envMgr.initSync();
} catch {
/* tolerate ESM cycle TDZ; bin entry will re-call later */
}
const STATUSES = {
RUNNING: 'running',
STOPPED: 'stopped',
Expand Down
35 changes: 15 additions & 20 deletions bin/upgrade.js → bin/upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,28 @@
'use strict';

/**
* The upgrade module is used to facilitate the upgrade process for existing instances of HDB that pull down a new version
* of HDB from NPM that requires a specific upgrade script be run - e.g. there are changes required for the settings.js
* config file, a data model change requires a re-indexing script is run, etc.
*/

const env = require('../utility/environment/environmentManager.ts');
env.initSync();

const chalk = require('chalk');
const hdbLogger = require('../utility/logging/harper_logger.ts');
const hdbTerms = require('../utility/hdbTerms.ts');
const directivesManager = require('../upgrade/directivesManager.ts');
const installation = require('../utility/installation.ts');
const hdbInfoController = require('../dataLayer/hdbInfoController.ts');
const upgradePrompt = require('../upgrade/upgradePrompt.ts');
const globalSchema = require('../utility/globalSchema.ts');
const { packageJson } = require('../utility/packageUtils.js');
const promisify = require('util').promisify;
import * as env from '../utility/environment/environmentManager.ts';

import chalk from 'chalk';
import _hdbLogger from '../utility/logging/harper_logger.ts';
const hdbLogger = _hdbLogger;
import * as hdbTerms from '../utility/hdbTerms.ts';
import * as directivesManager from '../upgrade/directivesManager.ts';
import * as installation from '../utility/installation.ts';
import * as hdbInfoController from '../dataLayer/hdbInfoController.ts';
import * as upgradePrompt from '../upgrade/upgradePrompt.ts';
import * as globalSchema from '../utility/globalSchema.ts';
import { packageJson } from '../utility/packageUtils.js';
import { promisify } from 'util';
const pSchemaToGlobal = promisify(globalSchema.setSchemaDataToGlobal);
let pm2Utils;

const { UPGRADE_VERSION } = hdbTerms.UPGRADE_JSON_FIELD_NAMES_ENUM;

module.exports = {
upgrade,
};

export { upgrade };
/**
* Runs the upgrade directives, if needed, for an updated version of Harper.
*
Expand All @@ -39,7 +34,7 @@ async function upgrade(upgradeObj) {

// Requiring the processManagement mod will create the .pm2 dir. This code is here to allow install to set
// pm2 env vars before that is done.
if (pm2Utils === undefined) pm2Utils = require('../utility/processManagement/processManagement.js');
if (pm2Utils === undefined) pm2Utils = await import('../utility/processManagement/processManagement.ts');

//We have to make sure HDB is installed before doing anything else
const installed = installation.isHdbInstalled(env, hdbLogger);
Expand Down
2 changes: 1 addition & 1 deletion components/Application.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { type Logger } from '../utility/logging/logger.ts';
import { getConfigObj, getConfigValue, getConfigPath } from '../config/configUtils.js';
import { getConfigObj, getConfigValue, getConfigPath } from '../config/configUtils.ts';
import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
import logger from '../utility/logging/harper_logger.ts';

Expand Down
10 changes: 5 additions & 5 deletions components/Component.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { resolveBaseURLPath } from './resolveBaseURLPath';
import { deriveCommonPatternBase } from './deriveCommonPatternBase';
import { deriveGlobOptions, FastGlobOptions, FilesOption } from './deriveGlobOptions';
import { scan } from 'micromatch';

import { resolveBaseURLPath } from './resolveBaseURLPath.ts';
import { deriveCommonPatternBase } from './deriveCommonPatternBase.ts';
import { deriveGlobOptions, type FastGlobOptions, type FilesOption } from './deriveGlobOptions.ts';
import _micromatch from 'micromatch';
const { scan } = _micromatch;
interface ComponentConfig {
files: FilesOption;
urlPath?: string;
Expand Down
Loading
Loading