@harperfast/harper 5.2.1 → 5.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/copyDb.ts +19 -7
- package/components/deploymentRecorder.ts +102 -11
- package/components/operations.js +4 -0
- package/config/configUtils.ts +66 -11
- package/config/harperConfigEnvVars.ts +114 -17
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
- package/dist/bin/copyDb.js +20 -7
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +6 -0
- package/dist/components/deploymentRecorder.js +94 -12
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/operations.js +4 -0
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +6 -0
- package/dist/config/configUtils.js +65 -9
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/harperConfigEnvVars.js +108 -16
- package/dist/config/harperConfigEnvVars.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
- package/dist/resources/DatabaseTransaction.d.ts +2 -0
- package/dist/resources/DatabaseTransaction.js +6 -5
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/Table.d.ts +2 -1
- package/dist/resources/Table.js +66 -16
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.js +27 -1
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +15 -2
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +128 -39
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/search.d.ts +1 -1
- package/dist/resources/search.js +7 -3
- package/dist/resources/search.js.map +1 -1
- package/dist/server/threads/manageThreads.js +8 -0
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/sqlEngine/parser/normalizer.js +37 -9
- package/dist/sqlEngine/parser/normalizer.js.map +1 -1
- package/dist/utility/environment/environmentManager.d.ts +11 -0
- package/dist/utility/environment/environmentManager.js +154 -0
- package/dist/utility/environment/environmentManager.js.map +1 -1
- package/dist/utility/lmdb/environmentUtility.js +2 -1
- package/dist/utility/lmdb/environmentUtility.js.map +1 -1
- package/npm-shrinkwrap.json +13 -7
- package/package.json +2 -2
- package/resources/DESIGN.md +1 -0
- package/resources/DatabaseTransaction.ts +7 -5
- package/resources/Table.ts +77 -16
- package/resources/auditStore.ts +32 -1
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +119 -45
- package/resources/search.ts +8 -3
- package/server/threads/manageThreads.js +8 -0
- package/studio/web/assets/{Chat-aApwhRmz.js → Chat-BMUjPklt.js} +5 -5
- package/studio/web/assets/{Chat-aApwhRmz.js.map → Chat-BMUjPklt.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-sC0H91n9.js → FloatingChat-Cbghuxe5.js} +5 -5
- package/studio/web/assets/{FloatingChat-sC0H91n9.js.map → FloatingChat-Cbghuxe5.js.map} +1 -1
- package/studio/web/assets/{apiToken-DZ8JhHOI.js → apiToken-CkgTQAqc.js} +2 -2
- package/studio/web/assets/{apiToken-DZ8JhHOI.js.map → apiToken-CkgTQAqc.js.map} +1 -1
- package/studio/web/assets/{applications-9SEVLIO5.js → applications-WvF4NOTN.js} +13 -13
- package/studio/web/assets/applications-WvF4NOTN.js.map +1 -0
- package/studio/web/assets/chevron-right-anDJCGlp.js +2 -0
- package/studio/web/assets/chevron-right-anDJCGlp.js.map +1 -0
- package/studio/web/assets/index-CDeqVx71.css +1 -0
- package/studio/web/assets/index-Sevt3MVx.js +819 -0
- package/studio/web/assets/index-Sevt3MVx.js.map +1 -0
- package/studio/web/assets/index.lazy-Dg5YYPGg.js +14 -0
- package/studio/web/assets/{index.lazy-CN1zq4I4.js.map → index.lazy-Dg5YYPGg.js.map} +1 -1
- package/studio/web/assets/{jsonMode-Cjj7dtlr.js → jsonMode-CBSEleE7.js} +2 -2
- package/studio/web/assets/{jsonMode-Cjj7dtlr.js.map → jsonMode-CBSEleE7.js.map} +1 -1
- package/studio/web/assets/languageServices-gxedeeTo.js +2 -0
- package/studio/web/assets/languageServices-gxedeeTo.js.map +1 -0
- package/studio/web/assets/{notifications-BFAF07xr.js → notifications-Ap84YyQp.js} +2 -2
- package/studio/web/assets/{notifications-BFAF07xr.js.map → notifications-Ap84YyQp.js.map} +1 -1
- package/studio/web/assets/notifications-vehqyOLM.js +2 -0
- package/studio/web/assets/{notifications-CKlYVvVN.js.map → notifications-vehqyOLM.js.map} +1 -1
- package/studio/web/assets/{pollUnlessForbidden-HHdQZW1N.js → pollUnlessForbidden-BhzefVRn.js} +2 -2
- package/studio/web/assets/pollUnlessForbidden-BhzefVRn.js.map +1 -0
- package/studio/web/assets/{profile-A1zhEdFG.js → profile-DzDpRLHY.js} +2 -2
- package/studio/web/assets/{profile-A1zhEdFG.js.map → profile-DzDpRLHY.js.map} +1 -1
- package/studio/web/assets/queryClient-CgtkoiCY.js +2 -0
- package/studio/web/assets/queryClient-CgtkoiCY.js.map +1 -0
- package/studio/web/assets/regions-DpJ8s7iN.js +2 -0
- package/studio/web/assets/regions-DpJ8s7iN.js.map +1 -0
- package/studio/web/assets/register-BDy01-q3.js +3 -0
- package/studio/web/assets/register-BDy01-q3.js.map +1 -0
- package/studio/web/assets/register-CQHRwNP4.js +3 -0
- package/studio/web/assets/register-CQHRwNP4.js.map +1 -0
- package/studio/web/assets/{setComponentFile-SEtBt_GV.js → setComponentFile-D0aPcI3W.js} +2 -2
- package/studio/web/assets/{setComponentFile-SEtBt_GV.js.map → setComponentFile-D0aPcI3W.js.map} +1 -1
- package/studio/web/assets/setup-I15FAF9H.js +3 -0
- package/studio/web/assets/{setup-BisINqdH.js.map → setup-I15FAF9H.js.map} +1 -1
- package/studio/web/assets/status-BF1eJshJ.js +62 -0
- package/studio/web/assets/status-BF1eJshJ.js.map +1 -0
- package/studio/web/assets/{swagger-ui-react-CAi_s1PC.js → swagger-ui-react-DwSPXXhl.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-CAi_s1PC.js.map → swagger-ui-react-DwSPXXhl.js.map} +1 -1
- package/studio/web/assets/{textarea-DhLMZ3PA.js → table-DpOjNQHt.js} +2 -10
- package/studio/web/assets/table-DpOjNQHt.js.map +1 -0
- package/studio/web/assets/{tsMode-DT74tlkM.js → tsMode-Bu51A-VG.js} +2 -2
- package/studio/web/assets/{tsMode-DT74tlkM.js.map → tsMode-Bu51A-VG.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-CGRGc1n7.js → useEntityRestURL-DrV1zMrN.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-CGRGc1n7.js.map → useEntityRestURL-DrV1zMrN.js.map} +1 -1
- package/studio/web/index.html +6 -7
- package/utility/environment/environmentManager.ts +152 -0
- package/utility/lmdb/environmentUtility.ts +2 -1
- package/studio/web/assets/applications-9SEVLIO5.js.map +0 -1
- package/studio/web/assets/index-C8Mv9kHV.css +0 -1
- package/studio/web/assets/index-Dy3uDGXb.js +0 -809
- package/studio/web/assets/index-Dy3uDGXb.js.map +0 -1
- package/studio/web/assets/index.lazy-CN1zq4I4.js +0 -14
- package/studio/web/assets/notifications-CKlYVvVN.js +0 -2
- package/studio/web/assets/pollUnlessForbidden-HHdQZW1N.js.map +0 -1
- package/studio/web/assets/queryClient-CbA8wM7J.js +0 -2
- package/studio/web/assets/queryClient-CbA8wM7J.js.map +0 -1
- package/studio/web/assets/setup-BisINqdH.js +0 -3
- package/studio/web/assets/status-BuQoCc7l.js +0 -62
- package/studio/web/assets/status-BuQoCc7l.js.map +0 -1
- package/studio/web/assets/textarea-DhLMZ3PA.js.map +0 -1
|
@@ -9,6 +9,7 @@ import * as commonUtils from '../common_utils.ts';
|
|
|
9
9
|
import * as hdbTerms from '../hdbTerms.ts';
|
|
10
10
|
import * as configUtils from '../../config/configUtils.ts';
|
|
11
11
|
import { mkdirSync } from 'node:fs';
|
|
12
|
+
import { workerData } from 'node:worker_threads';
|
|
12
13
|
|
|
13
14
|
const INIT_ERR = 'Error initializing environment manager';
|
|
14
15
|
const BOOT_PROPS_FILE_PATH = 'BOOT_PROPS_FILE_PATH';
|
|
@@ -24,6 +25,19 @@ const installPropsToSave = {
|
|
|
24
25
|
let installProps: any = {};
|
|
25
26
|
export { BOOT_PROPS_FILE_PATH };
|
|
26
27
|
|
|
28
|
+
// Every param passed to setProperty() on this thread, keyed by its canonical config param so
|
|
29
|
+
// aliases collapse to one last-write-wins entry. Workers inherit this map (getConfigOverrides() and
|
|
30
|
+
// the workerDataProvider registration in server/threads/manageThreads.js) and it is replayed after
|
|
31
|
+
// a forced config reload; `base` is the configured value the override displaced, which is how
|
|
32
|
+
// reapplyAllOverrides() tells a reload that left the param alone from one that is itself the change.
|
|
33
|
+
//
|
|
34
|
+
// setProperty() therefore means operator/harness *intent* only. Code caching a value it derived
|
|
35
|
+
// from config it just read (initializePaths.js, utility/lmdb/environmentUtility.ts) must call
|
|
36
|
+
// configUtils.updateConfigObject() directly, or the derived value outlives what it was derived from
|
|
37
|
+
// and ships to every worker as an override.
|
|
38
|
+
const appliedOverrides = new Map<string, { value: any; base: any }>();
|
|
39
|
+
let inheritedOverridesApplied = false;
|
|
40
|
+
|
|
27
41
|
/**
|
|
28
42
|
* The base path of the HDB install is often referenced, but is referenced as a const variable at the top of many
|
|
29
43
|
* modules. This is a problem during install, as the path may not yet be defined. We offer a function to get the
|
|
@@ -62,11 +76,42 @@ export function get(propName: string): any {
|
|
|
62
76
|
* Note - this function will NOT update the config file. If you want to update the file
|
|
63
77
|
* use the updateConfigValue method in configUtils.
|
|
64
78
|
*
|
|
79
|
+
* The override is also recorded so it propagates to every worker thread spawned from here and
|
|
80
|
+
* survives a forced config reload — except a reload that changes this param on disk, which wins
|
|
81
|
+
* and retires the override (see reapplyAllOverrides()). `value` must be structured-cloneable.
|
|
82
|
+
*
|
|
65
83
|
* This function should only be used by the installer and unit tests.
|
|
66
84
|
* @param propName
|
|
67
85
|
* @param value
|
|
68
86
|
*/
|
|
69
87
|
export function setProperty(propName: string, value: any) {
|
|
88
|
+
// Snapshot rather than keep the caller's reference: initializePaths.js and environmentUtility.ts
|
|
89
|
+
// cache derived per-database paths by mutating the live `databases` value in place, which is
|
|
90
|
+
// often the very object handed to setProperty. Cloning here also settles cloneability at the
|
|
91
|
+
// caller that can still fix it — manageThreads.js's provider only logs a clone failure and
|
|
92
|
+
// spawns the worker on the on-disk config anyway.
|
|
93
|
+
let snapshot;
|
|
94
|
+
try {
|
|
95
|
+
snapshot = structuredClone(value);
|
|
96
|
+
} catch (err) {
|
|
97
|
+
throw new Error(`Config property '${propName}' cannot be set to a value that is not structured-cloneable`, {
|
|
98
|
+
cause: err,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Key by the canonical param name, not the raw alias passed in: CONFIG_PARAM_MAP is many-to-one
|
|
103
|
+
// (e.g. SERVER_PORT_KEY and OPERATIONSAPI_NETWORK_PORT both canonicalize to
|
|
104
|
+
// 'operationsApi_network_port'), and replay of a stale alias after the canonical one would
|
|
105
|
+
// reintroduce the parent/worker skew this map exists to prevent.
|
|
106
|
+
const canonicalName = (hdbTerms.CONFIG_PARAM_MAP as any)[propName.toLowerCase()] ?? propName;
|
|
107
|
+
const existing = appliedOverrides.get(canonicalName);
|
|
108
|
+
appliedOverrides.set(canonicalName, {
|
|
109
|
+
value: snapshot,
|
|
110
|
+
// Keep the first override's base: everything after it displaced an override, not a
|
|
111
|
+
// configured value, so it says nothing about what the config file held.
|
|
112
|
+
base: existing ? existing.base : snapshotConfigValue(configUtils.getConfigValue(propName)),
|
|
113
|
+
});
|
|
114
|
+
|
|
70
115
|
if (installPropsToSave[propName]) {
|
|
71
116
|
installProps[propName] = value;
|
|
72
117
|
}
|
|
@@ -74,6 +119,98 @@ export function setProperty(propName: string, value: any) {
|
|
|
74
119
|
configUtils.updateConfigObject(propName, value);
|
|
75
120
|
}
|
|
76
121
|
|
|
122
|
+
/**
|
|
123
|
+
* Every config override applied on this thread via setProperty(), for propagation to worker
|
|
124
|
+
* threads spawned from here (see server/threads/manageThreads.js's 'configOverrides'
|
|
125
|
+
* workerData provider). Returns undefined when nothing has been overridden, so a normal
|
|
126
|
+
* production boot contributes no extra workerData.
|
|
127
|
+
*/
|
|
128
|
+
export function getConfigOverrides(): Record<string, any> | undefined {
|
|
129
|
+
if (!appliedOverrides.size) return undefined;
|
|
130
|
+
const overrides = {};
|
|
131
|
+
for (const [propName, { value }] of appliedOverrides) {
|
|
132
|
+
overrides[propName] = value;
|
|
133
|
+
}
|
|
134
|
+
return overrides;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Replays the parent thread's config overrides (received via workerData) on THIS thread, so a
|
|
139
|
+
* worker's effective config matches its parent's rather than silently falling back to whatever
|
|
140
|
+
* is installed on disk. Applied once per thread, right after the on-disk config is first read —
|
|
141
|
+
* see initSync(). A later forced re-init is handled separately by reapplyAllOverrides(), since by
|
|
142
|
+
* then these inherited values are already folded into appliedOverrides.
|
|
143
|
+
*/
|
|
144
|
+
function applyInheritedConfigOverrides() {
|
|
145
|
+
const inherited = (workerData as any)?.configOverrides;
|
|
146
|
+
if (!inherited) return;
|
|
147
|
+
for (const propName of Object.keys(inherited)) {
|
|
148
|
+
setProperty(propName, inherited[propName]);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/** Detached copy of a config value; falls back to the value itself for the (unreached) non-cloneable case. */
|
|
153
|
+
function snapshotConfigValue(value: any) {
|
|
154
|
+
try {
|
|
155
|
+
return structuredClone(value);
|
|
156
|
+
} catch {
|
|
157
|
+
return value;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Key-ordering is a YAML formatting choice, not a config change, so it must not affect equality. */
|
|
162
|
+
function stableStringify(value: any) {
|
|
163
|
+
if (value === null || typeof value !== 'object') return JSON.stringify(value);
|
|
164
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(',')}]`;
|
|
165
|
+
return `{${Object.keys(value)
|
|
166
|
+
.sort()
|
|
167
|
+
.map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`)
|
|
168
|
+
.join(',')}}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* A reload rebuilds config values into fresh objects, so identity says nothing about whether the
|
|
173
|
+
* file changed. Anything this cannot serialize (a cyclic value, which structuredClone accepts)
|
|
174
|
+
* counts as changed rather than throwing out of the reload path — initSync()'s catch exits the
|
|
175
|
+
* process.
|
|
176
|
+
*/
|
|
177
|
+
function sameConfigValue(a: any, b: any) {
|
|
178
|
+
if (Object.is(a, b)) return true;
|
|
179
|
+
if (a === null || b === null || typeof a !== 'object' || typeof b !== 'object') return false;
|
|
180
|
+
try {
|
|
181
|
+
return stableStringify(a) === stableStringify(b);
|
|
182
|
+
} catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Replays this thread's overrides on top of a config that was just re-read from disk. A forced
|
|
189
|
+
* initSync() rebuilds the whole config, which would otherwise revert the thread to the on-disk
|
|
190
|
+
* config regardless of any overrides — the same defect applyInheritedConfigOverrides() closes for a
|
|
191
|
+
* worker's first boot, but for a reload on any thread, main included.
|
|
192
|
+
*
|
|
193
|
+
* An override does NOT survive a reload that changed its own param on disk. A forced reload is how
|
|
194
|
+
* an operator's config edit takes effect (the RESTART handlers in security/keys.ts and
|
|
195
|
+
* processManagement.js), so replaying unconditionally would make editing an overridden param a
|
|
196
|
+
* silent no-op for the life of the process. The edit is the newer intent, so the override retires
|
|
197
|
+
* entirely — dropped from the map, so it stops reaching newly spawned workers as well.
|
|
198
|
+
*
|
|
199
|
+
* Call only after configUtils.initConfig(force) has repopulated the config from disk.
|
|
200
|
+
*/
|
|
201
|
+
function reapplyAllOverrides() {
|
|
202
|
+
if (appliedOverrides.size === 0) return;
|
|
203
|
+
// Iterate a snapshot: setProperty() re-inserts into appliedOverrides as we go.
|
|
204
|
+
for (const [propName, { value, base }] of [...appliedOverrides]) {
|
|
205
|
+
if (!sameConfigValue(configUtils.getConfigValue(propName), base)) {
|
|
206
|
+
appliedOverrides.delete(propName);
|
|
207
|
+
log.notify(`Config reload changed '${propName}'; dropping this thread's in-process override of it`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
setProperty(propName, value);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
77
214
|
/**
|
|
78
215
|
* Checks to see if the Harper boot props file exists.
|
|
79
216
|
* If it does, it grabs the install user and settings path for future reference.
|
|
@@ -108,6 +245,21 @@ export function initSync(force: boolean = false) {
|
|
|
108
245
|
try {
|
|
109
246
|
if (propFileExists || doesPropFileExist() || commonUtils.noBootFile() || force) {
|
|
110
247
|
configUtils.initConfig(force);
|
|
248
|
+
if (!inheritedOverridesApplied) {
|
|
249
|
+
inheritedOverridesApplied = true;
|
|
250
|
+
applyInheritedConfigOverrides();
|
|
251
|
+
}
|
|
252
|
+
// force implies the on-disk config was just re-read from scratch, which would otherwise
|
|
253
|
+
// silently drop every override applied on this thread so far (inherited or local) —
|
|
254
|
+
// except the ones the re-read itself changed, which the config file wins.
|
|
255
|
+
if (force) reapplyAllOverrides();
|
|
256
|
+
|
|
257
|
+
// Sync installProps' HDB_ROOT from the config's rootPath *after* any override replay
|
|
258
|
+
// above (not before): setProperty() records a rootPath override under its canonical
|
|
259
|
+
// CONFIG_PARAM_MAP name, not under HDB_ROOT_KEY, so replaying it doesn't retrigger this
|
|
260
|
+
// same side effect below in setProperty() — this sync is what makes getHdbBasePath()
|
|
261
|
+
// track an isolated rootPath override instead of staying pinned to whatever the pre-replay
|
|
262
|
+
// disk read produced.
|
|
111
263
|
const configHdbRoot = configUtils.getConfigValue(hdbTerms.HDB_SETTINGS_NAMES.HDB_ROOT_KEY);
|
|
112
264
|
// Only overwrite if we actually got a value from config
|
|
113
265
|
if (configHdbRoot !== undefined) {
|
|
@@ -13,6 +13,7 @@ import * as lmdbTerms from './terms.ts';
|
|
|
13
13
|
import * as hdbTerms from '../hdbTerms.ts';
|
|
14
14
|
import { resetDatabases } from '../../resources/databases.ts';
|
|
15
15
|
import * as envMngr from '../environment/environmentManager.ts';
|
|
16
|
+
import { updateConfigObject } from '../../config/configUtils.ts';
|
|
16
17
|
|
|
17
18
|
const INTERNAL_DBIS_NAME = lmdbTerms.INTERNAL_DBIS_NAME;
|
|
18
19
|
const DBI_DEFINITION_NAME = lmdbTerms.DBI_DEFINITION_NAME;
|
|
@@ -109,7 +110,7 @@ export async function createEnvironment(
|
|
|
109
110
|
|
|
110
111
|
envName = envName.toString();
|
|
111
112
|
let schemasConfig = envMngr.get(hdbTerms.CONFIG_PARAMS.DATABASES);
|
|
112
|
-
if (!schemasConfig)
|
|
113
|
+
if (!schemasConfig) updateConfigObject(hdbTerms.CONFIG_PARAMS.DATABASES, (schemasConfig = {}));
|
|
113
114
|
if (!schemasConfig[dbName]) schemasConfig[dbName] = {};
|
|
114
115
|
schemasConfig[dbName].path = basePath;
|
|
115
116
|
try {
|