@restatedev/restate-sdk-cloudflare-workers 1.11.1 → 1.12.0

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.
Files changed (117) hide show
  1. package/dist/_virtual/rolldown_runtime.cjs +9 -0
  2. package/dist/common_api.cjs +2 -1
  3. package/dist/common_api.d.cts +6 -2
  4. package/dist/common_api.d.cts.map +1 -1
  5. package/dist/common_api.d.ts +6 -2
  6. package/dist/common_api.d.ts.map +1 -1
  7. package/dist/common_api.js +2 -1
  8. package/dist/common_api.js.map +1 -1
  9. package/dist/context.cjs +13 -9
  10. package/dist/context.d.cts +36 -29
  11. package/dist/context.d.cts.map +1 -1
  12. package/dist/context.d.ts +36 -29
  13. package/dist/context.d.ts.map +1 -1
  14. package/dist/context.js +13 -9
  15. package/dist/context.js.map +1 -1
  16. package/dist/context_impl.cjs +150 -91
  17. package/dist/context_impl.d.cts +8 -0
  18. package/dist/context_impl.d.ts +8 -0
  19. package/dist/context_impl.js +151 -93
  20. package/dist/context_impl.js.map +1 -1
  21. package/dist/endpoint/components.cjs +35 -20
  22. package/dist/endpoint/components.d.cts +5 -0
  23. package/dist/endpoint/components.d.ts +5 -0
  24. package/dist/endpoint/components.js +35 -20
  25. package/dist/endpoint/components.js.map +1 -1
  26. package/dist/endpoint/endpoint.cjs +2 -2
  27. package/dist/endpoint/endpoint.d.cts +5 -0
  28. package/dist/endpoint/endpoint.d.ts +5 -0
  29. package/dist/endpoint/endpoint.js +2 -2
  30. package/dist/endpoint/endpoint.js.map +1 -1
  31. package/dist/endpoint/handlers/generic.cjs +115 -39
  32. package/dist/endpoint/handlers/generic.js +115 -39
  33. package/dist/endpoint/handlers/generic.js.map +1 -1
  34. package/dist/endpoint/handlers/types.d.cts +1 -0
  35. package/dist/endpoint/handlers/types.d.ts +1 -0
  36. package/dist/endpoint/handlers/utils.cjs +1 -1
  37. package/dist/endpoint/handlers/utils.js +1 -1
  38. package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings.d.ts +4 -1
  39. package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.js +48 -2
  40. package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.wasm +0 -0
  41. package/dist/endpoint/handlers/vm/sdk_shared_core_wasm_bindings_bg.wasm.d.ts +4 -1
  42. package/dist/endpoint/node_endpoint.cjs +28 -12
  43. package/dist/endpoint/node_endpoint.js +27 -11
  44. package/dist/endpoint/node_endpoint.js.map +1 -1
  45. package/dist/endpoint/types.d.cts +1 -1
  46. package/dist/endpoint/types.d.ts +1 -1
  47. package/dist/endpoint.d.cts +87 -5
  48. package/dist/endpoint.d.cts.map +1 -1
  49. package/dist/endpoint.d.ts +87 -5
  50. package/dist/endpoint.d.ts.map +1 -1
  51. package/dist/error_sanitization.cjs +26 -0
  52. package/dist/error_sanitization.js +26 -0
  53. package/dist/error_sanitization.js.map +1 -0
  54. package/dist/fetch.cjs +3 -1
  55. package/dist/fetch.d.cts +5 -3
  56. package/dist/fetch.d.cts.map +1 -1
  57. package/dist/fetch.d.ts +5 -3
  58. package/dist/fetch.d.ts.map +1 -1
  59. package/dist/fetch.js.map +1 -1
  60. package/dist/hooks.d.cts +87 -0
  61. package/dist/hooks.d.cts.map +1 -0
  62. package/dist/hooks.d.ts +87 -0
  63. package/dist/hooks.d.ts.map +1 -0
  64. package/dist/index.cjs +3 -1
  65. package/dist/index.d.cts +6 -4
  66. package/dist/index.d.ts +6 -4
  67. package/dist/index.js +3 -2
  68. package/dist/internal.cjs +3 -1
  69. package/dist/internal.d.cts +186 -2
  70. package/dist/internal.d.cts.map +1 -1
  71. package/dist/internal.d.ts +186 -2
  72. package/dist/internal.d.ts.map +1 -1
  73. package/dist/internal.js +4 -1
  74. package/dist/internal.js.map +1 -1
  75. package/dist/io.d.cts +1 -0
  76. package/dist/io.d.ts +1 -0
  77. package/dist/lambda.cjs +3 -1
  78. package/dist/lambda.d.cts +5 -3
  79. package/dist/lambda.d.cts.map +1 -1
  80. package/dist/lambda.d.ts +5 -3
  81. package/dist/lambda.d.ts.map +1 -1
  82. package/dist/lambda.js +3 -2
  83. package/dist/lambda.js.map +1 -1
  84. package/dist/logging/logger.d.cts +1 -0
  85. package/dist/logging/logger.d.ts +1 -0
  86. package/dist/node.cjs +23 -6
  87. package/dist/node.d.cts +46 -8
  88. package/dist/node.d.cts.map +1 -1
  89. package/dist/node.d.ts +46 -8
  90. package/dist/node.d.ts.map +1 -1
  91. package/dist/node.js +23 -7
  92. package/dist/node.js.map +1 -1
  93. package/dist/package.cjs +1 -1
  94. package/dist/package.js +1 -1
  95. package/dist/package.js.map +1 -1
  96. package/dist/promises.cjs +90 -53
  97. package/dist/promises.d.cts +18 -0
  98. package/dist/promises.d.cts.map +1 -0
  99. package/dist/promises.d.ts +18 -0
  100. package/dist/promises.d.ts.map +1 -0
  101. package/dist/promises.js +85 -48
  102. package/dist/promises.js.map +1 -1
  103. package/dist/types/errors.cjs +13 -0
  104. package/dist/types/errors.d.cts +11 -5
  105. package/dist/types/errors.d.cts.map +1 -1
  106. package/dist/types/errors.d.ts +11 -5
  107. package/dist/types/errors.d.ts.map +1 -1
  108. package/dist/types/errors.js +13 -1
  109. package/dist/types/errors.js.map +1 -1
  110. package/dist/types/rpc.cjs +7 -19
  111. package/dist/types/rpc.d.cts +174 -0
  112. package/dist/types/rpc.d.cts.map +1 -1
  113. package/dist/types/rpc.d.ts +174 -0
  114. package/dist/types/rpc.d.ts.map +1 -1
  115. package/dist/types/rpc.js +7 -19
  116. package/dist/types/rpc.js.map +1 -1
  117. package/package.json +3 -3
@@ -0,0 +1,5 @@
1
+ import "./components.cjs";
2
+ import "../logging/logger_transport.cjs";
3
+ import "../logging/logger.cjs";
4
+ import "../endpoint.cjs";
5
+ import { JournalValueCodec, ServiceDefinition, VirtualObjectDefinition, WorkflowDefinition } from "@restatedev/restate-sdk-core";
@@ -0,0 +1,5 @@
1
+ import "./components.js";
2
+ import "../logging/logger_transport.js";
3
+ import "../logging/logger.js";
4
+ import "../endpoint.js";
5
+ import { JournalValueCodec, ServiceDefinition, VirtualObjectDefinition, WorkflowDefinition } from "@restatedev/restate-sdk-core";
@@ -1,7 +1,7 @@
1
- import { LogSource } from "../logging/logger_transport.js";
2
- import { defaultLoggerTransport } from "../logging/console_logger_transport.js";
3
1
  import { HandlerWrapper } from "../types/rpc.js";
4
2
  import { ServiceComponent, VirtualObjectComponent, WorkflowComponent } from "./components.js";
3
+ import { LogSource } from "../logging/logger_transport.js";
4
+ import { defaultLoggerTransport } from "../logging/console_logger_transport.js";
5
5
  import { createLogger } from "../logging/logger.js";
6
6
 
7
7
  //#region src/endpoint/endpoint.ts
@@ -1 +1 @@
1
- {"version":3,"file":"endpoint.js","names":["name"],"sources":["../../src/endpoint/endpoint.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH\n *\n * This file is part of the Restate SDK for Node.js/TypeScript,\n * which is released under the MIT license.\n *\n * You can find a copy of the license in file LICENSE in the root\n * directory of this repository or package, or at\n * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type {\n JournalValueCodec,\n ServiceDefinition,\n VirtualObjectDefinition,\n WorkflowDefinition,\n} from \"@restatedev/restate-sdk-core\";\n\nimport type {\n ObjectOptions,\n ServiceOptions,\n WorkflowOptions,\n} from \"../types/rpc.js\";\nimport { HandlerWrapper } from \"../types/rpc.js\";\nimport type { Component } from \"./components.js\";\nimport {\n ServiceComponent,\n VirtualObjectComponent,\n WorkflowComponent,\n} from \"./components.js\";\nimport type * as discovery from \"./discovery.js\";\nimport { defaultLoggerTransport } from \"../logging/console_logger_transport.js\";\nimport {\n type LoggerTransport,\n LogSource,\n} from \"../logging/logger_transport.js\";\nimport type { Logger } from \"../logging/logger.js\";\nimport { createLogger } from \"../logging/logger.js\";\nimport type { DefaultServiceOptions } from \"../endpoint.js\";\n\nfunction isServiceDefinition<P extends string, M>(\n m: Record<string, any>\n): m is ServiceDefinition<P, M> & { service: M } {\n return m && m.service !== undefined;\n}\n\nfunction isObjectDefinition<P extends string, M>(\n m: Record<string, any>\n): m is VirtualObjectDefinition<P, M> & { object: M } {\n return m && m.object !== undefined;\n}\n\nfunction isWorkflowDefinition<P extends string, M>(\n m: Record<string, any>\n): m is WorkflowDefinition<P, M> & { workflow: M } {\n return m && m.workflow !== undefined;\n}\n\n/**\n * Services can have additional information that is not part of the definition.\n * For example a description or metadata.\n */\ntype ServiceAuxInfo = {\n description?: string;\n metadata?: Record<string, any>;\n options?: ServiceOptions | ObjectOptions | WorkflowOptions;\n};\n\nexport type Endpoint = {\n loggerTransport: LoggerTransport;\n components: Map<string, Component>;\n keySet: string[];\n /**\n * This is a simple console without contextual info.\n *\n * This should be used only in cases where no contextual info is available.\n */\n rlog: Logger;\n /**\n * All discovery metadata, except protocol mode provided by the node/fetch/lambda endpoint implementations\n */\n discoveryMetadata: Omit<discovery.Endpoint, \"protocolMode\">;\n\n /**\n * Codec provider to use for journal values.\n */\n journalValueCodec?: Promise<JournalValueCodec>;\n};\n\nexport class EndpointBuilder {\n private readonly serviceDefinitions: Map<\n string,\n | ServiceDefinition<string, any>\n | VirtualObjectDefinition<string, any>\n | WorkflowDefinition<string, any>\n > = new Map();\n private loggerTransport: LoggerTransport = defaultLoggerTransport;\n private keySet: string[] = [];\n private defaultServiceOptions: DefaultServiceOptions = {};\n private journalValueCodecProvider?: () => Promise<JournalValueCodec>;\n\n public bind<P extends string, M>(\n definition:\n | ServiceDefinition<P, M>\n | VirtualObjectDefinition<P, M>\n | WorkflowDefinition<P, M>\n ) {\n // Validate service name\n if (definition.name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n\n this.serviceDefinitions.set(definition.name, definition);\n }\n\n public addIdentityKeys(...keys: string[]) {\n this.keySet.push(...keys);\n }\n\n public setDefaultServiceOptions(options: DefaultServiceOptions) {\n this.defaultServiceOptions = options;\n }\n\n public setLogger(newLogger: LoggerTransport) {\n this.loggerTransport = newLogger;\n }\n\n public setJournalValueCodecProvider(\n codecProvider: () => Promise<JournalValueCodec>\n ) {\n this.journalValueCodecProvider = codecProvider;\n }\n\n public build(): Endpoint {\n const rlog = createLogger(this.loggerTransport, LogSource.SYSTEM);\n\n // Build the components\n const components = new Map<string, Component>();\n for (const [name, definition] of this.serviceDefinitions) {\n if (isServiceDefinition(definition)) {\n const { name, service } = definition;\n if (!service) {\n throw new TypeError(`no service implementation found.`);\n }\n components.set(\n name,\n buildServiceComponent(\n name,\n service,\n definition as ServiceAuxInfo,\n this.defaultServiceOptions\n )\n );\n } else if (isObjectDefinition(definition)) {\n const { name, object } = definition;\n if (!object) {\n throw new TypeError(`no object implementation found.`);\n }\n components.set(\n name,\n buildVirtualObjectComponent(\n name,\n object,\n definition as ServiceAuxInfo,\n this.defaultServiceOptions\n )\n );\n } else if (isWorkflowDefinition(definition)) {\n const { name, workflow } = definition;\n if (!workflow) {\n throw new TypeError(`no workflow implementation found.`);\n }\n components.set(\n name,\n buildWorkflowComponent(\n name,\n workflow,\n definition as ServiceAuxInfo,\n this.defaultServiceOptions\n )\n );\n } else {\n throw new TypeError(\n `cannot bind ${name}, can only bind a service or a virtual object or a workflow definition`\n );\n }\n }\n\n // Compute discovery metadata\n const discoveryMetadata = computeDiscovery(components);\n\n return {\n keySet: this.keySet,\n loggerTransport: this.loggerTransport,\n rlog,\n components,\n discoveryMetadata,\n journalValueCodec: this.journalValueCodecProvider\n ? this.journalValueCodecProvider()\n : undefined,\n };\n }\n}\n\nfunction computeDiscovery(\n components: Map<string, Component>\n): discovery.Endpoint {\n return {\n minProtocolVersion: 5,\n maxProtocolVersion: 6,\n services: [...components.values()].map((c) => c.discovery()),\n };\n}\n\nfunction buildServiceComponent(\n name: string,\n router: any,\n definition: ServiceAuxInfo,\n defaultServiceOptions: DefaultServiceOptions\n): ServiceComponent {\n if (name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n const component = new ServiceComponent(\n name,\n definition.description,\n definition.metadata,\n {\n ...defaultServiceOptions,\n ...(definition?.options as ServiceOptions),\n }\n );\n\n for (const [route, handler] of Object.entries(\n router as { [s: string]: any }\n )) {\n const wrapper = HandlerWrapper.fromHandler(handler);\n if (!wrapper) {\n throw new TypeError(`${route} is not a restate handler.`);\n }\n wrapper.bindInstance(router);\n component.add(route, wrapper);\n }\n\n return component;\n}\n\nfunction buildVirtualObjectComponent(\n name: string,\n router: any,\n definition: ServiceAuxInfo,\n defaultServiceOptions: DefaultServiceOptions\n): VirtualObjectComponent {\n if (name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n const component = new VirtualObjectComponent(\n name,\n definition.description,\n definition.metadata,\n {\n ...defaultServiceOptions,\n ...(definition?.options as ObjectOptions),\n }\n );\n\n for (const [route, handler] of Object.entries(\n router as { [s: string]: any }\n )) {\n const wrapper = HandlerWrapper.fromHandler(handler);\n if (!wrapper) {\n throw new TypeError(`${route} is not a restate handler.`);\n }\n wrapper.bindInstance(router);\n component.add(route, wrapper);\n }\n return component;\n}\n\nfunction buildWorkflowComponent(\n name: string,\n workflow: any,\n definition: ServiceAuxInfo,\n defaultServiceOptions: DefaultServiceOptions\n): WorkflowComponent {\n if (name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n const component = new WorkflowComponent(\n name,\n definition.description,\n definition.metadata,\n {\n ...defaultServiceOptions,\n ...(definition?.options as WorkflowOptions),\n }\n );\n\n for (const [route, handler] of Object.entries(\n workflow as { [s: string]: any }\n )) {\n const wrapper = HandlerWrapper.fromHandler(handler);\n if (!wrapper) {\n throw new TypeError(`${route} is not a restate handler.`);\n }\n wrapper.bindInstance(workflow);\n component.add(route, wrapper);\n }\n return component;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"endpoint.js","names":["name"],"sources":["../../src/endpoint/endpoint.ts"],"sourcesContent":["/*\n * Copyright (c) 2023-2024 - Restate Software, Inc., Restate GmbH\n *\n * This file is part of the Restate SDK for Node.js/TypeScript,\n * which is released under the MIT license.\n *\n * You can find a copy of the license in file LICENSE in the root\n * directory of this repository or package, or at\n * https://github.com/restatedev/sdk-typescript/blob/main/LICENSE\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type {\n JournalValueCodec,\n ServiceDefinition,\n VirtualObjectDefinition,\n WorkflowDefinition,\n} from \"@restatedev/restate-sdk-core\";\n\nimport type {\n ObjectOptions,\n ServiceOptions,\n WorkflowOptions,\n} from \"../types/rpc.js\";\nimport { HandlerWrapper } from \"../types/rpc.js\";\nimport type { Component } from \"./components.js\";\nimport {\n ServiceComponent,\n VirtualObjectComponent,\n WorkflowComponent,\n} from \"./components.js\";\nimport type * as discovery from \"./discovery.js\";\nimport { defaultLoggerTransport } from \"../logging/console_logger_transport.js\";\nimport {\n type LoggerTransport,\n LogSource,\n} from \"../logging/logger_transport.js\";\nimport type { Logger } from \"../logging/logger.js\";\nimport { createLogger } from \"../logging/logger.js\";\nimport type { DefaultServiceOptions } from \"../endpoint.js\";\n\nfunction isServiceDefinition<P extends string, M>(\n m: Record<string, any>\n): m is ServiceDefinition<P, M> & { service: M } {\n return m && m.service !== undefined;\n}\n\nfunction isObjectDefinition<P extends string, M>(\n m: Record<string, any>\n): m is VirtualObjectDefinition<P, M> & { object: M } {\n return m && m.object !== undefined;\n}\n\nfunction isWorkflowDefinition<P extends string, M>(\n m: Record<string, any>\n): m is WorkflowDefinition<P, M> & { workflow: M } {\n return m && m.workflow !== undefined;\n}\n\n/**\n * Services can have additional information that is not part of the definition.\n * For example a description or metadata.\n */\ntype ServiceAuxInfo = {\n description?: string;\n metadata?: Record<string, any>;\n options?: ServiceOptions | ObjectOptions | WorkflowOptions;\n};\n\nexport type Endpoint = {\n loggerTransport: LoggerTransport;\n components: Map<string, Component>;\n keySet: string[];\n /**\n * This is a simple console without contextual info.\n *\n * This should be used only in cases where no contextual info is available.\n */\n rlog: Logger;\n /**\n * All discovery metadata, except protocol mode provided by the node/fetch/lambda endpoint implementations\n */\n discoveryMetadata: Omit<discovery.Endpoint, \"protocolMode\">;\n\n /**\n * Codec provider to use for journal values.\n */\n journalValueCodec?: Promise<JournalValueCodec>;\n};\n\nexport class EndpointBuilder {\n private readonly serviceDefinitions: Map<\n string,\n | ServiceDefinition<string, any>\n | VirtualObjectDefinition<string, any>\n | WorkflowDefinition<string, any>\n > = new Map();\n private loggerTransport: LoggerTransport = defaultLoggerTransport;\n private keySet: string[] = [];\n private defaultServiceOptions: DefaultServiceOptions = {};\n private journalValueCodecProvider?: () => Promise<JournalValueCodec>;\n\n public bind<P extends string, M>(\n definition:\n | ServiceDefinition<P, M>\n | VirtualObjectDefinition<P, M>\n | WorkflowDefinition<P, M>\n ) {\n // Validate service name\n if (definition.name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n\n this.serviceDefinitions.set(definition.name, definition);\n }\n\n public addIdentityKeys(...keys: string[]) {\n this.keySet.push(...keys);\n }\n\n public setDefaultServiceOptions(options: DefaultServiceOptions) {\n this.defaultServiceOptions = options;\n }\n\n public setLogger(newLogger: LoggerTransport) {\n this.loggerTransport = newLogger;\n }\n\n public setJournalValueCodecProvider(\n codecProvider: () => Promise<JournalValueCodec>\n ) {\n this.journalValueCodecProvider = codecProvider;\n }\n\n public build(): Endpoint {\n const rlog = createLogger(this.loggerTransport, LogSource.SYSTEM);\n\n // Build the components\n const components = new Map<string, Component>();\n for (const [name, definition] of this.serviceDefinitions) {\n if (isServiceDefinition(definition)) {\n const { name, service } = definition;\n if (!service) {\n throw new TypeError(`no service implementation found.`);\n }\n components.set(\n name,\n buildServiceComponent(\n name,\n service,\n definition as ServiceAuxInfo,\n this.defaultServiceOptions\n )\n );\n } else if (isObjectDefinition(definition)) {\n const { name, object } = definition;\n if (!object) {\n throw new TypeError(`no object implementation found.`);\n }\n components.set(\n name,\n buildVirtualObjectComponent(\n name,\n object,\n definition as ServiceAuxInfo,\n this.defaultServiceOptions\n )\n );\n } else if (isWorkflowDefinition(definition)) {\n const { name, workflow } = definition;\n if (!workflow) {\n throw new TypeError(`no workflow implementation found.`);\n }\n components.set(\n name,\n buildWorkflowComponent(\n name,\n workflow,\n definition as ServiceAuxInfo,\n this.defaultServiceOptions\n )\n );\n } else {\n throw new TypeError(\n `cannot bind ${name}, can only bind a service or a virtual object or a workflow definition`\n );\n }\n }\n\n // Compute discovery metadata\n const discoveryMetadata = computeDiscovery(components);\n\n return {\n keySet: this.keySet,\n loggerTransport: this.loggerTransport,\n rlog,\n components,\n discoveryMetadata,\n journalValueCodec: this.journalValueCodecProvider\n ? this.journalValueCodecProvider()\n : undefined,\n };\n }\n}\n\nfunction computeDiscovery(\n components: Map<string, Component>\n): discovery.Endpoint {\n return {\n minProtocolVersion: 5,\n maxProtocolVersion: 6,\n services: [...components.values()].map((c) => c.discovery()),\n };\n}\n\nfunction buildServiceComponent(\n name: string,\n router: any,\n definition: ServiceAuxInfo,\n defaultServiceOptions: DefaultServiceOptions\n): ServiceComponent {\n if (name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n const component = new ServiceComponent(\n name,\n definition.description,\n definition.metadata,\n {\n ...defaultServiceOptions,\n ...(definition?.options as ServiceOptions | undefined),\n }\n );\n\n for (const [route, handler] of Object.entries(\n router as { [s: string]: any }\n )) {\n const wrapper = HandlerWrapper.fromHandler(handler);\n if (!wrapper) {\n throw new TypeError(`${route} is not a restate handler.`);\n }\n wrapper.bindInstance(router);\n component.add(route, wrapper);\n }\n\n return component;\n}\n\nfunction buildVirtualObjectComponent(\n name: string,\n router: any,\n definition: ServiceAuxInfo,\n defaultServiceOptions: DefaultServiceOptions\n): VirtualObjectComponent {\n if (name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n const component = new VirtualObjectComponent(\n name,\n definition.description,\n definition.metadata,\n {\n ...defaultServiceOptions,\n ...(definition?.options as ObjectOptions | undefined),\n }\n );\n\n for (const [route, handler] of Object.entries(\n router as { [s: string]: any }\n )) {\n const wrapper = HandlerWrapper.fromHandler(handler);\n if (!wrapper) {\n throw new TypeError(`${route} is not a restate handler.`);\n }\n wrapper.bindInstance(router);\n component.add(route, wrapper);\n }\n return component;\n}\n\nfunction buildWorkflowComponent(\n name: string,\n workflow: any,\n definition: ServiceAuxInfo,\n defaultServiceOptions: DefaultServiceOptions\n): WorkflowComponent {\n if (name.indexOf(\"/\") !== -1) {\n throw new Error(\"service name must not contain any slash '/'\");\n }\n const component = new WorkflowComponent(\n name,\n definition.description,\n definition.metadata,\n {\n ...defaultServiceOptions,\n ...(definition?.options as WorkflowOptions | undefined),\n }\n );\n\n for (const [route, handler] of Object.entries(\n workflow as { [s: string]: any }\n )) {\n const wrapper = HandlerWrapper.fromHandler(handler);\n if (!wrapper) {\n throw new TypeError(`${route} is not a restate handler.`);\n }\n wrapper.bindInstance(workflow);\n component.add(route, wrapper);\n }\n return component;\n}\n"],"mappings":"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"}
@@ -1,21 +1,23 @@
1
1
  const require_rolldown_runtime = require('../../_virtual/rolldown_runtime.cjs');
2
- const require_logger_transport = require('../../logging/logger_transport.cjs');
3
- const require_console_logger_transport = require('../../logging/console_logger_transport.cjs');
4
- const require_core_logging = require('./core_logging.cjs');
5
- const require_sdk_shared_core_wasm_bindings = require('./vm/sdk_shared_core_wasm_bindings.cjs');
6
2
  const require_errors = require('../../types/errors.cjs');
7
- const require_rpc = require('../../types/rpc.cjs');
8
3
  const require_completable_promise = require('../../utils/completable_promise.cjs');
9
- const require_context_impl = require('../../context_impl.cjs');
4
+ const require_rpc = require('../../types/rpc.cjs');
10
5
  const require_components = require('../components.cjs');
6
+ const require_logger_transport = require('../../logging/logger_transport.cjs');
7
+ const require_console_logger_transport = require('../../logging/console_logger_transport.cjs');
11
8
  const require_logger = require('../../logging/logger.cjs');
12
9
  const require_user_agent = require('../../user_agent.cjs');
10
+ const require_core_logging = require('./core_logging.cjs');
11
+ const require_sdk_shared_core_wasm_bindings = require('./vm/sdk_shared_core_wasm_bindings.cjs');
12
+ const require_context_impl = require('../../context_impl.cjs');
13
+ const require_error_sanitization = require('../../error_sanitization.cjs');
13
14
  const require_utils = require('./utils.cjs');
14
15
  const require_discovery = require('./discovery.cjs');
15
16
  let __restatedev_restate_sdk_core = require("@restatedev/restate-sdk-core");
16
17
  __restatedev_restate_sdk_core = require_rolldown_runtime.__toESM(__restatedev_restate_sdk_core);
17
18
 
18
19
  //#region src/endpoint/handlers/generic.ts
20
+ const HOOK_CONTEXT_IS_PROCESSING_SYMBOL = Symbol.for("@restatedev/restate-sdk/hooks.isProcessing");
19
21
  function createRestateHandler(endpoint, protocolMode, additionalDiscoveryFields) {
20
22
  return new RestateHandlerImpl(endpoint, protocolMode, additionalDiscoveryFields);
21
23
  }
@@ -110,7 +112,7 @@ var RestateInvokeResponse = class {
110
112
  this.loggerId = Math.floor(Math.random() * 4294967295);
111
113
  const isJournalCodecDefined = this.journalValueCodecInit !== void 0;
112
114
  const vmHeaders = Object.entries(this.attemptHeaders).filter(([, v]) => v !== void 0).map(([k, v]) => new require_sdk_shared_core_wasm_bindings.WasmHeader(k, v instanceof Array ? v[0] : v));
113
- this.coreVm = new require_sdk_shared_core_wasm_bindings.WasmVM(vmHeaders, restateLogLevelToWasmLogLevel(require_console_logger_transport.DEFAULT_CONSOLE_LOGGER_LOG_LEVEL), this.loggerId, isJournalCodecDefined);
115
+ this.coreVm = new require_sdk_shared_core_wasm_bindings.WasmVM(vmHeaders, restateLogLevelToWasmLogLevel(require_console_logger_transport.DEFAULT_CONSOLE_LOGGER_LOG_LEVEL), this.loggerId, isJournalCodecDefined, handler.executionOptions.explicitCancellation ?? false);
114
116
  const responseHead = this.coreVm.get_response_head();
115
117
  this.statusCode = responseHead.status_code;
116
118
  this.headers = responseHead.headers.reduce((headers, { key, value }) => ({
@@ -134,6 +136,14 @@ var RestateInvokeResponse = class {
134
136
  await bufferJournalReplayInCoreVm(this.coreVm, inputReader);
135
137
  const input = this.coreVm.sys_input();
136
138
  const invocationRequest = {
139
+ target: {
140
+ service: this.service.name(),
141
+ handler: this.handler.name(),
142
+ key: input.key || void 0,
143
+ toString() {
144
+ return this.key !== void 0 ? `${this.service}/${this.key}/${this.handler}` : `${this.service}/${this.handler}`;
145
+ }
146
+ },
137
147
  id: input.invocation_id,
138
148
  headers: input.headers.reduce((headers, { key, value }) => {
139
149
  headers.set(key, value);
@@ -153,18 +163,26 @@ var RestateInvokeResponse = class {
153
163
  require_core_logging.registerLogger(this.loggerId, this.vmLogger);
154
164
  if (!this.coreVm.is_processing()) this.vmLogger.info("Replaying invocation.");
155
165
  else this.vmLogger.info("Starting invocation.");
156
- ctx = new require_context_impl.ContextImpl(this.coreVm, input, ctxLogger, this.handler.kind(), this.vmLogger, invocationRequest, invocationEndPromise, inputReader, outputWriter, journalValueCodec, this.service.options?.serde, this.service.options?.asTerminalError);
166
+ ctx = new require_context_impl.ContextImpl(this.coreVm, input, ctxLogger, this.handler.kind(), this.vmLogger, invocationRequest, invocationEndPromise, inputReader, outputWriter, journalValueCodec, this.handler.executionOptions);
157
167
  } catch (e) {
158
168
  const error = require_errors.ensureError(e);
159
169
  this.coreVm.notify_error(error.message, error.message);
160
170
  await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
161
171
  return;
162
172
  }
163
- startUserHandler(ctx, this.service, this.handler, journalValueCodec).finally(() => {
164
- invocationEndPromise.resolve();
165
- });
166
- await invocationEndPromise.promise;
167
- await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
173
+ abortSignal.addEventListener("abort", () => {
174
+ setImmediate(() => {
175
+ const msg = "Connection closed";
176
+ this.coreVm.notify_error(msg, msg);
177
+ });
178
+ }, { once: true });
179
+ try {
180
+ await startUserHandler(ctx, this.service, this.handler, journalValueCodec);
181
+ } catch (e) {
182
+ notifyError(e, ctx, this.handler.executionOptions.asTerminalError);
183
+ } finally {
184
+ await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
185
+ }
168
186
  }
169
187
  };
170
188
  async function bufferJournalReplayInCoreVm(coreVm, inputReader) {
@@ -178,35 +196,57 @@ async function bufferJournalReplayInCoreVm(coreVm, inputReader) {
178
196
  }
179
197
  }
180
198
  async function startUserHandler(ctx, service, handler, journalValueCodec) {
199
+ const hooks = [];
200
+ for (const provider of handler.executionOptions.hooks ?? []) {
201
+ const hookContext = { request: ctx.request() };
202
+ Object.defineProperty(hookContext, HOOK_CONTEXT_IS_PROCESSING_SYMBOL, {
203
+ value: () => ctx.isProcessing(),
204
+ enumerable: false
205
+ });
206
+ hooks.push(provider(hookContext));
207
+ }
208
+ const handlerInterceptor = composeInterceptors(hooks.map((h) => h.interceptor?.handler).filter(isDefined));
209
+ ctx.setRunInterceptor(composeInterceptors(hooks.map((h) => h.interceptor?.run).filter(isDefined)));
210
+ let encodedOutput;
211
+ await raceWithAttemptEnd(ctx, handlerInterceptor)(async () => {
212
+ const decodedInput = await journalValueCodec.decode(ctx.request().body).catch((e) => Promise.reject(new require_errors.TerminalError(`Failed to decode input using journal value codec: ${require_errors.ensureError(e).message}`, { errorCode: 400 })));
213
+ const output = await handler.invoke(ctx, decodedInput);
214
+ encodedOutput = journalValueCodec.encode(output);
215
+ });
216
+ ctx.coreVm.sys_write_output_success(encodedOutput);
217
+ ctx.coreVm.sys_end();
218
+ ctx.vmLogger.info("Invocation completed successfully.");
219
+ }
220
+ /**
221
+ * Classifies the error and notifies the VM. Called from the process() catch
222
+ * block — the single place that decides how to report errors to the VM.
223
+ */
224
+ function notifyError(e, ctx, asTerminalError) {
225
+ if (e instanceof require_context_impl.CommandError) {
226
+ const cause = require_errors.ensureError(e.cause);
227
+ require_errors.logError(ctx.vmLogger, cause);
228
+ if (e.hasCommandIndex) ctx.coreVm.notify_error_for_specific_command(cause.message, cause.stack, e.commandType, e.commandIndex, null);
229
+ else ctx.coreVm.notify_error_for_next_command(cause.message, cause.stack, e.commandType);
230
+ return;
231
+ }
232
+ const error = require_errors.ensureError(e, asTerminalError);
233
+ require_errors.logError(ctx.vmLogger, error);
181
234
  try {
182
- try {
183
- const decodedInput = await journalValueCodec.decode(ctx.request().body).catch((e) => Promise.reject(new require_errors.TerminalError(`Failed to decode input using journal value codec: ${require_errors.ensureError(e).message}`, { errorCode: 400 })));
184
- const output = await handler.invoke(ctx, decodedInput);
185
- const encodedOutput = journalValueCodec.encode(output);
186
- ctx.coreVm.sys_write_output_success(encodedOutput);
235
+ if (error instanceof require_errors.TerminalError) {
236
+ ctx.coreVm.sys_write_output_failure({
237
+ code: error.code,
238
+ message: error.message,
239
+ metadata: Object.entries(error.metadata ?? {}).map(([key, value]) => ({
240
+ key,
241
+ value
242
+ }))
243
+ });
187
244
  ctx.coreVm.sys_end();
188
- ctx.vmLogger.info("Invocation completed successfully.");
189
- } catch (e) {
190
- const error = require_errors.ensureError(e, service.options?.asTerminalError);
191
- require_errors.logError(ctx.vmLogger, error);
192
- if (error instanceof require_errors.TerminalError) {
193
- ctx.coreVm.sys_write_output_failure({
194
- code: error.code,
195
- message: error.message,
196
- metadata: Object.entries(error.metadata ?? {}).map(([key, value]) => ({
197
- key,
198
- value
199
- }))
200
- });
201
- ctx.coreVm.sys_end();
202
- return;
203
- }
204
- throw error;
205
- }
206
- } catch (e) {
207
- const error = require_errors.ensureError(e);
208
- if (error instanceof require_errors.RetryableError) ctx.coreVm.notify_error_with_delay_override(error.message, error.stack, error.retryAfter !== void 0 ? BigInt((0, __restatedev_restate_sdk_core.millisOrDurationToMillis)(error.retryAfter)) : void 0);
245
+ } else if (error instanceof require_errors.RetryableError) ctx.coreVm.notify_error_with_delay_override(error.message, error.stack, error.retryAfter !== void 0 ? BigInt((0, __restatedev_restate_sdk_core.millisOrDurationToMillis)(error.retryAfter)) : void 0);
209
246
  else ctx.coreVm.notify_error(error.message, error.stack);
247
+ } catch (vmError) {
248
+ const inner = require_errors.ensureError(vmError);
249
+ ctx.coreVm.notify_error(inner.message, inner.stack);
210
250
  }
211
251
  }
212
252
  async function flushAndClose(coreVm, vmLogger, inputReader, outputWriter) {
@@ -234,6 +274,42 @@ async function flushAndClose(coreVm, vmLogger, inputReader, outputWriter) {
234
274
  function isAbortErrorOnWrite(error) {
235
275
  return error.name === "AbortError" || error.message === "Invalid state: WritableStream is closed" || error.code === "ERR_HTTP2_INVALID_STREAM";
236
276
  }
277
+ function composeInterceptors(interceptors) {
278
+ return interceptors.reduceRight((innerInterceptor, interceptor) => (...args) => {
279
+ const context = args.slice(0, -1);
280
+ const callback = args.at(-1);
281
+ return interceptor(...context, () => innerInterceptor(...context, callback));
282
+ }, (...args) => args.at(-1)());
283
+ }
284
+ /**
285
+ * Wraps an interceptor so that both `next()` and the interceptor body race
286
+ * against `invocationEndPromise`. When the attempt ends (suspension, retryable
287
+ * error, etc.), the promise rejects and the interceptor chain unwinds through
288
+ * catch/finally blocks — preventing interceptors from hanging on a `next()`
289
+ * that will never settle.
290
+ *
291
+ * SDK-internal metadata (CommandError, retryAfter) is stripped before the
292
+ * interceptor sees the error and restored after the chain exits.
293
+ */
294
+ function raceWithAttemptEnd(ctx, interceptor) {
295
+ return (...args) => {
296
+ let originalError;
297
+ const signal = ctx.invocationEndPromise.promise.catch((e) => {
298
+ originalError = e;
299
+ throw require_error_sanitization.sanitizeError(e);
300
+ });
301
+ const originalNext = args.at(-1);
302
+ const racingNext = () => Promise.race([originalNext(), signal]);
303
+ const newArgs = [...args.slice(0, -1), racingNext];
304
+ return Promise.race([interceptor(...newArgs), signal]).catch((e) => {
305
+ if (originalError !== void 0) throw require_error_sanitization.restoreError(e, originalError);
306
+ throw e;
307
+ });
308
+ };
309
+ }
310
+ function isDefined(value) {
311
+ return value != null;
312
+ }
237
313
  function restateLogLevelToWasmLogLevel(level) {
238
314
  switch (level) {
239
315
  case require_logger_transport.RestateLogLevel.TRACE: return require_sdk_shared_core_wasm_bindings.LogLevel.TRACE;
@@ -1,19 +1,21 @@
1
- import { LogSource, LoggerContext, RestateLogLevel } from "../../logging/logger_transport.js";
2
- import { DEFAULT_CONSOLE_LOGGER_LOG_LEVEL } from "../../logging/console_logger_transport.js";
3
- import { destroyLogger, registerLogger } from "./core_logging.js";
4
- import { LogLevel, WasmHeader, WasmIdentityVerifier, WasmVM, set_log_level } from "./vm/sdk_shared_core_wasm_bindings.js";
5
1
  import { RestateError, RetryableError, TerminalError, ensureError, logError } from "../../types/errors.js";
6
- import { HandlerKind } from "../../types/rpc.js";
7
2
  import { CompletablePromise } from "../../utils/completable_promise.js";
8
- import { ContextImpl } from "../../context_impl.js";
3
+ import { HandlerKind } from "../../types/rpc.js";
9
4
  import { parseUrlComponents } from "../components.js";
5
+ import { LogSource, LoggerContext, RestateLogLevel } from "../../logging/logger_transport.js";
6
+ import { DEFAULT_CONSOLE_LOGGER_LOG_LEVEL } from "../../logging/console_logger_transport.js";
10
7
  import { createLogger } from "../../logging/logger.js";
11
8
  import { X_RESTATE_SERVER } from "../../user_agent.js";
9
+ import { destroyLogger, registerLogger } from "./core_logging.js";
10
+ import { LogLevel, WasmHeader, WasmIdentityVerifier, WasmVM, set_log_level } from "./vm/sdk_shared_core_wasm_bindings.js";
11
+ import { CommandError, ContextImpl } from "../../context_impl.js";
12
+ import { restoreError, sanitizeError } from "../../error_sanitization.js";
12
13
  import { errorResponse, invocationIdFromHeaders, simpleResponse, tryCreateContextualLogger } from "./utils.js";
13
14
  import { handleDiscovery } from "./discovery.js";
14
15
  import { millisOrDurationToMillis } from "@restatedev/restate-sdk-core";
15
16
 
16
17
  //#region src/endpoint/handlers/generic.ts
18
+ const HOOK_CONTEXT_IS_PROCESSING_SYMBOL = Symbol.for("@restatedev/restate-sdk/hooks.isProcessing");
17
19
  function createRestateHandler(endpoint, protocolMode, additionalDiscoveryFields) {
18
20
  return new RestateHandlerImpl(endpoint, protocolMode, additionalDiscoveryFields);
19
21
  }
@@ -108,7 +110,7 @@ var RestateInvokeResponse = class {
108
110
  this.loggerId = Math.floor(Math.random() * 4294967295);
109
111
  const isJournalCodecDefined = this.journalValueCodecInit !== void 0;
110
112
  const vmHeaders = Object.entries(this.attemptHeaders).filter(([, v]) => v !== void 0).map(([k, v]) => new WasmHeader(k, v instanceof Array ? v[0] : v));
111
- this.coreVm = new WasmVM(vmHeaders, restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL), this.loggerId, isJournalCodecDefined);
113
+ this.coreVm = new WasmVM(vmHeaders, restateLogLevelToWasmLogLevel(DEFAULT_CONSOLE_LOGGER_LOG_LEVEL), this.loggerId, isJournalCodecDefined, handler.executionOptions.explicitCancellation ?? false);
112
114
  const responseHead = this.coreVm.get_response_head();
113
115
  this.statusCode = responseHead.status_code;
114
116
  this.headers = responseHead.headers.reduce((headers, { key, value }) => ({
@@ -132,6 +134,14 @@ var RestateInvokeResponse = class {
132
134
  await bufferJournalReplayInCoreVm(this.coreVm, inputReader);
133
135
  const input = this.coreVm.sys_input();
134
136
  const invocationRequest = {
137
+ target: {
138
+ service: this.service.name(),
139
+ handler: this.handler.name(),
140
+ key: input.key || void 0,
141
+ toString() {
142
+ return this.key !== void 0 ? `${this.service}/${this.key}/${this.handler}` : `${this.service}/${this.handler}`;
143
+ }
144
+ },
135
145
  id: input.invocation_id,
136
146
  headers: input.headers.reduce((headers, { key, value }) => {
137
147
  headers.set(key, value);
@@ -151,18 +161,26 @@ var RestateInvokeResponse = class {
151
161
  registerLogger(this.loggerId, this.vmLogger);
152
162
  if (!this.coreVm.is_processing()) this.vmLogger.info("Replaying invocation.");
153
163
  else this.vmLogger.info("Starting invocation.");
154
- ctx = new ContextImpl(this.coreVm, input, ctxLogger, this.handler.kind(), this.vmLogger, invocationRequest, invocationEndPromise, inputReader, outputWriter, journalValueCodec, this.service.options?.serde, this.service.options?.asTerminalError);
164
+ ctx = new ContextImpl(this.coreVm, input, ctxLogger, this.handler.kind(), this.vmLogger, invocationRequest, invocationEndPromise, inputReader, outputWriter, journalValueCodec, this.handler.executionOptions);
155
165
  } catch (e) {
156
166
  const error = ensureError(e);
157
167
  this.coreVm.notify_error(error.message, error.message);
158
168
  await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
159
169
  return;
160
170
  }
161
- startUserHandler(ctx, this.service, this.handler, journalValueCodec).finally(() => {
162
- invocationEndPromise.resolve();
163
- });
164
- await invocationEndPromise.promise;
165
- await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
171
+ abortSignal.addEventListener("abort", () => {
172
+ setImmediate(() => {
173
+ const msg = "Connection closed";
174
+ this.coreVm.notify_error(msg, msg);
175
+ });
176
+ }, { once: true });
177
+ try {
178
+ await startUserHandler(ctx, this.service, this.handler, journalValueCodec);
179
+ } catch (e) {
180
+ notifyError(e, ctx, this.handler.executionOptions.asTerminalError);
181
+ } finally {
182
+ await flushAndClose(this.coreVm, this.vmLogger, inputReader, outputWriter);
183
+ }
166
184
  }
167
185
  };
168
186
  async function bufferJournalReplayInCoreVm(coreVm, inputReader) {
@@ -176,35 +194,57 @@ async function bufferJournalReplayInCoreVm(coreVm, inputReader) {
176
194
  }
177
195
  }
178
196
  async function startUserHandler(ctx, service, handler, journalValueCodec) {
197
+ const hooks = [];
198
+ for (const provider of handler.executionOptions.hooks ?? []) {
199
+ const hookContext = { request: ctx.request() };
200
+ Object.defineProperty(hookContext, HOOK_CONTEXT_IS_PROCESSING_SYMBOL, {
201
+ value: () => ctx.isProcessing(),
202
+ enumerable: false
203
+ });
204
+ hooks.push(provider(hookContext));
205
+ }
206
+ const handlerInterceptor = composeInterceptors(hooks.map((h) => h.interceptor?.handler).filter(isDefined));
207
+ ctx.setRunInterceptor(composeInterceptors(hooks.map((h) => h.interceptor?.run).filter(isDefined)));
208
+ let encodedOutput;
209
+ await raceWithAttemptEnd(ctx, handlerInterceptor)(async () => {
210
+ const decodedInput = await journalValueCodec.decode(ctx.request().body).catch((e) => Promise.reject(new TerminalError(`Failed to decode input using journal value codec: ${ensureError(e).message}`, { errorCode: 400 })));
211
+ const output = await handler.invoke(ctx, decodedInput);
212
+ encodedOutput = journalValueCodec.encode(output);
213
+ });
214
+ ctx.coreVm.sys_write_output_success(encodedOutput);
215
+ ctx.coreVm.sys_end();
216
+ ctx.vmLogger.info("Invocation completed successfully.");
217
+ }
218
+ /**
219
+ * Classifies the error and notifies the VM. Called from the process() catch
220
+ * block — the single place that decides how to report errors to the VM.
221
+ */
222
+ function notifyError(e, ctx, asTerminalError) {
223
+ if (e instanceof CommandError) {
224
+ const cause = ensureError(e.cause);
225
+ logError(ctx.vmLogger, cause);
226
+ if (e.hasCommandIndex) ctx.coreVm.notify_error_for_specific_command(cause.message, cause.stack, e.commandType, e.commandIndex, null);
227
+ else ctx.coreVm.notify_error_for_next_command(cause.message, cause.stack, e.commandType);
228
+ return;
229
+ }
230
+ const error = ensureError(e, asTerminalError);
231
+ logError(ctx.vmLogger, error);
179
232
  try {
180
- try {
181
- const decodedInput = await journalValueCodec.decode(ctx.request().body).catch((e) => Promise.reject(new TerminalError(`Failed to decode input using journal value codec: ${ensureError(e).message}`, { errorCode: 400 })));
182
- const output = await handler.invoke(ctx, decodedInput);
183
- const encodedOutput = journalValueCodec.encode(output);
184
- ctx.coreVm.sys_write_output_success(encodedOutput);
233
+ if (error instanceof TerminalError) {
234
+ ctx.coreVm.sys_write_output_failure({
235
+ code: error.code,
236
+ message: error.message,
237
+ metadata: Object.entries(error.metadata ?? {}).map(([key, value]) => ({
238
+ key,
239
+ value
240
+ }))
241
+ });
185
242
  ctx.coreVm.sys_end();
186
- ctx.vmLogger.info("Invocation completed successfully.");
187
- } catch (e) {
188
- const error = ensureError(e, service.options?.asTerminalError);
189
- logError(ctx.vmLogger, error);
190
- if (error instanceof TerminalError) {
191
- ctx.coreVm.sys_write_output_failure({
192
- code: error.code,
193
- message: error.message,
194
- metadata: Object.entries(error.metadata ?? {}).map(([key, value]) => ({
195
- key,
196
- value
197
- }))
198
- });
199
- ctx.coreVm.sys_end();
200
- return;
201
- }
202
- throw error;
203
- }
204
- } catch (e) {
205
- const error = ensureError(e);
206
- if (error instanceof RetryableError) ctx.coreVm.notify_error_with_delay_override(error.message, error.stack, error.retryAfter !== void 0 ? BigInt(millisOrDurationToMillis(error.retryAfter)) : void 0);
243
+ } else if (error instanceof RetryableError) ctx.coreVm.notify_error_with_delay_override(error.message, error.stack, error.retryAfter !== void 0 ? BigInt(millisOrDurationToMillis(error.retryAfter)) : void 0);
207
244
  else ctx.coreVm.notify_error(error.message, error.stack);
245
+ } catch (vmError) {
246
+ const inner = ensureError(vmError);
247
+ ctx.coreVm.notify_error(inner.message, inner.stack);
208
248
  }
209
249
  }
210
250
  async function flushAndClose(coreVm, vmLogger, inputReader, outputWriter) {
@@ -232,6 +272,42 @@ async function flushAndClose(coreVm, vmLogger, inputReader, outputWriter) {
232
272
  function isAbortErrorOnWrite(error) {
233
273
  return error.name === "AbortError" || error.message === "Invalid state: WritableStream is closed" || error.code === "ERR_HTTP2_INVALID_STREAM";
234
274
  }
275
+ function composeInterceptors(interceptors) {
276
+ return interceptors.reduceRight((innerInterceptor, interceptor) => (...args) => {
277
+ const context = args.slice(0, -1);
278
+ const callback = args.at(-1);
279
+ return interceptor(...context, () => innerInterceptor(...context, callback));
280
+ }, (...args) => args.at(-1)());
281
+ }
282
+ /**
283
+ * Wraps an interceptor so that both `next()` and the interceptor body race
284
+ * against `invocationEndPromise`. When the attempt ends (suspension, retryable
285
+ * error, etc.), the promise rejects and the interceptor chain unwinds through
286
+ * catch/finally blocks — preventing interceptors from hanging on a `next()`
287
+ * that will never settle.
288
+ *
289
+ * SDK-internal metadata (CommandError, retryAfter) is stripped before the
290
+ * interceptor sees the error and restored after the chain exits.
291
+ */
292
+ function raceWithAttemptEnd(ctx, interceptor) {
293
+ return (...args) => {
294
+ let originalError;
295
+ const signal = ctx.invocationEndPromise.promise.catch((e) => {
296
+ originalError = e;
297
+ throw sanitizeError(e);
298
+ });
299
+ const originalNext = args.at(-1);
300
+ const racingNext = () => Promise.race([originalNext(), signal]);
301
+ const newArgs = [...args.slice(0, -1), racingNext];
302
+ return Promise.race([interceptor(...newArgs), signal]).catch((e) => {
303
+ if (originalError !== void 0) throw restoreError(e, originalError);
304
+ throw e;
305
+ });
306
+ };
307
+ }
308
+ function isDefined(value) {
309
+ return value != null;
310
+ }
235
311
  function restateLogLevelToWasmLogLevel(level) {
236
312
  switch (level) {
237
313
  case RestateLogLevel.TRACE: return LogLevel.TRACE;