litestar-vite-plugin 0.27.0 → 0.28.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.
package/README.md CHANGED
@@ -20,19 +20,10 @@ npm install litestar-vite-plugin
20
20
  ## Quick Start
21
21
 
22
22
  ```python
23
- import os
24
- from pathlib import Path
25
23
  from litestar import Litestar
26
- from litestar_vite import PathConfig, ViteConfig, VitePlugin
24
+ from litestar_vite import VitePlugin
27
25
 
28
- DEV_MODE = os.getenv("VITE_DEV_MODE", "true").lower() in ("true", "1", "yes")
29
-
30
- app = Litestar(
31
- plugins=[VitePlugin(config=ViteConfig(
32
- dev_mode=DEV_MODE,
33
- paths=PathConfig(root=Path(__file__).parent),
34
- ))]
35
- )
26
+ app = Litestar(plugins=[VitePlugin()])
36
27
  ```
37
28
 
38
29
  ```bash
@@ -41,14 +32,42 @@ litestar assets install
41
32
  litestar run --reload
42
33
  ```
43
34
 
44
- ## Documentation
35
+ `VitePlugin()` detects development and production behavior without manual `VITE_DEV_MODE` parsing. The effective
36
+ `litestar run --host/--port` values are also passed to the frontend environment and `.litestar.json` bridge.
37
+
38
+ ### Optional Granian native static serving
39
+
40
+ Granian 0.16+ can serve eligible production bundle files before the request enters Python:
41
+
42
+ ```python
43
+ from litestar import Litestar
44
+ from litestar_granian import GranianPlugin
45
+ from litestar_vite import VitePlugin
46
+
47
+ app = Litestar(
48
+ plugins=[
49
+ VitePlugin(),
50
+ GranianPlugin(static="auto"),
51
+ ]
52
+ )
53
+ ```
54
+
55
+ This is an optimization, not a separate application configuration. The Litestar static route stays registered, so
56
+ Uvicorn and other ASGI servers serve the same files. Granian automatically falls back to Litestar when assets are
57
+ protected, customized, missing, or otherwise unsafe to intercept.
45
58
 
46
- - **[Usage Guide](https://litestar-org.github.io/litestar-vite/usage/index.html)**: Core concepts, configuration, and type generation.
47
- - **[Inertia](https://litestar-org.github.io/litestar-vite/frameworks/inertia/index.html)**: Fullstack protocols and SSR.
48
- - **[Frameworks](https://litestar-org.github.io/litestar-vite/frameworks/index.html)**: Guides for React, Vue, Svelte, Angular, Astro, and Nuxt.
49
- - **[Reference](https://litestar-org.github.io/litestar-vite/reference/index.html)**: API and CLI documentation.
59
+ Native hits bypass ASGI middleware, guards, compression, custom headers, and Python access logging. Keep protected or
60
+ customized assets on the Litestar fallback path. See the
61
+ [production guide](https://litestar-org.github.io/litestar-vite/latest/usage/production.html) for the server matrix and
62
+ advanced configuration.
63
+
64
+ ## Documentation
50
65
 
51
- For a full list of changes, see the [Changelog](https://litestar-org.github.io/litestar-vite/changelog.html).
66
+ - Get started: <https://litestar-org.github.io/litestar-vite/latest/usage/index.html>
67
+ - Framework guides: <https://litestar-org.github.io/litestar-vite/latest/frameworks/index.html>
68
+ - Inertia: <https://litestar-org.github.io/litestar-vite/latest/frameworks/inertia/index.html>
69
+ - API reference: <https://litestar-org.github.io/litestar-vite/latest/reference/index.html>
70
+ - Changelog: <https://litestar-org.github.io/litestar-vite/latest/changelog.html>
52
71
 
53
72
  ## Common Commands
54
73
 
@@ -76,7 +95,7 @@ litestar-vite now defaults Inertia apps to script-element bootstrap. Inertia v3
76
95
 
77
96
  ## Links
78
97
 
79
- - Docs: <https://litestar-org.github.io/litestar-vite/>
98
+ - Docs: <https://litestar-org.github.io/litestar-vite/latest/>
80
99
  - Examples: `examples/` (React, Vue, Svelte, HTMX, Inertia, Astro, Nuxt, SvelteKit, Angular)
81
100
  - Real-world example: [litestar-fullstack](https://github.com/litestar-org/litestar-fullstack)
82
101
  - Issues: <https://github.com/litestar-org/litestar-vite/issues/>
package/dist/js/astro.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { readBridgeConfig } from "./shared/bridge-schema.js";
4
+ import { installManagedShutdown } from "./shared/managed-shutdown.js";
4
5
  import { normalizeHost, resolveHotFilePath, resolveLitestarPort } from "./shared/network.js";
5
6
  import { createLitestarTypeGenPlugin, resolveTypesConfig } from "./shared/typegen-plugin.js";
6
7
  import { hmrServerConfig } from "./shared/vite-compat.js";
@@ -91,6 +92,9 @@ function createProxyPlugin(config) {
91
92
  }
92
93
  }
93
94
  };
95
+ },
96
+ configureServer(server) {
97
+ installManagedShutdown(server);
94
98
  }
95
99
  };
96
100
  }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Browser helper for WebSocket routes generated by Litestar's ChannelsPlugin.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { createChannelsStream } from "litestar-vite-plugin/helpers"
7
+ *
8
+ * const stream = createChannelsStream({
9
+ * basePath: "/ws",
10
+ * channel: "notifications",
11
+ * onEvent: (event) => console.log(event),
12
+ * })
13
+ * stream.connect()
14
+ * ```
15
+ *
16
+ * @module
17
+ */
18
+ import { type EventStream, type EventStreamConfig } from "./stream.js";
19
+ export type ChannelName = string | (() => string);
20
+ export interface ChannelsStreamOptions<TFrame = unknown> extends Omit<EventStreamConfig<TFrame>, "EventSourceCtor" | "sseEvents" | "transport"> {
21
+ channel: ChannelName;
22
+ basePath?: string;
23
+ transformUrl?: (url: URL) => string | URL;
24
+ }
25
+ /**
26
+ * Create a stream for a WebSocket route generated by ChannelsPlugin.
27
+ *
28
+ * @param options - Channel name, server route prefix, and stream callbacks.
29
+ * @returns A disposable stream that connects only when `connect()` is called.
30
+ */
31
+ export declare function createChannelsStream<TFrame = unknown>(options: ChannelsStreamOptions<TFrame>): EventStream;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Browser helper for WebSocket routes generated by Litestar's ChannelsPlugin.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { createChannelsStream } from "litestar-vite-plugin/helpers"
7
+ *
8
+ * const stream = createChannelsStream({
9
+ * basePath: "/ws",
10
+ * channel: "notifications",
11
+ * onEvent: (event) => console.log(event),
12
+ * })
13
+ * stream.connect()
14
+ * ```
15
+ *
16
+ * @module
17
+ */
18
+ import { createEventStream, resolveStreamUrl } from "./stream.js";
19
+ /**
20
+ * Create a stream for a WebSocket route generated by ChannelsPlugin.
21
+ *
22
+ * @param options - Channel name, server route prefix, and stream callbacks.
23
+ * @returns A disposable stream that connects only when `connect()` is called.
24
+ */
25
+ export function createChannelsStream(options) {
26
+ const { basePath = "/", channel, transformUrl, ...streamOptions } = options;
27
+ return createEventStream({
28
+ ...streamOptions,
29
+ buildUrl: () => {
30
+ const channelName = typeof channel === "function" ? channel() : channel;
31
+ const prefix = basePath.endsWith("/") ? basePath : `${basePath}/`;
32
+ const url = new URL(resolveStreamUrl(`${prefix}${encodeURIComponent(channelName)}`, "websocket"));
33
+ return transformUrl?.(url) ?? url;
34
+ },
35
+ transport: "websocket",
36
+ });
37
+ }
@@ -39,5 +39,9 @@
39
39
  * @module
40
40
  */
41
41
  export { csrfFetch, csrfHeaders, getCsrfToken } from "./csrf.js";
42
+ export { createChannelsStream, type ChannelName, type ChannelsStreamOptions } from "./channels.js";
42
43
  export { addDirective, registerHtmxExtension, setDebug as setHtmxDebug, swapJson } from "./htmx.js";
44
+ export { createQueueEventStream, QUEUE_SSE_EVENTS, type QueueEventStreamOptions, type QueueStreamTarget, type QueueStreamValue } from "./queues.js";
43
45
  export { createRouteHelpers, currentRoute, isCurrentRoute, isRoute, type RouteDefinition, type RouteDefinitions, type RouteHelpers, toRoute } from "./routes.js";
46
+ export { createEventStream, type EventStream, type EventStreamOptions, type StreamGap } from "./stream.js";
47
+ export { defineStreamElement, LitestarStreamElement, type StreamElementOptions } from "./stream-element.js";
@@ -40,7 +40,15 @@
40
40
  */
41
41
  // CSRF utilities
42
42
  export { csrfFetch, csrfHeaders, getCsrfToken } from "./csrf.js";
43
+ // Litestar Channels utilities
44
+ export { createChannelsStream } from "./channels.js";
43
45
  // HTMX utilities
44
46
  export { addDirective, registerHtmxExtension, setDebug as setHtmxDebug, swapJson } from "./htmx.js";
47
+ // Litestar Queues utilities
48
+ export { createQueueEventStream, QUEUE_SSE_EVENTS } from "./queues.js";
45
49
  // Route matching utilities
46
50
  export { createRouteHelpers, currentRoute, isCurrentRoute, isRoute, toRoute } from "./routes.js";
51
+ // Realtime stream utilities
52
+ export { createEventStream } from "./stream.js";
53
+ // Declarative realtime stream element
54
+ export { defineStreamElement, LitestarStreamElement } from "./stream-element.js";
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Browser helper for routes generated by Litestar Queues EventStreamConfig.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { createQueueEventStream } from "litestar-vite-plugin/helpers"
7
+ *
8
+ * const stream = createQueueEventStream({
9
+ * scope: "task",
10
+ * taskId: "01J...",
11
+ * onEvent: (event) => console.log(event),
12
+ * })
13
+ * stream.connect()
14
+ * ```
15
+ *
16
+ * @module
17
+ */
18
+ import { type EventStream, type EventStreamConfig, type StreamUrl } from "./stream.js";
19
+ export declare const QUEUE_SSE_EVENTS: readonly ["task.started", "task.progress", "task.log", "task.event", "task.completed", "task.failed", "task.cancelled", "task.claim_lost", "task.stale_failed", "worker.heartbeat", "worker.stale_recovery"];
20
+ export type QueueStreamValue = string | (() => string);
21
+ export type QueueStreamTarget = {
22
+ scope: "task";
23
+ taskId: QueueStreamValue;
24
+ } | {
25
+ scope: "queue";
26
+ queue: QueueStreamValue;
27
+ } | {
28
+ scope: "worker";
29
+ workerId: QueueStreamValue;
30
+ } | {
31
+ scope: "global";
32
+ } | {
33
+ scope: "custom";
34
+ scopeKey: QueueStreamValue;
35
+ };
36
+ type QueueRouteStreamOptions = QueueStreamTarget & {
37
+ url?: never;
38
+ buildUrl?: never;
39
+ basePath?: string;
40
+ };
41
+ type QueueDirectStreamOptions = {
42
+ url: StreamUrl;
43
+ buildUrl?: never;
44
+ basePath?: never;
45
+ } | {
46
+ url?: never;
47
+ buildUrl: () => string | URL;
48
+ basePath?: never;
49
+ };
50
+ export type QueueEventStreamOptions<TFrame = unknown> = EventStreamConfig<TFrame> & (QueueRouteStreamOptions | QueueDirectStreamOptions) & {
51
+ basePath?: string;
52
+ transformUrl?: (url: URL) => string | URL;
53
+ };
54
+ /**
55
+ * Create a stream for routes generated by QueuePlugin EventStreamConfig.
56
+ *
57
+ * @param options - Queue target, transport, and stream callbacks.
58
+ * @returns A disposable stream that connects only when `connect()` is called.
59
+ */
60
+ export declare function createQueueEventStream<TFrame = unknown>(options: QueueEventStreamOptions<TFrame>): EventStream;
61
+ export {};
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Browser helper for routes generated by Litestar Queues EventStreamConfig.
3
+ *
4
+ * @example
5
+ * ```ts
6
+ * import { createQueueEventStream } from "litestar-vite-plugin/helpers"
7
+ *
8
+ * const stream = createQueueEventStream({
9
+ * scope: "task",
10
+ * taskId: "01J...",
11
+ * onEvent: (event) => console.log(event),
12
+ * })
13
+ * stream.connect()
14
+ * ```
15
+ *
16
+ * @module
17
+ */
18
+ import { createEventStream, resolveStreamUrl } from "./stream.js";
19
+ export const QUEUE_SSE_EVENTS = [
20
+ "task.started",
21
+ "task.progress",
22
+ "task.log",
23
+ "task.event",
24
+ "task.completed",
25
+ "task.failed",
26
+ "task.cancelled",
27
+ "task.claim_lost",
28
+ "task.stale_failed",
29
+ "worker.heartbeat",
30
+ "worker.stale_recovery",
31
+ ];
32
+ function asRecord(value) {
33
+ return typeof value === "object" && value !== null ? value : undefined;
34
+ }
35
+ function defaultQueueIsHeartbeat(frame) {
36
+ return asRecord(frame)?.type === "ping";
37
+ }
38
+ function defaultQueueGetEventKey(frame) {
39
+ const record = asRecord(frame);
40
+ const key = record?.eventKey ?? record?.id;
41
+ return typeof key === "string" ? key : undefined;
42
+ }
43
+ function defaultQueueGetSequence(frame) {
44
+ const record = asRecord(frame);
45
+ const sequence = record?.sequence;
46
+ const taskId = record?.taskId;
47
+ const attempt = record?.attempt;
48
+ if (typeof sequence !== "number" || !Number.isFinite(sequence) || typeof taskId !== "string" || (typeof attempt !== "string" && typeof attempt !== "number")) {
49
+ return undefined;
50
+ }
51
+ return { stream: `${taskId}:${attempt}`, value: sequence };
52
+ }
53
+ function resolveValue(value) {
54
+ return typeof value === "function" ? value() : value;
55
+ }
56
+ function queueRouteSuffix(target) {
57
+ switch (target.scope) {
58
+ case "task":
59
+ return `tasks/${encodeURIComponent(resolveValue(target.taskId))}`;
60
+ case "queue":
61
+ return `queues/${encodeURIComponent(resolveValue(target.queue))}`;
62
+ case "worker":
63
+ return `workers/${encodeURIComponent(resolveValue(target.workerId))}`;
64
+ case "global":
65
+ return "global";
66
+ case "custom":
67
+ return `custom/${encodeURIComponent(resolveValue(target.scopeKey))}`;
68
+ }
69
+ }
70
+ function queueStreamUrl(target, basePath, transport) {
71
+ const root = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
72
+ const transportPath = transport === "sse" ? "sse/" : "";
73
+ return new URL(resolveStreamUrl(`${root}/${transportPath}${queueRouteSuffix(target)}`, transport));
74
+ }
75
+ /**
76
+ * Create a stream for routes generated by QueuePlugin EventStreamConfig.
77
+ *
78
+ * @param options - Queue target, transport, and stream callbacks.
79
+ * @returns A disposable stream that connects only when `connect()` is called.
80
+ */
81
+ export function createQueueEventStream(options) {
82
+ const { baseDelayMs, basePath = "/queues/events", dedupWindow, EventSourceCtor, getEventKey = defaultQueueGetEventKey, getSequence = defaultQueueGetSequence, isHeartbeat = defaultQueueIsHeartbeat, maxDelayMs, onEvent, onClose, onGap, onHealthChange, onOpen, onReconnect, parseFrame, shouldReconnect, sseEvents = QUEUE_SSE_EVENTS, transformUrl, transport = "websocket", WebSocketCtor, } = options;
83
+ return createEventStream({
84
+ baseDelayMs,
85
+ buildUrl: () => {
86
+ let endpoint;
87
+ if ("buildUrl" in options && options.buildUrl !== undefined) {
88
+ endpoint = options.buildUrl();
89
+ }
90
+ else if ("url" in options && options.url !== undefined) {
91
+ endpoint = typeof options.url === "function" ? options.url() : options.url;
92
+ }
93
+ else {
94
+ endpoint = queueStreamUrl(options, basePath, transport);
95
+ }
96
+ const url = new URL(resolveStreamUrl(endpoint, transport));
97
+ return transformUrl?.(url) ?? url;
98
+ },
99
+ dedupWindow,
100
+ EventSourceCtor,
101
+ getEventKey,
102
+ getSequence,
103
+ isHeartbeat,
104
+ maxDelayMs,
105
+ onEvent,
106
+ onClose,
107
+ onGap,
108
+ onHealthChange,
109
+ onOpen,
110
+ onReconnect,
111
+ parseFrame,
112
+ shouldReconnect,
113
+ sseEvents,
114
+ transport,
115
+ WebSocketCtor,
116
+ });
117
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Declarative lifecycle binding for JSON WebSocket and SSE streams.
3
+ *
4
+ * This element replaces `htmx-ext-ws` and `htmx-ext-sse` for JSON streams.
5
+ * Do not layer both reconnect implementations on the same endpoint.
6
+ *
7
+ * @example
8
+ * ```html
9
+ * <litestar-stream url="/events" transport="sse" swap="json">
10
+ * <template ls-for="item in $data.items"><p>${item}</p></template>
11
+ * </litestar-stream>
12
+ * ```
13
+ *
14
+ * @module
15
+ */
16
+ import { type EventStreamConfig } from "./stream.js";
17
+ type StreamCallbackConfig = Pick<EventStreamConfig, "EventSourceCtor" | "WebSocketCtor" | "getEventKey" | "getSequence" | "isHeartbeat" | "onEvent" | "onGap" | "onHealthChange" | "onReconnect" | "parseFrame" | "shouldReconnect">;
18
+ type HTMLElementConstructor = new (...args: never[]) => HTMLElement;
19
+ declare const HTMLElementBase: HTMLElementConstructor;
20
+ export interface StreamElementOptions {
21
+ tagName?: string;
22
+ }
23
+ export declare class LitestarStreamElement extends HTMLElementBase {
24
+ static readonly observedAttributes: string[];
25
+ private stream;
26
+ private _buildUrl;
27
+ private _onEvent;
28
+ private _onGap;
29
+ private _onHealthChange;
30
+ private _onReconnect;
31
+ private _shouldReconnect;
32
+ private _isHeartbeat;
33
+ private _getEventKey;
34
+ private _getSequence;
35
+ private _parseFrame;
36
+ private _WebSocketCtor;
37
+ private _EventSourceCtor;
38
+ constructor();
39
+ get buildUrl(): (() => string | URL) | undefined;
40
+ set buildUrl(value: (() => string | URL) | undefined);
41
+ get onEvent(): StreamCallbackConfig["onEvent"] | undefined;
42
+ set onEvent(value: StreamCallbackConfig["onEvent"] | undefined);
43
+ get onGap(): StreamCallbackConfig["onGap"];
44
+ set onGap(value: StreamCallbackConfig["onGap"]);
45
+ get onHealthChange(): StreamCallbackConfig["onHealthChange"];
46
+ set onHealthChange(value: StreamCallbackConfig["onHealthChange"]);
47
+ get onReconnect(): StreamCallbackConfig["onReconnect"];
48
+ set onReconnect(value: StreamCallbackConfig["onReconnect"]);
49
+ get shouldReconnect(): StreamCallbackConfig["shouldReconnect"];
50
+ set shouldReconnect(value: StreamCallbackConfig["shouldReconnect"]);
51
+ get isHeartbeat(): StreamCallbackConfig["isHeartbeat"];
52
+ set isHeartbeat(value: StreamCallbackConfig["isHeartbeat"]);
53
+ get getEventKey(): StreamCallbackConfig["getEventKey"];
54
+ set getEventKey(value: StreamCallbackConfig["getEventKey"]);
55
+ get getSequence(): StreamCallbackConfig["getSequence"];
56
+ set getSequence(value: StreamCallbackConfig["getSequence"]);
57
+ get parseFrame(): StreamCallbackConfig["parseFrame"];
58
+ set parseFrame(value: StreamCallbackConfig["parseFrame"]);
59
+ get WebSocketCtor(): StreamCallbackConfig["WebSocketCtor"];
60
+ set WebSocketCtor(value: StreamCallbackConfig["WebSocketCtor"]);
61
+ get EventSourceCtor(): StreamCallbackConfig["EventSourceCtor"];
62
+ set EventSourceCtor(value: StreamCallbackConfig["EventSourceCtor"]);
63
+ get healthy(): boolean;
64
+ connectedCallback(): void;
65
+ disconnectedCallback(): void;
66
+ attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void;
67
+ private upgradeProperty;
68
+ private start;
69
+ private stop;
70
+ private restart;
71
+ private dispatchStreamEvent;
72
+ }
73
+ /**
74
+ * Register the declarative stream element.
75
+ *
76
+ * @param options - Optional custom tag name.
77
+ */
78
+ export declare function defineStreamElement(options?: StreamElementOptions): void;
79
+ export {};
@@ -0,0 +1,229 @@
1
+ /**
2
+ * Declarative lifecycle binding for JSON WebSocket and SSE streams.
3
+ *
4
+ * This element replaces `htmx-ext-ws` and `htmx-ext-sse` for JSON streams.
5
+ * Do not layer both reconnect implementations on the same endpoint.
6
+ *
7
+ * @example
8
+ * ```html
9
+ * <litestar-stream url="/events" transport="sse" swap="json">
10
+ * <template ls-for="item in $data.items"><p>${item}</p></template>
11
+ * </litestar-stream>
12
+ * ```
13
+ *
14
+ * @module
15
+ */
16
+ import { swapJson } from "./htmx.js";
17
+ import { createQueueEventStream } from "./queues.js";
18
+ import { createEventStream } from "./stream.js";
19
+ const HTMLElementBase = (typeof HTMLElement === "undefined" ? class {
20
+ } : HTMLElement);
21
+ export class LitestarStreamElement extends HTMLElementBase {
22
+ static observedAttributes = ["url", "transport", "sse-events", "preset", "swap", "disabled"];
23
+ stream = null;
24
+ _buildUrl;
25
+ _onEvent;
26
+ _onGap;
27
+ _onHealthChange;
28
+ _onReconnect;
29
+ _shouldReconnect;
30
+ _isHeartbeat;
31
+ _getEventKey;
32
+ _getSequence;
33
+ _parseFrame;
34
+ _WebSocketCtor;
35
+ _EventSourceCtor;
36
+ constructor() {
37
+ super();
38
+ for (const property of [
39
+ "buildUrl",
40
+ "onEvent",
41
+ "onGap",
42
+ "onHealthChange",
43
+ "onReconnect",
44
+ "shouldReconnect",
45
+ "isHeartbeat",
46
+ "getEventKey",
47
+ "getSequence",
48
+ "parseFrame",
49
+ "WebSocketCtor",
50
+ "EventSourceCtor",
51
+ ]) {
52
+ this.upgradeProperty(property);
53
+ }
54
+ }
55
+ get buildUrl() {
56
+ return this._buildUrl;
57
+ }
58
+ set buildUrl(value) {
59
+ this._buildUrl = value;
60
+ this.restart();
61
+ }
62
+ get onEvent() {
63
+ return this._onEvent;
64
+ }
65
+ set onEvent(value) {
66
+ this._onEvent = value;
67
+ }
68
+ get onGap() {
69
+ return this._onGap;
70
+ }
71
+ set onGap(value) {
72
+ this._onGap = value;
73
+ }
74
+ get onHealthChange() {
75
+ return this._onHealthChange;
76
+ }
77
+ set onHealthChange(value) {
78
+ this._onHealthChange = value;
79
+ }
80
+ get onReconnect() {
81
+ return this._onReconnect;
82
+ }
83
+ set onReconnect(value) {
84
+ this._onReconnect = value;
85
+ }
86
+ get shouldReconnect() {
87
+ return this._shouldReconnect;
88
+ }
89
+ set shouldReconnect(value) {
90
+ this._shouldReconnect = value;
91
+ }
92
+ get isHeartbeat() {
93
+ return this._isHeartbeat;
94
+ }
95
+ set isHeartbeat(value) {
96
+ this._isHeartbeat = value;
97
+ }
98
+ get getEventKey() {
99
+ return this._getEventKey;
100
+ }
101
+ set getEventKey(value) {
102
+ this._getEventKey = value;
103
+ }
104
+ get getSequence() {
105
+ return this._getSequence;
106
+ }
107
+ set getSequence(value) {
108
+ this._getSequence = value;
109
+ }
110
+ get parseFrame() {
111
+ return this._parseFrame;
112
+ }
113
+ set parseFrame(value) {
114
+ this._parseFrame = value;
115
+ }
116
+ get WebSocketCtor() {
117
+ return this._WebSocketCtor;
118
+ }
119
+ set WebSocketCtor(value) {
120
+ this._WebSocketCtor = value;
121
+ }
122
+ get EventSourceCtor() {
123
+ return this._EventSourceCtor;
124
+ }
125
+ set EventSourceCtor(value) {
126
+ this._EventSourceCtor = value;
127
+ }
128
+ get healthy() {
129
+ return this.stream?.healthy ?? false;
130
+ }
131
+ connectedCallback() {
132
+ this.start();
133
+ }
134
+ disconnectedCallback() {
135
+ this.stop();
136
+ }
137
+ attributeChangedCallback(name, oldValue, newValue) {
138
+ if (oldValue === newValue || name === "swap") {
139
+ return;
140
+ }
141
+ this.restart();
142
+ }
143
+ upgradeProperty(property) {
144
+ if (!Object.prototype.hasOwnProperty.call(this, property)) {
145
+ return;
146
+ }
147
+ const value = this[property];
148
+ delete this[property];
149
+ this[property] = value;
150
+ }
151
+ start() {
152
+ if (!this.isConnected || this.hasAttribute("disabled") || this.stream !== null) {
153
+ return;
154
+ }
155
+ const url = this.getAttribute("url");
156
+ if (this._buildUrl === undefined && url === null) {
157
+ return;
158
+ }
159
+ const transport = this.getAttribute("transport") === "sse" ? "sse" : "websocket";
160
+ const sseEvents = this.getAttribute("sse-events")
161
+ ?.split(",")
162
+ .map((event) => event.trim())
163
+ .filter(Boolean);
164
+ const config = {
165
+ EventSourceCtor: this._EventSourceCtor,
166
+ WebSocketCtor: this._WebSocketCtor,
167
+ getEventKey: this._getEventKey,
168
+ getSequence: this._getSequence,
169
+ isHeartbeat: this._isHeartbeat,
170
+ onClose: () => this.dispatchStreamEvent("litestar:stream-close", {}),
171
+ onEvent: (frame) => {
172
+ this._onEvent?.(frame);
173
+ this.dispatchStreamEvent("litestar:stream-event", frame);
174
+ if (this.getAttribute("swap") === "json") {
175
+ swapJson(this, frame);
176
+ }
177
+ },
178
+ onGap: (gap) => {
179
+ this._onGap?.(gap);
180
+ this.dispatchStreamEvent("litestar:stream-gap", gap);
181
+ },
182
+ onHealthChange: (healthy) => {
183
+ this._onHealthChange?.(healthy);
184
+ this.dispatchStreamEvent("litestar:stream-health", { healthy });
185
+ },
186
+ onOpen: (resolvedUrl) => this.dispatchStreamEvent("litestar:stream-open", { url: resolvedUrl }),
187
+ onReconnect: () => this._onReconnect?.(),
188
+ parseFrame: this._parseFrame,
189
+ shouldReconnect: this._shouldReconnect,
190
+ sseEvents,
191
+ transport: transport,
192
+ };
193
+ const endpoint = this._buildUrl === undefined ? { url: url } : { buildUrl: this._buildUrl };
194
+ this.stream = this.getAttribute("preset") === "queues" ? createQueueEventStream({ ...config, ...endpoint }) : createEventStream({ ...config, ...endpoint });
195
+ this.stream.connect();
196
+ }
197
+ stop() {
198
+ const stream = this.stream;
199
+ this.stream = null;
200
+ stream?.dispose();
201
+ }
202
+ restart() {
203
+ if (!this.isConnected) {
204
+ return;
205
+ }
206
+ this.stop();
207
+ this.start();
208
+ }
209
+ dispatchStreamEvent(type, detail) {
210
+ this.dispatchEvent(new CustomEvent(type, { bubbles: true, composed: false, detail }));
211
+ }
212
+ }
213
+ /**
214
+ * Register the declarative stream element.
215
+ *
216
+ * @param options - Optional custom tag name.
217
+ */
218
+ export function defineStreamElement(options = {}) {
219
+ if (typeof window === "undefined" || typeof customElements === "undefined") {
220
+ return;
221
+ }
222
+ const tagName = options.tagName ?? "litestar-stream";
223
+ if (customElements.get(tagName) !== undefined) {
224
+ return;
225
+ }
226
+ const ElementClass = tagName === "litestar-stream" ? LitestarStreamElement : class extends LitestarStreamElement {
227
+ };
228
+ customElements.define(tagName, ElementClass);
229
+ }