harper 5.1.20 → 5.2.0-alpha.5
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/operations.ts +13 -16
- package/components/Application.ts +188 -7
- package/components/ApplicationScope.ts +3 -0
- package/components/Scope.ts +12 -0
- package/components/anthropic/index.ts +9 -2
- package/components/bedrock/index.ts +25 -2
- package/components/componentLoader.ts +56 -7
- package/components/componentSecrets.ts +451 -0
- package/components/deploymentRecorder.ts +7 -1
- package/components/mcp/adapters/fastify.ts +3 -0
- package/components/mcp/adapters/harperHttp.ts +3 -0
- package/components/mcp/audit.ts +21 -6
- package/components/mcp/customResourceRegistry.ts +17 -38
- package/components/mcp/listChanged.ts +37 -20
- package/components/mcp/quota.ts +145 -0
- package/components/mcp/rateLimit.ts +152 -14
- package/components/mcp/resources.ts +2 -4
- package/components/mcp/toolRegistry.ts +73 -4
- package/components/mcp/tools/application.ts +77 -8
- package/components/mcp/tools/operations.ts +106 -36
- package/components/mcp/tools/schemas/operationDescriptions.ts +4 -4
- package/components/mcp/tools/schemas/operations.ts +5 -0
- package/components/mcp/transport.ts +55 -4
- package/components/ollama/index.ts +6 -2
- package/components/openai/index.ts +9 -2
- package/components/operations.js +204 -10
- package/components/operationsValidation.js +173 -0
- package/components/secretOperations.ts +498 -0
- package/config/configUtils.ts +25 -1
- package/config-app.schema.json +81 -0
- package/config-root.schema.json +8 -1
- package/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
- package/dataLayer/readAuditLog.ts +14 -0
- package/dataLayer/schemaDescribe.ts +1 -0
- package/dist/agent/operations.d.ts +7 -4
- package/dist/agent/operations.js +13 -15
- package/dist/agent/operations.js.map +1 -1
- package/dist/components/Application.d.ts +25 -2
- package/dist/components/Application.js +166 -7
- package/dist/components/Application.js.map +1 -1
- package/dist/components/ApplicationScope.d.ts +2 -0
- package/dist/components/ApplicationScope.js +3 -0
- package/dist/components/ApplicationScope.js.map +1 -1
- package/dist/components/Scope.d.ts +8 -0
- package/dist/components/Scope.js +11 -0
- package/dist/components/Scope.js.map +1 -1
- package/dist/components/anthropic/index.d.ts +4 -1
- package/dist/components/anthropic/index.js +4 -2
- package/dist/components/anthropic/index.js.map +1 -1
- package/dist/components/bedrock/index.d.ts +4 -1
- package/dist/components/bedrock/index.js +22 -2
- package/dist/components/bedrock/index.js.map +1 -1
- package/dist/components/componentLoader.d.ts +2 -0
- package/dist/components/componentLoader.js +52 -10
- package/dist/components/componentLoader.js.map +1 -1
- package/dist/components/componentSecrets.d.ts +63 -0
- package/dist/components/componentSecrets.js +405 -0
- package/dist/components/componentSecrets.js.map +1 -0
- package/dist/components/deploymentRecorder.d.ts +3 -0
- package/dist/components/deploymentRecorder.js +4 -3
- package/dist/components/deploymentRecorder.js.map +1 -1
- package/dist/components/mcp/adapters/fastify.d.ts +2 -0
- package/dist/components/mcp/adapters/fastify.js +1 -0
- package/dist/components/mcp/adapters/fastify.js.map +1 -1
- package/dist/components/mcp/adapters/harperHttp.d.ts +2 -0
- package/dist/components/mcp/adapters/harperHttp.js +1 -0
- package/dist/components/mcp/adapters/harperHttp.js.map +1 -1
- package/dist/components/mcp/audit.d.ts +2 -2
- package/dist/components/mcp/audit.js +20 -5
- package/dist/components/mcp/audit.js.map +1 -1
- package/dist/components/mcp/customResourceRegistry.d.ts +5 -12
- package/dist/components/mcp/customResourceRegistry.js +15 -30
- package/dist/components/mcp/customResourceRegistry.js.map +1 -1
- package/dist/components/mcp/listChanged.d.ts +7 -2
- package/dist/components/mcp/listChanged.js +28 -0
- package/dist/components/mcp/listChanged.js.map +1 -1
- package/dist/components/mcp/quota.d.ts +31 -0
- package/dist/components/mcp/quota.js +155 -0
- package/dist/components/mcp/quota.js.map +1 -0
- package/dist/components/mcp/rateLimit.d.ts +19 -3
- package/dist/components/mcp/rateLimit.js +129 -13
- package/dist/components/mcp/rateLimit.js.map +1 -1
- package/dist/components/mcp/resources.js.map +1 -1
- package/dist/components/mcp/toolRegistry.d.ts +39 -1
- package/dist/components/mcp/toolRegistry.js +60 -5
- package/dist/components/mcp/toolRegistry.js.map +1 -1
- package/dist/components/mcp/tools/application.d.ts +8 -1
- package/dist/components/mcp/tools/application.js +65 -5
- package/dist/components/mcp/tools/application.js.map +1 -1
- package/dist/components/mcp/tools/operations.d.ts +15 -3
- package/dist/components/mcp/tools/operations.js +99 -38
- package/dist/components/mcp/tools/operations.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operationDescriptions.js +4 -4
- package/dist/components/mcp/tools/schemas/operationDescriptions.js.map +1 -1
- package/dist/components/mcp/tools/schemas/operations.js +4 -0
- package/dist/components/mcp/tools/schemas/operations.js.map +1 -1
- package/dist/components/mcp/transport.d.ts +6 -0
- package/dist/components/mcp/transport.js +47 -3
- package/dist/components/mcp/transport.js.map +1 -1
- package/dist/components/ollama/index.d.ts +4 -1
- package/dist/components/ollama/index.js +4 -2
- package/dist/components/ollama/index.js.map +1 -1
- package/dist/components/openai/index.d.ts +4 -1
- package/dist/components/openai/index.js +4 -2
- package/dist/components/openai/index.js.map +1 -1
- package/dist/components/operations.d.ts +39 -0
- package/dist/components/operations.js +194 -2
- package/dist/components/operations.js.map +1 -1
- package/dist/components/operationsValidation.d.ts +38 -0
- package/dist/components/operationsValidation.js +155 -0
- package/dist/components/operationsValidation.js.map +1 -1
- package/dist/components/secretOperations.d.ts +113 -0
- package/dist/components/secretOperations.js +475 -0
- package/dist/components/secretOperations.js.map +1 -0
- package/dist/config/configUtils.d.ts +4 -1
- package/dist/config/configUtils.js +18 -1
- package/dist/config/configUtils.js.map +1 -1
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js +4 -3
- package/dist/dataLayer/harperBridge/lmdbBridge/lmdbUtility/initializePaths.js.map +1 -1
- package/dist/dataLayer/readAuditLog.js +11 -0
- package/dist/dataLayer/readAuditLog.js.map +1 -1
- package/dist/dataLayer/schemaDescribe.js +2 -0
- package/dist/dataLayer/schemaDescribe.js.map +1 -1
- package/dist/globals.d.ts +1 -1
- package/dist/globals.js +1 -0
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/json/systemSchema.json +38 -0
- package/dist/resources/DatabaseTransaction.d.ts +31 -3
- package/dist/resources/DatabaseTransaction.js +190 -29
- package/dist/resources/DatabaseTransaction.js.map +1 -1
- package/dist/resources/LMDBTransaction.d.ts +1 -1
- package/dist/resources/LMDBTransaction.js +43 -5
- package/dist/resources/LMDBTransaction.js.map +1 -1
- package/dist/resources/PrimaryRocksDatabase.d.ts +51 -0
- package/dist/resources/PrimaryRocksDatabase.js +194 -0
- package/dist/resources/PrimaryRocksDatabase.js.map +1 -0
- package/dist/resources/RecordEncoder.d.ts +1 -0
- package/dist/resources/RecordEncoder.js +81 -75
- package/dist/resources/RecordEncoder.js.map +1 -1
- package/dist/resources/RequestTarget.d.ts +19 -0
- package/dist/resources/RequestTarget.js.map +1 -1
- package/dist/resources/Resource.d.ts +0 -1
- package/dist/resources/Resource.js +103 -16
- package/dist/resources/Resource.js.map +1 -1
- package/dist/resources/ResourceInterface.d.ts +6 -2
- package/dist/resources/ResourceInterface.js.map +1 -1
- package/dist/resources/Resources.js.map +1 -1
- package/dist/resources/Table.d.ts +6 -4
- package/dist/resources/Table.js +247 -24
- package/dist/resources/Table.js.map +1 -1
- package/dist/resources/analytics/read.js +25 -28
- package/dist/resources/analytics/read.js.map +1 -1
- package/dist/resources/blob.js +165 -22
- package/dist/resources/blob.js.map +1 -1
- package/dist/resources/crdt.d.ts +2 -0
- package/dist/resources/crdt.js +45 -36
- package/dist/resources/crdt.js.map +1 -1
- package/dist/resources/databases.d.ts +1 -0
- package/dist/resources/databases.js +134 -7
- package/dist/resources/databases.js.map +1 -1
- package/dist/resources/graphql.js +8 -0
- package/dist/resources/graphql.js.map +1 -1
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.d.ts +35 -5
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js +239 -14
- package/dist/resources/indexes/HierarchicalNavigableSmallWorld.js.map +1 -1
- package/dist/resources/jsResource.js +6 -0
- package/dist/resources/jsResource.js.map +1 -1
- package/dist/resources/loadEnv.js +30 -3
- package/dist/resources/loadEnv.js.map +1 -1
- package/dist/resources/login.js +2 -2
- package/dist/resources/login.js.map +1 -1
- package/dist/resources/models/Models.js +14 -3
- package/dist/resources/models/Models.js.map +1 -1
- package/dist/resources/models/embedHook.js +10 -2
- package/dist/resources/models/embedHook.js.map +1 -1
- package/dist/resources/models/openaiStream.d.ts +56 -0
- package/dist/resources/models/openaiStream.js +94 -0
- package/dist/resources/models/openaiStream.js.map +1 -0
- package/dist/resources/replayLogs.js +4 -3
- package/dist/resources/replayLogs.js.map +1 -1
- package/dist/resources/search.d.ts +1 -1
- package/dist/resources/search.js +104 -11
- package/dist/resources/search.js.map +1 -1
- package/dist/resources/secretDecryptor.d.ts +54 -0
- package/dist/resources/secretDecryptor.js +131 -0
- package/dist/resources/secretDecryptor.js.map +1 -0
- package/dist/resources/tracked.js +15 -5
- package/dist/resources/tracked.js.map +1 -1
- package/dist/security/auth.js +71 -8
- package/dist/security/auth.js.map +1 -1
- package/dist/security/jsLoader.js +11 -0
- package/dist/security/jsLoader.js.map +1 -1
- package/dist/security/tokenAuthentication.d.ts +9 -1
- package/dist/security/tokenAuthentication.js +31 -1
- package/dist/security/tokenAuthentication.js.map +1 -1
- package/dist/server/DurableSubscriptionsSession.d.ts +1 -1
- package/dist/server/DurableSubscriptionsSession.js +8 -4
- package/dist/server/DurableSubscriptionsSession.js.map +1 -1
- package/dist/server/REST.js +28 -9
- package/dist/server/REST.js.map +1 -1
- package/dist/server/fastifyRoutes.js +11 -1
- package/dist/server/fastifyRoutes.js.map +1 -1
- package/dist/server/graphqlQuerying.js +4 -3
- package/dist/server/graphqlQuerying.js.map +1 -1
- package/dist/server/http.d.ts +40 -2
- package/dist/server/http.js +560 -77
- package/dist/server/http.js.map +1 -1
- package/dist/server/itc/serverHandlers.js +64 -0
- package/dist/server/itc/serverHandlers.js.map +1 -1
- package/dist/server/liveSubscriptionAuth.d.ts +12 -0
- package/dist/server/liveSubscriptionAuth.js +140 -0
- package/dist/server/liveSubscriptionAuth.js.map +1 -0
- package/dist/server/loadRootComponents.js +2 -1
- package/dist/server/loadRootComponents.js.map +1 -1
- package/dist/server/middlewareChain.d.ts +44 -1
- package/dist/server/middlewareChain.js +86 -10
- package/dist/server/middlewareChain.js.map +1 -1
- package/dist/server/mqtt.js +2 -1
- package/dist/server/mqtt.js.map +1 -1
- package/dist/server/operationsServer.js +1 -1
- package/dist/server/operationsServer.js.map +1 -1
- package/dist/server/serverHelpers/Headers.d.ts +9 -0
- package/dist/server/serverHelpers/Headers.js +22 -0
- package/dist/server/serverHelpers/Headers.js.map +1 -1
- package/dist/server/serverHelpers/JSONStream.js +3 -3
- package/dist/server/serverHelpers/JSONStream.js.map +1 -1
- package/dist/server/serverHelpers/Request.d.ts +52 -1
- package/dist/server/serverHelpers/Request.js +122 -1
- package/dist/server/serverHelpers/Request.js.map +1 -1
- package/dist/server/serverHelpers/contentTypes.js +3 -8
- package/dist/server/serverHelpers/contentTypes.js.map +1 -1
- package/dist/server/serverHelpers/progressEmitter.d.ts +19 -0
- package/dist/server/serverHelpers/progressEmitter.js +52 -4
- package/dist/server/serverHelpers/progressEmitter.js.map +1 -1
- package/dist/server/serverHelpers/registeredOperations.d.ts +40 -0
- package/dist/server/serverHelpers/registeredOperations.js +286 -0
- package/dist/server/serverHelpers/registeredOperations.js.map +1 -0
- package/dist/server/serverHelpers/serverHandlers.js +11 -5
- package/dist/server/serverHelpers/serverHandlers.js.map +1 -1
- package/dist/server/serverHelpers/serverUtilities.d.ts +1 -0
- package/dist/server/serverHelpers/serverUtilities.js +67 -5
- package/dist/server/serverHelpers/serverUtilities.js.map +1 -1
- package/dist/server/serverHelpers/uwsServer.d.ts +80 -0
- package/dist/server/serverHelpers/uwsServer.js +433 -0
- package/dist/server/serverHelpers/uwsServer.js.map +1 -0
- package/dist/server/static.d.ts +0 -15
- package/dist/server/static.js +161 -2
- package/dist/server/static.js.map +1 -1
- package/dist/server/status/index.d.ts +4 -1
- package/dist/server/status/index.js +64 -4
- package/dist/server/status/index.js.map +1 -1
- package/dist/server/threads/manageThreads.d.ts +10 -0
- package/dist/server/threads/manageThreads.js +107 -4
- package/dist/server/threads/manageThreads.js.map +1 -1
- package/dist/server/threads/socketRouter.js +5 -1
- package/dist/server/threads/socketRouter.js.map +1 -1
- package/dist/server/threads/threadServer.js +92 -14
- package/dist/server/threads/threadServer.js.map +1 -1
- package/dist/upgrade/directives/5-2-0.d.ts +7 -0
- package/dist/upgrade/directives/5-2-0.js +108 -0
- package/dist/upgrade/directives/5-2-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/common_utils.js +5 -0
- package/dist/utility/common_utils.js.map +1 -1
- package/dist/utility/envFile.d.ts +41 -0
- package/dist/utility/envFile.js +221 -0
- package/dist/utility/envFile.js.map +1 -0
- package/dist/utility/globalSchema.d.ts +9 -0
- package/dist/utility/hdbTerms.d.ts +25 -0
- package/dist/utility/hdbTerms.js +31 -0
- package/dist/utility/hdbTerms.js.map +1 -1
- package/dist/utility/logging/harper_logger.d.ts +21 -0
- package/dist/utility/logging/harper_logger.js +160 -8
- package/dist/utility/logging/harper_logger.js.map +1 -1
- package/dist/utility/logging/logRotator.js +29 -17
- package/dist/utility/logging/logRotator.js.map +1 -1
- package/dist/utility/logging/readLog.d.ts +1 -1
- package/dist/utility/logging/readLog.js +305 -2
- package/dist/utility/logging/readLog.js.map +1 -1
- package/dist/utility/operationPermissions.d.ts +18 -0
- package/dist/utility/operationPermissions.js +35 -1
- package/dist/utility/operationPermissions.js.map +1 -1
- package/dist/utility/operation_authorization.d.ts +17 -0
- package/dist/utility/operation_authorization.js +38 -0
- package/dist/utility/operation_authorization.js.map +1 -1
- package/dist/utility/secretEnvelope.d.ts +30 -0
- package/dist/utility/secretEnvelope.js +94 -0
- package/dist/utility/secretEnvelope.js.map +1 -0
- package/dist/utility/signalling.d.ts +7 -0
- package/dist/utility/signalling.js +16 -0
- package/dist/utility/signalling.js.map +1 -1
- package/dist/validation/configValidator.js +70 -24
- package/dist/validation/configValidator.js.map +1 -1
- package/index.ts +4 -0
- package/json/systemSchema.json +38 -0
- package/npm-shrinkwrap.json +15066 -9347
- package/package.json +13 -8
- package/resources/DESIGN.md +15 -12
- package/resources/DatabaseTransaction.ts +206 -37
- package/resources/LMDBTransaction.ts +43 -9
- package/resources/PrimaryRocksDatabase.ts +201 -0
- package/resources/RecordEncoder.ts +81 -69
- package/resources/RequestTarget.ts +21 -0
- package/resources/Resource.ts +109 -25
- package/resources/ResourceInterface.ts +6 -2
- package/resources/Resources.ts +1 -3
- package/resources/Table.ts +238 -29
- package/resources/analytics/read.ts +25 -30
- package/resources/blob.ts +159 -23
- package/resources/crdt.ts +37 -31
- package/resources/databases.ts +143 -6
- package/resources/graphql.ts +8 -0
- package/resources/indexes/HierarchicalNavigableSmallWorld.ts +251 -17
- package/resources/jsResource.ts +6 -0
- package/resources/loadEnv.ts +34 -3
- package/resources/login.ts +2 -2
- package/resources/models/Models.ts +19 -3
- package/resources/models/embedHook.ts +13 -2
- package/resources/models/openaiStream.ts +133 -0
- package/resources/replayLogs.ts +4 -3
- package/resources/search.ts +113 -11
- package/resources/secretDecryptor.ts +159 -0
- package/resources/tracked.ts +24 -17
- package/security/auth.ts +70 -10
- package/security/jsLoader.ts +12 -0
- package/security/tokenAuthentication.ts +42 -1
- package/server/DESIGN.md +10 -0
- package/server/DurableSubscriptionsSession.ts +8 -4
- package/server/REST.ts +26 -9
- package/server/fastifyRoutes.ts +10 -1
- package/server/graphqlQuerying.ts +4 -3
- package/server/http.ts +563 -77
- package/server/itc/serverHandlers.js +70 -0
- package/server/liveSubscriptionAuth.ts +152 -0
- package/server/loadRootComponents.js +2 -1
- package/server/middlewareChain.ts +107 -17
- package/server/mqtt.ts +2 -1
- package/server/operationsServer.ts +2 -2
- package/server/serverHelpers/Headers.ts +24 -0
- package/server/serverHelpers/JSONStream.ts +3 -3
- package/server/serverHelpers/Request.ts +127 -0
- package/server/serverHelpers/contentTypes.ts +3 -8
- package/server/serverHelpers/progressEmitter.ts +55 -5
- package/server/serverHelpers/registeredOperations.ts +259 -0
- package/server/serverHelpers/serverHandlers.js +11 -5
- package/server/serverHelpers/serverUtilities.ts +88 -5
- package/server/serverHelpers/uwsServer.ts +488 -0
- package/server/static.ts +180 -2
- package/server/status/index.ts +72 -4
- package/server/threads/manageThreads.js +102 -4
- package/server/threads/socketRouter.ts +5 -1
- package/server/threads/threadServer.js +93 -10
- package/studio/web/assets/{Chat-SdD1EZca.js → Chat-Cv_2paZE.js} +2 -2
- package/studio/web/assets/{Chat-SdD1EZca.js.map → Chat-Cv_2paZE.js.map} +1 -1
- package/studio/web/assets/{FloatingChat-GMq6tHK9.js → FloatingChat-CPyPIcVR.js} +4 -4
- package/studio/web/assets/{FloatingChat-GMq6tHK9.js.map → FloatingChat-CPyPIcVR.js.map} +1 -1
- package/studio/web/assets/{applications-B5hhv7uA.js → applications-C0jT2vdZ.js} +2 -2
- package/studio/web/assets/{applications-B5hhv7uA.js.map → applications-C0jT2vdZ.js.map} +1 -1
- package/studio/web/assets/{index-VFcpNWgq.js → index-D_GKOkhn.js} +6 -6
- package/studio/web/assets/index-D_GKOkhn.js.map +1 -0
- package/studio/web/assets/{index.lazy-DgcJojIA.js → index.lazy-CAmCIA65.js} +4 -4
- package/studio/web/assets/{index.lazy-DgcJojIA.js.map → index.lazy-CAmCIA65.js.map} +1 -1
- package/studio/web/assets/{profile-BqN-8vzm.js → profile-CaF-4aZe.js} +2 -2
- package/studio/web/assets/{profile-BqN-8vzm.js.map → profile-CaF-4aZe.js.map} +1 -1
- package/studio/web/assets/{setComponentFile-CHFvFq9u.js → setComponentFile-DLW1DHCt.js} +2 -2
- package/studio/web/assets/{setComponentFile-CHFvFq9u.js.map → setComponentFile-DLW1DHCt.js.map} +1 -1
- package/studio/web/assets/{setup-CQ3vWJSr.js → setup-TGCErIhq.js} +2 -2
- package/studio/web/assets/{setup-CQ3vWJSr.js.map → setup-TGCErIhq.js.map} +1 -1
- package/studio/web/assets/{status-BsW5fjuh.js → status-DG0Maoao.js} +2 -2
- package/studio/web/assets/{status-BsW5fjuh.js.map → status-DG0Maoao.js.map} +1 -1
- package/studio/web/assets/{swagger-ui-react-DITdeB9-.js → swagger-ui-react-8T4SgQ1m.js} +2 -2
- package/studio/web/assets/{swagger-ui-react-DITdeB9-.js.map → swagger-ui-react-8T4SgQ1m.js.map} +1 -1
- package/studio/web/assets/{tsMode-Dyph-OUs.js → tsMode-DJ6Sl24Q.js} +2 -2
- package/studio/web/assets/{tsMode-Dyph-OUs.js.map → tsMode-DJ6Sl24Q.js.map} +1 -1
- package/studio/web/assets/{useEntityRestURL-D0QTUqex.js → useEntityRestURL-JO2mfWTQ.js} +2 -2
- package/studio/web/assets/{useEntityRestURL-D0QTUqex.js.map → useEntityRestURL-JO2mfWTQ.js.map} +1 -1
- package/studio/web/index.html +1 -1
- package/upgrade/directives/5-2-0.ts +82 -0
- package/upgrade/directives/directivesController.ts +2 -1
- package/utility/common_utils.ts +5 -0
- package/utility/envFile.ts +218 -0
- package/utility/hdbTerms.ts +31 -0
- package/utility/logging/harper_logger.ts +156 -8
- package/utility/logging/logRotator.ts +24 -14
- package/utility/logging/readLog.ts +323 -2
- package/utility/operationPermissions.ts +35 -1
- package/utility/operation_authorization.ts +74 -1
- package/utility/secretEnvelope.ts +113 -0
- package/utility/signalling.ts +15 -0
- package/validation/configValidator.ts +78 -25
- package/studio/web/assets/index-VFcpNWgq.js.map +0 -1
package/security/auth.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { getSuperUser } from './user.ts';
|
|
2
2
|
import { server } from '../server/Server.ts';
|
|
3
3
|
import { resources } from '../resources/Resources.ts';
|
|
4
|
-
import { validateOperationToken, validateRefreshToken } from './tokenAuthentication.ts';
|
|
4
|
+
import { validateOperationToken, validateRefreshToken, validateLoginToken, decodeJWT } from './tokenAuthentication.ts';
|
|
5
5
|
import { table, type Table } from '../resources/databases.ts';
|
|
6
6
|
import { v4 as uuid } from 'uuid';
|
|
7
7
|
import * as env from '../utility/environment/environmentManager.ts';
|
|
@@ -10,7 +10,7 @@ import harperLogger from '../utility/logging/harper_logger.ts';
|
|
|
10
10
|
const { forComponent, AuthAuditLog } = harperLogger;
|
|
11
11
|
import serverHandlers from '../server/itc/serverHandlers.js';
|
|
12
12
|
const { user } = serverHandlers;
|
|
13
|
-
import { Headers } from '../server/serverHelpers/Headers.ts';
|
|
13
|
+
import { Headers, addVaryHeader } from '../server/serverHelpers/Headers.ts';
|
|
14
14
|
import { convertToMS } from '../utility/common_utils.ts';
|
|
15
15
|
import { verifyCertificate } from './certificateVerification/index.ts';
|
|
16
16
|
import { serializeMessage } from '../server/serverHelpers/contentTypes.ts';
|
|
@@ -33,16 +33,27 @@ function getSessionTable() {
|
|
|
33
33
|
return _sessionTable;
|
|
34
34
|
}
|
|
35
35
|
const ENABLE_SESSIONS = env.get(CONFIG_PARAMS.AUTHENTICATION_ENABLESESSIONS) ?? true;
|
|
36
|
+
// env-var strings need boolean parsing: a raw 'false'/'0' string is truthy, which would turn
|
|
37
|
+
// AUTHENTICATION_AUTHORIZELOCAL=false into an *enabled* auth bypass
|
|
38
|
+
function envFlag(value: string | undefined): boolean | undefined {
|
|
39
|
+
if (value === undefined) return undefined;
|
|
40
|
+
const normalized = value.trim().toLowerCase();
|
|
41
|
+
return normalized !== 'false' && normalized !== '0' && normalized !== '';
|
|
42
|
+
}
|
|
36
43
|
// check the environment for a flag to bypass authentication (for testing) since it doesn't necessarily get set on child threads
|
|
37
44
|
let AUTHORIZE_LOCAL =
|
|
38
|
-
process.env.AUTHENTICATION_AUTHORIZELOCAL ??
|
|
45
|
+
envFlag(process.env.AUTHENTICATION_AUTHORIZELOCAL) ??
|
|
39
46
|
env.get(CONFIG_PARAMS.AUTHENTICATION_AUTHORIZELOCAL) ??
|
|
40
|
-
process.env.DEV_MODE;
|
|
47
|
+
envFlag(process.env.DEV_MODE);
|
|
41
48
|
const LOG_AUTH_SUCCESSFUL = env.get(CONFIG_PARAMS.LOGGING_AUDITAUTHEVENTS_LOGSUCCESSFUL) ?? false;
|
|
42
49
|
const LOG_AUTH_FAILED = env.get(CONFIG_PARAMS.LOGGING_AUDITAUTHEVENTS_LOGFAILED) ?? false;
|
|
43
50
|
|
|
44
51
|
const DEFAULT_COOKIE_EXPIRES = 'Tue, 01 Oct 8307 19:33:20 GMT';
|
|
45
52
|
|
|
53
|
+
// RFC 9111 cache-scope directives; boundaries on both sides so a token like `public-foo` doesn't match
|
|
54
|
+
const SHARED_CACHE_OPTIN = /(^|[,\s])(public|s-maxage)($|[\s,;=])/i;
|
|
55
|
+
const PRIVATE_SCOPE = /(^|[,\s])(private|no-store)($|[\s,;=])/i;
|
|
56
|
+
|
|
46
57
|
let authorizationCache = new Map();
|
|
47
58
|
server.onInvalidatedUser(() => {
|
|
48
59
|
// TODO: Eventually we probably want to be able to invalidate individual users
|
|
@@ -61,6 +72,9 @@ export async function authentication(request, nextHandler) {
|
|
|
61
72
|
const cookie = headers.cookie;
|
|
62
73
|
let origin = headers.origin;
|
|
63
74
|
let responseHeaders = [];
|
|
75
|
+
// a 401 that gets rewritten to a login-redirect 302 below must still be treated as
|
|
76
|
+
// identity-dependent by applyResponseHeaders
|
|
77
|
+
let wasUnauthorized = false;
|
|
64
78
|
try {
|
|
65
79
|
if (origin) {
|
|
66
80
|
const accessList = request.isOperationsServer
|
|
@@ -80,6 +94,8 @@ export async function authentication(request, nextHandler) {
|
|
|
80
94
|
['Access-Control-Allow-Methods', 'POST, GET, PUT, DELETE, PATCH, OPTIONS'],
|
|
81
95
|
['Access-Control-Allow-Headers', accessControlAllowHeaders],
|
|
82
96
|
['Access-Control-Allow-Origin', origin],
|
|
97
|
+
// the preflight response is keyed on the request Origin — a shared cache must partition on it
|
|
98
|
+
['Vary', 'Origin'],
|
|
83
99
|
]);
|
|
84
100
|
if (ENABLE_SESSIONS) headers.set('Access-Control-Allow-Credentials', 'true');
|
|
85
101
|
return {
|
|
@@ -201,6 +217,10 @@ export async function authentication(request, nextHandler) {
|
|
|
201
217
|
case 'Bearer':
|
|
202
218
|
try {
|
|
203
219
|
newUser = await validateOperationToken(credentials);
|
|
220
|
+
// Capture the token's expiry so a live subscription opened with this bearer
|
|
221
|
+
// token can be revoked once it expires (#1414).
|
|
222
|
+
const decoded = decodeJWT(credentials);
|
|
223
|
+
if (newUser && decoded?.exp) newUser.authExpiresAt = decoded.exp;
|
|
204
224
|
} catch (error) {
|
|
205
225
|
if (error.message === 'invalid token') {
|
|
206
226
|
// see if they provided a refresh token; we can allow that and pass it on to operations API
|
|
@@ -315,14 +335,17 @@ export async function authentication(request, nextHandler) {
|
|
|
315
335
|
expiresAt: expires ? Date.now() + convertToMS(expires) : undefined,
|
|
316
336
|
});
|
|
317
337
|
};
|
|
318
|
-
request.login = async function (username: string, password
|
|
319
|
-
const user: any = (request.user =
|
|
338
|
+
request.login = async function (username: string, password?: string, token?: string) {
|
|
339
|
+
const user: any = (request.user = token
|
|
340
|
+
? await validateLoginToken(token)
|
|
341
|
+
: await server.authenticateUser(username, password, request));
|
|
320
342
|
request.session.update({ user: user && (user.getId?.() ?? user.username) });
|
|
321
343
|
};
|
|
322
344
|
}
|
|
323
345
|
const response = await nextHandler(request);
|
|
324
346
|
if (!response) return response;
|
|
325
347
|
if (response.status === 401) {
|
|
348
|
+
wasUnauthorized = true;
|
|
326
349
|
if (
|
|
327
350
|
headers['user-agent']?.startsWith('Mozilla') &&
|
|
328
351
|
headers.accept?.startsWith('text/html') &&
|
|
@@ -340,13 +363,50 @@ export async function authentication(request, nextHandler) {
|
|
|
340
363
|
}
|
|
341
364
|
function applyResponseHeaders(response) {
|
|
342
365
|
const l = responseHeaders.length;
|
|
343
|
-
if
|
|
366
|
+
// a response is identity-dependent if a principal was resolved (Authorization/session/mTLS) or
|
|
367
|
+
// if we are rejecting the credentials (401, possibly rewritten to a login redirect); such a
|
|
368
|
+
// response must never be stored by a shared cache and served to a different principal (#1565)
|
|
369
|
+
const rejectedAuth = response?.status === 401 || wasUnauthorized;
|
|
370
|
+
const identityDependent = !!request.user || rejectedAuth;
|
|
371
|
+
// with CORS enabled the response is origin-dependent — ACAO reflects the request Origin, and
|
|
372
|
+
// its absence when no Origin was sent is origin-dependent too — so a shared cache must
|
|
373
|
+
// partition on Origin either way (#1518)
|
|
374
|
+
const corsEnabled = request.isOperationsServer ? operationsCors : appsCors;
|
|
375
|
+
if ((l > 0 || corsEnabled || identityDependent) && typeof response === 'object') {
|
|
344
376
|
let headers = response.headers;
|
|
345
|
-
|
|
346
|
-
|
|
377
|
+
// normalize plain-object headers so get/set below work uniformly
|
|
378
|
+
if (!headers || typeof headers.set !== 'function') response.headers = headers = new Headers(headers);
|
|
379
|
+
for (let i = 0; i < l;) {
|
|
347
380
|
const name = responseHeaders[i++];
|
|
348
381
|
headers.set(name, responseHeaders[i++]);
|
|
349
382
|
}
|
|
383
|
+
if (corsEnabled) addVaryHeader(headers, 'Origin');
|
|
384
|
+
// #1565: keep identity-dependent responses out of shared caches. An explicit
|
|
385
|
+
// `public`/`s-maxage` set by the app is RFC 9111's opt-in for shared-caching an
|
|
386
|
+
// authenticated response, so that is trusted as-is — but only for successfully
|
|
387
|
+
// authenticated responses; a credential rejection must never opt into shared caching.
|
|
388
|
+
// Otherwise partition on the credential headers and ensure a `private` scope
|
|
389
|
+
// (`private, no-cache` when none was set, so browsers still revalidate against the
|
|
390
|
+
// ETag/Last-Modified we emit).
|
|
391
|
+
if (identityDependent) {
|
|
392
|
+
const existingCacheControl = headers.get('Cache-Control');
|
|
393
|
+
const cacheControlString = existingCacheControl
|
|
394
|
+
? Array.isArray(existingCacheControl)
|
|
395
|
+
? existingCacheControl.join(', ')
|
|
396
|
+
: existingCacheControl
|
|
397
|
+
: '';
|
|
398
|
+
const optedIn = !rejectedAuth && SHARED_CACHE_OPTIN.test(cacheControlString);
|
|
399
|
+
if (!optedIn) {
|
|
400
|
+
addVaryHeader(headers, 'Authorization');
|
|
401
|
+
if (ENABLE_SESSIONS) addVaryHeader(headers, 'Cookie');
|
|
402
|
+
// a 401 never opts into shared caching: any non-private Cache-Control it carries is
|
|
403
|
+
// replaced outright (an existing private/no-store is already at least as strict)
|
|
404
|
+
if (!cacheControlString || (rejectedAuth && !PRIVATE_SCOPE.test(cacheControlString)))
|
|
405
|
+
headers.set('Cache-Control', 'private, no-cache');
|
|
406
|
+
else if (!PRIVATE_SCOPE.test(cacheControlString))
|
|
407
|
+
headers.set('Cache-Control', cacheControlString + ', private');
|
|
408
|
+
}
|
|
409
|
+
}
|
|
350
410
|
}
|
|
351
411
|
responseHeaders = null;
|
|
352
412
|
return response;
|
|
@@ -374,7 +434,7 @@ export async function login(loginObject) {
|
|
|
374
434
|
loginObject.baseResponse.headers.set = (name, value) => {
|
|
375
435
|
loginObject.fastifyResponse.header(name, value);
|
|
376
436
|
};
|
|
377
|
-
await loginObject.baseRequest.login(loginObject.username, loginObject.password ?? '');
|
|
437
|
+
await loginObject.baseRequest.login(loginObject.username, loginObject.password ?? '', loginObject.token);
|
|
378
438
|
return 'Login successful';
|
|
379
439
|
}
|
|
380
440
|
|
package/security/jsLoader.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { dirname, isAbsolute } from 'node:path';
|
|
|
8
8
|
import { pathToFileURL, fileURLToPath } from 'node:url';
|
|
9
9
|
import { SourceTextModule, SyntheticModule, createContext, runInContext, runInThisContext } from 'node:vm';
|
|
10
10
|
import { ApplicationScope } from '../components/ApplicationScope.ts';
|
|
11
|
+
import { getSecretsForComponent, runWithComponentBinding } from '../components/componentSecrets.ts';
|
|
11
12
|
import logger from '../utility/logging/harper_logger.ts';
|
|
12
13
|
import { createRequire } from 'node:module';
|
|
13
14
|
import * as env from '../utility/environment/environmentManager';
|
|
@@ -76,6 +77,13 @@ export async function scopedImport(filePath: string | URL, scope?: ApplicationSc
|
|
|
76
77
|
}
|
|
77
78
|
}
|
|
78
79
|
const moduleUrl = (filePath instanceof URL ? filePath : pathToFileURL(filePath)).toString();
|
|
80
|
+
// Bind the component-load context for the duration of the load, so the process-wide `secrets`
|
|
81
|
+
// export (reached under the native loader, or by natively-loaded dependencies that import the
|
|
82
|
+
// real `harper` package) resolves to this scope's component while its modules evaluate.
|
|
83
|
+
return runWithComponentBinding(scope?.name, () => importScoped(moduleUrl, scope));
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function importScoped(moduleUrl: string, scope?: ApplicationScope) {
|
|
79
87
|
try {
|
|
80
88
|
const loaderMode = scope?.mode;
|
|
81
89
|
if (scope && loaderMode !== 'native') {
|
|
@@ -773,6 +781,7 @@ function getGlobalObject(scope: ApplicationScope, copyIntrinsics = false) {
|
|
|
773
781
|
logger: scope.logger ?? logger,
|
|
774
782
|
resources: scope.resources,
|
|
775
783
|
config: scope.config ?? {},
|
|
784
|
+
secrets: getSecretsForComponent(scope.name),
|
|
776
785
|
fetch: APPLICATIONS_LOCKDOWN === 'ses' ? secureOnlyFetch : fetch,
|
|
777
786
|
console,
|
|
778
787
|
global: appGlobal,
|
|
@@ -790,6 +799,9 @@ function getHarperExports(scope: ApplicationScope) {
|
|
|
790
799
|
logger: scope.logger ?? logger,
|
|
791
800
|
resources: scope.resources,
|
|
792
801
|
config: scope.config ?? {},
|
|
802
|
+
// The scoped-secrets view (#1550), bound exactly to this scope's component — per-scope, unlike
|
|
803
|
+
// the process-wide singletons below.
|
|
804
|
+
secrets: getSecretsForComponent(scope.name),
|
|
793
805
|
Resource,
|
|
794
806
|
tables,
|
|
795
807
|
databases,
|
|
@@ -25,11 +25,20 @@ env.initSync();
|
|
|
25
25
|
type StringValue = SignOptions['expiresIn'];
|
|
26
26
|
const OPERATION_TOKEN_TIMEOUT: StringValue = env.get(CONFIG_PARAMS.AUTHENTICATION_OPERATIONTOKENTIMEOUT) || '1d';
|
|
27
27
|
const REFRESH_TOKEN_TIMEOUT: StringValue = env.get(CONFIG_PARAMS.AUTHENTICATION_REFRESHTOKENTIMEOUT) || '30d';
|
|
28
|
+
// Default lifetime of a login-purpose token (see TOKEN_TYPE.LOGIN below). It only exists to be
|
|
29
|
+
// exchanged for a session cookie, so it defaults far shorter than an operation token; callers can
|
|
30
|
+
// still override via expires_in.
|
|
31
|
+
const LOGIN_TOKEN_TIMEOUT: StringValue = '1m';
|
|
28
32
|
const RSA_ALGORITHM: Algorithm = 'RS256';
|
|
29
33
|
|
|
30
34
|
const TOKEN_TYPE = {
|
|
31
35
|
OPERATION: 'operation',
|
|
32
36
|
REFRESH: 'refresh',
|
|
37
|
+
// Purpose-scoped exchange token minted by createTokens({ purpose: 'login' }) and accepted only
|
|
38
|
+
// by validateLoginToken (the `login` operation). Its `sub` claim differs from
|
|
39
|
+
// TOKEN_TYPE.OPERATION, so validateOperationToken's Bearer-API path rejects it automatically —
|
|
40
|
+
// it can't be replayed as a general API credential the way a full operation token could.
|
|
41
|
+
LOGIN: 'login',
|
|
33
42
|
};
|
|
34
43
|
|
|
35
44
|
interface JWTRSAKeys {
|
|
@@ -45,6 +54,9 @@ interface AuthObject {
|
|
|
45
54
|
expires_in?: string | number;
|
|
46
55
|
bypass_auth?: boolean;
|
|
47
56
|
hdb_user?: User;
|
|
57
|
+
// 'login' mints a single short-lived, login-scoped token instead of an operation/refresh pair —
|
|
58
|
+
// see TOKEN_TYPE.LOGIN.
|
|
59
|
+
purpose?: 'login';
|
|
48
60
|
}
|
|
49
61
|
|
|
50
62
|
interface TokenObject {
|
|
@@ -52,6 +64,8 @@ interface TokenObject {
|
|
|
52
64
|
}
|
|
53
65
|
|
|
54
66
|
interface JWTTokens {
|
|
67
|
+
// Always the mint result: an operation JWT normally, or (purpose: 'login') the login-scoped
|
|
68
|
+
// exchange JWT — same field, same response shape, callers don't need to branch on purpose.
|
|
55
69
|
operation_token: string;
|
|
56
70
|
refresh_token?: string;
|
|
57
71
|
}
|
|
@@ -99,7 +113,8 @@ export function clearJWTRSAKeysCache(): void {
|
|
|
99
113
|
}
|
|
100
114
|
|
|
101
115
|
/**
|
|
102
|
-
* Creates a new operation token and refresh token
|
|
116
|
+
* Creates a new operation token and refresh token (or, with `purpose: 'login'`, a single
|
|
117
|
+
* login-scoped token — see TOKEN_TYPE.LOGIN).
|
|
103
118
|
* If there is no username and password, the hdb_user making the request is used in the token.
|
|
104
119
|
* An optional role can be provided which will be saved in the token payload.
|
|
105
120
|
* The token expires in the time specified in the expires_in field or the default time.
|
|
@@ -113,6 +128,7 @@ export async function createTokens(authObj: AuthObject): Promise<JWTTokens> {
|
|
|
113
128
|
password: Joi.string().optional(),
|
|
114
129
|
role: Joi.string().optional(),
|
|
115
130
|
expires_in: Joi.alternatives(Joi.string(), Joi.number()).optional(),
|
|
131
|
+
purpose: Joi.string().valid('login').optional(),
|
|
116
132
|
})
|
|
117
133
|
);
|
|
118
134
|
if (validation) throw new ClientError(validation.message);
|
|
@@ -147,6 +163,22 @@ export async function createTokens(authObj: AuthObject): Promise<JWTTokens> {
|
|
|
147
163
|
if (authObj.role) payload.role = authObj.role;
|
|
148
164
|
|
|
149
165
|
const keys: JWTRSAKeys = await getJWTRSAKeys();
|
|
166
|
+
|
|
167
|
+
if (authObj.purpose === 'login') {
|
|
168
|
+
// Login-scoped exchange token: no refresh token, no user record update — it's a one-shot
|
|
169
|
+
// ticket for the `login` operation to trade for a session cookie, not a standing credential.
|
|
170
|
+
const loginToken = jwt.sign(
|
|
171
|
+
{ username: authObj.username },
|
|
172
|
+
{ key: keys.privateKey, passphrase: keys.passphrase } satisfies Secret,
|
|
173
|
+
{
|
|
174
|
+
expiresIn: (authObj.expires_in ?? LOGIN_TOKEN_TIMEOUT) as StringValue,
|
|
175
|
+
algorithm: RSA_ALGORITHM,
|
|
176
|
+
subject: TOKEN_TYPE.LOGIN,
|
|
177
|
+
} satisfies SignOptions
|
|
178
|
+
);
|
|
179
|
+
return { operation_token: loginToken };
|
|
180
|
+
}
|
|
181
|
+
|
|
150
182
|
const operationToken = jwt.sign(
|
|
151
183
|
payload,
|
|
152
184
|
{ key: keys.privateKey, passphrase: keys.passphrase } satisfies Secret,
|
|
@@ -219,6 +251,15 @@ export async function validateRefreshToken(token: string): Promise<any> {
|
|
|
219
251
|
return validateToken(token, TOKEN_TYPE.REFRESH);
|
|
220
252
|
}
|
|
221
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Validates a login-purpose token minted via createTokens({ purpose: 'login' }). Used solely by
|
|
256
|
+
* the `login` operation to exchange the token for an httpOnly session cookie — this is the only
|
|
257
|
+
* place a `sub: 'login'` token is accepted.
|
|
258
|
+
*/
|
|
259
|
+
export async function validateLoginToken(token: string): Promise<any> {
|
|
260
|
+
return validateToken(token, TOKEN_TYPE.LOGIN);
|
|
261
|
+
}
|
|
262
|
+
|
|
222
263
|
async function validateToken(token: string, tokenType: string): Promise<any> {
|
|
223
264
|
try {
|
|
224
265
|
const keys: JWTRSAKeys = await getJWTRSAKeys();
|
package/server/DESIGN.md
CHANGED
|
@@ -120,6 +120,16 @@ The default WebSocket upgrade handler is registered automatically inside `onWebS
|
|
|
120
120
|
|
|
121
121
|
`REST.ts → http(request, nextHandler)` is the chief integration point: it takes a `Request`, asks the `Resources` registry for a match, builds a `RequestTarget`, and dispatches into the Resource class's static method. Cache headers are translated to `request.expiresAt` / `onlyIfCached` / `noCache` flags within the same function.
|
|
122
122
|
|
|
123
|
+
### Response Cache-Control / Vary policy (#1518, #1565)
|
|
124
|
+
|
|
125
|
+
Three tiers, applied in two places:
|
|
126
|
+
|
|
127
|
+
1. **App/resource explicit** — a `Cache-Control` set by the resource (or `@table(cacheControl: "...")` for anonymous reads, emitted in `REST.ts → http()`) always wins. The declaration is required: anonymous readability alone never emits shared-cache headers, because a request-attribute-gated `allowRead` (IP, headers) would make inferred `public` unsound.
|
|
128
|
+
2. **Identity floor** — `security/auth.ts → applyResponseHeaders` stamps `Cache-Control: private, no-cache` + `Vary: Authorization` (+ `Cookie` when sessions are on) on any response where a principal was resolved or credentials were rejected (401), _unless_ the app opted into shared caching with `public`/`s-maxage` (the RFC 9111 opt-in).
|
|
129
|
+
3. **CORS partitioning** — when CORS is enabled, every response gets `Vary: Origin` (the ACAO header is reflected per-origin, and its absence on no-Origin requests is origin-dependent too).
|
|
130
|
+
|
|
131
|
+
The `@table(cacheControl:)` value is persisted on the primary-key attribute (like `expiration`), so all threads and future boots see it; `resources/databases.ts → table()` treats `null` as "schema explicitly has none" (clears on reload) and `undefined` as "caller is not schema-defining" (no clobber from `add_attribute`/cluster schema events).
|
|
132
|
+
|
|
123
133
|
---
|
|
124
134
|
|
|
125
135
|
## "Where is X" cheat sheet
|
|
@@ -126,7 +126,11 @@ export async function getSession({
|
|
|
126
126
|
if (will) {
|
|
127
127
|
will.id = sessionId;
|
|
128
128
|
will.user = { username: user?.username };
|
|
129
|
-
|
|
129
|
+
// Must be durably persisted before CONNACK is sent (getSession() resolving is what lets
|
|
130
|
+
// mqtt.ts send CONNACK). Otherwise a client that connects and then disconnects abruptly
|
|
131
|
+
// (no DISCONNECT packet) can race ahead of this write: SubscriptionsSession.disconnect()
|
|
132
|
+
// reads this same record back to publish the will, finds nothing, and silently drops it.
|
|
133
|
+
await getLastWill().put(will);
|
|
130
134
|
}
|
|
131
135
|
if (keepalive) {
|
|
132
136
|
// keep alive is the interval in seconds that the client will send a ping to the server
|
|
@@ -480,8 +484,8 @@ export class DurableSubscriptionsSession extends SubscriptionsSession {
|
|
|
480
484
|
}
|
|
481
485
|
subscription.acks.push(update.timestamp);
|
|
482
486
|
trace('Received ack', topic, update.timestamp);
|
|
483
|
-
|
|
484
|
-
return;
|
|
487
|
+
// add source: true context to bypass any overloaded checks, as skipping this can lead to increased load
|
|
488
|
+
return getDurableSession().put(this.sessionRecord, { source: true });
|
|
485
489
|
}
|
|
486
490
|
}
|
|
487
491
|
}
|
|
@@ -493,7 +497,7 @@ export class DurableSubscriptionsSession extends SubscriptionsSession {
|
|
|
493
497
|
subscription.startTime = update.timestamp;
|
|
494
498
|
}
|
|
495
499
|
}
|
|
496
|
-
getDurableSession().put(this.sessionRecord, { source: true });
|
|
500
|
+
return getDurableSession().put(this.sessionRecord, { source: true });
|
|
497
501
|
// TODO: Increment the timestamp for the corresponding subscription, possibly recording any interim unacked messages
|
|
498
502
|
}
|
|
499
503
|
|
package/server/REST.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { Request } from '../server/serverHelpers/Request.ts';
|
|
|
13
13
|
import { RequestTarget } from '../resources/RequestTarget';
|
|
14
14
|
import { entryMap } from '../resources/RecordEncoder.ts';
|
|
15
15
|
|
|
16
|
-
const { errorToString } = harperLogger;
|
|
16
|
+
const { errorToString, errorForLog } = harperLogger;
|
|
17
17
|
const etagBytes = new Uint8Array(8);
|
|
18
18
|
const etagFloat = new Float64Array(etagBytes.buffer, 0, 1);
|
|
19
19
|
let httpOptions = {};
|
|
@@ -254,6 +254,20 @@ async function http(request: Request, nextHandler) {
|
|
|
254
254
|
headers,
|
|
255
255
|
body: undefined,
|
|
256
256
|
};
|
|
257
|
+
// #1565 converse: a declared `@table(cacheControl: "...")`/static cacheControl is emitted on
|
|
258
|
+
// anonymous reads so a shared cache/CDN can store public responses. Emission requires the
|
|
259
|
+
// explicit declaration — "the anonymous request succeeded" alone doesn't establish that the
|
|
260
|
+
// content is uniformly public (an allowRead gated on IP or headers would leak across a
|
|
261
|
+
// URL-keyed cache). Authenticated responses are excluded here — the auth layer applies a
|
|
262
|
+
// `private` floor to them instead.
|
|
263
|
+
if (!request.user && (method === 'GET' || method === 'HEAD')) {
|
|
264
|
+
const responseStatus = responseObject.status;
|
|
265
|
+
if (responseStatus === 200 || responseStatus === 304) {
|
|
266
|
+
const cacheControl = (resource as any)?.cacheControl;
|
|
267
|
+
// setIfNone: a resource-set Cache-Control (including a full no-store opt-out) always wins
|
|
268
|
+
if (cacheControl) headers.setIfNone('Cache-Control', cacheControl);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
257
271
|
const loadedFromSource = target.loadedFromSource;
|
|
258
272
|
if (loadedFromSource !== undefined) {
|
|
259
273
|
// this appears to be a caching table with a source
|
|
@@ -270,10 +284,13 @@ async function http(request: Request, nextHandler) {
|
|
|
270
284
|
return responseObject;
|
|
271
285
|
} catch (error) {
|
|
272
286
|
error ??= new Error('Unknown error occurred');
|
|
273
|
-
|
|
287
|
+
// a thrown Response short-circuits as the response, the same as returning one
|
|
288
|
+
if (error instanceof Response) return finalizeResponse(error, headers, request.response.status, request);
|
|
289
|
+
// the HTTP status may be carried as `statusCode` (our error classes) or `status` (e.g. a thrown plain object)
|
|
290
|
+
let statusCode = error.statusCode ?? error.status ?? request.response.status;
|
|
274
291
|
if (statusCode) {
|
|
275
|
-
if (statusCode === 500) harperLogger.warn(error);
|
|
276
|
-
else harperLogger.info(error);
|
|
292
|
+
if (statusCode === 500) harperLogger.warn(errorForLog(error));
|
|
293
|
+
else harperLogger.info(errorForLog(error));
|
|
277
294
|
if (statusCode === 405) {
|
|
278
295
|
if (error.method) error.message += ` to handle HTTP method ${error.method.toUpperCase() || ''}`;
|
|
279
296
|
if (error.allow) {
|
|
@@ -281,7 +298,7 @@ async function http(request: Request, nextHandler) {
|
|
|
281
298
|
headers.setIfNone('Allow', error.allow.map((method) => method.toUpperCase()).join(', '));
|
|
282
299
|
}
|
|
283
300
|
}
|
|
284
|
-
} else harperLogger.error(error);
|
|
301
|
+
} else harperLogger.error(errorForLog(error));
|
|
285
302
|
|
|
286
303
|
// RFC 9457 Problem Details
|
|
287
304
|
const status = statusCode || 500;
|
|
@@ -348,7 +365,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
348
365
|
let hasError;
|
|
349
366
|
(ws as any).on('error', (error) => {
|
|
350
367
|
hasError = true;
|
|
351
|
-
harperLogger.warn(error);
|
|
368
|
+
harperLogger.warn(errorForLog(error));
|
|
352
369
|
});
|
|
353
370
|
let deserializer;
|
|
354
371
|
(ws as any).on('message', function message(body) {
|
|
@@ -411,9 +428,9 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
411
428
|
}
|
|
412
429
|
} catch (error) {
|
|
413
430
|
if (error.statusCode) {
|
|
414
|
-
if (error.statusCode === 500) harperLogger.warn(error);
|
|
415
|
-
else harperLogger.info(error);
|
|
416
|
-
} else harperLogger.error(error);
|
|
431
|
+
if (error.statusCode === 500) harperLogger.warn(errorForLog(error));
|
|
432
|
+
else harperLogger.info(errorForLog(error));
|
|
433
|
+
} else harperLogger.error(errorForLog(error));
|
|
417
434
|
ws.close(
|
|
418
435
|
HTTP_TO_WEBSOCKET_CLOSE_CODES[error.statusCode] || // try to return a helpful code
|
|
419
436
|
1011, // otherwise generic internal error
|
package/server/fastifyRoutes.ts
CHANGED
|
@@ -17,6 +17,7 @@ import getHeaderTimeoutConfig from './fastifyRoutes/helpers/getHeaderTimeoutConf
|
|
|
17
17
|
import { serverErrorHandler } from '../server/serverHelpers/serverHandlers.js';
|
|
18
18
|
import { registerContentHandlers } from '../server/serverHelpers/contentTypes.ts';
|
|
19
19
|
import { server } from './Server.ts';
|
|
20
|
+
import { registerFastifyInstance } from './http.ts';
|
|
20
21
|
|
|
21
22
|
let fastifyServer;
|
|
22
23
|
const routeFolders = new Set();
|
|
@@ -44,7 +45,15 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
44
45
|
}
|
|
45
46
|
if (!fastifyServer) {
|
|
46
47
|
fastifyServer = buildServer(isHttps);
|
|
47
|
-
|
|
48
|
+
const built = await fastifyServer;
|
|
49
|
+
server.http(built.server);
|
|
50
|
+
// Register the Fastify app for the http port(s). On plain Node the http.Server above
|
|
51
|
+
// cascades unhandled requests via the 'unhandled' event, but the Bun and uWS backends bind
|
|
52
|
+
// those ports themselves and have no Node http.Server to hand off to — they delegate to
|
|
53
|
+
// these legacy routes via inject() and look the instance up by port (see injectToFastify).
|
|
54
|
+
for (const port of [env.get(CONFIG_PARAMS.HTTP_PORT), env.get(CONFIG_PARAMS.HTTP_SECUREPORT)]) {
|
|
55
|
+
if (port != null) registerFastifyInstance(port, built);
|
|
56
|
+
}
|
|
48
57
|
}
|
|
49
58
|
const resolvedServer = await fastifyServer;
|
|
50
59
|
const routeFolder = dirname(entry.absolutePath);
|
|
@@ -559,8 +559,9 @@ async function graphqlQueryingHandler(request: Request) {
|
|
|
559
559
|
}
|
|
560
560
|
case 'POST': {
|
|
561
561
|
const requestBodyDeserialize = getDeserializer(request.headers.get('content-type'), true);
|
|
562
|
-
//
|
|
563
|
-
|
|
562
|
+
// Read the body through request.body (as REST.ts does): it is a Readable-compatible
|
|
563
|
+
// body stream on every adapter, whereas _nodeRequest is null on the Bun/uWS adapters.
|
|
564
|
+
const requestParams = await requestBodyDeserialize(request.body as any);
|
|
564
565
|
assertRequestParams(requestParams);
|
|
565
566
|
return resolver(requestParams, request);
|
|
566
567
|
}
|
|
@@ -582,7 +583,7 @@ export function handleApplication(scope: import('../components/Scope.ts').Scope)
|
|
|
582
583
|
// Await the `graphqlHandler` call here so that errors are caught.
|
|
583
584
|
return await graphqlQueryingHandler(request as any);
|
|
584
585
|
} catch (error) {
|
|
585
|
-
logger.error(error);
|
|
586
|
+
logger.error(logger.errorForLog(error));
|
|
586
587
|
|
|
587
588
|
// Error Handling
|
|
588
589
|
// Based on the GraphQL specification, a GraphQL response (non-http) are a map with a `data` field and an `errors` field.
|