@harperfast/harper 5.2.13 → 5.3.0-beta.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 +58 -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 +120 -4
- package/bin/run.ts +6 -11
- package/bin/upgrade.js +7 -3
- package/bin/workloadIdentity.ts +119 -0
- package/components/Application.ts +3436 -242
- package/components/ApplicationScope.ts +8 -0
- package/components/EntryHandler.ts +59 -39
- package/components/OptionsWatcher.ts +440 -98
- package/components/RuntimeModuleTracker.ts +38 -7
- package/components/Scope.ts +56 -15
- package/components/awaitRestart.ts +84 -0
- package/components/componentLoader.ts +381 -32
- package/components/componentPreparationLock.ts +16 -5
- package/components/deploymentOperations.ts +4 -1
- package/components/deploymentRecorder.ts +9 -2
- 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 +537 -113
- package/components/operationsValidation.js +98 -3
- package/components/packageComponent.ts +25 -1
- package/components/requestRestart.ts +11 -0
- package/components/status/ComponentStatusRegistry.ts +59 -0
- package/config/RootConfigWatcher.ts +240 -40
- package/config/configReadRetry.ts +62 -0
- package/config/configUtils.ts +357 -48
- package/config/harperConfigEnvVars.ts +170 -27
- package/config/parseConfigFile.ts +34 -0
- package/config/readConfigFileSync.ts +44 -0
- package/config/watcherArming.ts +59 -0
- package/config-root.schema.json +33 -0
- package/dataLayer/blobBackup.ts +160 -50
- package/dataLayer/delete.ts +6 -1
- package/dataLayer/harperBridge/ResourceBridge.ts +80 -10
- 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 +61 -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 +91 -6
- 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 +235 -17
- package/dist/components/Application.js +3038 -238
- 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 +4 -0
- package/dist/components/OptionsWatcher.js +440 -99
- 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 +52 -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 +313 -24
- 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/deploymentOperations.js +4 -1
- package/dist/components/deploymentOperations.js.map +1 -1
- package/dist/components/deploymentRecorder.d.ts +4 -2
- package/dist/components/deploymentRecorder.js +1 -0
- package/dist/components/deploymentRecorder.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.d.ts +28 -0
- package/dist/components/operations.js +476 -113
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.js +97 -3
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/packageComponent.js +24 -0
- package/dist/components/packageComponent.js.map +1 -1
- package/dist/components/requestRestart.d.ts +1 -0
- package/dist/components/requestRestart.js +7 -0
- package/dist/components/requestRestart.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 +9 -1
- package/dist/config/RootConfigWatcher.js +228 -37
- package/dist/config/RootConfigWatcher.js.map +1 -1
- package/dist/config/configReadRetry.d.ts +8 -0
- package/dist/config/configReadRetry.js +62 -0
- package/dist/config/configReadRetry.js.map +1 -0
- package/dist/config/configUtils.d.ts +11 -2
- package/dist/config/configUtils.js +304 -47
- 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/config/parseConfigFile.d.ts +4 -0
- package/dist/config/parseConfigFile.js +35 -0
- package/dist/config/parseConfigFile.js.map +1 -0
- package/dist/config/readConfigFileSync.d.ts +1 -0
- package/dist/config/readConfigFileSync.js +47 -0
- package/dist/config/readConfigFileSync.js.map +1 -0
- package/dist/config/watcherArming.d.ts +15 -0
- package/dist/config/watcherArming.js +59 -0
- package/dist/config/watcherArming.js.map +1 -0
- 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 +72 -12
- 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 +2 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +55 -0
- package/dist/resources/DatabaseTransaction.d.ts +77 -0
- package/dist/resources/DatabaseTransaction.js +586 -54
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +2 -1
- package/dist/resources/LMDBTransaction.js +43 -6
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +1 -0
- package/dist/resources/PrimaryRocksDatabase.js +49 -5
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -1
- package/dist/resources/RecordEncoder.d.ts +20 -0
- package/dist/resources/RecordEncoder.js +110 -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 +117 -24
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +28 -1
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/RocksIndexStore.d.ts +6 -1
- package/dist/resources/RocksIndexStore.js +24 -9
- package/dist/resources/RocksIndexStore.js.map +1 -1
- package/dist/resources/RocksTransactionLogStore.d.ts +24 -1
- package/dist/resources/RocksTransactionLogStore.js +160 -49
- package/dist/resources/RocksTransactionLogStore.js.map +1 -1
- package/dist/resources/Table.d.ts +165 -10
- package/dist/resources/Table.js +1805 -303
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/write.js +10 -3
- package/dist/resources/analytics/write.js.map +1 -1
- package/dist/resources/auditStore.d.ts +181 -2
- package/dist/resources/auditStore.js +640 -29
- 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/dataLoader.js +3 -4
- package/dist/resources/dataLoader.js.map +1 -1
- package/dist/resources/databases.d.ts +157 -14
- package/dist/resources/databases.js +1462 -325
- 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/derivedIndexRegistry.d.ts +5 -0
- package/dist/resources/derivedIndexRegistry.js +68 -0
- package/dist/resources/derivedIndexRegistry.js.map +1 -0
- package/dist/resources/derivedIndexRuntime.d.ts +215 -0
- package/dist/resources/derivedIndexRuntime.js +2027 -0
- package/dist/resources/derivedIndexRuntime.js.map +1 -0
- package/dist/resources/graphql.d.ts +1 -1
- package/dist/resources/graphql.js +52 -16
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +103 -9
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +854 -41
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/indexes/hnswDerivedIndex.d.ts +67 -0
- package/dist/resources/indexes/hnswDerivedIndex.js +464 -0
- package/dist/resources/indexes/hnswDerivedIndex.js.map +1 -0
- package/dist/resources/indexes/hnswPlaneBinding.d.ts +65 -0
- package/dist/resources/indexes/hnswPlaneBinding.js +91 -0
- package/dist/resources/indexes/hnswPlaneBinding.js.map +1 -0
- 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/nodeIdMapping.d.ts +5 -0
- package/dist/resources/nodeIdMapping.js +49 -0
- package/dist/resources/nodeIdMapping.js.map +1 -1
- package/dist/resources/recordLock.d.ts +123 -0
- package/dist/resources/recordLock.js +315 -0
- package/dist/resources/recordLock.js.map +1 -0
- package/dist/resources/recordLockCoordinator.d.ts +557 -0
- package/dist/resources/recordLockCoordinator.js +2565 -0
- package/dist/resources/recordLockCoordinator.js.map +1 -0
- package/dist/resources/replayLogs.d.ts +14 -1
- package/dist/resources/replayLogs.js +157 -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/replicatedApplyFailure.d.ts +16 -0
- package/dist/resources/replicatedApplyFailure.js +63 -0
- package/dist/resources/replicatedApplyFailure.js.map +1 -0
- package/dist/resources/scheduler/scheduler.js +3 -3
- package/dist/resources/scheduler/scheduler.js.map +1 -1
- package/dist/resources/search.d.ts +10 -4
- package/dist/resources/search.js +283 -37
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/tracked.d.ts +5 -1
- package/dist/resources/tracked.js +74 -23
- package/dist/resources/tracked.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 +77 -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 +106 -2
- 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 +16 -1
- package/dist/server/http.js +122 -17
- 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 +27 -0
- package/dist/server/serverHelpers/Headers.js +145 -1
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/NodeAdapterResponse.d.ts +48 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js +220 -0
- package/dist/server/serverHelpers/NodeAdapterResponse.js.map +1 -0
- package/dist/server/serverHelpers/Request.d.ts +5 -10
- package/dist/server/serverHelpers/Request.js +38 -136
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.d.ts +11 -0
- package/dist/server/serverHelpers/contentTypes.js +221 -40
- 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 +178 -29
- 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 +17 -1
- package/dist/server/storageReclamation.js.map +1 -1
- package/dist/server/threads/isolatedApplications.d.ts +47 -0
- package/dist/server/threads/isolatedApplications.js +171 -0
- package/dist/server/threads/isolatedApplications.js.map +1 -0
- 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/logRotationTransport.d.ts +1 -0
- package/dist/server/threads/logRotationTransport.js +33 -0
- package/dist/server/threads/logRotationTransport.js.map +1 -0
- package/dist/server/threads/manageThreads.d.ts +83 -3
- package/dist/server/threads/manageThreads.js +769 -75
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.d.ts +1 -0
- package/dist/server/threads/socketRouter.js +277 -31
- 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 +76 -22
- 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 +33 -0
- package/dist/utility/errors/hdbError.js +58 -1
- package/dist/utility/errors/hdbError.js.map +1 -1
- package/dist/utility/globalSchema.d.ts +18 -0
- package/dist/utility/hdbTerms.d.ts +18 -0
- package/dist/utility/hdbTerms.js +20 -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 +286 -30
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logGenerationCoordinator.d.ts +35 -0
- package/dist/utility/logging/logGenerationCoordinator.js +184 -0
- package/dist/utility/logging/logGenerationCoordinator.js.map +1 -0
- package/dist/utility/logging/logRotation.d.ts +46 -0
- package/dist/utility/logging/logRotation.js +365 -0
- package/dist/utility/logging/logRotation.js.map +1 -0
- package/dist/utility/logging/logRotator.d.ts +1 -1
- package/dist/utility/logging/logRotator.js +192 -85
- 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 +11 -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 +41 -0
- package/dist/utility/watcherFallback.js +153 -0
- package/dist/utility/watcherFallback.js.map +1 -1
- package/dist/validation/configValidator.d.ts +12 -0
- package/dist/validation/configValidator.js +205 -75
- 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 +8 -0
- package/json/systemSchema.json +55 -0
- package/npm-shrinkwrap.json +286 -194
- package/package.json +15 -7
- package/resources/DESIGN.md +219 -52
- package/resources/DatabaseTransaction.ts +657 -52
- package/resources/LMDBTransaction.ts +46 -6
- package/resources/PrimaryRocksDatabase.ts +46 -7
- package/resources/RecordEncoder.ts +124 -9
- package/resources/RequestTarget.ts +2 -0
- package/resources/Resource.ts +114 -22
- package/resources/ResourceInterface.ts +31 -0
- package/resources/RocksIndexStore.ts +30 -9
- package/resources/RocksTransactionLogStore.ts +188 -51
- package/resources/Table.ts +1960 -301
- package/resources/analytics/write.ts +10 -3
- package/resources/auditStore.ts +647 -32
- package/resources/blob.ts +1029 -110
- package/resources/branchDatabase.ts +941 -0
- package/resources/crdt.ts +76 -12
- package/resources/dataLoader.ts +3 -4
- package/resources/databases.ts +1584 -320
- package/resources/defineTable.ts +18 -2
- package/resources/derivedIndexRegistry.ts +56 -0
- package/resources/derivedIndexRuntime.ts +2292 -0
- package/resources/graphql.ts +72 -17
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +911 -48
- package/resources/indexes/hnswDerivedIndex.ts +531 -0
- package/resources/indexes/hnswPlaneBinding.ts +174 -0
- package/resources/longLivedTransactions.ts +360 -0
- package/resources/models/backendRegistry.ts +84 -2
- package/resources/models/bootstrap.ts +473 -28
- package/resources/nodeIdMapping.ts +50 -0
- package/resources/recordLock.ts +419 -0
- package/resources/recordLockCoordinator.ts +3043 -0
- package/resources/replayLogs.ts +152 -26
- package/resources/replayLogsGuards.ts +171 -8
- package/resources/replicatedApplyFailure.ts +77 -0
- package/resources/scheduler/scheduler.ts +4 -4
- package/resources/search.ts +288 -46
- package/resources/tracked.ts +73 -22
- 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 +83 -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 +194 -16
- package/server/DurableSubscriptionsSession.ts +71 -11
- package/server/REST.ts +115 -4
- package/server/graphqlQuerying.ts +4 -0
- package/server/http.ts +133 -20
- 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 +135 -0
- package/server/serverHelpers/NodeAdapterResponse.ts +221 -0
- package/server/serverHelpers/Request.ts +33 -131
- package/server/serverHelpers/contentTypes.ts +217 -36
- 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 +232 -40
- package/server/serverHelpers/sharedMessageEncoding.ts +307 -0
- package/server/serverHelpers/uwsServer.ts +17 -2
- package/server/static.ts +23 -29
- package/server/storageReclamation.ts +15 -2
- package/server/threads/isolatedApplications.ts +157 -0
- package/server/threads/itc.js +11 -1
- package/server/threads/logRotationTransport.ts +40 -0
- package/server/threads/manageThreads.js +771 -66
- package/server/threads/socketRouter.ts +291 -30
- package/server/threads/threadHeapMemory.ts +26 -0
- package/server/threads/threadServer.js +73 -22
- package/sqlTranslator/index.ts +31 -8
- package/studio/web/assets/{Chat-4RrB5134.js → Chat-D3j-1yY1.js} +1 -1
- package/studio/web/assets/{FloatingChat-omlNMDcJ.js → FloatingChat-BxJGYcfB.js} +3 -3
- package/studio/web/assets/{apiToken-Bke3wvfZ.js → apiToken-CT55oWOe.js} +1 -1
- package/studio/web/assets/{applications-DvFDYJgK.js → applications-D9Ct9_vm.js} +1 -1
- package/studio/web/assets/{cssMode-C1vRa7zh.js → cssMode-DV8H7VwA.js} +1 -1
- package/studio/web/assets/{editor-3XRWEDWX.js → editor-uatc0unt.js} +1 -1
- package/studio/web/assets/{html-CTY5tdMr.js → html-Bm6D6paN.js} +1 -1
- package/studio/web/assets/{htmlMode-CG1vSD9t.js → htmlMode-CEn7tpLG.js} +1 -1
- package/studio/web/assets/{index-Dfpeofdu.js → index-BIXW6Pu4.js} +5 -5
- package/studio/web/assets/{index.lazy-B1VOIv-t.js → index.lazy-UI7L-Vrk.js} +1 -1
- package/studio/web/assets/{javascript-CJeJzGnI.js → javascript-CJ0G3AFZ.js} +1 -1
- package/studio/web/assets/{jsonMode-CYPBwM82.js → jsonMode-DQADAYEa.js} +1 -1
- package/studio/web/assets/{languageServices-IKH4GUHl.js → languageServices-CAQJXWcI.js} +1 -1
- package/studio/web/assets/{lspLanguageFeatures-BC8_gxKG.js → lspLanguageFeatures-CCQ8P5sY.js} +1 -1
- package/studio/web/assets/{notifications-BfKBpYcq.js → notifications-BbxTU6Aw.js} +1 -1
- package/studio/web/assets/{notifications-DFbArTfC.js → notifications-Cvb3P1lB.js} +1 -1
- package/studio/web/assets/{profile-Q4-T6c-S.js → profile-Yyb7gsvL.js} +1 -1
- package/studio/web/assets/{regions-CtkV0xje.js → regions-OgjGHlU5.js} +1 -1
- package/studio/web/assets/{register-1ZZuMsiA.js → register-6qwNEOY3.js} +2 -2
- package/studio/web/assets/{setComponentFile-ZWLcWv5X.js → setComponentFile-BilDMtgB.js} +1 -1
- package/studio/web/assets/{setup-DWprJyJy.js → setup-J6qJ7OIU.js} +2 -2
- package/studio/web/assets/{status--aNm8isn.js → status-0RWGcfyD.js} +1 -1
- package/studio/web/assets/{toggleHighContrast-Cq_lt3XD.js → toggleHighContrast-BIn-vErT.js} +1 -1
- package/studio/web/assets/{tsMode-pjgytARx.js → tsMode-DgUXku4d.js} +1 -1
- package/studio/web/assets/{typescript-Cdg0mqUh.js → typescript-C9orXcsM.js} +1 -1
- package/studio/web/assets/{useEntityRestURL-RZhaY8Rn.js → useEntityRestURL-BEoXXbUB.js} +1 -1
- package/studio/web/assets/{workers-BgoXIqQe.js → workers-JVzSDmgx.js} +1 -1
- package/studio/web/assets/{xml-BwSeDMiP.js → xml-Cq-S8S4X.js} +1 -1
- package/studio/web/assets/{yaml-DotCUG5l.js → yaml-sfoRdh1M.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 +57 -0
- package/utility/hdbTerms.ts +19 -0
- package/utility/logging/harper_logger.ts +278 -26
- package/utility/logging/logGenerationCoordinator.ts +196 -0
- package/utility/logging/logRotation.ts +367 -0
- package/utility/logging/logRotator.ts +213 -81
- package/utility/nodeIdentity.ts +45 -0
- package/utility/npmUtilities.ts +12 -8
- package/utility/operationPermissions.ts +18 -1
- package/utility/operation_authorization.ts +231 -42
- package/utility/watchPath.ts +63 -0
- package/utility/watcherFallback.ts +148 -0
- package/validation/configValidator.ts +215 -75
- package/validation/installValidator.ts +15 -0
- package/validation/validationWrapper.ts +18 -4
|
@@ -3,6 +3,7 @@ import type { RequestParams } from 'graphql-http';
|
|
|
3
3
|
import { getDeserializer } from './serverHelpers/contentTypes.ts';
|
|
4
4
|
import { resources } from '../resources/Resources.ts';
|
|
5
5
|
import logger from '../utility/logging/harper_logger.ts';
|
|
6
|
+
import { settleDeferredCredentialRejection } from '../security/deferredAuthentication.ts';
|
|
6
7
|
|
|
7
8
|
// This code makes heavy use of the word "node" to refer to a node in the GraphQL AST.
|
|
8
9
|
|
|
@@ -580,6 +581,9 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
580
581
|
return nextLayer(request);
|
|
581
582
|
}
|
|
582
583
|
|
|
584
|
+
const settledCredentialRejection = settleDeferredCredentialRejection(request);
|
|
585
|
+
if (settledCredentialRejection) return settledCredentialRejection;
|
|
586
|
+
|
|
583
587
|
try {
|
|
584
588
|
// Await the `graphqlHandler` call here so that errors are caught.
|
|
585
589
|
return await graphqlQueryingHandler(request as any);
|
package/server/http.ts
CHANGED
|
@@ -12,13 +12,24 @@ import * as env from '../utility/environment/environmentManager.ts';
|
|
|
12
12
|
import * as terms from '../utility/hdbTerms.ts';
|
|
13
13
|
import { getConfigPath } from '../config/configUtils.ts';
|
|
14
14
|
import { getTicketKeys, getWorkerIndex } from './threads/manageThreads.js';
|
|
15
|
+
import {
|
|
16
|
+
applicationSocketName,
|
|
17
|
+
isolatedApplicationRoute,
|
|
18
|
+
thisThreadsIsolatedApplication,
|
|
19
|
+
} from './threads/isolatedApplications.ts';
|
|
15
20
|
import { createTLSSelector, getEffectiveTlsCiphers } from '../security/keys.ts';
|
|
16
21
|
import { createSecureServer, createServer as createH2CServer } from 'node:http2';
|
|
17
22
|
import { createServer as createSecureServerHttp1 } from 'node:https';
|
|
18
23
|
import { createServer, IncomingMessage, validateHeaderName, validateHeaderValue } from 'node:http';
|
|
19
24
|
import { createServer as createNetServer } from 'node:net';
|
|
20
25
|
import { Request, BunRequest, UwsRequest, isBun } from './serverHelpers/Request.ts';
|
|
21
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
appendHeader,
|
|
28
|
+
bridgeChainHeadersToNodeResponse,
|
|
29
|
+
Headers,
|
|
30
|
+
mergeChainHeadersIntoFallback,
|
|
31
|
+
toWriteHeadHeaders,
|
|
32
|
+
} from './serverHelpers/Headers.ts';
|
|
22
33
|
import {
|
|
23
34
|
decodeProxyHeader,
|
|
24
35
|
applyProxyHeader,
|
|
@@ -34,7 +45,7 @@ import { server, type ServerOptions, type HttpOptions, type UpgradeOptions, Upgr
|
|
|
34
45
|
import { setPortServerMap, SERVERS, socketOptionDefaults } from './serverRegistry.ts';
|
|
35
46
|
import { getComponentName } from '../components/componentLoader.ts';
|
|
36
47
|
import { throttle } from './throttle.ts';
|
|
37
|
-
import { makeCallbackChain as buildCallbackChain, describeChains } from './middlewareChain.ts';
|
|
48
|
+
import { makeCallbackChain as buildCallbackChain, describeChains, type HttpEntry } from './middlewareChain.ts';
|
|
38
49
|
import { WebSocketServer } from 'ws';
|
|
39
50
|
|
|
40
51
|
const { errorToString, errorForLog } = harperLogger;
|
|
@@ -190,10 +201,21 @@ export function writeUdsMetadata(
|
|
|
190
201
|
port: number | string,
|
|
191
202
|
secureServer: any,
|
|
192
203
|
protocol?: string,
|
|
193
|
-
mtlsForwarding = true
|
|
204
|
+
mtlsForwarding = true,
|
|
205
|
+
route: { application: string; hosts: string[] } | undefined = isolatedApplicationRoute()
|
|
194
206
|
) {
|
|
195
207
|
const contexts = secureServer.secureContexts;
|
|
196
208
|
let yaml = `pid: ${process.pid}\ntid: ${currentThreadId()}\nport: ${port}\n`;
|
|
209
|
+
// Routing identity, separate from certificate coverage: a proxy sends this application's hosts to
|
|
210
|
+
// this socket alone, whatever hostnames the (shared, possibly wildcard) certificates carry.
|
|
211
|
+
if (route) {
|
|
212
|
+
yaml += `application: ${JSON.stringify(route.application)}\n`;
|
|
213
|
+
if (route.hosts.length === 0) yaml += `applicationHosts: []\n`;
|
|
214
|
+
else {
|
|
215
|
+
yaml += `applicationHosts:\n`;
|
|
216
|
+
for (const host of route.hosts) yaml += ` - ${JSON.stringify(host)}\n`;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
197
219
|
// Which application protocol this socket speaks (absent = http/1.1, the historical
|
|
198
220
|
// default) — lets a fronting proxy route by negotiated ALPN.
|
|
199
221
|
if (protocol) yaml += `protocol: ${protocol}\n`;
|
|
@@ -261,6 +283,19 @@ export function cleanupSocketsDirectory() {
|
|
|
261
283
|
} catch {}
|
|
262
284
|
}
|
|
263
285
|
|
|
286
|
+
export function cleanupApplicationSockets(application: string) {
|
|
287
|
+
const socketsDir = join(env.getHdbBasePath(), 'sockets');
|
|
288
|
+
const prefix = applicationSocketName(application, '');
|
|
289
|
+
try {
|
|
290
|
+
for (const file of readdirSync(socketsDir)) {
|
|
291
|
+
if (!file.startsWith(prefix) || (!file.endsWith('.sock') && !file.endsWith('.yaml'))) continue;
|
|
292
|
+
try {
|
|
293
|
+
unlinkSync(join(socketsDir, file));
|
|
294
|
+
} catch {}
|
|
295
|
+
}
|
|
296
|
+
} catch {}
|
|
297
|
+
}
|
|
298
|
+
|
|
264
299
|
// Entries in `universalHeaders` that were pushed by `applySecurityHeaders`, so a config
|
|
265
300
|
// hot-reload can remove exactly the entries it owns without clobbering entries pushed by
|
|
266
301
|
// other components.
|
|
@@ -490,7 +525,7 @@ export function httpServer(listener, options) {
|
|
|
490
525
|
httpResponders[options?.runFirst ? 'unshift' : 'push'](entry);
|
|
491
526
|
} else if (isBun) {
|
|
492
527
|
// On Bun, store non-function listeners (e.g. Fastify's http.Server) for fallback delegation
|
|
493
|
-
|
|
528
|
+
registerFallbackServer(port, listener);
|
|
494
529
|
} else if ((httpServers[port] as any)?.uws) {
|
|
495
530
|
// uWS HTTP path (#914, HARPER_UWS_HTTP): the port is backed by uWebSockets.js, not a Node
|
|
496
531
|
// http server, so a raw non-function listener (e.g. Fastify's http.Server via
|
|
@@ -499,12 +534,12 @@ export function httpServer(listener, options) {
|
|
|
499
534
|
// port. Divert it to the fallback map like the Bun path; makeUwsHandler delegates unhandled
|
|
500
535
|
// requests to it via inject(). The { uws: true } marker is guaranteed present here: the
|
|
501
536
|
// getServer(port) call above (same loop iteration) sets it before this branch runs.
|
|
502
|
-
|
|
537
|
+
registerFallbackServer(port, listener);
|
|
503
538
|
} else {
|
|
504
539
|
listener.isSecure = secure;
|
|
505
540
|
registerServer(listener, port, false);
|
|
506
541
|
}
|
|
507
|
-
httpChain
|
|
542
|
+
buildChains(httpChain, httpResponders, port);
|
|
508
543
|
}
|
|
509
544
|
|
|
510
545
|
return servers;
|
|
@@ -543,10 +578,47 @@ export function pipeBodyToResponse(
|
|
|
543
578
|
} else {
|
|
544
579
|
recordAction(performance.now() - endTime, 'transfer', handlerPath, method);
|
|
545
580
|
recordAction(bytesSent, 'bytes-sent', handlerPath, method);
|
|
581
|
+
endConnectionIfClientExpectsClose(nodeResponse);
|
|
546
582
|
}
|
|
547
583
|
});
|
|
548
584
|
}
|
|
549
585
|
|
|
586
|
+
/**
|
|
587
|
+
* Bun's node:http never derives keep-alive from the request, so a stream-ended response delivers its
|
|
588
|
+
* full body and then leaves the client attached until its own timeout (#2210). Ending the *request's*
|
|
589
|
+
* socket is the only remedy that works there, and it is graceful, so nothing is truncated (measured
|
|
590
|
+
* on plain TCP and TLS). DESIGN.md carries the measurements and the alternatives that don't work.
|
|
591
|
+
*/
|
|
592
|
+
function endConnectionIfClientExpectsClose(nodeResponse: any) {
|
|
593
|
+
if (!isBun) return;
|
|
594
|
+
const nodeRequest = nodeResponse.req;
|
|
595
|
+
if (nodeRequest?.httpVersionMajor !== 1) return;
|
|
596
|
+
const socket = nodeRequest.socket;
|
|
597
|
+
// a peer that RSTs right after the terminal chunk can leave this callback holding a destroyed
|
|
598
|
+
// socket; end()ing it would throw from inside stream internals, i.e. an uncaughtException
|
|
599
|
+
if (!socket || socket.destroyed) return;
|
|
600
|
+
const connection = nodeRequest.headers?.connection;
|
|
601
|
+
const tokens = typeof connection === 'string' ? connection.split(',') : [];
|
|
602
|
+
if (hasConnectionToken(tokens, 'close')) {
|
|
603
|
+
socket.end();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
// HTTP/1.0 persistence needs an explicit keep-alive AND a length to read to, so a 1.0 response
|
|
607
|
+
// that got no Content-Length is delimited by the close — the same line Node draws
|
|
608
|
+
if (
|
|
609
|
+
nodeRequest.httpVersionMinor === 0 &&
|
|
610
|
+
!(hasConnectionToken(tokens, 'keep-alive') && nodeResponse.hasHeader?.('content-length'))
|
|
611
|
+
)
|
|
612
|
+
socket.end();
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
function hasConnectionToken(tokens: string[], wanted: string) {
|
|
616
|
+
for (const token of tokens) {
|
|
617
|
+
if (token.trim().toLowerCase() === wanted) return true;
|
|
618
|
+
}
|
|
619
|
+
return false;
|
|
620
|
+
}
|
|
621
|
+
|
|
550
622
|
function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
551
623
|
const { mtls: isMtls, usageType } = options || {};
|
|
552
624
|
const isOperationsServer = usageType === 'operations-api';
|
|
@@ -642,9 +714,7 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
642
714
|
// This means the HDB stack didn't handle the request, and we can then cascade the request
|
|
643
715
|
// to the server-level handler, forming the bridge to the slower legacy fastify framework that expects
|
|
644
716
|
// to interact with a node HTTP server object.
|
|
645
|
-
|
|
646
|
-
nodeResponse.setHeader(headerPair[0], headerPair[1]);
|
|
647
|
-
}
|
|
717
|
+
bridgeChainHeadersToNodeResponse(response.headers, nodeResponse);
|
|
648
718
|
nodeRequest.baseRequest = request;
|
|
649
719
|
nodeResponse.baseResponse = response;
|
|
650
720
|
return httpServers[port].emit('unhandled', nodeRequest, nodeResponse);
|
|
@@ -834,7 +904,10 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
834
904
|
if (secure && env.get(terms.CONFIG_PARAMS.TLS_UNIXDOMAINSOCKETS)) {
|
|
835
905
|
const socketsDir = join(env.getHdbBasePath(), 'sockets');
|
|
836
906
|
mkdirSync(socketsDir, { recursive: true });
|
|
837
|
-
const
|
|
907
|
+
const isolatedApplication = thisThreadsIsolatedApplication();
|
|
908
|
+
const socketName = isolatedApplication
|
|
909
|
+
? applicationSocketName(isolatedApplication, port)
|
|
910
|
+
: `${getWorkerIndex()}-${port}`;
|
|
838
911
|
const udsPath = join(socketsDir, `${socketName}.sock`);
|
|
839
912
|
const yamlPath = join(socketsDir, `${socketName}.yaml`);
|
|
840
913
|
|
|
@@ -947,7 +1020,7 @@ function getHTTPServer(port: number, secure: boolean, options: ServerOptions) {
|
|
|
947
1020
|
* and a Fastify fallback is registered for the port, it delegates via inject() (see injectToFastify),
|
|
948
1021
|
* mirroring the Bun path — so legacy Fastify routes work behind uWS too.
|
|
949
1022
|
*/
|
|
950
|
-
function makeUwsHandler(port: number | string, isOperationsServer: boolean, requestQueueLimit?: number) {
|
|
1023
|
+
export function makeUwsHandler(port: number | string, isOperationsServer: boolean, requestQueueLimit?: number) {
|
|
951
1024
|
// Build a fresh response descriptor rather than mutating what the chain returned: a handler may
|
|
952
1025
|
// return a WHATWG `Response` (read-only `status`/`body` accessors), which the Bun path also never
|
|
953
1026
|
// mutates. `headers` is normalized in place the same way the Bun path does.
|
|
@@ -983,6 +1056,7 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
|
|
|
983
1056
|
if (Array.isArray(v)) respHeaders.set(k, k.toLowerCase() === 'set-cookie' ? v : v.join(', '));
|
|
984
1057
|
else respHeaders.set(k, String(v));
|
|
985
1058
|
}
|
|
1059
|
+
mergeChainHeadersIntoFallback(headers, respHeaders);
|
|
986
1060
|
if (universalHeaders.length > 0) applyUniversalHeaders(respHeaders);
|
|
987
1061
|
logHttpRequest(request, injectResult.statusCode, requestId, performance.now() - startTime);
|
|
988
1062
|
const responseStream = injectResult.stream();
|
|
@@ -1006,6 +1080,7 @@ function makeUwsHandler(port: number | string, isOperationsServer: boolean, requ
|
|
|
1006
1080
|
}
|
|
1007
1081
|
logHttpRequest(request, 404, requestId, performance.now() - startTime);
|
|
1008
1082
|
const notFoundHeaders = new Headers({ 'content-type': 'text/plain' });
|
|
1083
|
+
mergeChainHeadersIntoFallback(headers, notFoundHeaders);
|
|
1009
1084
|
if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
|
|
1010
1085
|
return { status: 404, headers: notFoundHeaders, body: 'Not found\n' };
|
|
1011
1086
|
}
|
|
@@ -1180,10 +1255,11 @@ function getBunHTTPServer(port: number, secure: boolean, options: ServerOptions)
|
|
|
1180
1255
|
// Delegate to the fallback server (e.g. Fastify) via node:http compatibility.
|
|
1181
1256
|
// We create a Node-compatible IncomingMessage/ServerResponse and emit 'request'
|
|
1182
1257
|
// on the fallback server, then capture the response.
|
|
1183
|
-
return await bunDelegateToNodeServer(fallbackServer, webRequest, request);
|
|
1258
|
+
return await bunDelegateToNodeServer(fallbackServer, webRequest, request, response.headers);
|
|
1184
1259
|
}
|
|
1185
1260
|
logHttpRequest(request, 404, requestId, performance.now() - startTime);
|
|
1186
1261
|
const notFoundHeaders = new globalThis.Headers();
|
|
1262
|
+
mergeChainHeadersIntoFallback(response.headers, notFoundHeaders);
|
|
1187
1263
|
if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
|
|
1188
1264
|
return new Response('Not found\n', { status: 404, headers: notFoundHeaders });
|
|
1189
1265
|
}
|
|
@@ -1367,6 +1443,10 @@ let fastifyInstances: Record<string | number, any> = {};
|
|
|
1367
1443
|
export function registerFastifyInstance(port: string | number, instance: any) {
|
|
1368
1444
|
fastifyInstances[port] = instance;
|
|
1369
1445
|
}
|
|
1446
|
+
|
|
1447
|
+
export function registerFallbackServer(port: string | number, listener: any) {
|
|
1448
|
+
fallbackServers[port] = listener;
|
|
1449
|
+
}
|
|
1370
1450
|
const INTERNAL_USER_HEADER = 'x-harper-internal-pre-auth-user';
|
|
1371
1451
|
|
|
1372
1452
|
/**
|
|
@@ -1397,10 +1477,11 @@ function injectToFastify(
|
|
|
1397
1477
|
return fastify.inject({ method: req.method, url: req.url, headers, payload: req.body, payloadAsStream: true });
|
|
1398
1478
|
}
|
|
1399
1479
|
|
|
1400
|
-
async function bunDelegateToNodeServer(
|
|
1480
|
+
export async function bunDelegateToNodeServer(
|
|
1401
1481
|
nodeServer: any,
|
|
1402
1482
|
webRequest: globalThis.Request,
|
|
1403
|
-
bunRequest?: any
|
|
1483
|
+
bunRequest?: any,
|
|
1484
|
+
chainHeaders?: any
|
|
1404
1485
|
): Promise<Response> {
|
|
1405
1486
|
// Check if there's a Fastify instance registered for this port (preferred path)
|
|
1406
1487
|
for (const port in fallbackServers) {
|
|
@@ -1421,13 +1502,19 @@ async function bunDelegateToNodeServer(
|
|
|
1421
1502
|
});
|
|
1422
1503
|
const webHeaders = new globalThis.Headers();
|
|
1423
1504
|
for (const [k, v] of Object.entries(injectResult.headers)) {
|
|
1424
|
-
if (v
|
|
1505
|
+
if (v == null) continue;
|
|
1506
|
+
if (Array.isArray(v)) {
|
|
1507
|
+
if (k.toLowerCase() === 'set-cookie') {
|
|
1508
|
+
for (const single of v) webHeaders.append(k, String(single));
|
|
1509
|
+
} else webHeaders.set(k, v.join(', '));
|
|
1510
|
+
} else webHeaders.set(k, String(v));
|
|
1425
1511
|
}
|
|
1426
1512
|
// Propagate Connection: close so Bun closes the TCP connection after this response,
|
|
1427
1513
|
// preventing stale keep-alive sockets from causing silent hangs on subsequent requests.
|
|
1428
1514
|
if (webRequest.headers.get('connection')?.toLowerCase() === 'close') {
|
|
1429
1515
|
webHeaders.set('connection', 'close');
|
|
1430
1516
|
}
|
|
1517
|
+
mergeChainHeadersIntoFallback(chainHeaders, webHeaders);
|
|
1431
1518
|
if (universalHeaders.length > 0) applyUniversalHeaders(webHeaders);
|
|
1432
1519
|
const responseStream = injectResult.stream();
|
|
1433
1520
|
// Event-stream responses (MCP SSE) must reach the client incrementally — return
|
|
@@ -1462,6 +1549,7 @@ async function bunDelegateToNodeServer(
|
|
|
1462
1549
|
}
|
|
1463
1550
|
// No Fastify instance found — return 404
|
|
1464
1551
|
const notFoundHeaders = new globalThis.Headers();
|
|
1552
|
+
mergeChainHeadersIntoFallback(chainHeaders, notFoundHeaders);
|
|
1465
1553
|
if (universalHeaders.length > 0) applyUniversalHeaders(notFoundHeaders);
|
|
1466
1554
|
return new Response('Not found\n', { status: 404, headers: notFoundHeaders });
|
|
1467
1555
|
}
|
|
@@ -1470,14 +1558,39 @@ type SerializedRoute = { host?: string; urlPath?: string; order: string[] };
|
|
|
1470
1558
|
// Resolved order captured at chain-build time, keyed identically to httpChain/upgradeChains/
|
|
1471
1559
|
// websocketChains (kind → port → routes). Reporting the stored build-time order rather than
|
|
1472
1560
|
// recomputing from current responders guarantees get_status matches the callback chain actually
|
|
1473
|
-
// serving that port
|
|
1474
|
-
//
|
|
1561
|
+
// serving that port: buildChains() writes both in the same pass, so a description can never
|
|
1562
|
+
// describe an order the port isn't running (#1573).
|
|
1475
1563
|
const resolvedChainDescriptions: Record<string, Record<string, SerializedRoute[]>> = {
|
|
1476
1564
|
http: {},
|
|
1477
1565
|
upgrade: {},
|
|
1478
1566
|
websocket: {},
|
|
1479
1567
|
};
|
|
1480
1568
|
|
|
1569
|
+
// Preserve the original port type because route selection uses strict equality.
|
|
1570
|
+
const builtChainPorts: Record<string, Map<string, number | string>> = {
|
|
1571
|
+
http: new Map(),
|
|
1572
|
+
upgrade: new Map(),
|
|
1573
|
+
websocket: new Map(),
|
|
1574
|
+
};
|
|
1575
|
+
|
|
1576
|
+
// A late registration on 'all' must rebuild every already-bound concrete port (#2418).
|
|
1577
|
+
function buildChains(
|
|
1578
|
+
chains: Record<string, Function>,
|
|
1579
|
+
listeners: HttpEntry[],
|
|
1580
|
+
port: number | string,
|
|
1581
|
+
requestArgIndex: number = 0,
|
|
1582
|
+
kind: string = 'http'
|
|
1583
|
+
) {
|
|
1584
|
+
builtChainPorts[kind].set(String(port), port);
|
|
1585
|
+
if (port !== 'all') {
|
|
1586
|
+
chains[port] = makeCallbackChain(listeners, port, requestArgIndex, kind);
|
|
1587
|
+
return;
|
|
1588
|
+
}
|
|
1589
|
+
for (const builtPort of builtChainPorts[kind].values()) {
|
|
1590
|
+
chains[builtPort] = makeCallbackChain(listeners, builtPort, requestArgIndex, kind);
|
|
1591
|
+
}
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1481
1594
|
function makeCallbackChain(
|
|
1482
1595
|
responders: typeof httpResponders,
|
|
1483
1596
|
portNum: number | string,
|
|
@@ -1603,7 +1716,7 @@ function onUpgrade(listener: UpgradeListener, options: UpgradeOptions) {
|
|
|
1603
1716
|
host: options?.host || undefined,
|
|
1604
1717
|
};
|
|
1605
1718
|
upgradeListeners[options?.runFirst ? 'unshift' : 'push'](entry);
|
|
1606
|
-
upgradeChains
|
|
1719
|
+
buildChains(upgradeChains, upgradeListeners, port, 0, 'upgrade');
|
|
1607
1720
|
}
|
|
1608
1721
|
}
|
|
1609
1722
|
|
|
@@ -1742,10 +1855,10 @@ function onWebSocket(listener: (ws: WebSocket) => void, options: OnWebSocketOpti
|
|
|
1742
1855
|
host: options?.host || undefined,
|
|
1743
1856
|
};
|
|
1744
1857
|
websocketListeners[options?.runFirst ? 'unshift' : 'push'](wsEntry);
|
|
1745
|
-
websocketChains
|
|
1858
|
+
buildChains(websocketChains, websocketListeners, port, 1, 'websocket');
|
|
1746
1859
|
|
|
1747
1860
|
// mqtt doesn't invoke the http handler so this needs to be here to load up the http chains.
|
|
1748
|
-
httpChain
|
|
1861
|
+
buildChains(httpChain, httpResponders, port);
|
|
1749
1862
|
}
|
|
1750
1863
|
|
|
1751
1864
|
return servers;
|
|
@@ -12,7 +12,7 @@ const harperBridge =
|
|
|
12
12
|
require('../../dataLayer/harperBridge/harperBridge.ts');
|
|
13
13
|
const process = require('process');
|
|
14
14
|
const { isMainThread, threadId, workerData } = require('node:worker_threads');
|
|
15
|
-
const { resetDatabases, closeDatabase } = require('../../resources/databases.ts');
|
|
15
|
+
const { resetDatabases, closeDatabase, reloadBranchAt } = require('../../resources/databases.ts');
|
|
16
16
|
|
|
17
17
|
/**
|
|
18
18
|
* This object/functions are passed to the ITC client instance and dynamically added as event handlers.
|
|
@@ -76,6 +76,13 @@ schemaHandler.addListener = function (listener) {
|
|
|
76
76
|
*/
|
|
77
77
|
async function syncSchemaMetadata(msg) {
|
|
78
78
|
try {
|
|
79
|
+
// A change to a scope-private branch is not a change to any database in the global map, so the
|
|
80
|
+
// rescan below has nothing to find; a thread holding that branch open reloads it instead.
|
|
81
|
+
if (msg.branchPath) {
|
|
82
|
+
// No write barrier here: the symbol-keyed put below has never been one (harper#2522).
|
|
83
|
+
reloadBranchAt(msg.branchPath);
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
79
86
|
// TODO: Eventually should indicate which database/table changed so we don't have to scan everything
|
|
80
87
|
let databases = resetDatabases();
|
|
81
88
|
if (msg.table && msg.database)
|
|
@@ -9,6 +9,8 @@ import harperLogger from '../../utility/logging/harper_logger.ts';
|
|
|
9
9
|
import * as globalSchema from '../../utility/globalSchema.ts';
|
|
10
10
|
import * as user from '../../security/user.ts';
|
|
11
11
|
import * as serverUtils from '../serverHelpers/serverUtilities.ts';
|
|
12
|
+
import { runWithDispatchedOperation } from '../serverHelpers/operationAuthorizationState.ts';
|
|
13
|
+
import { stripSuppliedParsedSqlObject } from '../serverHelpers/requestSanitization.ts';
|
|
12
14
|
import moment from 'moment';
|
|
13
15
|
import * as jobs from './jobs.ts';
|
|
14
16
|
import { cloneDeep } from 'lodash';
|
|
@@ -54,12 +56,16 @@ const JOB_ID = JOB_NAME.substring(4);
|
|
|
54
56
|
throw new Error('Did not find job request in hdb_job table, unable to proceed');
|
|
55
57
|
}
|
|
56
58
|
request = cloneDeep(request);
|
|
59
|
+
// The worker re-enters from the persisted row rather than re-dispatching, so a row queued before
|
|
60
|
+
// the dispatch-time strip existed — or written directly — is sanitized here.
|
|
61
|
+
stripSuppliedParsedSqlObject(request);
|
|
57
62
|
|
|
58
63
|
const operation = serverUtils.getOperationFunction(request);
|
|
59
64
|
harperLogger.trace('Running operation:', request.operation, 'for job', JOB_ID);
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
const results = await runWithDispatchedOperation(request.operation, () =>
|
|
67
|
+
operation.job_operation_function(request)
|
|
68
|
+
);
|
|
63
69
|
harperLogger.trace('Result from job:', JOB_ID, results);
|
|
64
70
|
|
|
65
71
|
jobObj.status = hdbTerms.JOB_STATUS_ENUM.COMPLETE;
|
package/server/jobs/jobs.ts
CHANGED
|
@@ -11,6 +11,7 @@ import * as search from '../../dataLayer/search.ts';
|
|
|
11
11
|
import Search_Object from '../../dataLayer/SearchObject.ts';
|
|
12
12
|
import searchByHashObj from '../../dataLayer/SearchByHashObject.ts';
|
|
13
13
|
import SQL_Search_Object from '../../dataLayer/SqlSearchObject.ts';
|
|
14
|
+
import { runWithOperationAuthorizationBypass } from '../serverHelpers/operationAuthorizationState.ts';
|
|
14
15
|
import * as hdbTerms from '../../utility/hdbTerms.ts';
|
|
15
16
|
import JobObject from './JobObject.ts';
|
|
16
17
|
import UpdateObject from '../../dataLayer/UpdateObject.ts';
|
|
@@ -249,7 +250,9 @@ export async function getJobsInDateRange(jsonBody: any) {
|
|
|
249
250
|
const hdbSql = require('../../sqlTranslator/index');
|
|
250
251
|
pSqlEvaluate = promisify(hdbSql.evaluateSQL);
|
|
251
252
|
}
|
|
252
|
-
|
|
253
|
+
// Harper's own statement, not the caller's. A carrier is not equivalent here — see DESIGN.md.
|
|
254
|
+
// Wraps only this statement: a later caller-dependent one must not inherit the bypass.
|
|
255
|
+
return await runWithOperationAuthorizationBypass(true, () => pSqlEvaluate(sqlSearchObj));
|
|
253
256
|
} catch (e) {
|
|
254
257
|
log.error(
|
|
255
258
|
`there was a problem searching for jobs from date ${jsonBody.from_date} to date ${jsonBody.to_date} ${e}`
|
|
@@ -23,8 +23,24 @@ interface LiveSubscription {
|
|
|
23
23
|
authExpiresAt?: number;
|
|
24
24
|
/** Returns true if the principal is still authorized for this subscription. */
|
|
25
25
|
recheck: () => Promise<boolean>;
|
|
26
|
-
/** Stop delivery and tear down
|
|
27
|
-
terminate: () => void
|
|
26
|
+
/** Stop delivery and tear down. May be async (e.g. a shared-feed refcount release). */
|
|
27
|
+
terminate: () => void | Promise<void>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function errorMessage(error: unknown): string {
|
|
31
|
+
try {
|
|
32
|
+
return error instanceof Error ? error.message : String(error);
|
|
33
|
+
} catch {
|
|
34
|
+
return '<error message unavailable>';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function safeLog(log: ((message: string) => void) | undefined, message: string): void {
|
|
39
|
+
try {
|
|
40
|
+
log?.(message);
|
|
41
|
+
} catch {
|
|
42
|
+
/* a broken logger must not turn a contained failure into a new one */
|
|
43
|
+
}
|
|
28
44
|
}
|
|
29
45
|
|
|
30
46
|
const registry = new Set<LiveSubscription>();
|
|
@@ -32,9 +48,15 @@ let sweepTimer: any = null;
|
|
|
32
48
|
let itcListenerInstalled = false;
|
|
33
49
|
let sweeping = false;
|
|
34
50
|
|
|
51
|
+
const NOOP_HANDLE = { unregister: () => {} };
|
|
52
|
+
|
|
53
|
+
function triggerSweep(): void {
|
|
54
|
+
void sweep().catch((error) => safeLog(hdbLogger.error, `liveSubscriptionAuth: sweep failed: ${errorMessage(error)}`));
|
|
55
|
+
}
|
|
56
|
+
|
|
35
57
|
function ensureStarted(): void {
|
|
36
58
|
if (!sweepTimer) {
|
|
37
|
-
sweepTimer = setInterval(
|
|
59
|
+
sweepTimer = setInterval(triggerSweep, RECHECK_INTERVAL_MS);
|
|
38
60
|
// don't keep the worker alive solely for the recheck timer
|
|
39
61
|
sweepTimer.unref?.();
|
|
40
62
|
}
|
|
@@ -44,7 +66,7 @@ function ensureStarted(): void {
|
|
|
44
66
|
// user/role cache before invoking listeners, so recheck() observes the new permissions.
|
|
45
67
|
const handlers = require('./itc/serverHandlers.js');
|
|
46
68
|
if (handlers?.userHandler?.addListener) {
|
|
47
|
-
handlers.userHandler.addListener(
|
|
69
|
+
handlers.userHandler.addListener(triggerSweep);
|
|
48
70
|
itcListenerInstalled = true;
|
|
49
71
|
}
|
|
50
72
|
} catch (error) {
|
|
@@ -61,32 +83,49 @@ function stopIfIdle(): void {
|
|
|
61
83
|
}
|
|
62
84
|
|
|
63
85
|
/**
|
|
64
|
-
* Register a live subscription for continuous re-authorization.
|
|
65
|
-
*
|
|
86
|
+
* Register a live subscription for continuous re-authorization.
|
|
87
|
+
*
|
|
88
|
+
* Without `revoke`, teardown is end()/close()/emit('close') on `subscription`, and the
|
|
89
|
+
* subscription's own teardown unregisters the entry so callers need not.
|
|
90
|
+
*
|
|
91
|
+
* With `revoke`, that becomes the teardown and `subscription` is never touched — no `end` wrapping,
|
|
92
|
+
* no 'close' listener — because a feed shared by many subscribers must stay revocable per subscriber,
|
|
93
|
+
* so the registry can neither own the shared object nor let every registrant mutate it. A `revoke`
|
|
94
|
+
* caller owns the entry's lifetime: nothing detects a leaked registration, and a forgotten
|
|
95
|
+
* `unregister()` degrades sweep latency for every other tracked subscriber. It also owns teardown
|
|
96
|
+
* recovery — the entry is untracked before `revoke` runs and `revoke` is invoked exactly once, so one
|
|
97
|
+
* that throws, rejects or never settles is logged and never retried. A `recheck` shared across
|
|
98
|
+
* subscribers must not mutate state shared across them: `registerLiveSubscriptionForContext` in
|
|
99
|
+
* resources/Resource.ts mutates `context.user`, which is safe only while each context has exactly
|
|
100
|
+
* one subscriber.
|
|
66
101
|
*/
|
|
67
|
-
export function registerLiveSubscription(
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
|
|
102
|
+
export function registerLiveSubscription(
|
|
103
|
+
opts: {
|
|
104
|
+
username: string;
|
|
105
|
+
authExpiresAt?: number;
|
|
106
|
+
recheck: () => Promise<boolean>;
|
|
107
|
+
} & (
|
|
108
|
+
| { subscription: any; revoke?: undefined }
|
|
109
|
+
// requiring one of the two modes stops a caller that supplies neither from type-checking
|
|
110
|
+
// into a silent no-op registration; a revoke-only registrant may own no subscription object
|
|
111
|
+
| { subscription?: any; revoke: () => void | Promise<void> }
|
|
112
|
+
)
|
|
113
|
+
): { unregister: () => void } {
|
|
114
|
+
const { subscription, username, authExpiresAt, recheck, revoke } = opts;
|
|
115
|
+
if (!revoke && (!subscription || typeof subscription !== 'object' || subscription.closed)) return NOOP_HANDLE;
|
|
75
116
|
|
|
76
117
|
const entry: LiveSubscription = {
|
|
77
118
|
username,
|
|
78
119
|
authExpiresAt,
|
|
79
120
|
recheck,
|
|
80
|
-
terminate:
|
|
81
|
-
|
|
82
|
-
|
|
121
|
+
terminate:
|
|
122
|
+
revoke ??
|
|
123
|
+
(() => {
|
|
124
|
+
// end() removes the subscription from the broadcast loop and closes its iterable queue.
|
|
83
125
|
if (subscription.end) subscription.end();
|
|
84
126
|
else if (subscription.close) subscription.close();
|
|
85
127
|
else subscription.emit?.('close');
|
|
86
|
-
}
|
|
87
|
-
/* ignore */
|
|
88
|
-
}
|
|
89
|
-
},
|
|
128
|
+
}),
|
|
90
129
|
};
|
|
91
130
|
registry.add(entry);
|
|
92
131
|
|
|
@@ -94,51 +133,90 @@ export function registerLiveSubscription(opts: {
|
|
|
94
133
|
registry.delete(entry);
|
|
95
134
|
stopIfIdle();
|
|
96
135
|
};
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
subscription
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
136
|
+
|
|
137
|
+
if (!revoke) {
|
|
138
|
+
// Both transports ultimately call end() on normal teardown (MQTT unsubscribe/disconnect; SSE close
|
|
139
|
+
// is wired to end()); wrap it so a closed stream never leaks a registry entry. Also listen for
|
|
140
|
+
// 'close' to cover any iterable that closes without an end(). Skipped when `revoke` is supplied:
|
|
141
|
+
// the caller owns unregistration, and a subscription shared by many subscribers must not be
|
|
142
|
+
// mutated once per registrant.
|
|
143
|
+
const originalEnd = typeof subscription.end === 'function' ? subscription.end.bind(subscription) : null;
|
|
144
|
+
if (originalEnd) {
|
|
145
|
+
subscription.end = function (...args: any[]) {
|
|
146
|
+
unregister();
|
|
147
|
+
return originalEnd(...args);
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
subscription.on?.('close', unregister);
|
|
106
151
|
}
|
|
107
|
-
subscription.on?.('close', unregister);
|
|
108
152
|
|
|
109
153
|
ensureStarted();
|
|
154
|
+
return { unregister };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** Untrack first: a `terminate` that hangs or fails must not wedge the sweep or be re-entered by a later one. */
|
|
158
|
+
function terminateEntry(
|
|
159
|
+
entry: LiveSubscription,
|
|
160
|
+
reason: string,
|
|
161
|
+
notice: ((message: string) => void) | undefined = hdbLogger.info
|
|
162
|
+
): void {
|
|
163
|
+
registry.delete(entry);
|
|
164
|
+
safeLog(notice, `liveSubscriptionAuth: revoking subscription for ${entry.username} (${reason})`);
|
|
165
|
+
const failed = (error: unknown) =>
|
|
166
|
+
safeLog(
|
|
167
|
+
hdbLogger.error,
|
|
168
|
+
`liveSubscriptionAuth: terminate failed for ${entry.username} (${reason}): ${errorMessage(error)}`
|
|
169
|
+
);
|
|
170
|
+
try {
|
|
171
|
+
// an async terminate's rejection would otherwise surface as an unhandled rejection on the timer's stack
|
|
172
|
+
Promise.resolve(entry.terminate()).catch(failed);
|
|
173
|
+
} catch (error) {
|
|
174
|
+
failed(error);
|
|
175
|
+
}
|
|
110
176
|
}
|
|
111
177
|
|
|
112
178
|
async function sweep(): Promise<void> {
|
|
113
179
|
if (sweeping) return; // a slow recheck must not overlap with the next tick/event
|
|
114
180
|
sweeping = true;
|
|
181
|
+
// the per-subscriber lines are info, which the shipped default (logging.level: warn) drops; one
|
|
182
|
+
// aggregate keeps the pass visible without making a mass role change a warn per subscriber
|
|
183
|
+
const revokedByReason = new Map<string, number>();
|
|
184
|
+
const countRevocation = (reason: string) => revokedByReason.set(reason, (revokedByReason.get(reason) ?? 0) + 1);
|
|
115
185
|
try {
|
|
116
|
-
|
|
117
|
-
for (const entry of registry) {
|
|
186
|
+
// snapshot bounds the pass to entries present at its start; the has() guards cover the rest
|
|
187
|
+
for (const entry of Array.from(registry)) {
|
|
188
|
+
if (!registry.has(entry)) continue;
|
|
118
189
|
try {
|
|
119
|
-
const expired = entry.authExpiresAt != null && now >= entry.authExpiresAt * 1000;
|
|
190
|
+
const expired = entry.authExpiresAt != null && Date.now() >= entry.authExpiresAt * 1000;
|
|
120
191
|
const stillAuthorized = expired ? false : await entry.recheck();
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
entry
|
|
192
|
+
if (!registry.has(entry)) continue;
|
|
193
|
+
if (expired || !stillAuthorized) {
|
|
194
|
+
const reason = expired ? 'token expired' : 'no longer authorized';
|
|
195
|
+
terminateEntry(entry, reason);
|
|
196
|
+
countRevocation(reason);
|
|
125
197
|
}
|
|
126
198
|
} catch (error) {
|
|
127
199
|
// fail closed: if authorization can't be confirmed, revoke
|
|
128
|
-
registry.
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
} catch {
|
|
132
|
-
/* ignore */
|
|
200
|
+
if (registry.has(entry)) {
|
|
201
|
+
terminateEntry(entry, `recheck error: ${errorMessage(error)}`, hdbLogger.warn);
|
|
202
|
+
countRevocation('recheck error');
|
|
133
203
|
}
|
|
134
|
-
hdbLogger.warn?.(
|
|
135
|
-
`liveSubscriptionAuth: revoked subscription for ${entry.username} after recheck error: ${(error as Error).message}`
|
|
136
|
-
);
|
|
137
204
|
}
|
|
138
205
|
}
|
|
139
206
|
} finally {
|
|
140
207
|
sweeping = false;
|
|
141
208
|
stopIfIdle();
|
|
209
|
+
if (revokedByReason.size > 0) {
|
|
210
|
+
let total = 0;
|
|
211
|
+
for (const count of revokedByReason.values()) total += count;
|
|
212
|
+
const breakdown = Array.from(revokedByReason, ([reason, count]) => `${reason}: ${count}`).join(', ');
|
|
213
|
+
safeLog(
|
|
214
|
+
hdbLogger.warn,
|
|
215
|
+
// "revoking", like the per-subscriber line: teardown is dispatched, not awaited, and a
|
|
216
|
+
// failure surfaces on its own error line
|
|
217
|
+
`liveSubscriptionAuth: revoking ${total} live subscription${total === 1 ? '' : 's'} (${breakdown})`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
142
220
|
}
|
|
143
221
|
}
|
|
144
222
|
|
|
@@ -146,3 +224,8 @@ async function sweep(): Promise<void> {
|
|
|
146
224
|
export function _liveSubscriptionCount(): number {
|
|
147
225
|
return registry.size;
|
|
148
226
|
}
|
|
227
|
+
|
|
228
|
+
/** Test-only: run a sweep synchronously, bypassing the interval/ITC triggers. */
|
|
229
|
+
export function _sweepNow(): Promise<void> {
|
|
230
|
+
return sweep();
|
|
231
|
+
}
|