@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
|
@@ -25,13 +25,26 @@
|
|
|
25
25
|
import type { Logger } from '../utility/logging/logger.ts';
|
|
26
26
|
import * as fs from 'fs-extra';
|
|
27
27
|
import * as path from 'node:path';
|
|
28
|
+
import { isMainThread } from 'node:worker_threads';
|
|
28
29
|
import * as crypto from 'node:crypto';
|
|
29
30
|
import { cloneDeep } from 'lodash';
|
|
30
31
|
import { getBackupDirPath } from './configHelpers.ts';
|
|
31
|
-
import { atomicWriteFile } from './configUtils.ts';
|
|
32
|
+
import { atomicWriteFile, renameWithRetry } from './configUtils.ts';
|
|
32
33
|
import * as hdbTerms from '../utility/hdbTerms.ts';
|
|
33
34
|
|
|
34
35
|
const STATE_FILE_NAME = '.harper-config-state.json';
|
|
36
|
+
// Staged beside the confirmed state while a config-file write is in flight, then renamed over it.
|
|
37
|
+
// Per-process, because every CLI invocation and worker runs this and one shared name would let one
|
|
38
|
+
// clear another's in-flight commit. See DESIGN.md, boot-path config persistence.
|
|
39
|
+
const PENDING_STATE_PREFIX = '.harper-config-state.pending.';
|
|
40
|
+
const PENDING_STATE_SUFFIX = '.json';
|
|
41
|
+
const pendingStateFileName = () => `${PENDING_STATE_PREFIX}${process.pid}${PENDING_STATE_SUFFIX}`;
|
|
42
|
+
// Recovery from a recycled pid only has to be eventual, and deleting a slow-but-live writer's
|
|
43
|
+
// sidecar is the worse error - it strands that writer's config file against an unpromoted state. So
|
|
44
|
+
// the age-out is far longer than a commit (three synchronous steps) could ever legitimately take:
|
|
45
|
+
// long enough that a stalled writer or clock skew between containers sharing a volume cannot reach
|
|
46
|
+
// it, short enough that leaked wreckage does not suspend drift detection indefinitely.
|
|
47
|
+
const PENDING_STATE_STALE_MS = 60 * 60 * 1000;
|
|
35
48
|
|
|
36
49
|
/**
|
|
37
50
|
* Get logger instance with tag - lazy loaded to avoid circular dependencies
|
|
@@ -546,15 +559,7 @@ function parseConfigEnvVar(envVarValue: string | undefined, envVarName: string):
|
|
|
546
559
|
function loadConfigState(rootPath: string): ConfigState {
|
|
547
560
|
const statePath = path.join(getBackupDirPath(rootPath), STATE_FILE_NAME);
|
|
548
561
|
|
|
549
|
-
if (!fs.existsSync(statePath))
|
|
550
|
-
return {
|
|
551
|
-
version: '1.0',
|
|
552
|
-
sources: {},
|
|
553
|
-
originalValues: {},
|
|
554
|
-
emptyScopeOriginals: {},
|
|
555
|
-
snapshots: {},
|
|
556
|
-
};
|
|
557
|
-
}
|
|
562
|
+
if (!fs.existsSync(statePath)) return freshConfigState();
|
|
558
563
|
|
|
559
564
|
try {
|
|
560
565
|
const state = fs.readJsonSync(statePath) as ConfigState;
|
|
@@ -573,20 +578,85 @@ function loadConfigState(rootPath: string): ConfigState {
|
|
|
573
578
|
// If state file is corrupted, start fresh
|
|
574
579
|
const logger = getLogger();
|
|
575
580
|
logger.warn(`Failed to load config state file, starting fresh: ${(error as Error).message}`);
|
|
576
|
-
return
|
|
577
|
-
version: '1.0',
|
|
578
|
-
sources: {},
|
|
579
|
-
originalValues: {},
|
|
580
|
-
emptyScopeOriginals: {},
|
|
581
|
-
snapshots: {},
|
|
582
|
-
};
|
|
581
|
+
return freshConfigState();
|
|
583
582
|
}
|
|
584
583
|
}
|
|
585
584
|
|
|
585
|
+
/**
|
|
586
|
+
* Clear a sidecar left by an interrupted commit, reporting whether one was there. The caller skips
|
|
587
|
+
* drift detection for that boot: it cannot tell a manual user edit from the write that was in
|
|
588
|
+
* flight, and calling it an edit hands those paths to 'user' for good.
|
|
589
|
+
*/
|
|
590
|
+
function takeInterruptedCommit(rootPath: string): boolean {
|
|
591
|
+
const backupDir = getBackupDirPath(rootPath);
|
|
592
|
+
let entries: string[];
|
|
593
|
+
try {
|
|
594
|
+
entries = fs.readdirSync(backupDir);
|
|
595
|
+
} catch {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
let interrupted = false;
|
|
599
|
+
for (const entry of entries) {
|
|
600
|
+
if (!entry.startsWith(PENDING_STATE_PREFIX) || !entry.endsWith(PENDING_STATE_SUFFIX)) continue;
|
|
601
|
+
const pid = Number(entry.slice(PENDING_STATE_PREFIX.length, -PENDING_STATE_SUFFIX.length));
|
|
602
|
+
const entryPath = path.join(backupDir, entry);
|
|
603
|
+
if (pid !== process.pid && isProcessAlive(pid) && !isStale(entryPath)) {
|
|
604
|
+
// A live owner is mid-commit, so its sidecar is not wreckage to clear - but the pair it is
|
|
605
|
+
// halfway through is no more comparable than an interrupted one, so drift detection is off
|
|
606
|
+
// for this boot either way.
|
|
607
|
+
interrupted = true;
|
|
608
|
+
continue;
|
|
609
|
+
}
|
|
610
|
+
try {
|
|
611
|
+
fs.removeSync(entryPath);
|
|
612
|
+
} catch (error) {
|
|
613
|
+
// Keep drift detection on rather than disabling it every boot over a sidecar we cannot clear
|
|
614
|
+
getLogger().warn(`Could not remove an interrupted env config commit (${entry}): ${(error as Error).message}`);
|
|
615
|
+
continue;
|
|
616
|
+
}
|
|
617
|
+
interrupted = true;
|
|
618
|
+
}
|
|
619
|
+
if (interrupted) getLogger().warn('An env config commit was interrupted; skipping drift detection for this boot');
|
|
620
|
+
return interrupted;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function isStale(entryPath: string): boolean {
|
|
624
|
+
try {
|
|
625
|
+
return Date.now() - fs.statSync(entryPath).mtimeMs > PENDING_STATE_STALE_MS;
|
|
626
|
+
} catch {
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
function isProcessAlive(pid: number): boolean {
|
|
632
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
633
|
+
try {
|
|
634
|
+
process.kill(pid, 0);
|
|
635
|
+
return true;
|
|
636
|
+
} catch (error) {
|
|
637
|
+
return (error as NodeJS.ErrnoException).code === 'EPERM';
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function freshConfigState(): ConfigState {
|
|
642
|
+
return {
|
|
643
|
+
version: '1.0',
|
|
644
|
+
sources: {},
|
|
645
|
+
originalValues: {},
|
|
646
|
+
emptyScopeOriginals: {},
|
|
647
|
+
snapshots: {},
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
|
|
586
651
|
/**
|
|
587
652
|
* Save configuration state to file
|
|
588
653
|
*/
|
|
589
|
-
function
|
|
654
|
+
function serializeConfigState(state: ConfigState): string {
|
|
655
|
+
return JSON.stringify(state, null, 2) + '\n';
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
// Returns true when the file was rewritten, false when it already held this state.
|
|
659
|
+
function saveConfigState(rootPath: string, state: ConfigState): boolean {
|
|
590
660
|
const backupDir = getBackupDirPath(rootPath);
|
|
591
661
|
const statePath = path.join(backupDir, STATE_FILE_NAME);
|
|
592
662
|
|
|
@@ -595,7 +665,49 @@ function saveConfigState(rootPath: string, state: ConfigState): void {
|
|
|
595
665
|
|
|
596
666
|
// Atomic write: a torn state file resets to fresh on the next load, losing every
|
|
597
667
|
// restoration record — the blast radius is user config-file content
|
|
598
|
-
atomicWriteFile(statePath,
|
|
668
|
+
return atomicWriteFile(statePath, serializeConfigState(state), { skipIfUnchanged: true });
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function configStateMatchesDisk(rootPath: string, state: ConfigState): boolean {
|
|
672
|
+
try {
|
|
673
|
+
const statePath = path.join(getBackupDirPath(rootPath), STATE_FILE_NAME);
|
|
674
|
+
return fs.readFileSync(statePath, 'utf8') === serializeConfigState(state);
|
|
675
|
+
} catch {
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
function stageConfigState(rootPath: string, state: ConfigState): boolean {
|
|
681
|
+
const backupDir = getBackupDirPath(rootPath);
|
|
682
|
+
fs.ensureDirSync(backupDir);
|
|
683
|
+
return atomicWriteFile(path.join(backupDir, pendingStateFileName()), serializeConfigState(state));
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
// A rename, so an exhausted volume cannot refuse it and leave the config file described by nothing.
|
|
687
|
+
|
|
688
|
+
export function commitStagedConfigState(rootPath: string): boolean {
|
|
689
|
+
const backupDir = getBackupDirPath(rootPath);
|
|
690
|
+
const pendingPath = path.join(backupDir, pendingStateFileName());
|
|
691
|
+
try {
|
|
692
|
+
renameWithRetry(pendingPath, path.join(backupDir, STATE_FILE_NAME));
|
|
693
|
+
return true;
|
|
694
|
+
} catch (error) {
|
|
695
|
+
// Loud: the config file is already on disk, so the confirmed state now describes values it no
|
|
696
|
+
// longer has, and the next boot reads that difference as a manual user edit.
|
|
697
|
+
getLogger().error(`Could not promote the staged env config state at ${pendingPath}: ${(error as Error).message}`);
|
|
698
|
+
return false;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
// Drops the staged state for a config-file write that did not happen; the confirmed record stays.
|
|
703
|
+
|
|
704
|
+
export function discardConfigState(rootPath: string): void {
|
|
705
|
+
const pendingPath = path.join(getBackupDirPath(rootPath), pendingStateFileName());
|
|
706
|
+
try {
|
|
707
|
+
fs.removeSync(pendingPath);
|
|
708
|
+
} catch (error) {
|
|
709
|
+
getLogger().warn(`Could not remove the staged env config state at ${pendingPath}: ${(error as Error).message}`);
|
|
710
|
+
}
|
|
599
711
|
}
|
|
600
712
|
|
|
601
713
|
/**
|
|
@@ -957,20 +1069,49 @@ export function applyRuntimeEnvConfig(
|
|
|
957
1069
|
rootPath: string,
|
|
958
1070
|
options: { isInstall?: boolean } = {}
|
|
959
1071
|
): ConfigObject {
|
|
1072
|
+
const { config, commitState } = prepareRuntimeEnvConfig(fileConfig, rootPath, options);
|
|
1073
|
+
commitState();
|
|
1074
|
+
return config;
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
/**
|
|
1078
|
+
* Apply the env layers and hand the state writes back to the caller, so a caller that also persists
|
|
1079
|
+
* the merged config file can commit the pair as a unit (#847): saveState() stages the new state
|
|
1080
|
+
* beside the confirmed one, confirmConfigWritten() renames it over the confirmed one after the
|
|
1081
|
+
* config file lands, and discardConfigState() drops the staged copy if that write never happens.
|
|
1082
|
+
*/
|
|
1083
|
+
export function prepareRuntimeEnvConfig(
|
|
1084
|
+
fileConfig: ConfigObject,
|
|
1085
|
+
rootPath: string,
|
|
1086
|
+
options: { isInstall?: boolean } = {}
|
|
1087
|
+
): {
|
|
1088
|
+
config: ConfigObject;
|
|
1089
|
+
saveState: () => boolean;
|
|
1090
|
+
confirmConfigWritten: () => boolean;
|
|
1091
|
+
commitState: () => boolean;
|
|
1092
|
+
} {
|
|
960
1093
|
const defaultEnvValue = process.env.HARPER_DEFAULT_CONFIG;
|
|
961
1094
|
const configEnvValue = process.env.HARPER_CONFIG;
|
|
962
1095
|
const setEnvValue = process.env.HARPER_SET_CONFIG;
|
|
963
1096
|
|
|
964
|
-
// Load existing state
|
|
1097
|
+
// Load existing state. Only the main thread persists, so only the main thread has wreckage to
|
|
1098
|
+
// clear - and a worker shares its pid, so letting one scan would delete the main thread's
|
|
1099
|
+
// in-flight sidecar as if it were last boot's.
|
|
1100
|
+
const interruptedCommit = isMainThread && takeInterruptedCommit(rootPath);
|
|
965
1101
|
const state = loadConfigState(rootPath);
|
|
966
1102
|
|
|
967
1103
|
// No env vars set and no previous state, nothing to do
|
|
968
1104
|
if (!defaultEnvValue && !configEnvValue && !setEnvValue && Object.keys(state.snapshots).length === 0) {
|
|
969
|
-
return fileConfig;
|
|
1105
|
+
return { config: fileConfig, saveState: () => false, confirmConfigWritten: () => false, commitState: () => false };
|
|
970
1106
|
}
|
|
971
1107
|
|
|
972
|
-
// Detect drift (user manual edits) - only at runtime, not install
|
|
973
|
-
|
|
1108
|
+
// Detect drift (user manual edits) - only at runtime, not install, not on a boot that found an
|
|
1109
|
+
// interrupted commit (where a difference could equally be the write that was in flight), and only
|
|
1110
|
+
// on the main thread: a worker never owns the state, and one re-deriving inside the main thread's
|
|
1111
|
+
// commit window would call its half-written config file a user edit and drop the env-supplied
|
|
1112
|
+
// value for itself alone - serving different config than its siblings, with nothing on disk to
|
|
1113
|
+
// show why.
|
|
1114
|
+
if (!options.isInstall && !interruptedCommit && isMainThread) {
|
|
974
1115
|
const driftedPaths = detectConfigDrift(fileConfig, state);
|
|
975
1116
|
for (const path of driftedPaths) {
|
|
976
1117
|
state.sources[path] = 'user';
|
|
@@ -998,8 +1139,10 @@ export function applyRuntimeEnvConfig(
|
|
|
998
1139
|
processEnvVar(fileConfig, state, 'HARPER_CONFIG', 'HARPER_CONFIG', options);
|
|
999
1140
|
processEnvVar(fileConfig, state, 'HARPER_SET_CONFIG', 'HARPER_SET_CONFIG', options);
|
|
1000
1141
|
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1142
|
+
return {
|
|
1143
|
+
config: fileConfig,
|
|
1144
|
+
saveState: () => (configStateMatchesDisk(rootPath, state) ? false : stageConfigState(rootPath, state)),
|
|
1145
|
+
confirmConfigWritten: () => commitStagedConfigState(rootPath),
|
|
1146
|
+
commitState: () => saveConfigState(rootPath, state),
|
|
1147
|
+
};
|
|
1005
1148
|
}
|
package/config-root.schema.json
CHANGED
|
@@ -519,6 +519,10 @@
|
|
|
519
519
|
"type": "string",
|
|
520
520
|
"description": "Max write queue time before rejecting (e.g. '45s')."
|
|
521
521
|
},
|
|
522
|
+
"longTransactionReportThreshold": {
|
|
523
|
+
"type": ["string", "number"],
|
|
524
|
+
"description": "How old a RocksDB transaction handle must be before it is reported in the log (e.g. '5m'). A handle this old can hold write intents that park other writers' commits and pin a read snapshot. Set to 0 to disable the reporting. Default: '5m'"
|
|
525
|
+
},
|
|
522
526
|
"noReadAhead": { "type": "boolean", "description": "Advise OS to not read ahead. Default: false" },
|
|
523
527
|
"randomAccessFields": {
|
|
524
528
|
"type": "boolean",
|
|
@@ -534,6 +538,31 @@
|
|
|
534
538
|
"oneOf": [{ "type": "string" }, { "type": "array", "items": { "type": "string" } }],
|
|
535
539
|
"description": "Path or array of paths for blob storage. Default: <rootPath>/blobs"
|
|
536
540
|
},
|
|
541
|
+
"blobs": {
|
|
542
|
+
"type": "object",
|
|
543
|
+
"description": "File-backed blob storage settings.",
|
|
544
|
+
"properties": {
|
|
545
|
+
"compression": {
|
|
546
|
+
"type": "object",
|
|
547
|
+
"description": "Opt-in compression for newly written file-backed blobs, keyed by content type: an exact type ('application/json'), a wildcard ('text/*'), or 'default'. Absent means no compression. Each entry is false (never compress the type) or an object { codec, threshold }: codec currently only 'deflate' (the default); threshold is the minimum content size in bytes to compress (default 8192). Matching precedence is exact type, then 'type/*', then 'default'. Types that are already compressed (application/gzip, application/zip, application/zstd, image/*, video/*, audio/*) ship as false and stay uncompressed unless explicitly overridden. Blobs streamed without a known size are not compressed, and a threshold below 8192 only affects streamed writes — buffers under 8KB are stored inline in the record (covered by record-level compression) and never reach blob files. Applies to new writes only; existing blob files are never rewritten.",
|
|
548
|
+
"propertyNames": { "pattern": "^([\\w.+-]+/(\\*|[\\w.+-]+)|default)$" },
|
|
549
|
+
"additionalProperties": {
|
|
550
|
+
"oneOf": [
|
|
551
|
+
{ "const": false },
|
|
552
|
+
{
|
|
553
|
+
"type": "object",
|
|
554
|
+
"properties": {
|
|
555
|
+
"codec": { "const": "deflate" },
|
|
556
|
+
"threshold": { "type": "number", "minimum": 0 }
|
|
557
|
+
},
|
|
558
|
+
"additionalProperties": false
|
|
559
|
+
}
|
|
560
|
+
]
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
},
|
|
564
|
+
"additionalProperties": false
|
|
565
|
+
},
|
|
537
566
|
"blobReadTimeout": {
|
|
538
567
|
"type": "number",
|
|
539
568
|
"description": "Max time (ms) a blob read will wait for an in-progress write to finish before failing with a 503, rather than holding the connection. Default: 20000"
|
package/dataLayer/blobBackup.ts
CHANGED
|
@@ -5,6 +5,12 @@ import { copyFile, link, mkdir, readdir, rename, rm, unlink, writeFile } from 'n
|
|
|
5
5
|
import { dirname, join, relative } from 'node:path';
|
|
6
6
|
import { ClientError } from '../utility/errors/hdbError.ts';
|
|
7
7
|
import logger from '../utility/logging/harper_logger.ts';
|
|
8
|
+
import {
|
|
9
|
+
type BlobCaptureDisposition,
|
|
10
|
+
classifyBlobFileForCapture,
|
|
11
|
+
createCaptureMarker,
|
|
12
|
+
isSystemicIoError,
|
|
13
|
+
} from '../resources/blob.ts';
|
|
8
14
|
|
|
9
15
|
/**
|
|
10
16
|
* Managed-backup snapshotting of a database's file-backed blobs.
|
|
@@ -21,17 +27,17 @@ import logger from '../utility/logging/harper_logger.ts';
|
|
|
21
27
|
* possible (cheap, no extra space on the same filesystem) and copied otherwise (never symlinked, so
|
|
22
28
|
* a snapshot is a standalone set of files that survives independent of the live blob).
|
|
23
29
|
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
30
|
+
* Harper does not freeze blob writes for a backup, and a blob is written in place at its final path,
|
|
31
|
+
* so the walk can meet one that is still growing. Hard-linking that inode would put bytes in the
|
|
32
|
+
* snapshot that keep changing after it was taken. Every entry returned by the walk is therefore
|
|
33
|
+
* classified (`captureBlobFile`): a complete blob is linked, and one that becomes unavailable or is
|
|
34
|
+
* not yet complete is replaced by a marker rather than dropped. This closes the classify-to-capture
|
|
35
|
+
* race and keeps that file id reserved; a file reclaimed before its parent directory is read remains
|
|
36
|
+
* outside the snapshot.
|
|
29
37
|
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
* bytes alive even after the live blob is deleted, and no in-place overwrite can retroactively alter
|
|
34
|
-
* a snapshot.
|
|
38
|
+
* Past that gate, hard-linking is safe against later mutation: each write takes a fresh monotonic
|
|
39
|
+
* file id, and an update, delete, or in-place repair replaces or unlinks the path rather than
|
|
40
|
+
* rewriting the inode.
|
|
35
41
|
*/
|
|
36
42
|
|
|
37
43
|
/** Directory holding all blob snapshots for a backup repository. */
|
|
@@ -46,17 +52,17 @@ export function blobSnapshotDir(backupDir: string, backupId: number): string {
|
|
|
46
52
|
|
|
47
53
|
/**
|
|
48
54
|
* Hard-link `src` to `dest`, falling back to a copy when the two are on different filesystems (or
|
|
49
|
-
* the filesystem does not support additional hard links). Never creates a symlink.
|
|
50
|
-
*
|
|
55
|
+
* the filesystem does not support additional hard links). Never creates a symlink. Returns false
|
|
56
|
+
* when the source vanishes so the caller can substitute a marker for it. `counts.copied` is what a
|
|
57
|
+
* caller on a latency budget needs: the fallback turns a constant-time clone into an O(bytes) one.
|
|
51
58
|
*/
|
|
52
|
-
async function linkOrCopy(src: string, dest: string): Promise<
|
|
59
|
+
async function linkOrCopy(src: string, dest: string, counts?: { copied: number }): Promise<boolean> {
|
|
53
60
|
await mkdir(dirname(dest), { recursive: true });
|
|
54
61
|
try {
|
|
55
62
|
await link(src, dest);
|
|
56
63
|
} catch (error: any) {
|
|
57
64
|
if (error.code === 'ENOENT') {
|
|
58
|
-
|
|
59
|
-
if (!existsSync(src)) return;
|
|
65
|
+
if (!existsSync(src)) return false; // vanished since it was classified
|
|
60
66
|
throw error;
|
|
61
67
|
}
|
|
62
68
|
if (
|
|
@@ -66,24 +72,86 @@ async function linkOrCopy(src: string, dest: string): Promise<void> {
|
|
|
66
72
|
error.code === 'ENOTSUP' ||
|
|
67
73
|
error.code === 'EOPNOTSUPP'
|
|
68
74
|
) {
|
|
69
|
-
|
|
70
|
-
|
|
75
|
+
try {
|
|
76
|
+
await copyFile(src, dest);
|
|
77
|
+
} catch (copyError: any) {
|
|
78
|
+
if (copyError.code === 'ENOENT') return false;
|
|
79
|
+
throw copyError;
|
|
80
|
+
}
|
|
81
|
+
if (counts) counts.copied++;
|
|
82
|
+
return true;
|
|
71
83
|
}
|
|
72
84
|
if (error.code === 'EEXIST') {
|
|
73
85
|
await unlink(dest);
|
|
74
|
-
|
|
75
|
-
return;
|
|
86
|
+
return linkOrCopy(src, dest, counts);
|
|
76
87
|
}
|
|
77
88
|
throw error;
|
|
78
89
|
}
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** What a substituted marker says happened, so a branch clone doesn't report itself as a backup. */
|
|
94
|
+
export interface CaptureMarkerReasons {
|
|
95
|
+
gone: string;
|
|
96
|
+
pending: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const BACKUP_MARKER_REASONS: CaptureMarkerReasons = {
|
|
100
|
+
gone: 'blob was deleted while this backup was being taken',
|
|
101
|
+
pending: 'blob was not yet complete when this backup was taken',
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** Put one blob file into the destination, returning how the entry was captured. */
|
|
105
|
+
async function captureBlobFile(
|
|
106
|
+
srcPath: string,
|
|
107
|
+
destPath: string,
|
|
108
|
+
reasons: CaptureMarkerReasons = BACKUP_MARKER_REASONS,
|
|
109
|
+
counts?: { copied: number }
|
|
110
|
+
): Promise<BlobCaptureDisposition> {
|
|
111
|
+
let disposition: BlobCaptureDisposition;
|
|
112
|
+
try {
|
|
113
|
+
disposition = await classifyBlobFileForCapture(srcPath);
|
|
114
|
+
} catch (error) {
|
|
115
|
+
// This rethrow is unverified: only the membership of SYSTEMIC_IO_ERRORS is tested, because
|
|
116
|
+
// reaching it needs a real host-level fault no test here can produce.
|
|
117
|
+
if (isSystemicIoError(error)) throw error;
|
|
118
|
+
// Fall back to what this walk did before it classified anything. `link()` needs no read permission
|
|
119
|
+
// on the source, so a blob this failed to *read* may still be perfectly capturable; substituting
|
|
120
|
+
// here would turn an unreadable-but-valid root into a backup of nothing but stubs.
|
|
121
|
+
logger.warn(`Could not verify blob ${srcPath} for snapshot; capturing it unverified`, error);
|
|
122
|
+
disposition = 'capture';
|
|
123
|
+
}
|
|
124
|
+
if (disposition === 'skip') return disposition;
|
|
125
|
+
if (disposition === 'capture') {
|
|
126
|
+
if (await linkOrCopy(srcPath, destPath, counts)) return disposition;
|
|
127
|
+
disposition = 'gone';
|
|
128
|
+
}
|
|
129
|
+
await mkdir(dirname(destPath), { recursive: true });
|
|
130
|
+
await writeFile(destPath, createCaptureMarker(disposition, disposition === 'gone' ? reasons.gone : reasons.pending));
|
|
131
|
+
return disposition;
|
|
79
132
|
}
|
|
80
133
|
|
|
81
134
|
/**
|
|
82
135
|
* Recursively copy every file under `srcRoot` into `destRoot` (hard-link-else-copy), preserving the
|
|
83
136
|
* relative directory structure. Missing `srcRoot` is a no-op (a database with no blobs yet).
|
|
137
|
+
*
|
|
138
|
+
* `classify` substitutes a marker for a file that is mid-write or already gone, so the destination
|
|
139
|
+
* fails loudly on that blob rather than carrying a truncated one. It belongs to the capture direction
|
|
140
|
+
* only; restore must replace exactly what the snapshot holds.
|
|
141
|
+
*
|
|
142
|
+
* Also used by branch materialization (harper#644), which clones a base's blob roots into the
|
|
143
|
+
* branch's own so the OS inode refcount does the reference counting. Its walk runs inside a window
|
|
144
|
+
* other threads wait on, which is what `onProgress` is for.
|
|
84
145
|
*/
|
|
85
|
-
async function copyTree(
|
|
86
|
-
|
|
146
|
+
export async function copyTree(
|
|
147
|
+
srcRoot: string,
|
|
148
|
+
destRoot: string,
|
|
149
|
+
classify = false,
|
|
150
|
+
reasons?: CaptureMarkerReasons,
|
|
151
|
+
onProgress?: () => void
|
|
152
|
+
): Promise<{ substituted: number; captured: number; copied: number }> {
|
|
153
|
+
const counts = { substituted: 0, captured: 0, copied: 0 };
|
|
154
|
+
if (!existsSync(srcRoot)) return counts;
|
|
87
155
|
const stack: string[] = [srcRoot];
|
|
88
156
|
while (stack.length > 0) {
|
|
89
157
|
const dir = stack.pop() as string;
|
|
@@ -99,63 +167,103 @@ async function copyTree(srcRoot: string, destRoot: string): Promise<void> {
|
|
|
99
167
|
if (entry.isDirectory()) {
|
|
100
168
|
stack.push(srcPath);
|
|
101
169
|
} else if (entry.isFile()) {
|
|
102
|
-
|
|
170
|
+
const destPath = join(destRoot, relative(srcRoot, srcPath));
|
|
171
|
+
if (classify) {
|
|
172
|
+
const disposition = await captureBlobFile(srcPath, destPath, reasons, counts);
|
|
173
|
+
if (disposition === 'pending' || disposition === 'gone') counts.substituted++;
|
|
174
|
+
else if (disposition === 'capture') counts.captured++;
|
|
175
|
+
} else {
|
|
176
|
+
await linkOrCopy(srcPath, destPath, counts);
|
|
177
|
+
}
|
|
178
|
+
onProgress?.();
|
|
103
179
|
}
|
|
104
180
|
// symlinks/other node types in a blob root are not expected and are intentionally skipped
|
|
105
181
|
}
|
|
106
182
|
}
|
|
183
|
+
return counts;
|
|
107
184
|
}
|
|
108
185
|
|
|
109
186
|
/**
|
|
110
|
-
*
|
|
111
|
-
* and atomically renames into place so a
|
|
112
|
-
*
|
|
113
|
-
*
|
|
187
|
+
* Copy every blob root into `destDir` as `<rootIndex>/<relpath>`, hard-linking where possible.
|
|
188
|
+
* Writes to a temporary sibling and atomically renames into place so a run that fails mid-copy never
|
|
189
|
+
* leaves a partial directory a later restore would trust, and replaces any pre-existing `destDir`.
|
|
190
|
+
* Shared by managed-backup snapshots and `copy-db`'s standalone blob copy (harper#2048).
|
|
114
191
|
*/
|
|
115
|
-
export async function
|
|
116
|
-
const
|
|
117
|
-
const tempDir = join(blobsRootDir(backupDir), `.tmp-${backupId}`);
|
|
192
|
+
export async function copyBlobRootsByIndex(destDir: string, blobRoots: string[]): Promise<void> {
|
|
193
|
+
const tempDir = destDir + '.tmp';
|
|
118
194
|
await rm(tempDir, { recursive: true, force: true });
|
|
119
195
|
await mkdir(tempDir, { recursive: true });
|
|
120
196
|
try {
|
|
197
|
+
let substituted = 0;
|
|
198
|
+
let captured = 0;
|
|
121
199
|
for (let index = 0; index < blobRoots.length; index++) {
|
|
122
|
-
await copyTree(blobRoots[index], join(tempDir, String(index)));
|
|
200
|
+
const counts = await copyTree(blobRoots[index], join(tempDir, String(index)), true);
|
|
201
|
+
substituted += counts.substituted;
|
|
202
|
+
captured += counts.captured;
|
|
123
203
|
}
|
|
124
|
-
|
|
125
|
-
|
|
204
|
+
if (substituted > 0) {
|
|
205
|
+
logger.warn(
|
|
206
|
+
`Blob copy into ${destDir} substituted ${substituted} of ${substituted + captured} blob ` +
|
|
207
|
+
`file(s) with PENDING or ERROR markers because they were not capturable whole.`
|
|
208
|
+
);
|
|
209
|
+
}
|
|
210
|
+
await rm(destDir, { recursive: true, force: true });
|
|
211
|
+
await rename(tempDir, destDir);
|
|
126
212
|
} catch (error) {
|
|
127
213
|
await rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
128
214
|
throw error;
|
|
129
215
|
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Snapshot a database's blob roots into a backup's blob directory. Overwrites any pre-existing
|
|
220
|
+
* snapshot for the same id (create_backup always produces a fresh id, so this only matters on a
|
|
221
|
+
* retried offline run).
|
|
222
|
+
*/
|
|
223
|
+
export async function snapshotBlobs(backupDir: string, backupId: number, blobRoots: string[]): Promise<void> {
|
|
224
|
+
await copyBlobRootsByIndex(blobSnapshotDir(backupDir, backupId), blobRoots);
|
|
130
225
|
await writeBlobsReadme(backupDir, blobRoots);
|
|
131
226
|
}
|
|
132
227
|
|
|
133
228
|
/**
|
|
134
229
|
* Build the `blobs/README.md` documenting the blob snapshot layout, so an operator inspecting or
|
|
135
|
-
* hand-recovering a backup can decode the numeric directories.
|
|
136
|
-
* - managed (default): a create_backup repository, where snapshots are keyed by backup id
|
|
230
|
+
* hand-recovering a backup can decode the numeric directories. Three variants:
|
|
231
|
+
* - `managed` (default): a create_backup repository, where snapshots are keyed by backup id
|
|
137
232
|
* (`<backupId>/<rootIndex>/…`) and restore is automatic via `restore_backup`.
|
|
138
|
-
* -
|
|
139
|
-
*
|
|
233
|
+
* - `archive`: a downloaded `get_backup` tar, which holds a single snapshot with no backup-id level
|
|
234
|
+
* (`<rootIndex>/…`) and is restored by extracting the files back into the roots.
|
|
235
|
+
* - `copy`: the companion directory `copy-db` writes beside a database copy, restored by hand.
|
|
140
236
|
*/
|
|
141
|
-
export function blobsReadmeContent(
|
|
237
|
+
export function blobsReadmeContent(
|
|
238
|
+
blobRoots: string[],
|
|
239
|
+
{ variant = 'managed' }: { variant?: 'managed' | 'archive' | 'copy' } = {}
|
|
240
|
+
): string {
|
|
142
241
|
const rootMapping =
|
|
143
242
|
blobRoots.length > 0 ? blobRoots.map((root, index) => ` ${index} -> ${root}`).join('\n') : ' (none)';
|
|
144
|
-
const layout =
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
243
|
+
const layout =
|
|
244
|
+
variant === 'managed'
|
|
245
|
+
? '<backupId>/<rootIndex>/<shard1>/<shard2>/<fileId>'
|
|
246
|
+
: '<rootIndex>/<shard1>/<shard2>/<fileId>';
|
|
247
|
+
const intro =
|
|
248
|
+
variant === 'copy'
|
|
249
|
+
? `This directory holds the file-backed blobs of a \`copy-db\` database copy; the database file itself
|
|
250
|
+
is the sibling \`.mdb\` this directory is named after. Blobs are addressed by database NAME and the
|
|
251
|
+
configured blob roots — never by the database file's path — so a copy is only restorable with these
|
|
252
|
+
files: put each \`<rootIndex>/\` tree into the matching blob root of whatever database name you
|
|
253
|
+
restore the copy as (mapping below).`
|
|
254
|
+
: variant === 'archive'
|
|
255
|
+
? `This directory holds this database's file-backed blobs within a downloaded \`get_backup\` archive.
|
|
149
256
|
To restore them, extract each \`<rootIndex>/\` tree back into the matching blob root (see the mapping
|
|
150
257
|
below and ../README.md).`
|
|
151
|
-
|
|
258
|
+
: `This directory holds point-in-time snapshots of this database's file-backed blobs, captured
|
|
152
259
|
alongside each RocksDB managed backup. You do not restore these by hand — \`restore_backup\` puts
|
|
153
260
|
them back automatically (see ../README.md); this file just documents the layout.`;
|
|
154
|
-
const backupIdBullet =
|
|
155
|
-
|
|
156
|
-
|
|
261
|
+
const backupIdBullet =
|
|
262
|
+
variant === 'managed'
|
|
263
|
+
? `- **<backupId>** matches the RocksDB backup id (\`harper list_backups\`). Each id is a full,
|
|
157
264
|
independent snapshot (not incremental).
|
|
158
|
-
|
|
265
|
+
`
|
|
266
|
+
: '';
|
|
159
267
|
return `# Harper blob snapshots
|
|
160
268
|
|
|
161
269
|
${intro}
|
|
@@ -166,7 +274,7 @@ ${intro}
|
|
|
166
274
|
|
|
167
275
|
${backupIdBullet}- **<rootIndex>** is which of the database's blob roots the file came from — the index into
|
|
168
276
|
\`storage.blobPaths[n]\`. When \`storage.blobPaths\` is not configured there is a single default root
|
|
169
|
-
(\`<rootPath>/blobs/<db>\`) at index 0. Current mapping
|
|
277
|
+
(\`<rootPath>/blobs/<db>\`) at index 0. Current mapping:
|
|
170
278
|
|
|
171
279
|
${rootMapping}
|
|
172
280
|
|
|
@@ -175,8 +283,10 @@ ${rootMapping}
|
|
|
175
283
|
4096 entries per directory). E.g. a blob with id \`0x12345678\` lives at \`12/345/678\`; a short id
|
|
176
284
|
like \`0xc1a\` lives at \`0/0/c1a\`.
|
|
177
285
|
|
|
178
|
-
|
|
179
|
-
otherwise.
|
|
286
|
+
Complete blobs are hard links to the live blobs when the destination is on the same filesystem, and
|
|
287
|
+
copies otherwise. A blob that was not capturable whole is stored as a marker instead: header type
|
|
288
|
+
\`0xfe\` is retryable (PENDING), while \`0xff\` is terminal (ERROR). The marker preserves the file id
|
|
289
|
+
but does not contain the original blob bytes.
|
|
180
290
|
`;
|
|
181
291
|
}
|
|
182
292
|
|
package/dataLayer/delete.ts
CHANGED
|
@@ -110,7 +110,12 @@ export async function deleteAuditLogsBefore(deleteObj: any) {
|
|
|
110
110
|
harperLogger.info(`Finished deleting audit logs before ${deleteObj.timestamp}`);
|
|
111
111
|
|
|
112
112
|
// `deleteTransactionLogsBefore` returns `entries_deleted`; the legacy result exposes it as `transactions_deleted`.
|
|
113
|
-
return new DeleteAuditLogsBeforeResults(
|
|
113
|
+
return new DeleteAuditLogsBeforeResults(
|
|
114
|
+
results.start_timestamp,
|
|
115
|
+
results.end_timestamp,
|
|
116
|
+
results.entries_deleted,
|
|
117
|
+
results.log_files_deleted
|
|
118
|
+
);
|
|
114
119
|
}
|
|
115
120
|
|
|
116
121
|
/**
|