Files
memind/sse-billing.mjs
T
John 6ee6fd64dd Add MindSpace page live edit, chat skills, and H5 deploy tooling.
Introduce page edit sessions with draft preview and patch API, chat skill picker, user memory profile, h5ApiBase resolution, voice WAV transport, and scripts for 105/g2 deployment and Plaza local dev.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-15 22:09:38 -07:00

74 lines
1.8 KiB
JavaScript

import { Transform } from 'node:stream';
function parseSseChunk(chunk) {
const events = [];
const blocks = chunk.split('\n\n');
for (const block of blocks) {
if (!block.trim()) continue;
let data;
for (const line of block.split('\n')) {
if (line.startsWith('data:')) {
data = line.slice(5).trim();
}
}
if (!data) continue;
try {
events.push(JSON.parse(data));
} catch {
// ignore malformed frames
}
}
return events;
}
export function createSseBillingTransform({ onFinish }) {
let buffer = '';
return new Transform({
transform(chunk, _encoding, callback) {
buffer += chunk.toString('utf8');
const parts = buffer.split('\n\n');
buffer = parts.pop() ?? '';
for (const block of parts) {
if (!block.trim()) continue;
let dataLine;
for (const line of block.split('\n')) {
if (line.startsWith('data:')) dataLine = line.slice(5).trim();
}
if (!dataLine) continue;
try {
const event = JSON.parse(dataLine);
if (event?.type === 'Finish' && event.token_state) {
void onFinish(event).catch(() => {});
}
} catch {
// ignore
}
}
callback(null, chunk);
},
flush(callback) {
if (buffer.trim()) {
for (const event of parseSseChunk(`${buffer}\n\n`)) {
if (event?.type === 'Finish' && event.token_state) {
void onFinish(event).catch(() => {});
}
}
}
callback();
},
});
}
export function appendBalanceEvent(payload) {
const data =
typeof payload === 'number'
? { balanceCents: payload }
: payload && typeof payload === 'object'
? payload
: { balanceCents: 0 };
return `event: balance\ndata: ${JSON.stringify(data)}\n\n`;
}