@samva/vite 0.5.0 → 0.6.0
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/editor/assets/index-CxXHDnD3.css +2 -0
- package/dist/editor/assets/index-DWbrYu6R.js +32 -0
- package/dist/editor/index.html +2 -2
- package/dist/index.js +38 -13
- package/package.json +2 -2
- package/dist/editor/assets/index-BZ-5-1V0.js +0 -31
- package/dist/editor/assets/index-Cg4x6ewh.css +0 -2
package/dist/editor/index.html
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
<meta charset="utf-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
6
6
|
<title>Samva editor</title>
|
|
7
|
-
<script type="module" crossorigin src="./assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-DWbrYu6R.js"></script>
|
|
8
|
+
<link rel="stylesheet" crossorigin href="./assets/index-CxXHDnD3.css">
|
|
9
9
|
</head>
|
|
10
10
|
<body>
|
|
11
11
|
<div id="root"></div>
|
package/dist/index.js
CHANGED
|
@@ -4,6 +4,7 @@ import { createEmailRenderer } from "./email.js";
|
|
|
4
4
|
import { isTemplateDefinition, readEmailProject, renderEmailFixtures } from "./project.js";
|
|
5
5
|
import { existsSync } from "node:fs";
|
|
6
6
|
import { open, readFile, realpath, rename, rm, stat } from "node:fs/promises";
|
|
7
|
+
import { isIP } from "node:net";
|
|
7
8
|
import { extname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
8
9
|
import { fileURLToPath } from "node:url";
|
|
9
10
|
import { checkEmailCompatibility, emailSelections } from "@samva/markup/compiler";
|
|
@@ -109,13 +110,12 @@ var EditorFileStore = class {
|
|
|
109
110
|
const rel = full.slice(this.dir.length + 1).split("\\").join("/");
|
|
110
111
|
return kindForId(rel) === void 0 ? void 0 : rel;
|
|
111
112
|
}
|
|
112
|
-
async load(id, previous) {
|
|
113
|
+
async load(id, previous, fixture = this.session.get(id)?.fixture) {
|
|
113
114
|
const path = await this.resolvePath(id);
|
|
114
115
|
if (path === void 0) return { diagnostics: [diagnostic(id, "error", "template-unavailable", "Template file is missing or resolves outside the configured templates directory.")] };
|
|
115
116
|
const authoredSource = await readFile(path, "utf8").catch(() => void 0);
|
|
116
117
|
if (authoredSource === void 0) return { diagnostics: [diagnostic(id, "error", "template-read-error", "Template file could not be read.")] };
|
|
117
|
-
const
|
|
118
|
-
const name = session?.name ?? deriveName(id);
|
|
118
|
+
const name = this.session.get(id)?.name ?? deriveName(id);
|
|
119
119
|
if (this.evaluator === void 0) return {
|
|
120
120
|
authoredSource,
|
|
121
121
|
diagnostics: [diagnostic(id, "error", "tsx-evaluator-unavailable", "TSX templates require the Vite evaluator.")]
|
|
@@ -124,7 +124,7 @@ var EditorFileStore = class {
|
|
|
124
124
|
path,
|
|
125
125
|
id,
|
|
126
126
|
name,
|
|
127
|
-
fixture
|
|
127
|
+
fixture
|
|
128
128
|
}).catch((cause) => ({
|
|
129
129
|
ok: false,
|
|
130
130
|
diagnostics: [diagnostic(id, "error", "module-load-error", cause instanceof Error ? cause.message : String(cause))]
|
|
@@ -408,12 +408,7 @@ var EditorFileStore = class {
|
|
|
408
408
|
error: `Template declares no fixture named ${JSON.stringify(fixture)}.`
|
|
409
409
|
};
|
|
410
410
|
const meta = this.session.get(id);
|
|
411
|
-
this.
|
|
412
|
-
...meta,
|
|
413
|
-
metadata: meta?.metadata ?? {},
|
|
414
|
-
fixture
|
|
415
|
-
});
|
|
416
|
-
const loaded = await this.load(id, entry);
|
|
411
|
+
const loaded = await this.load(id, entry, fixture);
|
|
417
412
|
if (loaded.entry === void 0) {
|
|
418
413
|
if (loaded.diagnostics.length > 0) this.diagnostics.set(id, loaded.diagnostics);
|
|
419
414
|
return {
|
|
@@ -422,6 +417,11 @@ var EditorFileStore = class {
|
|
|
422
417
|
error: `Template ${id} could not be re-rendered.`
|
|
423
418
|
};
|
|
424
419
|
}
|
|
420
|
+
this.session.set(id, {
|
|
421
|
+
...meta,
|
|
422
|
+
metadata: meta?.metadata ?? {},
|
|
423
|
+
fixture
|
|
424
|
+
});
|
|
425
425
|
this.entries.set(id, loaded.entry);
|
|
426
426
|
this.publishEntry(id, loaded.entry, client);
|
|
427
427
|
return { ok: true };
|
|
@@ -513,6 +513,28 @@ const isViteInternalPath = (pathname) => VITE_INTERNAL_PREFIXES.some((prefix) =>
|
|
|
513
513
|
const hasFileExtension = (pathname) => extname(pathname) !== "";
|
|
514
514
|
const underRoute = (route, pathname) => route === "/" || pathname === route || pathname.startsWith(`${route}/`);
|
|
515
515
|
const childRoute = (route, child) => route === "/" ? `/${child}` : `${route}/${child}`;
|
|
516
|
+
const isLoopbackHostname = (hostname) => {
|
|
517
|
+
const normalized = hostname.toLowerCase().replace(/^\[|\]$/g, "");
|
|
518
|
+
if (normalized === "localhost" || normalized.endsWith(".localhost") || normalized === "::1") return true;
|
|
519
|
+
if (isIP(normalized) !== 4) return false;
|
|
520
|
+
return normalized.split(".")[0] === "127";
|
|
521
|
+
};
|
|
522
|
+
const isLoopbackAddress = (address) => {
|
|
523
|
+
if (address === void 0) return false;
|
|
524
|
+
const normalized = address.startsWith("::ffff:") ? address.slice(7) : address;
|
|
525
|
+
return isLoopbackHostname(normalized);
|
|
526
|
+
};
|
|
527
|
+
const isTrustedSourceSave = (req) => {
|
|
528
|
+
if (!isLoopbackAddress(req.socket.remoteAddress)) return false;
|
|
529
|
+
const origin = req.headers.origin;
|
|
530
|
+
if (origin === void 0) return false;
|
|
531
|
+
try {
|
|
532
|
+
const parsed = new URL(origin);
|
|
533
|
+
return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.username === "" && parsed.password === "" && isLoopbackHostname(parsed.hostname);
|
|
534
|
+
} catch {
|
|
535
|
+
return false;
|
|
536
|
+
}
|
|
537
|
+
};
|
|
516
538
|
const MAX_BODY_BYTES = 6295552;
|
|
517
539
|
var EditorBodyTooLarge = class extends Error {};
|
|
518
540
|
const readBody = (req) => new Promise((done, fail) => {
|
|
@@ -716,7 +738,11 @@ const samvaEditor = (options = {}) => {
|
|
|
716
738
|
runtime: "automatic",
|
|
717
739
|
importSource: "@samva/markup"
|
|
718
740
|
} },
|
|
719
|
-
optimizeDeps: { exclude: ["@samva/markup"] }
|
|
741
|
+
optimizeDeps: { exclude: ["@samva/markup"] },
|
|
742
|
+
server: {
|
|
743
|
+
host: "127.0.0.1",
|
|
744
|
+
allowedHosts: ["localhost", ".localhost"]
|
|
745
|
+
}
|
|
720
746
|
};
|
|
721
747
|
},
|
|
722
748
|
configResolved(resolved) {
|
|
@@ -895,8 +921,7 @@ const samvaEditor = (options = {}) => {
|
|
|
895
921
|
const id = query.get("id");
|
|
896
922
|
if (!id) return sendJson(res, 400, { error: "missing id" });
|
|
897
923
|
if (req.method === "POST") {
|
|
898
|
-
|
|
899
|
-
if (origin !== void 0 && origin !== `http://${req.headers.host}` && origin !== `https://${req.headers.host}`) return sendJson(res, 403, { error: "Source saves require the editor origin." });
|
|
924
|
+
if (!isTrustedSourceSave(req)) return sendJson(res, 403, { error: "Source saves require the editor origin." });
|
|
900
925
|
let body;
|
|
901
926
|
try {
|
|
902
927
|
body = JSON.parse(await readBody(req));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@samva/vite",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Unified Vite editor for Samva email, SMS, and WhatsApp templates",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"email",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
},
|
|
71
71
|
"dependencies": {
|
|
72
72
|
"@babel/parser": "^8.0.4",
|
|
73
|
-
"@samva/markup": "^0.
|
|
73
|
+
"@samva/markup": "^0.6.0",
|
|
74
74
|
"better-result": "3.0.1",
|
|
75
75
|
"caniemail": "2.0.2",
|
|
76
76
|
"css-tree": "3.2.1",
|