siteplane 0.1.67 → 0.2.0-next.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/README.md +43 -130
- package/dist/bin/siteplane.js +1892 -6606
- package/dist/chunk-KZ3AQ6OA.js +27 -0
- package/dist/chunk-YDCNEIOB.js +339 -0
- package/dist/components.d.ts +19 -0
- package/dist/components.js +33 -0
- package/dist/config.d.ts +8 -0
- package/dist/config.js +6 -0
- package/dist/index.d.ts +38 -1
- package/dist/index.js +112 -3716
- package/dist/internal/bridge-loader.js +28 -0
- package/dist/internal/bridge.js +1 -0
- package/dist/next.cjs +204 -0
- package/dist/next.d.ts +29 -120
- package/dist/next.js +123 -4509
- package/dist/route.d.ts +13 -0
- package/dist/route.js +144 -0
- package/package.json +45 -11
package/dist/route.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The handlers of `app/api/siteplane/[...siteplane]/route.ts`:
|
|
3
|
+
*
|
|
4
|
+
* ```ts
|
|
5
|
+
* export { GET, POST } from "siteplane/route";
|
|
6
|
+
* ```
|
|
7
|
+
*
|
|
8
|
+
* GET serves `draft` (Draft Mode for the editor) and `status`, POST `revalidate`.
|
|
9
|
+
*/
|
|
10
|
+
declare const GET: (request: Request) => Promise<Response>;
|
|
11
|
+
declare const POST: (request: Request) => Promise<Response>;
|
|
12
|
+
|
|
13
|
+
export { GET, POST };
|
package/dist/route.js
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PREVIEW_COOKIE,
|
|
3
|
+
signingKeys,
|
|
4
|
+
siteCacheTag,
|
|
5
|
+
siteConfig,
|
|
6
|
+
verifySitePreviewToken,
|
|
7
|
+
verifySiteRevalidation
|
|
8
|
+
} from "./chunk-YDCNEIOB.js";
|
|
9
|
+
import "./chunk-KZ3AQ6OA.js";
|
|
10
|
+
|
|
11
|
+
// src/route.ts
|
|
12
|
+
import { revalidateTag } from "next/cache";
|
|
13
|
+
import { cookies, draftMode } from "next/headers";
|
|
14
|
+
|
|
15
|
+
// ../shared/dist/safe-redirect.js
|
|
16
|
+
var UNSAFE_URL_CHARACTERS = /[\\\u0000-\u001f\u007f]/u;
|
|
17
|
+
var MAX_REDIRECT_PATH_LENGTH = 2048;
|
|
18
|
+
var INTERNAL_BASE = "https://siteplane.invalid";
|
|
19
|
+
function hasUnsafeUrlCharacters(value) {
|
|
20
|
+
return UNSAFE_URL_CHARACTERS.test(value);
|
|
21
|
+
}
|
|
22
|
+
function parseSafeRedirectPath(value) {
|
|
23
|
+
if (typeof value !== "string" || value.length > MAX_REDIRECT_PATH_LENGTH || !value.startsWith("/") || value.startsWith("//") || hasUnsafeUrlCharacters(value)) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
let url;
|
|
27
|
+
try {
|
|
28
|
+
url = new URL(value, INTERNAL_BASE);
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return url.origin === INTERNAL_BASE && !url.pathname.startsWith("//") ? `${url.pathname}${url.search}${url.hash}` : null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// src/route/handlers.ts
|
|
36
|
+
var MAX_REVALIDATION_BYTES = 16 * 1024;
|
|
37
|
+
var noStore = { "Cache-Control": "no-store" };
|
|
38
|
+
function createRouteHandlers(dependencies) {
|
|
39
|
+
async function GET2(request) {
|
|
40
|
+
const route = routeName(request);
|
|
41
|
+
const config = dependencies.config();
|
|
42
|
+
if (!config || route !== "status" && route !== "draft") return notFound();
|
|
43
|
+
return route === "status" ? status(config) : draft(request, config);
|
|
44
|
+
}
|
|
45
|
+
async function POST2(request) {
|
|
46
|
+
const config = dependencies.config();
|
|
47
|
+
if (!config || routeName(request) !== "revalidate") return notFound();
|
|
48
|
+
return revalidate(request, config);
|
|
49
|
+
}
|
|
50
|
+
function status(config) {
|
|
51
|
+
const { VERCEL_GIT_COMMIT_SHA: commit, VERCEL_ENV: environment } = dependencies.env;
|
|
52
|
+
return Response.json(
|
|
53
|
+
{ siteId: config.siteId, version: dependencies.version, commit: commit || null, environment: environment || null },
|
|
54
|
+
{ headers: noStore }
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
async function draft(request, config) {
|
|
58
|
+
const url = new URL(request.url);
|
|
59
|
+
const query = url.searchParams;
|
|
60
|
+
const path = parseSafeRedirectPath(query.get("path") ?? "/");
|
|
61
|
+
if (!path) return Response.json({ error: "invalid_path" }, { status: 400, headers: noStore });
|
|
62
|
+
const now = dependencies.now();
|
|
63
|
+
const token = query.get("token") ?? "";
|
|
64
|
+
const claims = token ? await verifySitePreviewToken(config, token, dependencies.keys, now) : null;
|
|
65
|
+
if (!claims) return Response.json({ error: "preview_token_invalid" }, { status: 401, headers: noStore });
|
|
66
|
+
await dependencies.enableDraftMode({ token, maxAgeSeconds: claims.exp - Math.floor(now.getTime() / 1e3) });
|
|
67
|
+
const root = url.pathname.endsWith("/") ? "../../../" : "../../";
|
|
68
|
+
return new Response(null, { status: 307, headers: { ...noStore, Location: `${root}${path.slice(1)}` } });
|
|
69
|
+
}
|
|
70
|
+
async function revalidate(request, config) {
|
|
71
|
+
const payload = await readBoundedText(request);
|
|
72
|
+
if (payload === null) return Response.json({ error: "too_large" }, { status: 413, headers: noStore });
|
|
73
|
+
const now = dependencies.now();
|
|
74
|
+
if (!await verifySiteRevalidation(config, { headers: request.headers, payload }, dependencies.keys, now)) {
|
|
75
|
+
return Response.json({ error: "invalid_signature" }, { status: 401, headers: noStore });
|
|
76
|
+
}
|
|
77
|
+
if (readSiteId(payload) !== config.siteId) {
|
|
78
|
+
return Response.json({ error: "wrong_site" }, { status: 403, headers: noStore });
|
|
79
|
+
}
|
|
80
|
+
dependencies.revalidateTag(siteCacheTag(config.siteId));
|
|
81
|
+
return Response.json({ revalidated: true }, { headers: noStore });
|
|
82
|
+
}
|
|
83
|
+
return { GET: GET2, POST: POST2 };
|
|
84
|
+
}
|
|
85
|
+
function routeName(request) {
|
|
86
|
+
return /\/api\/siteplane\/([a-z]+)\/?$/u.exec(new URL(request.url).pathname)?.[1] ?? null;
|
|
87
|
+
}
|
|
88
|
+
function notFound() {
|
|
89
|
+
return Response.json({ error: "not_found" }, { status: 404, headers: noStore });
|
|
90
|
+
}
|
|
91
|
+
function readSiteId(payload) {
|
|
92
|
+
try {
|
|
93
|
+
return JSON.parse(payload).data?.siteId;
|
|
94
|
+
} catch {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function readBoundedText(request) {
|
|
99
|
+
if (!request.body) return "";
|
|
100
|
+
const reader = request.body.getReader();
|
|
101
|
+
const chunks = [];
|
|
102
|
+
let bytes = 0;
|
|
103
|
+
for (; ; ) {
|
|
104
|
+
const chunk = await reader.read();
|
|
105
|
+
if (chunk.done) break;
|
|
106
|
+
bytes += chunk.value.byteLength;
|
|
107
|
+
if (bytes > MAX_REVALIDATION_BYTES) {
|
|
108
|
+
await reader.cancel();
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
chunks.push(chunk.value);
|
|
112
|
+
}
|
|
113
|
+
return new TextDecoder().decode(Buffer.concat(chunks));
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// src/version.ts
|
|
117
|
+
var PACKAGE_VERSION = "0.2.0-next.0";
|
|
118
|
+
|
|
119
|
+
// src/route.ts
|
|
120
|
+
var embeddable = { httpOnly: true, secure: true, sameSite: "none", partitioned: true, path: "/" };
|
|
121
|
+
function revalidateNow(tag) {
|
|
122
|
+
revalidateTag(tag, { expire: 0 });
|
|
123
|
+
}
|
|
124
|
+
var handlers = createRouteHandlers({
|
|
125
|
+
config: () => siteConfig,
|
|
126
|
+
keys: signingKeys.keys,
|
|
127
|
+
revalidateTag: revalidateNow,
|
|
128
|
+
enableDraftMode: async ({ token, maxAgeSeconds }) => {
|
|
129
|
+
(await draftMode()).enable();
|
|
130
|
+
const store = await cookies();
|
|
131
|
+
const bypass = store.get("__prerender_bypass");
|
|
132
|
+
if (bypass) store.set({ ...embeddable, name: "__prerender_bypass", value: bypass.value });
|
|
133
|
+
store.set({ ...embeddable, name: PREVIEW_COOKIE, value: token, maxAge: maxAgeSeconds });
|
|
134
|
+
},
|
|
135
|
+
env: process.env,
|
|
136
|
+
version: PACKAGE_VERSION,
|
|
137
|
+
now: () => /* @__PURE__ */ new Date()
|
|
138
|
+
});
|
|
139
|
+
var GET = handlers.GET;
|
|
140
|
+
var POST = handlers.POST;
|
|
141
|
+
export {
|
|
142
|
+
GET,
|
|
143
|
+
POST
|
|
144
|
+
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "siteplane",
|
|
3
3
|
"private": false,
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0-next.0",
|
|
5
|
+
"description": "Siteplane for Next.js websites: editable content with code fallbacks, Draft Mode preview, Booking and the siteplane CLI.",
|
|
5
6
|
"type": "module",
|
|
6
7
|
"license": "SEE LICENSE IN LICENSE",
|
|
7
8
|
"publishConfig": {
|
|
8
|
-
"access": "public"
|
|
9
|
+
"access": "public",
|
|
10
|
+
"tag": "next"
|
|
9
11
|
},
|
|
10
12
|
"bin": {
|
|
11
13
|
"siteplane": "dist/bin/siteplane.js"
|
|
@@ -13,36 +15,68 @@
|
|
|
13
15
|
"exports": {
|
|
14
16
|
".": {
|
|
15
17
|
"types": "./dist/index.d.ts",
|
|
16
|
-
"
|
|
18
|
+
"default": "./dist/index.js"
|
|
17
19
|
},
|
|
18
20
|
"./next": {
|
|
19
21
|
"types": "./dist/next.d.ts",
|
|
20
22
|
"import": "./dist/next.js",
|
|
21
|
-
"
|
|
22
|
-
}
|
|
23
|
+
"require": "./dist/next.cjs"
|
|
24
|
+
},
|
|
25
|
+
"./components": {
|
|
26
|
+
"types": "./dist/components.d.ts",
|
|
27
|
+
"default": "./dist/components.js"
|
|
28
|
+
},
|
|
29
|
+
"./route": {
|
|
30
|
+
"types": "./dist/route.d.ts",
|
|
31
|
+
"default": "./dist/route.js"
|
|
32
|
+
},
|
|
33
|
+
"./config": {
|
|
34
|
+
"types": "./dist/config.d.ts",
|
|
35
|
+
"default": "./dist/config.js"
|
|
36
|
+
},
|
|
37
|
+
"./internal/bridge-loader": "./dist/internal/bridge-loader.js",
|
|
38
|
+
"./internal/bridge": "./dist/internal/bridge.js",
|
|
39
|
+
"./package.json": "./package.json"
|
|
23
40
|
},
|
|
24
41
|
"types": "./dist/index.d.ts",
|
|
42
|
+
"sideEffects": false,
|
|
25
43
|
"engines": {
|
|
26
44
|
"node": ">=20"
|
|
27
45
|
},
|
|
28
46
|
"scripts": {
|
|
29
|
-
"build": "
|
|
47
|
+
"build": "node -e \"require('node:fs').rmSync('dist', { recursive: true, force: true })\" && tsup",
|
|
30
48
|
"lint": "eslint .",
|
|
31
|
-
"prepack": "npm run build -w @siteplane/cli && npm run build",
|
|
49
|
+
"prepack": "npm run build -w @siteplane/shared -w @siteplane/booking-core -w @siteplane/cli && npm run build",
|
|
32
50
|
"test": "vitest run",
|
|
33
51
|
"typecheck": "tsc --noEmit"
|
|
34
52
|
},
|
|
35
53
|
"dependencies": {
|
|
36
|
-
"minimatch": "10.2.5",
|
|
37
|
-
"semver": "7.8.5",
|
|
38
54
|
"typescript": "6.0.3",
|
|
39
|
-
"yaml": "2.9.0",
|
|
40
55
|
"zod": "4.4.3"
|
|
41
56
|
},
|
|
57
|
+
"peerDependencies": {
|
|
58
|
+
"next": ">=15 <17",
|
|
59
|
+
"react": ">=19 <20",
|
|
60
|
+
"react-dom": ">=19 <20"
|
|
61
|
+
},
|
|
62
|
+
"peerDependenciesMeta": {
|
|
63
|
+
"next": {
|
|
64
|
+
"optional": true
|
|
65
|
+
},
|
|
66
|
+
"react": {
|
|
67
|
+
"optional": true
|
|
68
|
+
},
|
|
69
|
+
"react-dom": {
|
|
70
|
+
"optional": true
|
|
71
|
+
}
|
|
72
|
+
},
|
|
42
73
|
"files": [
|
|
43
74
|
"dist",
|
|
44
75
|
"README.md",
|
|
45
76
|
"LICENSE",
|
|
46
77
|
"package.json"
|
|
47
|
-
]
|
|
78
|
+
],
|
|
79
|
+
"devDependencies": {
|
|
80
|
+
"happy-dom": "^20.14.5"
|
|
81
|
+
}
|
|
48
82
|
}
|