@olonjs/next 0.0.2

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,41 @@
1
+ /**
2
+ * Admin access gate (Vite tenant-alpha middleware parity).
3
+ * JWT ECDSA P-256, sub=admin-access. Active only when VERCEL_ENV is set.
4
+ */
5
+ export declare const ADMIN_SESSION_COOKIE = "__olonjs_admin_session";
6
+
7
+ export declare const ADMIN_SESSION_MAX_AGE = 3600;
8
+
9
+ export declare type AdminGateEnv = {
10
+ VERCEL_ENV?: string;
11
+ ADMIN_PUBLIC_KEY?: string;
12
+ };
13
+
14
+ export declare type AdminGateResult = {
15
+ kind: 'bypass';
16
+ } | {
17
+ kind: 'allow';
18
+ } | {
19
+ kind: 'deny';
20
+ hint: string;
21
+ } | {
22
+ kind: 'set-session';
23
+ token: string;
24
+ location: string;
25
+ };
26
+
27
+ /**
28
+ * Authorize an admin HTML or mutate-API request.
29
+ * Cookie Path must be `/` so session covers `/admin` and `/api/*` mutate routes.
30
+ */
31
+ export declare function authorizeAdminRequest(request: Request, env: AdminGateEnv): Promise<AdminGateResult>;
32
+
33
+ export declare function buildAdminSessionCookie(token: string): string;
34
+
35
+ export declare function parseCookieHeader(header: string | null): Record<string, string>;
36
+
37
+ export declare function verifyAdminJwt(token: string, publicKey: CryptoKey, options?: {
38
+ checkExp: boolean;
39
+ }): Promise<boolean>;
40
+
41
+ export { }
@@ -0,0 +1,83 @@
1
+ const d = "__olonjs_admin_session", E = 3600;
2
+ function p(e) {
3
+ return e.replace(/-/g, "+").replace(/_/g, "/") + "=".repeat((4 - e.length % 4) % 4);
4
+ }
5
+ function y(e) {
6
+ const t = atob(e), o = new Uint8Array(t.length);
7
+ for (let n = 0; n < t.length; n++) o[n] = t.charCodeAt(n);
8
+ return o.buffer;
9
+ }
10
+ function k(e) {
11
+ const t = e.replace(/-----BEGIN PUBLIC KEY-----/g, "").replace(/-----END PUBLIC KEY-----/g, "").replace(/\\n/g, "").replace(/\s+/g, "");
12
+ return y(t);
13
+ }
14
+ async function m(e) {
15
+ return crypto.subtle.importKey(
16
+ "spki",
17
+ k(e),
18
+ { name: "ECDSA", namedCurve: "P-256" },
19
+ !1,
20
+ ["verify"]
21
+ );
22
+ }
23
+ async function f(e, t, o = { checkExp: !0 }) {
24
+ try {
25
+ const n = e.split(".");
26
+ if (n.length !== 3) return !1;
27
+ const [s, l, c] = n, i = JSON.parse(atob(p(l)));
28
+ if (i.sub !== "admin-access") return !1;
29
+ if (o.checkExp) {
30
+ const r = Math.floor(Date.now() / 1e3);
31
+ if (typeof i.exp != "number" || i.exp < r || typeof i.iat != "number" || r - i.iat > 45) return !1;
32
+ }
33
+ const u = new TextEncoder().encode(`${s}.${l}`), a = y(p(c));
34
+ return crypto.subtle.verify({ name: "ECDSA", hash: "SHA-256" }, t, a, u);
35
+ } catch {
36
+ return !1;
37
+ }
38
+ }
39
+ function _(e) {
40
+ return e ? Object.fromEntries(
41
+ e.split(";").map((t) => {
42
+ const [o, ...n] = t.trim().split("=");
43
+ return [o.trim(), decodeURIComponent(n.join("="))];
44
+ })
45
+ ) : {};
46
+ }
47
+ async function h(e, t) {
48
+ if (!t.VERCEL_ENV) return { kind: "bypass" };
49
+ const o = t.ADMIN_PUBLIC_KEY;
50
+ if (!o) return { kind: "deny", hint: "missing_public_key" };
51
+ let n;
52
+ try {
53
+ n = await m(o);
54
+ } catch (r) {
55
+ return {
56
+ kind: "deny",
57
+ hint: `key_import_failed: ${r instanceof Error ? r.message : String(r)}`
58
+ };
59
+ }
60
+ const s = new URL(e.url), c = _(e.headers.get("Cookie"))[d];
61
+ if (c && await f(c, n, { checkExp: !1 }))
62
+ return { kind: "allow" };
63
+ const i = e.headers.get("Authorization") ?? "", u = i.startsWith("Bearer ") ? i.slice(7) : null;
64
+ if (u && await f(u, n))
65
+ return { kind: "allow" };
66
+ const a = s.searchParams.get("token");
67
+ if (a && await f(a, n)) {
68
+ const r = new URL(s.toString());
69
+ return r.searchParams.delete("token"), { kind: "set-session", token: a, location: r.toString() };
70
+ }
71
+ return { kind: "deny", hint: "token_invalid" };
72
+ }
73
+ function A(e) {
74
+ return `${d}=${encodeURIComponent(e)}; Path=/; HttpOnly; Secure; SameSite=Lax; Max-Age=3600`;
75
+ }
76
+ export {
77
+ d as ADMIN_SESSION_COOKIE,
78
+ E as ADMIN_SESSION_MAX_AGE,
79
+ h as authorizeAdminRequest,
80
+ A as buildAdminSessionCookie,
81
+ _ as parseCookieHeader,
82
+ f as verifyAdminJwt
83
+ };
@@ -0,0 +1,18 @@
1
+ import { JsonPagesConfig } from '@olonjs/core';
2
+ import { JSX } from 'react/jsx-runtime';
3
+ import { ReactNode } from 'react';
4
+
5
+ export declare function AdminIsland({ config, children }: AdminIslandProps): JSX.Element;
6
+
7
+ export declare type AdminIslandProps = {
8
+ config: JsonPagesConfig;
9
+ /** Optional overlays (e.g. Save2Repo DopaDrawer) rendered beside the engine. */
10
+ children?: ReactNode;
11
+ };
12
+
13
+ export declare const OLONJS_NEXT_CLIENT: {
14
+ name: string;
15
+ surface: "client";
16
+ };
17
+
18
+ export { }
package/dist/client.js ADDED
@@ -0,0 +1,13 @@
1
+ import { O as x } from "./packageInfo-BtT3WSEw.js";
2
+ import { jsxs as o, Fragment as e, jsx as m } from "react/jsx-runtime";
3
+ import { JsonPagesEngine as s } from "@olonjs/react";
4
+ function a({ config: r, children: n }) {
5
+ return /* @__PURE__ */ o(e, { children: [
6
+ /* @__PURE__ */ m(s, { config: r }),
7
+ n
8
+ ] });
9
+ }
10
+ export {
11
+ a as AdminIsland,
12
+ x as OLONJS_NEXT_CLIENT
13
+ };
@@ -0,0 +1,54 @@
1
+ export declare function listLocalImages(assetsImagesDir: string, urlPrefix?: string): {
2
+ id: string;
3
+ url: string;
4
+ alt: string;
5
+ tags: string[];
6
+ }[];
7
+
8
+ declare type LocalDataRoots = {
9
+ configDir: string;
10
+ pagesDir: string;
11
+ collectionsDir: string;
12
+ assetsImagesDir: string;
13
+ };
14
+
15
+ export declare const MAX_UPLOAD_BYTES: number;
16
+
17
+ export declare const OLONJS_NEXT_CLIENT: {
18
+ name: string;
19
+ surface: "client";
20
+ };
21
+
22
+ export declare const OLONJS_NEXT_SERVER: {
23
+ name: string;
24
+ surface: "server";
25
+ };
26
+
27
+ declare type ProjectStateLike = {
28
+ site?: unknown;
29
+ menu?: unknown;
30
+ theme?: unknown;
31
+ page?: unknown;
32
+ collections?: Record<string, unknown>;
33
+ };
34
+
35
+ export declare function resolveLocalDataRoots(appRoot: string): LocalDataRoots;
36
+
37
+ /**
38
+ * Resolve a page JSON path under rootDir without leaving that root.
39
+ * Mirrors tenant-alpha Vite plugin sanitization.
40
+ */
41
+ export declare function safeDataSlugPath(rootDir: string, rawSlug: string, fallback: string): string;
42
+
43
+ export declare function saveProjectStateToDisk(roots: LocalDataRoots, projectState: ProjectStateLike, slug: string): void;
44
+
45
+ export declare function saveUploadedImage(options: {
46
+ assetsImagesDir: string;
47
+ filename: string;
48
+ mimeType?: string;
49
+ base64Data: string;
50
+ }): {
51
+ url: string;
52
+ };
53
+
54
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ import { M as e, O as o, l as t, r as l, s as r, a as O, b as E } from "./localPersistence-CCNj_74I.js";
2
+ import { O as S } from "./packageInfo-BtT3WSEw.js";
3
+ export {
4
+ e as MAX_UPLOAD_BYTES,
5
+ S as OLONJS_NEXT_CLIENT,
6
+ o as OLONJS_NEXT_SERVER,
7
+ t as listLocalImages,
8
+ l as resolveLocalDataRoots,
9
+ r as safeDataSlugPath,
10
+ O as saveProjectStateToDisk,
11
+ E as saveUploadedImage
12
+ };
@@ -0,0 +1,71 @@
1
+ import r from "node:fs";
2
+ import s from "node:path";
3
+ const v = {
4
+ name: "@olonjs/next",
5
+ surface: "server"
6
+ };
7
+ function f(e, n, t) {
8
+ const c = String(n || t).replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").split("/").map((g) => g.replace(/[^a-zA-Z0-9_[\]-]/g, "_")).filter(Boolean), a = s.resolve(e, `${c.join(s.sep) || t}.json`);
9
+ if (!(a.startsWith(`${e}${s.sep}`) || a === e)) throw new Error("Invalid data path");
10
+ return a;
11
+ }
12
+ const o = /* @__PURE__ */ new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg", ".avif"]), u = /* @__PURE__ */ new Set([
13
+ "image/jpeg",
14
+ "image/png",
15
+ "image/webp",
16
+ "image/gif",
17
+ "image/svg+xml",
18
+ "image/avif"
19
+ ]), m = 5 * 1024 * 1024;
20
+ function w(e) {
21
+ return {
22
+ configDir: s.resolve(e, "src", "data", "config"),
23
+ pagesDir: s.resolve(e, "src", "data", "pages"),
24
+ collectionsDir: s.resolve(e, "src", "data", "collections"),
25
+ assetsImagesDir: s.resolve(e, "public", "assets", "images")
26
+ };
27
+ }
28
+ function h(e, n, t) {
29
+ for (const i of [e.configDir, e.pagesDir, e.collectionsDir])
30
+ r.existsSync(i) || r.mkdirSync(i, { recursive: !0 });
31
+ if (n.site != null && r.writeFileSync(s.join(e.configDir, "site.json"), JSON.stringify(n.site, null, 2), "utf8"), n.menu != null && r.writeFileSync(s.join(e.configDir, "menu.json"), JSON.stringify(n.menu, null, 2), "utf8"), n.theme != null && r.writeFileSync(s.join(e.configDir, "theme.json"), JSON.stringify(n.theme, null, 2), "utf8"), n.page != null) {
32
+ const i = f(e.pagesDir, t, "page");
33
+ r.mkdirSync(s.dirname(i), { recursive: !0 }), r.writeFileSync(i, JSON.stringify(n.page, null, 2), "utf8");
34
+ }
35
+ if (n.collections && typeof n.collections == "object")
36
+ for (const [i, c] of Object.entries(n.collections)) {
37
+ const a = String(i).replace(/[^a-zA-Z0-9-_]/g, "_");
38
+ if (!a) continue;
39
+ const l = s.join(e.collectionsDir, a);
40
+ r.mkdirSync(l, { recursive: !0 }), r.writeFileSync(s.join(l, `${a}.json`), JSON.stringify(c, null, 2), "utf8");
41
+ }
42
+ }
43
+ function y(e, n = "/assets/images") {
44
+ const t = [];
45
+ if (!r.existsSync(e)) return t;
46
+ for (const i of r.readdirSync(e))
47
+ o.has(s.extname(i).toLowerCase()) && t.push({ id: i, url: `${n}/${i}`, alt: i, tags: [] });
48
+ return t;
49
+ }
50
+ function d(e, n) {
51
+ const t = e.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9-_]/g, "_").slice(0, 128) || "image", i = e.includes(".") ? s.extname(e).toLowerCase() : n != null && n.startsWith("image/") ? `.${(n.split("/")[1] || "png").replace("jpeg", "jpg")}` : ".png";
52
+ return `${Date.now()}-${t}${o.has(i) ? i : ".png"}`;
53
+ }
54
+ function D(e) {
55
+ const { assetsImagesDir: n, filename: t, mimeType: i, base64Data: c } = e, a = Buffer.from(c, "base64");
56
+ if (a.length > m)
57
+ throw new Error("File too large. Max 5MB.");
58
+ if (i && !u.has(i))
59
+ throw new Error("Invalid file type");
60
+ const l = d(t, i);
61
+ return r.existsSync(n) || r.mkdirSync(n, { recursive: !0 }), r.writeFileSync(s.join(n, l), a), { url: `/assets/images/${l}` };
62
+ }
63
+ export {
64
+ m as M,
65
+ v as O,
66
+ h as a,
67
+ D as b,
68
+ y as l,
69
+ w as r,
70
+ f as s
71
+ };
@@ -0,0 +1,7 @@
1
+ const e = {
2
+ name: "@olonjs/next",
3
+ surface: "client"
4
+ };
5
+ export {
6
+ e as O
7
+ };
@@ -0,0 +1,220 @@
1
+ import { JsonPagesConfig } from '@olonjs/core';
2
+ import { MenuConfig } from '@olonjs/core';
3
+ import { PageConfig } from '@olonjs/core';
4
+ import { SiteConfig } from '@olonjs/core';
5
+
6
+ /**
7
+ * Admin access gate (Vite tenant-alpha middleware parity).
8
+ * JWT ECDSA P-256, sub=admin-access. Active only when VERCEL_ENV is set.
9
+ */
10
+ export declare const ADMIN_SESSION_COOKIE = "__olonjs_admin_session";
11
+
12
+ export declare const ADMIN_SESSION_MAX_AGE = 3600;
13
+
14
+ export declare type AdminGateEnv = {
15
+ VERCEL_ENV?: string;
16
+ ADMIN_PUBLIC_KEY?: string;
17
+ };
18
+
19
+ export declare type AdminGateResult = {
20
+ kind: 'bypass';
21
+ } | {
22
+ kind: 'allow';
23
+ } | {
24
+ kind: 'deny';
25
+ hint: string;
26
+ } | {
27
+ kind: 'set-session';
28
+ token: string;
29
+ location: string;
30
+ };
31
+
32
+ /**
33
+ * Vite `applyDevSliceFilters` parity — filter resolved collection maps by authored `$sliceFilter`.
34
+ */
35
+ export declare function applyDevSliceFilters<T extends {
36
+ sections?: Array<{
37
+ data?: Record<string, unknown>;
38
+ }>;
39
+ }>(page: T, authored: {
40
+ sections?: Array<{
41
+ data?: Record<string, unknown>;
42
+ }>;
43
+ } | undefined, params: Record<string, string>): T;
44
+
45
+ /**
46
+ * Authorize an admin HTML or mutate-API request.
47
+ * Cookie Path must be `/` so session covers `/admin` and `/api/*` mutate routes.
48
+ */
49
+ export declare function authorizeAdminRequest(request: Request, env: AdminGateEnv): Promise<AdminGateResult>;
50
+
51
+ export declare function buildAdminSessionCookie(token: string): string;
52
+
53
+ /**
54
+ * Next.js rewrites so `/*.json` and `/pages/*.json` hit the public-page API
55
+ * instead of the RSC catch-all.
56
+ */
57
+ export declare function buildPublicPageJsonRewrites(): PublicPageJsonRewrite[];
58
+
59
+ /** Map resolve result → HTTP status + JSON body (Vite page JSON parity). */
60
+ export declare function createPublicPageJsonHttpResult(result: ResolvePublicPageJsonResult | null): PublicPageJsonHttpResult;
61
+
62
+ export declare function listLocalImages(assetsImagesDir: string, urlPrefix?: string): {
63
+ id: string;
64
+ url: string;
65
+ alt: string;
66
+ tags: string[];
67
+ }[];
68
+
69
+ export declare type LivePublicPageContent = {
70
+ pages: Record<string, PageConfig>;
71
+ siteConfig: SiteConfig;
72
+ menuConfig: MenuConfig;
73
+ };
74
+
75
+ /**
76
+ * Live boot parity — single-page SPP `GET {apiBase}/render?path=…`.
77
+ */
78
+ export declare function loadLivePublicPageContent(input: LoadLivePublicPageContentInput): Promise<LivePublicPageContent>;
79
+
80
+ export declare type LoadLivePublicPageContentInput = {
81
+ slug: string;
82
+ apiBases: string[];
83
+ apiKey: string;
84
+ fetchImpl?: typeof fetch;
85
+ signal?: AbortSignal;
86
+ };
87
+
88
+ /**
89
+ * Save2Repo static boot parity — fetch published `config/site.json` + `pages/{slug}.json`.
90
+ */
91
+ export declare function loadPublishedStaticContent(input: LoadPublishedStaticContentInput): Promise<{
92
+ pages: Record<string, PageConfig>;
93
+ siteConfig: SiteConfig;
94
+ }>;
95
+
96
+ export declare type LoadPublishedStaticContentInput = {
97
+ knownSlugs: string[];
98
+ /** Absolute origin + optional base path, e.g. `https://cdn.example/` or `http://localhost:3000/`. */
99
+ baseUrl: string;
100
+ fetchImpl?: typeof fetch;
101
+ };
102
+
103
+ /**
104
+ * Visitor page loader for RSC hosts (ADR-0017).
105
+ * Empty registry → empty tenant; unknown slug → not-found (no home fallback).
106
+ */
107
+ export declare function loadVisitorPage(input: VisitorLoadInput): VisitorLoadResult;
108
+
109
+ export declare type LocalDataRoots = {
110
+ configDir: string;
111
+ pagesDir: string;
112
+ collectionsDir: string;
113
+ assetsImagesDir: string;
114
+ };
115
+
116
+ export declare const MAX_UPLOAD_BYTES: number;
117
+
118
+ /**
119
+ * Normalize public page JSON request paths:
120
+ * `/pages/home.json`, `home.json`, `libri/1984.json` → registry/request slug.
121
+ */
122
+ export declare function normalizePublicPageSlug(raw: string): string;
123
+
124
+ /** Alpha `normalizeRouteSlug` / `normalizeSlugForCache` parity. */
125
+ export declare function normalizePublishedSlug(value: string): string;
126
+
127
+ export declare const OLONJS_NEXT_SERVER: {
128
+ name: string;
129
+ surface: "server";
130
+ };
131
+
132
+ export declare function parseCookieHeader(header: string | null): Record<string, string>;
133
+
134
+ export declare type ProjectStateLike = {
135
+ site?: unknown;
136
+ menu?: unknown;
137
+ theme?: unknown;
138
+ page?: unknown;
139
+ collections?: Record<string, unknown>;
140
+ };
141
+
142
+ export declare type PublicPageContentBundle = {
143
+ pages: Record<string, PageConfig>;
144
+ siteConfig: SiteConfig;
145
+ themeConfig: JsonPagesConfig['themeConfig'];
146
+ menuConfig: MenuConfig;
147
+ collections?: JsonPagesConfig['collections'];
148
+ collectionSchemas?: JsonPagesConfig['collectionSchemas'];
149
+ refDocuments?: JsonPagesConfig['refDocuments'];
150
+ };
151
+
152
+ export declare type PublicPageJsonHttpResult = {
153
+ status: 200 | 404;
154
+ body: unknown;
155
+ };
156
+
157
+ export declare type PublicPageJsonRewrite = {
158
+ source: string;
159
+ destination: string;
160
+ };
161
+
162
+ export declare function resolveLocalDataRoots(appRoot: string): LocalDataRoots;
163
+
164
+ /**
165
+ * Resolve a public page document for JSON responses (Vite `/slug.json` parity).
166
+ * Returns null when the slug does not match the page registry.
167
+ */
168
+ export declare function resolvePublicPageJson(input: {
169
+ slug: string;
170
+ bundle: PublicPageContentBundle;
171
+ }): ResolvePublicPageJsonResult | null;
172
+
173
+ export declare type ResolvePublicPageJsonResult = {
174
+ page: PageConfig;
175
+ registrySlug: string;
176
+ params: Record<string, string>;
177
+ };
178
+
179
+ /**
180
+ * Resolve a page JSON path under rootDir without leaving that root.
181
+ * Mirrors tenant-alpha Vite plugin sanitization.
182
+ */
183
+ export declare function safeDataSlugPath(rootDir: string, rawSlug: string, fallback: string): string;
184
+
185
+ export declare function saveProjectStateToDisk(roots: LocalDataRoots, projectState: ProjectStateLike, slug: string): void;
186
+
187
+ export declare function saveUploadedImage(options: {
188
+ assetsImagesDir: string;
189
+ filename: string;
190
+ mimeType?: string;
191
+ base64Data: string;
192
+ }): {
193
+ url: string;
194
+ };
195
+
196
+ /** Map public JSON slug → SPP `render?path=` (bootLive parity). */
197
+ export declare function slugToRenderPath(slug: string): string;
198
+
199
+ export declare function verifyAdminJwt(token: string, publicKey: CryptoKey, options?: {
200
+ checkExp: boolean;
201
+ }): Promise<boolean>;
202
+
203
+ export declare type VisitorLoadInput = {
204
+ pages: Record<string, PageConfig>;
205
+ slug: string;
206
+ };
207
+
208
+ export declare type VisitorLoadResult = {
209
+ kind: 'empty';
210
+ } | {
211
+ kind: 'not-found';
212
+ } | {
213
+ kind: 'page';
214
+ slug: string;
215
+ registrySlug: string;
216
+ page: PageConfig;
217
+ params: Record<string, string>;
218
+ };
219
+
220
+ export { }
package/dist/server.js ADDED
@@ -0,0 +1,220 @@
1
+ import { M as x, O as B, l as W, r as F, s as H, a as T, b as U } from "./localPersistence-CCNj_74I.js";
2
+ import { resolvePageMatchFromRegistry as P, withBasePath as j, resolvePublicPageDocument as w } from "@olonjs/core";
3
+ import { ADMIN_SESSION_COOKIE as V, ADMIN_SESSION_MAX_AGE as X, authorizeAdminRequest as G, buildAdminSessionCookie as K, parseCookieHeader as Y, verifyAdminJwt as Q } from "./admin-gate.js";
4
+ function I(e) {
5
+ const t = e.pages ?? {};
6
+ if (Object.keys(t).length === 0) return { kind: "empty" };
7
+ const n = P(t, e.slug || "home");
8
+ return n ? {
9
+ kind: "page",
10
+ slug: n.requestedSlug,
11
+ registrySlug: n.registrySlug,
12
+ page: n.page,
13
+ params: n.params
14
+ } : { kind: "not-found" };
15
+ }
16
+ function O(e, t, n) {
17
+ if (!(t != null && t.sections) || !(e != null && e.sections)) return e;
18
+ const s = (r, a) => a.split(".").reduce((c, i) => c == null ? void 0 : c[i], r);
19
+ return {
20
+ ...e,
21
+ sections: e.sections.map((r, a) => {
22
+ var g, l;
23
+ const c = (l = (g = t.sections) == null ? void 0 : g[a]) == null ? void 0 : l.data;
24
+ if (!c || !r.data) return r;
25
+ const i = { ...r.data };
26
+ for (const [u, o] of Object.entries(c)) {
27
+ if (!(o != null && o.$sliceFilter) || typeof i[u] != "object" || i[u] === null) continue;
28
+ const m = Object.fromEntries(
29
+ Object.entries(o.$sliceFilter).map(([p, f]) => [
30
+ p,
31
+ typeof f == "string" ? f : n[(f == null ? void 0 : f.$routeParam) ?? ""] ?? ""
32
+ ]).filter(([, p]) => p)
33
+ );
34
+ i[u] = Object.fromEntries(
35
+ Object.entries(i[u]).filter(
36
+ ([, p]) => Object.entries(m).every(([f, y]) => String(s(p, f) ?? "") === y)
37
+ )
38
+ );
39
+ }
40
+ return { ...r, data: i };
41
+ })
42
+ };
43
+ }
44
+ function d(e) {
45
+ return typeof e == "object" && e !== null;
46
+ }
47
+ function h(e, t) {
48
+ return typeof e == "string" && e.trim() ? e : t;
49
+ }
50
+ function S(e) {
51
+ return e.toLowerCase().replace(/[^a-z0-9/_[\]-]/g, "-").replace(/^\/+|\/+$/g, "") || "home";
52
+ }
53
+ function $(e, t) {
54
+ let n = t;
55
+ if (typeof n == "string")
56
+ try {
57
+ n = JSON.parse(n);
58
+ } catch {
59
+ return null;
60
+ }
61
+ if (!d(n) || !Array.isArray(n.sections)) return null;
62
+ const s = d(n.meta) ? n.meta : {}, r = h(n.slug, e);
63
+ return {
64
+ id: h(n.id, `${r}-page`),
65
+ slug: r,
66
+ meta: {
67
+ title: h(s.title, r),
68
+ description: h(s.description, "")
69
+ },
70
+ sections: n.sections,
71
+ ...d(n.collection) ? { collection: n.collection } : {},
72
+ ...typeof n["global-header"] == "boolean" ? { "global-header": n["global-header"] } : {}
73
+ };
74
+ }
75
+ function C(e) {
76
+ let t = e;
77
+ if (typeof t == "string")
78
+ try {
79
+ t = JSON.parse(t);
80
+ } catch {
81
+ return null;
82
+ }
83
+ return !d(t) || !d(t.identity) ? null : t;
84
+ }
85
+ function E(e) {
86
+ if (!d(e)) return {};
87
+ const t = {};
88
+ for (const [n, s] of Object.entries(e)) {
89
+ const r = S(n), a = $(r, s);
90
+ a && (t[r] = { ...a, slug: r });
91
+ }
92
+ return t;
93
+ }
94
+ function b(e, t) {
95
+ const n = e.endsWith("/") ? e.slice(0, -1) : e, s = j(t, "/");
96
+ return `${n}${s.startsWith("/") ? s : `/${s}`}`;
97
+ }
98
+ async function L(e) {
99
+ const t = e.fetchImpl ?? fetch, n = b(e.baseUrl, "/config/site.json"), s = await t(n, { cache: "no-store" });
100
+ if (!s.ok)
101
+ throw new Error(`Static site config unavailable: ${s.status}`);
102
+ const r = await s.json().catch(() => null), a = C(r);
103
+ if (!a)
104
+ throw new Error("Static site config is invalid.");
105
+ const c = await Promise.all(
106
+ e.knownSlugs.map(async (g) => {
107
+ const l = b(e.baseUrl, `/pages/${S(g)}.json`), u = await t(l, { cache: "no-store" });
108
+ if (!u.ok)
109
+ throw new Error(`Static page unavailable for slug "${g}": ${u.status}`);
110
+ return [g, await u.json().catch(() => null)];
111
+ })
112
+ ), i = E(Object.fromEntries(c));
113
+ if (Object.keys(i).length === 0)
114
+ throw new Error("Static published pages are empty.");
115
+ return { pages: i, siteConfig: a };
116
+ }
117
+ function k(e) {
118
+ const t = e.trim().replace(/^\/+/, "").replace(/\.json$/i, "").replace(/\/+$/, "") || "home";
119
+ return t === "home" ? "/" : `/${t}`;
120
+ }
121
+ function R(e, t) {
122
+ if (t != null && t.trim()) return t.trim();
123
+ const n = typeof e.slug == "string" ? e.slug.trim() : "";
124
+ return n.includes("[") || n ? n : "home";
125
+ }
126
+ async function M(e) {
127
+ var a, c;
128
+ const t = e.fetchImpl ?? fetch, n = k(e.slug), s = new URLSearchParams({ path: n });
129
+ let r = "Live render unavailable";
130
+ for (const i of e.apiBases) {
131
+ const g = `${i.replace(/\/+$/, "")}/render?${s.toString()}`, l = await t(g, {
132
+ method: "GET",
133
+ cache: "no-store",
134
+ headers: { Authorization: `Bearer ${e.apiKey}` },
135
+ signal: e.signal
136
+ });
137
+ if (!(l.headers.get("content-type") || "").toLowerCase().includes("application/json")) {
138
+ r = `Live render unavailable: non-JSON from ${i}`;
139
+ continue;
140
+ }
141
+ const o = await l.json().catch(() => ({}));
142
+ if (!l.ok || !o.ok || !o.page || !((a = o.context) != null && a.siteConfig)) {
143
+ r = `Live render unavailable: ${o.error || o.code || `HTTP_${l.status}`}`;
144
+ continue;
145
+ }
146
+ return {
147
+ pages: { [R(o.page, (c = o.route) == null ? void 0 : c.template)]: o.page },
148
+ siteConfig: o.context.siteConfig,
149
+ menuConfig: o.context.menuConfig ?? {}
150
+ };
151
+ }
152
+ throw new Error(r);
153
+ }
154
+ function _(e) {
155
+ return e ? { status: 200, body: e.page } : { status: 404, body: { error: "Page JSON not found" } };
156
+ }
157
+ function D() {
158
+ return [
159
+ {
160
+ source: "/pages/:path*.json",
161
+ destination: "/api/public-page/:path*"
162
+ },
163
+ {
164
+ source: "/:path*.json",
165
+ destination: "/api/public-page/:path*"
166
+ }
167
+ ];
168
+ }
169
+ function A(e) {
170
+ let t = e.trim();
171
+ return t.startsWith("/") && (t = t.slice(1)), t.startsWith("pages/") && (t = t.slice(6)), t.endsWith(".json") && (t = t.slice(0, -5)), t || "home";
172
+ }
173
+ function N(e) {
174
+ const t = A(e.slug), n = w({
175
+ slug: t,
176
+ pages: e.bundle.pages,
177
+ siteConfig: e.bundle.siteConfig,
178
+ themeConfig: e.bundle.themeConfig,
179
+ menuConfig: e.bundle.menuConfig,
180
+ collections: e.bundle.collections,
181
+ collectionSchemas: e.bundle.collectionSchemas,
182
+ refDocuments: e.bundle.refDocuments
183
+ });
184
+ if (!n) return null;
185
+ const s = e.bundle.pages[n.pageMatch.registrySlug];
186
+ return {
187
+ page: O(
188
+ n.page,
189
+ s,
190
+ n.pageMatch.params ?? {}
191
+ ),
192
+ registrySlug: n.pageMatch.registrySlug,
193
+ params: n.pageMatch.params
194
+ };
195
+ }
196
+ export {
197
+ V as ADMIN_SESSION_COOKIE,
198
+ X as ADMIN_SESSION_MAX_AGE,
199
+ x as MAX_UPLOAD_BYTES,
200
+ B as OLONJS_NEXT_SERVER,
201
+ O as applyDevSliceFilters,
202
+ G as authorizeAdminRequest,
203
+ K as buildAdminSessionCookie,
204
+ D as buildPublicPageJsonRewrites,
205
+ _ as createPublicPageJsonHttpResult,
206
+ W as listLocalImages,
207
+ M as loadLivePublicPageContent,
208
+ L as loadPublishedStaticContent,
209
+ I as loadVisitorPage,
210
+ A as normalizePublicPageSlug,
211
+ S as normalizePublishedSlug,
212
+ Y as parseCookieHeader,
213
+ F as resolveLocalDataRoots,
214
+ N as resolvePublicPageJson,
215
+ H as safeDataSlugPath,
216
+ T as saveProjectStateToDisk,
217
+ U as saveUploadedImage,
218
+ k as slugToRenderPath,
219
+ Q as verifyAdminJwt
220
+ };
package/package.json ADDED
@@ -0,0 +1,87 @@
1
+ {
2
+ "name": "@olonjs/next",
3
+ "version": "0.0.2",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Next.js App Router host binding for OlonJS — RSC visitors + admin client island (ADR-0017).",
7
+ "author": "JsonPages Team",
8
+ "license": "MIT",
9
+ "homepage": "https://github.com/olonjs/npm-jpcore#readme",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/olonjs/npm-jpcore.git",
13
+ "directory": "packages/next"
14
+ },
15
+ "bugs": {
16
+ "url": "https://github.com/olonjs/npm-jpcore/issues"
17
+ },
18
+ "publishConfig": {
19
+ "access": "public"
20
+ },
21
+ "main": "./dist/index.js",
22
+ "module": "./dist/index.js",
23
+ "types": "./dist/index.d.ts",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "import": "./dist/index.js",
28
+ "default": "./dist/index.js"
29
+ },
30
+ "./server": {
31
+ "types": "./dist/server.d.ts",
32
+ "import": "./dist/server.js",
33
+ "default": "./dist/server.js"
34
+ },
35
+ "./admin-gate": {
36
+ "types": "./dist/admin-gate.d.ts",
37
+ "import": "./dist/admin-gate.js",
38
+ "default": "./dist/admin-gate.js"
39
+ },
40
+ "./client": {
41
+ "types": "./dist/client.d.ts",
42
+ "import": "./dist/client.js",
43
+ "default": "./dist/client.js"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "files": [
48
+ "dist"
49
+ ],
50
+ "sideEffects": false,
51
+ "scripts": {
52
+ "build": "vite build",
53
+ "test": "vitest run"
54
+ },
55
+ "peerDependencies": {
56
+ "@olonjs/core": "^1.1.0",
57
+ "@olonjs/react": "^0.1.0",
58
+ "next": "^15.0.0 || ^16.0.0",
59
+ "react": "^19.0.0",
60
+ "react-dom": "^19.0.0",
61
+ "react-router-dom": "^6.29.0 || ^7.0.0"
62
+ },
63
+ "dependencies": {
64
+ "@olonjs/core": "^1.1.22"
65
+ },
66
+ "peerDependenciesMeta": {
67
+ "@olonjs/react": {
68
+ "optional": true
69
+ },
70
+ "next": {
71
+ "optional": true
72
+ },
73
+ "react-router-dom": {
74
+ "optional": true
75
+ }
76
+ },
77
+ "devDependencies": {
78
+ "@types/node": "^22.13.1",
79
+ "@types/react": "^19.0.0",
80
+ "@types/react-dom": "^19.0.0",
81
+ "@vitejs/plugin-react": "^4.0.0",
82
+ "typescript": "^5.7.3",
83
+ "vite": "^6.0.0",
84
+ "vite-plugin-dts": "^4.0.0",
85
+ "vitest": "^3.0.0"
86
+ }
87
+ }