@harperfast/harper 5.2.0 → 5.2.1
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 +21 -4
- package/bin/harper.ts +20 -51
- package/bin/help.ts +216 -0
- package/components/Application.ts +236 -46
- package/components/ApplicationScope.ts +26 -0
- package/components/EntryHandler.ts +410 -105
- package/components/RuntimeModuleTracker.ts +189 -0
- package/components/Scope.ts +68 -24
- package/components/componentLoader.ts +70 -16
- package/components/deployLifecycle.ts +119 -33
- package/components/mcp/tools/operations.ts +6 -0
- package/components/operations.js +4 -6
- package/config/configUtils.ts +12 -9
- package/config-root.schema.json +10 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +26 -5
- package/dataLayer/hdbInfoController.ts +8 -0
- package/dataLayer/schemaDescribe.ts +2 -1
- package/dist/bin/copyDb.js +13 -2
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/bin/harper.d.ts +6 -0
- package/dist/bin/harper.js +18 -50
- package/dist/bin/harper.js.map +1 -1
- package/dist/bin/help.d.ts +8 -0
- package/dist/bin/help.js +192 -0
- package/dist/bin/help.js.map +1 -0
- package/dist/components/Application.d.ts +16 -1
- package/dist/components/Application.js +210 -38
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +7 -0
- package/dist/components/ApplicationScope.js +22 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/EntryHandler.d.ts +4 -4
- package/dist/components/EntryHandler.js +386 -95
- package/dist/components/EntryHandler.js.map +1 -1
- package/dist/components/RuntimeModuleTracker.d.ts +11 -0
- package/dist/components/RuntimeModuleTracker.js +189 -0
- package/dist/components/RuntimeModuleTracker.js.map +1 -0
- package/dist/components/Scope.d.ts +1 -0
- package/dist/components/Scope.js +69 -22
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/componentLoader.js +69 -10
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/deployLifecycle.d.ts +6 -2
- package/dist/components/deployLifecycle.js +109 -31
- package/dist/components/deployLifecycle.js.map +1 -1
- package/dist/components/mcp/tools/operations.js +6 -0
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/operations.js +4 -6
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.js +13 -9
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +12 -5
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/hdbInfoController.js +4 -0
- package/dist/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +2 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/resources/DatabaseTransaction.d.ts +55 -0
- package/dist/resources/DatabaseTransaction.js +282 -103
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +8 -2
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Resources.js +22 -4
- package/dist/resources/Resources.js.map +1 -1
- package/dist/resources/Table.d.ts +6 -5
- package/dist/resources/Table.js +92 -15
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.js +6 -6
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/blob.d.ts +0 -1
- package/dist/resources/blob.js +15 -7
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/databases.d.ts +22 -5
- package/dist/resources/databases.js +118 -9
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/jsResource.d.ts +4 -26
- package/dist/resources/jsResource.js +5 -59
- package/dist/resources/jsResource.js.map +1 -1
- package/dist/resources/models/Models.d.ts +11 -1
- package/dist/resources/models/Models.js +10 -1
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/backendRegistry.d.ts +9 -0
- package/dist/resources/models/backendRegistry.js +10 -0
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/openaiStream.d.ts +16 -1
- package/dist/resources/models/openaiStream.js +113 -21
- package/dist/resources/models/openaiStream.js.map +1 -1
- package/dist/resources/models/v1/chatCompletions.d.ts +54 -0
- package/dist/resources/models/v1/chatCompletions.js +115 -0
- package/dist/resources/models/v1/chatCompletions.js.map +1 -0
- package/dist/resources/models/v1/embeddings.d.ts +11 -0
- package/dist/resources/models/v1/embeddings.js +71 -0
- package/dist/resources/models/v1/embeddings.js.map +1 -0
- package/dist/resources/models/v1/errors.d.ts +54 -0
- package/dist/resources/models/v1/errors.js +130 -0
- package/dist/resources/models/v1/errors.js.map +1 -0
- package/dist/resources/models/v1/index.d.ts +36 -0
- package/dist/resources/models/v1/index.js +75 -0
- package/dist/resources/models/v1/index.js.map +1 -0
- package/dist/resources/models/v1/models.d.ts +26 -0
- package/dist/resources/models/v1/models.js +44 -0
- package/dist/resources/models/v1/models.js.map +1 -0
- package/dist/resources/models/v1/translation.d.ts +133 -0
- package/dist/resources/models/v1/translation.js +298 -0
- package/dist/resources/models/v1/translation.js.map +1 -0
- package/dist/resources/roles.d.ts +1 -1
- package/dist/resources/roles.js +54 -7
- package/dist/resources/roles.js.map +1 -1
- package/dist/security/jsLoader.js +84 -33
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/role.js +4 -0
- package/dist/security/role.js.map +1 -1
- package/dist/security/superUserGuard.d.ts +7 -0
- package/dist/security/superUserGuard.js +23 -0
- package/dist/security/superUserGuard.js.map +1 -0
- package/dist/security/tokenAuthentication.d.ts +0 -1
- package/dist/security/tokenAuthentication.js +6 -2
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/security/user.d.ts +6 -1
- package/dist/security/user.js +23 -1
- package/dist/security/user.js.map +1 -1
- package/dist/server/http.d.ts +23 -2
- package/dist/server/http.js +99 -12
- package/dist/server/http.js.map +1 -1
- package/dist/server/operationsServer.d.ts +0 -2
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/multipartParser.js +9 -0
- package/dist/server/serverHelpers/multipartParser.js.map +1 -1
- package/dist/server/serverHelpers/operationAuthorizationState.d.ts +2 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js +13 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -0
- package/dist/server/serverHelpers/registeredOperations.d.ts +3 -2
- package/dist/server/serverHelpers/registeredOperations.js +14 -13
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
- package/dist/server/serverHelpers/serverHandlers.js +19 -3
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +1 -1
- package/dist/server/serverHelpers/serverUtilities.js +10 -7
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/static.js +57 -18
- package/dist/server/static.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +17 -0
- package/dist/server/storageReclamation.js +90 -6
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/socketRouter.js +20 -0
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +12 -0
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/diff/differential.js +7 -3
- package/dist/sqlEngine/diff/differential.js.map +1 -1
- package/dist/sqlTranslator/index.js +6 -1
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/upgrade/upgradePrompt.d.ts +2 -2
- package/dist/upgrade/upgradePrompt.js +22 -3
- package/dist/upgrade/upgradePrompt.js.map +1 -1
- package/dist/utility/errors/commonErrors.d.ts +1 -0
- package/dist/utility/errors/commonErrors.js +1 -0
- package/dist/utility/errors/commonErrors.js.map +1 -1
- package/dist/utility/hdbTerms.d.ts +2 -0
- package/dist/utility/hdbTerms.js +2 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/install/installer.js +32 -0
- package/dist/utility/install/installer.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +7 -0
- package/dist/utility/logging/harper_logger.js +57 -24
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/npm-shrinkwrap.json +197 -193
- package/package.json +9 -7
- package/resources/DatabaseTransaction.ts +310 -101
- package/resources/ResourceInterface.ts +8 -2
- package/resources/Resources.ts +22 -4
- package/resources/Table.ts +321 -243
- package/resources/analytics/write.ts +22 -20
- package/resources/blob.ts +15 -8
- package/resources/databases.ts +123 -12
- package/resources/jsResource.ts +5 -62
- package/resources/models/Models.ts +14 -1
- package/resources/models/backendRegistry.ts +10 -0
- package/resources/models/openaiStream.ts +131 -19
- package/resources/models/v1/chatCompletions.ts +128 -0
- package/resources/models/v1/embeddings.ts +70 -0
- package/resources/models/v1/errors.ts +141 -0
- package/resources/models/v1/index.ts +72 -0
- package/resources/models/v1/models.ts +53 -0
- package/resources/models/v1/translation.ts +362 -0
- package/resources/roles.ts +67 -7
- package/security/jsLoader.ts +84 -30
- package/security/role.ts +7 -0
- package/security/superUserGuard.ts +20 -0
- package/security/tokenAuthentication.ts +6 -3
- package/security/user.ts +26 -1
- package/server/DESIGN.md +45 -34
- package/server/http.ts +100 -13
- package/server/operationsServer.ts +0 -2
- package/server/serverHelpers/multipartParser.ts +9 -0
- package/server/serverHelpers/operationAuthorizationState.ts +11 -0
- package/server/serverHelpers/registeredOperations.ts +19 -15
- package/server/serverHelpers/serverHandlers.js +20 -3
- package/server/serverHelpers/serverUtilities.ts +10 -7
- package/server/static.ts +75 -20
- package/server/storageReclamation.ts +104 -8
- package/server/threads/socketRouter.ts +20 -0
- package/server/threads/threadServer.js +11 -0
- package/sqlTranslator/index.ts +6 -1
- package/static/defaultConfig.yaml +2 -0
- package/studio/web/assets/{Chat-DoVWScmq.js → Chat-aApwhRmz.js} +2 -2
- package/studio/web/assets/{Chat-DoVWScmq.js.map → Chat-aApwhRmz.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js → FloatingChat-sC0H91n9.js} +4 -4
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js.map → FloatingChat-sC0H91n9.js.map} +1 -1
- package/studio/web/assets/{apiToken-BUI_04o7.js → apiToken-DZ8JhHOI.js} +2 -2
- package/studio/web/assets/{apiToken-BUI_04o7.js.map → apiToken-DZ8JhHOI.js.map} +1 -1
- package/studio/web/assets/{applications-D03NA7wW.js → applications-9SEVLIO5.js} +2 -2
- package/studio/web/assets/{applications-D03NA7wW.js.map → applications-9SEVLIO5.js.map} +1 -1
- package/studio/web/assets/{index-Bh_CNAHr.js → index-Dy3uDGXb.js} +6 -6
- package/studio/web/assets/{index-Bh_CNAHr.js.map → index-Dy3uDGXb.js.map} +1 -1
- package/studio/web/assets/{index.lazy-Dx3MpyDC.js → index.lazy-CN1zq4I4.js} +4 -4
- package/studio/web/assets/{index.lazy-Dx3MpyDC.js.map → index.lazy-CN1zq4I4.js.map} +1 -1
- package/studio/web/assets/{notifications-0edoFTsb.js → notifications-BFAF07xr.js} +2 -2
- package/studio/web/assets/{notifications-0edoFTsb.js.map → notifications-BFAF07xr.js.map} +1 -1
- package/studio/web/assets/{notifications-CwKhipK7.js → notifications-CKlYVvVN.js} +2 -2
- package/studio/web/assets/{notifications-CwKhipK7.js.map → notifications-CKlYVvVN.js.map} +1 -1
- package/studio/web/assets/{profile-DUfEPQtx.js → profile-A1zhEdFG.js} +2 -2
- package/studio/web/assets/{profile-DUfEPQtx.js.map → profile-A1zhEdFG.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js → setComponentFile-SEtBt_GV.js} +2 -2
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js.map → setComponentFile-SEtBt_GV.js.map} +1 -1
- package/studio/web/assets/{setup-B56Oz1_u.js → setup-BisINqdH.js} +2 -2
- package/studio/web/assets/{setup-B56Oz1_u.js.map → setup-BisINqdH.js.map} +1 -1
- package/studio/web/assets/{status-BAod7p3o.js → status-BuQoCc7l.js} +2 -2
- package/studio/web/assets/{status-BAod7p3o.js.map → status-BuQoCc7l.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js → swagger-ui-react-CAi_s1PC.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.js.map → swagger-ui-react-CAi_s1PC.js.map} +1 -1
- package/studio/web/assets/{tsMode-CrHCRjTK.js → tsMode-DT74tlkM.js} +2 -2
- package/studio/web/assets/{tsMode-CrHCRjTK.js.map → tsMode-DT74tlkM.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js → useEntityRestURL-CGRGc1n7.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js.map → useEntityRestURL-CGRGc1n7.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/upgradePrompt.ts +22 -3
- package/utility/errors/commonErrors.ts +2 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/install/installer.ts +37 -0
- package/utility/logging/harper_logger.ts +57 -26
package/security/jsLoader.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { models as harperModelsSingleton } from '../resources/models/Models.ts';
|
|
|
6
6
|
import { defineTable, types } from '../resources/defineTable.ts';
|
|
7
7
|
import { defineResource, t, schemaOf, projectTableFragment } from '../resources/defineResource.ts';
|
|
8
8
|
import { readFile } from 'node:fs/promises';
|
|
9
|
-
import { dirname, isAbsolute } from 'node:path';
|
|
9
|
+
import { dirname, isAbsolute, join, relative, resolve, sep } from 'node:path';
|
|
10
10
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
11
11
|
import { SourceTextModule, SyntheticModule, createContext, runInContext, runInThisContext } from 'node:vm';
|
|
12
12
|
import { ApplicationScope } from '../components/ApplicationScope.ts';
|
|
@@ -28,7 +28,6 @@ import {
|
|
|
28
28
|
statSync,
|
|
29
29
|
realpathSync,
|
|
30
30
|
} from 'node:fs';
|
|
31
|
-
import { join } from 'node:path';
|
|
32
31
|
import { EventEmitter } from 'node:events';
|
|
33
32
|
import { whenComponentsLoaded } from '../server/threads/threadServer.js';
|
|
34
33
|
|
|
@@ -105,6 +104,7 @@ async function importScoped(moduleUrl: string, scope?: ApplicationScope) {
|
|
|
105
104
|
return await loadModuleWithVM(moduleUrl, scope, true);
|
|
106
105
|
}
|
|
107
106
|
}
|
|
107
|
+
if (scope) scope.markNativeRuntime?.();
|
|
108
108
|
// important! we need to await the import, otherwise the error will not be caught
|
|
109
109
|
return await import(moduleUrl);
|
|
110
110
|
} catch (err) {
|
|
@@ -178,7 +178,10 @@ function walkExportsConditions(entry: unknown, conditions: readonly string[]): s
|
|
|
178
178
|
* ERR_PACKAGE_PATH_NOT_EXPORTED for pure-ESM packages (exports map with only "import"
|
|
179
179
|
* conditions and no "require").
|
|
180
180
|
*/
|
|
181
|
-
function resolveESMPackageExports(
|
|
181
|
+
function resolveESMPackageExports(
|
|
182
|
+
specifier: string,
|
|
183
|
+
fromDir: string
|
|
184
|
+
): { resolvedUrl: string; packageJsonUrl: string; packageJsonSource: Buffer } | null {
|
|
182
185
|
const isScoped = specifier.startsWith('@');
|
|
183
186
|
const parts = specifier.split('/');
|
|
184
187
|
const packageName = isScoped ? `${parts[0]}/${parts[1]}` : parts[0];
|
|
@@ -189,8 +192,11 @@ function resolveESMPackageExports(specifier: string, fromDir: string): string |
|
|
|
189
192
|
while (true) {
|
|
190
193
|
const pkgRoot = join(dir, 'node_modules', packageName);
|
|
191
194
|
let pkgJson: any;
|
|
195
|
+
let packageJsonSource: Buffer;
|
|
196
|
+
const packageJsonPath = join(pkgRoot, 'package.json');
|
|
192
197
|
try {
|
|
193
|
-
|
|
198
|
+
packageJsonSource = readFileSync(packageJsonPath);
|
|
199
|
+
pkgJson = JSON.parse(packageJsonSource.toString());
|
|
194
200
|
} catch {
|
|
195
201
|
const parent = dirname(dir);
|
|
196
202
|
if (parent === dir) return null;
|
|
@@ -220,10 +226,25 @@ function resolveESMPackageExports(specifier: string, fromDir: string): string |
|
|
|
220
226
|
const relative = walkExportsConditions(entry, ['import', 'node', 'default']);
|
|
221
227
|
if (!relative) return null;
|
|
222
228
|
|
|
223
|
-
return
|
|
229
|
+
return {
|
|
230
|
+
resolvedUrl: pathToFileURL(realpathSync(join(pkgRoot, relative))).toString(),
|
|
231
|
+
packageJsonUrl: pathToFileURL(realpathSync(packageJsonPath)).toString(),
|
|
232
|
+
packageJsonSource,
|
|
233
|
+
};
|
|
224
234
|
}
|
|
225
235
|
}
|
|
226
236
|
|
|
237
|
+
function normalizeImportedModule(importedModule: any): any {
|
|
238
|
+
const cjsModule = importedModule['module.exports'];
|
|
239
|
+
if (cjsModule) {
|
|
240
|
+
importedModule = importedModule.default ? { default: importedModule.default, ...cjsModule } : cjsModule;
|
|
241
|
+
}
|
|
242
|
+
if (!importedModule.default) {
|
|
243
|
+
importedModule = { default: importedModule, ...importedModule };
|
|
244
|
+
}
|
|
245
|
+
return importedModule;
|
|
246
|
+
}
|
|
247
|
+
|
|
227
248
|
/**
|
|
228
249
|
* Load a module using Node's vm.Module API with optional sandboxing
|
|
229
250
|
* @param moduleUrl - The URL of the module to load
|
|
@@ -286,11 +307,18 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
286
307
|
try {
|
|
287
308
|
const resolved = createRequire(resolveReferrer).resolve(specifier);
|
|
288
309
|
if (isAbsolute(resolved)) {
|
|
289
|
-
|
|
310
|
+
const resolvedUrl = pathToFileURL(resolved).toString();
|
|
311
|
+
scope.recordModuleResolution?.(specifier, resolveReferrer, resolvedUrl);
|
|
312
|
+
return resolvedUrl;
|
|
290
313
|
}
|
|
291
314
|
return resolved;
|
|
292
315
|
} catch (err) {
|
|
293
|
-
|
|
316
|
+
const errorCode = (err as { code?: string })?.code;
|
|
317
|
+
const isBarePackage = !specifier.startsWith('.') && !isAbsolute(specifier) && !specifier.includes(':');
|
|
318
|
+
if (
|
|
319
|
+
isBarePackage &&
|
|
320
|
+
(errorCode === 'ERR_PACKAGE_PATH_NOT_EXPORTED' || (process.versions.bun && errorCode === 'MODULE_NOT_FOUND'))
|
|
321
|
+
) {
|
|
294
322
|
// Pure-ESM package: CJS resolver cannot match an exports map that only has
|
|
295
323
|
// "import" conditions (no "require"). Resolve the entry file by walking
|
|
296
324
|
// the filesystem and evaluating the exports map with ESM import conditions.
|
|
@@ -298,7 +326,10 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
298
326
|
? dirname(fileURLToPath(resolveReferrer))
|
|
299
327
|
: dirname(resolveReferrer);
|
|
300
328
|
const esmResolved = resolveESMPackageExports(specifier, referrerDir);
|
|
301
|
-
if (esmResolved)
|
|
329
|
+
if (esmResolved) {
|
|
330
|
+
scope.recordLoadedModule?.(esmResolved.packageJsonUrl, esmResolved.packageJsonSource);
|
|
331
|
+
return esmResolved.resolvedUrl;
|
|
332
|
+
}
|
|
302
333
|
}
|
|
303
334
|
throw err;
|
|
304
335
|
}
|
|
@@ -336,8 +367,15 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
336
367
|
return getHarperExports(scope);
|
|
337
368
|
}
|
|
338
369
|
if (resolvedUrl.startsWith('file://')) {
|
|
339
|
-
|
|
340
|
-
|
|
370
|
+
if (resolvedUrl.endsWith('.node')) {
|
|
371
|
+
checkAllowedModulePath(resolvedUrl, scope.allowedPath);
|
|
372
|
+
const nativeModule = require(fileURLToPath(resolvedUrl));
|
|
373
|
+
scope.markNativeRuntime?.();
|
|
374
|
+
return nativeModule;
|
|
375
|
+
}
|
|
376
|
+
const contents = readFileSync(new URL(resolvedUrl));
|
|
377
|
+
scope.recordLoadedModule?.(resolvedUrl, contents);
|
|
378
|
+
return loadCJS(resolvedUrl, contents.toString('utf-8')).exports;
|
|
341
379
|
}
|
|
342
380
|
return require(resolvedUrl);
|
|
343
381
|
};
|
|
@@ -456,11 +494,11 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
456
494
|
if (specifier.startsWith('.')) {
|
|
457
495
|
return true;
|
|
458
496
|
}
|
|
459
|
-
|
|
460
497
|
// Check the dependencyLoader for definitive settings, if it is native we always use native loader
|
|
461
498
|
if (scope.dependencyLoader === 'native') {
|
|
462
499
|
return false;
|
|
463
500
|
}
|
|
501
|
+
if (isApplicationLocalModule(resolvedUrl)) return true;
|
|
464
502
|
|
|
465
503
|
// If it is set to always use the app module loader
|
|
466
504
|
if (scope.dependencyLoader === 'app') {
|
|
@@ -474,6 +512,16 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
474
512
|
return false;
|
|
475
513
|
}
|
|
476
514
|
|
|
515
|
+
function isApplicationLocalModule(url: string): boolean {
|
|
516
|
+
if (!scope.runtimeRoot || !url.startsWith('file://') || url.includes('/node_modules/')) return false;
|
|
517
|
+
const modulePath = fileURLToPath(url);
|
|
518
|
+
const relativePath = relative(resolve(scope.runtimeRoot), modulePath);
|
|
519
|
+
return (
|
|
520
|
+
relativePath === '' ||
|
|
521
|
+
(!relativePath.startsWith(`..${sep}`) && relativePath !== '..' && !isAbsolute(relativePath))
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
477
525
|
/**
|
|
478
526
|
* Linker function for module resolution during instantiation.
|
|
479
527
|
* This is synchronous because Node's module.link() requires the linker
|
|
@@ -544,22 +592,6 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
544
592
|
);
|
|
545
593
|
}
|
|
546
594
|
|
|
547
|
-
/**
|
|
548
|
-
* Normalize imported module to ensure it has proper exports including default
|
|
549
|
-
*/
|
|
550
|
-
function normalizeImportedModule(importedModule: any): any {
|
|
551
|
-
const cjsModule = importedModule['module.exports'];
|
|
552
|
-
if (cjsModule) {
|
|
553
|
-
// back-compat import
|
|
554
|
-
importedModule = importedModule.default ? { default: importedModule.default, ...cjsModule } : cjsModule;
|
|
555
|
-
}
|
|
556
|
-
// Ensure there's a default export for ESM imports that expect it
|
|
557
|
-
if (!importedModule.default) {
|
|
558
|
-
importedModule = { default: importedModule, ...importedModule };
|
|
559
|
-
}
|
|
560
|
-
return importedModule;
|
|
561
|
-
}
|
|
562
|
-
|
|
563
595
|
/**
|
|
564
596
|
* Create a SourceTextModule or SyntheticModule from source code
|
|
565
597
|
*/
|
|
@@ -638,8 +670,14 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
638
670
|
|
|
639
671
|
if (url.startsWith('file://') && usePrivateGlobal) {
|
|
640
672
|
checkAllowedModulePath(url, scope.allowedPath);
|
|
641
|
-
|
|
642
|
-
|
|
673
|
+
if (url.endsWith('.node')) {
|
|
674
|
+
const nativeModule = createRequire(url)(fileURLToPath(url));
|
|
675
|
+
scope.markNativeRuntime?.();
|
|
676
|
+
return createSyntheticModule(url, normalizeImportedModule(nativeModule));
|
|
677
|
+
}
|
|
678
|
+
const contents = readFileSync(new URL(url));
|
|
679
|
+
scope.recordLoadedModule?.(url, contents);
|
|
680
|
+
return createModuleFromSource(url, contents.toString('utf-8'), usePrivateGlobal);
|
|
643
681
|
}
|
|
644
682
|
|
|
645
683
|
// For Node.js built-in modules (node:) and npm packages without application loader for dependency
|
|
@@ -647,6 +685,7 @@ async function loadModuleWithVM(moduleUrl: string, scope: ApplicationScope, useC
|
|
|
647
685
|
if (replacedModule) {
|
|
648
686
|
return createSyntheticModule(url, normalizeImportedModule(replacedModule));
|
|
649
687
|
}
|
|
688
|
+
if (isApplicationLocalModule(url)) scope.markNativeRuntime?.();
|
|
650
689
|
return import(url).then((importedModule) => createSyntheticModule(url, normalizeImportedModule(importedModule)));
|
|
651
690
|
}
|
|
652
691
|
// Load the entry module
|
|
@@ -679,6 +718,7 @@ async function getCompartment(scope: ApplicationScope, globals) {
|
|
|
679
718
|
const resolved = createRequire(moduleReferrer).resolve(moduleSpecifier);
|
|
680
719
|
if (isAbsolute(resolved)) {
|
|
681
720
|
const resolvedURL = pathToFileURL(resolved).toString();
|
|
721
|
+
scope.recordModuleResolution?.(moduleSpecifier, moduleReferrer, resolvedURL);
|
|
682
722
|
return resolvedURL;
|
|
683
723
|
}
|
|
684
724
|
return moduleSpecifier;
|
|
@@ -693,8 +733,22 @@ async function getCompartment(scope: ApplicationScope, globals) {
|
|
|
693
733
|
Object.assign(exports, harperExports);
|
|
694
734
|
},
|
|
695
735
|
};
|
|
736
|
+
} else if (moduleSpecifier.startsWith('file:') && moduleSpecifier.endsWith('.node')) {
|
|
737
|
+
checkAllowedModulePath(moduleSpecifier, scope.allowedPath);
|
|
738
|
+
const nativeModule = createRequire(moduleSpecifier)(fileURLToPath(moduleSpecifier));
|
|
739
|
+
scope.markNativeRuntime?.();
|
|
740
|
+
const moduleExports = normalizeImportedModule(nativeModule);
|
|
741
|
+
return {
|
|
742
|
+
imports: [],
|
|
743
|
+
exports: Object.keys(moduleExports),
|
|
744
|
+
execute(exports) {
|
|
745
|
+
Object.assign(exports, moduleExports);
|
|
746
|
+
},
|
|
747
|
+
};
|
|
696
748
|
} else if (moduleSpecifier.startsWith('file:') && !moduleSpecifier.includes('node_modules')) {
|
|
697
|
-
|
|
749
|
+
const moduleContents = await readFile(new URL(moduleSpecifier));
|
|
750
|
+
scope.recordLoadedModule?.(moduleSpecifier, moduleContents);
|
|
751
|
+
let moduleText = moduleContents.toString('utf-8');
|
|
698
752
|
// Handle JSON files in compartment mode the same way as in VM mode
|
|
699
753
|
if (moduleSpecifier.endsWith('.json')) {
|
|
700
754
|
const jsonData = parseJsonModule(moduleText, moduleSpecifier);
|
package/security/role.ts
CHANGED
|
@@ -16,6 +16,7 @@ import SearchObject from '../dataLayer/SearchObject.ts';
|
|
|
16
16
|
import SearchByHashObject from '../dataLayer/SearchByHashObject.ts';
|
|
17
17
|
import { handleHDBError } from '../utility/errors/hdbError.ts';
|
|
18
18
|
import { HDB_ERROR_MSGS, HTTP_STATUS_CODES } from '../utility/errors/commonErrors.ts';
|
|
19
|
+
import { assertActiveSuperUserRemains } from './user.ts';
|
|
19
20
|
|
|
20
21
|
import { UserEventMsg } from '../server/threads/itc.js';
|
|
21
22
|
|
|
@@ -104,6 +105,12 @@ export async function alterRole(role: any) {
|
|
|
104
105
|
|
|
105
106
|
role = scrubRoleDetails(role);
|
|
106
107
|
|
|
108
|
+
if (role.permission) {
|
|
109
|
+
await assertActiveSuperUserRemains((user) =>
|
|
110
|
+
user.role?.id === role.id ? { ...user, role: { ...user.role, permission: role.permission } } : user
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
107
114
|
let updateObject = {
|
|
108
115
|
operation: 'update',
|
|
109
116
|
schema: 'system',
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { User } from './user.ts';
|
|
2
|
+
|
|
3
|
+
function isActiveSuperUser(user?: User): boolean {
|
|
4
|
+
return Boolean(user?.active && user.role?.permission?.super_user);
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Whether an active super_user still exists once `simulate` is applied to every user; `simulate`
|
|
9
|
+
* returns undefined for a user the change removes. True when none exists beforehand — there is no
|
|
10
|
+
* last one to protect, and refusing would block the repair that restores one.
|
|
11
|
+
*/
|
|
12
|
+
export function activeSuperUserRemains(users: Iterable<User>, simulate: (user: User) => User | undefined): boolean {
|
|
13
|
+
let present = false;
|
|
14
|
+
let remains = false;
|
|
15
|
+
for (const user of users) {
|
|
16
|
+
if (isActiveSuperUser(user)) present = true;
|
|
17
|
+
if (isActiveSuperUser(simulate(user))) remains = true;
|
|
18
|
+
}
|
|
19
|
+
return remains || !present;
|
|
20
|
+
}
|
|
@@ -18,6 +18,7 @@ import { findAndValidateUser, type User } from './user.ts';
|
|
|
18
18
|
import { update } from '../dataLayer/insert.ts';
|
|
19
19
|
import UpdateObject from '../dataLayer/UpdateObject.ts';
|
|
20
20
|
import * as signalling from '../utility/signalling.ts';
|
|
21
|
+
import { isOperationAuthorizationBypassed } from '../server/serverHelpers/operationAuthorizationState.ts';
|
|
21
22
|
import { UserEventMsg } from '../server/threads/itc.js';
|
|
22
23
|
import * as env from '../utility/environment/environmentManager.ts';
|
|
23
24
|
env.initSync();
|
|
@@ -52,7 +53,6 @@ interface AuthObject {
|
|
|
52
53
|
password?: string;
|
|
53
54
|
role?: string;
|
|
54
55
|
expires_in?: string | number;
|
|
55
|
-
bypass_auth?: boolean;
|
|
56
56
|
hdb_user?: User;
|
|
57
57
|
// 'login' mints a single short-lived, login-scoped token instead of an operation/refresh pair —
|
|
58
58
|
// see TOKEN_TYPE.LOGIN.
|
|
@@ -135,8 +135,11 @@ export async function createTokens(authObj: AuthObject): Promise<JWTTokens> {
|
|
|
135
135
|
|
|
136
136
|
let user: any;
|
|
137
137
|
try {
|
|
138
|
-
//
|
|
139
|
-
|
|
138
|
+
// Trusted bypass is dispatch/async-context state (set by a component calling
|
|
139
|
+
// server.operation(..., false)), never a body field — authObj.bypass_auth is
|
|
140
|
+
// caller-controlled and would let anyone mint tokens for an arbitrary username
|
|
141
|
+
// without a password (see operationAuthorizationState.ts).
|
|
142
|
+
let validatePassword: boolean = !isOperationAuthorizationBypassed();
|
|
140
143
|
if (!authObj.username && !authObj.password) {
|
|
141
144
|
// if the username and password are not provided, use the hdb_user making the request.
|
|
142
145
|
authObj.username = authObj.hdb_user?.username;
|
package/security/user.ts
CHANGED
|
@@ -9,6 +9,7 @@ const ACTIVE_BOOLEAN = 'active must be true or false';
|
|
|
9
9
|
export {
|
|
10
10
|
addUser,
|
|
11
11
|
alterUser,
|
|
12
|
+
assertActiveSuperUserRemains,
|
|
12
13
|
dropUser,
|
|
13
14
|
getSuperUser,
|
|
14
15
|
userInfo,
|
|
@@ -105,6 +106,7 @@ import * as password from '../utility/password.ts';
|
|
|
105
106
|
import { server } from '../server/Server.ts';
|
|
106
107
|
import * as terms from '../utility/hdbTerms.ts';
|
|
107
108
|
import { expandOperationsPerms } from '../utility/operationPermissions.ts';
|
|
109
|
+
import { activeSuperUserRemains } from './superUserGuard.ts';
|
|
108
110
|
|
|
109
111
|
server.getUser = (username: string, password?: string | null): Promise<User> => {
|
|
110
112
|
return findAndValidateUser(username, password, password != null);
|
|
@@ -202,6 +204,7 @@ async function alterUser(jsonMessage) {
|
|
|
202
204
|
throw new Error(EMPTY_ROLE);
|
|
203
205
|
}
|
|
204
206
|
// Invalid roles will be found in the role search
|
|
207
|
+
let nextRole;
|
|
205
208
|
if (cleanUser.role) {
|
|
206
209
|
const roleData = await search.searchByValue({
|
|
207
210
|
schema: 'system',
|
|
@@ -217,7 +220,16 @@ async function alterUser(jsonMessage) {
|
|
|
217
220
|
if (roleData.length > 1)
|
|
218
221
|
throw new ClientError(HDB_ERROR_MSGS.DUP_ROLES_FOUND(cleanUser.role), HTTP_STATUS_CODES.CONFLICT);
|
|
219
222
|
|
|
220
|
-
|
|
223
|
+
nextRole = roleData[0];
|
|
224
|
+
cleanUser.role = nextRole.id;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (nextRole !== undefined || cleanUser.active !== undefined) {
|
|
228
|
+
await assertActiveSuperUserRemains((user) =>
|
|
229
|
+
user.username === cleanUser.username
|
|
230
|
+
? { ...user, role: nextRole ?? user.role, active: cleanUser.active ?? user.active }
|
|
231
|
+
: user
|
|
232
|
+
);
|
|
221
233
|
}
|
|
222
234
|
|
|
223
235
|
const updateResponse = await insert.update({
|
|
@@ -240,6 +252,8 @@ async function dropUser(user: User | any): Promise<string> {
|
|
|
240
252
|
if (usersWithRolesMap.get(user.username) === undefined)
|
|
241
253
|
throw new ClientError(HDB_ERROR_MSGS.USER_NOT_EXIST(user.username), HTTP_STATUS_CODES.NOT_FOUND);
|
|
242
254
|
|
|
255
|
+
await assertActiveSuperUserRemains((existing) => (existing.username === user.username ? undefined : existing));
|
|
256
|
+
|
|
243
257
|
const deleteResponse = await promiseDelete({
|
|
244
258
|
table: 'hdb_user',
|
|
245
259
|
schema: 'system',
|
|
@@ -381,6 +395,17 @@ async function getUsersWithRolesCache() {
|
|
|
381
395
|
return usersWithRolesMap;
|
|
382
396
|
}
|
|
383
397
|
|
|
398
|
+
/**
|
|
399
|
+
* `simulate` maps each user to what the pending change would make it; undefined means removed.
|
|
400
|
+
* Local view only — `system` is replicated, so a lagging node can approve what another rejects.
|
|
401
|
+
*/
|
|
402
|
+
async function assertActiveSuperUserRemains(simulate: (user: User) => User | undefined): Promise<void> {
|
|
403
|
+
const users = await getUsersWithRolesCache();
|
|
404
|
+
if (!users) return;
|
|
405
|
+
if (activeSuperUserRemains(users.values(), simulate)) return;
|
|
406
|
+
throw new ClientError(HDB_ERROR_MSGS.LAST_SUPER_USER, HTTP_STATUS_CODES.CONFLICT);
|
|
407
|
+
}
|
|
408
|
+
|
|
384
409
|
/**
|
|
385
410
|
* iterates global.hdb_users to find and validate the username & optionally the password as well as if they are active.
|
|
386
411
|
* @param {string} username
|
package/server/DESIGN.md
CHANGED
|
@@ -43,20 +43,20 @@ A request entering `http.ts` does **not** go through Fastify. The two `handleApp
|
|
|
43
43
|
|
|
44
44
|
### Helpers
|
|
45
45
|
|
|
46
|
-
| File | Purpose
|
|
47
|
-
| ------------------------------------------ |
|
|
48
|
-
| `serverHelpers/Request.ts` | Wraps `IncomingMessage` with Harper-specific fields (user, response, headers).
|
|
49
|
-
| `serverHelpers/Headers.ts` | Header mutation/merge utilities.
|
|
50
|
-
| `serverHelpers/contentTypes.ts` | (de)serialization registry; `serialize`, `serializeMessage`, `getDeserializer`.
|
|
51
|
-
| `serverHelpers/serverUtilities.ts` | `OperationDefinition` and shared helpers.
|
|
52
|
-
| `serverHelpers/OperationFunctionObject.ts` | Wraps an operation handler with metadata.
|
|
53
|
-
| `serverHelpers/JSONStream.ts` | Streaming JSON output for large responses.
|
|
54
|
-
| `nodeName.ts` | Resolves this node's name (config → hostname).
|
|
55
|
-
| `static.ts` | Static file serving for component-bundled assets.
|
|
56
|
-
| `throttle.ts` | Per-IP / per-user request throttling.
|
|
57
|
-
| `storageReclamation.ts` | Disk-pressure signals to downstream consumers.
|
|
58
|
-
| `serverRegistry.ts` | Trivial registry export.
|
|
59
|
-
| `status/` | Server status reporting (cluster status, per-port info).
|
|
46
|
+
| File | Purpose |
|
|
47
|
+
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
48
|
+
| `serverHelpers/Request.ts` | Wraps `IncomingMessage` with Harper-specific fields (user, response, headers). |
|
|
49
|
+
| `serverHelpers/Headers.ts` | Header mutation/merge utilities. |
|
|
50
|
+
| `serverHelpers/contentTypes.ts` | (de)serialization registry; `serialize`, `serializeMessage`, `getDeserializer`. |
|
|
51
|
+
| `serverHelpers/serverUtilities.ts` | `OperationDefinition` and shared helpers. |
|
|
52
|
+
| `serverHelpers/OperationFunctionObject.ts` | Wraps an operation handler with metadata. |
|
|
53
|
+
| `serverHelpers/JSONStream.ts` | Streaming JSON output for large responses. |
|
|
54
|
+
| `nodeName.ts` | Resolves this node's name (config → hostname). |
|
|
55
|
+
| `static.ts` | Static file serving for component-bundled assets. |
|
|
56
|
+
| `throttle.ts` | Per-IP / per-user request throttling. |
|
|
57
|
+
| `storageReclamation.ts` | Disk-pressure signals to downstream consumers; `getStorageSpaceStats()` is the shared quota-aware (falls back to `statfs`) source of available/free/size storage numbers — used by `Table.getStorageStats()` (#1976). NOT used for blob storage path weighting (`resources/blob.ts`): quota-status.json is a single instance-wide figure, so it can't distinguish between multiple `STORAGE_BLOBPATHS` disks — that still needs raw per-path `statfs`. |
|
|
58
|
+
| `serverRegistry.ts` | Trivial registry export. |
|
|
59
|
+
| `status/` | Server status reporting (cluster status, per-port info). |
|
|
60
60
|
|
|
61
61
|
### Threads
|
|
62
62
|
|
|
@@ -88,26 +88,26 @@ Single-instance background tasks pick their thread by what state they touch:
|
|
|
88
88
|
|
|
89
89
|
Every entry is a top-level function or named const. Jump via go-to-symbol or `grep -n 'function <name>' server/http.ts`.
|
|
90
90
|
|
|
91
|
-
| Symbol
|
|
92
|
-
|
|
|
93
|
-
| `registerUdsCleanupPaths`, `cleanupUdsFiles`, `writeUdsMetadata`, `cleanupSocketsDirectory` | UDS socket / metadata file lifecycle.
|
|
94
|
-
| `handleApplication(scope)`
|
|
95
|
-
| `getHttpOptions()`
|
|
96
|
-
| `deliverSocket()`
|
|
97
|
-
| `proxyRequest()`
|
|
98
|
-
| `registerServer()`
|
|
99
|
-
| `getPorts()`
|
|
100
|
-
| `httpServer()`
|
|
101
|
-
| `getHTTPServer(port, secure, options)`
|
|
102
|
-
| `makeCallbackChain()`
|
|
103
|
-
| `unhandled()`
|
|
104
|
-
| `onRequest()`
|
|
105
|
-
| `onUpgrade()` / `upgradeListeners` (const)
|
|
106
|
-
| `onWebSocket()` / `websocketListeners` (const)
|
|
107
|
-
| `enableProxyProtocol()`
|
|
108
|
-
| `defaultNotFound()`
|
|
109
|
-
| `logRequest()`
|
|
110
|
-
| `getRequestId()`
|
|
91
|
+
| Symbol | What it does |
|
|
92
|
+
| ---------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
93
|
+
| `registerUdsCleanupPaths`, `recordUdsBindSuccess`, `cleanupUdsFiles`, `markUdsBindFailed`, `writeUdsMetadata`, `cleanupSocketsDirectory` | UDS socket / metadata file lifecycle. Ownership-aware: `recordUdsBindSuccess` captures the inode a worker's own bind confirmed; `cleanupUdsFiles`/`markUdsBindFailed` only unlink a path when the inode on disk still matches, so an overlapping restart's outgoing worker can never delete the replacement that already rebound the same path (see restartWorkers() in manageThreads.js). `cleanupSocketsDirectory` is the separate crash-path sweep, run once from `socketRouter.ts`'s `startHTTPThreads` on main-thread startup, before any worker can bind. |
|
|
94
|
+
| `handleApplication(scope)` | Component entry point — captures `httpOptions` for the scope. |
|
|
95
|
+
| `getHttpOptions()` | Returns the current scope's `HttpOptions`. |
|
|
96
|
+
| `deliverSocket()` | IPC-delivered socket handoff from `socketRouter`. |
|
|
97
|
+
| `proxyRequest()` | Cross-port request routing. |
|
|
98
|
+
| `registerServer()` | Records a server for a port in the `SERVERS` map. |
|
|
99
|
+
| `getPorts()` | Resolves listener options → list of `{port, secure}`. |
|
|
100
|
+
| `httpServer()` | Main listener registration entry point. |
|
|
101
|
+
| `getHTTPServer(port, secure, options)` | **The largest function in the file.** Creates/retrieves the underlying Node HTTP/HTTPS server. Wires `request`, `upgrade`, error handlers, TLS context, and the per-port middleware chain. |
|
|
102
|
+
| `makeCallbackChain()` | Builds the per-port handler chain via `middlewareChain.topoSort`. |
|
|
103
|
+
| `unhandled()` | Terminal 404 handler. |
|
|
104
|
+
| `onRequest()` | Thin alias of `httpServer({requestOnly: true})`. |
|
|
105
|
+
| `onUpgrade()` / `upgradeListeners` (const) | Register HTTP upgrade listener; underlying list. |
|
|
106
|
+
| `onWebSocket()` / `websocketListeners` (const) | Register WebSocket listener; auto-adds default upgrade handler the first time it runs for a port. Underlying list of registrations. |
|
|
107
|
+
| `enableProxyProtocol()` | PROXY v1/v2 stripping on UDS mirrors (Node 24+-compatible workaround). Decoding lives in `serverHelpers/proxyProtocol.ts`; v2 TLVs forward the client source address plus the connection's TLS facts a fronting proxy (symphony) observed — ALPN, SNI authority, TLS version/cipher, JA3/JA4 fingerprints, and the mTLS client cert chain. These are surfaced on `request.connectionInfo` (see below); the verified cert chain is additionally exposed with TLSSocket semantics (`authorized`, `getPeerCertificate()`) so HTTP/MQTT mTLS auth works unchanged, and the SSL TLV lets `request.protocol` report `https` on the plaintext UDS mirror. A peer that stalls mid-header is destroyed after `prehandoffTimeout` (default 10s), matching `withProxyProtocol`'s guard on the raw-socket path. |
|
|
108
|
+
| `defaultNotFound()` | Default 404 response. |
|
|
109
|
+
| `logRequest()` | Per-request access log line. |
|
|
110
|
+
| `getRequestId()` | Generates the per-request correlation ID. |
|
|
111
111
|
|
|
112
112
|
### Middleware ordering (`before` / `after`)
|
|
113
113
|
|
|
@@ -137,6 +137,17 @@ Consequences worth knowing:
|
|
|
137
137
|
|
|
138
138
|
---
|
|
139
139
|
|
|
140
|
+
## Operations authorization boundary
|
|
141
|
+
|
|
142
|
+
Operations request bodies are untrusted data. `serverHandlers.js → handlePostRequest()` rejects
|
|
143
|
+
prototype-mutating property names and strips the legacy `bypass_auth` property before dispatch.
|
|
144
|
+
`serverUtilities.ts → chooseOperation()` never reads authorization control from the body: trusted
|
|
145
|
+
internal callers pass bypass state as a separate argument and expose it to operation handlers only
|
|
146
|
+
through `operationAuthorizationState.ts`'s async context. When an operation registered by a component
|
|
147
|
+
must run on a worker, `registeredOperations.ts` carries that state in the same-process ITC envelope,
|
|
148
|
+
separately from the structured-cloned body. Never attach trusted dispatch state to an operation
|
|
149
|
+
payload.
|
|
150
|
+
|
|
140
151
|
## Resource ↔ HTTP boundary
|
|
141
152
|
|
|
142
153
|
`REST.ts → http(request, nextHandler)` is the chief integration point: it takes a `Request`, asks the `Resources` registry for a match, builds a `RequestTarget`, and dispatches into the Resource class's static method. Cache headers are translated to `request.expiresAt` / `onlyIfCached` / `noCache` flags within the same function.
|