@pulse-compute/runtime 0.0.0 → 1.0.0-beta.2

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/src/host.d.ts ADDED
@@ -0,0 +1,420 @@
1
+ import type {
2
+ Handler,
3
+ PulseEffect,
4
+ PulseKvGeneration, PulseKvVersionedResult, PulseKvConditionalResult,
5
+ PulseFetchResponse,
6
+ PulseParallelEffect,
7
+ PulseParallelResult,
8
+ Router
9
+ } from './index';
10
+
11
+ /** Provider-owned execution metadata supplied to every JavaScript effect. */
12
+ export interface PulseJavascriptEffectHostExecution {
13
+ readonly kind: 'request' | 'event';
14
+ readonly request?: Request;
15
+ readonly event?: PulseEventFrame;
16
+ readonly application?: unknown;
17
+ readonly signal?: AbortSignal;
18
+ readonly deadlineMonotonicMs?: number;
19
+ /** Adds execution-owned sensitive text to runtime redaction before provider work continues. */
20
+ registerRedactionValue(value: string | Uint8Array): void;
21
+ /** Applies one project-owned schema codec directly to an in-memory semantic value. */
22
+ validateSchemaValue(
23
+ schemaId: string,
24
+ value: unknown,
25
+ context?: Readonly<{ source?: string; operationId?: string }>
26
+ ): unknown;
27
+ }
28
+
29
+ /** Canonical execution-owned effect descriptor delivered to a trusted provider adapter. */
30
+ export interface PulseJavascriptEffectDescriptor {
31
+ readonly version: 'pulse.javascript-effect.v1';
32
+ readonly id: string;
33
+ readonly kind: string;
34
+ readonly providerKind: string;
35
+ readonly operation: string;
36
+ readonly capability: string;
37
+ readonly [field: string]: unknown;
38
+ }
39
+
40
+ /** Host-only preparation hook. Resolving/staging must not dispatch; invoking
41
+ * the returned primitive is the conservative storage dispatch boundary. */
42
+ export interface PulseConditionalKvExecution {
43
+ readonly signal?: AbortSignal;
44
+ readonly deadlineMonotonicMs?: number;
45
+ registerRedactionValue?(value: string): void;
46
+ onKvObservation?(value: Readonly<Record<string, unknown>>): void;
47
+ }
48
+ export interface PulseKvClock {
49
+ now(): number;
50
+ setTimeout(callback: () => void, delayMs: number): unknown;
51
+ clearTimeout(handle: unknown): void;
52
+ }
53
+ export type PulseConditionalKvPreparation = (
54
+ effect: Readonly<Record<string, unknown>>,
55
+ execution: PulseConditionalKvExecution
56
+ ) => Promise<(() => unknown) | undefined> | (() => unknown) | undefined;
57
+
58
+ export interface PulseJavascriptEffectAdapterInput {
59
+ prepareConditionalKv?: PulseConditionalKvPreparation;
60
+ readonly id?: string;
61
+ dispatch(
62
+ effect: PulseJavascriptEffectDescriptor,
63
+ execution: PulseJavascriptEffectHostExecution
64
+ ): Promise<unknown> | unknown;
65
+ dispose?(execution: PulseJavascriptEffectHostExecution): Promise<void> | void;
66
+ }
67
+
68
+ export interface PulseJavascriptEffectAdapter {
69
+ readonly version: 'pulse.javascript-effect-adapter.v1';
70
+ readonly prepareConditionalKv?: PulseConditionalKvPreparation;
71
+ readonly id: string;
72
+ dispatch(
73
+ effect: PulseJavascriptEffectDescriptor,
74
+ execution: PulseJavascriptEffectHostExecution
75
+ ): Promise<unknown> | unknown;
76
+ dispose?(execution: PulseJavascriptEffectHostExecution): Promise<void> | void;
77
+ }
78
+
79
+ export interface PulseEventAdapterSemantics {
80
+ readonly acceptance: 'host-accepted';
81
+ readonly deliveryGuarantee: 'none';
82
+ readonly autoLoopback: false;
83
+ readonly sameStackReentry: false;
84
+ readonly queueDiscipline: 'fifo-per-declared-adapter';
85
+ readonly executionIsolation: 'one-event-frame-per-execution';
86
+ }
87
+
88
+ export interface PulseEventRecordingAdapterOptions {
89
+ readonly id?: string;
90
+ readonly maxQueueDepth?: number;
91
+ }
92
+
93
+ /** Bounded deterministic event acceptance adapter. It never invokes a local application handler. */
94
+ export interface PulseEventRecordingAdapter extends PulseJavascriptEffectAdapter {
95
+ readonly eventAdapter: {
96
+ readonly version: 'pulse.event-adapter.v1';
97
+ readonly id: string;
98
+ readonly maxQueueDepth: number;
99
+ readonly semantics: PulseEventAdapterSemantics;
100
+ };
101
+ acceptedFrames(): readonly PulseEventFrame[];
102
+ }
103
+
104
+ export interface PulseRuntimeHostKvNamespace<T = unknown> {
105
+ getVersioned?(key: string): Promise<PulseKvVersionedResult<T>> | PulseKvVersionedResult<T>;
106
+ insertIfAbsent?(key: string, value: T): Promise<PulseKvConditionalResult> | PulseKvConditionalResult;
107
+ compareAndSwap?(key: string, generation: PulseKvGeneration, value: T): Promise<PulseKvConditionalResult> | PulseKvConditionalResult;
108
+ get(key: string): Promise<T | undefined> | T | undefined;
109
+ put(key: string, value: T): Promise<boolean> | boolean;
110
+ }
111
+
112
+ /** Compatibility injection shape retained while providers move to one effect adapter. */
113
+ export interface PulseRuntimeHostCapabilities {
114
+ prepareConditionalKv?: PulseConditionalKvPreparation;
115
+ fetch?(
116
+ url: string,
117
+ init?: unknown,
118
+ execution?: PulseJavascriptEffectHostExecution
119
+ ): Promise<Response | PulseFetchResponse> | Response | PulseFetchResponse;
120
+ config?(
121
+ name: string,
122
+ execution?: PulseJavascriptEffectHostExecution
123
+ ): Promise<string | undefined> | string | undefined;
124
+ secret?(
125
+ name: string,
126
+ execution?: PulseJavascriptEffectHostExecution
127
+ ): Promise<string | undefined> | string | undefined;
128
+ kv?<T = unknown>(
129
+ name: string,
130
+ execution?: PulseJavascriptEffectHostExecution
131
+ ): PulseRuntimeHostKvNamespace<T> | undefined;
132
+ emit?(
133
+ frame: PulseEventFrame,
134
+ execution?: PulseJavascriptEffectHostExecution
135
+ ): Promise<void> | void;
136
+ effect?(
137
+ effect: PulseJavascriptEffectDescriptor,
138
+ execution: PulseJavascriptEffectHostExecution
139
+ ): Promise<unknown> | unknown;
140
+ }
141
+
142
+ export interface PulseJavascriptEffectObservation {
143
+ readonly version: 'pulse.javascript-effect-observation.v1';
144
+ readonly type: 'effect-dispatched' | 'effect-settled' | 'parallel-dispatched' | 'parallel-settled' | 'kv-lifecycle';
145
+ readonly [field: string]: unknown;
146
+ }
147
+
148
+ export interface PulseJavascriptEffectGroupSummary {
149
+ readonly id: string;
150
+ readonly keys: readonly string[];
151
+ readonly effectIds: readonly string[];
152
+ }
153
+
154
+ export interface PulseJavascriptEffectSummary {
155
+ readonly version: 'pulse.javascript-effect-observation.v1';
156
+ readonly adapter: {
157
+ readonly version: 'pulse.javascript-effect-adapter.v1';
158
+ readonly prepareConditionalKv?: PulseConditionalKvPreparation;
159
+ readonly id: string;
160
+ };
161
+ readonly effectCount: number;
162
+ readonly localEffectCount: number;
163
+ readonly ownedEffectCount: number;
164
+ readonly parallelCount: number;
165
+ readonly resolutionOrder: readonly string[];
166
+ readonly groups: readonly PulseJavascriptEffectGroupSummary[];
167
+ readonly observations: readonly PulseJavascriptEffectObservation[];
168
+ readonly redactedSecretCount: number;
169
+ readonly aborted: boolean;
170
+ readonly abortCode?: string;
171
+ readonly closed: boolean;
172
+ }
173
+
174
+ /** Maintainer bridge used by the managed Router lifecycle and focused adapter tests. */
175
+ export interface PulseJavascriptEffectExecution {
176
+ /** Execution-owned local projection. It participates in lifecycle containment but is not parallel-eligible. */
177
+ local<T = unknown>(
178
+ kind: string,
179
+ producer: (execution: PulseJavascriptEffectHostExecution) => T | PromiseLike<T>
180
+ ): PulseEffect<T>;
181
+ dispatch<T = unknown>(
182
+ effect: Omit<PulseJavascriptEffectDescriptor, 'version' | 'id'> & { readonly id?: string },
183
+ projector?: (value: unknown) => T
184
+ ): PulseParallelEffect<T>;
185
+ project<T, U>(
186
+ effect: PulseParallelEffect<T>,
187
+ projection: string,
188
+ projector: (value: T) => U | PromiseLike<U>
189
+ ): PulseParallelEffect<U>;
190
+ parallel<T extends Readonly<Record<string, PulseParallelEffect<unknown>>>>(
191
+ effects: T
192
+ ): PulseEffect<PulseParallelResult<T>>;
193
+ /** Applies execution-owned secret redaction to an error before it crosses a managed boundary. */
194
+ redactError(error: unknown): unknown;
195
+ /** Applies execution-owned secret and sensitive-field redaction to structured evidence. */
196
+ redactValue<T = unknown>(value: T): unknown;
197
+ assertIdle(): Promise<void>;
198
+ summary(): PulseJavascriptEffectSummary;
199
+ close(): Promise<void>;
200
+ }
201
+
202
+ export interface PulseRuntimeExecutionOptions {
203
+ /** Host-only monotonic clock/scheduler injection for deterministic lifecycle evidence. */
204
+ readonly kvClock?: PulseKvClock;
205
+ /** Host monotonic deadline; conditional KV uses the earlier of this and 10 seconds. */
206
+ readonly deadlineMonotonicMs?: number;
207
+ /** Compatibility bridge. New providers should inject one shared effectAdapter instead. */
208
+ readonly capabilities?: PulseRuntimeHostCapabilities;
209
+ readonly effectAdapter?: PulseJavascriptEffectAdapter | PulseJavascriptEffectAdapterInput;
210
+ readonly application?: unknown;
211
+ /** Managed execution plane. Direct effect executions default to request. */
212
+ readonly executionKind?: 'request' | 'event';
213
+ /** Normalized event input supplied only for event-owned adapter work. */
214
+ readonly event?: PulseEventFrame;
215
+ readonly requestHeaders?: readonly (readonly [string, string])[];
216
+ readonly signal?: AbortSignal;
217
+ readonly maxEffects?: number;
218
+ /** Optional stricter limits for direct event ingress normalization. */
219
+ readonly eventLimits?: PulseEventLimits;
220
+ /** Maximum bytes materialized by ctx.req.text()/json(). */
221
+ readonly maxRequestBodyBytes?: number;
222
+ /** Maximum bytes materialized by fetched response text()/json() projections. */
223
+ readonly maxFetchBodyBytes?: number;
224
+ /** Shared fallback for request and fetched structured bodies. */
225
+ readonly maxStructuredBodyBytes?: number;
226
+ /** Maximum UTF-8 bytes in config and secret names. */
227
+ readonly maxBindingNameBytes?: number;
228
+ /** Maximum UTF-8 bytes returned by config and secret providers. */
229
+ readonly maxBindingValueBytes?: number;
230
+ /** Maximum UTF-8 bytes in a KV namespace name. */
231
+ readonly maxKvNamespaceBytes?: number;
232
+ /** Maximum UTF-8 bytes in a KV key. */
233
+ readonly maxKvKeyBytes?: number;
234
+ /** Maximum serialized bytes in a detached KV value. */
235
+ readonly maxKvValueBytes?: number;
236
+ /** Maximum nesting depth in a detached KV value. */
237
+ readonly maxKvValueDepth?: number;
238
+ /** Maximum entries in a detached KV value. */
239
+ readonly maxKvValueEntries?: number;
240
+ /** Compiler-generated project schema codec table. This is a host integration surface, not an authoring API. */
241
+ readonly schemaCodecs?: {
242
+ readonly registry?: unknown;
243
+ has(schemaId: string): boolean;
244
+ decode(schemaId: string, value: unknown, source?: string): unknown;
245
+ decodeJsonText(schemaId: string, text: string, source?: string): unknown;
246
+ encodeJsonText(schemaId: string, value: unknown, source?: string): string;
247
+ responseCase?(responseCaseId: string): { readonly id: string; readonly status: number; readonly schemaId: string } | undefined;
248
+ createTraceEvent?(event: unknown, options?: unknown): unknown;
249
+ };
250
+ /** Enforces registered schema IDs at every semantic JSON boundary. */
251
+ readonly strict?: boolean;
252
+ /** Provider-known sensitive values available before the first secret read. */
253
+ readonly redactionValues?: readonly string[];
254
+ /** Resolved synchronous logging threshold. */
255
+ readonly reporting?: 'off' | 'error' | 'warn' | 'info' | 'debug' | 0 | 1 | 2 | 3 | 4;
256
+ /** Provider-owned best-effort logging sink. */
257
+ readonly log?: (level: 1 | 2 | 3 | 4, message: string, event: Readonly<Record<string, unknown>>) => void;
258
+ readonly onLogObservation?: (event: Readonly<Record<string, unknown>>) => void;
259
+ readonly onJsonTrace?: (event: unknown) => void;
260
+ readonly onEffectObservation?: (observation: PulseJavascriptEffectObservation) => void;
261
+ readonly onEffectSummary?: (summary: PulseJavascriptEffectSummary) => void;
262
+ }
263
+
264
+ export declare class PulseRuntimeContractError extends Error {
265
+ readonly code: string;
266
+ readonly detail?: unknown;
267
+ }
268
+
269
+ export declare class PulseUnhandledError extends Error {
270
+ readonly code: 'PULSE_RUNTIME_UNHANDLED_ERROR';
271
+ }
272
+
273
+ export declare function createJavascriptEffectAdapter(
274
+ input: PulseJavascriptEffectAdapterInput
275
+ ): PulseJavascriptEffectAdapter;
276
+ export declare function createCapabilityEffectAdapter(
277
+ capabilities?: PulseRuntimeHostCapabilities
278
+ ): PulseJavascriptEffectAdapter;
279
+ export declare function createEventRecordingAdapter(
280
+ options?: PulseEventRecordingAdapterOptions
281
+ ): PulseEventRecordingAdapter;
282
+ export declare function createJavascriptEffectExecution(
283
+ options?: PulseRuntimeExecutionOptions & { readonly request?: Request }
284
+ ): PulseJavascriptEffectExecution;
285
+ export interface PulseBindingValueLimits {
286
+ readonly maxBindingNameBytes?: number;
287
+ readonly maxBindingValueBytes?: number;
288
+ readonly maxKvNamespaceBytes?: number;
289
+ readonly maxKvKeyBytes?: number;
290
+ readonly maxKvValueBytes?: number;
291
+ readonly maxKvValueDepth?: number;
292
+ readonly maxKvValueEntries?: number;
293
+ }
294
+ export declare function normalizeBindingName(kind: string, value: unknown, options?: PulseBindingValueLimits): string;
295
+ export declare function normalizeBindingValue(
296
+ capability: string,
297
+ name: string,
298
+ value: unknown,
299
+ options?: PulseBindingValueLimits
300
+ ): string | undefined;
301
+ export declare function normalizeKvNamespace(value: unknown, options?: PulseBindingValueLimits): string;
302
+ export declare function normalizeKvKey(value: unknown, options?: PulseBindingValueLimits): string;
303
+ export declare function cloneKvValue<T>(
304
+ value: T,
305
+ options?: {
306
+ readonly operation?: string;
307
+ readonly allowUndefined?: boolean;
308
+ readonly maxDepth?: number;
309
+ readonly maxEntries?: number;
310
+ readonly maxBytes?: number;
311
+ readonly maxKvValueBytes?: number;
312
+ readonly maxKvValueDepth?: number;
313
+ readonly maxKvValueEntries?: number;
314
+ }
315
+ ): T;
316
+ export declare function normalizeKvPutResult(value: unknown): boolean;
317
+ export interface PulseRedactionState {
318
+ add(value: unknown): unknown;
319
+ count(): number;
320
+ redactString(value: unknown): string;
321
+ redactValue(value: unknown): unknown;
322
+ redactError(error: unknown): unknown;
323
+ values(): readonly '<redacted>'[];
324
+ summary(): { readonly registeredSecretCount: number };
325
+ }
326
+ export declare function createRedactionState(initialValues?: Iterable<unknown> | string): PulseRedactionState;
327
+ export declare function isPulseJavascriptEffect(value: unknown): boolean;
328
+
329
+ export declare function isRouterApplication(value: unknown): value is Router;
330
+ export declare function assertRouterApplication(value: unknown): Router;
331
+ export declare function normalizeApplication(value: unknown): Router;
332
+ export declare function executeRouter(
333
+ application: Router,
334
+ request: Request,
335
+ options?: PulseRuntimeExecutionOptions
336
+ ): Promise<Response>;
337
+ export declare function executeApplication(
338
+ application: Router | Handler,
339
+ request: Request,
340
+ options?: PulseRuntimeExecutionOptions
341
+ ): Promise<Response>;
342
+ export interface PulseEventLimits {
343
+ readonly maxTypeBytes?: number;
344
+ readonly maxSchemaIdBytes?: number;
345
+ readonly maxPayloadBytes?: number;
346
+ readonly maxPayloadDepth?: number;
347
+ readonly maxPayloadEntries?: number;
348
+ readonly maxEvents?: number;
349
+ }
350
+ export type PulseEventFrame<Payload = unknown> =
351
+ | Readonly<{
352
+ version: 'pulse.event-frame.v1';
353
+ type: string;
354
+ schemaId: string;
355
+ payload: Payload;
356
+ }>
357
+ | Readonly<{
358
+ version: 'pulse.event-frame.v1';
359
+ type: string;
360
+ schemaId: null;
361
+ payload?: never;
362
+ }>;
363
+ export type PulseEventExecutionResult =
364
+ | Readonly<{
365
+ version: 'pulse.event-execution-result.v1';
366
+ status: 'completed';
367
+ }>
368
+ | Readonly<{
369
+ version: 'pulse.event-execution-result.v1';
370
+ status: 'failed';
371
+ error: unknown;
372
+ }>;
373
+ /** Direct, transport-free JavaScript execution of one canonical Pulse event frame. */
374
+ export declare function executeEvent(
375
+ application: Router,
376
+ frame: PulseEventFrame,
377
+ options?: PulseRuntimeExecutionOptions
378
+ ): Promise<PulseEventExecutionResult>;
379
+ export declare function responseBodyClass(response: Response): 'structured' | 'opaque';
380
+ export declare function responseHeaderPairs(response: Response): readonly (readonly [string, string])[];
381
+ export declare function isPulseFetchResponse(value: unknown): value is PulseFetchResponse;
382
+ /** Marks a provider-owned Web Response as an opaque pass-through without copying its body. */
383
+ export declare function markOpaqueResponse(
384
+ response: Response,
385
+ headers?: Headers | Readonly<Record<string, string>> | readonly (readonly [string, string])[]
386
+ ): Response;
387
+ export declare function wrapStructuredResponse(response: Response): PulseFetchResponse;
388
+ export declare function createOpaqueFetchResponse(input?: {
389
+ readonly status?: number;
390
+ readonly headers?: Headers | Readonly<Record<string, string>> | readonly (readonly [string, string])[];
391
+ readonly bodyHandle?: unknown;
392
+ readonly response?: Response;
393
+ }): PulseFetchResponse;
394
+
395
+ export declare const RUNTIME_HOST_API_VERSION: 'pulse.runtime-host.v3';
396
+ export declare const JAVASCRIPT_EFFECT_PROTOCOL_VERSION: 'pulse.javascript-effect.v1';
397
+ export declare const JAVASCRIPT_EFFECT_ADAPTER_VERSION: 'pulse.javascript-effect-adapter.v1';
398
+ export declare const JAVASCRIPT_EFFECT_OBSERVATION_VERSION: 'pulse.javascript-effect-observation.v1';
399
+ export declare const EVENT_ADAPTER_VERSION: 'pulse.event-adapter.v1';
400
+ export declare const EVENT_ADAPTER_SEMANTICS: PulseEventAdapterSemantics;
401
+ export declare const EVENT_EMIT_CODES: Readonly<{
402
+ INPUT_INVALID: 'PULSE_RUNTIME_EVENT_EMIT_INPUT_INVALID';
403
+ SCHEMA_INVALID: 'PULSE_RUNTIME_EVENT_EMIT_SCHEMA_INVALID';
404
+ ACCEPTANCE_INVALID: 'PULSE_RUNTIME_EVENT_EMIT_ACCEPTANCE_INVALID';
405
+ QUEUE_FULL: 'PULSE_RUNTIME_EVENT_EMIT_QUEUE_FULL';
406
+ ADAPTER_INVALID: 'PULSE_RUNTIME_EVENT_ADAPTER_INVALID';
407
+ }>;
408
+
409
+ /** Shared host-only conditional KV boundary; no provider SDK tokens enter as numbers. */
410
+ export declare const KV_CONDITIONAL_KINDS: readonly string[];
411
+ export declare const KV_CONDITIONAL_LIMITS: Readonly<Record<string, number>>;
412
+ export declare function isConditionalKv(kind: string): boolean;
413
+ export declare function normalizeKvGeneration(value: unknown): PulseKvGeneration;
414
+ export declare function normalizeConditionalKvKey(value: unknown, options?: PulseBindingValueLimits): string;
415
+ export declare function encodeConditionalKvValue(value: unknown, options?: PulseBindingValueLimits): Uint8Array;
416
+ export declare function decodeConditionalKvValue(bytes: Uint8Array, options?: PulseBindingValueLimits): unknown;
417
+ export declare function admitConditionalKv(input: Readonly<Record<string, unknown>>, options?: PulseRuntimeExecutionOptions): Readonly<Record<string, unknown>>;
418
+ export declare function normalizeConditionalKvResult(effect: Readonly<{ kind: string }>, value: unknown, options?: PulseBindingValueLimits): PulseKvVersionedResult<unknown> | PulseKvConditionalResult;
419
+ export declare function executeConditionalKv(effect: Readonly<Record<string, unknown>>, prepare: PulseConditionalKvPreparation, execution?: PulseConditionalKvExecution, options?: PulseRuntimeExecutionOptions): Promise<PulseKvVersionedResult<unknown> | PulseKvConditionalResult>;
420
+ export declare function registerKvRedactions(effect: Readonly<Record<string, unknown>>, register?: (value: string) => void): void;
package/src/host.js ADDED
@@ -0,0 +1,115 @@
1
+ 'use strict';
2
+
3
+ const {
4
+ PulseRuntimeContractError,
5
+ PulseUnhandledError,
6
+ createOpaqueFetchResponse,
7
+ isPulseFetchResponse,
8
+ markOpaqueResponse,
9
+ responseBodyClass,
10
+ executeRouter,
11
+ routerEntries,
12
+ wrapStructuredResponse
13
+ } = require('./internal/index.js');
14
+ const {
15
+ JAVASCRIPT_EFFECT_PROTOCOL_VERSION,
16
+ JAVASCRIPT_EFFECT_ADAPTER_VERSION,
17
+ JAVASCRIPT_EFFECT_OBSERVATION_VERSION,
18
+ createJavascriptEffectAdapter,
19
+ createCapabilityEffectAdapter,
20
+ createJavascriptEffectExecution,
21
+ isPulseJavascriptEffect
22
+ } = require('./internal/effect-adapter.js');
23
+ const {
24
+ cloneKvValue,
25
+ normalizeBindingName,
26
+ normalizeBindingValue,
27
+ normalizeKvKey,
28
+ normalizeKvNamespace,
29
+ normalizeKvPutResult
30
+ } = require('./internal/bindings.js');
31
+ const { createRedactionState } = require('./internal/redaction.js');
32
+ const { executeEvent } = require('./internal/event-execution.js');
33
+ const {
34
+ EVENT_ADAPTER_VERSION,
35
+ EVENT_ADAPTER_SEMANTICS,
36
+ EVENT_EMIT_CODES,
37
+ createEventRecordingAdapter
38
+ } = require('./internal/event-emission.js');
39
+ const { responseHeaderPairs } = require('./internal/response.js');
40
+ const { Router } = require('./index.js');
41
+
42
+ const RUNTIME_HOST_API_VERSION = 'pulse.runtime-host.v3';
43
+
44
+ function isRouterApplication(value) {
45
+ try {
46
+ routerEntries(value);
47
+ return true;
48
+ } catch (_) {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ function normalizeApplication(value) {
54
+ if (isRouterApplication(value)) return value;
55
+ if (typeof value === 'function') {
56
+ const router = new Router();
57
+ router.use(async (ctx) => value(ctx));
58
+ return router;
59
+ }
60
+ throw new PulseRuntimeContractError(
61
+ 'PULSE_RUNTIME_APPLICATION_EXPORT_INVALID',
62
+ 'Pulse JavaScript application modules must default-export a Router, Pulse application, or managed handler.'
63
+ );
64
+ }
65
+
66
+ function assertRouterApplication(value) {
67
+ if (!isRouterApplication(value)) {
68
+ throw new PulseRuntimeContractError(
69
+ 'PULSE_RUNTIME_APPLICATION_EXPORT_INVALID',
70
+ 'Expected a Router or Pulse application.'
71
+ );
72
+ }
73
+ return value;
74
+ }
75
+
76
+ async function executeApplication(application, request, options = {}) {
77
+ return executeRouter(normalizeApplication(application), request, options);
78
+ }
79
+
80
+ module.exports = Object.freeze({
81
+ ...require('./internal/conditional-kv.js'),
82
+ RUNTIME_HOST_API_VERSION,
83
+ JAVASCRIPT_EFFECT_PROTOCOL_VERSION,
84
+ JAVASCRIPT_EFFECT_ADAPTER_VERSION,
85
+ JAVASCRIPT_EFFECT_OBSERVATION_VERSION,
86
+ EVENT_ADAPTER_VERSION,
87
+ EVENT_ADAPTER_SEMANTICS,
88
+ EVENT_EMIT_CODES,
89
+ PulseRuntimeContractError,
90
+ PulseUnhandledError,
91
+ assertRouterApplication,
92
+ cloneKvValue,
93
+ createCapabilityEffectAdapter,
94
+ createEventRecordingAdapter,
95
+ createJavascriptEffectAdapter,
96
+ createJavascriptEffectExecution,
97
+ createRedactionState,
98
+ createOpaqueFetchResponse,
99
+ executeApplication,
100
+ executeEvent,
101
+ executeRouter,
102
+ isPulseFetchResponse,
103
+ isPulseJavascriptEffect,
104
+ isRouterApplication,
105
+ markOpaqueResponse,
106
+ normalizeBindingName,
107
+ normalizeBindingValue,
108
+ normalizeKvKey,
109
+ normalizeKvNamespace,
110
+ normalizeKvPutResult,
111
+ normalizeApplication,
112
+ responseBodyClass,
113
+ responseHeaderPairs,
114
+ wrapStructuredResponse
115
+ });