@harperfast/harper 5.2.0 → 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 +40 -11
- 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/deploymentRecorder.ts +102 -11
- package/components/mcp/tools/operations.ts +6 -0
- package/components/operations.js +8 -6
- package/config/configUtils.ts +78 -20
- package/config/harperConfigEnvVars.ts +114 -17
- package/config-root.schema.json +10 -0
- package/dataLayer/harperBridge/ResourceBridge.ts +26 -5
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -4
- package/dataLayer/hdbInfoController.ts +8 -0
- package/dataLayer/schemaDescribe.ts +2 -1
- package/dist/bin/copyDb.js +33 -9
- 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/deploymentRecorder.d.ts +6 -0
- package/dist/components/deploymentRecorder.js +94 -12
- package/dist/components/deploymentRecorder.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 +8 -6
- package/dist/components/operations.js.map +1 -1
- package/dist/config/configUtils.d.ts +6 -0
- package/dist/config/configUtils.js +78 -18
- 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/ResourceBridge.js +12 -5
- package/dist/dataLayer/harperBridge/ResourceBridge.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/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 +57 -0
- package/dist/resources/DatabaseTransaction.js +284 -104
- 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 +8 -6
- package/dist/resources/Table.js +158 -31
- 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/auditStore.js +27 -1
- package/dist/resources/auditStore.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/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/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/resources/search.d.ts +1 -1
- package/dist/resources/search.js +7 -3
- package/dist/resources/search.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/manageThreads.js +8 -0
- package/dist/server/threads/manageThreads.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/sqlEngine/parser/normalizer.js +37 -9
- package/dist/sqlEngine/parser/normalizer.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/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/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/lmdb/environmentUtility.js +2 -1
- package/dist/utility/lmdb/environmentUtility.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 +208 -198
- package/package.json +10 -8
- package/resources/DESIGN.md +1 -0
- package/resources/DatabaseTransaction.ts +313 -102
- package/resources/ResourceInterface.ts +8 -2
- package/resources/Resources.ts +22 -4
- package/resources/Table.ts +398 -259
- package/resources/analytics/write.ts +22 -20
- package/resources/auditStore.ts +32 -1
- package/resources/blob.ts +15 -8
- package/resources/databases.ts +123 -12
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +119 -45
- 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/resources/search.ts +8 -3
- 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/manageThreads.js +8 -0
- 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-BMUjPklt.js} +5 -5
- package/studio/web/assets/{Chat-DoVWScmq.js.map → Chat-BMUjPklt.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js → FloatingChat-Cbghuxe5.js} +5 -5
- package/studio/web/assets/{FloatingChat-UZ2NsUOZ.js.map → FloatingChat-Cbghuxe5.js.map} +1 -1
- package/studio/web/assets/{apiToken-BUI_04o7.js → apiToken-CkgTQAqc.js} +2 -2
- package/studio/web/assets/{apiToken-BUI_04o7.js.map → apiToken-CkgTQAqc.js.map} +1 -1
- package/studio/web/assets/{applications-D03NA7wW.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-Dx3MpyDC.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-0edoFTsb.js → notifications-Ap84YyQp.js} +2 -2
- package/studio/web/assets/{notifications-0edoFTsb.js.map → notifications-Ap84YyQp.js.map} +1 -1
- package/studio/web/assets/notifications-vehqyOLM.js +2 -0
- package/studio/web/assets/{notifications-CwKhipK7.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-DUfEPQtx.js → profile-DzDpRLHY.js} +2 -2
- package/studio/web/assets/{profile-DUfEPQtx.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-DMPo4UjC.js → setComponentFile-D0aPcI3W.js} +2 -2
- package/studio/web/assets/{setComponentFile-DMPo4UjC.js.map → setComponentFile-D0aPcI3W.js.map} +1 -1
- package/studio/web/assets/setup-I15FAF9H.js +3 -0
- package/studio/web/assets/{setup-B56Oz1_u.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-lQrBxfwM.js → swagger-ui-react-DwSPXXhl.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-lQrBxfwM.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-CrHCRjTK.js → tsMode-Bu51A-VG.js} +2 -2
- package/studio/web/assets/{tsMode-CrHCRjTK.js.map → tsMode-Bu51A-VG.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js → useEntityRestURL-DrV1zMrN.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-DoaBMEvU.js.map → useEntityRestURL-DrV1zMrN.js.map} +1 -1
- package/studio/web/index.html +6 -7
- package/upgrade/upgradePrompt.ts +22 -3
- package/utility/environment/environmentManager.ts +152 -0
- package/utility/errors/commonErrors.ts +2 -0
- package/utility/hdbTerms.ts +2 -0
- package/utility/install/installer.ts +37 -0
- package/utility/lmdb/environmentUtility.ts +2 -1
- package/utility/logging/harper_logger.ts +57 -26
- package/studio/web/assets/applications-D03NA7wW.js.map +0 -1
- package/studio/web/assets/index-Bh_CNAHr.js +0 -809
- package/studio/web/assets/index-Bh_CNAHr.js.map +0 -1
- package/studio/web/assets/index-C8Mv9kHV.css +0 -1
- package/studio/web/assets/index.lazy-Dx3MpyDC.js +0 -14
- package/studio/web/assets/notifications-CwKhipK7.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-B56Oz1_u.js +0 -3
- package/studio/web/assets/status-BAod7p3o.js +0 -62
- package/studio/web/assets/status-BAod7p3o.js.map +0 -1
- package/studio/web/assets/textarea-DhLMZ3PA.js.map +0 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { lstatSync, readFileSync, readlinkSync } from 'node:fs';
|
|
3
|
+
import { lstat, readFile, readlink } from 'node:fs/promises';
|
|
4
|
+
import { createRequire, Module } from 'node:module';
|
|
5
|
+
import { dirname, isAbsolute, relative, resolve, sep } from 'node:path';
|
|
6
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
7
|
+
|
|
8
|
+
type Source = string | Buffer;
|
|
9
|
+
|
|
10
|
+
const MODULE_COMPARE_CONCURRENCY = 16;
|
|
11
|
+
const RESOLUTION_EXTENSIONS = ['', '.js', '.json', '.node', '.ts', '.tsx', '.cjs', '.mjs'];
|
|
12
|
+
|
|
13
|
+
type Resolution = {
|
|
14
|
+
resolvedUrl: string;
|
|
15
|
+
higherPriorityCandidates?: Map<string, string>;
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export class RuntimeModuleTracker {
|
|
19
|
+
#getRoot: () => string | undefined;
|
|
20
|
+
#modules = new Map<string, string>();
|
|
21
|
+
#resolutions = new Map<string, Resolution>();
|
|
22
|
+
#nativeRuntime = false;
|
|
23
|
+
#deployInFlight = false;
|
|
24
|
+
#loadedDuringDeploy = false;
|
|
25
|
+
#comparison?: Promise<boolean>;
|
|
26
|
+
|
|
27
|
+
constructor(getRoot: () => string | undefined) {
|
|
28
|
+
this.#getRoot = getRoot;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
beginDeploy(): void {
|
|
32
|
+
if (this.#deployInFlight) return;
|
|
33
|
+
this.#deployInFlight = true;
|
|
34
|
+
this.#loadedDuringDeploy = false;
|
|
35
|
+
this.#comparison = undefined;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
markNativeRuntime(): void {
|
|
39
|
+
this.#nativeRuntime = true;
|
|
40
|
+
if (this.#deployInFlight) this.#loadedDuringDeploy = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
recordModule(moduleUrl: string, source: Source): void {
|
|
44
|
+
const modulePath = this.#localPath(moduleUrl);
|
|
45
|
+
if (!modulePath) return;
|
|
46
|
+
if (!this.#modules.has(modulePath)) this.#modules.set(modulePath, digest(source));
|
|
47
|
+
if (this.#deployInFlight) this.#loadedDuringDeploy = true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
recordResolution(specifier: string, referrer: string, resolvedUrl: string): void {
|
|
51
|
+
const referrerPath = this.#localPath(referrer);
|
|
52
|
+
const resolvedPath = this.#localPath(resolvedUrl);
|
|
53
|
+
if (!referrerPath || !resolvedPath) return;
|
|
54
|
+
const key = `${referrerPath}\0${specifier}`;
|
|
55
|
+
if (!this.#resolutions.has(key)) {
|
|
56
|
+
const candidates = higherPriorityResolutionCandidates(specifier, referrerPath, resolvedPath);
|
|
57
|
+
this.#resolutions.set(key, {
|
|
58
|
+
resolvedUrl,
|
|
59
|
+
higherPriorityCandidates: candidates
|
|
60
|
+
? new Map(candidates.map((candidate) => [candidate, candidateState(candidate)]))
|
|
61
|
+
: undefined,
|
|
62
|
+
});
|
|
63
|
+
if (this.#deployInFlight) this.#loadedDuringDeploy = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
finishDeploy(): Promise<boolean> {
|
|
68
|
+
if (!this.#deployInFlight) return this.#comparison ?? Promise.resolve(false);
|
|
69
|
+
this.#deployInFlight = false;
|
|
70
|
+
this.#comparison = this.#compare();
|
|
71
|
+
return this.#comparison;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
async #compare(): Promise<boolean> {
|
|
75
|
+
if (this.#nativeRuntime || this.#loadedDuringDeploy) return true;
|
|
76
|
+
const modules = [...this.#modules];
|
|
77
|
+
for (let start = 0; start < modules.length; start += MODULE_COMPARE_CONCURRENCY) {
|
|
78
|
+
const changed = await Promise.all(
|
|
79
|
+
modules.slice(start, start + MODULE_COMPARE_CONCURRENCY).map(async ([modulePath, previousDigest]) => {
|
|
80
|
+
try {
|
|
81
|
+
return digest(await readFile(modulePath)) !== previousDigest;
|
|
82
|
+
} catch {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
);
|
|
87
|
+
if (changed.includes(true)) return true;
|
|
88
|
+
}
|
|
89
|
+
for (const [key, resolution] of this.#resolutions) {
|
|
90
|
+
const separator = key.indexOf('\0');
|
|
91
|
+
const referrerPath = key.slice(0, separator);
|
|
92
|
+
const specifier = key.slice(separator + 1);
|
|
93
|
+
if (
|
|
94
|
+
resolution.higherPriorityCandidates &&
|
|
95
|
+
(
|
|
96
|
+
await Promise.all(
|
|
97
|
+
[...resolution.higherPriorityCandidates].map(
|
|
98
|
+
async ([candidate, previousState]) => (await candidateStateAsync(candidate)) !== previousState
|
|
99
|
+
)
|
|
100
|
+
)
|
|
101
|
+
).includes(true)
|
|
102
|
+
)
|
|
103
|
+
return true;
|
|
104
|
+
try {
|
|
105
|
+
// Node does not invalidate this cache when a component tree is replaced in place.
|
|
106
|
+
if (!invalidateResolutionCache(specifier, referrerPath, resolution.resolvedUrl)) return true;
|
|
107
|
+
const resolved = createRequire(pathToFileURL(referrerPath)).resolve(specifier);
|
|
108
|
+
const resolvedUrl = isAbsolute(resolved) ? pathToFileURL(resolved).toString() : resolved;
|
|
109
|
+
if (resolvedUrl !== resolution.resolvedUrl) return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
#localPath(moduleUrl: string): string | undefined {
|
|
118
|
+
if (!moduleUrl.startsWith('file:')) return;
|
|
119
|
+
const root = this.#getRoot();
|
|
120
|
+
if (!root) return;
|
|
121
|
+
const modulePath = fileURLToPath(moduleUrl);
|
|
122
|
+
const relativePath = relative(resolve(root), modulePath);
|
|
123
|
+
if (
|
|
124
|
+
relativePath === '' ||
|
|
125
|
+
(!relativePath.startsWith(`..${sep}`) && relativePath !== '..' && !isAbsolute(relativePath))
|
|
126
|
+
)
|
|
127
|
+
return modulePath;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function higherPriorityResolutionCandidates(
|
|
132
|
+
specifier: string,
|
|
133
|
+
referrerPath: string,
|
|
134
|
+
resolvedPath: string
|
|
135
|
+
): string[] | undefined {
|
|
136
|
+
if (!specifier.startsWith('.')) return;
|
|
137
|
+
const basePath = resolve(dirname(referrerPath), specifier);
|
|
138
|
+
const candidates = [...new Set(RESOLUTION_EXTENSIONS.map((extension) => basePath + extension))];
|
|
139
|
+
candidates.push(resolve(basePath, 'package.json'));
|
|
140
|
+
for (const extension of RESOLUTION_EXTENSIONS.slice(1)) candidates.push(resolve(basePath, `index${extension}`));
|
|
141
|
+
const resolvedIndex = candidates.indexOf(resolvedPath);
|
|
142
|
+
if (resolvedIndex === -1) return;
|
|
143
|
+
return candidates.slice(0, resolvedIndex);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function candidateState(path: string): string {
|
|
147
|
+
try {
|
|
148
|
+
const stats = lstatSync(path);
|
|
149
|
+
if (stats.isSymbolicLink()) return `link:${readlinkSync(path)}`;
|
|
150
|
+
if (stats.isDirectory()) return 'directory';
|
|
151
|
+
if (!stats.isFile()) return 'other';
|
|
152
|
+
return path.endsWith('package.json') ? `file:${digest(readFileSync(path))}` : 'file';
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing';
|
|
155
|
+
return `error:${(error as NodeJS.ErrnoException).code ?? 'unknown'}`;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function candidateStateAsync(path: string): Promise<string> {
|
|
160
|
+
try {
|
|
161
|
+
const stats = await lstat(path);
|
|
162
|
+
if (stats.isSymbolicLink()) return `link:${await readlink(path)}`;
|
|
163
|
+
if (stats.isDirectory()) return 'directory';
|
|
164
|
+
if (!stats.isFile()) return 'other';
|
|
165
|
+
return path.endsWith('package.json') ? `file:${digest(await readFile(path))}` : 'file';
|
|
166
|
+
} catch (error) {
|
|
167
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') return 'missing';
|
|
168
|
+
return `error:${(error as NodeJS.ErrnoException).code ?? 'unknown'}`;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function invalidateResolutionCache(specifier: string, referrerPath: string, previousResolvedUrl: string): boolean {
|
|
173
|
+
const pathCache = (Module as unknown as { _pathCache?: Record<string, string> })._pathCache;
|
|
174
|
+
if (!pathCache) return false;
|
|
175
|
+
const previousPath = previousResolvedUrl.startsWith('file:')
|
|
176
|
+
? fileURLToPath(previousResolvedUrl)
|
|
177
|
+
: previousResolvedUrl;
|
|
178
|
+
const relativeKey = `${specifier}\0${dirname(referrerPath)}`;
|
|
179
|
+
if (pathCache[relativeKey] === previousPath) delete pathCache[relativeKey];
|
|
180
|
+
if (specifier.startsWith('.')) return true;
|
|
181
|
+
for (const cacheKey of Object.keys(pathCache)) {
|
|
182
|
+
if (cacheKey.startsWith(`${specifier}\0`) && pathCache[cacheKey] === previousPath) delete pathCache[cacheKey];
|
|
183
|
+
}
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function digest(source: Source): string {
|
|
188
|
+
return createHash('sha256').update(source).digest('base64');
|
|
189
|
+
}
|
package/components/Scope.ts
CHANGED
|
@@ -38,8 +38,10 @@ export type ScopeEventsMap = {
|
|
|
38
38
|
// file-driven work to avoid acting on intermediate states.
|
|
39
39
|
'deploy:start': [componentName: string];
|
|
40
40
|
// Fired after deploy I/O completes (success or failure). The scope's
|
|
41
|
-
// EntryHandlers have been
|
|
42
|
-
//
|
|
41
|
+
// EntryHandlers have been resumed by this point; their replacement watcher
|
|
42
|
+
// generation compares the post-deploy scan with the retained pre-deploy
|
|
43
|
+
// snapshot, so subsequent events are the logical differences of that tree,
|
|
44
|
+
// not a replay of every surviving file as an `add`.
|
|
43
45
|
'deploy:end': [componentName: string];
|
|
44
46
|
[record: string]: [...args: unknown[]];
|
|
45
47
|
};
|
|
@@ -64,11 +66,8 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
64
66
|
#pendingInitialLoads: Set<Promise<void>>;
|
|
65
67
|
#deployStartHandler: (name: string) => void;
|
|
66
68
|
#deployEndHandler: (name: string) => void;
|
|
67
|
-
// While a deploy of this component is in flight, EntryHandler events do not
|
|
68
|
-
// drive requestRestart() — the deploy itself produces hundreds of file
|
|
69
|
-
// changes that would otherwise pile up. A single coalesced restart is
|
|
70
|
-
// triggered by the post-deploy re-scan instead.
|
|
71
69
|
#deployInFlight: boolean = false;
|
|
70
|
+
#restartRequestedDuringDeploy: boolean = false;
|
|
72
71
|
applicationScope?: ApplicationScope;
|
|
73
72
|
|
|
74
73
|
options: OptionsWatcher;
|
|
@@ -113,6 +112,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
113
112
|
this.#directory = directory;
|
|
114
113
|
this.#configFilePath = configFilePath;
|
|
115
114
|
this.#logger = loggerWithTag(this.#appName);
|
|
115
|
+
this.#deployInFlight = deployLifecycle.isDeployInFlight(this.#appName);
|
|
116
116
|
|
|
117
117
|
this.databaseEvents = databaseEventsEmitter;
|
|
118
118
|
this.applicationScope = applicationScope;
|
|
@@ -156,6 +156,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
156
156
|
this.options = new OptionsWatcher(pluginName, configFilePath, this.#logger, isRootConfig)
|
|
157
157
|
.on('error', this.#handleError.bind(this))
|
|
158
158
|
.on('change', this.#optionsWatcherChangeListener.bind(this)())
|
|
159
|
+
.on('remove', this.#optionsWatcherRemoveListener())
|
|
159
160
|
.on('ready', this.#handleOptionsWatcherReady.bind(this));
|
|
160
161
|
|
|
161
162
|
// Bridge cross-thread deploy lifecycle events for this component. The
|
|
@@ -254,7 +255,8 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
254
255
|
}
|
|
255
256
|
|
|
256
257
|
#handleError(error: unknown): void {
|
|
257
|
-
this.emit('error', error);
|
|
258
|
+
if (this.listenerCount('error') > 0) this.emit('error', error);
|
|
259
|
+
else this.#logger.error?.('Error in component scope:', error);
|
|
258
260
|
}
|
|
259
261
|
|
|
260
262
|
async close(): Promise<this> {
|
|
@@ -301,6 +303,8 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
301
303
|
|
|
302
304
|
#onDeployStart(componentName: string): void {
|
|
303
305
|
this.#deployInFlight = true;
|
|
306
|
+
this.#restartRequestedDuringDeploy = false;
|
|
307
|
+
this.applicationScope?.beginDeploy();
|
|
304
308
|
// Pause each EntryHandler so it stops emitting events for the
|
|
305
309
|
// intermediate filesystem state the deploy is writing, and so it
|
|
306
310
|
// releases its inotify handles while npm install is unpacking
|
|
@@ -315,25 +319,27 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
315
319
|
|
|
316
320
|
#onDeployEnd(componentName: string): void {
|
|
317
321
|
this.#deployInFlight = false;
|
|
322
|
+
const restartRequestedDuringDeploy = this.#restartRequestedDuringDeploy;
|
|
323
|
+
this.#restartRequestedDuringDeploy = false;
|
|
318
324
|
|
|
319
|
-
// Resume
|
|
320
|
-
// throwing in its deploy:end handler would abort this function and
|
|
321
|
-
// leave the watchers permanently paused (surfaced by Gemini review).
|
|
322
|
-
// The fresh chokidar watcher does an initial scan and emits add events
|
|
323
|
-
// for the post-deploy tree; the existing per-event listener calls
|
|
324
|
-
// scope.requestRestart() for each, and the restart debounce in
|
|
325
|
-
// componentLoader collapses them into a single restart cycle. Plugin
|
|
326
|
-
// handlers stay attached across the pause.
|
|
325
|
+
// Resume before notifying plugins so a throwing deploy:end listener cannot strand the watchers.
|
|
327
326
|
for (const entryHandler of this.#entryHandlers) {
|
|
328
|
-
void entryHandler.resume();
|
|
327
|
+
void entryHandler.resume().catch(() => {});
|
|
329
328
|
}
|
|
329
|
+
if (restartRequestedDuringDeploy) this.requestRestart();
|
|
330
|
+
void this.applicationScope
|
|
331
|
+
?.finishDeploy()
|
|
332
|
+
.then((runtimeChanged) => {
|
|
333
|
+
if (runtimeChanged) this.requestRestart();
|
|
334
|
+
})
|
|
335
|
+
.catch((error) => {
|
|
336
|
+
this.#logger.error?.(`Could not verify the loaded runtime after deploying ${this.#appName}:`, error);
|
|
337
|
+
this.requestRestart();
|
|
338
|
+
});
|
|
330
339
|
|
|
331
340
|
this.#safeEmit('deploy:end', componentName);
|
|
332
341
|
}
|
|
333
342
|
|
|
334
|
-
// Swallow and log listener exceptions so one buggy plugin can't stop us
|
|
335
|
-
// from running the rest of the deploy-lifecycle bookkeeping. EventEmitter
|
|
336
|
-
// by default rethrows from synchronous listeners.
|
|
337
343
|
#safeEmit(event: 'deploy:start' | 'deploy:end', componentName: string): void {
|
|
338
344
|
try {
|
|
339
345
|
this.emit(event, componentName);
|
|
@@ -350,6 +356,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
350
356
|
.on('unlink', this.#defaultEntryHandlerListener('unlink'))
|
|
351
357
|
.on('addDir', this.#defaultEntryHandlerListener('addDir'))
|
|
352
358
|
.on('unlinkDir', this.#defaultEntryHandlerListener('unlinkDir'));
|
|
359
|
+
if (this.#deployInFlight) entryHandler.pause();
|
|
353
360
|
|
|
354
361
|
this.#entryHandlers.push(entryHandler);
|
|
355
362
|
|
|
@@ -391,8 +398,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
391
398
|
return;
|
|
392
399
|
}
|
|
393
400
|
|
|
394
|
-
|
|
395
|
-
scope.#entryHandler.update(config as FileAndURLPathConfig);
|
|
401
|
+
void scope.#entryHandler.update(config as FileAndURLPathConfig).catch(() => {});
|
|
396
402
|
|
|
397
403
|
return;
|
|
398
404
|
}
|
|
@@ -407,6 +413,23 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
407
413
|
};
|
|
408
414
|
}
|
|
409
415
|
|
|
416
|
+
#optionsWatcherRemoveListener() {
|
|
417
|
+
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
|
418
|
+
const scope = this;
|
|
419
|
+
// Deleting a component's config block emits `remove` (not `change`), so without
|
|
420
|
+
// this listener a running component keeps serving until some unrelated restart —
|
|
421
|
+
// even though absence is the canonical disabled state for opt-in built-ins like
|
|
422
|
+
// the /v1 models gateway. Mirrors the change listener: a plugin that registers
|
|
423
|
+
// its own `remove` handler owns the response and no restart is requested.
|
|
424
|
+
return function handleOptionsWatcherRemove(this: OptionsWatcher) {
|
|
425
|
+
if (this.listenerCount('remove') > 1) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
scope.#logger.debug?.('Options removed, requesting restart');
|
|
429
|
+
scope.requestRestart();
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
410
433
|
#getFilesOption(): FileAndURLPathConfig | undefined {
|
|
411
434
|
const config = this.options.getAll();
|
|
412
435
|
if (
|
|
@@ -520,9 +543,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
520
543
|
|
|
521
544
|
requestRestart() {
|
|
522
545
|
if (this.#deployInFlight) {
|
|
523
|
-
|
|
524
|
-
// post-deploy re-scan in #onDeployEnd will trigger the coalesced
|
|
525
|
-
// restart instead.
|
|
546
|
+
this.#restartRequestedDuringDeploy = true;
|
|
526
547
|
this.#logger.debug?.(`Restart suppressed (deploy in flight) for ${this.#appName}`);
|
|
527
548
|
return;
|
|
528
549
|
}
|
|
@@ -541,6 +562,29 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
|
|
|
541
562
|
}
|
|
542
563
|
}
|
|
543
564
|
|
|
565
|
+
waitForDeployCompletion(timeoutMs = 6 * 60 * 60 * 1000): Promise<void> {
|
|
566
|
+
if (!this.#deployInFlight) return Promise.resolve();
|
|
567
|
+
return new Promise((resolve, reject) => {
|
|
568
|
+
const timer = setTimeout(() => {
|
|
569
|
+
deployLifecycle.off('deploy:end', handleDeployEnd);
|
|
570
|
+
reject(new Error(`Timed out waiting for deployment of ${this.#appName} to complete`));
|
|
571
|
+
}, timeoutMs);
|
|
572
|
+
timer.unref?.();
|
|
573
|
+
const handleDeployEnd = (componentName: string) => {
|
|
574
|
+
if (componentName !== this.#appName || this.#deployInFlight) return;
|
|
575
|
+
deployLifecycle.off('deploy:end', handleDeployEnd);
|
|
576
|
+
clearTimeout(timer);
|
|
577
|
+
resolve();
|
|
578
|
+
};
|
|
579
|
+
deployLifecycle.on('deploy:end', handleDeployEnd);
|
|
580
|
+
if (!this.#deployInFlight) {
|
|
581
|
+
deployLifecycle.off('deploy:end', handleDeployEnd);
|
|
582
|
+
clearTimeout(timer);
|
|
583
|
+
resolve();
|
|
584
|
+
}
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
544
588
|
/**
|
|
545
589
|
* Import a file into the scope's sandbox.
|
|
546
590
|
* @param filePath - The path of the file to import.
|
|
@@ -29,6 +29,7 @@ import * as scheduler from '../resources/scheduler/scheduler.ts';
|
|
|
29
29
|
import { restartWorkers, getWorkerIndex } from '../server/threads/manageThreads.js';
|
|
30
30
|
import { resetRestartNeeded, subscribeToRestartRequests } from './requestRestart.ts';
|
|
31
31
|
import { trackScopeClose } from './scopeShutdown.ts';
|
|
32
|
+
import { deployLifecycle } from './deployLifecycle.ts';
|
|
32
33
|
import { toScopeMount, nestScopeMount, type ScopeMount } from './scopeMount.ts';
|
|
33
34
|
import { scopedImport } from '../security/jsLoader.ts';
|
|
34
35
|
import { server } from '../server/Server.ts';
|
|
@@ -170,6 +171,16 @@ export const TRUSTED_RESOURCE_PLUGINS: any = {
|
|
|
170
171
|
return require('../server/fastifyRoutes');
|
|
171
172
|
},
|
|
172
173
|
login,
|
|
174
|
+
// String entry: the loader `await import()`s these lazily when the component is actually
|
|
175
|
+
// processed, so the gateway's module graph is not pulled into componentLoader's own
|
|
176
|
+
// evaluation. `#src/*` is used rather than a relative path because it resolves under both
|
|
177
|
+
// conditions (source under --conditions=typestrip, dist otherwise); a relative extensionless
|
|
178
|
+
// require would only resolve against dist.
|
|
179
|
+
//
|
|
180
|
+
// The component is only *processed* when a `modelsGateway` key exists in the config, since
|
|
181
|
+
// the root loop skips absent keys (`if (!componentConfig) continue`). `defaultConfig.yaml`
|
|
182
|
+
// ships no such key, so an instance that never opts in pays nothing for this entry.
|
|
183
|
+
modelsGateway: '#src/resources/models/v1/index',
|
|
173
184
|
static: staticFiles,
|
|
174
185
|
customFunctions: {},
|
|
175
186
|
http: httpComponent,
|
|
@@ -312,6 +323,7 @@ function symlinkHarperModule(componentDirectory: string) {
|
|
|
312
323
|
*/
|
|
313
324
|
function sequentiallyHandleApplication(scope: Scope, plugin: PluginModule) {
|
|
314
325
|
return scope.ready.then(async () => {
|
|
326
|
+
await scope.waitForDeployCompletion();
|
|
315
327
|
// Timeout priority is user config, plugin default, finally 30 seconds
|
|
316
328
|
const timeout = scope.options.get(['timeout']) || plugin.defaultTimeout || 30_000; // default 30 second timeout
|
|
317
329
|
if (typeof timeout !== 'number') {
|
|
@@ -333,35 +345,76 @@ function sequentiallyHandleApplication(scope: Scope, plugin: PluginModule) {
|
|
|
333
345
|
}, timeout + 5_000); // extra time for lock acquisition
|
|
334
346
|
});
|
|
335
347
|
}
|
|
336
|
-
let loadTimeout: NodeJS.Timeout;
|
|
337
348
|
try {
|
|
338
349
|
// note that handleApplication can throw sync or async errors, need to run finally block for both
|
|
339
|
-
await
|
|
350
|
+
await withDeployAwareTimeout(
|
|
340
351
|
Promise.resolve(plugin.handleApplication(scope)).then(async () => {
|
|
341
352
|
// Wait for any initial entry handler loads to complete
|
|
342
353
|
// This ensures all async operations (like secureImport) finish before the component is marked as loaded
|
|
343
354
|
await scope.waitForInitialLoads();
|
|
344
355
|
}),
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
() =>
|
|
349
|
-
reject(
|
|
350
|
-
new Error(
|
|
351
|
-
`handleApplication timed out after ${timeout}ms for ${scope.pluginName} on behalf of ${scope.appName}`
|
|
352
|
-
)
|
|
353
|
-
),
|
|
354
|
-
timeout
|
|
355
|
-
))
|
|
356
|
-
),
|
|
357
|
-
]);
|
|
356
|
+
scope,
|
|
357
|
+
timeout
|
|
358
|
+
);
|
|
358
359
|
} finally {
|
|
359
360
|
Status.primaryStore.unlock(scope.pluginName);
|
|
360
|
-
clearTimeout(loadTimeout);
|
|
361
361
|
}
|
|
362
362
|
});
|
|
363
363
|
}
|
|
364
364
|
|
|
365
|
+
function withDeployAwareTimeout<T>(operation: Promise<T>, scope: Scope, timeout: number): Promise<T> {
|
|
366
|
+
return new Promise((resolve, reject) => {
|
|
367
|
+
const absoluteTimeout = timeout + 6 * 60 * 60 * 1000;
|
|
368
|
+
let remaining = timeout;
|
|
369
|
+
let activeSince = 0;
|
|
370
|
+
let timer: NodeJS.Timeout | undefined;
|
|
371
|
+
let absoluteTimer: NodeJS.Timeout;
|
|
372
|
+
const cleanup = () => {
|
|
373
|
+
if (timer) clearTimeout(timer);
|
|
374
|
+
clearTimeout(absoluteTimer);
|
|
375
|
+
deployLifecycle.off('deploy:start', handleDeployStart);
|
|
376
|
+
deployLifecycle.off('deploy:end', handleDeployEnd);
|
|
377
|
+
};
|
|
378
|
+
const rejectTimeout = (limit = timeout) => {
|
|
379
|
+
cleanup();
|
|
380
|
+
reject(
|
|
381
|
+
new Error(`handleApplication timed out after ${limit}ms for ${scope.pluginName} on behalf of ${scope.appName}`)
|
|
382
|
+
);
|
|
383
|
+
};
|
|
384
|
+
absoluteTimer = setTimeout(() => rejectTimeout(absoluteTimeout), absoluteTimeout);
|
|
385
|
+
absoluteTimer.unref?.();
|
|
386
|
+
const arm = () => {
|
|
387
|
+
if (deployLifecycle.isDeployInFlight(scope.appName)) return;
|
|
388
|
+
if (remaining <= 0) return rejectTimeout();
|
|
389
|
+
activeSince = Date.now();
|
|
390
|
+
timer = setTimeout(rejectTimeout, remaining);
|
|
391
|
+
};
|
|
392
|
+
function handleDeployStart(componentName: string) {
|
|
393
|
+
if (componentName !== scope.appName || !timer) return;
|
|
394
|
+
remaining -= Date.now() - activeSince;
|
|
395
|
+
clearTimeout(timer);
|
|
396
|
+
timer = undefined;
|
|
397
|
+
}
|
|
398
|
+
function handleDeployEnd(componentName: string) {
|
|
399
|
+
if (componentName === scope.appName) arm();
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
deployLifecycle.on('deploy:start', handleDeployStart);
|
|
403
|
+
deployLifecycle.on('deploy:end', handleDeployEnd);
|
|
404
|
+
operation.then(
|
|
405
|
+
(value) => {
|
|
406
|
+
cleanup();
|
|
407
|
+
resolve(value);
|
|
408
|
+
},
|
|
409
|
+
(error) => {
|
|
410
|
+
cleanup();
|
|
411
|
+
reject(error);
|
|
412
|
+
}
|
|
413
|
+
);
|
|
414
|
+
arm();
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
|
|
365
418
|
export interface LoadComponentOptions {
|
|
366
419
|
isRoot?: boolean;
|
|
367
420
|
applicationScope?: ApplicationScope;
|
|
@@ -405,6 +458,7 @@ export async function loadComponent(
|
|
|
405
458
|
appName,
|
|
406
459
|
mount,
|
|
407
460
|
} = options;
|
|
461
|
+
applicationScope.runtimeRoot ??= resolvedFolder;
|
|
408
462
|
applicationScope.allowedPath ??= realpathSync(componentDirectory);
|
|
409
463
|
if (providedLoadedComponents) loadedComponents = providedLoadedComponents;
|
|
410
464
|
try {
|