@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.
Files changed (117) hide show
  1. package/bin/copyDb.ts +19 -7
  2. package/components/deploymentRecorder.ts +102 -11
  3. package/components/operations.js +4 -0
  4. package/config/configUtils.ts +66 -11
  5. package/config/harperConfigEnvVars.ts +114 -17
  6. package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
  7. package/dist/bin/copyDb.js +20 -7
  8. package/dist/bin/copyDb.js.map +1 -1
  9. package/dist/components/deploymentRecorder.d.ts +6 -0
  10. package/dist/components/deploymentRecorder.js +94 -12
  11. package/dist/components/deploymentRecorder.js.map +1 -1
  12. package/dist/components/operations.js +4 -0
  13. package/dist/components/operations.js.map +1 -1
  14. package/dist/config/configUtils.d.ts +6 -0
  15. package/dist/config/configUtils.js +65 -9
  16. package/dist/config/configUtils.js.map +1 -1
  17. package/dist/config/harperConfigEnvVars.js +108 -16
  18. package/dist/config/harperConfigEnvVars.js.map +1 -1
  19. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
  20. package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
  21. package/dist/resources/DatabaseTransaction.d.ts +2 -0
  22. package/dist/resources/DatabaseTransaction.js +6 -5
  23. package/dist/resources/DatabaseTransaction.js.map +1 -1
  24. package/dist/resources/Table.d.ts +2 -1
  25. package/dist/resources/Table.js +66 -16
  26. package/dist/resources/Table.js.map +1 -1
  27. package/dist/resources/auditStore.js +27 -1
  28. package/dist/resources/auditStore.js.map +1 -1
  29. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +15 -2
  30. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +128 -39
  31. package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
  32. package/dist/resources/search.d.ts +1 -1
  33. package/dist/resources/search.js +7 -3
  34. package/dist/resources/search.js.map +1 -1
  35. package/dist/server/threads/manageThreads.js +8 -0
  36. package/dist/server/threads/manageThreads.js.map +1 -1
  37. package/dist/sqlEngine/parser/normalizer.js +37 -9
  38. package/dist/sqlEngine/parser/normalizer.js.map +1 -1
  39. package/dist/utility/environment/environmentManager.d.ts +11 -0
  40. package/dist/utility/environment/environmentManager.js +154 -0
  41. package/dist/utility/environment/environmentManager.js.map +1 -1
  42. package/dist/utility/lmdb/environmentUtility.js +2 -1
  43. package/dist/utility/lmdb/environmentUtility.js.map +1 -1
  44. package/npm-shrinkwrap.json +13 -7
  45. package/package.json +2 -2
  46. package/resources/DESIGN.md +1 -0
  47. package/resources/DatabaseTransaction.ts +7 -5
  48. package/resources/Table.ts +77 -16
  49. package/resources/auditStore.ts +32 -1
  50. package/resources/indexes/HierarchicalNavigableSmallWorld.ts +119 -45
  51. package/resources/search.ts +8 -3
  52. package/server/threads/manageThreads.js +8 -0
  53. package/studio/web/assets/{Chat-aApwhRmz.js → Chat-BMUjPklt.js} +5 -5
  54. package/studio/web/assets/{Chat-aApwhRmz.js.map → Chat-BMUjPklt.js.map} +1 -1
  55. package/studio/web/assets/{FloatingChat-sC0H91n9.js → FloatingChat-Cbghuxe5.js} +5 -5
  56. package/studio/web/assets/{FloatingChat-sC0H91n9.js.map → FloatingChat-Cbghuxe5.js.map} +1 -1
  57. package/studio/web/assets/{apiToken-DZ8JhHOI.js → apiToken-CkgTQAqc.js} +2 -2
  58. package/studio/web/assets/{apiToken-DZ8JhHOI.js.map → apiToken-CkgTQAqc.js.map} +1 -1
  59. package/studio/web/assets/{applications-9SEVLIO5.js → applications-WvF4NOTN.js} +13 -13
  60. package/studio/web/assets/applications-WvF4NOTN.js.map +1 -0
  61. package/studio/web/assets/chevron-right-anDJCGlp.js +2 -0
  62. package/studio/web/assets/chevron-right-anDJCGlp.js.map +1 -0
  63. package/studio/web/assets/index-CDeqVx71.css +1 -0
  64. package/studio/web/assets/index-Sevt3MVx.js +819 -0
  65. package/studio/web/assets/index-Sevt3MVx.js.map +1 -0
  66. package/studio/web/assets/index.lazy-Dg5YYPGg.js +14 -0
  67. package/studio/web/assets/{index.lazy-CN1zq4I4.js.map → index.lazy-Dg5YYPGg.js.map} +1 -1
  68. package/studio/web/assets/{jsonMode-Cjj7dtlr.js → jsonMode-CBSEleE7.js} +2 -2
  69. package/studio/web/assets/{jsonMode-Cjj7dtlr.js.map → jsonMode-CBSEleE7.js.map} +1 -1
  70. package/studio/web/assets/languageServices-gxedeeTo.js +2 -0
  71. package/studio/web/assets/languageServices-gxedeeTo.js.map +1 -0
  72. package/studio/web/assets/{notifications-BFAF07xr.js → notifications-Ap84YyQp.js} +2 -2
  73. package/studio/web/assets/{notifications-BFAF07xr.js.map → notifications-Ap84YyQp.js.map} +1 -1
  74. package/studio/web/assets/notifications-vehqyOLM.js +2 -0
  75. package/studio/web/assets/{notifications-CKlYVvVN.js.map → notifications-vehqyOLM.js.map} +1 -1
  76. package/studio/web/assets/{pollUnlessForbidden-HHdQZW1N.js → pollUnlessForbidden-BhzefVRn.js} +2 -2
  77. package/studio/web/assets/pollUnlessForbidden-BhzefVRn.js.map +1 -0
  78. package/studio/web/assets/{profile-A1zhEdFG.js → profile-DzDpRLHY.js} +2 -2
  79. package/studio/web/assets/{profile-A1zhEdFG.js.map → profile-DzDpRLHY.js.map} +1 -1
  80. package/studio/web/assets/queryClient-CgtkoiCY.js +2 -0
  81. package/studio/web/assets/queryClient-CgtkoiCY.js.map +1 -0
  82. package/studio/web/assets/regions-DpJ8s7iN.js +2 -0
  83. package/studio/web/assets/regions-DpJ8s7iN.js.map +1 -0
  84. package/studio/web/assets/register-BDy01-q3.js +3 -0
  85. package/studio/web/assets/register-BDy01-q3.js.map +1 -0
  86. package/studio/web/assets/register-CQHRwNP4.js +3 -0
  87. package/studio/web/assets/register-CQHRwNP4.js.map +1 -0
  88. package/studio/web/assets/{setComponentFile-SEtBt_GV.js → setComponentFile-D0aPcI3W.js} +2 -2
  89. package/studio/web/assets/{setComponentFile-SEtBt_GV.js.map → setComponentFile-D0aPcI3W.js.map} +1 -1
  90. package/studio/web/assets/setup-I15FAF9H.js +3 -0
  91. package/studio/web/assets/{setup-BisINqdH.js.map → setup-I15FAF9H.js.map} +1 -1
  92. package/studio/web/assets/status-BF1eJshJ.js +62 -0
  93. package/studio/web/assets/status-BF1eJshJ.js.map +1 -0
  94. package/studio/web/assets/{swagger-ui-react-CAi_s1PC.js → swagger-ui-react-DwSPXXhl.js} +2 -2
  95. package/studio/web/assets/{swagger-ui-react-CAi_s1PC.js.map → swagger-ui-react-DwSPXXhl.js.map} +1 -1
  96. package/studio/web/assets/{textarea-DhLMZ3PA.js → table-DpOjNQHt.js} +2 -10
  97. package/studio/web/assets/table-DpOjNQHt.js.map +1 -0
  98. package/studio/web/assets/{tsMode-DT74tlkM.js → tsMode-Bu51A-VG.js} +2 -2
  99. package/studio/web/assets/{tsMode-DT74tlkM.js.map → tsMode-Bu51A-VG.js.map} +1 -1
  100. package/studio/web/assets/{useEntityRestURL-CGRGc1n7.js → useEntityRestURL-DrV1zMrN.js} +2 -2
  101. package/studio/web/assets/{useEntityRestURL-CGRGc1n7.js.map → useEntityRestURL-DrV1zMrN.js.map} +1 -1
  102. package/studio/web/index.html +6 -7
  103. package/utility/environment/environmentManager.ts +152 -0
  104. package/utility/lmdb/environmentUtility.ts +2 -1
  105. package/studio/web/assets/applications-9SEVLIO5.js.map +0 -1
  106. package/studio/web/assets/index-C8Mv9kHV.css +0 -1
  107. package/studio/web/assets/index-Dy3uDGXb.js +0 -809
  108. package/studio/web/assets/index-Dy3uDGXb.js.map +0 -1
  109. package/studio/web/assets/index.lazy-CN1zq4I4.js +0 -14
  110. package/studio/web/assets/notifications-CKlYVvVN.js +0 -2
  111. package/studio/web/assets/pollUnlessForbidden-HHdQZW1N.js.map +0 -1
  112. package/studio/web/assets/queryClient-CbA8wM7J.js +0 -2
  113. package/studio/web/assets/queryClient-CbA8wM7J.js.map +0 -1
  114. package/studio/web/assets/setup-BisINqdH.js +0 -3
  115. package/studio/web/assets/status-BuQoCc7l.js +0 -62
  116. package/studio/web/assets/status-BuQoCc7l.js.map +0 -1
  117. 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) envMngr.setProperty(hdbTerms.CONFIG_PARAMS.DATABASES, (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 {