@harperfast/harper 5.2.10 → 5.3.0-alpha.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/agent/mcpTools.ts +1 -1
- package/agent/session.ts +25 -14
- package/bin/cliOperations.ts +46 -9
- package/bin/copyDb.ts +282 -59
- package/bin/deploySetup.ts +16 -5
- package/bin/harper.ts +1 -1
- package/bin/help.ts +4 -1
- package/bin/lite.ts +4 -1
- package/bin/restart.ts +59 -3
- package/bin/run.ts +6 -11
- package/bin/upgrade.js +7 -3
- package/bin/workloadIdentity.ts +119 -0
- package/components/Application.ts +2414 -242
- package/components/ApplicationScope.ts +8 -0
- package/components/EntryHandler.ts +59 -39
- package/components/OptionsWatcher.ts +150 -74
- package/components/RuntimeModuleTracker.ts +38 -7
- package/components/Scope.ts +37 -15
- package/components/awaitRestart.ts +84 -0
- package/components/componentLoader.ts +340 -30
- package/components/componentPreparationLock.ts +16 -5
- package/components/mcp/adapters/harperHttp.ts +4 -0
- package/components/mcp/listChanged.ts +4 -0
- package/components/mcp/toolRegistry.ts +2 -0
- package/components/mcp/tools/operations.ts +9 -0
- package/components/mcp/tools/schemas/operationDescriptions.ts +2 -2
- package/components/operations.js +270 -78
- package/components/operationsValidation.js +49 -1
- package/components/status/ComponentStatusRegistry.ts +59 -0
- package/config/RootConfigWatcher.ts +80 -34
- package/config/configUtils.ts +291 -34
- package/config/harperConfigEnvVars.ts +170 -27
- package/config-root.schema.json +29 -0
- package/dataLayer/blobBackup.ts +160 -50
- package/dataLayer/delete.ts +6 -1
- package/dataLayer/harperBridge/ResourceBridge.ts +52 -8
- package/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dataLayer/hdbInfoController.ts +34 -1
- package/dataLayer/insert.ts +44 -1
- package/dataLayer/rocksdbBackup.ts +53 -10
- package/dataLayer/schema.ts +11 -1
- package/dataLayer/schemaDescribe.ts +8 -1
- package/dist/agent/mcpTools.js +1 -1
- package/dist/agent/mcpTools.js.map +1 -1
- package/dist/agent/session.d.ts +22 -0
- package/dist/agent/session.js +26 -15
- package/dist/agent/session.js.map +1 -1
- package/dist/bin/cliOperations.js +48 -9
- package/dist/bin/cliOperations.js.map +1 -1
- package/dist/bin/copyDb.d.ts +12 -1
- package/dist/bin/copyDb.js +248 -60
- package/dist/bin/copyDb.js.map +1 -1
- package/dist/bin/deploySetup.d.ts +2 -0
- package/dist/bin/deploySetup.js +11 -3
- package/dist/bin/deploySetup.js.map +1 -1
- package/dist/bin/harper.js +1 -1
- package/dist/bin/harper.js.map +1 -1
- package/dist/bin/help.js +4 -1
- package/dist/bin/help.js.map +1 -1
- package/dist/bin/lite.js +4 -1
- package/dist/bin/lite.js.map +1 -1
- package/dist/bin/restart.js +54 -5
- package/dist/bin/restart.js.map +1 -1
- package/dist/bin/run.js +4 -10
- package/dist/bin/run.js.map +1 -1
- package/dist/bin/upgrade.js +4 -3
- package/dist/bin/upgrade.js.map +1 -1
- package/dist/bin/workloadIdentity.d.ts +18 -0
- package/dist/bin/workloadIdentity.js +100 -0
- package/dist/bin/workloadIdentity.js.map +1 -0
- package/dist/components/Application.d.ts +139 -16
- package/dist/components/Application.js +2215 -267
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +8 -0
- package/dist/components/ApplicationScope.js +7 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/EntryHandler.js +26 -10
- package/dist/components/EntryHandler.js.map +1 -1
- package/dist/components/OptionsWatcher.d.ts +1 -0
- package/dist/components/OptionsWatcher.js +141 -74
- package/dist/components/OptionsWatcher.js.map +1 -1
- package/dist/components/RuntimeModuleTracker.js +40 -6
- package/dist/components/RuntimeModuleTracker.js.map +1 -1
- package/dist/components/Scope.js +38 -13
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/awaitRestart.d.ts +33 -0
- package/dist/components/awaitRestart.js +61 -0
- package/dist/components/awaitRestart.js.map +1 -0
- package/dist/components/componentLoader.d.ts +38 -1
- package/dist/components/componentLoader.js +279 -22
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentPreparationLock.d.ts +5 -0
- package/dist/components/componentPreparationLock.js +14 -6
- package/dist/components/componentPreparationLock.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.js +4 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/listChanged.js +5 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +1 -0
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +5 -0
- package/dist/components/mcp/tools/operations.js +9 -0
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +2 -2
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/operations.js +231 -77
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +49 -1
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/status/ComponentStatusRegistry.d.ts +0 -4
- package/dist/components/status/ComponentStatusRegistry.js +63 -0
- package/dist/components/status/ComponentStatusRegistry.js.map +1 -1
- package/dist/config/RootConfigWatcher.d.ts +7 -1
- package/dist/config/RootConfigWatcher.js +64 -27
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configUtils.d.ts +9 -1
- package/dist/config/configUtils.js +254 -33
- package/dist/config/configUtils.js.map +1 -1
- package/dist/config/harperConfigEnvVars.d.ts +16 -0
- package/dist/config/harperConfigEnvVars.js +162 -25
- package/dist/config/harperConfigEnvVars.js.map +1 -1
- package/dist/dataLayer/blobBackup.d.ts +49 -20
- package/dist/dataLayer/blobBackup.js +139 -50
- package/dist/dataLayer/blobBackup.js.map +1 -1
- package/dist/dataLayer/delete.js +1 -1
- package/dist/dataLayer/delete.js.map +1 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.d.ts +14 -1
- package/dist/dataLayer/harperBridge/ResourceBridge.js +51 -10
- package/dist/dataLayer/harperBridge/ResourceBridge.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.d.ts +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js +3 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbMethods/DeleteAuditLogsBeforeResults.js.map +1 -1
- package/dist/dataLayer/hdbInfoController.d.ts +10 -0
- package/dist/dataLayer/hdbInfoController.js +30 -1
- package/dist/dataLayer/hdbInfoController.js.map +1 -1
- package/dist/dataLayer/insert.d.ts +9 -1
- package/dist/dataLayer/insert.js +30 -0
- package/dist/dataLayer/insert.js.map +1 -1
- package/dist/dataLayer/rocksdbBackup.d.ts +2 -2
- package/dist/dataLayer/rocksdbBackup.js +45 -8
- package/dist/dataLayer/rocksdbBackup.js.map +1 -1
- package/dist/dataLayer/schema.js +8 -0
- package/dist/dataLayer/schema.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +8 -1
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +52 -0
- package/dist/resources/DatabaseTransaction.d.ts +55 -3
- package/dist/resources/DatabaseTransaction.js +460 -134
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.js +22 -4
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
- package/dist/resources/PrimaryRocksDatabase.js +30 -2
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +20 -0
- package/dist/resources/RecordEncoder.js +94 -9
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +2 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.js +20 -11
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +20 -1
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/RocksIndexStore.d.ts +6 -1
- package/dist/resources/RocksIndexStore.js +24 -10
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +14 -1
- package/dist/resources/RocksTransactionLogStore.js +57 -17
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +112 -8
- package/dist/resources/Table.js +1071 -197
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/auditStore.d.ts +11 -2
- package/dist/resources/auditStore.js +183 -18
- package/dist/resources/auditStore.js.map +1 -1
- package/dist/resources/blob.d.ts +129 -9
- package/dist/resources/blob.js +992 -114
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/branchDatabase.d.ts +48 -0
- package/dist/resources/branchDatabase.js +892 -0
- package/dist/resources/branchDatabase.js.map +1 -0
- package/dist/resources/crdt.js +50 -12
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/databases.d.ts +150 -9
- package/dist/resources/databases.js +1247 -525
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/defineTable.d.ts +10 -2
- package/dist/resources/defineTable.js +9 -1
- package/dist/resources/defineTable.js.map +1 -1
- package/dist/resources/graphql.d.ts +1 -1
- package/dist/resources/graphql.js +50 -15
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +1 -0
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +6 -2
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/longLivedTransactions.d.ts +71 -0
- package/dist/resources/longLivedTransactions.js +358 -0
- package/dist/resources/longLivedTransactions.js.map +1 -0
- package/dist/resources/models/backendRegistry.d.ts +26 -0
- package/dist/resources/models/backendRegistry.js +60 -2
- package/dist/resources/models/backendRegistry.js.map +1 -1
- package/dist/resources/models/bootstrap.d.ts +33 -1
- package/dist/resources/models/bootstrap.js +416 -31
- package/dist/resources/models/bootstrap.js.map +1 -1
- package/dist/resources/recordLock.d.ts +80 -0
- package/dist/resources/recordLock.js +184 -0
- package/dist/resources/recordLock.js.map +1 -0
- package/dist/resources/replayLogs.d.ts +14 -1
- package/dist/resources/replayLogs.js +152 -24
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/replayLogsGuards.d.ts +94 -7
- package/dist/resources/replayLogsGuards.js +111 -7
- package/dist/resources/replayLogsGuards.js.map +1 -1
- package/dist/resources/search.js +158 -32
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/transactionBroadcast.d.ts +1 -1
- package/dist/resources/transactionBroadcast.js +2 -2
- package/dist/resources/transactionBroadcast.js.map +1 -1
- package/dist/security/auth.js +61 -30
- package/dist/security/auth.js.map +1 -1
- package/dist/security/authn/oidc/claims.d.ts +22 -0
- package/dist/security/authn/oidc/claims.js +71 -0
- package/dist/security/authn/oidc/claims.js.map +1 -0
- package/dist/security/authn/oidc/identityToken.d.ts +27 -0
- package/dist/security/authn/oidc/identityToken.js +111 -0
- package/dist/security/authn/oidc/identityToken.js.map +1 -0
- package/dist/security/authn/oidc/jwks.d.ts +25 -0
- package/dist/security/authn/oidc/jwks.js +261 -0
- package/dist/security/authn/oidc/jwks.js.map +1 -0
- package/dist/security/authn/oidc/providers/generic.d.ts +13 -0
- package/dist/security/authn/oidc/providers/generic.js +34 -0
- package/dist/security/authn/oidc/providers/generic.js.map +1 -0
- package/dist/security/authn/oidc/providers/githubActions.d.ts +11 -0
- package/dist/security/authn/oidc/providers/githubActions.js +129 -0
- package/dist/security/authn/oidc/providers/githubActions.js.map +1 -0
- package/dist/security/authn/oidc/providers/index.d.ts +37 -0
- package/dist/security/authn/oidc/providers/index.js +24 -0
- package/dist/security/authn/oidc/providers/index.js.map +1 -0
- package/dist/security/authn/oidc/tokenExchange.d.ts +12 -0
- package/dist/security/authn/oidc/tokenExchange.js +306 -0
- package/dist/security/authn/oidc/tokenExchange.js.map +1 -0
- package/dist/security/authn/oidc/trustPolicyOperations.d.ts +49 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js +358 -0
- package/dist/security/authn/oidc/trustPolicyOperations.js.map +1 -0
- package/dist/security/authn/oidc/types.d.ts +38 -0
- package/dist/security/authn/oidc/types.js +6 -0
- package/dist/security/authn/oidc/types.js.map +1 -0
- package/dist/security/certificateVerification/index.js +40 -11
- package/dist/security/certificateVerification/index.js.map +1 -1
- package/dist/security/certificateVerification/trustedIssuers.d.ts +24 -0
- package/dist/security/certificateVerification/trustedIssuers.js +79 -0
- package/dist/security/certificateVerification/trustedIssuers.js.map +1 -0
- package/dist/security/certificateVerification/types.d.ts +1 -0
- package/dist/security/credentialProvenance.d.ts +35 -0
- package/dist/security/credentialProvenance.js +51 -0
- package/dist/security/credentialProvenance.js.map +1 -0
- package/dist/security/credentialRejection.d.ts +4 -0
- package/dist/security/credentialRejection.js +24 -0
- package/dist/security/credentialRejection.js.map +1 -0
- package/dist/security/deferredAuthentication.d.ts +36 -0
- package/dist/security/deferredAuthentication.js +70 -0
- package/dist/security/deferredAuthentication.js.map +1 -0
- package/dist/security/impersonation.d.ts +21 -0
- package/dist/security/impersonation.js +108 -9
- package/dist/security/impersonation.js.map +1 -1
- package/dist/security/jsLoader.d.ts +6 -0
- package/dist/security/jsLoader.js +75 -15
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/keys.js +301 -71
- package/dist/security/keys.js.map +1 -1
- package/dist/security/operationScope.d.ts +21 -0
- package/dist/security/operationScope.js +36 -0
- package/dist/security/operationScope.js.map +1 -0
- package/dist/security/permissionsTranslator.js +21 -0
- package/dist/security/permissionsTranslator.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +19 -1
- package/dist/security/tokenAuthentication.js +191 -10
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/security/user.js +4 -3
- package/dist/security/user.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +2 -2
- package/dist/server/DurableSubscriptionsSession.js +67 -10
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +73 -0
- package/dist/server/REST.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -0
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +11 -0
- package/dist/server/http.js +88 -15
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +8 -1
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/jobs/jobProcess.js +6 -2
- package/dist/server/jobs/jobProcess.js.map +1 -1
- package/dist/server/jobs/jobs.js +4 -1
- package/dist/server/jobs/jobs.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +26 -4
- package/dist/server/liveSubscriptionAuth.js +105 -39
- package/dist/server/liveSubscriptionAuth.js.map +1 -1
- package/dist/server/loadRootComponents.js +49 -10
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/mqtt.d.ts +2 -0
- package/dist/server/mqtt.js +165 -30
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/nodeName.d.ts +2 -0
- package/dist/server/nodeName.js +107 -23
- package/dist/server/nodeName.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +25 -0
- package/dist/server/serverHelpers/Headers.js +139 -1
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +9 -0
- package/dist/server/serverHelpers/contentTypes.js +32 -25
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/deployValidationState.d.ts +3 -0
- package/dist/server/serverHelpers/deployValidationState.js +9 -19
- package/dist/server/serverHelpers/deployValidationState.js.map +1 -1
- package/dist/server/serverHelpers/operationAuthorizationState.d.ts +12 -0
- package/dist/server/serverHelpers/operationAuthorizationState.js +24 -2
- package/dist/server/serverHelpers/operationAuthorizationState.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +5 -4
- package/dist/server/serverHelpers/registeredOperations.js +74 -21
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -1
- package/dist/server/serverHelpers/requestSanitization.d.ts +11 -0
- package/dist/server/serverHelpers/requestSanitization.js +20 -0
- package/dist/server/serverHelpers/requestSanitization.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +6 -3
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +18 -0
- package/dist/server/serverHelpers/serverUtilities.js +90 -20
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/sharedMessageEncoding.d.ts +67 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js +280 -0
- package/dist/server/serverHelpers/sharedMessageEncoding.js.map +1 -0
- package/dist/server/serverHelpers/uwsServer.js +19 -1
- package/dist/server/serverHelpers/uwsServer.js.map +1 -1
- package/dist/server/static.js +24 -28
- package/dist/server/static.js.map +1 -1
- package/dist/server/storageReclamation.d.ts +5 -0
- package/dist/server/storageReclamation.js +16 -0
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/itc.d.ts +7 -2
- package/dist/server/threads/itc.js +5 -1
- package/dist/server/threads/itc.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +26 -4
- package/dist/server/threads/manageThreads.js +517 -72
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.js +89 -26
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadHeapMemory.d.ts +2 -0
- package/dist/server/threads/threadHeapMemory.js +31 -0
- package/dist/server/threads/threadHeapMemory.js.map +1 -0
- package/dist/server/threads/threadServer.js +46 -15
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/sqlEngine/config.d.ts +1 -3
- package/dist/sqlEngine/config.js +19 -16
- package/dist/sqlEngine/config.js.map +1 -1
- package/dist/sqlTranslator/index.d.ts +1 -1
- package/dist/sqlTranslator/index.js +30 -7
- package/dist/sqlTranslator/index.js.map +1 -1
- package/dist/upgrade/directives/5-3-0.d.ts +7 -0
- package/dist/upgrade/directives/5-3-0.js +148 -0
- package/dist/upgrade/directives/5-3-0.js.map +1 -0
- package/dist/upgrade/directives/directivesController.js +2 -1
- package/dist/upgrade/directives/directivesController.js.map +1 -1
- package/dist/utility/OperationFunctionCaller.js +2 -1
- package/dist/utility/OperationFunctionCaller.js.map +1 -1
- package/dist/utility/common_utils.d.ts +16 -0
- package/dist/utility/common_utils.js +32 -6
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/componentNames.d.ts +8 -0
- package/dist/utility/componentNames.js +12 -1
- package/dist/utility/componentNames.js.map +1 -1
- package/dist/utility/environment/environmentManager.js +3 -6
- package/dist/utility/environment/environmentManager.js.map +1 -1
- package/dist/utility/environment/systemInformation.d.ts +1 -0
- package/dist/utility/environment/systemInformation.js +1 -0
- package/dist/utility/environment/systemInformation.js.map +1 -1
- package/dist/utility/errors/commonErrors.d.ts +2 -0
- package/dist/utility/errors/commonErrors.js +2 -0
- package/dist/utility/errors/commonErrors.js.map +1 -1
- package/dist/utility/errors/hdbError.d.ts +17 -0
- package/dist/utility/errors/hdbError.js +30 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +16 -0
- package/dist/utility/hdbTerms.js +18 -2
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +2 -0
- package/dist/utility/logging/harper_logger.js +91 -14
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logRotator.js +76 -49
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/nodeIdentity.d.ts +9 -0
- package/dist/utility/nodeIdentity.js +58 -0
- package/dist/utility/nodeIdentity.js.map +1 -0
- package/dist/utility/npmUtilities.js +9 -7
- package/dist/utility/npmUtilities.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +3 -1
- package/dist/utility/operationPermissions.js +16 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +10 -7
- package/dist/utility/operation_authorization.js +212 -41
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/watchPath.d.ts +29 -0
- package/dist/utility/watchPath.js +68 -0
- package/dist/utility/watchPath.js.map +1 -0
- package/dist/utility/watcherFallback.d.ts +86 -0
- package/dist/utility/watcherFallback.js +278 -1
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.d.ts +12 -0
- package/dist/validation/configValidator.js +199 -72
- package/dist/validation/configValidator.js.map +1 -1
- package/dist/validation/installValidator.js +12 -0
- package/dist/validation/installValidator.js.map +1 -1
- package/dist/validation/validationWrapper.d.ts +11 -0
- package/dist/validation/validationWrapper.js +16 -3
- package/dist/validation/validationWrapper.js.map +1 -1
- package/index.ts +2 -0
- package/json/systemSchema.json +52 -0
- package/npm-shrinkwrap.json +235 -233
- package/package.json +8 -7
- package/resources/DESIGN.md +114 -52
- package/resources/DatabaseTransaction.ts +522 -130
- package/resources/LMDBTransaction.ts +26 -4
- package/resources/PrimaryRocksDatabase.ts +29 -3
- package/resources/RecordEncoder.ts +103 -8
- package/resources/RequestTarget.ts +2 -0
- package/resources/Resource.ts +17 -9
- package/resources/ResourceInterface.ts +23 -0
- package/resources/RocksIndexStore.ts +30 -10
- package/resources/RocksTransactionLogStore.ts +78 -21
- package/resources/Table.ts +1107 -164
- package/resources/auditStore.ts +187 -21
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/databases.ts +1354 -522
- package/resources/defineTable.ts +18 -2
- package/resources/graphql.ts +70 -16
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +6 -2
- package/resources/longLivedTransactions.ts +360 -0
- package/resources/models/backendRegistry.ts +84 -2
- package/resources/models/bootstrap.ts +473 -28
- package/resources/recordLock.ts +253 -0
- package/resources/replayLogs.ts +147 -26
- package/resources/replayLogsGuards.ts +171 -8
- package/resources/search.ts +154 -32
- package/resources/transactionBroadcast.ts +3 -3
- package/security/auth.ts +68 -29
- package/security/authn/oidc/claims.ts +72 -0
- package/security/authn/oidc/identityToken.ts +129 -0
- package/security/authn/oidc/jwks.ts +260 -0
- package/security/authn/oidc/providers/generic.ts +40 -0
- package/security/authn/oidc/providers/githubActions.ts +137 -0
- package/security/authn/oidc/providers/index.ts +52 -0
- package/security/authn/oidc/tokenExchange.ts +300 -0
- package/security/authn/oidc/trustPolicyOperations.ts +343 -0
- package/security/authn/oidc/types.ts +41 -0
- package/security/certificateVerification/index.ts +54 -13
- package/security/certificateVerification/trustedIssuers.ts +76 -0
- package/security/certificateVerification/types.ts +1 -0
- package/security/credentialProvenance.ts +47 -0
- package/security/credentialRejection.ts +22 -0
- package/security/deferredAuthentication.ts +71 -0
- package/security/impersonation.ts +117 -12
- package/security/jsLoader.ts +81 -18
- package/security/keys.ts +298 -72
- package/security/operationScope.ts +33 -0
- package/security/permissionsTranslator.js +23 -0
- package/security/tokenAuthentication.ts +233 -12
- package/security/user.ts +4 -3
- package/server/DESIGN.md +183 -16
- package/server/DurableSubscriptionsSession.ts +71 -11
- package/server/REST.ts +79 -1
- package/server/graphqlQuerying.ts +4 -0
- package/server/http.ts +99 -18
- package/server/itc/serverHandlers.js +8 -1
- package/server/jobs/jobProcess.ts +8 -2
- package/server/jobs/jobs.ts +4 -1
- package/server/liveSubscriptionAuth.ts +129 -46
- package/server/loadRootComponents.js +50 -8
- package/server/mqtt.ts +179 -38
- package/server/nodeName.ts +103 -21
- package/server/serverHelpers/Headers.ts +131 -0
- package/server/serverHelpers/contentTypes.ts +29 -21
- package/server/serverHelpers/deployValidationState.ts +24 -13
- package/server/serverHelpers/operationAuthorizationState.ts +34 -3
- package/server/serverHelpers/registeredOperations.ts +79 -22
- package/server/serverHelpers/requestSanitization.ts +15 -0
- package/server/serverHelpers/serverHandlers.js +6 -3
- package/server/serverHelpers/serverUtilities.ts +104 -31
- package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
- package/server/serverHelpers/uwsServer.ts +17 -2
- package/server/static.ts +23 -29
- package/server/storageReclamation.ts +13 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/manageThreads.js +526 -63
- package/server/threads/socketRouter.ts +81 -26
- package/server/threads/threadHeapMemory.ts +26 -0
- package/server/threads/threadServer.js +43 -15
- package/sqlTranslator/index.ts +31 -8
- package/studio/web/assets/{Chat-Br06zdMA.js → Chat-BnCBegQz.js} +1 -1
- package/studio/web/assets/{FloatingChat-BWImX5fA.js → FloatingChat-CoDW1ySS.js} +3 -3
- package/studio/web/assets/{apiToken-DN0nmDsq.js → apiToken-Bwk5BLXW.js} +1 -1
- package/studio/web/assets/{applications-kSxVoyeU.js → applications-DHxGi7JH.js} +1 -1
- package/studio/web/assets/{cssMode-C1JeufH5.js → cssMode-s0cWI-_M.js} +1 -1
- package/studio/web/assets/{editor-qoo9CrGO.js → editor-DNcRHK54.js} +1 -1
- package/studio/web/assets/{html-Dt4IIy04.js → html-Bdssedlg.js} +1 -1
- package/studio/web/assets/{htmlMode-DXgKKr4C.js → htmlMode-CoDlJ3fw.js} +1 -1
- package/studio/web/assets/{index-6onkYFOG.js → index-D6sxmFLR.js} +5 -5
- package/studio/web/assets/{index.lazy-BrCFnpNJ.js → index.lazy-tmU5BS8s.js} +1 -1
- package/studio/web/assets/{javascript-DNCQGUBc.js → javascript-B8meVSTH.js} +1 -1
- package/studio/web/assets/{jsonMode-CR6HWruP.js → jsonMode-DpIPd35T.js} +1 -1
- package/studio/web/assets/{languageServices-BM4fI4rS.js → languageServices-C_5FMJzQ.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-DSa1ttcD.js → lspLanguageFeatures-BIzNBzPK.js} +1 -1
- package/studio/web/assets/{notifications-BHXLnh6x.js → notifications-CQf18QKb.js} +1 -1
- package/studio/web/assets/{notifications-CUmtIA6z.js → notifications-CvZivSbh.js} +1 -1
- package/studio/web/assets/{profile-8BeFSF3j.js → profile-DdOwtntb.js} +1 -1
- package/studio/web/assets/{regions-C8qR0HhD.js → regions-n69fwagr.js} +1 -1
- package/studio/web/assets/{register-B4n5i0SD.js → register-PfWTCXWB.js} +2 -2
- package/studio/web/assets/{setComponentFile-g0_B0lgX.js → setComponentFile-Bg6O7X0S.js} +1 -1
- package/studio/web/assets/{setup-B0CTj_Q5.js → setup-CUx_aUDl.js} +2 -2
- package/studio/web/assets/{status-C6Yib7-K.js → status-D7BVKqX9.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-Dgta7bVi.js → toggleHighContrast-DBSyXzMr.js} +1 -1
- package/studio/web/assets/{tsMode-CH_jHvU-.js → tsMode-BByKCjBS.js} +1 -1
- package/studio/web/assets/{typescript-Co9LCXd5.js → typescript-DDLnLpw9.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-wKC8NsC_.js → useEntityRestURL-31CHGaHk.js} +1 -1
- package/studio/web/assets/{workers-CWeLxCXA.js → workers-pR3jRY9D.js} +1 -1
- package/studio/web/assets/{xml-BadC-0Rk.js → xml-2iRnMhQO.js} +1 -1
- package/studio/web/assets/{yaml-BiUfxPbC.js → yaml-Bf92gJpd.js} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-3-0.ts +132 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/OperationFunctionCaller.ts +2 -1
- package/utility/common_utils.ts +30 -5
- package/utility/componentNames.ts +12 -0
- package/utility/environment/environmentManager.ts +3 -7
- package/utility/environment/systemInformation.ts +7 -0
- package/utility/errors/commonErrors.ts +4 -0
- package/utility/errors/hdbError.ts +29 -0
- package/utility/hdbTerms.ts +17 -0
- package/utility/logging/harper_logger.ts +87 -14
- package/utility/logging/logRotator.ts +72 -45
- package/utility/nodeIdentity.ts +45 -0
- package/utility/npmUtilities.ts +10 -8
- package/utility/operationPermissions.ts +18 -1
- package/utility/operation_authorization.ts +231 -42
- package/utility/watchPath.ts +63 -0
- package/utility/watcherFallback.ts +270 -0
- package/validation/configValidator.ts +209 -72
- package/validation/installValidator.ts +15 -0
- package/validation/validationWrapper.ts +18 -4
|
@@ -41,6 +41,8 @@ import * as status from '../status/index.ts';
|
|
|
41
41
|
import * as regDeprecated from '../../resources/registrationDeprecated.ts';
|
|
42
42
|
import * as deploymentOperations from '../../components/deploymentOperations.ts';
|
|
43
43
|
import * as secretOperations from '../../components/secretOperations.ts';
|
|
44
|
+
import * as trustPolicyOperations from '../../security/authn/oidc/trustPolicyOperations.ts';
|
|
45
|
+
import * as tokenExchange from '../../security/authn/oidc/tokenExchange.ts';
|
|
44
46
|
import { contextStorage } from '../../resources/transaction.ts';
|
|
45
47
|
import { isMainThread } from 'node:worker_threads';
|
|
46
48
|
import {
|
|
@@ -49,6 +51,7 @@ import {
|
|
|
49
51
|
setLocalOperationDispatch,
|
|
50
52
|
} from './registeredOperations.ts';
|
|
51
53
|
import { runWithOperationAuthorizationBypass } from './operationAuthorizationState.ts';
|
|
54
|
+
import { stripSuppliedParsedSqlObject } from './requestSanitization.ts';
|
|
52
55
|
|
|
53
56
|
const pSearchSearch = util.promisify(search.search);
|
|
54
57
|
let pEvaluateSql: (sql: string) => Promise<any>;
|
|
@@ -79,6 +82,42 @@ export type OperationFunctionName = ValueOf<typeof terms.OPERATIONS_ENUM>;
|
|
|
79
82
|
* handles the response to the sender.
|
|
80
83
|
*/
|
|
81
84
|
// TODO: Replace Function type with an actual function type (e.g. (): Thingy)
|
|
85
|
+
/**
|
|
86
|
+
* Fields stripped from an operation body before it reaches the operations log.
|
|
87
|
+
*
|
|
88
|
+
* `credentials` carries a transient token on deploy_component (`registryAuth` is its pre-rename
|
|
89
|
+
* name — still stripped, since this runs ahead of the validation that now rejects it). `value` /
|
|
90
|
+
* `values` carry .env secrets from set_env_value; `value` / `envelope` carry secrets from
|
|
91
|
+
* set_secret. `token` is the login-purpose token (login) and the CI identity token
|
|
92
|
+
* (exchange_oidc_token), and is also stripped defensively — no operation declares a top-level `token`,
|
|
93
|
+
* but validation allows unknown keys, so a mistyped `harper deploy setup token=…` must not log a live
|
|
94
|
+
* credential. `refresh_token` is the 30-day credential (refresh_operation_token).
|
|
95
|
+
*
|
|
96
|
+
* Redaction runs *before* the handler, so a rejected request logs a still-spendable credential —
|
|
97
|
+
* which is why a new secret-bearing field belongs here rather than left to the default (harper#1527
|
|
98
|
+
* was this same miss for set_env_value).
|
|
99
|
+
*/
|
|
100
|
+
export const UNLOGGABLE_OPERATION_FIELDS = [
|
|
101
|
+
'hdb_user',
|
|
102
|
+
'hdbAuthHeader',
|
|
103
|
+
'password',
|
|
104
|
+
'payload',
|
|
105
|
+
'credentials',
|
|
106
|
+
'registryAuth',
|
|
107
|
+
'value',
|
|
108
|
+
'values',
|
|
109
|
+
'envelope',
|
|
110
|
+
'token',
|
|
111
|
+
'refresh_token',
|
|
112
|
+
];
|
|
113
|
+
|
|
114
|
+
/** Callers gate this on log level: it allocates, and the operations log is often off. */
|
|
115
|
+
export function redactForOperationLog(body: Record<string, any>): Record<string, any> {
|
|
116
|
+
const clean = { ...body };
|
|
117
|
+
for (const field of UNLOGGABLE_OPERATION_FIELDS) delete clean[field];
|
|
118
|
+
return clean;
|
|
119
|
+
}
|
|
120
|
+
|
|
82
121
|
export async function processLocalTransaction(req: OperationRequest, operationFunction: Function) {
|
|
83
122
|
try {
|
|
84
123
|
if (
|
|
@@ -87,31 +126,7 @@ export async function processLocalTransaction(req: OperationRequest, operationFu
|
|
|
87
126
|
harperLogger.logLevel === terms.LOG_LEVELS.DEBUG ||
|
|
88
127
|
harperLogger.logLevel === terms.LOG_LEVELS.TRACE)
|
|
89
128
|
) {
|
|
90
|
-
|
|
91
|
-
// an object unless the logging is actually going to happen. credentials carries a
|
|
92
|
-
// transient token on deploy_component (registryAuth is its pre-rename name — still
|
|
93
|
-
// stripped, since this runs ahead of the validation that now rejects it, and a stale
|
|
94
|
-
// caller's token must not reach the log); value/values carry .env secrets from
|
|
95
|
-
// set_env_value; value/envelope carry secrets from set_secret — none may reach the
|
|
96
|
-
// operations log. `token` is stripped defensively: no operation declares one at the top
|
|
97
|
-
// level today, but validation allows unknown keys, so a caller that sends one anyway (a
|
|
98
|
-
// mistyped `harper deploy setup token=…`) would otherwise log a live credential.
|
|
99
|
-
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */
|
|
100
|
-
const {
|
|
101
|
-
hdb_user,
|
|
102
|
-
hdbAuthHeader,
|
|
103
|
-
password,
|
|
104
|
-
payload,
|
|
105
|
-
credentials,
|
|
106
|
-
registryAuth,
|
|
107
|
-
value,
|
|
108
|
-
values,
|
|
109
|
-
envelope,
|
|
110
|
-
token,
|
|
111
|
-
...cleanBody
|
|
112
|
-
} = req.body;
|
|
113
|
-
/* eslint-enable no-unused-vars, @typescript-eslint/no-unused-vars */
|
|
114
|
-
operationLog.info(cleanBody);
|
|
129
|
+
operationLog.info(redactForOperationLog(req.body));
|
|
115
130
|
}
|
|
116
131
|
} catch (e) {
|
|
117
132
|
operationLog.error(e);
|
|
@@ -172,6 +187,10 @@ export type OperationDefinition = {
|
|
|
172
187
|
requiresSuperUser?: boolean;
|
|
173
188
|
};
|
|
174
189
|
|
|
190
|
+
// Operation names this API installed a permission entry for, so a later registration that drops
|
|
191
|
+
// `requiresSuperUser` can retract exactly that entry and nothing else.
|
|
192
|
+
const declaredPermissionNames = new Set<string>();
|
|
193
|
+
|
|
175
194
|
/**
|
|
176
195
|
* Register an operation function with the server.
|
|
177
196
|
* @param operationDefinition
|
|
@@ -181,7 +200,14 @@ server.registerOperation = (operationDefinition: OperationDefinition) => {
|
|
|
181
200
|
if (isDeployValidating()) return;
|
|
182
201
|
const { name, execute, requiresSuperUser } = operationDefinition;
|
|
183
202
|
let handler = execute;
|
|
184
|
-
if (requiresSuperUser
|
|
203
|
+
if (requiresSuperUser === undefined) {
|
|
204
|
+
// A re-registration that drops the flag must also drop the entry the earlier one installed, or
|
|
205
|
+
// declaration and enforcement disagree: main retracts the grantable mark while this worker keeps
|
|
206
|
+
// honouring an already-persisted role grant. Scoped to names declared through this API, so one it
|
|
207
|
+
// never declared is untouched — a component that declared a built-in's name already overwrote
|
|
208
|
+
// that entry by declaring it, and this only follows.
|
|
209
|
+
if (declaredPermissionNames.delete(name)) opAuth.unregisterOperationPermission(name);
|
|
210
|
+
} else {
|
|
185
211
|
// verifyPerms keys requiredPermissions by the handler's function `.name`, but registered ops
|
|
186
212
|
// are typically anonymous arrows (all named "execute") which collide and can't be keyed. Wrap
|
|
187
213
|
// in a FRESH function named after the op so the lookup resolves the right entry. Wrap rather
|
|
@@ -191,12 +217,14 @@ server.registerOperation = (operationDefinition: OperationDefinition) => {
|
|
|
191
217
|
handler = (...args: any[]) => (execute as any)(...args);
|
|
192
218
|
Object.defineProperty(handler, 'name', { value: name, configurable: true });
|
|
193
219
|
opAuth.registerOperationPermission(name, { requiresSu: requiresSuperUser });
|
|
220
|
+
declaredPermissionNames.add(name);
|
|
194
221
|
}
|
|
195
222
|
OPERATION_FUNCTION_MAP.set(name as any, new OperationFunctionObject(handler));
|
|
196
223
|
// Components load per-worker, so a registration made there is invisible to the main-thread
|
|
197
224
|
// ops-API dispatcher (each thread has its own OPERATION_FUNCTION_MAP instance). Announce it
|
|
198
|
-
// so the main thread can forward calls
|
|
199
|
-
|
|
225
|
+
// so the main thread can forward calls here (#1736), and can mirror the role-allowlist mark that
|
|
226
|
+
// registerOperationPermission above made only in this thread's scope.
|
|
227
|
+
if (!isMainThread) announceRegisteredOperation(name, requiresSuperUser !== undefined);
|
|
200
228
|
};
|
|
201
229
|
|
|
202
230
|
// Register the durable MCP quota policy as a function (see components/mcp/quota.ts). Worker-local,
|
|
@@ -233,10 +261,30 @@ export function chooseOperation(json: OperationRequestBody, bypassAuth = false)
|
|
|
233
261
|
if (json.operation === 'sql' || (json.search_operation && json.search_operation.operation === 'sql')) {
|
|
234
262
|
const sql = require('../../sqlTranslator/index');
|
|
235
263
|
const sqlStatement = json.operation === 'sql' ? json.sql : json.search_operation.sql;
|
|
264
|
+
// Before this dispatch's own parse is assigned, so a body-supplied object cannot survive it.
|
|
265
|
+
stripSuppliedParsedSqlObject(json);
|
|
236
266
|
const parsedSqlObject = sql.convertSQLToAST(sqlStatement);
|
|
237
267
|
json.parsed_sql_object = parsedSqlObject;
|
|
238
268
|
if (!bypassAuth) {
|
|
239
|
-
|
|
269
|
+
// The SQL path never reaches verifyPerms, so the role `operations` allowlist must be
|
|
270
|
+
// enforced here — otherwise an allowlisted role reaches unlisted operations via `sql`.
|
|
271
|
+
// json.operation is already the API name ('sql', or the outer job op like 'export_local').
|
|
272
|
+
const allowlistDenial = opAuth.verifyOperationsAllowlist(json, json.operation);
|
|
273
|
+
if (allowlistDenial) {
|
|
274
|
+
operationLog.error(`${HTTP_STATUS_CODES.FORBIDDEN} from operation ${json.operation}`);
|
|
275
|
+
operationLog.warn(`User '${json.hdb_user?.username}' is not permitted to ${json.operation}`);
|
|
276
|
+
throw handleHDBError(
|
|
277
|
+
new Error(),
|
|
278
|
+
allowlistDenial,
|
|
279
|
+
hdbErrors.HTTP_STATUS_CODES.FORBIDDEN,
|
|
280
|
+
undefined,
|
|
281
|
+
undefined,
|
|
282
|
+
true
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
// `json.operation` explicitly — the operation this dispatch already resolved, not a
|
|
286
|
+
// field read back off the request body.
|
|
287
|
+
const astPermCheck = sql.checkASTPermissions(json, parsedSqlObject, json.operation);
|
|
240
288
|
if (astPermCheck) {
|
|
241
289
|
operationLog.error(`${HTTP_STATUS_CODES.FORBIDDEN} from operation ${json.operation}`);
|
|
242
290
|
operationLog.warn(`User '${json.hdb_user?.username}' is not permitted to ${json.operation}`);
|
|
@@ -255,7 +303,10 @@ export function chooseOperation(json: OperationRequestBody, bypassAuth = false)
|
|
|
255
303
|
!bypassAuth &&
|
|
256
304
|
json.operation !== terms.OPERATIONS_ENUM.CREATE_AUTHENTICATION_TOKENS &&
|
|
257
305
|
json.operation !== terms.OPERATIONS_ENUM.LOGIN &&
|
|
258
|
-
json.operation !== terms.OPERATIONS_ENUM.LOGOUT
|
|
306
|
+
json.operation !== terms.OPERATIONS_ENUM.LOGOUT &&
|
|
307
|
+
// Same rationale: the OIDC exchange authenticates its own caller (#2171), so there is no
|
|
308
|
+
// hdb_user for verifyPerms to check against.
|
|
309
|
+
json.operation !== terms.OPERATIONS_ENUM.EXCHANGE_OIDC_TOKEN
|
|
259
310
|
) {
|
|
260
311
|
const functionToCheck = job_operation_function === undefined ? operation_function : job_operation_function;
|
|
261
312
|
const operation_json = json.search_operation ? json.search_operation : json;
|
|
@@ -263,7 +314,12 @@ export function chooseOperation(json: OperationRequestBody, bypassAuth = false)
|
|
|
263
314
|
operation_json.hdb_user = json.hdb_user;
|
|
264
315
|
}
|
|
265
316
|
|
|
266
|
-
|
|
317
|
+
// Pass the top-level operation for the token-scope check: for an export job, operation_json
|
|
318
|
+
// is the nested search_operation, so json.operation (export_local/export_to_s3) is the op the
|
|
319
|
+
// scope must gate — not the inner search.
|
|
320
|
+
const verifyPermsResult = opAuth.verifyPerms(operation_json, functionToCheck, {
|
|
321
|
+
apiOperation: json.operation,
|
|
322
|
+
});
|
|
267
323
|
|
|
268
324
|
if (verifyPermsResult) {
|
|
269
325
|
operationLog.error(`${HTTP_STATUS_CODES.FORBIDDEN} from operation ${json.operation}`);
|
|
@@ -357,6 +413,9 @@ async function catchup(req: CatchupOperationRequest) {
|
|
|
357
413
|
case terms.OPERATIONS_ENUM.UPSERT:
|
|
358
414
|
await insert.upsert(transaction);
|
|
359
415
|
break;
|
|
416
|
+
case terms.OPERATIONS_ENUM.PUT:
|
|
417
|
+
await insert.put(transaction);
|
|
418
|
+
break;
|
|
360
419
|
case terms.OPERATIONS_ENUM.DELETE:
|
|
361
420
|
await delete_.deleteRecord(transaction);
|
|
362
421
|
break;
|
|
@@ -411,6 +470,7 @@ function initializeOperationFunctionMap(): Map<OperationFunctionName, OperationF
|
|
|
411
470
|
opFuncMap.set(terms.OPERATIONS_ENUM.INSERT, new OperationFunctionObject(insert.insert));
|
|
412
471
|
opFuncMap.set(terms.OPERATIONS_ENUM.UPDATE, new OperationFunctionObject(insert.update));
|
|
413
472
|
opFuncMap.set(terms.OPERATIONS_ENUM.UPSERT, new OperationFunctionObject(insert.upsert));
|
|
473
|
+
opFuncMap.set(terms.OPERATIONS_ENUM.PUT, new OperationFunctionObject(insert.put));
|
|
414
474
|
opFuncMap.set(terms.OPERATIONS_ENUM.SEARCH_BY_CONDITIONS, new OperationFunctionObject(search.searchByConditions));
|
|
415
475
|
opFuncMap.set(terms.OPERATIONS_ENUM.SEARCH_BY_HASH, new OperationFunctionObject(search.searchByHash));
|
|
416
476
|
opFuncMap.set(terms.OPERATIONS_ENUM.SEARCH_BY_ID, new OperationFunctionObject(search.searchByHash));
|
|
@@ -581,6 +641,19 @@ function initializeOperationFunctionMap(): Map<OperationFunctionName, OperationF
|
|
|
581
641
|
terms.OPERATIONS_ENUM.GET_SECRETS_PUBLIC_KEY,
|
|
582
642
|
new OperationFunctionObject(secretOperations.getSecretsPublicKey)
|
|
583
643
|
);
|
|
644
|
+
opFuncMap.set(terms.OPERATIONS_ENUM.ADD_OIDC_TRUST, new OperationFunctionObject(trustPolicyOperations.addOidcTrust));
|
|
645
|
+
opFuncMap.set(
|
|
646
|
+
terms.OPERATIONS_ENUM.LIST_OIDC_TRUST,
|
|
647
|
+
new OperationFunctionObject(trustPolicyOperations.listOidcTrust)
|
|
648
|
+
);
|
|
649
|
+
opFuncMap.set(
|
|
650
|
+
terms.OPERATIONS_ENUM.DROP_OIDC_TRUST,
|
|
651
|
+
new OperationFunctionObject(trustPolicyOperations.dropOidcTrust)
|
|
652
|
+
);
|
|
653
|
+
opFuncMap.set(
|
|
654
|
+
terms.OPERATIONS_ENUM.EXCHANGE_OIDC_TOKEN,
|
|
655
|
+
new OperationFunctionObject(tokenExchange.exchangeOidcToken)
|
|
656
|
+
);
|
|
584
657
|
opFuncMap.set(
|
|
585
658
|
terms.OPERATIONS_ENUM.READ_TRANSACTION_LOG,
|
|
586
659
|
new OperationFunctionObject(transactionLog.readTransactionLog)
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { serializeMessage, getMessageSerializer } from './contentTypes.ts';
|
|
2
|
+
import { logger } from '../../utility/logging/logger.ts';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The encoding work that every subscriber of a topic would otherwise redo for the same message.
|
|
6
|
+
* `payload` is the serialized message body; `frames` holds fully encoded protocol frames that are
|
|
7
|
+
* byte-identical across subscribers (MQTT QoS 0 PUBLISH packets), keyed by whatever the frame
|
|
8
|
+
* still varies on. `next` chains the encodings for any further content types the same message was
|
|
9
|
+
* delivered in — a chain rather than a map because a topic's subscribers realistically negotiate
|
|
10
|
+
* one or two content types, and this keeps the single-content-type case to one allocation.
|
|
11
|
+
*
|
|
12
|
+
* `hits` counts the subscribers after the first, so a caller can skip caching anything derived
|
|
13
|
+
* from the payload until a fan-out is known to exist. `failed` is a distinct state from an
|
|
14
|
+
* undefined `payload`, which a serializer can legitimately return (JSON of `undefined`).
|
|
15
|
+
* `bytes` is everything this entry holds — payload plus frames — and `epoch` records which
|
|
16
|
+
* generation those bytes are charged to, so work that lands after a rotation (a serialization that
|
|
17
|
+
* resolves late) is not charged to a budget it does not belong to, and a promotion re-charges the
|
|
18
|
+
* entry exactly once. That is what makes the budget an actual accounting rather than an estimate.
|
|
19
|
+
*/
|
|
20
|
+
export interface SharedMessageEncoding {
|
|
21
|
+
serializer: Function;
|
|
22
|
+
version: number | undefined;
|
|
23
|
+
payload: Buffer | string | Promise<Buffer | string> | undefined;
|
|
24
|
+
failed: boolean;
|
|
25
|
+
hits: number;
|
|
26
|
+
bytes: number;
|
|
27
|
+
epoch: number;
|
|
28
|
+
frames: Map<number, Map<string, Buffer>> | undefined;
|
|
29
|
+
next: SharedMessageEncoding | undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// A message that carries its own content type is passed through verbatim and never negotiated, so
|
|
33
|
+
// every subscriber shares one entry no matter what it negotiated. This sentinel stands in for the
|
|
34
|
+
// serializer that was deliberately not consulted.
|
|
35
|
+
const PASS_THROUGH = function passThrough() {};
|
|
36
|
+
|
|
37
|
+
// THE IDENTITY CONTRACT. A fan-out has a single shared origin: the same message object instance is
|
|
38
|
+
// dispatched to every subscription of a key (resources/transactionBroadcast.ts), so identity is
|
|
39
|
+
// what lets the first subscriber to encode do the work for all of them. This is only sound because
|
|
40
|
+
// every internal producer yields a *fresh object per version* — transactionBroadcast hands one
|
|
41
|
+
// auditRecord to every subscription, auditStore's getValue memoizes the decode in its closure, and
|
|
42
|
+
// primaryStore.getEntry is guarded by a version check — so identity implies equal content, and a
|
|
43
|
+
// miss (a primitive message, or a store cache eviction between subscribers) only costs the
|
|
44
|
+
// redundant encode we have today rather than yielding stale bytes.
|
|
45
|
+
//
|
|
46
|
+
// A custom Resource is not bound by that, so identity alone is not trusted: the event's record
|
|
47
|
+
// version is part of the key (see getSharedMessageEncoding), which makes reusing one envelope
|
|
48
|
+
// across publishes correct as long as the version advances, and refuses to share at all when there
|
|
49
|
+
// is no version to tell one publish from the next.
|
|
50
|
+
//
|
|
51
|
+
// Retention is bounded by rotating generations rather than by the message object's lifetime: for
|
|
52
|
+
// record events the message IS the store's cached record, so hanging encodings off it alone would
|
|
53
|
+
// keep serialized copies and whole PUBLISH buffers resident for as long as the record stays cached,
|
|
54
|
+
// while sharing is only ever needed for the duration of one fan-out. Two generations are kept so a
|
|
55
|
+
// fan-out spanning a rotation still hits; dropping a generation releases all of it at once.
|
|
56
|
+
const ROTATE_INTERVAL_MS = 10_000;
|
|
57
|
+
// Time alone bounds how LONG an entry is retained, not how MUCH: at high throughput on
|
|
58
|
+
// single-subscriber topics the interval's worth of payloads would all be live at once, for no
|
|
59
|
+
// benefit. The byte budget puts a ceiling on that, at the cost of a fan-out that spans an early
|
|
60
|
+
// rotation falling back to re-encoding — slower, never wrong. Note this is the ceiling for ONE
|
|
61
|
+
// generation; the previous generation can hold as much again, so steady-state retention is up to
|
|
62
|
+
// twice this per worker thread.
|
|
63
|
+
export const MAX_RETAINED_BYTES = 4_000_000;
|
|
64
|
+
let currentGeneration = new WeakMap<object, SharedMessageEncoding>();
|
|
65
|
+
let previousGeneration = new WeakMap<object, SharedMessageEncoding>();
|
|
66
|
+
// Rotation runs on a timer, not on a delivery counter: the bound has to hold when traffic stops,
|
|
67
|
+
// which is exactly when a delivery-gated check would never fire again. Insert counts only decide
|
|
68
|
+
// when the timer can stop — a WeakMap can not be asked whether it is empty.
|
|
69
|
+
let rotationTimer: ReturnType<typeof setInterval> | undefined;
|
|
70
|
+
let currentInserts = 0;
|
|
71
|
+
let currentBytes = 0;
|
|
72
|
+
// Identifies the generation an entry's bytes are charged to. Work can land after a rotation — a
|
|
73
|
+
// serialization that resolves late, a frame generated by a subscriber that arrived slowly — and
|
|
74
|
+
// charging that to the current budget would bill a generation the bytes do not belong to, then bill
|
|
75
|
+
// them again when a promotion re-charges the entry.
|
|
76
|
+
const UNRETAINED = -1;
|
|
77
|
+
// starts at 1 so UNRETAINED can never collide with `currentEpoch - 1`, which would make charge()
|
|
78
|
+
// treat a never-retained entry as one still live in the previous generation
|
|
79
|
+
let currentEpoch = 1;
|
|
80
|
+
|
|
81
|
+
/** Retire the older generation. Exported for the rotation timer and for tests. */
|
|
82
|
+
export function rotateSharedEncodings() {
|
|
83
|
+
previousGeneration = currentGeneration;
|
|
84
|
+
currentGeneration = new WeakMap();
|
|
85
|
+
currentEpoch++;
|
|
86
|
+
const rotatedInserts = currentInserts;
|
|
87
|
+
currentInserts = 0;
|
|
88
|
+
currentBytes = 0;
|
|
89
|
+
if (rotatedInserts === 0 && rotationTimer !== undefined) {
|
|
90
|
+
// nothing is retained in either generation, so stop ticking until the next insert
|
|
91
|
+
clearInterval(rotationTimer);
|
|
92
|
+
rotationTimer = undefined;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Whether the rotation timer is currently armed. Introspection for tests. */
|
|
97
|
+
export function isRotationScheduled(): boolean {
|
|
98
|
+
return rotationTimer !== undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/** Bytes charged to the live generation. Introspection so tests can pin the accounting exactly. */
|
|
102
|
+
export function getRetainedBytes(): number {
|
|
103
|
+
return currentBytes;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function retainedSize(payload: SharedMessageEncoding['payload']): number {
|
|
107
|
+
// a pending promise is accounted for when it resolves
|
|
108
|
+
if (typeof payload === 'string') return Buffer.byteLength(payload);
|
|
109
|
+
return (payload as Buffer)?.byteLength ?? 0;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Add to an entry's footprint, and to the budget if the entry is live. */
|
|
113
|
+
function charge(encoding: SharedMessageEncoding, bytes: number) {
|
|
114
|
+
encoding.bytes += bytes;
|
|
115
|
+
if (encoding.epoch === currentEpoch) currentBytes += bytes;
|
|
116
|
+
else if (encoding.epoch === currentEpoch - 1) {
|
|
117
|
+
// Bytes can land after a rotation — a serialization resolving late, or a frame generated by a
|
|
118
|
+
// subscriber whose await spanned one. The entry is still live in the previous generation, so
|
|
119
|
+
// fold its whole footprint into the live budget; charging only the new bytes would leave the
|
|
120
|
+
// rest counted by no generation at all, and charging nothing would under-report the ceiling.
|
|
121
|
+
encoding.epoch = currentEpoch;
|
|
122
|
+
currentBytes += encoding.bytes;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** Give back everything a chain holds, for an entry that is being discarded rather than refilled. */
|
|
127
|
+
function release(encoding: SharedMessageEncoding) {
|
|
128
|
+
for (let node = encoding; node !== undefined; node = node.next) {
|
|
129
|
+
if (node.epoch === currentEpoch) currentBytes -= node.bytes;
|
|
130
|
+
node.epoch = UNRETAINED;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function retain(message: object, encoding: SharedMessageEncoding) {
|
|
135
|
+
if (currentBytes >= MAX_RETAINED_BYTES) rotateSharedEncodings();
|
|
136
|
+
currentGeneration.set(message, encoding);
|
|
137
|
+
currentInserts++;
|
|
138
|
+
// charges the whole chain: on a promotion this re-charges everything the entry holds, since
|
|
139
|
+
// rotation zeroed the counter but the bytes are still live
|
|
140
|
+
for (let node = encoding; node !== undefined; node = node.next) {
|
|
141
|
+
if (node.epoch !== currentEpoch) {
|
|
142
|
+
node.epoch = currentEpoch;
|
|
143
|
+
currentBytes += node.bytes;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (rotationTimer === undefined) {
|
|
147
|
+
rotationTimer = setInterval(rotateSharedEncodings, ROTATE_INTERVAL_MS);
|
|
148
|
+
rotationTimer.unref?.();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function fill(encoding: SharedMessageEncoding, message: any, request: any) {
|
|
153
|
+
// whatever this entry was holding is being replaced, so give the bytes back before re-charging
|
|
154
|
+
if (encoding.epoch === currentEpoch) currentBytes -= encoding.bytes;
|
|
155
|
+
encoding.bytes = 0;
|
|
156
|
+
encoding.frames = undefined;
|
|
157
|
+
// Pessimistic: an entry is only valid once a payload is actually in hand. Set optimistically,
|
|
158
|
+
// a synchronous throw here would leave an already-retained entry claiming success with no
|
|
159
|
+
// payload, and every later subscriber would send an empty PUBLISH instead of erroring.
|
|
160
|
+
encoding.failed = true;
|
|
161
|
+
encoding.payload = undefined;
|
|
162
|
+
const payload = serializeMessage(message, request);
|
|
163
|
+
encoding.payload = payload;
|
|
164
|
+
encoding.failed = false;
|
|
165
|
+
// A pass-through payload IS the message's own data, already reachable from the WeakMap key, so
|
|
166
|
+
// caching it retains nothing extra and must not be billed against the budget.
|
|
167
|
+
if (encoding.serializer === PASS_THROUGH) return;
|
|
168
|
+
if (typeof (payload as any)?.then === 'function')
|
|
169
|
+
// collapse an async serialization in place so later subscribers, and frame sharing, see the
|
|
170
|
+
// finished buffer rather than re-entering the async path
|
|
171
|
+
encoding.payload = (payload as Promise<Buffer | string>).then(
|
|
172
|
+
(resolved) => {
|
|
173
|
+
charge(encoding, retainedSize(resolved));
|
|
174
|
+
return (encoding.payload = resolved);
|
|
175
|
+
},
|
|
176
|
+
(error) => {
|
|
177
|
+
// mark for retry rather than memoize the failure: a shared rejected promise would turn
|
|
178
|
+
// one transient serialization error into a permanent one for every later subscriber
|
|
179
|
+
encoding.failed = true;
|
|
180
|
+
encoding.payload = undefined;
|
|
181
|
+
throw error;
|
|
182
|
+
}
|
|
183
|
+
);
|
|
184
|
+
else charge(encoding, retainedSize(payload));
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function encode(message: any, request: any, serializer: Function, version?: number): SharedMessageEncoding {
|
|
188
|
+
const encoding: SharedMessageEncoding = {
|
|
189
|
+
serializer,
|
|
190
|
+
version,
|
|
191
|
+
payload: undefined,
|
|
192
|
+
failed: false,
|
|
193
|
+
hits: 0,
|
|
194
|
+
bytes: 0,
|
|
195
|
+
epoch: UNRETAINED,
|
|
196
|
+
frames: undefined,
|
|
197
|
+
next: undefined,
|
|
198
|
+
};
|
|
199
|
+
fill(encoding, message, request);
|
|
200
|
+
return encoding;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function reuse(encoding: SharedMessageEncoding, message: any, request: any): SharedMessageEncoding {
|
|
204
|
+
encoding.hits++;
|
|
205
|
+
if (encoding.failed) fill(encoding, message, request);
|
|
206
|
+
return encoding;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Get the encoding shared by every subscriber that negotiated the same content type. Keyed on the
|
|
211
|
+
* message object's identity, the serializer function (which is what `serializeMessage` output
|
|
212
|
+
* varies on), and the event's record `version`.
|
|
213
|
+
*
|
|
214
|
+
* The version is part of the key, not a permission to share. Identity alone would be unsafe for a
|
|
215
|
+
* producer that reuses one mutable envelope across publishes — an app Resource wrapping a table
|
|
216
|
+
* subscription to enrich events can easily do that, and copies the version straight through with a
|
|
217
|
+
* spread — so a cached entry whose version does not match is treated as a miss and re-encoded.
|
|
218
|
+
* That leaves only same-object-and-same-version mutation, which is indistinguishable from "the same
|
|
219
|
+
* message". An event with no version at all is not shared: nothing then distinguishes one publish
|
|
220
|
+
* from the next.
|
|
221
|
+
*/
|
|
222
|
+
export function getSharedMessageEncoding(message: any, request?: any, version?: number): SharedMessageEncoding {
|
|
223
|
+
// Negotiation must not run for a pass-through message: an Accept header naming only unsupported
|
|
224
|
+
// types throws 406, which would kill a delivery that used to succeed by returning message.data.
|
|
225
|
+
const serializer =
|
|
226
|
+
message?.contentType != null && message.data != null ? PASS_THROUGH : getMessageSerializer(request);
|
|
227
|
+
// primitives can not key a WeakMap, and are cheap to encode anyway; these stay per-subscriber
|
|
228
|
+
if (message === null || typeof message !== 'object' || typeof version !== 'number')
|
|
229
|
+
return encode(message, request, serializer);
|
|
230
|
+
let encoding = currentGeneration.get(message);
|
|
231
|
+
if (encoding === undefined) {
|
|
232
|
+
encoding = previousGeneration.get(message);
|
|
233
|
+
// promote a surviving entry so it lives through the next rotation
|
|
234
|
+
if (encoding !== undefined && encoding.version === version) retain(message, encoding);
|
|
235
|
+
}
|
|
236
|
+
if (encoding !== undefined && encoding.version !== version) {
|
|
237
|
+
// the same object carrying a new version: every content type on the chain is stale, so give
|
|
238
|
+
// back what the whole chain held and start over rather than serving the earlier message
|
|
239
|
+
release(encoding);
|
|
240
|
+
encoding = undefined;
|
|
241
|
+
}
|
|
242
|
+
if (encoding === undefined) {
|
|
243
|
+
retain(message, (encoding = encode(message, request, serializer, version)));
|
|
244
|
+
return encoding;
|
|
245
|
+
}
|
|
246
|
+
let last: SharedMessageEncoding;
|
|
247
|
+
do {
|
|
248
|
+
if (encoding.serializer === serializer) return reuse(encoding, message, request);
|
|
249
|
+
last = encoding;
|
|
250
|
+
encoding = encoding.next;
|
|
251
|
+
} while (encoding !== undefined);
|
|
252
|
+
// a second content type on an already-retained message is retained with it, so it is charged too
|
|
253
|
+
const chained = encode(message, request, serializer, version);
|
|
254
|
+
chained.epoch = last.epoch;
|
|
255
|
+
if (chained.epoch === currentEpoch) currentBytes += chained.bytes;
|
|
256
|
+
return (last.next = chained);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Resolve a still-pending shared serialization, retrying once through a fresh lookup if it failed.
|
|
261
|
+
* Every subscriber currently in the fan-out awaits the same promise, so without this one transient
|
|
262
|
+
* failure would disconnect all of them, where before the encoding was shared each subscriber
|
|
263
|
+
* serialized — and so retried — on its own.
|
|
264
|
+
*/
|
|
265
|
+
export async function resolveSharedPayload(
|
|
266
|
+
encoding: SharedMessageEncoding,
|
|
267
|
+
message: any,
|
|
268
|
+
request: any,
|
|
269
|
+
version?: number
|
|
270
|
+
): Promise<Buffer | string> {
|
|
271
|
+
try {
|
|
272
|
+
return await (encoding.payload as Promise<Buffer | string>);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
// otherwise a failure that the retry recovers from leaves no trace at all
|
|
275
|
+
logger.debug?.('Shared message serialization failed, retrying for this subscriber', error);
|
|
276
|
+
// The entry is marked for retry, or a peer in this same wave has already refilled it — either
|
|
277
|
+
// way, ask again. The first subscriber here re-encodes and the rest share that attempt; if it
|
|
278
|
+
// fails too, that rejection propagates and the caller handles it as it did before sharing.
|
|
279
|
+
return await getSharedMessageEncoding(message, request, version).payload;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
/**
|
|
284
|
+
* Look up a fully encoded frame shared across subscribers, or undefined if it has not been
|
|
285
|
+
* generated yet. Split from `setSharedFrame` so a cache hit — the whole point on a fan-out —
|
|
286
|
+
* allocates nothing, not even a generator closure.
|
|
287
|
+
*/
|
|
288
|
+
export function getSharedFrame(encoding: SharedMessageEncoding, version: number, topic: string): Buffer | undefined {
|
|
289
|
+
return encoding.frames?.get(version)?.get(topic);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Store a generated frame for reuse by the remaining subscribers, and return it. Call only when
|
|
294
|
+
* `encoding.hits > 0`: a frame stored for a fan-out of one is never read, and would keep a whole
|
|
295
|
+
* PUBLISH buffer reachable from the message object for the rest of its retention window.
|
|
296
|
+
*/
|
|
297
|
+
export function setSharedFrame(encoding: SharedMessageEncoding, version: number, topic: string, frame: Buffer): Buffer {
|
|
298
|
+
let frames = encoding.frames;
|
|
299
|
+
if (frames === undefined) encoding.frames = frames = new Map();
|
|
300
|
+
let byTopic = frames.get(version);
|
|
301
|
+
if (byTopic === undefined) frames.set(version, (byTopic = new Map()));
|
|
302
|
+
// concurrent subscribers can each generate before any of them stores; only the retained buffer
|
|
303
|
+
// is charged, or the budget would bill every duplicate that is immediately garbage
|
|
304
|
+
if (!byTopic.has(topic)) charge(encoding, frame.byteLength);
|
|
305
|
+
byTopic.set(topic, frame);
|
|
306
|
+
return frame;
|
|
307
|
+
}
|
|
@@ -225,8 +225,23 @@ export async function createUwsServer(options: UwsServerOptions): Promise<{ app:
|
|
|
225
225
|
app.any('/*', withBody);
|
|
226
226
|
|
|
227
227
|
await new Promise<void>((resolve, reject) => {
|
|
228
|
-
|
|
229
|
-
|
|
228
|
+
let settled = false;
|
|
229
|
+
// Backstop against the native listen callback never firing at all — without it, a stuck
|
|
230
|
+
// bind is indistinguishable from a slow one, and nothing here ever rejects.
|
|
231
|
+
const bindTimeout = setTimeout(() => {
|
|
232
|
+
if (settled) return;
|
|
233
|
+
settled = true;
|
|
234
|
+
reject(
|
|
235
|
+
new Error(`uWS timed out waiting to bind ${host ?? ''}:${port ?? socketPath} (listen callback never fired)`)
|
|
236
|
+
);
|
|
237
|
+
}, 30_000);
|
|
238
|
+
const onListen = (listenSocket: unknown) => {
|
|
239
|
+
if (settled) return;
|
|
240
|
+
settled = true;
|
|
241
|
+
clearTimeout(bindTimeout);
|
|
242
|
+
if (listenSocket) resolve();
|
|
243
|
+
else reject(new Error(`uWS failed to bind ${host ?? ''}:${port ?? socketPath}`));
|
|
244
|
+
};
|
|
230
245
|
// uWS shares the port across workers (SO_REUSEPORT) by default, matching the Node reusePort path.
|
|
231
246
|
if (port != null) {
|
|
232
247
|
if (host) app.listen(host, port, onListen);
|
package/server/static.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { resolveBaseURLPath } from '../components/resolveBaseURLPath.ts';
|
|
|
5
5
|
import { convertToMS } from '../utility/common_utils.ts';
|
|
6
6
|
import { isMatch } from 'micromatch';
|
|
7
7
|
import send from 'send';
|
|
8
|
+
import { settleDeferredCredentialRejection } from '../security/deferredAuthentication.ts';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* The static plugin handles serving static files from the respective application directory.
|
|
@@ -337,40 +338,30 @@ export function handleApplication(scope: Scope) {
|
|
|
337
338
|
// Retrieve index entry
|
|
338
339
|
staticFile = indexEntries.get(req.pathname);
|
|
339
340
|
|
|
340
|
-
//
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
if (staticFile && req.pathname === '/' && externalBaseURLPath !== '/') {
|
|
350
|
-
const originalPathname: string | undefined = (req as any).originalPathname;
|
|
351
|
-
if (originalPathname && !originalPathname.endsWith('/')) {
|
|
352
|
-
const queryIndex = (req.url as string).indexOf('?');
|
|
353
|
-
const query = queryIndex === -1 ? '' : (req.url as string).slice(queryIndex);
|
|
354
|
-
return {
|
|
355
|
-
status: 301,
|
|
356
|
-
headers: {
|
|
357
|
-
Location: externalBaseURLPath + query,
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}
|
|
341
|
+
// Prefix stripping makes `originalPathname` necessary to distinguish mounted roots (#1583).
|
|
342
|
+
const originalPathname: string | undefined = (req as any).originalPathname;
|
|
343
|
+
const redirectsMountRoot = !!(
|
|
344
|
+
staticFile &&
|
|
345
|
+
req.pathname === '/' &&
|
|
346
|
+
externalBaseURLPath !== '/' &&
|
|
347
|
+
originalPathname &&
|
|
348
|
+
!originalPathname.endsWith('/')
|
|
349
|
+
);
|
|
362
350
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const externalPath =
|
|
367
|
-
externalBaseURLPath === '/' ? req.pathname : externalBaseURLPath.slice(0, -1) + req.pathname;
|
|
351
|
+
if (redirectsMountRoot || staticFile === null) {
|
|
352
|
+
const settledCredentialRejection = settleDeferredCredentialRejection(req);
|
|
353
|
+
if (settledCredentialRejection) return settledCredentialRejection;
|
|
368
354
|
const queryIndex = (req.url as string).indexOf('?');
|
|
369
355
|
const query = queryIndex === -1 ? '' : (req.url as string).slice(queryIndex);
|
|
356
|
+
const location = redirectsMountRoot
|
|
357
|
+
? externalBaseURLPath + query
|
|
358
|
+
: (externalBaseURLPath === '/' ? req.pathname : externalBaseURLPath.slice(0, -1) + req.pathname) +
|
|
359
|
+
'/' +
|
|
360
|
+
query;
|
|
370
361
|
return {
|
|
371
362
|
status: 301,
|
|
372
363
|
headers: {
|
|
373
|
-
Location:
|
|
364
|
+
Location: location,
|
|
374
365
|
},
|
|
375
366
|
};
|
|
376
367
|
}
|
|
@@ -393,6 +384,8 @@ export function handleApplication(scope: Scope) {
|
|
|
393
384
|
|
|
394
385
|
// If an entry matched, serve it
|
|
395
386
|
if (staticFile) {
|
|
387
|
+
const settledCredentialRejection = settleDeferredCredentialRejection(req);
|
|
388
|
+
if (settledCredentialRejection) return settledCredentialRejection;
|
|
396
389
|
// The benefit to using `send` is that it handles a lot of edge cases and headers for us.
|
|
397
390
|
return {
|
|
398
391
|
handlesHeaders: true,
|
|
@@ -405,7 +398,8 @@ export function handleApplication(scope: Scope) {
|
|
|
405
398
|
return next(req);
|
|
406
399
|
}
|
|
407
400
|
|
|
408
|
-
|
|
401
|
+
const settledCredentialRejection = settleDeferredCredentialRejection(req);
|
|
402
|
+
if (settledCredentialRejection) return settledCredentialRejection;
|
|
409
403
|
|
|
410
404
|
const notFound = scope.options.get(['notFound']);
|
|
411
405
|
|