@cequrebackends/plugin-vite 1.0.0 → 1.0.1

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.
@@ -0,0 +1,26 @@
1
+ import type { Plugin } from "vite";
2
+ export interface CequreViteOptions {
3
+ /** URL prefix for API routes. Defaults to `"/api"`. */
4
+ apiPrefix?: string;
5
+ /** Custom entry point for the Cequre server. Defaults to auto-detecting `cequre/server/app.ts`, `cequre/server/index.ts`, or legacy `src/server/index.ts`. */
6
+ entry?: string;
7
+ /** Automatically run backend production build during Vite build closeBundle hook. Defaults to `false`. */
8
+ autoBuild?: boolean;
9
+ }
10
+ export declare function resolveApiPrefix(customPrefix?: string): string;
11
+ export declare function resolveEntryPoint(customEntry?: string): string;
12
+ export declare function getExecutableCommand(): {
13
+ command: string;
14
+ argsPrefix: string[];
15
+ };
16
+ export declare function cequreVite(options?: CequreViteOptions): Plugin;
17
+ export declare namespace cequreVite {
18
+ var build: (options?: CequreViteBuildOptions) => Promise<void>;
19
+ }
20
+ export interface CequreViteBuildOptions {
21
+ entry?: string;
22
+ outDir?: string;
23
+ outFile?: string;
24
+ minify?: boolean;
25
+ }
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAiB,MAAM,MAAM,CAAC;AAalD,MAAM,WAAW,iBAAiB;IAChC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8JAA8J;IAC9J,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0GAA0G;IAC1G,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAOD,wBAAgB,gBAAgB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAsD9D;AAED,wBAAgB,iBAAiB,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,MAAM,CAuB9D;AAED,wBAAgB,oBAAoB,IAAI;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,EAAE,CAAA;CAAE,CAMhF;AAsFD,wBAAgB,UAAU,CAAC,OAAO,GAAE,iBAAsB,GAAG,MAAM,CAkMlE;yBAlMe,UAAU;0BA2MkB,sBAAsB;;AAPlE,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB"}
package/dist/index.js ADDED
@@ -0,0 +1,393 @@
1
+ // @bun
2
+ var __require = import.meta.require;
3
+
4
+ // src/index.ts
5
+ import { spawn, execSync } from "child_process";
6
+ import path from "path";
7
+ import fs from "fs";
8
+ import os from "os";
9
+ import { createHash } from "crypto";
10
+ var logger = {
11
+ info: (msg) => console.log(msg),
12
+ error: (msg) => console.error(msg),
13
+ warn: (msg) => console.warn(msg)
14
+ };
15
+ var g = globalThis;
16
+ var globalWsPort = g.__CEQURE_WS_PORT || 0;
17
+ var globalCequreProcess = g.__CEQURE_PROCESS || null;
18
+ var globalCleanupRegistered = g.__CEQURE_CLEANUP || false;
19
+ function resolveApiPrefix(customPrefix) {
20
+ if (customPrefix) {
21
+ return customPrefix.startsWith("/") ? customPrefix : "/" + customPrefix;
22
+ }
23
+ const schemaPath = path.resolve(process.cwd(), "node_modules", ".cequre", "schema.json");
24
+ if (fs.existsSync(schemaPath)) {
25
+ try {
26
+ const schema = JSON.parse(fs.readFileSync(schemaPath, "utf-8"));
27
+ if (schema.core?.api?.prefix) {
28
+ const prefix = schema.core.api.prefix;
29
+ return prefix.startsWith("/") ? prefix : "/" + prefix;
30
+ }
31
+ } catch {}
32
+ }
33
+ const cequreSchemaDir = path.resolve(process.cwd(), "cequre", "schema");
34
+ if (fs.existsSync(cequreSchemaDir)) {
35
+ try {
36
+ const files = fs.readdirSync(cequreSchemaDir);
37
+ for (const f of files) {
38
+ if (f.endsWith(".cequre")) {
39
+ const content = fs.readFileSync(path.join(cequreSchemaDir, f), "utf-8");
40
+ const match = content.match(/prefix:\s*["']([^"']+)["']/);
41
+ if (match && match[1]) {
42
+ const prefix = match[1];
43
+ return prefix.startsWith("/") ? prefix : "/" + prefix;
44
+ }
45
+ }
46
+ }
47
+ } catch {}
48
+ }
49
+ const cequreDir = path.resolve(process.cwd(), "cequre");
50
+ if (fs.existsSync(cequreDir)) {
51
+ try {
52
+ const files = fs.readdirSync(cequreDir);
53
+ for (const f of files) {
54
+ if (f.endsWith(".cequre")) {
55
+ const content = fs.readFileSync(path.join(cequreDir, f), "utf-8");
56
+ const match = content.match(/prefix:\s*["']([^"']+)["']/);
57
+ if (match && match[1]) {
58
+ const prefix = match[1];
59
+ return prefix.startsWith("/") ? prefix : "/" + prefix;
60
+ }
61
+ }
62
+ }
63
+ } catch {}
64
+ }
65
+ return "/api";
66
+ }
67
+ function resolveEntryPoint(customEntry) {
68
+ if (customEntry) {
69
+ return customEntry;
70
+ }
71
+ const candidates = [
72
+ "cequre/index.ts",
73
+ "cequre/app.ts",
74
+ "cequre/server/app.ts",
75
+ "cequre/server/index.ts",
76
+ "src/server/app.ts",
77
+ "src/app.ts",
78
+ "src/server/index.ts",
79
+ "server/index.ts",
80
+ "server.ts",
81
+ "src/index.ts",
82
+ "api/index.ts"
83
+ ];
84
+ for (const cand of candidates) {
85
+ if (fs.existsSync(path.resolve(process.cwd(), cand))) {
86
+ return cand;
87
+ }
88
+ }
89
+ return "cequre/index.ts";
90
+ }
91
+ function getExecutableCommand() {
92
+ const localBin = path.join(process.cwd(), "node_modules", ".bin", "cequre");
93
+ if (fs.existsSync(localBin)) {
94
+ return { command: localBin, argsPrefix: [] };
95
+ }
96
+ return { command: "bunx", argsPrefix: ["cequre"] };
97
+ }
98
+ function lockPathFor(port) {
99
+ const root = path.resolve(process.cwd());
100
+ const digest = createHash("sha256").update(root).digest("hex").slice(0, 12);
101
+ return path.join(os.tmpdir(), `cequre-dev-${digest}-${port}.lock`);
102
+ }
103
+ function tryAcquireLock() {
104
+ if (!globalWsPort)
105
+ return false;
106
+ const lockPath = lockPathFor(globalWsPort);
107
+ try {
108
+ const fd = fs.openSync(lockPath, "wx");
109
+ fs.closeSync(fd);
110
+ return true;
111
+ } catch (e) {
112
+ if (e.code === "EEXIST")
113
+ return false;
114
+ throw e;
115
+ }
116
+ }
117
+ function releaseLock() {
118
+ if (!globalWsPort)
119
+ return;
120
+ const lockPath = lockPathFor(globalWsPort);
121
+ try {
122
+ fs.unlinkSync(lockPath);
123
+ } catch {}
124
+ }
125
+ function isGeneratedPath(filePath) {
126
+ const normalized = path.normalize(filePath);
127
+ return normalized.split(path.sep).some((part) => part === "_generated" || part === "generated") || normalized.endsWith("schema.json") || normalized.endsWith("cequre-sdk.ts");
128
+ }
129
+ function forwardStderr(stream, onLine) {
130
+ if (!stream)
131
+ return;
132
+ let pending = "";
133
+ stream.on("data", (data) => {
134
+ pending += data.toString();
135
+ const lines = pending.split(/\r?\n/);
136
+ pending = lines.pop() || "";
137
+ for (const line of lines) {
138
+ const trimmed = line.trim();
139
+ if (trimmed)
140
+ onLine(trimmed);
141
+ }
142
+ });
143
+ stream.on("end", () => {
144
+ const trimmed = pending.trim();
145
+ if (trimmed)
146
+ onLine(trimmed);
147
+ });
148
+ }
149
+ function attachBackendWatch(server, onReload) {
150
+ let reloadTimer;
151
+ let changedFile = "";
152
+ server.watcher.on("change", (filePath) => {
153
+ if (isGeneratedPath(filePath))
154
+ return;
155
+ const normalized = path.resolve(process.cwd(), filePath);
156
+ const relative = path.relative(process.cwd(), normalized);
157
+ const parts = relative.split(path.sep);
158
+ const startsWith = (...prefix) => prefix.every((part, index) => parts[index] === part);
159
+ const isBackendFile = startsWith("cequre") || startsWith("src", "server") || startsWith("src", "routes") || relative === "cequre.config.json" || relative.includes("cequre-generated");
160
+ if (!isBackendFile)
161
+ return;
162
+ changedFile = filePath;
163
+ if (reloadTimer)
164
+ clearTimeout(reloadTimer);
165
+ reloadTimer = setTimeout(() => {
166
+ reloadTimer = undefined;
167
+ const file = changedFile;
168
+ changedFile = "";
169
+ onReload(file);
170
+ }, 100);
171
+ });
172
+ }
173
+ function cequreVite(options = {}) {
174
+ let resolvedApiPrefix = resolveApiPrefix(options.apiPrefix);
175
+ if (!globalCleanupRegistered) {
176
+ globalCleanupRegistered = true;
177
+ g.__CEQURE_CLEANUP = true;
178
+ const cleanup = () => {
179
+ if (globalCequreProcess) {
180
+ globalCequreProcess.kill("SIGTERM");
181
+ globalCequreProcess = null;
182
+ g.__CEQURE_PROCESS = null;
183
+ }
184
+ releaseLock();
185
+ };
186
+ process.on("beforeExit", cleanup);
187
+ process.on("exit", cleanup);
188
+ }
189
+ return {
190
+ name: "cequre-vite-plugin",
191
+ async config(config, env) {
192
+ if (env.command === "serve") {
193
+ resolvedApiPrefix = resolveApiPrefix(options.apiPrefix);
194
+ const envPort = process.env.CEQURE_DEV_PORT;
195
+ if (envPort) {
196
+ globalWsPort = parseInt(envPort, 10);
197
+ g.__CEQURE_WS_PORT = globalWsPort;
198
+ } else if (!globalWsPort) {
199
+ const net = await import("net");
200
+ for (let port = 3001;port < 3100; port++) {
201
+ try {
202
+ await new Promise((resolve, reject) => {
203
+ const srv = net.createServer();
204
+ srv.unref();
205
+ srv.on("error", reject);
206
+ srv.listen(port, () => {
207
+ srv.close(resolve);
208
+ });
209
+ });
210
+ globalWsPort = port;
211
+ g.__CEQURE_WS_PORT = port;
212
+ releaseLock();
213
+ break;
214
+ } catch (e) {}
215
+ }
216
+ if (!globalWsPort) {
217
+ throw new Error("[cequre.vite] No available port found in range 3001-3099. Set CEQURE_DEV_PORT env var or free a port in this range.");
218
+ }
219
+ }
220
+ const viteConfig = {
221
+ define: {
222
+ __CEQURE_DEV_WS_URL__: JSON.stringify(`ws://127.0.0.1:${globalWsPort}`)
223
+ },
224
+ server: {
225
+ proxy: {
226
+ "/admin": {
227
+ target: `http://127.0.0.1:${globalWsPort}`,
228
+ changeOrigin: false
229
+ },
230
+ [resolvedApiPrefix]: {
231
+ target: `http://127.0.0.1:${globalWsPort}`,
232
+ changeOrigin: true,
233
+ ws: true,
234
+ configure: (proxy, _options) => {
235
+ proxy.on("proxyReq", (proxyReq, req) => {
236
+ if (req && req.headers && req.headers.host) {
237
+ proxyReq.setHeader("x-forwarded-host", req.headers.host);
238
+ proxyReq.setHeader("x-forwarded-proto", req.socket?.encrypted ? "https" : "http");
239
+ }
240
+ });
241
+ const originalEmit = proxy.emit.bind(proxy);
242
+ proxy.emit = function(event, ...args) {
243
+ if (event === "error" && args[0] && args[0].code === "ECONNREFUSED") {
244
+ const res = args[2];
245
+ if (res && !res.headersSent && typeof res.writeHead === "function") {
246
+ res.writeHead(503, {
247
+ "Content-Type": "application/json",
248
+ "Retry-After": "1"
249
+ });
250
+ res.end(JSON.stringify({ error: "Cequre backend restarting", code: "ECONNREFUSED_CEQURE" }));
251
+ }
252
+ return false;
253
+ }
254
+ return originalEmit(event, ...args);
255
+ };
256
+ }
257
+ }
258
+ }
259
+ }
260
+ };
261
+ return viteConfig;
262
+ }
263
+ return {};
264
+ },
265
+ async configureServer(server) {
266
+ const envName = server.environment?.name ?? server.config?.environments?.client?.name ?? "";
267
+ if (envName && envName !== "client")
268
+ return;
269
+ const spawnCequre = () => {
270
+ if (globalCequreProcess || !tryAcquireLock()) {
271
+ return;
272
+ }
273
+ const resolvedEntry = resolveEntryPoint(options.entry);
274
+ const exec = getExecutableCommand();
275
+ const args = [...exec.argsPrefix, "dev"];
276
+ if (resolvedEntry) {
277
+ args.push("--entry", resolvedEntry);
278
+ }
279
+ server.config.logger.info(`[cequre.vite] Attached Cequre API (${resolvedApiPrefix}) to Vite host.`);
280
+ globalCequreProcess = spawn(exec.command, args, {
281
+ cwd: process.cwd(),
282
+ stdio: ["ignore", "ignore", "pipe"],
283
+ env: { ...process.env, PORT: String(globalWsPort), HOST: "127.0.0.1", CEQURE_VITE_MODE: "1" }
284
+ });
285
+ g.__CEQURE_PROCESS = globalCequreProcess;
286
+ forwardStderr(globalCequreProcess.stderr, (msg) => {
287
+ server.config.logger.error(`[cequre] ${msg}`);
288
+ });
289
+ globalCequreProcess.on("close", (code) => {
290
+ if (code !== 0 && code !== null) {
291
+ server.config.logger.error(`[cequre.vite] Cequre binary exited with code ${code}`);
292
+ }
293
+ globalCequreProcess = null;
294
+ g.__CEQURE_PROCESS = null;
295
+ releaseLock();
296
+ });
297
+ };
298
+ const cequrePaths = [
299
+ path.resolve(process.cwd(), "cequre"),
300
+ path.resolve(process.cwd(), "cequre/server"),
301
+ path.resolve(process.cwd(), "cequre/schema"),
302
+ path.resolve(process.cwd(), "src/server"),
303
+ path.resolve(process.cwd(), "src/routes"),
304
+ path.resolve(process.cwd(), "cequre.config.json")
305
+ ].filter((p) => fs.existsSync(p));
306
+ if (cequrePaths.length > 0) {
307
+ server.watcher.add(cequrePaths);
308
+ }
309
+ attachBackendWatch(server, (filePath) => {
310
+ server.config.logger.info(`[cequre.vite] Backend / SDK updated (${path.basename(filePath)}), triggering Vite HMR reload`);
311
+ server.ws.send({ type: "full-reload" });
312
+ });
313
+ if (server.httpServer?.listening) {
314
+ spawnCequre();
315
+ } else if (server.httpServer?.once) {
316
+ server.httpServer.once("listening", spawnCequre);
317
+ } else {
318
+ server.httpServer?.on?.("listening", spawnCequre);
319
+ }
320
+ },
321
+ async closeBundle() {
322
+ if (options.autoBuild) {
323
+ const resolvedEntry = resolveEntryPoint(options.entry);
324
+ await cequreVite.build({ entry: resolvedEntry });
325
+ }
326
+ }
327
+ };
328
+ }
329
+ cequreVite.build = async function(options = {}) {
330
+ const entry = resolveEntryPoint(options.entry);
331
+ const outDir = options.outDir || "build/server";
332
+ const minify = options.minify !== false;
333
+ logger.info(`[cequre.vite] Starting production build...`);
334
+ const absEntry = path.resolve(process.cwd(), entry);
335
+ if (!fs.existsSync(absEntry)) {
336
+ throw new Error(`[cequre.vite] Entry point not found: ${absEntry}`);
337
+ }
338
+ let buildOutDir;
339
+ let naming;
340
+ if (options.outFile) {
341
+ const absOutFile = path.resolve(process.cwd(), options.outFile);
342
+ buildOutDir = path.dirname(absOutFile);
343
+ naming = path.basename(absOutFile);
344
+ } else {
345
+ buildOutDir = path.resolve(process.cwd(), outDir);
346
+ naming = "server.js";
347
+ }
348
+ if (!fs.existsSync(buildOutDir)) {
349
+ fs.mkdirSync(buildOutDir, { recursive: true });
350
+ }
351
+ logger.info(`[cequre.vite] Bundling Cequre API with Bun...`);
352
+ const targetOutputPath = path.join(buildOutDir, naming);
353
+ if (typeof Bun !== "undefined") {
354
+ const buildResult = await Bun.build({
355
+ entrypoints: [absEntry],
356
+ outdir: buildOutDir,
357
+ naming,
358
+ target: "bun",
359
+ minify
360
+ });
361
+ if (!buildResult.success) {
362
+ const errorDetails = buildResult.logs.map((log) => typeof log === "string" ? log : `${log.message ?? ""} (${log.position?.file ?? "?"}:${log.position?.line ?? "?"})`).join(`
363
+ `);
364
+ throw new Error(`[cequre.vite] Bun.build failed:
365
+ ${errorDetails}`);
366
+ }
367
+ } else {
368
+ const localBin = path.join(process.cwd(), "node_modules", ".bin", "bun");
369
+ const bunCmd = fs.existsSync(localBin) ? localBin : "bun";
370
+ const minifyFlag = minify ? " --minify" : "";
371
+ try {
372
+ execSync(`${bunCmd} build "${absEntry}" --outfile "${targetOutputPath}" --target bun${minifyFlag}`, {
373
+ stdio: "inherit",
374
+ env: process.env
375
+ });
376
+ } catch (err) {
377
+ throw new Error(`[cequre.vite] Bundling Cequre API failed using '${bunCmd}'. Ensure Bun is installed on your system or run builds using Bun ('bun run build'). Error: ${err.message}`);
378
+ }
379
+ }
380
+ const entryBasename = path.basename(absEntry, path.extname(absEntry)) + ".js";
381
+ const entryFallbackPath = path.join(buildOutDir, entryBasename);
382
+ const actualPath = fs.existsSync(targetOutputPath) ? targetOutputPath : fs.existsSync(entryFallbackPath) ? entryFallbackPath : targetOutputPath;
383
+ logger.info(`[cequre.vite] API bundled successfully to: ${actualPath}`);
384
+ if (!fs.existsSync(actualPath)) {
385
+ throw new Error(`[cequre.vite] Build reported success but output not found: ${actualPath}`);
386
+ }
387
+ };
388
+ export {
389
+ cequreVite,
390
+ getExecutableCommand,
391
+ resolveApiPrefix,
392
+ resolveEntryPoint
393
+ };
package/package.json CHANGED
@@ -1,17 +1,17 @@
1
1
  {
2
2
  "name": "@cequrebackends/plugin-vite",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "license": "BSL-1.0",
5
5
  "type": "module",
6
- "main": "./dist/next.js",
7
- "module": "./dist/next.js",
8
- "types": "./dist/next.d.ts",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
9
  "files": [
10
10
  "dist"
11
11
  ],
12
12
  "scripts": {
13
- "build": "rm -rf dist && bun build ./src/next.ts --outdir ./dist --target bun --packages external && tsc --emitDeclarationOnly",
14
- "dev": "bun build ./src/next.ts --outdir ./dist --target bun --packages external --watch"
13
+ "build": "rm -rf dist && bun build ./src/index.ts --outdir ./dist --target bun --packages external && tsc --emitDeclarationOnly",
14
+ "dev": "bun build ./src/index.ts --outdir ./dist --target bun --packages external --watch"
15
15
  },
16
16
  "peerDependencies": {
17
17
  "vite": "^8.0.0"
@@ -25,14 +25,9 @@
25
25
  "sideEffects": false,
26
26
  "exports": {
27
27
  ".": {
28
- "types": "./dist/next.d.ts",
29
- "import": "./dist/next.js",
30
- "default": "./dist/next.js"
31
- },
32
- "./next": {
33
- "types": "./dist/next.d.ts",
34
- "import": "./dist/next.js",
35
- "default": "./dist/next.js"
28
+ "types": "./dist/index.d.ts",
29
+ "import": "./dist/index.js",
30
+ "default": "./dist/index.js"
36
31
  }
37
32
  },
38
33
  "publishConfig": {
package/dist/bridge.d.ts DELETED
@@ -1,22 +0,0 @@
1
- import { IncomingMessage, ServerResponse } from "node:http";
2
- /**
3
- * Convert a Fetch Request into a node IncomingMessage suitable for Vite's
4
- * Connect-style middleware. Vite's dev server expects Node's http model;
5
- * Bun's server speaks Fetch — this shim is the entire bridge (spike-verified,
6
- * /tmp/vite-spike/spike1.ts).
7
- */
8
- export declare function fetchToIncoming(req: Request): IncomingMessage;
9
- export interface ShimmedResult {
10
- status: number;
11
- headers: Record<string, string | string[]>;
12
- body: Buffer;
13
- }
14
- /**
15
- * Collects node-style writes and hands back a Fetch-shaped result on end().
16
- * Vite middleware writes via res.writeHead/setHeader/write/end; we capture
17
- * those calls and resolve once so Bun.serve can respond.
18
- */
19
- export declare class ShimResponse extends ServerResponse {
20
- constructor(onFinish: (result: ShimmedResult) => void);
21
- }
22
- //# sourceMappingURL=bridge.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAI5D;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,CAkB7D;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IAC3C,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;GAIG;AACH,qBAAa,YAAa,SAAQ,cAAc;IAC9C,YAAY,QAAQ,EAAE,CAAC,MAAM,EAAE,aAAa,KAAK,IAAI,EAkCpD;CACF"}
package/dist/next.d.ts DELETED
@@ -1,38 +0,0 @@
1
- import type { CequrePlugin } from "cequre-ts";
2
- import { type InlineConfig } from "vite";
3
- export interface VitePluginNextOptions {
4
- /** Frontend root dir containing index.html. Default: process.cwd() */
5
- root?: string;
6
- /**
7
- * Built asset output dir (production). When present and readable at init,
8
- * every file registers a route under /assets/<file> whose handler reads
9
- * the file from disk lazily on first request — no eager in-memory load.
10
- */
11
- prodAssetsDir?: string;
12
- /** URL prefixes owned by Cequre that never fall through to Vite. */
13
- backendPrefixes?: string[];
14
- /** Loopback port for the HMR websocket. Default: CEQURE_VITE_HMR_PORT env or Vite's auto-pick. */
15
- hmrPort?: number;
16
- /** Extra inline Vite config merged into the server config (user vite.config.ts wins where both specify). */
17
- viteConfig?: InlineConfig;
18
- }
19
- /**
20
- * Vite as a first-class Cequre plugin.
21
- *
22
- * Dev mode: Vite dev server runs IN-PROCESS in middleware mode. preRequest
23
- * bridges frontend traffic to it; /api, /admin and /health stay with Cequre.
24
- * HMR runs on a dedicated loopback port (Fetch API cannot pass websocket
25
- * upgrades through — spike-verified).
26
- *
27
- * Prod mode: point prodAssetsDir at `vite build` output; hashed assets are
28
- * registered as routes whose handlers lazily read them from disk on first
29
- * request.
30
- *
31
- * Anti-lock-in guarantees:
32
- * - cequre-ts never imports this package (one-way dependency)
33
- * - options map to standard Vite config (mergeConfig), no proprietary schema
34
- * - eject = remove plugin from plugins[], run plain `vite` + proxy /api
35
- */
36
- export declare function vitePluginNext(options?: VitePluginNextOptions): CequrePlugin;
37
- export declare const cequreViteNext: typeof vitePluginNext;
38
- //# sourceMappingURL=next.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../src/next.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAE9C,OAAO,EAAiD,KAAK,YAAY,EAAE,MAAM,MAAM,CAAC;AAKxF,MAAM,WAAW,qBAAqB;IACpC,sEAAsE;IACtE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,kGAAkG;IAClG,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,4GAA4G;IAC5G,UAAU,CAAC,EAAE,YAAY,CAAC;CAC3B;AAwED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,YAAY,CAgEhF;AAID,eAAO,MAAM,cAAc,uBAAiB,CAAC"}
package/dist/next.js DELETED
@@ -1,164 +0,0 @@
1
- // @bun
2
- // src/next.ts
3
- import { createServer as createViteServer, mergeConfig } from "vite";
4
- import { readdirSync } from "fs";
5
- import path from "path";
6
-
7
- // src/bridge.ts
8
- import { IncomingMessage, ServerResponse } from "http";
9
- import { Socket } from "net";
10
- import { Readable } from "stream";
11
- function fetchToIncoming(req) {
12
- const url = new URL(req.url);
13
- const incoming = new IncomingMessage(new Socket);
14
- incoming.method = req.method;
15
- incoming.url = url.pathname + url.search;
16
- incoming.httpVersion = "1.1";
17
- incoming.rawHeaders = [];
18
- req.headers.forEach((value, key) => incoming.rawHeaders.push(key, value));
19
- if (req.body) {
20
- const nodeStream = Readable.fromWeb(req.body);
21
- nodeStream.on("data", (chunk) => incoming.push(chunk));
22
- nodeStream.on("end", () => incoming.push(null));
23
- nodeStream.on("error", (err) => incoming.destroy(err));
24
- }
25
- return incoming;
26
- }
27
-
28
- class ShimResponse extends ServerResponse {
29
- constructor(onFinish) {
30
- const stub = Object.create(null);
31
- stub.writable = true;
32
- stub.writableEnded = false;
33
- stub.allowHalfOpen = false;
34
- stub.emit = () => false;
35
- stub.on = stub.once = stub.off = stub.removeListener = () => stub;
36
- super(stub);
37
- const chunks = [];
38
- const headers = {};
39
- this.writeHead = (status, ...rest) => {
40
- const h = rest[rest.length - 1];
41
- if (h && typeof h === "object")
42
- Object.assign(headers, h);
43
- this.statusCode = status;
44
- return this;
45
- };
46
- this.setHeader = (k, v) => {
47
- headers[k] = v;
48
- return this;
49
- };
50
- this.write = (chunk) => {
51
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
52
- return true;
53
- };
54
- this.end = (chunk) => {
55
- if (chunk)
56
- chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : Buffer.from(chunk));
57
- onFinish({ status: this.statusCode || 200, headers, body: Buffer.concat(chunks) });
58
- return this;
59
- };
60
- }
61
- }
62
-
63
- // src/next.ts
64
- var DEFAULT_BACKEND_PREFIXES = ["/api", "/admin", "/health"];
65
- var MIME_BY_EXT = {
66
- ".js": "application/javascript",
67
- ".mjs": "application/javascript",
68
- ".css": "text/css",
69
- ".html": "text/html; charset=utf-8",
70
- ".svg": "image/svg+xml",
71
- ".json": "application/json",
72
- ".png": "image/png",
73
- ".jpg": "image/jpeg",
74
- ".jpeg": "image/jpeg",
75
- ".webp": "image/webp",
76
- ".woff": "font/woff",
77
- ".woff2": "font/woff2",
78
- ".map": "application/json",
79
- ".gif": "image/gif",
80
- ".ico": "image/x-icon",
81
- ".ttf": "font/ttf",
82
- ".otf": "font/otf",
83
- ".wasm": "application/wasm",
84
- ".webm": "video/webm",
85
- ".mp4": "video/mp4",
86
- ".pdf": "application/pdf"
87
- };
88
- function contentTypeFor(file) {
89
- return MIME_BY_EXT[path.extname(file).toLowerCase()] ?? "application/octet-stream";
90
- }
91
- async function registerProdAssets(app, prodAssetsDir) {
92
- if (!prodAssetsDir)
93
- return;
94
- let files;
95
- try {
96
- files = readdirSync(prodAssetsDir);
97
- } catch {
98
- return;
99
- }
100
- for (const file of files) {
101
- const filePath = path.join(prodAssetsDir, file);
102
- const contentType = contentTypeFor(file);
103
- app.router.get(`/assets/${file}`, async () => {
104
- const bytes = await Bun.file(filePath).arrayBuffer();
105
- return new Response(bytes, {
106
- headers: {
107
- "Content-Type": contentType,
108
- "Cache-Control": "public, max-age=31536000, immutable"
109
- }
110
- });
111
- }, {
112
- ignorePrefix: true
113
- });
114
- }
115
- }
116
- function vitePluginNext(options = {}) {
117
- let vite;
118
- const backendPrefixes = options.backendPrefixes ?? DEFAULT_BACKEND_PREFIXES;
119
- const isBackendPath = (pathname) => backendPrefixes.some((p) => pathname === p || pathname.startsWith(p + "/"));
120
- return {
121
- name: "cequre:vite",
122
- async onInit(app) {
123
- const hmrPort = options.hmrPort ?? (process.env.CEQURE_VITE_HMR_PORT ? Number(process.env.CEQURE_VITE_HMR_PORT) : undefined);
124
- vite = await createViteServer(mergeConfig({
125
- root: options.root ?? process.cwd(),
126
- logLevel: "info",
127
- server: {
128
- middlewareMode: true,
129
- hmr: hmrPort ? { port: hmrPort } : undefined
130
- },
131
- appType: "spa"
132
- }, options.viteConfig ?? {}));
133
- app.__vite = vite;
134
- await registerProdAssets(app, options.prodAssetsDir);
135
- },
136
- preRequest(request) {
137
- const pathname = new URL(request.url).pathname;
138
- if (isBackendPath(pathname))
139
- return;
140
- if (!vite)
141
- return;
142
- return new Promise((resolve) => {
143
- const shim = new ShimResponse(({ status, headers, body }) => resolve(new Response(new Uint8Array(body), { status, headers })));
144
- try {
145
- vite.middlewares(fetchToIncoming(request), shim, () => {
146
- resolve(undefined);
147
- });
148
- } catch (err) {
149
- console.error("[cequre:vite] middleware error:", err);
150
- resolve(undefined);
151
- }
152
- });
153
- },
154
- async onDestroy() {
155
- await vite?.close();
156
- vite = undefined;
157
- }
158
- };
159
- }
160
- var cequreViteNext = vitePluginNext;
161
- export {
162
- cequreViteNext,
163
- vitePluginNext
164
- };