@maravilla-labs/vite-plugin 0.3.10 → 0.3.12
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/dist/event-builder-CQL6EAPT.js +73 -0
- package/dist/event-builder-CQL6EAPT.js.map +1 -0
- package/dist/{event-dispatcher-R5FS6WGE.js → event-dispatcher-PC2QF2CK.js} +13 -6
- package/dist/event-dispatcher-PC2QF2CK.js.map +1 -0
- package/dist/index.d.ts +16 -2
- package/dist/index.js +105 -4
- package/dist/index.js.map +1 -1
- package/package.json +4 -3
- package/dist/event-dispatcher-R5FS6WGE.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// src/event-builder.ts
|
|
2
|
+
import { existsSync, mkdirSync, rmSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
async function rebuildEvents(opts) {
|
|
5
|
+
const { compileTransforms } = await import("@maravilla-labs/adapter-core");
|
|
6
|
+
const { buildEvents } = await import("@maravilla-labs/functions");
|
|
7
|
+
const compiled = compileTransforms(opts.transforms);
|
|
8
|
+
const eventsDir = join(opts.projectDir, "events");
|
|
9
|
+
const hasRoot = existsSync(join(opts.projectDir, "events.ts")) || existsSync(join(opts.projectDir, "events.js")) || existsSync(join(opts.projectDir, "events.mjs"));
|
|
10
|
+
const hasDir = existsSync(eventsDir);
|
|
11
|
+
if (!hasRoot && !hasDir && !compiled) return null;
|
|
12
|
+
const cleanup = [];
|
|
13
|
+
if (compiled) {
|
|
14
|
+
const eventsDirExisted = existsSync(eventsDir);
|
|
15
|
+
if (!eventsDirExisted) {
|
|
16
|
+
mkdirSync(eventsDir, { recursive: true });
|
|
17
|
+
cleanup.push(() => {
|
|
18
|
+
try {
|
|
19
|
+
rmSync(eventsDir, { recursive: false });
|
|
20
|
+
} catch {
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
const generatedPath = join(eventsDir, `${compiled.suggestedFileBasename}.ts`);
|
|
25
|
+
writeFileSync(generatedPath, compiled.source, "utf8");
|
|
26
|
+
cleanup.push(() => {
|
|
27
|
+
try {
|
|
28
|
+
rmSync(generatedPath, { force: true });
|
|
29
|
+
} catch {
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
mkdirSync(opts.outDir, { recursive: true });
|
|
35
|
+
const manifest = await buildEvents({
|
|
36
|
+
projectDir: opts.projectDir,
|
|
37
|
+
outputDir: opts.outDir,
|
|
38
|
+
production: false,
|
|
39
|
+
minify: false
|
|
40
|
+
});
|
|
41
|
+
return manifest?.handlers.length ?? 0;
|
|
42
|
+
} catch (err) {
|
|
43
|
+
opts.log?.(`[maravilla][events] build failed: ${err?.message ?? err}`);
|
|
44
|
+
throw err;
|
|
45
|
+
} finally {
|
|
46
|
+
for (let i = cleanup.length - 1; i >= 0; i -= 1) cleanup[i]();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function rebuildWorkflows(opts) {
|
|
50
|
+
const { buildWorkflows } = await import("@maravilla-labs/functions");
|
|
51
|
+
const workflowsDir = join(opts.projectDir, "workflows");
|
|
52
|
+
const hasRoot = existsSync(join(opts.projectDir, "workflows.ts")) || existsSync(join(opts.projectDir, "workflows.js")) || existsSync(join(opts.projectDir, "workflows.mjs"));
|
|
53
|
+
const hasDir = existsSync(workflowsDir);
|
|
54
|
+
if (!hasRoot && !hasDir) return null;
|
|
55
|
+
try {
|
|
56
|
+
mkdirSync(opts.outDir, { recursive: true });
|
|
57
|
+
const manifest = await buildWorkflows({
|
|
58
|
+
projectDir: opts.projectDir,
|
|
59
|
+
outputDir: opts.outDir,
|
|
60
|
+
production: false,
|
|
61
|
+
minify: false
|
|
62
|
+
});
|
|
63
|
+
return manifest?.workflows?.length ?? 0;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
opts.log?.(`[maravilla][workflows] build failed: ${err?.message ?? err}`);
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export {
|
|
70
|
+
rebuildEvents,
|
|
71
|
+
rebuildWorkflows
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=event-builder-CQL6EAPT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/event-builder.ts"],"sourcesContent":["/**\n * Dev-mode events + workflows builder. Replicates what\n * `@maravilla-labs/adapter-core::buildAndIntegrateEvents` /\n * `buildAndIntegrateWorkflows` do at deploy time, MINUS the manifest.json\n * integration step (dev never owns a `manifest.json` — that's the\n * adapter's deploy-time artefact).\n *\n * Output goes to `${outDir}/events.js` + `events.json` (and the workflows\n * equivalents). The vite-plugin's auto-fire dispatcher\n * (`event-dispatcher.ts`) imports those and the `dev-server`'s\n * `/api/events/_reload` reads the manifest.\n */\n\nimport { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';\nimport { join } from 'node:path';\n\ninterface RebuildOptions {\n projectDir: string;\n outDir: string;\n /** transforms block from maravilla.config.ts — drives synthesized handlers */\n transforms?: unknown;\n log?: (msg: string) => void;\n}\n\n/**\n * Rebuild the events bundle in-place. Returns the number of handlers\n * registered, or null if no events to build (no events/ dir, no\n * events.ts, no transforms).\n */\nexport async function rebuildEvents(opts: RebuildOptions): Promise<number | null> {\n const { compileTransforms } = await import('@maravilla-labs/adapter-core');\n const { buildEvents } = await import('@maravilla-labs/functions');\n\n const compiled = compileTransforms(opts.transforms as never);\n\n const eventsDir = join(opts.projectDir, 'events');\n const hasRoot = existsSync(join(opts.projectDir, 'events.ts'))\n || existsSync(join(opts.projectDir, 'events.js'))\n || existsSync(join(opts.projectDir, 'events.mjs'));\n const hasDir = existsSync(eventsDir);\n\n if (!hasRoot && !hasDir && !compiled) return null;\n\n // Materialize compiled transforms next to hand-written sources so\n // discoverEventSources picks them up. Track for rollback.\n const cleanup: Array<() => void> = [];\n if (compiled) {\n const eventsDirExisted = existsSync(eventsDir);\n if (!eventsDirExisted) {\n mkdirSync(eventsDir, { recursive: true });\n cleanup.push(() => {\n try { rmSync(eventsDir, { recursive: false }); } catch { /* not empty — leave */ }\n });\n }\n const generatedPath = join(eventsDir, `${(compiled as { suggestedFileBasename: string }).suggestedFileBasename}.ts`);\n writeFileSync(generatedPath, (compiled as { source: string }).source, 'utf8');\n cleanup.push(() => {\n try { rmSync(generatedPath, { force: true }); } catch { /* ignore */ }\n });\n }\n\n try {\n mkdirSync(opts.outDir, { recursive: true });\n const manifest = await buildEvents({\n projectDir: opts.projectDir,\n outputDir: opts.outDir,\n production: false,\n minify: false,\n });\n return manifest?.handlers.length ?? 0;\n } catch (err) {\n opts.log?.(`[maravilla][events] build failed: ${(err as Error)?.message ?? err}`);\n throw err;\n } finally {\n for (let i = cleanup.length - 1; i >= 0; i -= 1) cleanup[i]!();\n }\n}\n\ninterface RebuildWorkflowsOptions {\n projectDir: string;\n outDir: string;\n log?: (msg: string) => void;\n}\n\n/**\n * Rebuild the workflows bundle in-place. Returns the number of\n * workflows registered, or null if no workflows to build.\n */\nexport async function rebuildWorkflows(opts: RebuildWorkflowsOptions): Promise<number | null> {\n const { buildWorkflows } = await import('@maravilla-labs/functions');\n\n const workflowsDir = join(opts.projectDir, 'workflows');\n const hasRoot = existsSync(join(opts.projectDir, 'workflows.ts'))\n || existsSync(join(opts.projectDir, 'workflows.js'))\n || existsSync(join(opts.projectDir, 'workflows.mjs'));\n const hasDir = existsSync(workflowsDir);\n\n if (!hasRoot && !hasDir) return null;\n\n try {\n mkdirSync(opts.outDir, { recursive: true });\n const manifest = await buildWorkflows({\n projectDir: opts.projectDir,\n outputDir: opts.outDir,\n production: false,\n minify: false,\n });\n return manifest?.workflows?.length ?? 0;\n } catch (err) {\n opts.log?.(`[maravilla][workflows] build failed: ${(err as Error)?.message ?? err}`);\n throw err;\n }\n}\n"],"mappings":";AAaA,SAAS,YAAY,WAAW,QAAQ,qBAAqB;AAC7D,SAAS,YAAY;AAerB,eAAsB,cAAc,MAA8C;AAChF,QAAM,EAAE,kBAAkB,IAAI,MAAM,OAAO,8BAA8B;AACzE,QAAM,EAAE,YAAY,IAAI,MAAM,OAAO,2BAA2B;AAEhE,QAAM,WAAW,kBAAkB,KAAK,UAAmB;AAE3D,QAAM,YAAY,KAAK,KAAK,YAAY,QAAQ;AAChD,QAAM,UAAU,WAAW,KAAK,KAAK,YAAY,WAAW,CAAC,KACxD,WAAW,KAAK,KAAK,YAAY,WAAW,CAAC,KAC7C,WAAW,KAAK,KAAK,YAAY,YAAY,CAAC;AACnD,QAAM,SAAS,WAAW,SAAS;AAEnC,MAAI,CAAC,WAAW,CAAC,UAAU,CAAC,SAAU,QAAO;AAI7C,QAAM,UAA6B,CAAC;AACpC,MAAI,UAAU;AACZ,UAAM,mBAAmB,WAAW,SAAS;AAC7C,QAAI,CAAC,kBAAkB;AACrB,gBAAU,WAAW,EAAE,WAAW,KAAK,CAAC;AACxC,cAAQ,KAAK,MAAM;AACjB,YAAI;AAAE,iBAAO,WAAW,EAAE,WAAW,MAAM,CAAC;AAAA,QAAG,QAAQ;AAAA,QAA0B;AAAA,MACnF,CAAC;AAAA,IACH;AACA,UAAM,gBAAgB,KAAK,WAAW,GAAI,SAA+C,qBAAqB,KAAK;AACnH,kBAAc,eAAgB,SAAgC,QAAQ,MAAM;AAC5E,YAAQ,KAAK,MAAM;AACjB,UAAI;AAAE,eAAO,eAAe,EAAE,OAAO,KAAK,CAAC;AAAA,MAAG,QAAQ;AAAA,MAAe;AAAA,IACvE,CAAC;AAAA,EACH;AAEA,MAAI;AACF,cAAU,KAAK,QAAQ,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,WAAW,MAAM,YAAY;AAAA,MACjC,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,UAAU,SAAS,UAAU;AAAA,EACtC,SAAS,KAAK;AACZ,SAAK,MAAM,qCAAsC,KAAe,WAAW,GAAG,EAAE;AAChF,UAAM;AAAA,EACR,UAAE;AACA,aAAS,IAAI,QAAQ,SAAS,GAAG,KAAK,GAAG,KAAK,EAAG,SAAQ,CAAC,EAAG;AAAA,EAC/D;AACF;AAYA,eAAsB,iBAAiB,MAAuD;AAC5F,QAAM,EAAE,eAAe,IAAI,MAAM,OAAO,2BAA2B;AAEnE,QAAM,eAAe,KAAK,KAAK,YAAY,WAAW;AACtD,QAAM,UAAU,WAAW,KAAK,KAAK,YAAY,cAAc,CAAC,KAC3D,WAAW,KAAK,KAAK,YAAY,cAAc,CAAC,KAChD,WAAW,KAAK,KAAK,YAAY,eAAe,CAAC;AACtD,QAAM,SAAS,WAAW,YAAY;AAEtC,MAAI,CAAC,WAAW,CAAC,OAAQ,QAAO;AAEhC,MAAI;AACF,cAAU,KAAK,QAAQ,EAAE,WAAW,KAAK,CAAC;AAC1C,UAAM,WAAW,MAAM,eAAe;AAAA,MACpC,YAAY,KAAK;AAAA,MACjB,WAAW,KAAK;AAAA,MAChB,YAAY;AAAA,MACZ,QAAQ;AAAA,IACV,CAAC;AACD,WAAO,UAAU,WAAW,UAAU;AAAA,EACxC,SAAS,KAAK;AACZ,SAAK,MAAM,wCAAyC,KAAe,WAAW,GAAG,EAAE;AACnF,UAAM;AAAA,EACR;AACF;","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/event-dispatcher.ts
|
|
2
2
|
import { existsSync, statSync } from "fs";
|
|
3
|
-
import { resolve
|
|
3
|
+
import { resolve } from "path";
|
|
4
4
|
import { pathToFileURL } from "url";
|
|
5
5
|
|
|
6
6
|
// src/event-matcher.ts
|
|
@@ -125,10 +125,16 @@ function globMatch(pattern, input) {
|
|
|
125
125
|
|
|
126
126
|
// src/event-dispatcher.ts
|
|
127
127
|
async function startEventDispatcher(opts) {
|
|
128
|
-
const
|
|
129
|
-
|
|
128
|
+
const primaryBundlePath = resolve(opts.projectDir, opts.outDir, "events.js");
|
|
129
|
+
const fallbackBundlePath = resolve(opts.projectDir, ".maravilla", "events.js");
|
|
130
|
+
let bundlePath;
|
|
131
|
+
if (existsSync(primaryBundlePath)) {
|
|
132
|
+
bundlePath = primaryBundlePath;
|
|
133
|
+
} else if (existsSync(fallbackBundlePath)) {
|
|
134
|
+
bundlePath = fallbackBundlePath;
|
|
135
|
+
} else {
|
|
130
136
|
console.warn(
|
|
131
|
-
|
|
137
|
+
`[maravilla][events] no events.js found in ${opts.outDir}/ or .maravilla/ \u2014 event auto-fire disabled until first build.`
|
|
132
138
|
);
|
|
133
139
|
return () => {
|
|
134
140
|
};
|
|
@@ -159,8 +165,9 @@ async function startEventDispatcher(opts) {
|
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
167
|
await loadBundle();
|
|
168
|
+
const watchDir = (await import("path")).dirname(bundlePath);
|
|
162
169
|
const fsWatcher = (await import("fs")).watch(
|
|
163
|
-
|
|
170
|
+
watchDir,
|
|
164
171
|
{ persistent: false },
|
|
165
172
|
(event, filename) => {
|
|
166
173
|
if (filename === "events.js") {
|
|
@@ -283,4 +290,4 @@ async function startEventDispatcher(opts) {
|
|
|
283
290
|
export {
|
|
284
291
|
startEventDispatcher
|
|
285
292
|
};
|
|
286
|
-
//# sourceMappingURL=event-dispatcher-
|
|
293
|
+
//# sourceMappingURL=event-dispatcher-PC2QF2CK.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/event-dispatcher.ts","../src/event-matcher.ts"],"sourcesContent":["/**\n * Dev-mode event dispatcher. The runtime's\n * `crates/platform/src/events/dispatcher.rs` does this server-side in\n * Rust + Deno isolate. In dev there's no isolate, so we run the same\n * loop here in tenant Node.js (via the Vite dev server's process):\n *\n * 1. Load the events bundle the build pipeline produced\n * (`.maravilla/events.js` — sets `globalThis.__maravilla_event_registry`).\n * 2. Subscribe to the dev-server's REN SSE stream.\n * 3. For each `RenEvent`, run the matcher against every registered\n * trigger. Invoke matched handlers with `{ event, ctx: { platform } }`.\n *\n * Result: a tenant calling `platform.env.STORAGE.put('invites/.../videos/x.webm', ...)`\n * sees the synthesized transcode + thumbnail handlers fire in dev exactly\n * like they do in prod — same semantics, same timing model (after the put\n * returns, async via SSE), same content-addressed output keys.\n */\n\nimport { existsSync, statSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { matches, type RenEvent, type Trigger } from './event-matcher.js';\n\ninterface RegistryEntry {\n trigger: Trigger;\n handler: (event: unknown, ctx: unknown) => unknown | Promise<unknown>;\n}\n\ntype Registry = Record<string, RegistryEntry>;\n\ninterface DispatcherOptions {\n /** Project root. */\n projectDir: string;\n /** Where the events bundle lives (e.g. `build/` for SvelteKit/RR). */\n outDir: string;\n /** URL of the Maravilla dev-server. */\n devServerUrl: string;\n /** Tenant id sent on outgoing requests + used to key the SSE stream. */\n tenant: string;\n}\n\n/**\n * Stand up a dev-mode event dispatcher. Returns a teardown function.\n * Bundle path is resolved against `opts.outDir`; falls back to\n * `.maravilla/events.js` for older project layouts.\n */\nexport async function startEventDispatcher(opts: DispatcherOptions): Promise<() => void> {\n const primaryBundlePath = resolve(opts.projectDir, opts.outDir, 'events.js');\n const fallbackBundlePath = resolve(opts.projectDir, '.maravilla', 'events.js');\n let bundlePath: string;\n if (existsSync(primaryBundlePath)) {\n bundlePath = primaryBundlePath;\n } else if (existsSync(fallbackBundlePath)) {\n bundlePath = fallbackBundlePath;\n } else {\n console.warn(\n `[maravilla][events] no events.js found in ${opts.outDir}/ or .maravilla/ — event auto-fire disabled until first build.`,\n );\n return () => {};\n }\n\n let registry: Registry = {};\n let bundleMtime = 0;\n\n async function loadBundle() {\n const stat = statSync(bundlePath);\n if (stat.mtimeMs === bundleMtime) return;\n try {\n // The bundle is IIFE — when imported it executes top-level and\n // sets `globalThis.__maravilla_event_registry` as a side effect.\n // Cache-bust via querystring so HMR rebuilds get picked up.\n await import(pathToFileURL(bundlePath).href + `?t=${stat.mtimeMs}`);\n const reg = (globalThis as { __maravilla_event_registry?: Registry })\n .__maravilla_event_registry;\n if (reg && typeof reg === 'object') {\n registry = reg;\n bundleMtime = stat.mtimeMs;\n const handlerCount = Object.keys(registry).length;\n console.log(\n `[maravilla][events] loaded ${handlerCount} handler${handlerCount === 1 ? '' : 's'} from .maravilla/events.js`,\n );\n } else {\n console.warn('[maravilla][events] events bundle did not expose __maravilla_event_registry');\n }\n } catch (err) {\n console.error(\n '[maravilla][events] failed to load events bundle:',\n (err as Error)?.message ?? err,\n );\n }\n }\n\n await loadBundle();\n\n // Watch the bundle for HMR-style refresh.\n const watchDir = (await import('node:path')).dirname(bundlePath);\n const fsWatcher = (await import('node:fs')).watch(\n watchDir,\n { persistent: false },\n (event, filename) => {\n if (filename === 'events.js') {\n // Slight debounce: filesystem may emit multiple `change` events\n // while esbuild rewrites the file.\n setTimeout(() => { void loadBundle(); }, 100);\n }\n },\n );\n\n // SSE consumer. Use the `eventsource` polyfill in Node — `EventSource`\n // is only available in browsers natively.\n let aborter: AbortController | null = new AbortController();\n let reconnectTimer: NodeJS.Timeout | null = null;\n let stopped = false;\n\n async function connect(): Promise<void> {\n if (stopped) return;\n aborter = new AbortController();\n const url = `${opts.devServerUrl}/api/maravilla/ren`;\n try {\n const res = await fetch(url, {\n headers: {\n Accept: 'text/event-stream',\n 'X-Tenant-Id': opts.tenant,\n },\n signal: aborter.signal,\n });\n if (!res.ok || !res.body) {\n throw new Error(`SSE connect ${res.status}`);\n }\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = '';\n // Read SSE frames. `data: <json>` lines are events; blank line ends\n // the frame.\n while (!stopped) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let idx: number;\n while ((idx = buf.indexOf('\\n\\n')) !== -1) {\n const frame = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n handleFrame(frame);\n }\n }\n } catch (err) {\n if (stopped) return;\n const msg = (err as Error)?.message ?? String(err);\n if (!msg.includes('aborted')) {\n // Reconnect on transport errors. Constant 2s backoff is fine\n // for a dev tool — operator can ctrl-c if the loop is wrong.\n console.warn(`[maravilla][events] REN SSE disconnected: ${msg}; reconnecting in 2s`);\n reconnectTimer = setTimeout(() => { void connect(); }, 2000);\n }\n }\n }\n\n function handleFrame(frame: string): void {\n for (const line of frame.split('\\n')) {\n if (!line.startsWith('data:')) continue;\n const payload = line.slice(5).trim();\n if (!payload) continue;\n let event: RenEvent;\n try {\n event = JSON.parse(payload) as RenEvent;\n } catch {\n continue;\n }\n void dispatch(event);\n }\n }\n\n async function dispatch(event: RenEvent): Promise<void> {\n const platform = (await import('@maravilla-labs/platform')).getPlatform();\n // Stamp platform onto globalThis so the bundle's buildCtx() picks it\n // up — same shape the runtime uses inside the Deno isolate.\n (globalThis as { platform?: unknown }).platform = platform;\n\n for (const [id, entry] of Object.entries(registry)) {\n if (!matches(entry.trigger, event)) continue;\n try {\n await entry.handler(eventPayloadFor(event), {\n platform,\n handlerId: id,\n tenant: opts.tenant,\n traceId: globalThis.crypto?.randomUUID?.() ?? String(Date.now()),\n });\n } catch (err) {\n console.error(\n `[maravilla][events] handler ${id} failed:`,\n (err as Error)?.message ?? err,\n );\n }\n }\n }\n\n /**\n * Mirror `crates/platform/src/events/dispatcher.rs:163-247`'s payload\n * transform — handlers receive `{ op, key, data, ts }` for storage,\n * `{ namespace, key, value, ts, op }` for kv, etc., not the raw\n * RenEvent.\n */\n function eventPayloadFor(event: RenEvent): unknown {\n switch (event.r) {\n case 'storage': {\n const op = event.t.startsWith('storage.') ? event.t.slice('storage.'.length) : event.t;\n return { op, key: event.k, data: event.data, ts: event.ts };\n }\n case 'kv': {\n const op = event.t.startsWith('kv.') ? event.t.slice('kv.'.length) : event.t;\n return { op, namespace: event.ns, key: event.k, data: event.data, ts: event.ts };\n }\n case 'db': {\n const op = event.t.startsWith('db.document.') ? event.t.slice('db.document.'.length) : event.t;\n return { op, collection: event.ns, document: event.data, ts: event.ts };\n }\n case 'auth': {\n return { type: event.t, user_id: event.k, data: event.data, ts: event.ts };\n }\n case 'realtime':\n case 'presence': {\n return { type: event.t, channel: event.ch, data: event.data, ts: event.ts };\n }\n default:\n return event;\n }\n }\n\n void connect();\n\n return () => {\n stopped = true;\n aborter?.abort();\n if (reconnectTimer) clearTimeout(reconnectTimer);\n fsWatcher.close();\n };\n}\n","/**\n * TS port of `crates/platform/src/events/matcher.rs`. Used by the\n * dev-mode event dispatcher in this package to match incoming\n * `RenEvent`s against the trigger descriptors emitted in\n * `.maravilla/events.json`.\n *\n * Keep in lockstep with the Rust matcher — the golden-vector tests\n * across both implementations should produce the same yes/no for the\n * same (trigger, event) pair.\n */\n\nexport interface RenEvent {\n /** Event type, e.g. `\"storage.put\"`, `\"kv.put\"`, `\"presence.join\"` */\n t: string;\n /** Resource domain, e.g. `\"storage\"`, `\"kv\"`, `\"realtime\"`, `\"auth\"` */\n r: string;\n /** Resource key (e.g. storage path, kv key) */\n k?: string | null;\n /** Namespace / bucket / collection */\n ns?: string | null;\n /** Realtime channel name (for pub/sub) */\n ch?: string | null;\n /** Deployment id tagging the event origin */\n dep?: string | null;\n data?: unknown;\n ts?: number;\n}\n\n/** Trigger shapes mirror `crates/platform/src/events/types.rs` (camelCase). */\nexport type Trigger =\n | { kind: 'kv'; namespace?: string; keyPattern?: string; op?: 'put' | 'delete' | 'expired' }\n | { kind: 'db'; collection: string; op?: 'insert' | 'update' | 'delete' }\n | { kind: 'auth'; op?: string }\n | { kind: 'channel'; channel: string; type?: string }\n | { kind: 'storage'; keyPattern?: string; op?: 'put' | 'delete' }\n | { kind: 'deploy'; phase: 'ready' | 'draining' | 'stopped' }\n | { kind: 'ren'; match: { r?: string; t?: string; ns?: string } }\n | { kind: 'schedule'; cron: string } // never matches RenEvents\n | { kind: 'queue'; name: string }; // never matches RenEvents\n\n/**\n * Returns `true` if the event matches the trigger. Mirrors the Rust\n * `matches()` in `events/matcher.rs:12-34`.\n */\nexport function matches(trigger: Trigger, event: RenEvent): boolean {\n switch (trigger.kind) {\n case 'kv': return matchKv(trigger, event);\n case 'db': return matchDb(trigger, event);\n case 'auth': return matchAuth(trigger, event);\n case 'channel': return matchChannel(trigger, event);\n case 'storage': return matchStorage(trigger, event);\n case 'deploy': return matchDeploy(trigger, event);\n case 'ren': return matchRen(trigger, event);\n case 'schedule': return false;\n case 'queue': return false;\n default: return false;\n }\n}\n\nfunction matchKv(t: Extract<Trigger, { kind: 'kv' }>, e: RenEvent): boolean {\n if (e.r !== 'kv') return false;\n if (t.op !== undefined) {\n const eventOp = e.t === 'kv.put' ? 'put' : e.t === 'kv.delete' ? 'delete' : e.t === 'kv.expired' ? 'expired' : null;\n if (eventOp !== t.op) return false;\n }\n if (t.namespace !== undefined && e.ns !== t.namespace) return false;\n if (t.keyPattern !== undefined) {\n if (!e.k) return false;\n if (!globMatch(t.keyPattern, e.k)) return false;\n }\n return true;\n}\n\nfunction matchDb(t: Extract<Trigger, { kind: 'db' }>, e: RenEvent): boolean {\n if (e.r !== 'db') return false;\n if (e.ns !== t.collection) return false;\n if (t.op !== undefined) {\n const expected = t.op === 'insert' ? 'db.document.created'\n : t.op === 'update' ? 'db.document.updated'\n : t.op === 'delete' ? 'db.document.deleted' : null;\n if (e.t !== expected) return false;\n }\n return true;\n}\n\nfunction matchAuth(t: Extract<Trigger, { kind: 'auth' }>, e: RenEvent): boolean {\n if (e.r !== 'auth') return false;\n if (t.op !== undefined) {\n const expected = ({\n registered: 'auth.user.registered',\n loggedIn: 'auth.user.logged_in',\n loggedOut: 'auth.user.logged_out',\n loggedOutAll: 'auth.user.logged_out_all',\n deleted: 'auth.user.deleted',\n updated: 'auth.user.updated',\n } as Record<string, string>)[t.op];\n return e.t === expected;\n }\n return e.t.startsWith('auth.user.');\n}\n\nfunction matchChannel(t: Extract<Trigger, { kind: 'channel' }>, e: RenEvent): boolean {\n if (e.r !== 'realtime' && e.r !== 'presence') return false;\n if (!e.ch) return false;\n if (!globMatch(t.channel, e.ch)) return false;\n if (t.type !== undefined && e.t !== t.type) return false;\n return true;\n}\n\nfunction matchStorage(t: Extract<Trigger, { kind: 'storage' }>, e: RenEvent): boolean {\n if (e.r !== 'storage') return false;\n if (t.op !== undefined) {\n const eventOp = e.t === 'storage.put' ? 'put' : e.t === 'storage.delete' ? 'delete' : null;\n if (eventOp !== t.op) return false;\n }\n if (t.keyPattern !== undefined) {\n if (!e.k) return false;\n if (!globMatch(t.keyPattern, e.k)) return false;\n }\n return true;\n}\n\nfunction matchDeploy(t: Extract<Trigger, { kind: 'deploy' }>, e: RenEvent): boolean {\n if (e.r !== 'deploy') return false;\n const expected = `deploy.${t.phase}`;\n return e.t === expected;\n}\n\nfunction matchRen(t: Extract<Trigger, { kind: 'ren' }>, e: RenEvent): boolean {\n const m = t.match;\n if (m.r !== undefined && e.r !== m.r) return false;\n if (m.t !== undefined && e.t !== m.t) return false;\n if (m.ns !== undefined && e.ns !== m.ns) return false;\n return true;\n}\n\n/**\n * Minimal glob matcher supporting `*` (zero or more chars) and literal\n * segments. Mirrors `events/matcher.rs::glob_match`. Not full POSIX —\n * intentionally tiny. Handles `*`, `prefix:*`, `*:suffix`, `a:*:b`.\n */\nexport function globMatch(pattern: string, input: string): boolean {\n if (pattern === '*') return true;\n if (!pattern.includes('*')) return pattern === input;\n\n const segments = pattern.split('*');\n let cursor = 0;\n\n // First segment must match as prefix (unless empty, i.e. leading `*`).\n const first = segments[0];\n if (first && first.length > 0) {\n if (!input.startsWith(first)) return false;\n cursor = first.length;\n }\n\n // Middle segments: find each in order.\n for (let i = 1; i < segments.length - 1; i += 1) {\n const seg = segments[i];\n if (!seg || seg.length === 0) continue;\n const idx = input.indexOf(seg, cursor);\n if (idx === -1) return false;\n cursor = idx + seg.length;\n }\n\n // Last segment must match as suffix (unless empty, i.e. trailing `*`).\n if (segments.length > 1) {\n const last = segments[segments.length - 1];\n if (last && last.length > 0) {\n const tail = input.slice(cursor);\n return tail.endsWith(last) && tail.length >= last.length;\n }\n }\n return true;\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.ts
CHANGED
|
@@ -67,8 +67,14 @@ interface MaravillaPluginOptions {
|
|
|
67
67
|
*/
|
|
68
68
|
devServerUrl?: string;
|
|
69
69
|
/**
|
|
70
|
-
* Tenant ID for development.
|
|
71
|
-
*
|
|
70
|
+
* Tenant ID for development. Must match the dev-server's tenant id —
|
|
71
|
+
* `crates/dev-server/src/types.rs::extract_tenant_id` defaults to
|
|
72
|
+
* `dev-tenant-001` when no `X-Tenant-Id` header is supplied, and
|
|
73
|
+
* storage/db/etc. publish REN events keyed by the dev-server's
|
|
74
|
+
* `TenantContext.tenant_id` (also `dev-tenant-001`). If this option
|
|
75
|
+
* doesn't match, the dispatcher's SSE subscription lands on a
|
|
76
|
+
* different broadcast channel and no events ever fire.
|
|
77
|
+
* @default 'dev-tenant-001'
|
|
72
78
|
*/
|
|
73
79
|
tenant?: string;
|
|
74
80
|
/**
|
|
@@ -83,6 +89,14 @@ interface MaravillaPluginOptions {
|
|
|
83
89
|
* @default false
|
|
84
90
|
*/
|
|
85
91
|
disableProxy?: boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Output directory for events.js / events.json. Matches the framework
|
|
94
|
+
* adapter's `out` option so dev re-uses the same artefacts. Defaults
|
|
95
|
+
* to `'build'` (SvelteKit + React Router 7 convention).
|
|
96
|
+
*
|
|
97
|
+
* Override if your adapter writes elsewhere (e.g. `'.maravilla'`).
|
|
98
|
+
*/
|
|
99
|
+
outDir?: string;
|
|
86
100
|
}
|
|
87
101
|
declare function maravilla(options?: MaravillaPluginOptions): Plugin;
|
|
88
102
|
|
package/dist/index.js
CHANGED
|
@@ -275,9 +275,10 @@ var DEFAULT_PROXY_PREFIXES = [
|
|
|
275
275
|
];
|
|
276
276
|
function maravilla(options = {}) {
|
|
277
277
|
const devServerUrl = options.devServerUrl || "http://localhost:3001";
|
|
278
|
-
const tenant = options.tenant || "dev-tenant";
|
|
278
|
+
const tenant = options.tenant || "dev-tenant-001";
|
|
279
279
|
const disableProxy = options.disableProxy ?? false;
|
|
280
280
|
const additionalProxies = options.additionalProxies ?? [];
|
|
281
|
+
const outDir = options.outDir || "build";
|
|
281
282
|
return {
|
|
282
283
|
name: "vite-plugin-maravilla",
|
|
283
284
|
config() {
|
|
@@ -321,13 +322,113 @@ function maravilla(options = {}) {
|
|
|
321
322
|
}
|
|
322
323
|
});
|
|
323
324
|
try {
|
|
324
|
-
const { startEventDispatcher } = await import("./event-dispatcher-
|
|
325
|
+
const { startEventDispatcher } = await import("./event-dispatcher-PC2QF2CK.js");
|
|
326
|
+
const { rebuildEvents, rebuildWorkflows } = await import("./event-builder-CQL6EAPT.js");
|
|
327
|
+
const { loadMaravillaConfig } = await import("@maravilla-labs/adapter-core");
|
|
328
|
+
const projectDir = process.cwd();
|
|
329
|
+
let configCache = {};
|
|
330
|
+
try {
|
|
331
|
+
const loaded = await loadMaravillaConfig(projectDir);
|
|
332
|
+
configCache = loaded?.config ?? {};
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
const runRebuild = async (kind) => {
|
|
336
|
+
try {
|
|
337
|
+
if (kind === "events" || kind === "both") {
|
|
338
|
+
const n = await rebuildEvents({
|
|
339
|
+
projectDir,
|
|
340
|
+
outDir,
|
|
341
|
+
transforms: configCache.transforms,
|
|
342
|
+
log: (m) => console.warn(m)
|
|
343
|
+
});
|
|
344
|
+
if (n !== null) console.log(`[maravilla][events] built ${n} handler${n === 1 ? "" : "s"} \u2192 ${outDir}/events.json`);
|
|
345
|
+
}
|
|
346
|
+
if (kind === "workflows" || kind === "both") {
|
|
347
|
+
const n = await rebuildWorkflows({
|
|
348
|
+
projectDir,
|
|
349
|
+
outDir,
|
|
350
|
+
log: (m) => console.warn(m)
|
|
351
|
+
});
|
|
352
|
+
if (n !== null) console.log(`[maravilla][workflows] built ${n} workflow${n === 1 ? "" : "s"} \u2192 ${outDir}/workflows.json`);
|
|
353
|
+
}
|
|
354
|
+
try {
|
|
355
|
+
await fetch(`${devServerUrl}/api/events/_reload`, {
|
|
356
|
+
method: "POST",
|
|
357
|
+
headers: { "Content-Type": "application/json", "X-Tenant-Id": tenant }
|
|
358
|
+
});
|
|
359
|
+
} catch {
|
|
360
|
+
}
|
|
361
|
+
} catch (err) {
|
|
362
|
+
console.error(
|
|
363
|
+
`[maravilla][${kind}] rebuild failed:`,
|
|
364
|
+
err?.message ?? err
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
};
|
|
368
|
+
await runRebuild("both");
|
|
369
|
+
const watchPaths = [
|
|
370
|
+
"events.ts",
|
|
371
|
+
"events.js",
|
|
372
|
+
"events.mjs",
|
|
373
|
+
"workflows.ts",
|
|
374
|
+
"workflows.js",
|
|
375
|
+
"workflows.mjs",
|
|
376
|
+
"maravilla.config.ts",
|
|
377
|
+
"maravilla.config.js",
|
|
378
|
+
"maravilla.config.mjs"
|
|
379
|
+
].map((p) => `${projectDir}/${p}`);
|
|
380
|
+
const watchDirs = [`${projectDir}/events`, `${projectDir}/workflows`];
|
|
381
|
+
const fs = await import("fs");
|
|
382
|
+
let debounce = null;
|
|
383
|
+
let pendingKind = "events";
|
|
384
|
+
const triggerRebuild = (path) => {
|
|
385
|
+
const isWorkflow = path.includes("/workflows");
|
|
386
|
+
const isConfig = path.includes("maravilla.config");
|
|
387
|
+
const newKind = isConfig ? "both" : isWorkflow ? "workflows" : "events";
|
|
388
|
+
if (pendingKind !== "both" && pendingKind !== newKind) pendingKind = "both";
|
|
389
|
+
else if (debounce === null) pendingKind = newKind;
|
|
390
|
+
if (debounce) clearTimeout(debounce);
|
|
391
|
+
debounce = setTimeout(async () => {
|
|
392
|
+
const k = pendingKind;
|
|
393
|
+
pendingKind = "events";
|
|
394
|
+
debounce = null;
|
|
395
|
+
console.log(`[maravilla] source change detected \u2192 rebuilding ${k}`);
|
|
396
|
+
await runRebuild(k);
|
|
397
|
+
}, 200);
|
|
398
|
+
};
|
|
399
|
+
const watchers = [];
|
|
400
|
+
for (const p of watchPaths) {
|
|
401
|
+
if (!fs.existsSync(p)) continue;
|
|
402
|
+
try {
|
|
403
|
+
watchers.push(fs.watch(p, { persistent: false }, () => triggerRebuild(p)));
|
|
404
|
+
} catch {
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
for (const d of watchDirs) {
|
|
408
|
+
if (!fs.existsSync(d)) continue;
|
|
409
|
+
try {
|
|
410
|
+
watchers.push(fs.watch(d, { persistent: false, recursive: true }, (_evt, fname) => {
|
|
411
|
+
if (fname) triggerRebuild(`${d}/${fname}`);
|
|
412
|
+
}));
|
|
413
|
+
} catch {
|
|
414
|
+
}
|
|
415
|
+
}
|
|
325
416
|
const teardown = await startEventDispatcher({
|
|
326
|
-
projectDir
|
|
417
|
+
projectDir,
|
|
418
|
+
outDir,
|
|
327
419
|
devServerUrl,
|
|
328
420
|
tenant
|
|
329
421
|
});
|
|
330
|
-
server.httpServer?.once("close", () =>
|
|
422
|
+
server.httpServer?.once("close", () => {
|
|
423
|
+
for (const w of watchers) {
|
|
424
|
+
try {
|
|
425
|
+
w.close();
|
|
426
|
+
} catch {
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
if (debounce) clearTimeout(debounce);
|
|
430
|
+
teardown();
|
|
431
|
+
});
|
|
331
432
|
} catch (err) {
|
|
332
433
|
console.error(
|
|
333
434
|
"[maravilla][events] failed to start dispatcher:",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/functions.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["import type { Plugin, ViteDevServer } from 'vite';\nimport { buildFunctions, developmentServer } from '@maravilla-labs/functions';\nimport { join } from 'node:path';\n\nexport interface FunctionsPluginOptions {\n functionsDir?: string;\n functionsPort?: number;\n watch?: boolean;\n}\n\n/**\n * Vite plugin for Maravilla functions development\n * Handles building and serving functions with hot reload\n */\nexport function maravillaFunctions(options: FunctionsPluginOptions = {}): Plugin {\n const {\n functionsDir = 'functions',\n functionsPort = 3003,\n watch = true,\n } = options;\n\n let functionsBundle: any = null;\n let cleanupWatcher: (() => void) | null = null;\n\n return {\n name: 'vite-plugin-maravilla-functions',\n \n async configureServer(server: ViteDevServer) {\n const resolvedFunctionsDir = join(server.config.root, functionsDir);\n \n // Check if functions directory exists\n try {\n await import('node:fs/promises').then(fs => fs.access(resolvedFunctionsDir));\n } catch {\n console.log(`[maravilla-functions] No functions directory found at ${resolvedFunctionsDir}`);\n return;\n }\n\n console.log(`[maravilla-functions] Found functions at ${resolvedFunctionsDir}`);\n\n // Build functions initially\n try {\n functionsBundle = await buildFunctions({\n functionsDir: resolvedFunctionsDir,\n outputDir: join(server.config.root, '.maravilla/functions-dev'),\n production: false,\n minify: false,\n });\n\n console.log(`[maravilla-functions] Built ${functionsBundle.functions} functions:`);\n functionsBundle.routes.forEach((route: any) => {\n console.log(` ${route.path} → ${route.name} (${route.methods.join(', ')})`);\n });\n } catch (error) {\n console.error('[maravilla-functions] Build failed:', error);\n return;\n }\n\n // Set up dev server with watcher\n if (watch) {\n const cleanup = await developmentServer({\n functionsDir: resolvedFunctionsDir,\n outputDir: join(server.config.root, '.maravilla/functions-dev'),\n watch: true,\n onRebuild: async () => {\n // Reload functions bundle\n functionsBundle = await buildFunctions({\n functionsDir: resolvedFunctionsDir,\n outputDir: join(server.config.root, '.maravilla/functions-dev'),\n production: false,\n minify: false,\n });\n \n // Notify Vite of the change\n server.ws.send({\n type: 'custom',\n event: 'maravilla:functions-reload',\n data: { routes: functionsBundle.routes }\n });\n }\n });\n cleanupWatcher = cleanup || null;\n }\n\n // Add middleware to handle function requests\n server.middlewares.use(async (req, res, next) => {\n // Only handle /api routes\n if (!req.url?.startsWith('/api')) {\n return next();\n }\n\n // Check if this matches a function route\n const matchedRoute = functionsBundle?.routes.find((route: any) => {\n const routePath = route.path;\n const requestPath = req.url?.split('?')[0];\n return routePath === requestPath || \n (routePath === '/api' && requestPath === '/api') ||\n (routePath === '/api' && requestPath === '/api/');\n });\n\n if (!matchedRoute) {\n return next();\n }\n\n // Check if method is allowed\n const method = req.method?.toUpperCase() || 'GET';\n if (!matchedRoute.methods.includes(method)) {\n res.statusCode = 405;\n res.setHeader('Allow', matchedRoute.methods.join(', '));\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({\n error: 'Method not allowed',\n allowed: matchedRoute.methods\n }));\n return;\n }\n\n console.log(`[maravilla-functions] ${method} ${req.url} → ${matchedRoute.name}`);\n\n // For now, execute the function in a simple way\n // In production, this would use the V8 isolate runtime\n try {\n const functionModule = await import(\n join(server.config.root, '.maravilla/functions-dev/functions.js')\n );\n\n // Create a mock Request object\n const url = new URL(req.url!, `http://localhost:${server.config.server?.port || 5173}`);\n const headers = new Headers();\n Object.entries(req.headers).forEach(([key, value]) => {\n if (typeof value === 'string') {\n headers.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach(v => headers.append(key, v));\n }\n });\n\n // Get body if present\n let body = null;\n if (req.method && ['POST', 'PUT', 'PATCH'].includes(req.method)) {\n const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB\n body = await new Promise<string>((resolve, reject) => {\n let data = '';\n let size = 0;\n req.on('data', (chunk: Buffer | string) => {\n size += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length;\n if (size > MAX_BODY_SIZE) {\n req.destroy(new Error('Request body too large'));\n reject(new Error('Request body too large'));\n return;\n }\n data += chunk;\n });\n req.on('end', () => resolve(data));\n req.on('error', reject);\n }).catch((err) => {\n res.statusCode = 413;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Payload too large', message: err.message }));\n return null;\n });\n if (body === null) return;\n }\n\n const request = {\n url: url.toString(),\n method: req.method || 'GET',\n headers,\n json: body ? () => Promise.resolve(JSON.parse(body)) : undefined,\n text: () => Promise.resolve(body || ''),\n };\n\n // Call the function handler\n const response = await functionModule.handleFunctionRequest(request);\n\n // Send response\n res.statusCode = response.status || 200;\n Object.entries(response.headers || {}).forEach(([key, value]) => {\n res.setHeader(key, value as string);\n });\n \n if (response instanceof Response) {\n const responseBody = await response.text();\n res.end(responseBody);\n } else {\n res.end(JSON.stringify(response));\n }\n } catch (error: any) {\n console.error('[maravilla-functions] Execution error:', error);\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({\n error: 'Function execution failed',\n message: error.message,\n stack: process.env.NODE_ENV === 'development' ? error.stack : undefined\n }));\n }\n });\n },\n\n async closeBundle() {\n if (cleanupWatcher) {\n cleanupWatcher();\n }\n }\n };\n}","/**\n * Platform client for communicating with the dev server\n */\n\nexport interface PlatformClient {\n kv: KvClient;\n db: DbClient;\n}\n\nexport interface KvClient {\n get(namespace: string, key: string): Promise<any>;\n put(namespace: string, key: string, value: any, ttl?: number): Promise<void>;\n delete(namespace: string, key: string): Promise<void>;\n list(namespace: string, options?: KvListOptions): Promise<KvListResponse>;\n}\n\nexport interface DbClient {\n find(collection: string, filter?: any, options?: DbFindOptions): Promise<any[]>;\n findOne(collection: string, filter: any): Promise<any | null>;\n insertOne(collection: string, document: any): Promise<string>;\n updateOne(collection: string, filter: any, update: any): Promise<{ modified: number }>;\n deleteOne(collection: string, filter: any): Promise<{ deleted: number }>;\n}\n\nexport interface KvListOptions {\n prefix?: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface KvListResponse {\n success: boolean;\n result: Array<{ name: string; expiration?: number; metadata?: any }>;\n result_info: {\n count: number;\n cursor?: string;\n };\n}\n\nexport interface DbFindOptions {\n limit?: number;\n skip?: number;\n sort?: any;\n}\n\nexport function createPlatformClient(baseUrl: string, tenant?: string): PlatformClient {\n const tenantId = tenant || 'dev-tenant';\n const headers = {\n 'Content-Type': 'application/json',\n 'X-Tenant-Id': tenantId,\n };\n\n const fetchWithError = async (url: string, options: RequestInit = {}) => {\n try {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...headers,\n ...options.headers,\n },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n } catch (error) {\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw new Error(\n `Failed to connect to Maravilla dev server at ${baseUrl}. ` +\n 'Please ensure the dev server is running (cargo run -p dev-server)'\n );\n }\n throw error;\n }\n };\n\n const kv: KvClient = {\n async get(namespace: string, key: string) {\n const response = await fetchWithError(`${baseUrl}/api/kv/${namespace}/${key}`);\n if (response.status === 404) return null;\n return response.json();\n },\n\n async put(namespace: string, key: string, value: any, ttl?: number) {\n const requestHeaders: Record<string, string> = { ...headers };\n if (ttl) {\n requestHeaders['X-TTL'] = ttl.toString();\n }\n\n await fetchWithError(`${baseUrl}/api/kv/${namespace}/${key}`, {\n method: 'PUT',\n headers: requestHeaders,\n body: JSON.stringify(value),\n });\n },\n\n async delete(namespace: string, key: string) {\n await fetchWithError(`${baseUrl}/api/kv/${namespace}/${key}`, {\n method: 'DELETE',\n });\n },\n\n async list(namespace: string, options: KvListOptions = {}) {\n const response = await fetchWithError(`${baseUrl}/api/kv/${namespace}`, {\n method: 'POST',\n body: JSON.stringify(options),\n });\n return response.json() as Promise<KvListResponse>;\n },\n };\n\n const db: DbClient = {\n async find(collection: string, filter: any = {}, options: DbFindOptions = {}) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}`, {\n method: 'POST',\n body: JSON.stringify({ filter, options }),\n });\n return response.json() as Promise<any[]>;\n },\n\n async findOne(collection: string, filter: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}/findOne`, {\n method: 'POST',\n body: JSON.stringify(filter),\n });\n if (response.status === 404) return null;\n return response.json();\n },\n\n async insertOne(collection: string, document: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}`, {\n method: 'PUT',\n body: JSON.stringify(document),\n });\n const result = await response.json() as { id: string };\n return result.id;\n },\n\n async updateOne(collection: string, filter: any, update: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}/update`, {\n method: 'POST',\n body: JSON.stringify({ filter, update }),\n });\n return response.json() as Promise<{ modified: number }>;\n },\n\n async deleteOne(collection: string, filter: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}/delete`, {\n method: 'DELETE',\n body: JSON.stringify(filter),\n });\n return response.json() as Promise<{ deleted: number }>;\n },\n };\n\n return { kv, db };\n}","import type { Plugin, ProxyOptions } from 'vite';\n\nexport interface MaravillaPluginOptions {\n /**\n * URL of the Maravilla dev server. Used both for env-var injection and\n * as the proxy target for `/_auth`, `/_assets`, `/_rt`, and `/api`.\n * @default 'http://localhost:3001'\n */\n devServerUrl?: string;\n\n /**\n * Tenant ID for development.\n * @default 'dev-tenant'\n */\n tenant?: string;\n\n /**\n * Extra URL prefixes to forward to the platform dev server, in addition\n * to the defaults (`/_auth`, `/_assets`, `/_rt`, `/api`). Each prefix is\n * proxied with `changeOrigin: true`. Use this for custom platform routes.\n */\n additionalProxies?: string[];\n\n /**\n * Disable the dev-server proxy entirely. Useful if your project routes\n * platform requests through some other mechanism.\n * @default false\n */\n disableProxy?: boolean;\n}\n\n/**\n * URL prefixes the dev-server owns. Anything the browser navigates to\n * directly (auth pages, assets) or upgrades (realtime ws) must be proxied\n * here so it lands on the platform server instead of the framework dev\n * server. Mirrors the prefixes registered in\n * `crates/dev-server/src/router.rs`.\n *\n * `/api/*` is intentionally enumerated by sub-prefix instead of the\n * blanket `/api`. The framework's own `+server.ts` routes (e.g.\n * `src/routes/api/v/[...key]/+server.ts` in demo's photo proxy) live\n * under `/api/...` too — proxying them all to the dev-server would\n * 404 every tenant-defined API route. Each entry below corresponds to\n * a router.rs registration.\n */\nconst DEFAULT_PROXY_PREFIXES = [\n '/_auth',\n '/_assets',\n '/_rt',\n '/api/kv',\n '/api/db',\n '/api/storage',\n '/api/realtime',\n '/api/workflows',\n '/api/push',\n '/api/media',\n '/api/maravilla',\n '/api/platform',\n '/api/tenant',\n '/api/events',\n] as const;\n\nexport function maravilla(options: MaravillaPluginOptions = {}): Plugin {\n const devServerUrl = options.devServerUrl || 'http://localhost:3001';\n const tenant = options.tenant || 'dev-tenant';\n const disableProxy = options.disableProxy ?? false;\n const additionalProxies = options.additionalProxies ?? [];\n\n return {\n name: 'vite-plugin-maravilla',\n\n config() {\n if (disableProxy) return undefined;\n const proxy: Record<string, ProxyOptions> = {};\n for (const prefix of [...DEFAULT_PROXY_PREFIXES, ...additionalProxies]) {\n // `/_rt/*` includes the WebSocket upgrade path `/_rt/ws`; enabling\n // `ws: true` is harmless for the plain HTTP routes under `/_rt`.\n proxy[prefix] = {\n target: devServerUrl,\n changeOrigin: true,\n ws: prefix === '/_rt',\n };\n }\n return { server: { proxy } };\n },\n\n async configureServer(server) {\n // Set environment variables that @maravilla/platform will use\n process.env.MARAVILLA_DEV_SERVER = devServerUrl;\n process.env.MARAVILLA_TENANT = tenant;\n\n console.log(`[maravilla] Platform APIs configured for ${devServerUrl}`);\n console.log('[maravilla] Using tenant:', tenant);\n if (!disableProxy) {\n const proxied = [...DEFAULT_PROXY_PREFIXES, ...additionalProxies].join(', ');\n console.log(`[maravilla] Proxying ${proxied} → ${devServerUrl}`);\n }\n\n // Auto-open a per-request AsyncLocalStorage scope before any\n // framework hook runs. This is the dev-mode equivalent of the\n // runtime's per-request REQUEST_CTX (tokio::task_local! at\n // crates/runtime/src/request_ctx.rs). With this in place,\n // tenant code calls `platform.auth.setCurrentUser(token)` from\n // SvelteKit's `handle` hook (or RR/Remix loaders) without any\n // Maravilla-specific wrapper — same code as prod.\n //\n // Loaded via dynamic import so non-Maravilla apps that pull\n // this plugin in for the proxy alone don't pay the cost until\n // a request actually arrives.\n const { runWithRequest } = await import('@maravilla-labs/platform');\n server.middlewares.use((_req, _res, next) => {\n runWithRequest(() => {\n next();\n }).catch((err) => {\n // Connect's next() doesn't return a Promise we can await;\n // any unhandled error from downstream surfaces here only\n // if runWithRequest's own machinery rejects. Re-throw on\n // the next tick so it shows up in the dev console.\n setImmediate(() => { throw err; });\n });\n });\n\n // Hook into SSR module resolution to ensure platform is available\n server.ssrLoadModule = new Proxy(server.ssrLoadModule, {\n async apply(target, thisArg, args) {\n const result = await Reflect.apply(target, thisArg, args);\n\n // Clear platform cache before each SSR module load to ensure fresh instance\n if ((globalThis as any).__maravilla_platform) {\n delete (globalThis as any).__maravilla_platform;\n }\n\n return result;\n }\n });\n\n // Dev-mode events auto-fire dispatcher. Subscribes to dev-server's\n // REN SSE and invokes any matching handler from `.maravilla/events.js`\n // (synthesized from `transforms:` config + hand-written `events/*.ts`).\n // Mirrors prod's `EventDispatcher` so STORAGE.put on a configured\n // pattern auto-runs transcode/thumbnail/etc. without an explicit\n // call from app code.\n try {\n const { startEventDispatcher } = await import('./event-dispatcher.js');\n const teardown = await startEventDispatcher({\n projectDir: process.cwd(),\n devServerUrl,\n tenant,\n });\n // Vite's `server.httpServer.once('close')` fires on `vite dev` exit.\n server.httpServer?.once('close', () => teardown());\n } catch (err) {\n console.error(\n '[maravilla][events] failed to start dispatcher:',\n (err as Error)?.message ?? err,\n );\n }\n }\n };\n}\n\n// Functions plugin\nexport { maravillaFunctions } from './functions.js';\nexport type { FunctionsPluginOptions } from './functions.js';\n\n// Legacy exports for backwards compatibility\nexport { createPlatformClient } from './client.js';\nexport type { PlatformClient, KvClient, DbClient } from './client.js';\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/functions.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["import type { Plugin, ViteDevServer } from 'vite';\nimport { buildFunctions, developmentServer } from '@maravilla-labs/functions';\nimport { join } from 'node:path';\n\nexport interface FunctionsPluginOptions {\n functionsDir?: string;\n functionsPort?: number;\n watch?: boolean;\n}\n\n/**\n * Vite plugin for Maravilla functions development\n * Handles building and serving functions with hot reload\n */\nexport function maravillaFunctions(options: FunctionsPluginOptions = {}): Plugin {\n const {\n functionsDir = 'functions',\n functionsPort = 3003,\n watch = true,\n } = options;\n\n let functionsBundle: any = null;\n let cleanupWatcher: (() => void) | null = null;\n\n return {\n name: 'vite-plugin-maravilla-functions',\n \n async configureServer(server: ViteDevServer) {\n const resolvedFunctionsDir = join(server.config.root, functionsDir);\n \n // Check if functions directory exists\n try {\n await import('node:fs/promises').then(fs => fs.access(resolvedFunctionsDir));\n } catch {\n console.log(`[maravilla-functions] No functions directory found at ${resolvedFunctionsDir}`);\n return;\n }\n\n console.log(`[maravilla-functions] Found functions at ${resolvedFunctionsDir}`);\n\n // Build functions initially\n try {\n functionsBundle = await buildFunctions({\n functionsDir: resolvedFunctionsDir,\n outputDir: join(server.config.root, '.maravilla/functions-dev'),\n production: false,\n minify: false,\n });\n\n console.log(`[maravilla-functions] Built ${functionsBundle.functions} functions:`);\n functionsBundle.routes.forEach((route: any) => {\n console.log(` ${route.path} → ${route.name} (${route.methods.join(', ')})`);\n });\n } catch (error) {\n console.error('[maravilla-functions] Build failed:', error);\n return;\n }\n\n // Set up dev server with watcher\n if (watch) {\n const cleanup = await developmentServer({\n functionsDir: resolvedFunctionsDir,\n outputDir: join(server.config.root, '.maravilla/functions-dev'),\n watch: true,\n onRebuild: async () => {\n // Reload functions bundle\n functionsBundle = await buildFunctions({\n functionsDir: resolvedFunctionsDir,\n outputDir: join(server.config.root, '.maravilla/functions-dev'),\n production: false,\n minify: false,\n });\n \n // Notify Vite of the change\n server.ws.send({\n type: 'custom',\n event: 'maravilla:functions-reload',\n data: { routes: functionsBundle.routes }\n });\n }\n });\n cleanupWatcher = cleanup || null;\n }\n\n // Add middleware to handle function requests\n server.middlewares.use(async (req, res, next) => {\n // Only handle /api routes\n if (!req.url?.startsWith('/api')) {\n return next();\n }\n\n // Check if this matches a function route\n const matchedRoute = functionsBundle?.routes.find((route: any) => {\n const routePath = route.path;\n const requestPath = req.url?.split('?')[0];\n return routePath === requestPath || \n (routePath === '/api' && requestPath === '/api') ||\n (routePath === '/api' && requestPath === '/api/');\n });\n\n if (!matchedRoute) {\n return next();\n }\n\n // Check if method is allowed\n const method = req.method?.toUpperCase() || 'GET';\n if (!matchedRoute.methods.includes(method)) {\n res.statusCode = 405;\n res.setHeader('Allow', matchedRoute.methods.join(', '));\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({\n error: 'Method not allowed',\n allowed: matchedRoute.methods\n }));\n return;\n }\n\n console.log(`[maravilla-functions] ${method} ${req.url} → ${matchedRoute.name}`);\n\n // For now, execute the function in a simple way\n // In production, this would use the V8 isolate runtime\n try {\n const functionModule = await import(\n join(server.config.root, '.maravilla/functions-dev/functions.js')\n );\n\n // Create a mock Request object\n const url = new URL(req.url!, `http://localhost:${server.config.server?.port || 5173}`);\n const headers = new Headers();\n Object.entries(req.headers).forEach(([key, value]) => {\n if (typeof value === 'string') {\n headers.append(key, value);\n } else if (Array.isArray(value)) {\n value.forEach(v => headers.append(key, v));\n }\n });\n\n // Get body if present\n let body = null;\n if (req.method && ['POST', 'PUT', 'PATCH'].includes(req.method)) {\n const MAX_BODY_SIZE = 10 * 1024 * 1024; // 10MB\n body = await new Promise<string>((resolve, reject) => {\n let data = '';\n let size = 0;\n req.on('data', (chunk: Buffer | string) => {\n size += typeof chunk === 'string' ? Buffer.byteLength(chunk) : chunk.length;\n if (size > MAX_BODY_SIZE) {\n req.destroy(new Error('Request body too large'));\n reject(new Error('Request body too large'));\n return;\n }\n data += chunk;\n });\n req.on('end', () => resolve(data));\n req.on('error', reject);\n }).catch((err) => {\n res.statusCode = 413;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({ error: 'Payload too large', message: err.message }));\n return null;\n });\n if (body === null) return;\n }\n\n const request = {\n url: url.toString(),\n method: req.method || 'GET',\n headers,\n json: body ? () => Promise.resolve(JSON.parse(body)) : undefined,\n text: () => Promise.resolve(body || ''),\n };\n\n // Call the function handler\n const response = await functionModule.handleFunctionRequest(request);\n\n // Send response\n res.statusCode = response.status || 200;\n Object.entries(response.headers || {}).forEach(([key, value]) => {\n res.setHeader(key, value as string);\n });\n \n if (response instanceof Response) {\n const responseBody = await response.text();\n res.end(responseBody);\n } else {\n res.end(JSON.stringify(response));\n }\n } catch (error: any) {\n console.error('[maravilla-functions] Execution error:', error);\n res.statusCode = 500;\n res.setHeader('Content-Type', 'application/json');\n res.end(JSON.stringify({\n error: 'Function execution failed',\n message: error.message,\n stack: process.env.NODE_ENV === 'development' ? error.stack : undefined\n }));\n }\n });\n },\n\n async closeBundle() {\n if (cleanupWatcher) {\n cleanupWatcher();\n }\n }\n };\n}","/**\n * Platform client for communicating with the dev server\n */\n\nexport interface PlatformClient {\n kv: KvClient;\n db: DbClient;\n}\n\nexport interface KvClient {\n get(namespace: string, key: string): Promise<any>;\n put(namespace: string, key: string, value: any, ttl?: number): Promise<void>;\n delete(namespace: string, key: string): Promise<void>;\n list(namespace: string, options?: KvListOptions): Promise<KvListResponse>;\n}\n\nexport interface DbClient {\n find(collection: string, filter?: any, options?: DbFindOptions): Promise<any[]>;\n findOne(collection: string, filter: any): Promise<any | null>;\n insertOne(collection: string, document: any): Promise<string>;\n updateOne(collection: string, filter: any, update: any): Promise<{ modified: number }>;\n deleteOne(collection: string, filter: any): Promise<{ deleted: number }>;\n}\n\nexport interface KvListOptions {\n prefix?: string;\n limit?: number;\n cursor?: string;\n}\n\nexport interface KvListResponse {\n success: boolean;\n result: Array<{ name: string; expiration?: number; metadata?: any }>;\n result_info: {\n count: number;\n cursor?: string;\n };\n}\n\nexport interface DbFindOptions {\n limit?: number;\n skip?: number;\n sort?: any;\n}\n\nexport function createPlatformClient(baseUrl: string, tenant?: string): PlatformClient {\n const tenantId = tenant || 'dev-tenant';\n const headers = {\n 'Content-Type': 'application/json',\n 'X-Tenant-Id': tenantId,\n };\n\n const fetchWithError = async (url: string, options: RequestInit = {}) => {\n try {\n const response = await fetch(url, {\n ...options,\n headers: {\n ...headers,\n ...options.headers,\n },\n });\n\n if (!response.ok && response.status !== 404) {\n const error = await response.text();\n throw new Error(`Platform API error: ${response.status} - ${error}`);\n }\n\n return response;\n } catch (error) {\n if (error instanceof TypeError && error.message.includes('fetch')) {\n throw new Error(\n `Failed to connect to Maravilla dev server at ${baseUrl}. ` +\n 'Please ensure the dev server is running (cargo run -p dev-server)'\n );\n }\n throw error;\n }\n };\n\n const kv: KvClient = {\n async get(namespace: string, key: string) {\n const response = await fetchWithError(`${baseUrl}/api/kv/${namespace}/${key}`);\n if (response.status === 404) return null;\n return response.json();\n },\n\n async put(namespace: string, key: string, value: any, ttl?: number) {\n const requestHeaders: Record<string, string> = { ...headers };\n if (ttl) {\n requestHeaders['X-TTL'] = ttl.toString();\n }\n\n await fetchWithError(`${baseUrl}/api/kv/${namespace}/${key}`, {\n method: 'PUT',\n headers: requestHeaders,\n body: JSON.stringify(value),\n });\n },\n\n async delete(namespace: string, key: string) {\n await fetchWithError(`${baseUrl}/api/kv/${namespace}/${key}`, {\n method: 'DELETE',\n });\n },\n\n async list(namespace: string, options: KvListOptions = {}) {\n const response = await fetchWithError(`${baseUrl}/api/kv/${namespace}`, {\n method: 'POST',\n body: JSON.stringify(options),\n });\n return response.json() as Promise<KvListResponse>;\n },\n };\n\n const db: DbClient = {\n async find(collection: string, filter: any = {}, options: DbFindOptions = {}) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}`, {\n method: 'POST',\n body: JSON.stringify({ filter, options }),\n });\n return response.json() as Promise<any[]>;\n },\n\n async findOne(collection: string, filter: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}/findOne`, {\n method: 'POST',\n body: JSON.stringify(filter),\n });\n if (response.status === 404) return null;\n return response.json();\n },\n\n async insertOne(collection: string, document: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}`, {\n method: 'PUT',\n body: JSON.stringify(document),\n });\n const result = await response.json() as { id: string };\n return result.id;\n },\n\n async updateOne(collection: string, filter: any, update: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}/update`, {\n method: 'POST',\n body: JSON.stringify({ filter, update }),\n });\n return response.json() as Promise<{ modified: number }>;\n },\n\n async deleteOne(collection: string, filter: any) {\n const response = await fetchWithError(`${baseUrl}/api/db/${collection}/delete`, {\n method: 'DELETE',\n body: JSON.stringify(filter),\n });\n return response.json() as Promise<{ deleted: number }>;\n },\n };\n\n return { kv, db };\n}","import type { Plugin, ProxyOptions } from 'vite';\n\nexport interface MaravillaPluginOptions {\n /**\n * URL of the Maravilla dev server. Used both for env-var injection and\n * as the proxy target for `/_auth`, `/_assets`, `/_rt`, and `/api`.\n * @default 'http://localhost:3001'\n */\n devServerUrl?: string;\n\n /**\n * Tenant ID for development. Must match the dev-server's tenant id —\n * `crates/dev-server/src/types.rs::extract_tenant_id` defaults to\n * `dev-tenant-001` when no `X-Tenant-Id` header is supplied, and\n * storage/db/etc. publish REN events keyed by the dev-server's\n * `TenantContext.tenant_id` (also `dev-tenant-001`). If this option\n * doesn't match, the dispatcher's SSE subscription lands on a\n * different broadcast channel and no events ever fire.\n * @default 'dev-tenant-001'\n */\n tenant?: string;\n\n /**\n * Extra URL prefixes to forward to the platform dev server, in addition\n * to the defaults (`/_auth`, `/_assets`, `/_rt`, `/api`). Each prefix is\n * proxied with `changeOrigin: true`. Use this for custom platform routes.\n */\n additionalProxies?: string[];\n\n /**\n * Disable the dev-server proxy entirely. Useful if your project routes\n * platform requests through some other mechanism.\n * @default false\n */\n disableProxy?: boolean;\n\n /**\n * Output directory for events.js / events.json. Matches the framework\n * adapter's `out` option so dev re-uses the same artefacts. Defaults\n * to `'build'` (SvelteKit + React Router 7 convention).\n *\n * Override if your adapter writes elsewhere (e.g. `'.maravilla'`).\n */\n outDir?: string;\n}\n\n/**\n * URL prefixes the dev-server owns. Anything the browser navigates to\n * directly (auth pages, assets) or upgrades (realtime ws) must be proxied\n * here so it lands on the platform server instead of the framework dev\n * server. Mirrors the prefixes registered in\n * `crates/dev-server/src/router.rs`.\n *\n * `/api/*` is intentionally enumerated by sub-prefix instead of the\n * blanket `/api`. The framework's own `+server.ts` routes (e.g.\n * `src/routes/api/v/[...key]/+server.ts` in demo's photo proxy) live\n * under `/api/...` too — proxying them all to the dev-server would\n * 404 every tenant-defined API route. Each entry below corresponds to\n * a router.rs registration.\n */\nconst DEFAULT_PROXY_PREFIXES = [\n '/_auth',\n '/_assets',\n '/_rt',\n '/api/kv',\n '/api/db',\n '/api/storage',\n '/api/realtime',\n '/api/workflows',\n '/api/push',\n '/api/media',\n '/api/maravilla',\n '/api/platform',\n '/api/tenant',\n '/api/events',\n] as const;\n\nexport function maravilla(options: MaravillaPluginOptions = {}): Plugin {\n const devServerUrl = options.devServerUrl || 'http://localhost:3001';\n const tenant = options.tenant || 'dev-tenant-001';\n const disableProxy = options.disableProxy ?? false;\n const additionalProxies = options.additionalProxies ?? [];\n const outDir = options.outDir || 'build';\n\n return {\n name: 'vite-plugin-maravilla',\n\n config() {\n if (disableProxy) return undefined;\n const proxy: Record<string, ProxyOptions> = {};\n for (const prefix of [...DEFAULT_PROXY_PREFIXES, ...additionalProxies]) {\n // `/_rt/*` includes the WebSocket upgrade path `/_rt/ws`; enabling\n // `ws: true` is harmless for the plain HTTP routes under `/_rt`.\n proxy[prefix] = {\n target: devServerUrl,\n changeOrigin: true,\n ws: prefix === '/_rt',\n };\n }\n return { server: { proxy } };\n },\n\n async configureServer(server) {\n // Set environment variables that @maravilla/platform will use\n process.env.MARAVILLA_DEV_SERVER = devServerUrl;\n process.env.MARAVILLA_TENANT = tenant;\n\n console.log(`[maravilla] Platform APIs configured for ${devServerUrl}`);\n console.log('[maravilla] Using tenant:', tenant);\n if (!disableProxy) {\n const proxied = [...DEFAULT_PROXY_PREFIXES, ...additionalProxies].join(', ');\n console.log(`[maravilla] Proxying ${proxied} → ${devServerUrl}`);\n }\n\n // Auto-open a per-request AsyncLocalStorage scope before any\n // framework hook runs. This is the dev-mode equivalent of the\n // runtime's per-request REQUEST_CTX (tokio::task_local! at\n // crates/runtime/src/request_ctx.rs). With this in place,\n // tenant code calls `platform.auth.setCurrentUser(token)` from\n // SvelteKit's `handle` hook (or RR/Remix loaders) without any\n // Maravilla-specific wrapper — same code as prod.\n //\n // Loaded via dynamic import so non-Maravilla apps that pull\n // this plugin in for the proxy alone don't pay the cost until\n // a request actually arrives.\n const { runWithRequest } = await import('@maravilla-labs/platform');\n server.middlewares.use((_req, _res, next) => {\n runWithRequest(() => {\n next();\n }).catch((err) => {\n // Connect's next() doesn't return a Promise we can await;\n // any unhandled error from downstream surfaces here only\n // if runWithRequest's own machinery rejects. Re-throw on\n // the next tick so it shows up in the dev console.\n setImmediate(() => { throw err; });\n });\n });\n\n // Hook into SSR module resolution to ensure platform is available\n server.ssrLoadModule = new Proxy(server.ssrLoadModule, {\n async apply(target, thisArg, args) {\n const result = await Reflect.apply(target, thisArg, args);\n\n // Clear platform cache before each SSR module load to ensure fresh instance\n if ((globalThis as any).__maravilla_platform) {\n delete (globalThis as any).__maravilla_platform;\n }\n\n return result;\n }\n });\n\n // Dev-mode events auto-fire: rebuild the events + workflows\n // bundle on startup (and on file changes), then run the\n // dispatcher that subscribes to dev-server REN SSE and invokes\n // matching handlers. Mirrors prod's `EventDispatcher` so\n // STORAGE.put / KV.put / etc. on a configured pattern auto-fires\n // without explicit calls from app code.\n try {\n const { startEventDispatcher } = await import('./event-dispatcher.js');\n const { rebuildEvents, rebuildWorkflows } = await import('./event-builder.js');\n const { loadMaravillaConfig } = await import('@maravilla-labs/adapter-core');\n const projectDir = process.cwd();\n\n // Lazy-load config once. Source-of-truth for the transforms block.\n let configCache: { transforms?: unknown } = {};\n try {\n const loaded = await loadMaravillaConfig(projectDir);\n configCache = (loaded?.config ?? {}) as { transforms?: unknown };\n } catch {\n // Missing/invalid config is fine — tenant just won't have\n // synthesized transforms handlers.\n }\n\n const runRebuild = async (kind: 'events' | 'workflows' | 'both') => {\n try {\n if (kind === 'events' || kind === 'both') {\n const n = await rebuildEvents({\n projectDir,\n outDir,\n transforms: configCache.transforms,\n log: (m) => console.warn(m),\n });\n if (n !== null) console.log(`[maravilla][events] built ${n} handler${n === 1 ? '' : 's'} → ${outDir}/events.json`);\n }\n if (kind === 'workflows' || kind === 'both') {\n const n = await rebuildWorkflows({\n projectDir,\n outDir,\n log: (m) => console.warn(m),\n });\n if (n !== null) console.log(`[maravilla][workflows] built ${n} workflow${n === 1 ? '' : 's'} → ${outDir}/workflows.json`);\n }\n // Tell dev-server to re-read its events manifest.\n try {\n await fetch(`${devServerUrl}/api/events/_reload`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', 'X-Tenant-Id': tenant },\n });\n } catch {\n // dev-server may not be up yet on first run — fine, it\n // loads the manifest at boot anyway.\n }\n } catch (err) {\n console.error(\n `[maravilla][${kind}] rebuild failed:`,\n (err as Error)?.message ?? err,\n );\n }\n };\n\n // Initial build before the dispatcher attaches its file watcher.\n await runRebuild('both');\n\n // Watch tenant source files. Debounced so a single editor save\n // doesn't trigger multiple rebuilds while esbuild is mid-write.\n const watchPaths = [\n 'events.ts', 'events.js', 'events.mjs',\n 'workflows.ts', 'workflows.js', 'workflows.mjs',\n 'maravilla.config.ts', 'maravilla.config.js', 'maravilla.config.mjs',\n ].map((p) => `${projectDir}/${p}`);\n // Add directory paths\n const watchDirs = [`${projectDir}/events`, `${projectDir}/workflows`];\n\n const fs = await import('node:fs');\n let debounce: NodeJS.Timeout | null = null;\n let pendingKind: 'events' | 'workflows' | 'both' = 'events';\n const triggerRebuild = (path: string) => {\n const isWorkflow = path.includes('/workflows');\n const isConfig = path.includes('maravilla.config');\n // Config changes affect both since transforms compile into events\n // and (in future) other declarative blocks may affect workflows.\n const newKind: 'events' | 'workflows' | 'both' =\n isConfig ? 'both' : isWorkflow ? 'workflows' : 'events';\n if (pendingKind !== 'both' && pendingKind !== newKind) pendingKind = 'both';\n else if (debounce === null) pendingKind = newKind;\n if (debounce) clearTimeout(debounce);\n debounce = setTimeout(async () => {\n const k = pendingKind;\n pendingKind = 'events';\n debounce = null;\n console.log(`[maravilla] source change detected → rebuilding ${k}`);\n await runRebuild(k);\n }, 200);\n };\n const watchers: Array<{ close: () => void }> = [];\n for (const p of watchPaths) {\n if (!fs.existsSync(p)) continue;\n try {\n watchers.push(fs.watch(p, { persistent: false }, () => triggerRebuild(p)));\n } catch { /* skip files that can't be watched */ }\n }\n for (const d of watchDirs) {\n if (!fs.existsSync(d)) continue;\n try {\n watchers.push(fs.watch(d, { persistent: false, recursive: true }, (_evt, fname) => {\n if (fname) triggerRebuild(`${d}/${fname}`);\n }));\n } catch { /* skip — recursive watch unsupported on some platforms */ }\n }\n\n const teardown = await startEventDispatcher({\n projectDir,\n outDir,\n devServerUrl,\n tenant,\n });\n // Vite's `server.httpServer.once('close')` fires on `vite dev` exit.\n server.httpServer?.once('close', () => {\n for (const w of watchers) {\n try { w.close(); } catch { /* ignore */ }\n }\n if (debounce) clearTimeout(debounce);\n teardown();\n });\n } catch (err) {\n console.error(\n '[maravilla][events] failed to start dispatcher:',\n (err as Error)?.message ?? err,\n );\n }\n }\n };\n}\n\n// Functions plugin\nexport { maravillaFunctions } from './functions.js';\nexport type { FunctionsPluginOptions } from './functions.js';\n\n// Legacy exports for backwards compatibility\nexport { createPlatformClient } from './client.js';\nexport type { PlatformClient, KvClient, DbClient } from './client.js';\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@maravilla-labs/vite-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.12",
|
|
4
4
|
"description": "Vite plugin for Maravilla Runtime development",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
"typecheck": "tsc --noEmit"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@maravilla-labs/
|
|
23
|
+
"@maravilla-labs/adapter-core": "^0.3.11",
|
|
24
|
+
"@maravilla-labs/functions": "^0.3.11",
|
|
24
25
|
"node-fetch": "^3.3.2"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
|
-
"@maravilla-labs/platform": "^0.3.
|
|
28
|
+
"@maravilla-labs/platform": "^0.3.11",
|
|
28
29
|
"@types/node": "^20.10.5",
|
|
29
30
|
"tsup": "^8.0.1",
|
|
30
31
|
"typescript": "^5.3.3",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/event-dispatcher.ts","../src/event-matcher.ts"],"sourcesContent":["/**\n * Dev-mode event dispatcher. The runtime's\n * `crates/platform/src/events/dispatcher.rs` does this server-side in\n * Rust + Deno isolate. In dev there's no isolate, so we run the same\n * loop here in tenant Node.js (via the Vite dev server's process):\n *\n * 1. Load the events bundle the build pipeline produced\n * (`.maravilla/events.js` — sets `globalThis.__maravilla_event_registry`).\n * 2. Subscribe to the dev-server's REN SSE stream.\n * 3. For each `RenEvent`, run the matcher against every registered\n * trigger. Invoke matched handlers with `{ event, ctx: { platform } }`.\n *\n * Result: a tenant calling `platform.env.STORAGE.put('invites/.../videos/x.webm', ...)`\n * sees the synthesized transcode + thumbnail handlers fire in dev exactly\n * like they do in prod — same semantics, same timing model (after the put\n * returns, async via SSE), same content-addressed output keys.\n */\n\nimport { existsSync, statSync } from 'node:fs';\nimport { resolve, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport { matches, type RenEvent, type Trigger } from './event-matcher.js';\n\ninterface RegistryEntry {\n trigger: Trigger;\n handler: (event: unknown, ctx: unknown) => unknown | Promise<unknown>;\n}\n\ntype Registry = Record<string, RegistryEntry>;\n\ninterface DispatcherOptions {\n /** Project root (where `.maravilla/` lives). */\n projectDir: string;\n /** URL of the Maravilla dev-server. */\n devServerUrl: string;\n /** Tenant id sent on outgoing requests + used to key the SSE stream. */\n tenant: string;\n}\n\n/**\n * Stand up a dev-mode event dispatcher. Returns a teardown function.\n * Safe to call when no events bundle exists — emits one warning and\n * no-ops until a build materialises `.maravilla/events.js`.\n */\nexport async function startEventDispatcher(opts: DispatcherOptions): Promise<() => void> {\n const bundlePath = resolve(opts.projectDir, '.maravilla/events.js');\n\n if (!existsSync(bundlePath)) {\n // Most demos / tenants don't have a build until the first\n // production-style build runs. Keep the dev server up; document\n // the expectation in a single-line warning.\n console.warn(\n '[maravilla][events] no .maravilla/events.js found — event auto-fire disabled. ' +\n 'Run `pnpm build` (or `npm run build`) once to materialise the events bundle.',\n );\n return () => {};\n }\n\n let registry: Registry = {};\n let bundleMtime = 0;\n\n async function loadBundle() {\n const stat = statSync(bundlePath);\n if (stat.mtimeMs === bundleMtime) return;\n try {\n // The bundle is IIFE — when imported it executes top-level and\n // sets `globalThis.__maravilla_event_registry` as a side effect.\n // Cache-bust via querystring so HMR rebuilds get picked up.\n await import(pathToFileURL(bundlePath).href + `?t=${stat.mtimeMs}`);\n const reg = (globalThis as { __maravilla_event_registry?: Registry })\n .__maravilla_event_registry;\n if (reg && typeof reg === 'object') {\n registry = reg;\n bundleMtime = stat.mtimeMs;\n const handlerCount = Object.keys(registry).length;\n console.log(\n `[maravilla][events] loaded ${handlerCount} handler${handlerCount === 1 ? '' : 's'} from .maravilla/events.js`,\n );\n } else {\n console.warn('[maravilla][events] events bundle did not expose __maravilla_event_registry');\n }\n } catch (err) {\n console.error(\n '[maravilla][events] failed to load events bundle:',\n (err as Error)?.message ?? err,\n );\n }\n }\n\n await loadBundle();\n\n // Watch the bundle for HMR-style refresh.\n const fsWatcher = (await import('node:fs')).watch(\n join(opts.projectDir, '.maravilla'),\n { persistent: false },\n (event, filename) => {\n if (filename === 'events.js') {\n // Slight debounce: filesystem may emit multiple `change` events\n // while esbuild rewrites the file.\n setTimeout(() => { void loadBundle(); }, 100);\n }\n },\n );\n\n // SSE consumer. Use the `eventsource` polyfill in Node — `EventSource`\n // is only available in browsers natively.\n let aborter: AbortController | null = new AbortController();\n let reconnectTimer: NodeJS.Timeout | null = null;\n let stopped = false;\n\n async function connect(): Promise<void> {\n if (stopped) return;\n aborter = new AbortController();\n const url = `${opts.devServerUrl}/api/maravilla/ren`;\n try {\n const res = await fetch(url, {\n headers: {\n Accept: 'text/event-stream',\n 'X-Tenant-Id': opts.tenant,\n },\n signal: aborter.signal,\n });\n if (!res.ok || !res.body) {\n throw new Error(`SSE connect ${res.status}`);\n }\n const reader = res.body.getReader();\n const decoder = new TextDecoder();\n let buf = '';\n // Read SSE frames. `data: <json>` lines are events; blank line ends\n // the frame.\n while (!stopped) {\n const { done, value } = await reader.read();\n if (done) break;\n buf += decoder.decode(value, { stream: true });\n let idx: number;\n while ((idx = buf.indexOf('\\n\\n')) !== -1) {\n const frame = buf.slice(0, idx);\n buf = buf.slice(idx + 2);\n handleFrame(frame);\n }\n }\n } catch (err) {\n if (stopped) return;\n const msg = (err as Error)?.message ?? String(err);\n if (!msg.includes('aborted')) {\n // Reconnect on transport errors. Constant 2s backoff is fine\n // for a dev tool — operator can ctrl-c if the loop is wrong.\n console.warn(`[maravilla][events] REN SSE disconnected: ${msg}; reconnecting in 2s`);\n reconnectTimer = setTimeout(() => { void connect(); }, 2000);\n }\n }\n }\n\n function handleFrame(frame: string): void {\n for (const line of frame.split('\\n')) {\n if (!line.startsWith('data:')) continue;\n const payload = line.slice(5).trim();\n if (!payload) continue;\n let event: RenEvent;\n try {\n event = JSON.parse(payload) as RenEvent;\n } catch {\n continue;\n }\n void dispatch(event);\n }\n }\n\n async function dispatch(event: RenEvent): Promise<void> {\n const platform = (await import('@maravilla-labs/platform')).getPlatform();\n // Stamp platform onto globalThis so the bundle's buildCtx() picks it\n // up — same shape the runtime uses inside the Deno isolate.\n (globalThis as { platform?: unknown }).platform = platform;\n\n for (const [id, entry] of Object.entries(registry)) {\n if (!matches(entry.trigger, event)) continue;\n try {\n await entry.handler(eventPayloadFor(event), {\n platform,\n handlerId: id,\n tenant: opts.tenant,\n traceId: globalThis.crypto?.randomUUID?.() ?? String(Date.now()),\n });\n } catch (err) {\n console.error(\n `[maravilla][events] handler ${id} failed:`,\n (err as Error)?.message ?? err,\n );\n }\n }\n }\n\n /**\n * Mirror `crates/platform/src/events/dispatcher.rs:163-247`'s payload\n * transform — handlers receive `{ op, key, data, ts }` for storage,\n * `{ namespace, key, value, ts, op }` for kv, etc., not the raw\n * RenEvent.\n */\n function eventPayloadFor(event: RenEvent): unknown {\n switch (event.r) {\n case 'storage': {\n const op = event.t.startsWith('storage.') ? event.t.slice('storage.'.length) : event.t;\n return { op, key: event.k, data: event.data, ts: event.ts };\n }\n case 'kv': {\n const op = event.t.startsWith('kv.') ? event.t.slice('kv.'.length) : event.t;\n return { op, namespace: event.ns, key: event.k, data: event.data, ts: event.ts };\n }\n case 'db': {\n const op = event.t.startsWith('db.document.') ? event.t.slice('db.document.'.length) : event.t;\n return { op, collection: event.ns, document: event.data, ts: event.ts };\n }\n case 'auth': {\n return { type: event.t, user_id: event.k, data: event.data, ts: event.ts };\n }\n case 'realtime':\n case 'presence': {\n return { type: event.t, channel: event.ch, data: event.data, ts: event.ts };\n }\n default:\n return event;\n }\n }\n\n void connect();\n\n return () => {\n stopped = true;\n aborter?.abort();\n if (reconnectTimer) clearTimeout(reconnectTimer);\n fsWatcher.close();\n };\n}\n","/**\n * TS port of `crates/platform/src/events/matcher.rs`. Used by the\n * dev-mode event dispatcher in this package to match incoming\n * `RenEvent`s against the trigger descriptors emitted in\n * `.maravilla/events.json`.\n *\n * Keep in lockstep with the Rust matcher — the golden-vector tests\n * across both implementations should produce the same yes/no for the\n * same (trigger, event) pair.\n */\n\nexport interface RenEvent {\n /** Event type, e.g. `\"storage.put\"`, `\"kv.put\"`, `\"presence.join\"` */\n t: string;\n /** Resource domain, e.g. `\"storage\"`, `\"kv\"`, `\"realtime\"`, `\"auth\"` */\n r: string;\n /** Resource key (e.g. storage path, kv key) */\n k?: string | null;\n /** Namespace / bucket / collection */\n ns?: string | null;\n /** Realtime channel name (for pub/sub) */\n ch?: string | null;\n /** Deployment id tagging the event origin */\n dep?: string | null;\n data?: unknown;\n ts?: number;\n}\n\n/** Trigger shapes mirror `crates/platform/src/events/types.rs` (camelCase). */\nexport type Trigger =\n | { kind: 'kv'; namespace?: string; keyPattern?: string; op?: 'put' | 'delete' | 'expired' }\n | { kind: 'db'; collection: string; op?: 'insert' | 'update' | 'delete' }\n | { kind: 'auth'; op?: string }\n | { kind: 'channel'; channel: string; type?: string }\n | { kind: 'storage'; keyPattern?: string; op?: 'put' | 'delete' }\n | { kind: 'deploy'; phase: 'ready' | 'draining' | 'stopped' }\n | { kind: 'ren'; match: { r?: string; t?: string; ns?: string } }\n | { kind: 'schedule'; cron: string } // never matches RenEvents\n | { kind: 'queue'; name: string }; // never matches RenEvents\n\n/**\n * Returns `true` if the event matches the trigger. Mirrors the Rust\n * `matches()` in `events/matcher.rs:12-34`.\n */\nexport function matches(trigger: Trigger, event: RenEvent): boolean {\n switch (trigger.kind) {\n case 'kv': return matchKv(trigger, event);\n case 'db': return matchDb(trigger, event);\n case 'auth': return matchAuth(trigger, event);\n case 'channel': return matchChannel(trigger, event);\n case 'storage': return matchStorage(trigger, event);\n case 'deploy': return matchDeploy(trigger, event);\n case 'ren': return matchRen(trigger, event);\n case 'schedule': return false;\n case 'queue': return false;\n default: return false;\n }\n}\n\nfunction matchKv(t: Extract<Trigger, { kind: 'kv' }>, e: RenEvent): boolean {\n if (e.r !== 'kv') return false;\n if (t.op !== undefined) {\n const eventOp = e.t === 'kv.put' ? 'put' : e.t === 'kv.delete' ? 'delete' : e.t === 'kv.expired' ? 'expired' : null;\n if (eventOp !== t.op) return false;\n }\n if (t.namespace !== undefined && e.ns !== t.namespace) return false;\n if (t.keyPattern !== undefined) {\n if (!e.k) return false;\n if (!globMatch(t.keyPattern, e.k)) return false;\n }\n return true;\n}\n\nfunction matchDb(t: Extract<Trigger, { kind: 'db' }>, e: RenEvent): boolean {\n if (e.r !== 'db') return false;\n if (e.ns !== t.collection) return false;\n if (t.op !== undefined) {\n const expected = t.op === 'insert' ? 'db.document.created'\n : t.op === 'update' ? 'db.document.updated'\n : t.op === 'delete' ? 'db.document.deleted' : null;\n if (e.t !== expected) return false;\n }\n return true;\n}\n\nfunction matchAuth(t: Extract<Trigger, { kind: 'auth' }>, e: RenEvent): boolean {\n if (e.r !== 'auth') return false;\n if (t.op !== undefined) {\n const expected = ({\n registered: 'auth.user.registered',\n loggedIn: 'auth.user.logged_in',\n loggedOut: 'auth.user.logged_out',\n loggedOutAll: 'auth.user.logged_out_all',\n deleted: 'auth.user.deleted',\n updated: 'auth.user.updated',\n } as Record<string, string>)[t.op];\n return e.t === expected;\n }\n return e.t.startsWith('auth.user.');\n}\n\nfunction matchChannel(t: Extract<Trigger, { kind: 'channel' }>, e: RenEvent): boolean {\n if (e.r !== 'realtime' && e.r !== 'presence') return false;\n if (!e.ch) return false;\n if (!globMatch(t.channel, e.ch)) return false;\n if (t.type !== undefined && e.t !== t.type) return false;\n return true;\n}\n\nfunction matchStorage(t: Extract<Trigger, { kind: 'storage' }>, e: RenEvent): boolean {\n if (e.r !== 'storage') return false;\n if (t.op !== undefined) {\n const eventOp = e.t === 'storage.put' ? 'put' : e.t === 'storage.delete' ? 'delete' : null;\n if (eventOp !== t.op) return false;\n }\n if (t.keyPattern !== undefined) {\n if (!e.k) return false;\n if (!globMatch(t.keyPattern, e.k)) return false;\n }\n return true;\n}\n\nfunction matchDeploy(t: Extract<Trigger, { kind: 'deploy' }>, e: RenEvent): boolean {\n if (e.r !== 'deploy') return false;\n const expected = `deploy.${t.phase}`;\n return e.t === expected;\n}\n\nfunction matchRen(t: Extract<Trigger, { kind: 'ren' }>, e: RenEvent): boolean {\n const m = t.match;\n if (m.r !== undefined && e.r !== m.r) return false;\n if (m.t !== undefined && e.t !== m.t) return false;\n if (m.ns !== undefined && e.ns !== m.ns) return false;\n return true;\n}\n\n/**\n * Minimal glob matcher supporting `*` (zero or more chars) and literal\n * segments. Mirrors `events/matcher.rs::glob_match`. Not full POSIX —\n * intentionally tiny. Handles `*`, `prefix:*`, `*:suffix`, `a:*:b`.\n */\nexport function globMatch(pattern: string, input: string): boolean {\n if (pattern === '*') return true;\n if (!pattern.includes('*')) return pattern === input;\n\n const segments = pattern.split('*');\n let cursor = 0;\n\n // First segment must match as prefix (unless empty, i.e. leading `*`).\n const first = segments[0];\n if (first && first.length > 0) {\n if (!input.startsWith(first)) return false;\n cursor = first.length;\n }\n\n // Middle segments: find each in order.\n for (let i = 1; i < segments.length - 1; i += 1) {\n const seg = segments[i];\n if (!seg || seg.length === 0) continue;\n const idx = input.indexOf(seg, cursor);\n if (idx === -1) return false;\n cursor = idx + seg.length;\n }\n\n // Last segment must match as suffix (unless empty, i.e. trailing `*`).\n if (segments.length > 1) {\n const last = segments[segments.length - 1];\n if (last && last.length > 0) {\n const tail = input.slice(cursor);\n return tail.endsWith(last) && tail.length >= last.length;\n }\n }\n return true;\n}\n"],"mappings":"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","names":[]}
|