+ *
+ *
+ * )
+ * })
+ * await next()
+ * })
+ * ```
+ */
+ setRenderer = (renderer) => {
+ this.#renderer = renderer;
+ };
+ /**
+ * `.header()` can set headers.
+ *
+ * @see {@link https://hono.dev/docs/api/context#body}
+ *
+ * @example
+ * ```ts
+ * app.get('/welcome', (c) => {
+ * // Set headers
+ * c.header('X-Message', 'Hello!')
+ * c.header('Content-Type', 'text/plain')
+ *
+ * return c.body('Thank you for coming')
+ * })
+ * ```
+ */
+ header = (name, value, options) => {
+ if (value === void 0) {
+ if (this.#headers) {
+ this.#headers.delete(name);
+ } else if (this.#preparedHeaders) {
+ delete this.#preparedHeaders[name.toLocaleLowerCase()];
+ }
+ if (this.finalized) {
+ this.res.headers.delete(name);
+ }
+ return;
+ }
+ if (options?.append) {
+ if (!this.#headers) {
+ this.#isFresh = false;
+ this.#headers = new Headers(this.#preparedHeaders);
+ this.#preparedHeaders = {};
+ }
+ this.#headers.append(name, value);
+ } else {
+ if (this.#headers) {
+ this.#headers.set(name, value);
+ } else {
+ this.#preparedHeaders ??= {};
+ this.#preparedHeaders[name.toLowerCase()] = value;
+ }
+ }
+ if (this.finalized) {
+ if (options?.append) {
+ this.res.headers.append(name, value);
+ } else {
+ this.res.headers.set(name, value);
+ }
+ }
+ };
+ status = (status) => {
+ this.#isFresh = false;
+ this.#status = status;
+ };
+ /**
+ * `.set()` can set the value specified by the key.
+ *
+ * @see {@link https://hono.dev/docs/api/context#set-get}
+ *
+ * @example
+ * ```ts
+ * app.use('*', async (c, next) => {
+ * c.set('message', 'Hono is cool!!')
+ * await next()
+ * })
+ * ```
+ */
+ set = (key, value) => {
+ this.#var ??= /* @__PURE__ */ new Map();
+ this.#var.set(key, value);
+ };
+ /**
+ * `.get()` can use the value specified by the key.
+ *
+ * @see {@link https://hono.dev/docs/api/context#set-get}
+ *
+ * @example
+ * ```ts
+ * app.get('/', (c) => {
+ * const message = c.get('message')
+ * return c.text(`The message is "${message}"`)
+ * })
+ * ```
+ */
+ get = (key) => {
+ return this.#var ? this.#var.get(key) : void 0;
+ };
+ /**
+ * `.var` can access the value of a variable.
+ *
+ * @see {@link https://hono.dev/docs/api/context#var}
+ *
+ * @example
+ * ```ts
+ * const result = c.var.client.oneMethod()
+ * ```
+ */
+ // c.var.propName is a read-only
+ get var() {
+ if (!this.#var) {
+ return {};
+ }
+ return Object.fromEntries(this.#var);
+ }
+ #newResponse(data, arg, headers) {
+ if (this.#isFresh && !headers && !arg && this.#status === 200) {
+ return new Response(data, {
+ headers: this.#preparedHeaders
+ });
+ }
+ if (arg && typeof arg !== "number") {
+ const header = new Headers(arg.headers);
+ if (this.#headers) {
+ this.#headers.forEach((v, k) => {
+ if (k === "set-cookie") {
+ header.append(k, v);
+ } else {
+ header.set(k, v);
+ }
+ });
+ }
+ const headers2 = setHeaders(header, this.#preparedHeaders);
+ return new Response(data, {
+ headers: headers2,
+ status: arg.status ?? this.#status
+ });
+ }
+ const status = typeof arg === "number" ? arg : this.#status;
+ this.#preparedHeaders ??= {};
+ this.#headers ??= new Headers();
+ setHeaders(this.#headers, this.#preparedHeaders);
+ if (this.#res) {
+ this.#res.headers.forEach((v, k) => {
+ if (k === "set-cookie") {
+ this.#headers?.append(k, v);
+ } else {
+ this.#headers?.set(k, v);
+ }
+ });
+ setHeaders(this.#headers, this.#preparedHeaders);
+ }
+ headers ??= {};
+ for (const [k, v] of Object.entries(headers)) {
+ if (typeof v === "string") {
+ this.#headers.set(k, v);
+ } else {
+ this.#headers.delete(k);
+ for (const v2 of v) {
+ this.#headers.append(k, v2);
+ }
+ }
+ }
+ return new Response(data, {
+ status,
+ headers: this.#headers
+ });
+ }
+ newResponse = (...args) => this.#newResponse(...args);
+ /**
+ * `.body()` can return the HTTP response.
+ * You can set headers with `.header()` and set HTTP status code with `.status`.
+ * This can also be set in `.text()`, `.json()` and so on.
+ *
+ * @see {@link https://hono.dev/docs/api/context#body}
+ *
+ * @example
+ * ```ts
+ * app.get('/welcome', (c) => {
+ * // Set headers
+ * c.header('X-Message', 'Hello!')
+ * c.header('Content-Type', 'text/plain')
+ * // Set HTTP status code
+ * c.status(201)
+ *
+ * // Return the response body
+ * return c.body('Thank you for coming')
+ * })
+ * ```
+ */
+ body = (data, arg, headers) => {
+ return typeof arg === "number" ? this.#newResponse(data, arg, headers) : this.#newResponse(data, arg);
+ };
+ /**
+ * `.text()` can render text as `Content-Type:text/plain`.
+ *
+ * @see {@link https://hono.dev/docs/api/context#text}
+ *
+ * @example
+ * ```ts
+ * app.get('/say', (c) => {
+ * return c.text('Hello!')
+ * })
+ * ```
+ */
+ text = (text, arg, headers) => {
+ if (!this.#preparedHeaders) {
+ if (this.#isFresh && !headers && !arg) {
+ return new Response(text);
+ }
+ this.#preparedHeaders = {};
+ }
+ this.#preparedHeaders["content-type"] = TEXT_PLAIN;
+ if (typeof arg === "number") {
+ return this.#newResponse(text, arg, headers);
+ }
+ return this.#newResponse(text, arg);
+ };
+ /**
+ * `.json()` can render JSON as `Content-Type:application/json`.
+ *
+ * @see {@link https://hono.dev/docs/api/context#json}
+ *
+ * @example
+ * ```ts
+ * app.get('/api', (c) => {
+ * return c.json({ message: 'Hello!' })
+ * })
+ * ```
+ */
+ json = (object, arg, headers) => {
+ const body = JSON.stringify(object);
+ this.#preparedHeaders ??= {};
+ this.#preparedHeaders["content-type"] = "application/json";
+ return typeof arg === "number" ? this.#newResponse(body, arg, headers) : this.#newResponse(body, arg);
+ };
+ html = (html, arg, headers) => {
+ this.#preparedHeaders ??= {};
+ this.#preparedHeaders["content-type"] = "text/html; charset=UTF-8";
+ if (typeof html === "object") {
+ return resolveCallback(html, HtmlEscapedCallbackPhase.Stringify, false, {}).then((html2) => {
+ return typeof arg === "number" ? this.#newResponse(html2, arg, headers) : this.#newResponse(html2, arg);
+ });
+ }
+ return typeof arg === "number" ? this.#newResponse(html, arg, headers) : this.#newResponse(html, arg);
+ };
+ /**
+ * `.redirect()` can Redirect, default status code is 302.
+ *
+ * @see {@link https://hono.dev/docs/api/context#redirect}
+ *
+ * @example
+ * ```ts
+ * app.get('/redirect', (c) => {
+ * return c.redirect('/')
+ * })
+ * app.get('/redirect-permanently', (c) => {
+ * return c.redirect('/', 301)
+ * })
+ * ```
+ */
+ redirect = (location, status) => {
+ this.#headers ??= new Headers();
+ this.#headers.set("Location", String(location));
+ return this.newResponse(null, status ?? 302);
+ };
+ /**
+ * `.notFound()` can return the Not Found Response.
+ *
+ * @see {@link https://hono.dev/docs/api/context#notfound}
+ *
+ * @example
+ * ```ts
+ * app.get('/notfound', (c) => {
+ * return c.notFound()
+ * })
+ * ```
+ */
+ notFound = () => {
+ this.#notFoundHandler ??= () => new Response();
+ return this.#notFoundHandler(this);
+ };
+};
+
+// https://jsr.io/@hono/hono/4.6.19/src/compose.ts
+var compose = (middleware, onError, onNotFound) => {
+ return (context, next) => {
+ let index = -1;
+ const isContext = context instanceof Context;
+ return dispatch(0);
+ async function dispatch(i) {
+ if (i <= index) {
+ throw new Error("next() called multiple times");
+ }
+ index = i;
+ let res;
+ let isError = false;
+ let handler;
+ if (middleware[i]) {
+ handler = middleware[i][0][0];
+ if (isContext) {
+ context.req.routeIndex = i;
+ }
+ } else {
+ handler = i === middleware.length && next || void 0;
+ }
+ if (!handler) {
+ if (isContext && context.finalized === false && onNotFound) {
+ res = await onNotFound(context);
+ }
+ } else {
+ try {
+ res = await handler(context, () => {
+ return dispatch(i + 1);
+ });
+ } catch (err) {
+ if (err instanceof Error && isContext && onError) {
+ context.error = err;
+ res = await onError(err, context);
+ isError = true;
+ } else {
+ throw err;
+ }
+ }
+ }
+ if (res && (context.finalized === false || isError)) {
+ context.res = res;
+ }
+ return context;
+ }
+ };
+};
+
+// https://jsr.io/@hono/hono/4.6.19/src/router.ts
+var METHOD_NAME_ALL = "ALL";
+var METHOD_NAME_ALL_LOWERCASE = "all";
+var METHODS = ["get", "post", "put", "delete", "options", "patch"];
+var UnsupportedPathError = class extends Error {
+};
+
+// https://jsr.io/@hono/hono/4.6.19/src/utils/constants.ts
+var COMPOSED_HANDLER = "__COMPOSED_HANDLER";
+
+// https://jsr.io/@hono/hono/4.6.19/src/hono-base.ts
+var notFoundHandler = (c) => {
+ return c.text("404 Not Found", 404);
+};
+var errorHandler = (err, c) => {
+ if ("getResponse" in err) {
+ return err.getResponse();
+ }
+ console.error(err);
+ return c.text("Internal Server Error", 500);
+};
+var Hono = class _Hono {
+ get;
+ post;
+ put;
+ delete;
+ options;
+ patch;
+ all;
+ on;
+ use;
+ /*
+ This class is like an abstract class and does not have a router.
+ To use it, inherit the class and implement router in the constructor.
+ */
+ router;
+ getPath;
+ // Cannot use `#` because it requires visibility at JavaScript runtime.
+ _basePath = "/";
+ #path = "/";
+ routes = [];
+ constructor(options = {}) {
+ const allMethods = [...METHODS, METHOD_NAME_ALL_LOWERCASE];
+ allMethods.forEach((method) => {
+ this[method] = (args1, ...args) => {
+ if (typeof args1 === "string") {
+ this.#path = args1;
+ } else {
+ this.#addRoute(method, this.#path, args1);
+ }
+ args.forEach((handler) => {
+ this.#addRoute(method, this.#path, handler);
+ });
+ return this;
+ };
+ });
+ this.on = (method, path, ...handlers) => {
+ for (const p of [path].flat()) {
+ this.#path = p;
+ for (const m of [method].flat()) {
+ handlers.map((handler) => {
+ this.#addRoute(m.toUpperCase(), this.#path, handler);
+ });
+ }
+ }
+ return this;
+ };
+ this.use = (arg1, ...handlers) => {
+ if (typeof arg1 === "string") {
+ this.#path = arg1;
+ } else {
+ this.#path = "*";
+ handlers.unshift(arg1);
+ }
+ handlers.forEach((handler) => {
+ this.#addRoute(METHOD_NAME_ALL, this.#path, handler);
+ });
+ return this;
+ };
+ const strict = options.strict ?? true;
+ delete options.strict;
+ Object.assign(this, options);
+ this.getPath = strict ? options.getPath ?? getPath : getPathNoStrict;
+ }
+ #clone() {
+ const clone = new _Hono({
+ router: this.router,
+ getPath: this.getPath
+ });
+ clone.routes = this.routes;
+ return clone;
+ }
+ #notFoundHandler = notFoundHandler;
+ // Cannot use `#` because it requires visibility at JavaScript runtime.
+ errorHandler = errorHandler;
+ /**
+ * `.route()` allows grouping other Hono instance in routes.
+ *
+ * @see {@link https://hono.dev/docs/api/routing#grouping}
+ *
+ * @param {string} path - base Path
+ * @param {Hono} app - other Hono instance
+ * @returns {Hono} routed Hono instance
+ *
+ * @example
+ * ```ts
+ * const app = new Hono()
+ * const app2 = new Hono()
+ *
+ * app2.get("/user", (c) => c.text("user"))
+ * app.route("/api", app2) // GET /api/user
+ * ```
+ */
+ route(path, app) {
+ const subApp = this.basePath(path);
+ app.routes.map((r) => {
+ let handler;
+ if (app.errorHandler === errorHandler) {
+ handler = r.handler;
+ } else {
+ handler = async (c, next) => (await compose([], app.errorHandler)(c, () => r.handler(c, next))).res;
+ handler[COMPOSED_HANDLER] = r.handler;
+ }
+ subApp.#addRoute(r.method, r.path, handler);
+ });
+ return this;
+ }
+ /**
+ * `.basePath()` allows base paths to be specified.
+ *
+ * @see {@link https://hono.dev/docs/api/routing#base-path}
+ *
+ * @param {string} path - base Path
+ * @returns {Hono} changed Hono instance
+ *
+ * @example
+ * ```ts
+ * const api = new Hono().basePath('/api')
+ * ```
+ */
+ basePath(path) {
+ const subApp = this.#clone();
+ subApp._basePath = mergePath(this._basePath, path);
+ return subApp;
+ }
+ /**
+ * `.onError()` handles an error and returns a customized Response.
+ *
+ * @see {@link https://hono.dev/docs/api/hono#error-handling}
+ *
+ * @param {ErrorHandler} handler - request Handler for error
+ * @returns {Hono} changed Hono instance
+ *
+ * @example
+ * ```ts
+ * app.onError((err, c) => {
+ * console.error(`${err}`)
+ * return c.text('Custom Error Message', 500)
+ * })
+ * ```
+ */
+ onError = (handler) => {
+ this.errorHandler = handler;
+ return this;
+ };
+ /**
+ * `.notFound()` allows you to customize a Not Found Response.
+ *
+ * @see {@link https://hono.dev/docs/api/hono#not-found}
+ *
+ * @param {NotFoundHandler} handler - request handler for not-found
+ * @returns {Hono} changed Hono instance
+ *
+ * @example
+ * ```ts
+ * app.notFound((c) => {
+ * return c.text('Custom 404 Message', 404)
+ * })
+ * ```
+ */
+ notFound = (handler) => {
+ this.#notFoundHandler = handler;
+ return this;
+ };
+ /**
+ * `.mount()` allows you to mount applications built with other frameworks into your Hono application.
+ *
+ * @see {@link https://hono.dev/docs/api/hono#mount}
+ *
+ * @param {string} path - base Path
+ * @param {Function} applicationHandler - other Request Handler
+ * @param {MountOptions} [options] - options of `.mount()`
+ * @returns {Hono} mounted Hono instance
+ *
+ * @example
+ * ```ts
+ * import { Router as IttyRouter } from 'itty-router'
+ * import { Hono } from 'hono'
+ * // Create itty-router application
+ * const ittyRouter = IttyRouter()
+ * // GET /itty-router/hello
+ * ittyRouter.get('/hello', () => new Response('Hello from itty-router'))
+ *
+ * const app = new Hono()
+ * app.mount('/itty-router', ittyRouter.handle)
+ * ```
+ *
+ * @example
+ * ```ts
+ * const app = new Hono()
+ * // Send the request to another application without modification.
+ * app.mount('/app', anotherApp, {
+ * replaceRequest: (req) => req,
+ * })
+ * ```
+ */
+ mount(path, applicationHandler, options) {
+ let replaceRequest;
+ let optionHandler;
+ if (options) {
+ if (typeof options === "function") {
+ optionHandler = options;
+ } else {
+ optionHandler = options.optionHandler;
+ replaceRequest = options.replaceRequest;
+ }
+ }
+ const getOptions = optionHandler ? (c) => {
+ const options2 = optionHandler(c);
+ return Array.isArray(options2) ? options2 : [options2];
+ } : (c) => {
+ let executionContext = void 0;
+ try {
+ executionContext = c.executionCtx;
+ } catch {
+ }
+ return [c.env, executionContext];
+ };
+ replaceRequest ||= (() => {
+ const mergedPath = mergePath(this._basePath, path);
+ const pathPrefixLength = mergedPath === "/" ? 0 : mergedPath.length;
+ return (request) => {
+ const url = new URL(request.url);
+ url.pathname = url.pathname.slice(pathPrefixLength) || "/";
+ return new Request(url, request);
+ };
+ })();
+ const handler = async (c, next) => {
+ const res = await applicationHandler(replaceRequest(c.req.raw), ...getOptions(c));
+ if (res) {
+ return res;
+ }
+ await next();
+ };
+ this.#addRoute(METHOD_NAME_ALL, mergePath(path, "*"), handler);
+ return this;
+ }
+ #addRoute(method, path, handler) {
+ method = method.toUpperCase();
+ path = mergePath(this._basePath, path);
+ const r = { path, method, handler };
+ this.router.add(method, path, [handler, r]);
+ this.routes.push(r);
+ }
+ #handleError(err, c) {
+ if (err instanceof Error) {
+ return this.errorHandler(err, c);
+ }
+ throw err;
+ }
+ #dispatch(request, executionCtx, env, method) {
+ if (method === "HEAD") {
+ return (async () => new Response(null, await this.#dispatch(request, executionCtx, env, "GET")))();
+ }
+ const path = this.getPath(request, { env });
+ const matchResult = this.router.match(method, path);
+ const c = new Context(request, {
+ path,
+ matchResult,
+ env,
+ executionCtx,
+ notFoundHandler: this.#notFoundHandler
+ });
+ if (matchResult[0].length === 1) {
+ let res;
+ try {
+ res = matchResult[0][0][0][0](c, async () => {
+ c.res = await this.#notFoundHandler(c);
+ });
+ } catch (err) {
+ return this.#handleError(err, c);
+ }
+ return res instanceof Promise ? res.then(
+ (resolved) => resolved || (c.finalized ? c.res : this.#notFoundHandler(c))
+ ).catch((err) => this.#handleError(err, c)) : res ?? this.#notFoundHandler(c);
+ }
+ const composed = compose(matchResult[0], this.errorHandler, this.#notFoundHandler);
+ return (async () => {
+ try {
+ const context = await composed(c);
+ if (!context.finalized) {
+ throw new Error(
+ "Context is not finalized. Did you forget to return a Response object or `await next()`?"
+ );
+ }
+ return context.res;
+ } catch (err) {
+ return this.#handleError(err, c);
+ }
+ })();
+ }
+ /**
+ * `.fetch()` will be entry point of your app.
+ *
+ * @see {@link https://hono.dev/docs/api/hono#fetch}
+ *
+ * @param {Request} request - request Object of request
+ * @param {Env} Env - env Object
+ * @param {ExecutionContext} - context of execution
+ * @returns {Response | Promise} response of request
+ *
+ */
+ fetch = (request, ...rest) => {
+ return this.#dispatch(request, rest[1], rest[0], request.method);
+ };
+ /**
+ * `.request()` is a useful method for testing.
+ * You can pass a URL or pathname to send a GET request.
+ * app will return a Response object.
+ * ```ts
+ * test('GET /hello is ok', async () => {
+ * const res = await app.request('/hello')
+ * expect(res.status).toBe(200)
+ * })
+ * ```
+ * @see https://hono.dev/docs/api/hono#request
+ */
+ request = (input, requestInit, Env, executionCtx) => {
+ if (input instanceof Request) {
+ return this.fetch(requestInit ? new Request(input, requestInit) : input, Env, executionCtx);
+ }
+ input = input.toString();
+ return this.fetch(
+ new Request(
+ /^https?:\/\//.test(input) ? input : `http://localhost${mergePath("/", input)}`,
+ requestInit
+ ),
+ Env,
+ executionCtx
+ );
+ };
+ /**
+ * `.fire()` automatically adds a global fetch event listener.
+ * This can be useful for environments that adhere to the Service Worker API, such as non-ES module Cloudflare Workers.
+ * @see https://hono.dev/docs/api/hono#fire
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API
+ * @see https://developers.cloudflare.com/workers/reference/migrate-to-module-workers/
+ */
+ fire = () => {
+ addEventListener("fetch", (event) => {
+ event.respondWith(this.#dispatch(event.request, event, void 0, event.request.method));
+ });
+ };
+};
+
+// https://jsr.io/@hono/hono/4.6.19/src/router/pattern-router/router.ts
+var emptyParams = /* @__PURE__ */ Object.create(null);
+var PatternRouter = class {
+ name = "PatternRouter";
+ #routes = [];
+ add(method, path, handler) {
+ const endsWithWildcard = path.at(-1) === "*";
+ if (endsWithWildcard) {
+ path = path.slice(0, -2);
+ }
+ if (path.at(-1) === "?") {
+ path = path.slice(0, -1);
+ this.add(method, path.replace(/\/[^/]+$/, ""), handler);
+ }
+ const parts = (path.match(/\/?(:\w+(?:{(?:(?:{[\d,]+})|[^}])+})?)|\/?[^\/\?]+/g) || []).map(
+ (part) => {
+ const match = part.match(/^\/:([^{]+)(?:{(.*)})?/);
+ return match ? `/(?<${match[1]}>${match[2] || "[^/]+"})` : part === "/*" ? "/[^/]+" : part.replace(/[.\\+*[^\]$()]/g, "\\$&");
+ }
+ );
+ let re;
+ try {
+ re = new RegExp(`^${parts.join("")}${endsWithWildcard ? "" : "/?$"}`);
+ } catch {
+ throw new UnsupportedPathError();
+ }
+ this.#routes.push([re, method, handler]);
+ }
+ match(method, path) {
+ const handlers = [];
+ for (let i = 0, len = this.#routes.length; i < len; i++) {
+ const [pattern, routeMethod, handler] = this.#routes[i];
+ if (routeMethod === method || routeMethod === METHOD_NAME_ALL) {
+ const match = pattern.exec(path);
+ if (match) {
+ handlers.push([handler, match.groups || emptyParams]);
+ }
+ }
+ }
+ return [handlers];
+ }
+};
+
+// https://jsr.io/@hono/hono/4.6.19/src/preset/tiny.ts
+var Hono2 = class extends Hono {
+ constructor(options = {}) {
+ super(options);
+ this.router = new PatternRouter();
+ }
+};
+
+// ../../../sdk/typescript/src/types.ts
+var sseHeaders = {
+ "Cache-Control": "no-cache",
+ "Connection": "keep-alive",
+ "Content-Type": "text/event-stream"
+};
+
+// ../../../sdk/typescript/src/consts.ts
+var DefaultSseRetryDurationMs = 1e3;
+
+// ../../../sdk/typescript/src/abstractServerSentEventGenerator.ts
+var ServerSentEventGenerator = class {
+ constructor() {
+ }
+ /**
+ * Sends a server-sent event (SSE) to the client.
+ *
+ * Runtimes should override this method by calling the parent function
+ * with `super.send(event, dataLines, options)`. That will return all the
+ * datalines as an array of strings that should be streamed to the client.
+ *
+ * @param eventType - The type of the event.
+ * @param dataLines - Lines of data to send.
+ * @param [sendOptions] - Additional options for sending events.
+ */
+ send(event, dataLines, options) {
+ const { eventId, retryDuration } = options || {};
+ const typeLine = [`event: ${event}
+`];
+ const idLine = eventId ? [`id: ${eventId}
+`] : [];
+ const retryLine = [
+ `retry: ${retryDuration ?? DefaultSseRetryDurationMs}
+`
+ ];
+ return typeLine.concat(
+ idLine,
+ retryLine,
+ dataLines.map((data) => {
+ return `data: ${data}
+`;
+ }),
+ ["\n\n"]
+ );
+ }
+ eachNewlineIsADataLine(prefix, data) {
+ return data.split("\n").map((line) => {
+ return `${prefix} ${line}`;
+ });
+ }
+ eachOptionIsADataLine(options) {
+ return Object.keys(options).flatMap((key) => {
+ return this.eachNewlineIsADataLine(
+ key,
+ options[key].toString()
+ );
+ });
+ }
+ /**
+ * Sends a merge fragments event.
+ *
+ * @param fragments - HTML fragments that will be merged.
+ * @param [options] - Additional options for merging.
+ */
+ mergeFragments(data, options) {
+ const { eventId, retryDuration, ...renderOptions } = options || {};
+ const dataLines = this.eachOptionIsADataLine(renderOptions).concat(this.eachNewlineIsADataLine("fragments", data));
+ return this.send("datastar-merge-fragments", dataLines, {
+ eventId,
+ retryDuration
+ });
+ }
+ /**
+ * Sends a remove fragments event.
+ *
+ * @param selector - CSS selector of fragments to remove.
+ * @param [options] - Additional options for removing.
+ */
+ removeFragments(selector, options) {
+ const { eventId, retryDuration, ...eventOptions } = options || {};
+ const dataLines = this.eachOptionIsADataLine(eventOptions).concat(this.eachNewlineIsADataLine("selector", selector));
+ return this.send("datastar-remove-fragments", dataLines, {
+ eventId,
+ retryDuration
+ });
+ }
+ /**
+ * Sends a merge signals event.
+ *
+ * @param data - Data object that will be merged into the client's signals.
+ * @param options - Additional options for merging.
+ */
+ mergeSignals(data, options) {
+ const { eventId, retryDuration, ...eventOptions } = options || {};
+ const dataLines = this.eachOptionIsADataLine(eventOptions).concat(this.eachNewlineIsADataLine("signals", JSON.stringify(data)));
+ return this.send("datastar-merge-signals", dataLines, {
+ eventId,
+ retryDuration
+ });
+ }
+ /**
+ * Sends a remove signals event.
+ *
+ * @param paths - Array of paths to remove from the client's signals
+ * @param options - Additional options for removing signals.
+ */
+ removeSignals(paths, options) {
+ const eventOptions = options || {};
+ const dataLines = paths.flatMap((path) => path.split(" ")).map(
+ (path) => `paths ${path}`
+ );
+ return this.send("datastar-remove-signals", dataLines, eventOptions);
+ }
+ /**
+ * Executes a script on the client-side.
+ *
+ * @param script - Script code to execute.
+ * @param options - Additional options for execution.
+ */
+ executeScript(script, options) {
+ const {
+ eventId,
+ retryDuration,
+ attributes,
+ ...eventOptions
+ } = options || {};
+ const attributesDataLines = this.eachOptionIsADataLine(attributes ?? {}).map((line) => `attributes ${line}`);
+ const dataLines = attributesDataLines.concat(
+ this.eachOptionIsADataLine(eventOptions),
+ this.eachNewlineIsADataLine("script", script)
+ );
+ return this.send("datastar-execute-script", dataLines, {
+ eventId,
+ retryDuration
+ });
+ }
+};
+
+// ../../../sdk/typescript/src/web/serverSentEventGenerator.ts
+function isRecord(obj) {
+ return typeof obj === "object" && obj !== null;
+}
+var ServerSentEventGenerator2 = class _ServerSentEventGenerator extends ServerSentEventGenerator {
+ controller;
+ constructor(controller) {
+ super();
+ this.controller = controller;
+ }
+ /**
+ * Initializes the server-sent event generator and executes the streamFunc function.
+ *
+ * @param streamFunc - A function that will be passed the initialized ServerSentEventGenerator class as it's first parameter.
+ * @returns an HTTP Response
+ */
+ static stream(streamFunc) {
+ const stream = new ReadableStream({
+ async start(controller) {
+ const stream2 = streamFunc(new _ServerSentEventGenerator(controller));
+ if (stream2 instanceof Promise) await stream2;
+ controller.close();
+ }
+ });
+ return new Response(stream, {
+ headers: sseHeaders
+ });
+ }
+ send(event, dataLines, options) {
+ const eventLines = super.send(event, dataLines, options);
+ eventLines.forEach((line) => {
+ this.controller?.enqueue(new TextEncoder().encode(line));
+ });
+ return eventLines;
+ }
+ /**
+ * Reads client sent signals based on HTTP methods
+ *
+ * @params request - The HTTP Request object.
+ *
+ * @returns An object containing a success boolean and either the client's signals or an error message.
+ */
+ static async readSignals(request) {
+ try {
+ if (request.method === "GET") {
+ const url = new URL(request.url);
+ const params = url.searchParams;
+ if (params.has("datastar")) {
+ const signals2 = JSON.parse(params.get("datastar"));
+ if (isRecord(signals2)) {
+ return { success: true, signals: signals2 };
+ } else throw new Error("Datastar param is not a record");
+ } else throw new Error("No datastar object in request");
+ }
+ const signals = await request.json();
+ if (isRecord(signals)) {
+ return { success: true, signals };
+ }
+ throw new Error("Parsed JSON body is not of type record");
+ } catch (e) {
+ if (isRecord(e) && "message" in e && typeof e.message === "string") {
+ return { success: false, error: e.message };
+ }
+ return { success: false, error: "unknown error when parsing request" };
+ }
+ }
+};
+
+// src/hello-world.js
+function getHelloWorldHtml() {
+ return `
+
+
+ Datastar SDK Demo
+
+
+
+
+
+
+
+
+ Datastar SDK Demo
+
+
+
+
+ SSE events will be streamed from the backend to the frontend.
+
+
+
+
+
+
+
+
+
Hello, world!
+
+
+
+
\ No newline at end of file
diff --git a/sdk/typescript/examples/node.js b/examples/typescript/node/node.js
similarity index 92%
rename from sdk/typescript/examples/node.js
rename to examples/typescript/node/node.js
index 6dc10105a..fe5f7f879 100644
--- a/sdk/typescript/examples/node.js
+++ b/examples/typescript/node/node.js
@@ -1,6 +1,6 @@
import { createServer } from "node:http";
// for this to work the esm build needs to be generated, see ../README.md
-import { ServerSentEventGenerator } from "../npm/esm/node/serverSentEventGenerator.js";
+import { ServerSentEventGenerator } from "../../../sdk/typescript/npm/esm/node/serverSentEventGenerator.js";
const hostname = "127.0.0.1";
const port = 3000;
diff --git a/examples/typescript/node/public/hello-world.html b/examples/typescript/node/public/hello-world.html
new file mode 100644
index 000000000..6f403b8dd
--- /dev/null
+++ b/examples/typescript/node/public/hello-world.html
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+ Datastar SDK Demo
+
+
+
+
+
+
+
+
+ Datastar SDK Demo
+
+
+
+
+ SSE events will be streamed from the backend to the frontend.
+
+
+
+
+
+
+
+
+
Hello, world!
+
+
+
+
\ No newline at end of file
diff --git a/sdk/typescript/examples/deno.ts b/sdk/typescript/examples/deno.ts
deleted file mode 100644
index 82f0ddd2d..000000000
--- a/sdk/typescript/examples/deno.ts
+++ /dev/null
@@ -1,44 +0,0 @@
-import { serve } from "https://deno.land/std@0.140.0/http/server.ts";
-import { ServerSentEventGenerator } from "../src/web/serverSentEventGenerator.ts";
-
-serve(async (req: Request) => {
- const url = new URL(req.url);
-
- if (url.pathname === "/") {
- return new Response(
- `
Hello
`,
- {
- headers: { "Content-Type": "text/html" },
- },
- );
- } else if (url.pathname.includes("/merge")) {
- const reader = await ServerSentEventGenerator.readSignals(req);
-
- if (!reader.success) {
- console.error("Error while reading signals", reader.error);
-
- return new Response(`Error while reading signals`, {
- headers: { "Content-Type": "text/html" },
- });
- }
-
- if (!("foo" in reader.signals)) {
- console.error("The foo signal is not present");
-
- return new Response("The foo signal is not present", {
- headers: { "Content-Type": "text/html" },
- });
- }
-
- return ServerSentEventGenerator.stream((stream) => {
- stream.mergeFragments(
- `