effect 4.0.2 → 4.0.4
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/AGENTS.md +24 -0
- package/CLAUDE.md +24 -0
- package/ai-docs/package.json +2 -2
- package/ai-docs/src/52_graphql/10_basics.ts +111 -0
- package/ai-docs/src/52_graphql/20_testing.ts +45 -0
- package/ai-docs/src/52_graphql/30_batching.ts +116 -0
- package/ai-docs/src/52_graphql/40_aliased-batching.ts +122 -0
- package/ai-docs/src/52_graphql/50_errors-and-retries.ts +74 -0
- package/ai-docs/src/52_graphql/index.md +6 -0
- package/dist/Context.d.ts.map +1 -1
- package/dist/Context.js +12 -3
- package/dist/Context.js.map +1 -1
- package/dist/Crypto.d.ts +33 -5
- package/dist/Crypto.d.ts.map +1 -1
- package/dist/Crypto.js +8 -2
- package/dist/Crypto.js.map +1 -1
- package/dist/DateTime.d.ts +9 -6
- package/dist/DateTime.d.ts.map +1 -1
- package/dist/DateTime.js +9 -6
- package/dist/DateTime.js.map +1 -1
- package/dist/Duration.d.ts +6 -6
- package/dist/Duration.d.ts.map +1 -1
- package/dist/Duration.js +27 -8
- package/dist/Duration.js.map +1 -1
- package/dist/FileSystem.d.ts +44 -2
- package/dist/FileSystem.d.ts.map +1 -1
- package/dist/FileSystem.js.map +1 -1
- package/dist/RequestResolver.d.ts.map +1 -1
- package/dist/RequestResolver.js +4 -10
- package/dist/RequestResolver.js.map +1 -1
- package/dist/Scheduler.d.ts +9 -0
- package/dist/Scheduler.d.ts.map +1 -1
- package/dist/Scheduler.js +9 -0
- package/dist/Scheduler.js.map +1 -1
- package/dist/Schema.d.ts +481 -1
- package/dist/Schema.d.ts.map +1 -1
- package/dist/Schema.js +362 -4
- package/dist/Schema.js.map +1 -1
- package/dist/SchemaGetter.d.ts +4 -0
- package/dist/SchemaGetter.d.ts.map +1 -1
- package/dist/SchemaGetter.js +52 -33
- package/dist/SchemaGetter.js.map +1 -1
- package/dist/Sink.d.ts.map +1 -1
- package/dist/Sink.js +38 -17
- package/dist/Sink.js.map +1 -1
- package/dist/Stdio.d.ts +7 -1
- package/dist/Stdio.d.ts.map +1 -1
- package/dist/Stdio.js +1 -0
- package/dist/Stdio.js.map +1 -1
- package/dist/Stream.d.ts +22 -11
- package/dist/Stream.d.ts.map +1 -1
- package/dist/Stream.js +21 -6
- package/dist/Stream.js.map +1 -1
- package/dist/Utils.js +8 -3
- package/dist/Utils.js.map +1 -1
- package/dist/ai/AnthropicStructuredOutput.js +0 -2
- package/dist/ai/AnthropicStructuredOutput.js.map +1 -1
- package/dist/ai/Chat.d.ts +2 -2
- package/dist/ai/Chat.d.ts.map +1 -1
- package/dist/ai/LanguageModel.d.ts +38 -7
- package/dist/ai/LanguageModel.d.ts.map +1 -1
- package/dist/ai/LanguageModel.js +25 -7
- package/dist/ai/LanguageModel.js.map +1 -1
- package/dist/ai/McpServer.d.ts +61 -0
- package/dist/ai/McpServer.d.ts.map +1 -1
- package/dist/ai/McpServer.js +196 -44
- package/dist/ai/McpServer.js.map +1 -1
- package/dist/ai/Prompt.d.ts.map +1 -1
- package/dist/ai/Prompt.js +28 -5
- package/dist/ai/Prompt.js.map +1 -1
- package/dist/ai/Response.d.ts +4 -3
- package/dist/ai/Response.d.ts.map +1 -1
- package/dist/ai/Response.js.map +1 -1
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js +5 -0
- package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
- package/dist/ai/internal/mcpProtocol.js.map +1 -1
- package/dist/ai/internal/mcpRuntime.js +1 -0
- package/dist/ai/internal/mcpRuntime.js.map +1 -1
- package/dist/cli/CliOutput.d.ts +27 -0
- package/dist/cli/CliOutput.d.ts.map +1 -1
- package/dist/cli/CliOutput.js +33 -10
- package/dist/cli/CliOutput.js.map +1 -1
- package/dist/cli/Command.d.ts.map +1 -1
- package/dist/cli/Command.js +6 -3
- package/dist/cli/Command.js.map +1 -1
- package/dist/cli/Completions.d.ts +3 -3
- package/dist/cli/Completions.d.ts.map +1 -1
- package/dist/cli/Completions.js +8 -5
- package/dist/cli/Completions.js.map +1 -1
- package/dist/cli/GlobalFlag.d.ts +5 -4
- package/dist/cli/GlobalFlag.d.ts.map +1 -1
- package/dist/cli/GlobalFlag.js +3 -3
- package/dist/cli/GlobalFlag.js.map +1 -1
- package/dist/cli/Prompt.d.ts +17 -1
- package/dist/cli/Prompt.d.ts.map +1 -1
- package/dist/cli/Prompt.js +119 -114
- package/dist/cli/Prompt.js.map +1 -1
- package/dist/cli/internal/ansi.d.ts +0 -1
- package/dist/cli/internal/ansi.d.ts.map +1 -1
- package/dist/cli/internal/ansi.js +20 -3
- package/dist/cli/internal/ansi.js.map +1 -1
- package/dist/cli/internal/completions/powershell.d.ts +2 -0
- package/dist/cli/internal/completions/powershell.d.ts.map +1 -0
- package/dist/cli/internal/completions/powershell.js +273 -0
- package/dist/cli/internal/completions/powershell.js.map +1 -0
- package/dist/cli/internal/completions/zsh.js +3 -2
- package/dist/cli/internal/completions/zsh.js.map +1 -1
- package/dist/cli/internal/wizard.d.ts +3 -3
- package/dist/cli/internal/wizard.d.ts.map +1 -1
- package/dist/cli/internal/wizard.js +21 -18
- package/dist/cli/internal/wizard.js.map +1 -1
- package/dist/cluster/ClusterSchema.d.ts +28 -0
- package/dist/cluster/ClusterSchema.d.ts.map +1 -1
- package/dist/cluster/ClusterSchema.js +30 -0
- package/dist/cluster/ClusterSchema.js.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
- package/dist/cluster/ClusterWorkflowEngine.js +56 -26
- package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
- package/dist/cluster/MessageStorage.d.ts +18 -3
- package/dist/cluster/MessageStorage.d.ts.map +1 -1
- package/dist/cluster/MessageStorage.js +31 -11
- package/dist/cluster/MessageStorage.js.map +1 -1
- package/dist/cluster/Runners.d.ts.map +1 -1
- package/dist/cluster/Runners.js +8 -2
- package/dist/cluster/Runners.js.map +1 -1
- package/dist/cluster/Sharding.d.ts.map +1 -1
- package/dist/cluster/Sharding.js +187 -66
- package/dist/cluster/Sharding.js.map +1 -1
- package/dist/cluster/SqlMessageStorage.d.ts +59 -19
- package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
- package/dist/cluster/SqlMessageStorage.js +84 -55
- package/dist/cluster/SqlMessageStorage.js.map +1 -1
- package/dist/cluster/SqlRunnerStorage.d.ts +68 -19
- package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
- package/dist/cluster/SqlRunnerStorage.js +191 -113
- package/dist/cluster/SqlRunnerStorage.js.map +1 -1
- package/dist/cluster/internal/entityManager.js +104 -37
- package/dist/cluster/internal/entityManager.js.map +1 -1
- package/dist/encoding/Sse.d.ts +6 -0
- package/dist/encoding/Sse.d.ts.map +1 -1
- package/dist/encoding/Sse.js +62 -70
- package/dist/encoding/Sse.js.map +1 -1
- package/dist/graphql/GraphQL.d.ts +349 -0
- package/dist/graphql/GraphQL.d.ts.map +1 -0
- package/dist/graphql/GraphQL.js +207 -0
- package/dist/graphql/GraphQL.js.map +1 -0
- package/dist/graphql/GraphQLClient.d.ts +562 -0
- package/dist/graphql/GraphQLClient.d.ts.map +1 -0
- package/dist/graphql/GraphQLClient.js +498 -0
- package/dist/graphql/GraphQLClient.js.map +1 -0
- package/dist/graphql/GraphQLClientError.d.ts +445 -0
- package/dist/graphql/GraphQLClientError.d.ts.map +1 -0
- package/dist/graphql/GraphQLClientError.js +479 -0
- package/dist/graphql/GraphQLClientError.js.map +1 -0
- package/dist/graphql/GraphQLGroup.d.ts +118 -0
- package/dist/graphql/GraphQLGroup.d.ts.map +1 -0
- package/dist/graphql/GraphQLGroup.js +109 -0
- package/dist/graphql/GraphQLGroup.js.map +1 -0
- package/dist/graphql/GraphQLMiddleware.d.ts +249 -0
- package/dist/graphql/GraphQLMiddleware.d.ts.map +1 -0
- package/dist/graphql/GraphQLMiddleware.js +130 -0
- package/dist/graphql/GraphQLMiddleware.js.map +1 -0
- package/dist/graphql/GraphQLProtocol.d.ts +731 -0
- package/dist/graphql/GraphQLProtocol.d.ts.map +1 -0
- package/dist/graphql/GraphQLProtocol.js +719 -0
- package/dist/graphql/GraphQLProtocol.js.map +1 -0
- package/dist/graphql/GraphQLResolver.d.ts +453 -0
- package/dist/graphql/GraphQLResolver.d.ts.map +1 -0
- package/dist/graphql/GraphQLResolver.js +552 -0
- package/dist/graphql/GraphQLResolver.js.map +1 -0
- package/dist/graphql/index.d.ts +40 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +41 -0
- package/dist/graphql/index.js.map +1 -0
- package/dist/http/FindMyWay/internal/router.js +3 -6
- package/dist/http/FindMyWay/internal/router.js.map +1 -1
- package/dist/http/HttpClient.d.ts +21 -0
- package/dist/http/HttpClient.d.ts.map +1 -1
- package/dist/http/HttpClient.js +63 -12
- package/dist/http/HttpClient.js.map +1 -1
- package/dist/http/HttpEffect.d.ts.map +1 -1
- package/dist/http/HttpEffect.js +14 -7
- package/dist/http/HttpEffect.js.map +1 -1
- package/dist/http/HttpMiddleware.d.ts.map +1 -1
- package/dist/http/HttpMiddleware.js +31 -32
- package/dist/http/HttpMiddleware.js.map +1 -1
- package/dist/http/HttpRouter.d.ts +35 -0
- package/dist/http/HttpRouter.d.ts.map +1 -1
- package/dist/http/HttpRouter.js +35 -0
- package/dist/http/HttpRouter.js.map +1 -1
- package/dist/http/HttpServerResponse.d.ts +8 -6
- package/dist/http/HttpServerResponse.d.ts.map +1 -1
- package/dist/http/HttpServerResponse.js +27 -16
- package/dist/http/HttpServerResponse.js.map +1 -1
- package/dist/http/HttpTraceContext.d.ts.map +1 -1
- package/dist/http/HttpTraceContext.js +4 -2
- package/dist/http/HttpTraceContext.js.map +1 -1
- package/dist/http/Multipart.d.ts.map +1 -1
- package/dist/http/Multipart.js +15 -1
- package/dist/http/Multipart.js.map +1 -1
- package/dist/http-api/HttpApiScalar.d.ts +5 -0
- package/dist/http-api/HttpApiScalar.d.ts.map +1 -1
- package/dist/http-api/HttpApiScalar.js.map +1 -1
- package/dist/internal/dateTime.js +40 -35
- package/dist/internal/dateTime.js.map +1 -1
- package/dist/internal/effect.js +19 -9
- package/dist/internal/effect.js.map +1 -1
- package/dist/internal/graphql.d.ts +2 -0
- package/dist/internal/graphql.d.ts.map +1 -0
- package/dist/internal/graphql.js +195 -0
- package/dist/internal/graphql.js.map +1 -0
- package/dist/internal/graphqlHeaders.d.ts +4 -0
- package/dist/internal/graphqlHeaders.d.ts.map +1 -0
- package/dist/internal/graphqlHeaders.js +11 -0
- package/dist/internal/graphqlHeaders.js.map +1 -0
- package/dist/internal/request.js +20 -1
- package/dist/internal/request.js.map +1 -1
- package/dist/internal/schema/toCodeDocument.js +4 -1
- package/dist/internal/schema/toCodeDocument.js.map +1 -1
- package/dist/internal/schema/toJsonSchemaDocument.js +29 -5
- package/dist/internal/schema/toJsonSchemaDocument.js.map +1 -1
- package/dist/internal/version.js +1 -1
- package/dist/net/AddressResolver.d.ts +145 -0
- package/dist/net/AddressResolver.d.ts.map +1 -0
- package/dist/net/AddressResolver.js +114 -0
- package/dist/net/AddressResolver.js.map +1 -0
- package/dist/net/Dns.d.ts +596 -0
- package/dist/net/Dns.d.ts.map +1 -0
- package/dist/net/Dns.js +549 -0
- package/dist/net/Dns.js.map +1 -0
- package/dist/net/Host.d.ts +369 -0
- package/dist/net/Host.d.ts.map +1 -0
- package/dist/net/Host.js +397 -0
- package/dist/net/Host.js.map +1 -0
- package/dist/net/IpInterface.d.ts +76 -3
- package/dist/net/IpInterface.d.ts.map +1 -1
- package/dist/net/IpInterface.js +53 -4
- package/dist/net/IpInterface.js.map +1 -1
- package/dist/net/IpNetwork.d.ts +82 -8
- package/dist/net/IpNetwork.d.ts.map +1 -1
- package/dist/net/IpNetwork.js +60 -4
- package/dist/net/IpNetwork.js.map +1 -1
- package/dist/net/NetAddress.d.ts +396 -35
- package/dist/net/NetAddress.d.ts.map +1 -1
- package/dist/net/NetAddress.js +358 -34
- package/dist/net/NetAddress.js.map +1 -1
- package/dist/net/index.d.ts +15 -0
- package/dist/net/index.d.ts.map +1 -1
- package/dist/net/index.js +15 -0
- package/dist/net/index.js.map +1 -1
- package/dist/observability/OtlpMetrics.d.ts.map +1 -1
- package/dist/observability/OtlpMetrics.js +15 -21
- package/dist/observability/OtlpMetrics.js.map +1 -1
- package/dist/observability/PrometheusMetrics.js +1 -0
- package/dist/observability/PrometheusMetrics.js.map +1 -1
- package/dist/persistence/PersistedQueue.d.ts +32 -3
- package/dist/persistence/PersistedQueue.d.ts.map +1 -1
- package/dist/persistence/PersistedQueue.js +75 -16
- package/dist/persistence/PersistedQueue.js.map +1 -1
- package/dist/rpc/RpcClient.d.ts +22 -6
- package/dist/rpc/RpcClient.d.ts.map +1 -1
- package/dist/rpc/RpcClient.js +12 -5
- package/dist/rpc/RpcClient.js.map +1 -1
- package/dist/rpc/RpcServer.d.ts +6 -0
- package/dist/rpc/RpcServer.d.ts.map +1 -1
- package/dist/rpc/RpcServer.js +78 -29
- package/dist/rpc/RpcServer.js.map +1 -1
- package/dist/socket/DatagramSocket.d.ts +456 -0
- package/dist/socket/DatagramSocket.d.ts.map +1 -0
- package/dist/socket/DatagramSocket.js +535 -0
- package/dist/socket/DatagramSocket.js.map +1 -0
- package/dist/socket/Socket.d.ts +20 -1
- package/dist/socket/Socket.d.ts.map +1 -1
- package/dist/socket/Socket.js +9 -1
- package/dist/socket/Socket.js.map +1 -1
- package/dist/socket/index.d.ts +5 -0
- package/dist/socket/index.d.ts.map +1 -1
- package/dist/socket/index.js +5 -0
- package/dist/socket/index.js.map +1 -1
- package/dist/sql/Migrator.d.ts.map +1 -1
- package/dist/sql/Migrator.js +7 -2
- package/dist/sql/Migrator.js.map +1 -1
- package/dist/sql/SqlClient.d.ts +7 -1
- package/dist/sql/SqlClient.d.ts.map +1 -1
- package/dist/sql/SqlClient.js +31 -6
- package/dist/sql/SqlClient.js.map +1 -1
- package/dist/testing/TestCrypto.d.ts +73 -0
- package/dist/testing/TestCrypto.d.ts.map +1 -0
- package/dist/testing/TestCrypto.js +87 -0
- package/dist/testing/TestCrypto.js.map +1 -0
- package/dist/testing/index.d.ts +5 -0
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +5 -0
- package/dist/testing/index.js.map +1 -1
- package/dist/workflow/DurableDeferred.d.ts +54 -0
- package/dist/workflow/DurableDeferred.d.ts.map +1 -1
- package/dist/workflow/DurableDeferred.js +108 -50
- package/dist/workflow/DurableDeferred.js.map +1 -1
- package/dist/workflow/WorkflowEngine.d.ts +24 -2
- package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
- package/dist/workflow/WorkflowEngine.js +13 -3
- package/dist/workflow/WorkflowEngine.js.map +1 -1
- package/package.json +6 -3
- package/src/Context.ts +12 -3
- package/src/Crypto.ts +43 -9
- package/src/DateTime.ts +9 -6
- package/src/Duration.ts +36 -16
- package/src/FileSystem.ts +48 -2
- package/src/RequestResolver.ts +4 -10
- package/src/Scheduler.ts +9 -0
- package/src/Schema.ts +677 -5
- package/src/SchemaGetter.ts +47 -35
- package/src/Sink.ts +45 -18
- package/src/Stdio.ts +9 -2
- package/src/Stream.ts +44 -15
- package/src/Utils.ts +8 -5
- package/src/ai/AnthropicStructuredOutput.ts +2 -2
- package/src/ai/Chat.ts +2 -2
- package/src/ai/LanguageModel.ts +96 -24
- package/src/ai/McpServer.ts +288 -94
- package/src/ai/Prompt.ts +24 -10
- package/src/ai/Response.ts +6 -3
- package/src/ai/internal/mcpProtocol/v2026_07_28.ts +5 -0
- package/src/ai/internal/mcpProtocol.ts +3 -0
- package/src/ai/internal/mcpRuntime.ts +2 -0
- package/src/cli/CliOutput.ts +33 -16
- package/src/cli/Command.ts +4 -3
- package/src/cli/Completions.ts +9 -6
- package/src/cli/GlobalFlag.ts +19 -11
- package/src/cli/Prompt.ts +159 -149
- package/src/cli/internal/ansi.ts +26 -3
- package/src/cli/internal/completions/powershell.ts +322 -0
- package/src/cli/internal/completions/zsh.ts +4 -2
- package/src/cli/internal/wizard.ts +34 -26
- package/src/cluster/ClusterSchema.ts +32 -0
- package/src/cluster/ClusterWorkflowEngine.ts +64 -36
- package/src/cluster/MessageStorage.ts +45 -14
- package/src/cluster/Runners.ts +8 -2
- package/src/cluster/Sharding.ts +219 -58
- package/src/cluster/SqlMessageStorage.ts +118 -84
- package/src/cluster/SqlRunnerStorage.ts +224 -128
- package/src/cluster/internal/entityManager.ts +151 -55
- package/src/encoding/Sse.ts +69 -79
- package/src/graphql/GraphQL.ts +494 -0
- package/src/graphql/GraphQLClient.ts +912 -0
- package/src/graphql/GraphQLClientError.ts +542 -0
- package/src/graphql/GraphQLGroup.ts +167 -0
- package/src/graphql/GraphQLMiddleware.ts +253 -0
- package/src/graphql/GraphQLProtocol.ts +1286 -0
- package/src/graphql/GraphQLResolver.ts +1010 -0
- package/src/graphql/index.ts +48 -0
- package/src/http/FindMyWay/internal/router.ts +4 -7
- package/src/http/HttpClient.ts +110 -12
- package/src/http/HttpEffect.ts +11 -5
- package/src/http/HttpMiddleware.ts +33 -34
- package/src/http/HttpRouter.ts +35 -0
- package/src/http/HttpServerResponse.ts +28 -16
- package/src/http/HttpTraceContext.ts +8 -2
- package/src/http/Multipart.ts +17 -1
- package/src/http-api/HttpApiScalar.ts +5 -0
- package/src/internal/dateTime.ts +13 -10
- package/src/internal/effect.ts +22 -15
- package/src/internal/graphql.ts +263 -0
- package/src/internal/graphqlHeaders.ts +11 -0
- package/src/internal/request.ts +31 -3
- package/src/internal/schema/toCodeDocument.ts +7 -1
- package/src/internal/schema/toJsonSchemaDocument.ts +34 -2
- package/src/internal/version.ts +1 -1
- package/src/net/AddressResolver.ts +222 -0
- package/src/net/Dns.ts +1035 -0
- package/src/net/Host.ts +544 -0
- package/src/net/IpInterface.ts +117 -7
- package/src/net/IpNetwork.ts +116 -17
- package/src/net/NetAddress.ts +620 -76
- package/src/net/index.ts +18 -0
- package/src/observability/OtlpMetrics.ts +12 -23
- package/src/observability/PrometheusMetrics.ts +1 -0
- package/src/persistence/PersistedQueue.ts +146 -53
- package/src/rpc/RpcClient.ts +41 -19
- package/src/rpc/RpcServer.ts +115 -40
- package/src/socket/DatagramSocket.ts +919 -0
- package/src/socket/Socket.ts +28 -2
- package/src/socket/index.ts +6 -0
- package/src/sql/Migrator.ts +21 -5
- package/src/sql/SqlClient.ts +39 -15
- package/src/testing/TestCrypto.ts +94 -0
- package/src/testing/index.ts +6 -0
- package/src/workflow/DurableDeferred.ts +160 -47
- package/src/workflow/WorkflowEngine.ts +50 -5
|
@@ -86,6 +86,10 @@ export interface Residency {
|
|
|
86
86
|
readonly releaseUnsafe: () => void
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
interface RequestTransaction {
|
|
90
|
+
settle?: ((outcome: Exit.Exit<unknown, unknown>) => Effect.Effect<void>) | undefined
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
// Represents the entities managed by this entity manager
|
|
90
94
|
/**
|
|
91
95
|
* @internal
|
|
@@ -100,10 +104,13 @@ export type EntityState = {
|
|
|
100
104
|
/** Excludes requests awaiting their first dispatch from replay. */
|
|
101
105
|
delivered: boolean
|
|
102
106
|
sentExit: boolean
|
|
107
|
+
/** Treat early termination interrupts like shutdown interrupts. */
|
|
108
|
+
terminating?: boolean | undefined
|
|
103
109
|
lastSentChunk: Option.Option<Reply.Chunk<Rpc.Any>>
|
|
104
110
|
sequence: number
|
|
105
111
|
/** Set when the request should not outlive its caller. */
|
|
106
112
|
callerScope?: Scope.Scope | undefined
|
|
113
|
+
transaction?: RequestTransaction | undefined
|
|
107
114
|
}>
|
|
108
115
|
lastActiveCheck: number
|
|
109
116
|
write: RpcServer.RpcServer<any>["write"]
|
|
@@ -152,9 +159,13 @@ export const make = Effect.fnUntraced(function*<
|
|
|
152
159
|
entityRpcs.set(KeepAliveRpc._tag, KeepAliveRpc as any)
|
|
153
160
|
|
|
154
161
|
const activeServers = new Map<EntityId, EntityState>()
|
|
162
|
+
// Entities finishing their in-flight requests after removal, which still
|
|
163
|
+
// accept interrupts and acks
|
|
164
|
+
const drainingServers = new Map<EntityId, EntityState>()
|
|
155
165
|
const serverCloseLatches = new Map<EntityAddress, {
|
|
156
166
|
readonly closed: Latch.Latch
|
|
157
167
|
readonly force: Latch.Latch
|
|
168
|
+
closing: boolean
|
|
158
169
|
}>()
|
|
159
170
|
const processedRequestIds = new Set<Snowflake.Snowflake>()
|
|
160
171
|
|
|
@@ -181,7 +192,8 @@ export const make = Effect.fnUntraced(function*<
|
|
|
181
192
|
const keepAliveLatch = Latch.makeUnsafe()
|
|
182
193
|
const closeLatches = {
|
|
183
194
|
closed: Latch.makeUnsafe(),
|
|
184
|
-
force: Latch.makeUnsafe()
|
|
195
|
+
force: Latch.makeUnsafe(),
|
|
196
|
+
closing: false
|
|
185
197
|
}
|
|
186
198
|
|
|
187
199
|
yield* Scope.addFinalizer(
|
|
@@ -264,22 +276,23 @@ export const make = Effect.fnUntraced(function*<
|
|
|
264
276
|
// They will be retried when the entity is restarted.
|
|
265
277
|
// Also, if the request is uninterruptible, we ignore the
|
|
266
278
|
// interrupt.
|
|
279
|
+
const persisted = storageEnabled && Context.get(request.message.annotations, Persisted)
|
|
267
280
|
if (
|
|
268
|
-
|
|
269
|
-
Context.get(request.message.annotations, Persisted) &&
|
|
281
|
+
persisted &&
|
|
270
282
|
Exit.hasInterrupts(response.exit) &&
|
|
271
|
-
(isShuttingDown || isUninterruptibleForServer(request.message.annotations))
|
|
283
|
+
(isShuttingDown || request.terminating || isUninterruptibleForServer(request.message.annotations))
|
|
272
284
|
) {
|
|
273
|
-
if (!isShuttingDown) {
|
|
285
|
+
if (!isShuttingDown && !request.terminating) {
|
|
274
286
|
request.sentExit = false
|
|
275
287
|
return server.write(0, requestEnvelope(request), requestWriteOptions(request)).pipe(
|
|
288
|
+
Effect.setContext(handlerContext),
|
|
276
289
|
Effect.forkIn(handlerScope)
|
|
277
290
|
)
|
|
278
291
|
}
|
|
279
292
|
activeRequests.delete(Snowflake.Snowflake(response.requestId))
|
|
280
293
|
return options.storage.unregisterReplyHandler(request.message.envelope.requestId)
|
|
281
294
|
}
|
|
282
|
-
|
|
295
|
+
const save = retryRespond(
|
|
283
296
|
4,
|
|
284
297
|
Effect.suspend(() =>
|
|
285
298
|
request.message.respond(
|
|
@@ -290,22 +303,53 @@ export const make = Effect.fnUntraced(function*<
|
|
|
290
303
|
})
|
|
291
304
|
)
|
|
292
305
|
)
|
|
293
|
-
)
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
306
|
+
)
|
|
307
|
+
const complete = Effect.sync(() => {
|
|
308
|
+
if (storageEnabled) {
|
|
309
|
+
processedRequestIds.add(request.message.envelope.requestId)
|
|
310
|
+
}
|
|
311
|
+
activeRequests.delete(Snowflake.Snowflake(response.requestId))
|
|
299
312
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
313
|
+
// Start the idle timer when the last request completes.
|
|
314
|
+
if (activeRequests.size === 0) {
|
|
315
|
+
state.lastActiveCheck = clock.currentTimeMillisUnsafe()
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
const respond = Effect.orDie(Effect.andThen(save, complete))
|
|
319
|
+
const transaction = request.transaction
|
|
320
|
+
if (!transaction) return respond
|
|
321
|
+
const exit = response.exit
|
|
322
|
+
|
|
323
|
+
if (!persisted) {
|
|
324
|
+
if (Exit.isSuccess(exit)) return respond
|
|
325
|
+
transaction.settle = () => respond
|
|
326
|
+
return Effect.void
|
|
327
|
+
}
|
|
304
328
|
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
329
|
+
if (Exit.isSuccess(exit)) {
|
|
330
|
+
transaction.settle = (outcome) =>
|
|
331
|
+
Exit.isSuccess(outcome) ? complete : Effect.flatMap(
|
|
332
|
+
hasStoredExit(request.message.envelope.requestId),
|
|
333
|
+
(committed) => {
|
|
334
|
+
if (!committed) return restartFrom(outcome.cause)
|
|
335
|
+
// Commit succeeded despite the error; let callers read the saved reply.
|
|
336
|
+
return Effect.andThen(
|
|
337
|
+
complete,
|
|
338
|
+
options.storage.unregisterReplyHandler(request.message.envelope.requestId)
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
)
|
|
342
|
+
return Effect.orDie(save)
|
|
343
|
+
}
|
|
344
|
+
transaction.settle = (outcome) =>
|
|
345
|
+
// Transaction wrappers can add defects, but not typed errors; interrupts may be repeated.
|
|
346
|
+
Exit.isSuccess(outcome) || !outcome.cause.reasons.some((reason) =>
|
|
347
|
+
Cause.isDieReason(reason) &&
|
|
348
|
+
!exit.cause.reasons.some((r) => Cause.isDieReason(r) && r.defect === reason.defect)
|
|
349
|
+
)
|
|
350
|
+
? respond
|
|
351
|
+
: restartFrom(outcome.cause)
|
|
352
|
+
return Effect.void
|
|
309
353
|
}
|
|
310
354
|
case "Chunk": {
|
|
311
355
|
const request = activeRequests.get(Snowflake.Snowflake(response.requestId))
|
|
@@ -330,10 +374,7 @@ export const make = Effect.fnUntraced(function*<
|
|
|
330
374
|
))
|
|
331
375
|
}
|
|
332
376
|
case "Defect": {
|
|
333
|
-
|
|
334
|
-
const rebuild = writeRef.rebuildUnsafe({ from: server.write, prepare: replay })
|
|
335
|
-
if (!rebuild) return Effect.void
|
|
336
|
-
return Effect.forkIn(restart(Cause.die(response.defect), rebuild), managerScope)
|
|
377
|
+
return restartFrom(Cause.die(response.defect))
|
|
337
378
|
}
|
|
338
379
|
case "ClientEnd": {
|
|
339
380
|
return endLatch.open
|
|
@@ -345,6 +386,21 @@ export const make = Effect.fnUntraced(function*<
|
|
|
345
386
|
Effect.setContext(Context.merge(handlerContext, handlers))
|
|
346
387
|
)
|
|
347
388
|
|
|
389
|
+
// Replay if the stored reply cannot be read.
|
|
390
|
+
const hasStoredExit = (requestId: Snowflake.Snowflake): Effect.Effect<boolean> =>
|
|
391
|
+
options.storage.repliesForUnfiltered([requestId]).pipe(
|
|
392
|
+
Effect.map((replies) => replies.some((reply) => reply._tag === "WithExit")),
|
|
393
|
+
Effect.catchCause(() => Effect.succeed(false))
|
|
394
|
+
)
|
|
395
|
+
|
|
396
|
+
// Do not inherit the failed handler's transaction context.
|
|
397
|
+
const restartFrom = (cause: Cause.Cause<unknown>): Effect.Effect<void> => {
|
|
398
|
+
if (!isActive()) return endLatch.open
|
|
399
|
+
const rebuild = writeRef.rebuildUnsafe({ from: server.write, prepare: replay })
|
|
400
|
+
if (!rebuild) return Effect.void
|
|
401
|
+
return Effect.forkIn(Effect.setContext(restart(cause, rebuild), handlerContext), managerScope)
|
|
402
|
+
}
|
|
403
|
+
|
|
348
404
|
yield* Scope.addFinalizer(
|
|
349
405
|
handlerScope,
|
|
350
406
|
Effect.sync(() => {
|
|
@@ -415,15 +471,32 @@ export const make = Effect.fnUntraced(function*<
|
|
|
415
471
|
Effect.suspend(() => {
|
|
416
472
|
activeServers.delete(address.entityId)
|
|
417
473
|
retired.openUnsafe()
|
|
474
|
+
closeLatches.closing = true
|
|
418
475
|
acquireEntity(address)
|
|
476
|
+
drainingServers.set(address.entityId, state)
|
|
419
477
|
return Effect.raceFirst(
|
|
420
|
-
|
|
478
|
+
Effect.gen(function*() {
|
|
479
|
+
for (const [requestId, request] of activeRequests) {
|
|
480
|
+
if (
|
|
481
|
+
!request.delivered || request.sentExit ||
|
|
482
|
+
!Context.get(request.message.annotations, ClusterSchema.InterruptOnTermination) ||
|
|
483
|
+
isUninterruptibleForServer(request.message.annotations)
|
|
484
|
+
) continue
|
|
485
|
+
request.terminating = true
|
|
486
|
+
yield* state.write(0, { _tag: "Interrupt", requestId: requestId as any, interruptors: [] })
|
|
487
|
+
}
|
|
488
|
+
yield* state.write(0, { _tag: "Eof" })
|
|
489
|
+
}).pipe(
|
|
421
490
|
Effect.andThen(endLatch.await),
|
|
422
491
|
Effect.timeoutOption(config.entityTerminationTimeout),
|
|
423
492
|
Effect.interruptible
|
|
424
493
|
),
|
|
425
494
|
Effect.interruptible(closeLatches.force.await)
|
|
426
|
-
)
|
|
495
|
+
).pipe(Effect.ensuring(Effect.sync(() => {
|
|
496
|
+
if (drainingServers.get(address.entityId) === state) {
|
|
497
|
+
drainingServers.delete(address.entityId)
|
|
498
|
+
}
|
|
499
|
+
})))
|
|
427
500
|
})
|
|
428
501
|
)
|
|
429
502
|
if (!options.sharding.hasShardId(address.shardId)) {
|
|
@@ -458,9 +531,38 @@ export const make = Effect.fnUntraced(function*<
|
|
|
458
531
|
Effect.forkIn(managerScope)
|
|
459
532
|
)
|
|
460
533
|
|
|
534
|
+
function sendEnvelope(server: EntityState, message: Message.IncomingEnvelope): Effect.Effect<void> {
|
|
535
|
+
const entry = server.activeRequests.get(message.envelope.requestId)
|
|
536
|
+
if (!entry) {
|
|
537
|
+
return Effect.void
|
|
538
|
+
} else if (
|
|
539
|
+
message.envelope._tag === "AckChunk" &&
|
|
540
|
+
Option.isSome(entry.lastSentChunk) &&
|
|
541
|
+
message.envelope.replyId !== entry.lastSentChunk.value.id
|
|
542
|
+
) {
|
|
543
|
+
return Effect.void
|
|
544
|
+
}
|
|
545
|
+
return server.write(
|
|
546
|
+
0,
|
|
547
|
+
message.envelope._tag === "AckChunk"
|
|
548
|
+
? { _tag: "Ack", requestId: message.envelope.requestId as any }
|
|
549
|
+
: {
|
|
550
|
+
_tag: "Interrupt",
|
|
551
|
+
requestId: message.envelope.requestId as any,
|
|
552
|
+
interruptors: []
|
|
553
|
+
}
|
|
554
|
+
)
|
|
555
|
+
}
|
|
556
|
+
|
|
461
557
|
function sendLocal<R extends Rpc.Any>(
|
|
462
558
|
message: Message.IncomingLocal<R>
|
|
463
559
|
): Effect.Effect<void, EntityNotAssignedToRunner | MailboxFull | AlreadyProcessingMessage> {
|
|
560
|
+
if (message._tag === "IncomingEnvelope") {
|
|
561
|
+
const draining = drainingServers.get(message.envelope.address.entityId)
|
|
562
|
+
if (draining?.activeRequests.has(message.envelope.requestId)) {
|
|
563
|
+
return sendEnvelope(draining, message)
|
|
564
|
+
}
|
|
565
|
+
}
|
|
464
566
|
return Effect.provideService(
|
|
465
567
|
Effect.flatMap(
|
|
466
568
|
entities.get(message.envelope.address),
|
|
@@ -557,26 +659,7 @@ export const make = Effect.fnUntraced(function*<
|
|
|
557
659
|
return server.write(0, requestEnvelope(entry), requestWriteOptions(entry))
|
|
558
660
|
}
|
|
559
661
|
case "IncomingEnvelope": {
|
|
560
|
-
|
|
561
|
-
if (!entry) {
|
|
562
|
-
return Effect.void
|
|
563
|
-
} else if (
|
|
564
|
-
message.envelope._tag === "AckChunk" &&
|
|
565
|
-
Option.isSome(entry.lastSentChunk) &&
|
|
566
|
-
message.envelope.replyId !== entry.lastSentChunk.value.id
|
|
567
|
-
) {
|
|
568
|
-
return Effect.void
|
|
569
|
-
}
|
|
570
|
-
return server.write(
|
|
571
|
-
0,
|
|
572
|
-
message.envelope._tag === "AckChunk"
|
|
573
|
-
? { _tag: "Ack", requestId: message.envelope.requestId as any }
|
|
574
|
-
: {
|
|
575
|
-
_tag: "Interrupt",
|
|
576
|
-
requestId: message.envelope.requestId as any,
|
|
577
|
-
interruptors: []
|
|
578
|
-
}
|
|
579
|
-
)
|
|
662
|
+
return sendEnvelope(server, message)
|
|
580
663
|
}
|
|
581
664
|
}
|
|
582
665
|
}
|
|
@@ -596,11 +679,24 @@ export const make = Effect.fnUntraced(function*<
|
|
|
596
679
|
})
|
|
597
680
|
|
|
598
681
|
const requestWriteOptions = (
|
|
599
|
-
entry: {
|
|
682
|
+
entry: {
|
|
683
|
+
readonly message: Message.IncomingRequestLocal<any>
|
|
684
|
+
readonly callerScope?: Scope.Scope | undefined
|
|
685
|
+
transaction?: RequestTransaction | undefined
|
|
686
|
+
}
|
|
600
687
|
): Parameters<EntityState["write"]>[2] => {
|
|
601
|
-
const onTransaction = Context.get(entry.message.annotations, WithTransaction)
|
|
602
|
-
?
|
|
603
|
-
:
|
|
688
|
+
const onTransaction = !Context.get(entry.message.annotations, WithTransaction)
|
|
689
|
+
? undefined
|
|
690
|
+
: <A, E, R>(effect: Effect.Effect<A, E, R>) =>
|
|
691
|
+
// Interrupts during commit must not hide the outcome from settle.
|
|
692
|
+
Effect.uninterruptibleMask((restore) => {
|
|
693
|
+
const transaction: RequestTransaction = {}
|
|
694
|
+
entry.transaction = transaction
|
|
695
|
+
return Effect.onExit(options.storage.withTransaction(restore(effect)), (outcome) => {
|
|
696
|
+
if (entry.transaction === transaction) entry.transaction = undefined
|
|
697
|
+
return transaction.settle ? transaction.settle(outcome) : Effect.void
|
|
698
|
+
})
|
|
699
|
+
})
|
|
604
700
|
const onCaller = entry.callerScope && bindToCaller(entry.callerScope)
|
|
605
701
|
if (!onCaller) return onTransaction && { onRequest: onTransaction }
|
|
606
702
|
return { onRequest: onTransaction ? (effect) => onCaller(onTransaction(effect)) : onCaller }
|
|
@@ -621,13 +717,13 @@ export const make = Effect.fnUntraced(function*<
|
|
|
621
717
|
}
|
|
622
718
|
})
|
|
623
719
|
}
|
|
624
|
-
activeServers
|
|
625
|
-
|
|
626
|
-
fibers.push(runFork(entities.removeIgnore(state.address)))
|
|
627
|
-
}
|
|
628
|
-
})
|
|
720
|
+
// Look entities up by address: `activeServers` is keyed by entity id,
|
|
721
|
+
// so an entity with the same id on another shard can hide this one.
|
|
629
722
|
serverCloseLatches.forEach((latches, address) => {
|
|
630
723
|
if (shardId[Equal.symbol](address.shardId)) {
|
|
724
|
+
if (!latches.closing) {
|
|
725
|
+
fibers.push(runFork(entities.removeIgnore(address)))
|
|
726
|
+
}
|
|
631
727
|
fibers.push(runFork(latches.closed.await))
|
|
632
728
|
}
|
|
633
729
|
})
|
package/src/encoding/Sse.ts
CHANGED
|
@@ -92,6 +92,12 @@ export interface DecodeOptions {
|
|
|
92
92
|
* Maximum number of string code units retained for a pending event. The default is 10 MiB.
|
|
93
93
|
*/
|
|
94
94
|
readonly maxEventSize?: number | undefined
|
|
95
|
+
/**
|
|
96
|
+
* Also dispatch an event that sets an `event:` name but has no `data:`
|
|
97
|
+
* line, with empty `data`. The WHATWG algorithm drops such events, but some
|
|
98
|
+
* protocols use them, such as graphql-sse's `complete`. Defaults to `false`.
|
|
99
|
+
*/
|
|
100
|
+
readonly dispatchEmptyNamedEvents?: boolean | undefined
|
|
95
101
|
}
|
|
96
102
|
|
|
97
103
|
const defaultMaxEventSize = 10 * 1024 * 1024
|
|
@@ -270,13 +276,16 @@ export const decodeDataSchema = <Type, DecodingServices, IE, Done>(
|
|
|
270
276
|
*/
|
|
271
277
|
export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeOptions): Parser {
|
|
272
278
|
const maxEventSize = options?.maxEventSize ?? defaultMaxEventSize
|
|
279
|
+
const dispatchEmptyNamedEvents = options?.dispatchEmptyNamedEvents === true
|
|
273
280
|
|
|
274
281
|
// Processing state
|
|
275
282
|
let isFirstChunk: boolean
|
|
276
|
-
|
|
277
|
-
let
|
|
278
|
-
let
|
|
283
|
+
// Pieces of the unterminated line, joined once when the line ends
|
|
284
|
+
let pending: Array<string>
|
|
285
|
+
let pendingLength: number
|
|
279
286
|
let discardTrailingNewline: boolean
|
|
287
|
+
// Incremented by `reset`, so `feed` can stop if `onParse` resets the parser
|
|
288
|
+
let generation = 0
|
|
280
289
|
|
|
281
290
|
// Event state
|
|
282
291
|
let lastEventId: string | undefined
|
|
@@ -287,10 +296,10 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO
|
|
|
287
296
|
return { feed, reset }
|
|
288
297
|
|
|
289
298
|
function reset(): void {
|
|
299
|
+
generation++
|
|
290
300
|
isFirstChunk = true
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
startingFieldLength = -1
|
|
301
|
+
pending = []
|
|
302
|
+
pendingLength = 0
|
|
294
303
|
discardTrailingNewline = false
|
|
295
304
|
|
|
296
305
|
lastEventId = undefined
|
|
@@ -299,120 +308,98 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO
|
|
|
299
308
|
}
|
|
300
309
|
|
|
301
310
|
function feed(chunk: string): SseError | undefined {
|
|
302
|
-
buffer = buffer ? buffer + chunk : chunk
|
|
303
|
-
|
|
304
311
|
// Strip any UTF-8 byte order mark (BOM) at the start of the stream.
|
|
305
312
|
// Note that we do not strip any non - UTF8 BOM, as eventsource streams are
|
|
306
313
|
// always decoded as UTF8 as per the specification.
|
|
307
|
-
if (isFirstChunk &&
|
|
308
|
-
|
|
314
|
+
if (isFirstChunk && chunk.startsWith(BOM)) {
|
|
315
|
+
chunk = chunk.slice(BOM.length)
|
|
309
316
|
}
|
|
310
317
|
|
|
311
318
|
isFirstChunk = false
|
|
312
319
|
|
|
313
|
-
|
|
314
|
-
const
|
|
320
|
+
const length = chunk.length
|
|
321
|
+
const currentGeneration = generation
|
|
315
322
|
let position = 0
|
|
316
323
|
|
|
317
|
-
// Read the current buffer byte by byte
|
|
318
324
|
while (position < length) {
|
|
319
|
-
//
|
|
320
|
-
// need to ignore a linefeed character if the previous character was a
|
|
321
|
-
// carriage return
|
|
322
|
-
// @todo refactor to reduce nesting, consider checking previous byte?
|
|
323
|
-
// @todo but consider multiple chunks etc
|
|
325
|
+
// Skip the LF of a CRLF, whose CR may have ended the previous chunk
|
|
324
326
|
if (discardTrailingNewline) {
|
|
325
|
-
|
|
327
|
+
discardTrailingNewline = false
|
|
328
|
+
if (chunk.charCodeAt(position) === LF) {
|
|
326
329
|
++position
|
|
330
|
+
continue
|
|
327
331
|
}
|
|
328
|
-
discardTrailingNewline = false
|
|
329
332
|
}
|
|
330
333
|
|
|
331
|
-
let
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
character = buffer[index]
|
|
337
|
-
if (character === ":" && fieldLength < 0) {
|
|
338
|
-
fieldLength = index - position
|
|
339
|
-
} else if (character === "\r") {
|
|
340
|
-
discardTrailingNewline = true
|
|
341
|
-
lineLength = index - position
|
|
342
|
-
} else if (character === "\n") {
|
|
343
|
-
lineLength = index - position
|
|
344
|
-
}
|
|
334
|
+
let lineEnd = position
|
|
335
|
+
while (lineEnd < length) {
|
|
336
|
+
const code = chunk.charCodeAt(lineEnd)
|
|
337
|
+
if (code === CR || code === LF) break
|
|
338
|
+
++lineEnd
|
|
345
339
|
}
|
|
346
340
|
|
|
347
|
-
if (
|
|
348
|
-
|
|
349
|
-
|
|
341
|
+
if (lineEnd === length) {
|
|
342
|
+
pending.push(chunk.slice(position))
|
|
343
|
+
pendingLength += length - position
|
|
350
344
|
break
|
|
351
|
-
} else {
|
|
352
|
-
startingPosition = 0
|
|
353
|
-
startingFieldLength = -1
|
|
354
345
|
}
|
|
355
346
|
|
|
356
|
-
|
|
347
|
+
discardTrailingNewline = chunk.charCodeAt(lineEnd) === CR
|
|
357
348
|
|
|
358
|
-
|
|
359
|
-
|
|
349
|
+
let line = chunk.slice(position, lineEnd)
|
|
350
|
+
if (pending.length > 0) {
|
|
351
|
+
pending.push(line)
|
|
352
|
+
line = pending.join("")
|
|
353
|
+
pending = []
|
|
354
|
+
pendingLength = 0
|
|
355
|
+
}
|
|
356
|
+
parseLine(line)
|
|
357
|
+
|
|
358
|
+
if (generation !== currentGeneration) {
|
|
359
|
+
// `onParse` reset the parser, so drop the rest of this chunk
|
|
360
|
+
return
|
|
361
|
+
}
|
|
360
362
|
|
|
361
|
-
|
|
362
|
-
// If we consumed the entire buffer to read the event, reset the buffer
|
|
363
|
-
buffer = ""
|
|
364
|
-
} else if (position > 0) {
|
|
365
|
-
// If there are bytes left to process, set the buffer to the unprocessed
|
|
366
|
-
// portion of the buffer only
|
|
367
|
-
buffer = buffer.slice(position)
|
|
363
|
+
position = lineEnd + 1
|
|
368
364
|
}
|
|
369
365
|
|
|
370
|
-
if (
|
|
366
|
+
if (pendingLength + data.length > maxEventSize) {
|
|
371
367
|
const error = new SseError({ reason: new EventTooLarge({ maxEventSize }) })
|
|
372
368
|
reset()
|
|
373
369
|
return error
|
|
374
370
|
}
|
|
375
371
|
}
|
|
376
372
|
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
373
|
+
function parseLine(line: string) {
|
|
374
|
+
if (line.length === 0) {
|
|
375
|
+
// End of event. Clear the state first so a throwing `onParse` cannot
|
|
376
|
+
// leak it into the next event
|
|
377
|
+
const named = eventName !== undefined && eventName !== ""
|
|
378
|
+
const event = eventName || "message"
|
|
379
|
+
const eventData = data
|
|
380
|
+
eventName = undefined
|
|
381
|
+
data = ""
|
|
382
|
+
if (eventData.length > 0 || (dispatchEmptyNamedEvents && named)) {
|
|
386
383
|
onParse({
|
|
387
384
|
_tag: "Event",
|
|
388
385
|
id: lastEventId,
|
|
389
|
-
event
|
|
390
|
-
data:
|
|
386
|
+
event,
|
|
387
|
+
data: eventData.slice(0, -1) // remove trailing newline
|
|
391
388
|
})
|
|
392
|
-
data = ""
|
|
393
389
|
}
|
|
394
|
-
eventName = undefined
|
|
395
390
|
return
|
|
396
391
|
}
|
|
397
392
|
|
|
398
|
-
const
|
|
399
|
-
|
|
400
|
-
let
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
} else if (lineBuffer[index + fieldLength + 1] === " ") {
|
|
405
|
-
step = fieldLength + 2
|
|
406
|
-
} else {
|
|
407
|
-
step = fieldLength + 1
|
|
393
|
+
const colon = line.indexOf(":")
|
|
394
|
+
let field = line
|
|
395
|
+
let value = ""
|
|
396
|
+
if (colon >= 0) {
|
|
397
|
+
field = line.slice(0, colon)
|
|
398
|
+
value = line.slice(line.charCodeAt(colon + 1) === SPACE ? colon + 2 : colon + 1)
|
|
408
399
|
}
|
|
409
400
|
|
|
410
|
-
const position = index + step
|
|
411
|
-
const valueLength = lineLength - step
|
|
412
|
-
const value = lineBuffer.slice(position, position + valueLength).toString()
|
|
413
|
-
|
|
414
401
|
if (field === "data") {
|
|
415
|
-
data += value
|
|
402
|
+
data += value + "\n"
|
|
416
403
|
} else if (field === "event") {
|
|
417
404
|
eventName = value
|
|
418
405
|
} else if (field === "id" && !value.includes("\u0000")) {
|
|
@@ -425,6 +412,9 @@ export function makeParser(onParse: (event: AnyEvent) => void, options?: DecodeO
|
|
|
425
412
|
}
|
|
426
413
|
|
|
427
414
|
const BOM = "\uFEFF"
|
|
415
|
+
const LF = 10
|
|
416
|
+
const CR = 13
|
|
417
|
+
const SPACE = 32
|
|
428
418
|
|
|
429
419
|
/**
|
|
430
420
|
* Stateful Server-Sent Events parser returned by `makeParser`.
|