@lostgradient/weft 0.6.0 → 0.7.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 (92) hide show
  1. package/README.md +4 -4
  2. package/dist/cli/generated/operation-client.generated.d.ts +1 -0
  3. package/dist/cli-main.js +63 -63
  4. package/dist/client/event-stream-options.d.ts +58 -0
  5. package/dist/client/event-stream-options.js +0 -0
  6. package/dist/client/event-stream.d.ts +27 -44
  7. package/dist/client/event-stream.js +21 -56
  8. package/dist/client/event-tail-lifecycle.d.ts +21 -0
  9. package/dist/client/event-tail-lifecycle.js +71 -0
  10. package/dist/client/event-tail.d.ts +3 -1
  11. package/dist/client/http-client.d.ts +4 -5
  12. package/dist/client/http-client.js +4 -1
  13. package/dist/client/http-handle.d.ts +4 -5
  14. package/dist/client/http-request.d.ts +7 -0
  15. package/dist/client/index.d.ts +2 -1
  16. package/dist/client/interface.d.ts +23 -7
  17. package/dist/client/local-handles.d.ts +2 -0
  18. package/dist/client/local-handles.js +10 -1
  19. package/dist/client/local.d.ts +3 -3
  20. package/dist/client/open-event-subscription.d.ts +3 -2
  21. package/dist/client/open-event-subscription.js +28 -1
  22. package/dist/client/sse-event-stream.d.ts +27 -0
  23. package/dist/client/sse-event-stream.js +276 -0
  24. package/dist/client/start-body.d.ts +2 -2
  25. package/dist/client/start-body.js +1 -0
  26. package/dist/core/engine/construction.js +3 -1
  27. package/dist/core/engine/engine-internal-types.d.ts +2 -0
  28. package/dist/core/engine/errors.d.ts +6 -6
  29. package/dist/core/engine/errors.js +1 -1
  30. package/dist/core/engine/handles.js +1 -1
  31. package/dist/core/engine/index.d.ts +6 -5
  32. package/dist/core/engine/index.js +1 -0
  33. package/dist/core/engine/lifecycle/recovered-services.js +8 -1
  34. package/dist/core/engine/lifecycle/start-or-signal-create.d.ts +2 -2
  35. package/dist/core/engine/lifecycle/start-or-signal-create.js +17 -15
  36. package/dist/core/engine/lifecycle/start-or-signal-resolution.d.ts +1 -1
  37. package/dist/core/engine/lifecycle/start-or-signal-resolution.js +23 -5
  38. package/dist/core/engine/lifecycle/start-or-signal.d.ts +6 -4
  39. package/dist/core/engine/lifecycle/start-or-signal.js +21 -4
  40. package/dist/core/engine/schedule-run.js +12 -3
  41. package/dist/core/engine/storage-io.js +1 -1
  42. package/dist/core/engine/termination/complete.js +3 -3
  43. package/dist/core/scheduler/scheduler-class.js +2 -1
  44. package/dist/core/start-workflow-validation.d.ts +3 -4
  45. package/dist/core/start-workflow-validation.js +1 -1
  46. package/dist/core/timeouts.d.ts +13 -1
  47. package/dist/core/timeouts.js +4 -1
  48. package/dist/core/types/constants.d.ts +16 -0
  49. package/dist/core/types/options.d.ts +60 -12
  50. package/dist/core/types/services-resolution.d.ts +49 -3
  51. package/dist/core/weft-error.d.ts +1 -1
  52. package/dist/core/weft-error.js +1 -0
  53. package/dist/index.d.ts +3 -3
  54. package/dist/index.js +1 -0
  55. package/dist/json-schema.js +1 -1
  56. package/dist/mcp/cli.js +17 -17
  57. package/dist/server/asyncapi-channels.js +82 -16
  58. package/dist/server/handler/auth-context-principal.d.ts +19 -0
  59. package/dist/server/handler/auth-context-principal.js +25 -0
  60. package/dist/server/handler/index.d.ts +2 -1
  61. package/dist/server/handler/index.js +35 -18
  62. package/dist/server/handler/route-dispatch.d.ts +12 -22
  63. package/dist/server/handler/route-dispatch.js +30 -36
  64. package/dist/server/handler/sse-route-dispatch.d.ts +25 -0
  65. package/dist/server/handler/sse-route-dispatch.js +45 -0
  66. package/dist/server/handler.js +21 -21
  67. package/dist/server/index.d.ts +8 -7
  68. package/dist/server/index.js +15 -15
  69. package/dist/server/operations/fleet-events-sse.d.ts +11 -0
  70. package/dist/server/operations/fleet-events-sse.js +164 -0
  71. package/dist/server/operations/sse-stream.d.ts +43 -0
  72. package/dist/server/operations/sse-stream.js +152 -7
  73. package/dist/server/operations/start-or-signal-workflow.d.ts +2 -0
  74. package/dist/server/operations/start-or-signal-workflow.js +25 -14
  75. package/dist/server/operations/start-workflow-options.d.ts +9 -6
  76. package/dist/server/operations/start-workflow-options.js +13 -0
  77. package/dist/server/operations/workflow-events-sse.d.ts +17 -0
  78. package/dist/server/operations/workflow-events-sse.js +200 -0
  79. package/dist/server/rest-bindings.js +12 -0
  80. package/dist/server/runtime/authentication-bridge.js +4 -0
  81. package/dist/server/runtime/cors.d.ts +1 -1
  82. package/dist/server/runtime/cors.js +6 -1
  83. package/dist/server/runtime/websocket-stream.d.ts +4 -0
  84. package/dist/server/runtime/websocket-stream.js +20 -3
  85. package/dist/server/workflow-event-feed.d.ts +1 -0
  86. package/dist/server/workflow-event-feed.js +17 -6
  87. package/dist/service-worker/index.js +28 -28
  88. package/dist/storage/typed-storage.js +1 -1
  89. package/dist/testing/index.js +17 -17
  90. package/dist/version.d.ts +1 -1
  91. package/dist/version.js +1 -1
  92. package/package.json +1 -1
@@ -82,10 +82,59 @@ export function buildWebSocketMessages(operation, definitionSchemaToJsonSchema)
82
82
  };
83
83
  }
84
84
  export function buildSseMessages(operation, definitionSchemaToJsonSchema) {
85
- const names = sseMessageNames(operation), logicalEventSchema = eventJsonSchema(operation, definitionSchemaToJsonSchema);
85
+ const logicalEventSchema = eventJsonSchema(operation, definitionSchemaToJsonSchema);
86
+ if (!isTokenSseOperation(operation)) {
87
+ const eventNames = eventSseMessageNames(operation);
88
+ return {
89
+ [eventNames.eventEnvelopeEvent]: {
90
+ name: eventNames.eventEnvelopeEvent,
91
+ contentType: "text/event-stream",
92
+ payload: logicalEventSchema,
93
+ summary: `SSE event envelope for ${operation.name}.`,
94
+ bindings: {
95
+ http: {
96
+ event: "<envelope.kind>"
97
+ }
98
+ },
99
+ "x-weft-sse-frame": "id: <cursor>\\nevent: <envelope.kind>\\ndata: <JSON event envelope>\\n\\n"
100
+ },
101
+ [eventNames.pingEvent]: {
102
+ name: eventNames.pingEvent,
103
+ contentType: "text/event-stream",
104
+ payload: {
105
+ additionalProperties: !1,
106
+ properties: {
107
+ emittedAtMs: { type: "number" }
108
+ },
109
+ required: ["emittedAtMs"],
110
+ type: "object"
111
+ },
112
+ summary: `SSE ping keepalive for ${operation.name}.`,
113
+ bindings: {
114
+ http: {
115
+ event: "ping"
116
+ }
117
+ },
118
+ "x-weft-sse-frame": 'event: ping\\ndata: {"emittedAtMs":<timestamp>}\\n\\n'
119
+ },
120
+ [eventNames.errorEvent]: {
121
+ name: eventNames.errorEvent,
122
+ contentType: "text/event-stream",
123
+ payload: sseErrorPayloadSchema(),
124
+ summary: `SSE error event for ${operation.name}.`,
125
+ bindings: {
126
+ http: {
127
+ event: "error"
128
+ }
129
+ },
130
+ "x-weft-sse-frame": "event: error\\ndata: <JSON Error>\\n\\n"
131
+ }
132
+ };
133
+ }
134
+ const tokenNames = tokenSseMessageNames(operation);
86
135
  return {
87
- [names.tokenEvent]: {
88
- name: names.tokenEvent,
136
+ [tokenNames.tokenEvent]: {
137
+ name: tokenNames.tokenEvent,
89
138
  contentType: "text/event-stream",
90
139
  payload: { type: "string" },
91
140
  summary: `SSE token event for ${operation.name}. The data: line carries the token text.`,
@@ -97,8 +146,8 @@ export function buildSseMessages(operation, definitionSchemaToJsonSchema) {
97
146
  "x-weft-sse-frame": "event: token\\nid: <sequence>\\ndata: <token-text>\\n\\n",
98
147
  "x-weft-event-schema": logicalEventSchema
99
148
  },
100
- [names.doneEvent]: {
101
- name: names.doneEvent,
149
+ [tokenNames.doneEvent]: {
150
+ name: tokenNames.doneEvent,
102
151
  contentType: "text/event-stream",
103
152
  payload: {
104
153
  additionalProperties: !1,
@@ -113,18 +162,10 @@ export function buildSseMessages(operation, definitionSchemaToJsonSchema) {
113
162
  },
114
163
  "x-weft-sse-frame": "event: done\\n\\n"
115
164
  },
116
- [names.errorEvent]: {
117
- name: names.errorEvent,
165
+ [tokenNames.errorEvent]: {
166
+ name: tokenNames.errorEvent,
118
167
  contentType: "text/event-stream",
119
- payload: {
120
- properties: {
121
- code: { type: "string" },
122
- data: {},
123
- message: { type: "string" }
124
- },
125
- required: ["message"],
126
- type: "object"
127
- },
168
+ payload: sseErrorPayloadSchema(),
128
169
  summary: `SSE error event for ${operation.name}.`,
129
170
  bindings: {
130
171
  http: {
@@ -164,6 +205,17 @@ function webSocketMessageNames(operation) {
164
205
  };
165
206
  }
166
207
  function sseMessageNames(operation) {
208
+ return isTokenSseOperation(operation) ? tokenSseMessageNames(operation) : eventSseMessageNames(operation);
209
+ }
210
+ function eventSseMessageNames(operation) {
211
+ const prefix = operationPrefix(operation);
212
+ return {
213
+ eventEnvelopeEvent: `${prefix}_eventEnvelopeEvent`,
214
+ pingEvent: `${prefix}_pingEvent`,
215
+ errorEvent: `${prefix}_errorEvent`
216
+ };
217
+ }
218
+ function tokenSseMessageNames(operation) {
167
219
  const prefix = operationPrefix(operation);
168
220
  return {
169
221
  tokenEvent: `${prefix}_tokenEvent`,
@@ -171,6 +223,20 @@ function sseMessageNames(operation) {
171
223
  errorEvent: `${prefix}_errorEvent`
172
224
  };
173
225
  }
226
+ function isTokenSseOperation(operation) {
227
+ return operation.name === "weft.workflows.streams.sse";
228
+ }
229
+ function sseErrorPayloadSchema() {
230
+ return {
231
+ properties: {
232
+ code: { type: "string" },
233
+ data: {},
234
+ message: { type: "string" }
235
+ },
236
+ required: ["message"],
237
+ type: "object"
238
+ };
239
+ }
174
240
  function messageReferenceMap(names) {
175
241
  const references = {};
176
242
  for (const [key, name] of Object.entries(names).toSorted(([left], [right]) => compareStrings(left, right)))
@@ -0,0 +1,19 @@
1
+ import type { AuthContext } from '../authentication.ts';
2
+ import { type Principal } from '../principal.ts';
3
+ /**
4
+ * Convert the REST transport's `authContext` into a `Principal`. The
5
+ * authenticator (`serve()`) reports method + optional claims; this maps
6
+ * that transport authentication result into the `Principal` used by the
7
+ * operation pipeline.
8
+ * Returns `anonymousPrincipal()` when no context is provided (public
9
+ * request).
10
+ *
11
+ * @example
12
+ * ```ts
13
+ * import { authContextToPrincipal } from '@lostgradient/weft/server/handler';
14
+ *
15
+ * const principal = authContextToPrincipal({ method: 'api-key' });
16
+ * console.log(principal.method); // 'api-key'
17
+ * ```
18
+ */
19
+ export declare function authContextToPrincipal(authContext: AuthContext | undefined): Principal;
@@ -0,0 +1,25 @@
1
+ import {
2
+ anonymousPrincipal,
3
+ principalFromApiKey,
4
+ principalFromJwtClaims,
5
+ principalFromMutualTls
6
+ } from "../principal.js";
7
+ export function authContextToPrincipal(authContext) {
8
+ if (authContext === void 0)
9
+ return anonymousPrincipal();
10
+ if (authContext.principal !== void 0)
11
+ return authContext.principal;
12
+ switch (authContext.method) {
13
+ case "jwt": {
14
+ if (authContext.claims === void 0)
15
+ throw Error("authContextToPrincipal: jwt authContext reached the pipeline without claims \u2014 " + "authenticator contract violation");
16
+ return principalFromJwtClaims(authContext.claims);
17
+ }
18
+ case "api-key":
19
+ return principalFromApiKey({ subject: "api-key-caller", scopes: [] });
20
+ case "mtls":
21
+ return principalFromMutualTls({ subject: "mtls-caller", scopes: [] });
22
+ case "public":
23
+ return anonymousPrincipal();
24
+ }
25
+ }
@@ -11,7 +11,8 @@
11
11
  */
12
12
  import type { Engine } from '../../core/engine.ts';
13
13
  import { type HandlerOptions } from './route-dispatch.ts';
14
- export { authContextToPrincipal, isOperationFaultLike, type HandlerOptions, } from './route-dispatch.ts';
14
+ export { authContextToPrincipal } from './auth-context-principal.ts';
15
+ export { isOperationFaultLike, type HandlerOptions } from './route-dispatch.ts';
15
16
  export { extractRouteParameters, getRequiredRouteParameter } from './route-matching.ts';
16
17
  /**
17
18
  * Pure HTTP request handler. Maps Request to Response.
@@ -1,31 +1,23 @@
1
1
  import { MalformedRouteParameterError } from "../rest-binding.js";
2
+ import { authContextToPrincipal } from "./auth-context-principal.js";
2
3
  import { matchRestBinding } from "./binding-dispatch.js";
3
4
  import { errorResponse } from "./response-helpers.js";
4
5
  import {
5
- authContextToPrincipal,
6
6
  defaultOperationRegistry,
7
7
  defaultRestBindings,
8
8
  DIRECT_ROUTE_EXECUTORS,
9
9
  dispatchViaExecuteOperation
10
10
  } from "./route-dispatch.js";
11
11
  import { matchDirectRoute } from "./route-matching.js";
12
- export {
13
- authContextToPrincipal,
14
- isOperationFaultLike
15
- } from "./route-dispatch.js";
12
+ export { authContextToPrincipal } from "./auth-context-principal.js";
13
+ export { isOperationFaultLike } from "./route-dispatch.js";
16
14
  export { extractRouteParameters, getRequiredRouteParameter } from "./route-matching.js";
17
- function routeParameterErrorResponse(error) {
18
- if (error instanceof MalformedRouteParameterError)
19
- return errorResponse(error.message, 400);
20
- return null;
21
- }
22
15
  function matchRouteBoundary(matcher) {
23
16
  try {
24
17
  return { kind: "matched", value: matcher() };
25
18
  } catch (error) {
26
- const response = routeParameterErrorResponse(error);
27
- if (response !== null)
28
- return { kind: "malformed", response };
19
+ if (error instanceof MalformedRouteParameterError)
20
+ return { kind: "malformed", response: errorResponse(error.message, 400) };
29
21
  throw error;
30
22
  }
31
23
  }
@@ -34,10 +26,20 @@ function validateHandlerOptions(options) {
34
26
  return null;
35
27
  return errorResponse("`restBindings` and `operationRegistry` must be supplied together (or both omitted).", 500);
36
28
  }
29
+ function liveEventStreamContextFromOptions(options) {
30
+ const context = {};
31
+ if (options?.workflowEventFeed !== void 0)
32
+ context.workflowEventFeed = options.workflowEventFeed;
33
+ if (options?.fleetEventFeed !== void 0)
34
+ context.fleetEventFeed = options.fleetEventFeed;
35
+ if (options?.acquireWorkflowStreamConnection !== void 0)
36
+ context.acquireWorkflowStreamConnection = options.acquireWorkflowStreamConnection;
37
+ return context;
38
+ }
37
39
  async function dispatchRestBinding(request, engine, bindingMatch, operationRegistry, options, url) {
38
40
  try {
39
41
  const principal = authContextToPrincipal(options?.authContext);
40
- return await dispatchViaExecuteOperation(request, engine, bindingMatch.binding, bindingMatch.pathParams, operationRegistry, principal, options?.pipelineTrace, options?.maxRequestBodyBytes);
42
+ return await dispatchViaExecuteOperation(request, engine, bindingMatch.binding, bindingMatch.pathParams, operationRegistry, principal, options?.pipelineTrace, options?.maxRequestBodyBytes, options?.supportedAuthenticationSchemes, liveEventStreamContextFromOptions(options));
41
43
  } catch (error) {
42
44
  console.error("Unhandled error in dispatchViaExecuteOperation", {
43
45
  method: request.method,
@@ -60,10 +62,7 @@ async function dispatchDirectRoute(request, engine, route, options, url) {
60
62
  return errorResponse("Internal server error", 500);
61
63
  }
62
64
  }
63
- export async function handleRequest(request, engine, options) {
64
- const url = new URL(request.url), optionError = validateHandlerOptions(options);
65
- if (optionError !== null)
66
- return optionError;
65
+ async function dispatchMatchedRoute(request, engine, options, url) {
67
66
  const directRouteLookup = matchRouteBoundary(() => matchDirectRoute(request.method, url.pathname));
68
67
  if (directRouteLookup.kind === "malformed")
69
68
  return directRouteLookup.response;
@@ -76,3 +75,21 @@ export async function handleRequest(request, engine, options) {
76
75
  return dispatchRestBinding(request, engine, bindingLookup.value, operationRegistry, options, url);
77
76
  return errorResponse(`Not found: ${request.method} ${url.pathname}`, 404);
78
77
  }
78
+ async function dispatchMatchedRouteBoundary(request, engine, options, url) {
79
+ try {
80
+ return await dispatchMatchedRoute(request, engine, options, url);
81
+ } catch (error) {
82
+ console.error("Unhandled error in handleRequest route matching", {
83
+ method: request.method,
84
+ path: url.pathname,
85
+ error
86
+ });
87
+ return errorResponse("Internal server error", 500);
88
+ }
89
+ }
90
+ export async function handleRequest(request, engine, options) {
91
+ const url = new URL(request.url), optionError = validateHandlerOptions(options);
92
+ if (optionError !== null)
93
+ return optionError;
94
+ return dispatchMatchedRouteBoundary(request, engine, options, url);
95
+ }
@@ -2,14 +2,16 @@ import type { Engine } from '../../core/engine.ts';
2
2
  import { type PrometheusExporter } from '../../observability/metrics.ts';
3
3
  import type { AuthContext } from '../authentication.ts';
4
4
  import type { DiscoveryInfo } from '../discovery-info.ts';
5
+ import type { FleetEventFeed } from '../fleet-event-feed.ts';
5
6
  import { type OpenApiSecuritySchemeName } from '../openapi.ts';
6
7
  import { type OperationRegistry, type PipelineTrace } from '../operation-catalog.ts';
7
8
  import type { OperationFault } from '../operation-fault.ts';
8
- import { type Principal } from '../principal.ts';
9
+ import type { WorkflowStreamConnectionAcquirer } from '../operations/workflow-events-sse.ts';
10
+ import type { Principal } from '../principal.ts';
9
11
  import { type UnknownRestBinding } from '../rest-bindings.ts';
12
+ import type { WorkflowEventFeed } from '../workflow-event-feed.ts';
10
13
  import type { DirectRouteHandlerName } from './route-matching.ts';
11
- /** Alias for `AuthContext` kept local so handler-internal code reads naturally. */
12
- type AuthenticatedRequestContext = AuthContext;
14
+ import { type LiveEventStreamContext } from './sse-route-dispatch.ts';
13
15
  /**
14
16
  * Options bag passed to `handleRequest` by the HTTP server wrapper.
15
17
  *
@@ -83,6 +85,12 @@ export interface HandlerOptions {
83
85
  trustedHosts?: ReadonlyArray<string>;
84
86
  /** Maximum REST operation request body size in bytes. Defaults to 1 MB. */
85
87
  maxRequestBodyBytes?: number;
88
+ /** Event feed used by live workflow SSE routes. */
89
+ workflowEventFeed?: WorkflowEventFeed;
90
+ /** Fleet feed used by live fleet SSE routes. */
91
+ fleetEventFeed?: Pick<FleetEventFeed, 'subscribe'>;
92
+ /** Shared per-workflow long-lived stream connection limiter. */
93
+ acquireWorkflowStreamConnection?: WorkflowStreamConnectionAcquirer;
86
94
  /**
87
95
  * Optional pipeline-trace observer. **Internal test seam** used by the
88
96
  * dispatch-audit suite to prove every transport drives the full
@@ -100,7 +108,7 @@ export type RouteExecutionContext = {
100
108
  };
101
109
  export type RouteExecutor = (context: RouteExecutionContext) => Promise<Response>;
102
110
  export declare const DIRECT_ROUTE_EXECUTORS: Record<DirectRouteHandlerName, RouteExecutor>;
103
- export declare function dispatchViaExecuteOperation(request: Request, engine: Engine, binding: UnknownRestBinding, pathParams: Record<string, string>, registry: OperationRegistry, principal: Principal, pipelineTrace?: PipelineTrace, maxRequestBodyBytes?: number): Promise<Response>;
111
+ export declare function dispatchViaExecuteOperation(request: Request, engine: Engine, binding: UnknownRestBinding, pathParams: Record<string, string>, registry: OperationRegistry, principal: Principal, pipelineTrace?: PipelineTrace, maxRequestBodyBytes?: number, supportedAuthenticationSchemes?: ReadonlySet<OpenApiSecuritySchemeName>, liveEventStreamContext?: LiveEventStreamContext): Promise<Response>;
104
112
  /**
105
113
  * Type guard that returns true if the value structurally resembles an
106
114
  * {@link OperationFault} (carries `code`, `message`, and `data` properties).
@@ -125,23 +133,5 @@ export declare function dispatchViaExecuteOperation(request: Request, engine: En
125
133
  * ```
126
134
  */
127
135
  export declare function isOperationFaultLike(value: unknown): value is OperationFault;
128
- /**
129
- * Convert the REST transport's `authContext` into a `Principal`. The
130
- * authenticator (`serve()`) reports method + optional claims; this maps
131
- * that transport authentication result into the `Principal` used by the
132
- * operation pipeline.
133
- * Returns `anonymousPrincipal()` when no context is provided (public
134
- * request).
135
- *
136
- * @example
137
- * ```ts
138
- * import { authContextToPrincipal } from '@lostgradient/weft/server/handler';
139
- *
140
- * const principal = authContextToPrincipal({ method: 'api-key' });
141
- * console.log(principal.method); // 'api-key'
142
- * ```
143
- */
144
- export declare function authContextToPrincipal(authContext: AuthenticatedRequestContext | undefined): Principal;
145
136
  export declare function defaultOperationRegistry(): OperationRegistry;
146
137
  export declare function defaultRestBindings(): ReadonlyArray<UnknownRestBinding>;
147
- export {};
@@ -12,12 +12,6 @@ import {
12
12
  executeOperation
13
13
  } from "../operation-catalog.js";
14
14
  import { FAULT_CODE_TO_HTTP_STATUS } from "../operation-fault.js";
15
- import {
16
- anonymousPrincipal,
17
- principalFromApiKey,
18
- principalFromJwtClaims,
19
- principalFromMutualTls
20
- } from "../principal.js";
21
15
  import {
22
16
  createLiveOperationRegistry,
23
17
  createLiveRestBindings
@@ -28,6 +22,10 @@ import {
28
22
  jsonResponse,
29
23
  negotiatedResponse
30
24
  } from "./response-helpers.js";
25
+ import {
26
+ dispatchServerSentEventsBinding,
27
+ isDirectServerSentEventsOperation
28
+ } from "./sse-route-dispatch.js";
31
29
  const defaultPrometheusExporter = createMetricsCollectorExporter(void 0);
32
30
  function resolveDiscoveryOrigin(options) {
33
31
  const { absoluteUrlDescription, path, request, serverOptions } = options;
@@ -113,17 +111,22 @@ export const DIRECT_ROUTE_EXECUTORS = {
113
111
  ...options?.discoveryInfo !== void 0 ? { discoveryInfo: options.discoveryInfo } : {}
114
112
  }))
115
113
  };
116
- export async function dispatchViaExecuteOperation(request, engine, binding, pathParams, registry, principal, pipelineTrace, maxRequestBodyBytes) {
117
- let input;
118
- try {
119
- input = await binding.extractInput(request, pathParams, maxRequestBodyBytes !== void 0 ? { maxBodyBytes: maxRequestBodyBytes } : {});
120
- } catch (error) {
121
- if (isOperationFaultLike(error))
122
- return binding.shapeFault ? binding.shapeFault(error) : faultToHttpResponse(error);
123
- const message = error instanceof Error ? error.message : String(error);
124
- return errorResponse(message, 400);
125
- }
126
- const result = await executeOperation(binding.operationName, input, {
114
+ export async function dispatchViaExecuteOperation(request, engine, binding, pathParams, registry, principal, pipelineTrace, maxRequestBodyBytes, supportedAuthenticationSchemes, liveEventStreamContext) {
115
+ const extracted = await extractRestBindingInput(request, binding, pathParams, maxRequestBodyBytes);
116
+ if (!extracted.ok)
117
+ return extracted.response;
118
+ if (isDirectServerSentEventsOperation(binding.operationName))
119
+ return dispatchServerSentEventsBinding({
120
+ request,
121
+ binding,
122
+ rawInput: extracted.input,
123
+ principal,
124
+ registry,
125
+ ...pipelineTrace === void 0 ? {} : { pipelineTrace },
126
+ ...supportedAuthenticationSchemes === void 0 ? {} : { supportedAuthenticationSchemes },
127
+ ...liveEventStreamContext === void 0 ? {} : { liveEventStreamContext }
128
+ });
129
+ const result = await executeOperation(binding.operationName, extracted.input, {
127
130
  principal,
128
131
  engine,
129
132
  transport: "http-rest",
@@ -134,6 +137,16 @@ export async function dispatchViaExecuteOperation(request, engine, binding, path
134
137
  return binding.shapeSuccess ? binding.shapeSuccess(result.value, request) : defaultShapeSuccess(result.value, binding.success);
135
138
  return binding.shapeFault ? binding.shapeFault(result.fault) : faultToHttpResponse(result.fault);
136
139
  }
140
+ async function extractRestBindingInput(request, binding, pathParams, maxRequestBodyBytes) {
141
+ try {
142
+ return { ok: !0, input: await binding.extractInput(request, pathParams, maxRequestBodyBytes !== void 0 ? { maxBodyBytes: maxRequestBodyBytes } : {}) };
143
+ } catch (error) {
144
+ if (isOperationFaultLike(error))
145
+ return { ok: !1, response: binding.shapeFault ? binding.shapeFault(error) : faultToHttpResponse(error) };
146
+ const message = error instanceof Error ? error.message : String(error);
147
+ return { ok: !1, response: errorResponse(message, 400) };
148
+ }
149
+ }
137
150
  function hasRequiredFaultProperties(candidate) {
138
151
  return Object.hasOwn(candidate, "code") && Object.hasOwn(candidate, "message") && Object.hasOwn(candidate, "data");
139
152
  }
@@ -159,25 +172,6 @@ export function isOperationFaultLike(value) {
159
172
  return !1;
160
173
  return typeof fields.code === "string" && Object.hasOwn(FAULT_CODE_TO_HTTP_STATUS, fields.code) && typeof fields.message === "string" && typeof fields.data === "object" && fields.data !== null && !Array.isArray(fields.data);
161
174
  }
162
- export function authContextToPrincipal(authContext) {
163
- if (authContext === void 0)
164
- return anonymousPrincipal();
165
- if (authContext.principal !== void 0)
166
- return authContext.principal;
167
- switch (authContext.method) {
168
- case "jwt": {
169
- if (authContext.claims === void 0)
170
- throw Error("authContextToPrincipal: jwt authContext reached the pipeline without claims \u2014 " + "authenticator contract violation");
171
- return principalFromJwtClaims(authContext.claims);
172
- }
173
- case "api-key":
174
- return principalFromApiKey({ subject: "api-key-caller", scopes: [] });
175
- case "mtls":
176
- return principalFromMutualTls({ subject: "mtls-caller", scopes: [] });
177
- case "public":
178
- return anonymousPrincipal();
179
- }
180
- }
181
175
  let defaultOperationRegistryCache;
182
176
  export function defaultOperationRegistry() {
183
177
  if (defaultOperationRegistryCache === void 0)
@@ -0,0 +1,25 @@
1
+ import type { FleetEventFeed } from '../fleet-event-feed.ts';
2
+ import type { OpenApiSecuritySchemeName } from '../openapi.ts';
3
+ import { type OperationRegistry, type PipelineTrace } from '../operation-catalog.ts';
4
+ import { type WorkflowStreamConnectionAcquirer } from '../operations/workflow-events-sse.ts';
5
+ import { type Principal } from '../principal.ts';
6
+ import type { UnknownRestBinding } from '../rest-bindings.ts';
7
+ import type { WorkflowEventFeed } from '../workflow-event-feed.ts';
8
+ export type LiveEventStreamContext = {
9
+ workflowEventFeed?: WorkflowEventFeed;
10
+ fleetEventFeed?: Pick<FleetEventFeed, 'subscribe'>;
11
+ acquireWorkflowStreamConnection?: WorkflowStreamConnectionAcquirer;
12
+ };
13
+ type DispatchServerSentEventsBindingOptions = {
14
+ readonly request: Request;
15
+ readonly binding: UnknownRestBinding;
16
+ readonly rawInput: unknown;
17
+ readonly principal: Principal;
18
+ readonly registry: OperationRegistry;
19
+ readonly pipelineTrace?: PipelineTrace;
20
+ readonly supportedAuthenticationSchemes?: ReadonlySet<OpenApiSecuritySchemeName>;
21
+ readonly liveEventStreamContext?: LiveEventStreamContext;
22
+ };
23
+ export declare function isDirectServerSentEventsOperation(operationName: string): boolean;
24
+ export declare function dispatchServerSentEventsBinding(options: DispatchServerSentEventsBindingOptions): Promise<Response>;
25
+ export {};
@@ -0,0 +1,45 @@
1
+ import { faultToHttpResponse } from "../fault-to-http.js";
2
+ import {
3
+ executeOperation
4
+ } from "../operation-catalog.js";
5
+ import { fleetEventsSseOperation } from "../operations/fleet-events-sse.js";
6
+ import {
7
+ workflowEventsSseOperation
8
+ } from "../operations/workflow-events-sse.js";
9
+ import { isAuthenticated, principalFromStdioLocal } from "../principal.js";
10
+ export function isDirectServerSentEventsOperation(operationName) {
11
+ return operationName === workflowEventsSseOperation.name || operationName === fleetEventsSseOperation.name;
12
+ }
13
+ export async function dispatchServerSentEventsBinding(options) {
14
+ const { binding, registry } = options, missingOperationResponse = shapeMissingOperation(binding, registry);
15
+ if (missingOperationResponse !== null)
16
+ return missingOperationResponse;
17
+ const shapeSuccess = binding.shapeSuccess;
18
+ if (shapeSuccess === void 0)
19
+ throw Error("SSE binding missing success shaper");
20
+ const result = await executeOperation(binding.operationName, options.rawInput, {
21
+ principal: principalForServerSentEvents(options),
22
+ engine: options.liveEventStreamContext ?? {},
23
+ transport: "http-rest",
24
+ registry,
25
+ ...options.pipelineTrace === void 0 ? {} : { pipelineTrace: options.pipelineTrace }
26
+ });
27
+ if (result.ok)
28
+ return shapeSuccess(result.value, options.request);
29
+ return binding.shapeFault ? binding.shapeFault(result.fault) : faultToHttpResponse(result.fault);
30
+ }
31
+ function shapeMissingOperation(binding, registry) {
32
+ if (registry.get(binding.operationName) !== void 0)
33
+ return null;
34
+ const fault = {
35
+ code: "MethodNotFound",
36
+ message: `unknown operation: ${binding.operationName}`,
37
+ data: { method: binding.operationName }
38
+ };
39
+ return binding.shapeFault ? binding.shapeFault(fault) : faultToHttpResponse(fault);
40
+ }
41
+ function principalForServerSentEvents(options) {
42
+ if (options.supportedAuthenticationSchemes?.size === 0 && !isAuthenticated(options.principal))
43
+ return principalFromStdioLocal();
44
+ return options.principal;
45
+ }