@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
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DatabaseTransaction,
|
|
3
|
+
closeWriteInstance,
|
|
4
|
+
validateWrite,
|
|
5
|
+
shouldSpareCommitPhase,
|
|
3
6
|
transactionOpenTooLongError,
|
|
4
7
|
type CommitOptions,
|
|
5
8
|
type TransactionWrite,
|
|
6
9
|
type CommitResolution,
|
|
7
10
|
} from './DatabaseTransaction';
|
|
8
11
|
import { cleanupUnusedBlobs, collectRetainedFileIds } from './blob.ts';
|
|
12
|
+
import { ServerError } from '../utility/errors/hdbError.ts';
|
|
9
13
|
import { getNextMonotonicTime } from '../utility/lmdb/commonUtility.ts';
|
|
10
14
|
import * as harperLogger from '../utility/logging/harper_logger.ts';
|
|
11
15
|
import type { Context } from './ResourceInterface.ts';
|
|
@@ -35,10 +39,14 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
35
39
|
declare overloadChecked: boolean;
|
|
36
40
|
open = TRANSACTION_STATE.OPEN;
|
|
37
41
|
|
|
42
|
+
renewIdleTimeout(): void {
|
|
43
|
+
this.timeout = Math.max(txnExpiration, this.timeoutBudget ?? 0);
|
|
44
|
+
}
|
|
45
|
+
|
|
38
46
|
getReadTxn(): any {
|
|
39
47
|
// used optimistically
|
|
40
48
|
this.readTxnRefCount = (this.readTxnRefCount || 0) + 1;
|
|
41
|
-
this.
|
|
49
|
+
this.renewIdleTimeout();
|
|
42
50
|
if (this.stale) this.stale = false;
|
|
43
51
|
if (this.readTxn) {
|
|
44
52
|
if ((this.readTxn as any).openTimer) (this.readTxn as any).openTimer = 0;
|
|
@@ -111,12 +119,14 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
111
119
|
|
|
112
120
|
this.linkWrite(operation);
|
|
113
121
|
this.writes.push(operation); // standard path, add to current transaction
|
|
122
|
+
(this.ownedWrites ??= new WeakSet()).add(operation);
|
|
114
123
|
operation.stagedIn = this;
|
|
115
124
|
}
|
|
116
125
|
|
|
117
126
|
removeWrite(operation: TransactionWrite) {
|
|
118
127
|
const index = this.writes.indexOf(operation);
|
|
119
128
|
if (index > -1) this.writes[index] = null;
|
|
129
|
+
this.ownedWrites?.delete(operation);
|
|
120
130
|
}
|
|
121
131
|
|
|
122
132
|
/**
|
|
@@ -138,7 +148,11 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
138
148
|
this.validated = this.writes.length;
|
|
139
149
|
for (let i = start; i < this.validated; i++) {
|
|
140
150
|
const write = this.writes[i];
|
|
141
|
-
|
|
151
|
+
try {
|
|
152
|
+
if (write) validateWrite(write, this.timestamp, this);
|
|
153
|
+
} finally {
|
|
154
|
+
closeWriteInstance(write);
|
|
155
|
+
}
|
|
142
156
|
}
|
|
143
157
|
let hasBefore;
|
|
144
158
|
for (let i = start; i < this.validated; i++) {
|
|
@@ -153,6 +167,9 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
153
167
|
// source writes will finish (with right to refuse/abort) before proceeeding to less
|
|
154
168
|
// canonical sources.
|
|
155
169
|
if (hasBefore) {
|
|
170
|
+
// see `committing` in DatabaseTransaction (issue #2062)
|
|
171
|
+
this.setCommitPhase(true);
|
|
172
|
+
const stagedWrites = this.writes.length;
|
|
156
173
|
return (async () => {
|
|
157
174
|
try {
|
|
158
175
|
for (let phase = 0; phase < 2; phase++) {
|
|
@@ -172,9 +189,15 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
172
189
|
if (completion) await (completion.push ? Promise.all(completion) : completion);
|
|
173
190
|
}
|
|
174
191
|
} catch (error) {
|
|
192
|
+
this.setCommitPhase(false);
|
|
175
193
|
this.abort();
|
|
176
194
|
throw error;
|
|
177
195
|
}
|
|
196
|
+
this.setCommitPhase(false);
|
|
197
|
+
// aborted underneath us while parked above — see DatabaseTransaction's twin guard
|
|
198
|
+
if (this.timedOut) throw transactionOpenTooLongError();
|
|
199
|
+
if (stagedWrites > 0 && this.writes.length === 0 && this.open === TRANSACTION_STATE.CLOSED)
|
|
200
|
+
throw new ServerError('Transaction was aborted while its commit was waiting on pre-commit work', 500);
|
|
178
201
|
return this.commit(options);
|
|
179
202
|
})();
|
|
180
203
|
}
|
|
@@ -183,6 +206,7 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
183
206
|
throw error;
|
|
184
207
|
}
|
|
185
208
|
}
|
|
209
|
+
if (!retries && this.writes.some((write) => write)) this.renewChainForNativeCommit();
|
|
186
210
|
// release the read snapshot so we don't keep it open longer than necessary
|
|
187
211
|
if (!retries) this.doneReadTxn();
|
|
188
212
|
this.open = options?.doneWriting ? TRANSACTION_STATE.LINGERING : TRANSACTION_STATE.OPEN;
|
|
@@ -192,7 +216,12 @@ export class LMDBTransaction extends DatabaseTransaction {
|
|
|
192
216
|
let writeIndex = 0;
|
|
193
217
|
this.writes = this.writes.filter((write) => write); // filter out removed entries
|
|
194
218
|
const doWrite = (write) => {
|
|
195
|
-
|
|
219
|
+
let completion;
|
|
220
|
+
try {
|
|
221
|
+
completion = write.commit(txnTime, write.entry, retries);
|
|
222
|
+
} finally {
|
|
223
|
+
closeWriteInstance(write);
|
|
224
|
+
}
|
|
196
225
|
if (typeof completion?.then === 'function') {
|
|
197
226
|
// the aggregating Promise.all is attached a turn or more later (after the conditional batch
|
|
198
227
|
// or the exclusive transaction resolves), so handle rejection here to keep the gap from
|
|
@@ -372,10 +401,21 @@ let timer;
|
|
|
372
401
|
|
|
373
402
|
function startMonitoringTxns() {
|
|
374
403
|
timer = setInterval(function () {
|
|
404
|
+
const checkedCommitPhaseChains = new Set<DatabaseTransaction>();
|
|
375
405
|
for (const txn of trackedTxns) {
|
|
406
|
+
const commitChainHead = txn.commitChainHead ?? txn;
|
|
376
407
|
if (txn.timeout <= 0) {
|
|
377
408
|
const url = (txn.getContext() as any)?.url;
|
|
378
|
-
if (
|
|
409
|
+
if (shouldSpareCommitPhase(txn, checkedCommitPhaseChains)) {
|
|
410
|
+
// see DatabaseTransaction's monitor (issue #2062)
|
|
411
|
+
harperLogger.warn?.(
|
|
412
|
+
`Transaction has been in its commit phase past the open-transaction limit, waiting on pre-commit work (e.g. a large blob write); letting it complete, from table: ${
|
|
413
|
+
(txn.db as any)?.name + (url ? ' path: ' + url : '')
|
|
414
|
+
}`,
|
|
415
|
+
...(txn.startedFrom ? [`was started from ${txn.startedFrom.resourceName}.${txn.startedFrom.method}`] : [])
|
|
416
|
+
);
|
|
417
|
+
txn.timeout = Math.max(txnExpiration, txn.timeoutBudget ?? 0);
|
|
418
|
+
} else if (txn.hasPendingWrites() && !txn.sourceApply && !txn.isReplay) {
|
|
379
419
|
// Abort and surface an error rather than force-committing a partial write set: silently
|
|
380
420
|
// committing on the application's behalf breaks atomicity and can leave orphaned
|
|
381
421
|
// secondary-index entries that only a full index rebuild repairs (issue #1407). The app
|
|
@@ -390,7 +430,7 @@ function startMonitoringTxns() {
|
|
|
390
430
|
}`
|
|
391
431
|
);
|
|
392
432
|
try {
|
|
393
|
-
|
|
433
|
+
commitChainHead.abortDueToTimeout();
|
|
394
434
|
} catch (error) {
|
|
395
435
|
harperLogger.debug?.(`Error aborting timed out transaction: ${error.message}`);
|
|
396
436
|
}
|
|
@@ -408,7 +448,7 @@ function startMonitoringTxns() {
|
|
|
408
448
|
} catch (error) {
|
|
409
449
|
harperLogger.debug?.(`Error committing timed out transaction: ${error.message}`);
|
|
410
450
|
}
|
|
411
|
-
txn.timeout = txnExpiration;
|
|
451
|
+
txn.timeout = Math.max(txnExpiration, txn.timeoutBudget ?? 0);
|
|
412
452
|
}
|
|
413
453
|
} else {
|
|
414
454
|
txn.timeout -= txnExpiration;
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { trackReadRange } from './DatabaseTransaction.ts';
|
|
2
|
-
import { RocksDatabase, type RocksDatabaseOptions, constants, type Store } from '@harperfast/rocksdb-js';
|
|
2
|
+
import { RocksDatabase, type RocksDatabaseOptions, constants, type Store, Transaction } from '@harperfast/rocksdb-js';
|
|
3
3
|
|
|
4
4
|
const FRESH_VERSION_FLAG = constants.FRESH_VERSION_FLAG;
|
|
5
5
|
import { WeakLRUCache } from 'weak-lru-cache';
|
|
6
6
|
import { when } from '../utility/when.ts';
|
|
7
|
-
import { assignStoredFields, entryMap, METADATA, type Entry } from './RecordEncoder.ts';
|
|
7
|
+
import { assignStoredFields, entryMap, METADATA, VERSION_REUSED, type Entry } from './RecordEncoder.ts';
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* RocksDatabase subclass that owns all primary-store behaviour for Harper tables:
|
|
@@ -42,6 +42,33 @@ export class PrimaryRocksDatabase extends RocksDatabase {
|
|
|
42
42
|
}
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Match LMDB's remove(key, version) contract without splitting the version check from the delete.
|
|
46
|
+
async removeIfVersion(id: any, version: number): Promise<boolean> {
|
|
47
|
+
let retried = false;
|
|
48
|
+
while (true) {
|
|
49
|
+
let transaction: Transaction | undefined;
|
|
50
|
+
try {
|
|
51
|
+
transaction = new Transaction(this.store);
|
|
52
|
+
const entry = this.#processEntry(super.getSync(id, { transaction }), id);
|
|
53
|
+
if (!entry || entry.version !== version) {
|
|
54
|
+
transaction.abort();
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
this.#cache?.delete(id);
|
|
58
|
+
super.removeSync(id, { transaction });
|
|
59
|
+
await transaction.commit();
|
|
60
|
+
return true;
|
|
61
|
+
} catch (error: any) {
|
|
62
|
+
try {
|
|
63
|
+
transaction?.abort();
|
|
64
|
+
} catch {}
|
|
65
|
+
// rocksdb-js reports a writer that committed between our read and our commit as ERR_BUSY
|
|
66
|
+
if (retried || error?.code !== 'ERR_BUSY') throw error;
|
|
67
|
+
retried = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
/**
|
|
46
73
|
* Initialises encoder/decoder state. Must be called once after open() with the root
|
|
47
74
|
* RocksDatabase. Equivalent to the RocksDB branch of handleLocalTimeForGets, but as
|
|
@@ -95,6 +122,13 @@ export class PrimaryRocksDatabase extends RocksDatabase {
|
|
|
95
122
|
*/
|
|
96
123
|
getEntry(id: any, options?: any): any {
|
|
97
124
|
this.readCount++;
|
|
125
|
+
// Commit-path base reads must reflect the caller's transaction snapshot; the cache vouch
|
|
126
|
+
// answers "latest committed" — the wrong question there, and wrong outright for a version a
|
|
127
|
+
// resequenced write reused — so read the store directly, touching neither cache nor VT.
|
|
128
|
+
if (options?.uncachedRead) {
|
|
129
|
+
if (options.async) return when(super.get(id, options), (result) => this.#processEntry(result, id));
|
|
130
|
+
return this.#processEntry(super.getSync(id, options), id);
|
|
131
|
+
}
|
|
98
132
|
const cache = this.#cache;
|
|
99
133
|
// The cache stores the record *value* (weakly, via setValue) rather than
|
|
100
134
|
// the Entry: a WeakRef-wrapped value lets the LRFU expirer release it once
|
|
@@ -132,11 +166,16 @@ export class PrimaryRocksDatabase extends RocksDatabase {
|
|
|
132
166
|
if (cache && cachedValue !== undefined) cache.delete(id);
|
|
133
167
|
return undefined;
|
|
134
168
|
}
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
169
|
+
if (entry.version != null && cache) {
|
|
170
|
+
// its version no longer identifies its value, so drop any copy already held
|
|
171
|
+
if (entry.metadataFlags & VERSION_REUSED) {
|
|
172
|
+
if (cachedValue !== undefined) cache.delete(id);
|
|
173
|
+
} else if (entry.value != null && typeof entry.value === 'object') {
|
|
174
|
+
// Only object values can be weakly cached and mapped back to their Entry;
|
|
175
|
+
// primitive/empty values fall through uncached (no fast path, still correct).
|
|
176
|
+
entryMap.set(entry.value, entry);
|
|
177
|
+
cache.setValue(id, entry.value, (entry.size ?? 0) >> 10);
|
|
178
|
+
}
|
|
140
179
|
}
|
|
141
180
|
return entry;
|
|
142
181
|
});
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from './blob.ts';
|
|
31
31
|
import { getThisNodeId } from './nodeIdMapping.ts';
|
|
32
32
|
import { recordAction } from './analytics/write.ts';
|
|
33
|
-
import { RocksDatabase } from '@harperfast/rocksdb-js';
|
|
33
|
+
import { constants, RocksDatabase } from '@harperfast/rocksdb-js';
|
|
34
34
|
import { when } from '../utility/when.ts';
|
|
35
35
|
import { CONFIG_PARAMS } from '../utility/hdbTerms.ts';
|
|
36
36
|
import * as envMngr from '../utility/environment/environmentManager.js';
|
|
@@ -42,6 +42,33 @@ const StructonEncoder = createStructon(Encoder) as typeof Encoder;
|
|
|
42
42
|
// monitoring; the store name is passed as the metric path.
|
|
43
43
|
const MISSING_STRUCTURE_METRIC = 'decode-missing-structure';
|
|
44
44
|
|
|
45
|
+
// Reaching this bound is not an error: novel shapes fall back to plain msgpackr encoding, which
|
|
46
|
+
// round-trips correctly but gives up random-access field reads.
|
|
47
|
+
export const DEFAULT_MAX_TYPED_STRUCTURES = 256;
|
|
48
|
+
|
|
49
|
+
export interface StructureCounts {
|
|
50
|
+
typed: number;
|
|
51
|
+
classic: number;
|
|
52
|
+
typedLimit: number;
|
|
53
|
+
typedEnabled: boolean;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Mirrors structon's onLoadedStructures over the four durable payload shapes it accepts. The
|
|
57
|
+
// bare-array and cbor-x forms carry named structures only; structon keeps the encoder's own typed
|
|
58
|
+
// dictionary for them, and they are written by the capped fallback path (msgpackr's base save
|
|
59
|
+
// overwriting the combined payload, before structon's best-effort re-save). That window is by
|
|
60
|
+
// definition a saturated store, so reporting zero there would show an empty dictionary at the one
|
|
61
|
+
// moment the count matters most.
|
|
62
|
+
function countDurableStructures(sharedData: any, localTyped: number): { typed: number; classic: number } {
|
|
63
|
+
if (!sharedData) return { typed: 0, classic: 0 };
|
|
64
|
+
if (sharedData instanceof Map)
|
|
65
|
+
return { typed: sharedData.get('typed')?.length ?? 0, classic: sharedData.get('named')?.length ?? 0 };
|
|
66
|
+
if (Array.isArray(sharedData)) return { typed: localTyped, classic: sharedData.length };
|
|
67
|
+
if (!sharedData.named && !sharedData.typed && Array.isArray(sharedData.structures))
|
|
68
|
+
return { typed: localTyped, classic: sharedData.structures.length };
|
|
69
|
+
return { typed: sharedData.typed?.length ?? 0, classic: sharedData.named?.length ?? 0 };
|
|
70
|
+
}
|
|
71
|
+
|
|
45
72
|
// Terminal error messages msgpackr/structon throw when a record references a shared structure that
|
|
46
73
|
// is not in this node's structures buffer. Both the typed (random-access) path (structon's
|
|
47
74
|
// readStruct) and the classic path (msgpackr's createSecondByteReader) reload the structures from
|
|
@@ -77,6 +104,8 @@ export type Entry = {
|
|
|
77
104
|
export const TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 4, 0x40, 0, 0]);
|
|
78
105
|
// the first byte here indicates that we use the last timestamp
|
|
79
106
|
export const LAST_TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 1, 0, 0, 0]);
|
|
107
|
+
// Never write this into an audit entry value: the substitution resolves to 2.0 when no previous time
|
|
108
|
+
// was recorded, and the audit format reads a leading byte other than 0x42 as "no field here". See harper#2247.
|
|
80
109
|
export const PREVIOUS_TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 3, 0x40, 0, 0]);
|
|
81
110
|
export const NEW_TIMESTAMP_PLACEHOLDER = new Uint8Array([1, 1, 1, 1, 0, 0x40, 0, 0]);
|
|
82
111
|
export const LOCAL_TIMESTAMP = Symbol('local-timestamp');
|
|
@@ -95,6 +124,26 @@ export const HAS_NODE_ID = 64;
|
|
|
95
124
|
export const PENDING_LOCAL_TIME = 1;
|
|
96
125
|
export const HAS_STRUCTURE_UPDATE = 0x100;
|
|
97
126
|
export const HAS_ADDITIONAL_AUDIT_REFS = 0x80;
|
|
127
|
+
// A resequenced write keeps the (newer) version it merged onto, so one version identifies two
|
|
128
|
+
// different stored values and version equality proves nothing. The metadata word this is set in is
|
|
129
|
+
// the same header word the VerificationTable reads at value offset 8 — ACTION_32_BIT is the tag
|
|
130
|
+
// byte its predicate requires — so the bit is what stops the native layer vouching for the version.
|
|
131
|
+
export const VERSION_REUSED = constants.VERSION_NOT_UNIQUE_FLAG;
|
|
132
|
+
// The bit is persisted in every resequenced record, so a rocksdb-js that moved it down onto one of
|
|
133
|
+
// the flags above (or up into the tag byte) would silently change what records already on disk
|
|
134
|
+
// mean: it must stay a single bit strictly between them.
|
|
135
|
+
if (
|
|
136
|
+
typeof VERSION_REUSED !== 'number' ||
|
|
137
|
+
(VERSION_REUSED & (VERSION_REUSED - 1)) !== 0 ||
|
|
138
|
+
VERSION_REUSED < 0x10000 ||
|
|
139
|
+
VERSION_REUSED > 0x800000
|
|
140
|
+
)
|
|
141
|
+
throw new Error(
|
|
142
|
+
`rocksdb-js VERSION_NOT_UNIQUE_FLAG (${VERSION_REUSED}) is not a single bit in the range Harper record metadata reserves for it — requires @harperfast/rocksdb-js >= 2.8.0`
|
|
143
|
+
);
|
|
144
|
+
function versionIsReused(newVersion: number, existingEntry: { version?: number } | undefined): boolean {
|
|
145
|
+
return existingEntry?.version != null && newVersion <= existingEntry.version;
|
|
146
|
+
}
|
|
98
147
|
|
|
99
148
|
const TRACKED_WRITE_TYPES = new Set(['put', 'patch', 'delete', 'message', 'publish']);
|
|
100
149
|
// For now we use this as the private property mechanism for mapping records to entries.
|
|
@@ -197,6 +246,10 @@ export class RecordEncoder extends StructonEncoder {
|
|
|
197
246
|
declare saveStructures: any;
|
|
198
247
|
declare getStructures: any;
|
|
199
248
|
declare _writeStruct: any;
|
|
249
|
+
declare typedStructs: any[];
|
|
250
|
+
declare structures: any[];
|
|
251
|
+
declare maxOwnStructures: number;
|
|
252
|
+
randomAccessStructure = false;
|
|
200
253
|
structureUpdate?: any;
|
|
201
254
|
// Set by TableResource.updatedAttributes. Undefined for a store with no resolved attributes, which
|
|
202
255
|
// is what keeps the projection off that store's writes entirely.
|
|
@@ -217,7 +270,7 @@ export class RecordEncoder extends StructonEncoder {
|
|
|
217
270
|
// Bound the per-encoder typed-structure dictionary. It is append-only and pinned on the
|
|
218
271
|
// long-lived primary store, so a wide/sparse schema (whose records vary by per-field value
|
|
219
272
|
// width) can grow it unbounded and exhaust memory. Caller-overridable; default caps it.
|
|
220
|
-
options.maxOwnStructures ??=
|
|
273
|
+
options.maxOwnStructures ??= DEFAULT_MAX_TYPED_STRUCTURES;
|
|
221
274
|
/**
|
|
222
275
|
* The base class for records that provides the read-only methods for accessing
|
|
223
276
|
* metadata and will be assigned computed property getters. On its own, these instances
|
|
@@ -233,6 +286,7 @@ export class RecordEncoder extends StructonEncoder {
|
|
|
233
286
|
// (e.g. __dbis__, useVersions=false) must not — see the encode hook + harper#1307. Default to
|
|
234
287
|
// true when unspecified so an option that doesn't propagate can't silently strip prefixes.
|
|
235
288
|
this.useVersions = options.useVersions !== false;
|
|
289
|
+
this.randomAccessStructure = options.randomAccessStructure === true;
|
|
236
290
|
// structon (the StructonEncoder base) always installs the struct write hook. For DBIs
|
|
237
291
|
// that don't opt into struct mode (non-primary, e.g. __dbis__), force it to bail (return
|
|
238
292
|
// 0) so objects are written in plain msgpackr records mode — decodable by readers without
|
|
@@ -417,12 +471,14 @@ export class RecordEncoder extends StructonEncoder {
|
|
|
417
471
|
// HAS_STRUCTURE_UPDATE in the audit log for a structure that was never persisted.
|
|
418
472
|
if (committed === true) {
|
|
419
473
|
this.structureUpdate = structures;
|
|
474
|
+
this.checkStructureCapacity();
|
|
420
475
|
return true;
|
|
421
476
|
}
|
|
422
477
|
return false;
|
|
423
478
|
} else {
|
|
424
479
|
const result = superSaveStructures.call(this, structures, isCompatible);
|
|
425
480
|
this.structureUpdate = structures;
|
|
481
|
+
if (result !== false) this.checkStructureCapacity();
|
|
426
482
|
return result;
|
|
427
483
|
}
|
|
428
484
|
};
|
|
@@ -432,10 +488,51 @@ export class RecordEncoder extends StructonEncoder {
|
|
|
432
488
|
const buffer = this.rootStore.getBinarySync(sharedStructuresKey);
|
|
433
489
|
return buffer ? this.decode(buffer) : undefined;
|
|
434
490
|
} else {
|
|
435
|
-
|
|
491
|
+
// msgpackr only defines getStructures when the option was supplied; a store with no
|
|
492
|
+
// shared-structures mechanism has no durable dictionary to report.
|
|
493
|
+
return superGetStructures?.call(this);
|
|
436
494
|
}
|
|
437
495
|
};
|
|
438
496
|
}
|
|
497
|
+
/**
|
|
498
|
+
* Sizes of this store's record-structure dictionaries. Read from the durable payload, not this
|
|
499
|
+
* encoder's arrays: each worker owns an encoder and loads lazily, so a worker that never encoded
|
|
500
|
+
* for this store holds empty arrays for a store whose dictionary is full.
|
|
501
|
+
*/
|
|
502
|
+
getStructureCounts(): StructureCounts {
|
|
503
|
+
const { typed, classic } = countDurableStructures(this.getStructures(), this.typedStructs?.length ?? 0);
|
|
504
|
+
return {
|
|
505
|
+
typed,
|
|
506
|
+
classic,
|
|
507
|
+
typedLimit: this.maxOwnStructures ?? DEFAULT_MAX_TYPED_STRUCTURES,
|
|
508
|
+
typedEnabled: this.randomAccessStructure,
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
/**
|
|
512
|
+
* Warn once per encoder when its typed dictionary is at the bound. Fires from the post-save path,
|
|
513
|
+
* so it needs some structure save to happen -- any save, including a classic one -- and it needs
|
|
514
|
+
* this encoder to have loaded the dictionary. Several workers can therefore each warn for one
|
|
515
|
+
* table, and a worker that never saves after loading a full dictionary stays silent. The counts
|
|
516
|
+
* above are the reliable signal; this is the heads-up. It must not escape into the save's return
|
|
517
|
+
* value -- the structures are committed.
|
|
518
|
+
*/
|
|
519
|
+
#reportedStructureSaturation = false;
|
|
520
|
+
checkStructureCapacity() {
|
|
521
|
+
if (this.#reportedStructureSaturation) return;
|
|
522
|
+
const typedLimit = this.maxOwnStructures ?? DEFAULT_MAX_TYPED_STRUCTURES;
|
|
523
|
+
if ((this.typedStructs?.length ?? 0) < typedLimit) return;
|
|
524
|
+
this.#reportedStructureSaturation = true;
|
|
525
|
+
try {
|
|
526
|
+
harperLogger.warn(
|
|
527
|
+
`Typed-structure dictionary for store ${this.name ?? this.rootStore?.name ?? '(unnamed)'} reached its limit of ` +
|
|
528
|
+
`${typedLimit} structures; new record shapes will be stored without random-access field ` +
|
|
529
|
+
'encoding. Dictionary size follows the variety of shapes written -- field set, key order, ' +
|
|
530
|
+
'and per-field value width -- not column count (see HarperFast/harper#2220).'
|
|
531
|
+
);
|
|
532
|
+
} catch {
|
|
533
|
+
// the structures are already committed; a failing sink must not fail the write
|
|
534
|
+
}
|
|
535
|
+
}
|
|
439
536
|
decode(buffer, options) {
|
|
440
537
|
lastMetadata = null;
|
|
441
538
|
const start = options?.start || 0;
|
|
@@ -809,6 +906,10 @@ export function recordUpdater(store, tableId, auditStore) {
|
|
|
809
906
|
if (expiresAt >= 0) assignMetadata |= HAS_EXPIRATION;
|
|
810
907
|
metadataInNextEncoding = assignMetadata;
|
|
811
908
|
expiresAtNextEncoding = expiresAt;
|
|
909
|
+
// Math.max normalizes the -1 "no metadata word" sentinel to 0 first: OR-ing the flag into
|
|
910
|
+
// -1 directly would stay -1 and silently drop the metadata word (and the flag with it).
|
|
911
|
+
if (isRocksDB && record !== undefined && versionIsReused(newVersion, existingEntry))
|
|
912
|
+
metadataInNextEncoding = Math.max(metadataInNextEncoding, 0) | VERSION_REUSED;
|
|
812
913
|
const putOptions: {
|
|
813
914
|
version: number;
|
|
814
915
|
instructedWrite?: boolean;
|
|
@@ -829,9 +930,9 @@ export function recordUpdater(store, tableId, auditStore) {
|
|
|
829
930
|
metadataInNextEncoding |= HAS_RESIDENCY_ID;
|
|
830
931
|
extendedType |= HAS_CURRENT_RESIDENCY_ID;
|
|
831
932
|
} else residencyIdAtNextEncoding = 0;
|
|
832
|
-
const
|
|
833
|
-
if (
|
|
834
|
-
nodeIdAtNextEncoding =
|
|
933
|
+
const recordNodeId = options?.recordNodeId ?? options?.nodeId ?? (audit ? getThisNodeId(auditStore) : undefined);
|
|
934
|
+
if (recordNodeId >= 0) {
|
|
935
|
+
nodeIdAtNextEncoding = recordNodeId;
|
|
835
936
|
metadataInNextEncoding |= HAS_NODE_ID;
|
|
836
937
|
} else nodeIdAtNextEncoding = -1;
|
|
837
938
|
const additionalAuditRefs = options?.additionalAuditRefs;
|
|
@@ -904,8 +1005,22 @@ export function recordUpdater(store, tableId, auditStore) {
|
|
|
904
1005
|
const nodeId = options?.nodeId ?? getThisNodeId(auditStore) ?? 0;
|
|
905
1006
|
const viaNodeId = options?.viaNodeId ?? nodeId;
|
|
906
1007
|
if (resolveRecord && existingEntry?.localTime) {
|
|
907
|
-
|
|
908
|
-
|
|
1008
|
+
let replacingId = existingEntry.localTime;
|
|
1009
|
+
let replacingEntry;
|
|
1010
|
+
if (isRocksDB && existingEntry.additionalAuditRefs) {
|
|
1011
|
+
for (const ref of existingEntry.additionalAuditRefs) {
|
|
1012
|
+
const candidate = auditStore.get(ref.version, tableId, id, ref.nodeId);
|
|
1013
|
+
if (
|
|
1014
|
+
candidate?.version === existingEntry.version &&
|
|
1015
|
+
(candidate.nodeId ?? 0) === (existingEntry.nodeId ?? 0)
|
|
1016
|
+
) {
|
|
1017
|
+
replacingId = ref.version;
|
|
1018
|
+
replacingEntry = candidate;
|
|
1019
|
+
break;
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
replacingEntry ??= auditStore.get(replacingId, tableId, id, existingEntry.nodeId);
|
|
909
1024
|
if (replacingEntry) {
|
|
910
1025
|
const previousVersion = replacingEntry.previousVersion;
|
|
911
1026
|
result = auditStore[isRocksDB ? 'putSync' : 'put'](
|
|
@@ -934,7 +1049,7 @@ export function recordUpdater(store, tableId, auditStore) {
|
|
|
934
1049
|
result = auditStore[isRocksDB ? 'putSync' : 'put'](
|
|
935
1050
|
record === undefined ? NEW_TIMESTAMP_PLACEHOLDER : LAST_TIMESTAMP_PLACEHOLDER,
|
|
936
1051
|
{
|
|
937
|
-
version: newVersion,
|
|
1052
|
+
version: options?.recordVersion ?? newVersion,
|
|
938
1053
|
tableId,
|
|
939
1054
|
recordId: id,
|
|
940
1055
|
previousVersion: isRocksDB ? existingEntry?.version : existingEntry?.localTime,
|
|
@@ -36,6 +36,8 @@ export class RequestTarget extends URLSearchParams {
|
|
|
36
36
|
declare select?: Select;
|
|
37
37
|
/** Return an explanation of the query order */
|
|
38
38
|
declare explain?: boolean;
|
|
39
|
+
/** Request a total count of matching records for pagination (REST `Prefer: count=exact|estimated`). */
|
|
40
|
+
declare count?: 'exact' | 'estimated';
|
|
39
41
|
/** Force the query to be executed in the order of conditions */
|
|
40
42
|
declare enforceExecutionOrder?: boolean;
|
|
41
43
|
declare lazy?: boolean;
|
package/resources/Resource.ts
CHANGED
|
@@ -147,20 +147,55 @@ export class Resource<Record extends object = any> implements ResourceInterface<
|
|
|
147
147
|
static put = transactional(
|
|
148
148
|
function (resource: any, query: RequestTarget, request: Context, data: any) {
|
|
149
149
|
if (Array.isArray(data) && resource.#isCollection && resource.constructor.loadAsInstance !== false) {
|
|
150
|
+
const resourceClass = resource.constructor;
|
|
151
|
+
const primaryKey = resourceClass.primaryKey;
|
|
152
|
+
// Before dispatching anything: a malformed body must not race a sibling's write, and a bare
|
|
153
|
+
// dereference below would reach the client as a 500 carrying V8's wording. A primitive is
|
|
154
|
+
// malformed for the same reason — only a Table's own key validation rejects one later, so a
|
|
155
|
+
// plain Resource's override would otherwise be handed it.
|
|
156
|
+
for (let index = 0; index < data.length; index++) {
|
|
157
|
+
const element = data[index];
|
|
158
|
+
if (element == null || typeof element !== 'object')
|
|
159
|
+
throw new ClientError(
|
|
160
|
+
`Array element at index ${index} is ${element === null ? 'null' : typeof element}, expected a record`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
const elementTarget = elementTargetFactory(query);
|
|
150
164
|
const results = [];
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
165
|
+
try {
|
|
166
|
+
for (let index = 0; index < data.length; index++) {
|
|
167
|
+
const element = data[index];
|
|
168
|
+
const target = elementTarget(element[primaryKey]);
|
|
169
|
+
const elementResource = resourceClass.getResource(target, request, {
|
|
170
|
+
async: true,
|
|
171
|
+
});
|
|
172
|
+
// `target`, not `request`: this is the target slot, and Table's back-compat shift only
|
|
173
|
+
// recognizes a RequestTarget there — a context is taken for the record.
|
|
174
|
+
// `missingMethod` rather than an unguarded call, so a resource without `put` answers 405
|
|
175
|
+
// here exactly as it does on the single-record path below.
|
|
176
|
+
if (typeof elementResource.then === 'function')
|
|
177
|
+
results.push(
|
|
178
|
+
elementResource.then((resource) =>
|
|
179
|
+
resource.put ? resource.put(element, target) : missingMethod(resource, 'put')
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
else if (elementResource.put) results.push(elementResource.put(element, target));
|
|
183
|
+
else missingMethod(elementResource, 'put');
|
|
184
|
+
}
|
|
185
|
+
} catch (error) {
|
|
186
|
+
// Settle the already-dispatched elements or a rejection among them goes unhandled. Then
|
|
187
|
+
// report the earliest-index failure, the same rule `settleElements` applies: every sibling
|
|
188
|
+
// already dispatched precedes the element that threw, so the batch reports the same failure
|
|
189
|
+
// whether that element's dispatch resolved synchronously or asynchronously.
|
|
190
|
+
return Promise.allSettled(results).then((settled) => {
|
|
191
|
+
const failed = settled.find((sibling) => sibling.status === 'rejected');
|
|
192
|
+
if (!failed) throw error;
|
|
193
|
+
throw (failed as PromiseRejectedResult).reason;
|
|
158
194
|
});
|
|
159
|
-
if (typeof elementResource.then === 'function')
|
|
160
|
-
results.push(elementResource.then((resource) => resource.put(element, request)));
|
|
161
|
-
else results.push(elementResource.put(element, query));
|
|
162
195
|
}
|
|
163
|
-
|
|
196
|
+
// Not `Promise.all`: settling the batch while a slower element is still resolving lets that
|
|
197
|
+
// element commit outside the failed batch — a partially applied array PUT.
|
|
198
|
+
return settleElements(results);
|
|
164
199
|
}
|
|
165
200
|
return resource.put
|
|
166
201
|
? resource.constructor.loadAsInstance === false
|
|
@@ -571,6 +606,53 @@ export function snakeCase(camelCase: string) {
|
|
|
571
606
|
);
|
|
572
607
|
}
|
|
573
608
|
|
|
609
|
+
/**
|
|
610
|
+
* Mint per-element targets for a fanned-out batch write. Each element needs its own object — its id
|
|
611
|
+
* must not be visible to a sibling whose dispatch resolves later — but it also needs the request's
|
|
612
|
+
* query and route metadata, which a `put()` override can read. The request's contribution is
|
|
613
|
+
* therefore resolved once here rather than deep-cloned per element, and nested metadata is shared
|
|
614
|
+
* rather than copied. `checkPermission` never carries: the collection receiver's single verdict is
|
|
615
|
+
* authoritative and per-element dispatch must not re-arm it.
|
|
616
|
+
*/
|
|
617
|
+
function elementTargetFactory(query: any): (id: any) => RequestTarget {
|
|
618
|
+
const base = cloneRequestTarget(query);
|
|
619
|
+
const search = base.toString();
|
|
620
|
+
// Only what the element target would not otherwise have. `RequestTarget` declares `pathname`,
|
|
621
|
+
// `search` and `sort` without `declare`, so they are own properties on every instance and would
|
|
622
|
+
// otherwise always be "carried" — overwriting what this element's own construction just derived,
|
|
623
|
+
// and making the skip below dead. A nullish own value carries no metadata either.
|
|
624
|
+
const carried: Record<string, any> = {};
|
|
625
|
+
for (const key of Object.keys(base)) {
|
|
626
|
+
if (key === 'id' || key === 'isCollection' || key === 'pathname' || key === 'search') continue;
|
|
627
|
+
const value = (base as any)[key];
|
|
628
|
+
if (value != null) carried[key] = value;
|
|
629
|
+
}
|
|
630
|
+
const hasCarried = Object.keys(carried).length > 0;
|
|
631
|
+
return (id) => {
|
|
632
|
+
const target = search ? new RequestTarget(search) : new RequestTarget();
|
|
633
|
+
if (hasCarried) Object.assign(target, carried);
|
|
634
|
+
target.id = id;
|
|
635
|
+
target.isCollection = false;
|
|
636
|
+
return target;
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Report a fanned-out batch write's outcome only once every element has staged or failed its own
|
|
642
|
+
* write, so the enclosing transaction cannot unwind under one. Rejects with the first failure.
|
|
643
|
+
*/
|
|
644
|
+
function settleElements(results: any[]): Promise<any[]> {
|
|
645
|
+
return Promise.allSettled(results).then((settled) => {
|
|
646
|
+
const values = new Array(settled.length);
|
|
647
|
+
for (let index = 0; index < settled.length; index++) {
|
|
648
|
+
const element = settled[index];
|
|
649
|
+
if (element.status === 'rejected') throw element.reason;
|
|
650
|
+
values[index] = element.value;
|
|
651
|
+
}
|
|
652
|
+
return values;
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
574
656
|
/**
|
|
575
657
|
* This is responsible for arranging arguments in the main static methods and creating the appropriate context and default transaction wrapping
|
|
576
658
|
* @param action
|
|
@@ -733,8 +815,10 @@ function transactional(
|
|
|
733
815
|
if (!query) {
|
|
734
816
|
query = new RequestTarget();
|
|
735
817
|
query.id = id;
|
|
736
|
-
|
|
737
|
-
|
|
818
|
+
// The collection inferred from the batch's null id has to survive onto the synthesized target,
|
|
819
|
+
// or the array is dispatched as one record update against a null primary key. Array-only:
|
|
820
|
+
// a null id on non-array data means auto-generate, not collection.
|
|
821
|
+
if (isCollection && options.method === 'put' && Array.isArray(data)) query.isCollection = true;
|
|
738
822
|
}
|
|
739
823
|
if (options.method === 'query' && data && typeof data === 'object') {
|
|
740
824
|
// QUERY executes the independently parsed body target. Make its requested projection part of
|
|
@@ -963,15 +1047,23 @@ function registerLiveSubscriptionForContext(subscription: any, resource: any, ad
|
|
|
963
1047
|
// JWT exp of the bearer credential (set by the auth layer); undefined for password/mTLS/session.
|
|
964
1048
|
authExpiresAt: user.authExpiresAt,
|
|
965
1049
|
recheck: async () => {
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
1050
|
+
let fresh: any;
|
|
1051
|
+
if (user._scopedToken) {
|
|
1052
|
+
// A scoped token's identity IS its embedded role — never re-resolve its attribution
|
|
1053
|
+
// username against hdb_user (it may not exist, or may name an unrelated principal
|
|
1054
|
+
// created later). Expiry (authExpiresAt above) is its only revocation.
|
|
1055
|
+
fresh = user;
|
|
1056
|
+
} else {
|
|
1057
|
+
// Re-fetch current user state — the user/role cache is rebuilt on mutations — so a dropped or
|
|
1058
|
+
// role-stripped user no longer authorizes.
|
|
1059
|
+
const { findAndValidateUser } = require('../security/user');
|
|
1060
|
+
fresh = await findAndValidateUser(username, undefined, false);
|
|
1061
|
+
if (!fresh?.role) return false;
|
|
1062
|
+
// Advance the subscription's context to the fresh user so downstream checks — context.user
|
|
1063
|
+
// and getCurrentUser() (which reads the resource's context) — evaluate against current state,
|
|
1064
|
+
// not the stale user captured at subscribe time.
|
|
1065
|
+
if (context) (context as any).user = fresh;
|
|
1066
|
+
}
|
|
975
1067
|
// Re-run the same operation-level allowRead that granted the subscription.
|
|
976
1068
|
const reTarget: any = cloneRequestTarget(admittedTarget);
|
|
977
1069
|
reTarget.checkPermission = fresh.role?.permission;
|