Skip to content

Instantly share code, notes, and snippets.

@maxsei
Created June 15, 2026 12:51
Show Gist options
  • Select an option

  • Save maxsei/7140f2fb79ec3abf38276424493a71a2 to your computer and use it in GitHub Desktop.

Select an option

Save maxsei/7140f2fb79ec3abf38276424493a71a2 to your computer and use it in GitHub Desktop.
export type JsonObject = { [key: string]: Jsonifiable };
export type Jsonifiable =
| string
| number
| boolean
| null
| undefined
| Jsonifiable[]
| JsonObject;
export type DatastarEventOptions = {
id?: string;
retry?: number;
};
export type PatchOptions = DatastarEventOptions & {
useViewTransition?: true;
viewTransitionSelector?: string;
};
export type PatchElementsEvent = PatchOptions & {
event: "datastar-patch-elements";
namespace?: "html" | "svg" | "mathml";
} & (
| {
mode?: "outer" | "replace";
elements: string;
selector?: string;
}
| {
mode: "inner" | "prepend" | "append" | "before" | "after";
elements: string;
selector: string;
}
| { mode: "remove"; selector: string }
);
export type PatchSignalsEvent = PatchOptions & {
event: "datastar-patch-signals";
signals: JsonObject;
onlyIfMissing?: true;
};
export type PatchEvent = PatchElementsEvent | PatchSignalsEvent;
const Defaults = {
retry: 1000,
mode: "outer",
namespace: "html",
useViewTransition: false,
};
export function datastarSSE(patch: PatchEvent | Omit<PatchEvent, "event">) {
let lines = "";
const { event, id, retry, ...patchAndOptions } =
"event" in patch ? patch : {};
for (const [k, v] of Object.entries(event ? { event, id, retry } : {})) {
if (v == null || v === Defaults[k]) continue;
const s = v?.toString();
if (s && /[\r\n]/.test(s)) {
throw new Error(`${k} must not contain "\\r" or "\\n"`);
}
lines += `${k}: ${s}\n`;
}
const { elements, signals, ...options }: Record<string, Jsonifiable> =
patchAndOptions;
const entries = [
...Object.entries(options),
...Object.entries({
elements,
signals: JSON.stringify(signals),
}),
];
const prefix = "event" in patch ? "data:" : "";
for (const [k, v] of entries) {
if (v == null || v === Defaults[k]) continue;
for (const line of v.toString().split(/\r\n|\r|\n/)) {
lines += `${prefix} ${k} ${line}\n`;
}
}
return lines;
}
export function datastarResponse(patch: PatchEvent): Response {
const headers = new Headers();
const { event, id: _id, retry: _retry, ...optionsAndBody } = patch;
const contentTypes = {
"datastar-patch-elements": "text/html",
"datastar-patch-signals": "application/json",
} as const;
const contentType = contentTypes[event];
headers.set("Content-Type", contentType);
const { elements, signals, ...options } = optionsAndBody as Record<
string,
Jsonifiable
>;
const body = [elements?.toString() ?? "", JSON.stringify(signals)].find(
Boolean,
);
for (const [k, v] of Object.entries(options)) {
if (v == null) continue;
const kebab = k.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
const h = `datastar-${kebab}`;
headers.set(h, v.toString());
}
return new Response(body ?? "", { headers });
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment