@avocadostudio-ai/site-sdk 0.6.0 → 0.8.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/create-site-page.js +47 -84
- package/dist/draft-common.d.ts +1 -0
- package/dist/draft-common.js +13 -0
- package/dist/draft-context-core.d.ts +1 -1
- package/dist/draft-context-core.js +2 -3
- package/dist/draft-context.d.ts +1 -1
- package/dist/draft-context.js +1 -3
- package/dist/draft-routes.d.ts +7 -0
- package/dist/draft-routes.js +8 -3
- package/dist/draft.d.ts +1 -0
- package/dist/draft.js +3 -0
- package/dist/editor-api-handler-core.d.ts +85 -0
- package/dist/editor-api-handler-core.js +101 -0
- package/dist/editor-api-handler.d.ts +7 -49
- package/dist/editor-api-handler.js +12 -66
- package/dist/editor-render.d.ts +43 -0
- package/dist/editor-render.js +71 -0
- package/dist/editor-rewrite-core.d.ts +71 -0
- package/dist/editor-rewrite-core.js +37 -0
- package/dist/lens/create-lens.d.ts +48 -0
- package/dist/lens/create-lens.js +349 -0
- package/dist/lens/index.d.ts +7 -0
- package/dist/lens/index.js +25 -0
- package/dist/lens/register.d.ts +32 -0
- package/dist/lens/register.js +322 -0
- package/dist/lens/sanity.d.ts +28 -0
- package/dist/lens/sanity.js +187 -0
- package/dist/lens/scalar-codecs.d.ts +26 -0
- package/dist/lens/scalar-codecs.js +92 -0
- package/dist/lens/storyblok.d.ts +28 -0
- package/dist/lens/storyblok.js +232 -0
- package/dist/lens/types.d.ts +279 -0
- package/dist/lens/types.js +28 -0
- package/dist/markers.d.ts +37 -1
- package/dist/markers.js +32 -2
- package/dist/page-metadata.d.ts +21 -0
- package/dist/page-metadata.js +64 -0
- package/dist/proxy.d.ts +9 -30
- package/dist/proxy.js +18 -8
- package/dist/resolve-page-render.d.ts +94 -0
- package/dist/resolve-page-render.js +80 -0
- package/dist/routes-core.d.ts +12 -0
- package/dist/routes-core.js +24 -0
- package/package.json +35 -14
- package/dist/cli/register-notice.test.d.ts +0 -1
- package/dist/cli/register-notice.test.js +0 -21
- package/dist/draft-context-core.test.d.ts +0 -10
- package/dist/draft-context-core.test.js +0 -146
- package/dist/draft-fetch.test.d.ts +0 -1
- package/dist/draft-fetch.test.js +0 -153
- package/dist/editor-api-blocks-catalogue.test.d.ts +0 -1
- package/dist/editor-api-blocks-catalogue.test.js +0 -73
- package/dist/editor-cors.test.d.ts +0 -1
- package/dist/editor-cors.test.js +0 -85
- package/dist/manifest-utils.test.d.ts +0 -1
- package/dist/manifest-utils.test.js +0 -72
- package/dist/next-config.test.d.ts +0 -1
- package/dist/next-config.test.js +0 -393
- package/dist/page-metadata.test.d.ts +0 -1
- package/dist/page-metadata.test.js +0 -105
- package/dist/proxy.test.d.ts +0 -1
- package/dist/proxy.test.js +0 -182
- package/dist/publish/field-diff.test.d.ts +0 -1
- package/dist/publish/field-diff.test.js +0 -350
package/dist/page-metadata.js
CHANGED
|
@@ -108,3 +108,67 @@ export function buildPageMetadata(page, options = {}) {
|
|
|
108
108
|
...(options.canonical ? { alternates: { canonical: options.canonical } } : {}),
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
|
+
/** `&`, `<`, `>` and `"` inside an attribute value, which a title routinely contains. */
|
|
112
|
+
function escapeAttribute(value) {
|
|
113
|
+
return value
|
|
114
|
+
.replace(/&/g, "&")
|
|
115
|
+
.replace(/</g, "<")
|
|
116
|
+
.replace(/>/g, ">")
|
|
117
|
+
.replace(/"/g, """);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Render {@link PageMetadata} as the head tags it describes.
|
|
121
|
+
*
|
|
122
|
+
* Next consumes the object directly — a route exports `metadata` and the
|
|
123
|
+
* framework emits the tags — so nothing in this repo needed this until now.
|
|
124
|
+
* Every other host writes its own `<head>`, and an integrator left to do that
|
|
125
|
+
* by hand reproduces a subset: the title and description usually, Open Graph
|
|
126
|
+
* sometimes, `twitter:card` almost never, and the canonical link not at all.
|
|
127
|
+
* That is how `test:build` came to exist — three of those omissions had already
|
|
128
|
+
* shipped from the one route that built its head by hand.
|
|
129
|
+
*
|
|
130
|
+
* Returns a string because the alternative is a shape per templating language.
|
|
131
|
+
* The values are attribute-escaped; the caller inserts it as raw HTML
|
|
132
|
+
* (`set:html` in Astro, `v-html` in Vue, `{@html}` in Svelte).
|
|
133
|
+
*
|
|
134
|
+
* `og:image` and `twitter:image` are emitted exactly as given. A relative path
|
|
135
|
+
* is legal in the document but most crawlers will not resolve it, so pass
|
|
136
|
+
* absolute URLs — the same rule that applies to `buildPageMetadata`'s
|
|
137
|
+
* `canonical`.
|
|
138
|
+
*/
|
|
139
|
+
export function renderPageMetadata(metadata) {
|
|
140
|
+
const tags = [];
|
|
141
|
+
const meta = (attr, key, value) => {
|
|
142
|
+
if (value)
|
|
143
|
+
tags.push(`<meta ${attr}="${escapeAttribute(key)}" content="${escapeAttribute(value)}" />`);
|
|
144
|
+
};
|
|
145
|
+
if (metadata.title)
|
|
146
|
+
tags.push(`<title>${escapeAttribute(metadata.title)}</title>`);
|
|
147
|
+
meta("name", "description", metadata.description);
|
|
148
|
+
if (metadata.alternates?.canonical) {
|
|
149
|
+
tags.push(`<link rel="canonical" href="${escapeAttribute(metadata.alternates.canonical)}" />`);
|
|
150
|
+
}
|
|
151
|
+
const og = metadata.openGraph;
|
|
152
|
+
if (og) {
|
|
153
|
+
meta("property", "og:title", og.title);
|
|
154
|
+
meta("property", "og:description", og.description);
|
|
155
|
+
meta("property", "og:type", og.type);
|
|
156
|
+
meta("property", "og:site_name", og.siteName);
|
|
157
|
+
meta("property", "og:url", og.url);
|
|
158
|
+
for (const image of og.images ?? [])
|
|
159
|
+
meta("property", "og:image", image);
|
|
160
|
+
}
|
|
161
|
+
const twitter = metadata.twitter;
|
|
162
|
+
if (twitter) {
|
|
163
|
+
meta("name", "twitter:card", twitter.card);
|
|
164
|
+
meta("name", "twitter:title", twitter.title);
|
|
165
|
+
meta("name", "twitter:description", twitter.description);
|
|
166
|
+
for (const image of twitter.images ?? [])
|
|
167
|
+
meta("name", "twitter:image", image);
|
|
168
|
+
}
|
|
169
|
+
if (metadata.robots) {
|
|
170
|
+
const directives = [metadata.robots.index ? "index" : "noindex", metadata.robots.follow ? "follow" : "nofollow"];
|
|
171
|
+
meta("name", "robots", directives.join(", "));
|
|
172
|
+
}
|
|
173
|
+
return tags.join("\n");
|
|
174
|
+
}
|
package/dist/proxy.d.ts
CHANGED
|
@@ -1,37 +1,16 @@
|
|
|
1
1
|
import { NextResponse, type NextRequest } from "next/server";
|
|
2
|
+
import { type EditorRewriteOptions } from "./editor-rewrite-core.ts";
|
|
2
3
|
export { DEFAULT_PREVIEW_ROUTE, buildEditorMatcher } from "./editor-matcher.ts";
|
|
4
|
+
export { decideEditorRewrite } from "./editor-rewrite-core.ts";
|
|
5
|
+
export type { EditorRewriteDecision, EditorRewriteOptions } from "./editor-rewrite-core.ts";
|
|
3
6
|
/**
|
|
4
7
|
* Options for the editor proxy factory.
|
|
8
|
+
*
|
|
9
|
+
* `previewRoute`, `editorParam` and `draftCookie` are documented on
|
|
10
|
+
* {@link EditorRewriteOptions}, which every host shares; `trailingSlash` below
|
|
11
|
+
* is Next's alone.
|
|
5
12
|
*/
|
|
6
|
-
export type EditorProxyOptions = {
|
|
7
|
-
/**
|
|
8
|
-
* The internal route prefix that the dynamic editor/draft page lives under.
|
|
9
|
-
* @default "/preview-draft"
|
|
10
|
-
*/
|
|
11
|
-
previewRoute?: string;
|
|
12
|
-
/**
|
|
13
|
-
* Query parameter that signals an editor iframe request.
|
|
14
|
-
* @default "__editor"
|
|
15
|
-
*/
|
|
16
|
-
editorParam?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Name of the Next.js draft-mode bypass cookie, or `false` to ignore cookies
|
|
19
|
-
* entirely and key the rewrite on {@link EditorProxyOptions.editorParam} alone.
|
|
20
|
-
*
|
|
21
|
-
* The cookie is how navigation *inside* the editor iframe stays in draft mode:
|
|
22
|
-
* a link click carries no `__editor=1`, so without it the second page a user
|
|
23
|
-
* visits renders published content.
|
|
24
|
-
*
|
|
25
|
-
* But the cookie is Next's own, and it is not Avocado's to claim. Any other
|
|
26
|
-
* feature that calls `draftMode().enable()` sets the same
|
|
27
|
-
* `__prerender_bypass` — Sanity's Presentation tool and Contentful's live
|
|
28
|
-
* preview both do — and every one of *their* preview requests then lands on
|
|
29
|
-
* Avocado's preview route. On a site that already had Draft Mode before it had
|
|
30
|
-
* Avocado, pass `draftCookie: false` and the two stop fighting over it.
|
|
31
|
-
*
|
|
32
|
-
* @default "__prerender_bypass"
|
|
33
|
-
*/
|
|
34
|
-
draftCookie?: string | false;
|
|
13
|
+
export type EditorProxyOptions = EditorRewriteOptions & {
|
|
35
14
|
/**
|
|
36
15
|
* Set this to `true` on a site whose `next.config` sets `trailingSlash: true`.
|
|
37
16
|
*
|
|
@@ -118,4 +97,4 @@ export declare function trailingSlashRedirect(request: NextRequest): NextRespons
|
|
|
118
97
|
* The half of `createEditorProxy` a site with its own middleware needs; see
|
|
119
98
|
* {@link trailingSlashRedirect} for the composition.
|
|
120
99
|
*/
|
|
121
|
-
export declare function editorPreviewRewrite(request: NextRequest, options?:
|
|
100
|
+
export declare function editorPreviewRewrite(request: NextRequest, options?: EditorRewriteOptions): NextResponse | null;
|
package/dist/proxy.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { NextResponse } from "next/server";
|
|
2
2
|
import { DEFAULT_PREVIEW_ROUTE, buildEditorMatcher } from "./editor-matcher.js";
|
|
3
|
+
import { decideEditorRewrite } from "./editor-rewrite-core.js";
|
|
3
4
|
export { DEFAULT_PREVIEW_ROUTE, buildEditorMatcher } from "./editor-matcher.js";
|
|
5
|
+
export { decideEditorRewrite } from "./editor-rewrite-core.js";
|
|
4
6
|
/**
|
|
5
7
|
* Create a Next.js proxy function that rewrites editor/draft requests
|
|
6
8
|
* to a dynamic preview route, keeping the main page route fully static.
|
|
@@ -109,14 +111,22 @@ export function trailingSlashRedirect(request) {
|
|
|
109
111
|
* {@link trailingSlashRedirect} for the composition.
|
|
110
112
|
*/
|
|
111
113
|
export function editorPreviewRewrite(request, options) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
114
|
+
/*
|
|
115
|
+
* `nextUrl`, not `new URL(request.url)`: it has `basePath` stripped, and the
|
|
116
|
+
* preview prefix has to be prepended to the path Next routes on. The decision
|
|
117
|
+
* itself is shared with every other host — see `decideEditorRewrite`.
|
|
118
|
+
*/
|
|
119
|
+
const decision = decideEditorRewrite({
|
|
120
|
+
pathname: request.nextUrl.pathname,
|
|
121
|
+
searchParams: request.nextUrl.searchParams,
|
|
122
|
+
hasCookie: (name) => request.cookies.has(name),
|
|
123
|
+
}, options);
|
|
124
|
+
if (decision.kind === "pass")
|
|
118
125
|
return null;
|
|
119
126
|
const url = request.nextUrl.clone();
|
|
120
|
-
url.pathname =
|
|
121
|
-
|
|
127
|
+
url.pathname = decision.pathname;
|
|
128
|
+
const headers = new Headers(request.headers);
|
|
129
|
+
for (const [name, value] of Object.entries(decision.headers))
|
|
130
|
+
headers.set(name, value);
|
|
131
|
+
return NextResponse.rewrite(url, { request: { headers } });
|
|
122
132
|
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { PageDoc, SiteConfig } from "@avocadostudio-ai/shared";
|
|
2
|
+
import type { SearchParamsRecord } from "./types.ts";
|
|
3
|
+
import { type DraftModeAdapter } from "./draft-context-core.ts";
|
|
4
|
+
import { type NavItem } from "./navigation.ts";
|
|
5
|
+
import type { SiteHeaderBlock } from "./navigation.ts";
|
|
6
|
+
export type PageSourceConfig = {
|
|
7
|
+
siteId: string;
|
|
8
|
+
/** Orchestrator draft session. @default "dev" */
|
|
9
|
+
session?: string;
|
|
10
|
+
getPage: (slug: string) => Promise<PageDoc | null> | PageDoc | null;
|
|
11
|
+
getSlugs: () => Promise<string[]> | string[];
|
|
12
|
+
getSiteConfig?: () => Promise<SiteConfig | undefined> | SiteConfig | undefined;
|
|
13
|
+
/** @default "/logo.svg" */
|
|
14
|
+
defaultLogo?: string;
|
|
15
|
+
};
|
|
16
|
+
/** The editor session this render belongs to, when it is one. */
|
|
17
|
+
export type EditorRenderContext = {
|
|
18
|
+
session: string;
|
|
19
|
+
siteId: string;
|
|
20
|
+
/** `postMessage` target and the frame permitted to drive inline edits. Empty when none is trusted. */
|
|
21
|
+
editorOrigin: string;
|
|
22
|
+
/**
|
|
23
|
+
* `?session=…&siteId=…&editorOrigin=…`, to append to every link the preview
|
|
24
|
+
* renders. Without it the first in-preview navigation lands on the published
|
|
25
|
+
* page — the editor's own nav goes dead one click in.
|
|
26
|
+
*/
|
|
27
|
+
editorQuery: string;
|
|
28
|
+
};
|
|
29
|
+
/** Site chrome, already resolved against the right slug list. */
|
|
30
|
+
export type PageChrome = {
|
|
31
|
+
navItems: NavItem[];
|
|
32
|
+
siteName: string;
|
|
33
|
+
siteLogo: string;
|
|
34
|
+
homeHref: string;
|
|
35
|
+
/** The header as a block, for a host that renders Avocado's own chrome. */
|
|
36
|
+
header: SiteHeaderBlock;
|
|
37
|
+
siteConfig: SiteConfig;
|
|
38
|
+
};
|
|
39
|
+
export type PageRenderOutcome =
|
|
40
|
+
/** Render this page. `editor` is null for a published render. */
|
|
41
|
+
{
|
|
42
|
+
kind: "page";
|
|
43
|
+
slug: string;
|
|
44
|
+
page: PageDoc;
|
|
45
|
+
editor: EditorRenderContext | null;
|
|
46
|
+
chrome: PageChrome;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* No such page. The host must answer with a real 404 status — a rendered
|
|
50
|
+
* "404" body under HTTP 200 is a soft 404, which crawlers index as a page.
|
|
51
|
+
*/
|
|
52
|
+
| {
|
|
53
|
+
kind: "not-found";
|
|
54
|
+
slug: string;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* An editor render whose draft could not be fetched and which has no
|
|
58
|
+
* published fallback. Distinct from `not-found` because the page may well
|
|
59
|
+
* exist: the orchestrator is unreachable, and telling an editor "404" sends
|
|
60
|
+
* them looking for a content bug that is not there.
|
|
61
|
+
*/
|
|
62
|
+
| {
|
|
63
|
+
kind: "draft-unavailable";
|
|
64
|
+
slug: string;
|
|
65
|
+
editor: EditorRenderContext;
|
|
66
|
+
chrome: PageChrome;
|
|
67
|
+
};
|
|
68
|
+
export type ResolvePageRenderInput = {
|
|
69
|
+
slug: string;
|
|
70
|
+
config: PageSourceConfig;
|
|
71
|
+
/**
|
|
72
|
+
* - `auto`: draft mode or a resolvable editor context switches to the editor render.
|
|
73
|
+
* - `static`: published content only. Search params are not read.
|
|
74
|
+
* - `preview`: always the editor render.
|
|
75
|
+
* @default "auto"
|
|
76
|
+
*/
|
|
77
|
+
mode?: "auto" | "static" | "preview";
|
|
78
|
+
/** Required unless `mode` is `"static"`. */
|
|
79
|
+
search?: SearchParamsRecord;
|
|
80
|
+
/**
|
|
81
|
+
* How this runtime reports draft mode and reads cookies. Omit under
|
|
82
|
+
* `mode: "static"`; omit under `"preview"` only if the host has no draft-mode
|
|
83
|
+
* concept, in which case authorization rests on the `secret` query parameter.
|
|
84
|
+
*/
|
|
85
|
+
adapter?: DraftModeAdapter;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* Resolve what a page render should produce, without producing it.
|
|
89
|
+
*
|
|
90
|
+
* The published path reads the CMS. The editor path prefers the orchestrator's
|
|
91
|
+
* draft and falls back to the CMS for each of page and slug list independently
|
|
92
|
+
* — a session that has edited one page still needs the other twenty in its nav.
|
|
93
|
+
*/
|
|
94
|
+
export declare function resolvePageRender(input: ResolvePageRenderInput): Promise<PageRenderOutcome>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { resolveDraftContextCore } from "./draft-context-core.js";
|
|
2
|
+
import { fetchEditorPage, fetchEditorSlugs } from "./draft-fetch.js";
|
|
3
|
+
import { buildNavItems, buildSiteHeaderBlock } from "./navigation.js";
|
|
4
|
+
const NO_DRAFT_MODE = { isDraftMode: false, getCookie: () => undefined };
|
|
5
|
+
async function buildChrome(slug, navSlugs, siteConfig, config, editorQuery) {
|
|
6
|
+
const { navItems, siteName, siteLogo, homeHref } = buildNavItems({
|
|
7
|
+
navSlugs,
|
|
8
|
+
currentSlug: slug,
|
|
9
|
+
siteConfig,
|
|
10
|
+
siteId: config.siteId,
|
|
11
|
+
editorQuery,
|
|
12
|
+
defaultLogo: config.defaultLogo ?? "/logo.svg",
|
|
13
|
+
});
|
|
14
|
+
return {
|
|
15
|
+
navItems,
|
|
16
|
+
siteName,
|
|
17
|
+
siteLogo,
|
|
18
|
+
homeHref,
|
|
19
|
+
header: buildSiteHeaderBlock({ navItems, siteName, siteLogo, activePath: slug }),
|
|
20
|
+
siteConfig,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Resolve what a page render should produce, without producing it.
|
|
25
|
+
*
|
|
26
|
+
* The published path reads the CMS. The editor path prefers the orchestrator's
|
|
27
|
+
* draft and falls back to the CMS for each of page and slug list independently
|
|
28
|
+
* — a session that has edited one page still needs the other twenty in its nav.
|
|
29
|
+
*/
|
|
30
|
+
export async function resolvePageRender(input) {
|
|
31
|
+
const { slug, config } = input;
|
|
32
|
+
const mode = input.mode ?? "auto";
|
|
33
|
+
const defaultSession = config.session ?? "dev";
|
|
34
|
+
const getSiteConfig = () => config.getSiteConfig ? Promise.resolve(config.getSiteConfig()) : Promise.resolve({});
|
|
35
|
+
const editorCtx = mode === "static"
|
|
36
|
+
? null
|
|
37
|
+
: await resolveDraftContextCore(input.search ?? {}, input.adapter ?? NO_DRAFT_MODE, {
|
|
38
|
+
defaultSession,
|
|
39
|
+
defaultSiteId: config.siteId,
|
|
40
|
+
});
|
|
41
|
+
const isEditorRender = mode === "preview" || (mode === "auto" && (input.adapter?.isDraftMode === true || Boolean(editorCtx)));
|
|
42
|
+
if (!isEditorRender) {
|
|
43
|
+
const [page, navSlugs, siteConfig] = await Promise.all([
|
|
44
|
+
config.getPage(slug),
|
|
45
|
+
config.getSlugs(),
|
|
46
|
+
getSiteConfig(),
|
|
47
|
+
]);
|
|
48
|
+
// Before the chrome is built: none of it survives a 404.
|
|
49
|
+
if (!page)
|
|
50
|
+
return { kind: "not-found", slug };
|
|
51
|
+
return {
|
|
52
|
+
kind: "page",
|
|
53
|
+
slug,
|
|
54
|
+
page,
|
|
55
|
+
editor: null,
|
|
56
|
+
chrome: await buildChrome(slug, navSlugs, siteConfig ?? {}, config, ""),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const session = editorCtx?.session ?? defaultSession;
|
|
60
|
+
const currentSiteId = editorCtx?.siteId ?? config.siteId;
|
|
61
|
+
const editorOrigin = editorCtx?.editorOrigin ?? "";
|
|
62
|
+
const [page, navSlugs, siteConfig] = await Promise.all([
|
|
63
|
+
fetchEditorPage(slug, session, currentSiteId).then((p) => p ?? config.getPage(slug)),
|
|
64
|
+
fetchEditorSlugs(session, currentSiteId).then((s) => (s.length > 0 ? s : config.getSlugs())),
|
|
65
|
+
getSiteConfig(),
|
|
66
|
+
]);
|
|
67
|
+
const params = new URLSearchParams({ session, siteId: currentSiteId });
|
|
68
|
+
if (editorOrigin)
|
|
69
|
+
params.set("editorOrigin", editorOrigin);
|
|
70
|
+
const editor = {
|
|
71
|
+
session,
|
|
72
|
+
siteId: currentSiteId,
|
|
73
|
+
editorOrigin,
|
|
74
|
+
editorQuery: `?${params.toString()}`,
|
|
75
|
+
};
|
|
76
|
+
const chrome = await buildChrome(slug, navSlugs, siteConfig ?? {}, config, editor.editorQuery);
|
|
77
|
+
if (!page)
|
|
78
|
+
return { kind: "draft-unavailable", slug, editor, chrome };
|
|
79
|
+
return { kind: "page", slug, page, editor, chrome };
|
|
80
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { createDraftEnableHandlerCore, createDraftDisableHandlerCore } from "./draft-routes-core.ts";
|
|
2
|
+
export type { DraftRouteAdapter } from "./draft-routes-core.ts";
|
|
3
|
+
export { createEditorApiHandlerCore } from "./editor-api-handler-core.ts";
|
|
4
|
+
export type { EditorApiHandlerConfig, EditorApiCoreConfig } from "./editor-api-handler-core.ts";
|
|
5
|
+
export { createBlocksHandler, createPagesHandler, createPublishHandler } from "./editor-routes.ts";
|
|
6
|
+
export type { OnPublishFn, InlineAsset, PublishContext } from "./editor-routes.ts";
|
|
7
|
+
export { isSafeImageUrl, createImageResolver } from "./publish-utils.ts";
|
|
8
|
+
export type { ImageUploader } from "./publish-utils.ts";
|
|
9
|
+
export { createRevalidateHandler } from "./revalidate-handler.ts";
|
|
10
|
+
export type { RevalidateHandlerConfig } from "./revalidate-handler.ts";
|
|
11
|
+
export { getManifestImageFields } from "./manifest-utils.ts";
|
|
12
|
+
export { DRAFT_SESSION_COOKIE, DRAFT_SITE_COOKIE, EDITOR_ORIGIN_COOKIE, normalizeOrigin, single, } from "./draft-common.ts";
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* The editor API, for a host that is not Next.
|
|
3
|
+
*
|
|
4
|
+
* This is `./routes` with the two Next-shaped pieces replaced by their cores:
|
|
5
|
+
* the draft handlers take an adapter instead of calling `draftMode()`, and the
|
|
6
|
+
* catch-all handler takes the matched path instead of a Next route context.
|
|
7
|
+
* Everything else in `./routes` was already portable and is re-exported here
|
|
8
|
+
* unchanged — the same functions, not parallel implementations.
|
|
9
|
+
*/
|
|
10
|
+
// Draft route cores — supply a DraftRouteAdapter for your runtime.
|
|
11
|
+
export { createDraftEnableHandlerCore, createDraftDisableHandlerCore } from "./draft-routes-core.js";
|
|
12
|
+
// Catch-all editor API core.
|
|
13
|
+
export { createEditorApiHandlerCore } from "./editor-api-handler-core.js";
|
|
14
|
+
// Individual editor route handlers — already framework-free.
|
|
15
|
+
export { createBlocksHandler, createPagesHandler, createPublishHandler } from "./editor-routes.js";
|
|
16
|
+
// Publish utilities (SSRF check, image resolution).
|
|
17
|
+
export { isSafeImageUrl, createImageResolver } from "./publish-utils.js";
|
|
18
|
+
// Revalidation handler factory — takes a `revalidate` callback, so the host's
|
|
19
|
+
// cache API never appears here.
|
|
20
|
+
export { createRevalidateHandler } from "./revalidate-handler.js";
|
|
21
|
+
// Manifest utilities (derive image fields from block manifest).
|
|
22
|
+
export { getManifestImageFields } from "./manifest-utils.js";
|
|
23
|
+
// Cookie names and the query-value helper the draft contract is written in.
|
|
24
|
+
export { DRAFT_SESSION_COOKIE, DRAFT_SITE_COOKIE, EDITOR_ORIGIN_COOKIE, normalizeOrigin, single, } from "./draft-common.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@avocadostudio-ai/site-sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -26,6 +26,21 @@
|
|
|
26
26
|
"import": "./dist/markers.js",
|
|
27
27
|
"default": "./dist/markers.js"
|
|
28
28
|
},
|
|
29
|
+
"./lens": {
|
|
30
|
+
"types": "./dist/lens/index.d.ts",
|
|
31
|
+
"import": "./dist/lens/index.js",
|
|
32
|
+
"default": "./dist/lens/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./lens/storyblok": {
|
|
35
|
+
"types": "./dist/lens/storyblok.d.ts",
|
|
36
|
+
"import": "./dist/lens/storyblok.js",
|
|
37
|
+
"default": "./dist/lens/storyblok.js"
|
|
38
|
+
},
|
|
39
|
+
"./lens/sanity": {
|
|
40
|
+
"types": "./dist/lens/sanity.d.ts",
|
|
41
|
+
"import": "./dist/lens/sanity.js",
|
|
42
|
+
"default": "./dist/lens/sanity.js"
|
|
43
|
+
},
|
|
29
44
|
"./blocks": {
|
|
30
45
|
"types": "./dist/blocks.d.ts",
|
|
31
46
|
"import": "./dist/blocks.js",
|
|
@@ -52,9 +67,9 @@
|
|
|
52
67
|
"default": "./dist/draft-context-core.js"
|
|
53
68
|
},
|
|
54
69
|
"./routes/core": {
|
|
55
|
-
"types": "./dist/
|
|
56
|
-
"import": "./dist/
|
|
57
|
-
"default": "./dist/
|
|
70
|
+
"types": "./dist/routes-core.d.ts",
|
|
71
|
+
"import": "./dist/routes-core.js",
|
|
72
|
+
"default": "./dist/routes-core.js"
|
|
58
73
|
},
|
|
59
74
|
"./publish-handlers/json-file": {
|
|
60
75
|
"types": "./dist/publish-handlers/json-file.d.ts",
|
|
@@ -76,6 +91,11 @@
|
|
|
76
91
|
"import": "./dist/proxy.js",
|
|
77
92
|
"default": "./dist/proxy.js"
|
|
78
93
|
},
|
|
94
|
+
"./proxy/core": {
|
|
95
|
+
"types": "./dist/editor-rewrite-core.d.ts",
|
|
96
|
+
"import": "./dist/editor-rewrite-core.js",
|
|
97
|
+
"default": "./dist/editor-rewrite-core.js"
|
|
98
|
+
},
|
|
79
99
|
"./matcher": {
|
|
80
100
|
"types": "./dist/editor-matcher.d.ts",
|
|
81
101
|
"import": "./dist/editor-matcher.js",
|
|
@@ -91,6 +111,11 @@
|
|
|
91
111
|
"import": "./dist/create-site-page.js",
|
|
92
112
|
"default": "./dist/create-site-page.js"
|
|
93
113
|
},
|
|
114
|
+
"./page/core": {
|
|
115
|
+
"types": "./dist/resolve-page-render.d.ts",
|
|
116
|
+
"import": "./dist/resolve-page-render.js",
|
|
117
|
+
"default": "./dist/resolve-page-render.js"
|
|
118
|
+
},
|
|
94
119
|
"./seo": {
|
|
95
120
|
"types": "./dist/page-metadata.d.ts",
|
|
96
121
|
"import": "./dist/page-metadata.js",
|
|
@@ -106,11 +131,6 @@
|
|
|
106
131
|
"default": "./next-config.mjs"
|
|
107
132
|
}
|
|
108
133
|
},
|
|
109
|
-
"repository": {
|
|
110
|
-
"type": "git",
|
|
111
|
-
"url": "https://github.com/avocadostudio-ai/avocado.git",
|
|
112
|
-
"directory": "packages/site-sdk"
|
|
113
|
-
},
|
|
114
134
|
"publishConfig": {
|
|
115
135
|
"registry": "https://registry.npmjs.org",
|
|
116
136
|
"access": "public"
|
|
@@ -122,16 +142,17 @@
|
|
|
122
142
|
],
|
|
123
143
|
"dependencies": {
|
|
124
144
|
"zod": "^4.3.6",
|
|
125
|
-
"@avocadostudio-ai/blocks": "^0.
|
|
126
|
-
"@avocadostudio-ai/preview-adapter": "^0.
|
|
127
|
-
"@avocadostudio-ai/
|
|
145
|
+
"@avocadostudio-ai/blocks": "^0.8.0",
|
|
146
|
+
"@avocadostudio-ai/preview-adapter": "^0.8.0",
|
|
147
|
+
"@avocadostudio-ai/richtext": "^0.8.0",
|
|
148
|
+
"@avocadostudio-ai/shared": "^0.8.0"
|
|
128
149
|
},
|
|
129
150
|
"peerDependencies": {
|
|
130
151
|
"next": ">=15.0.0",
|
|
131
152
|
"react": ">=19.0.0",
|
|
132
153
|
"react-dom": ">=19.0.0",
|
|
133
154
|
"better-sqlite3": ">=12.0.0",
|
|
134
|
-
"@avocadostudio-ai/orchestrator-core": "^0.
|
|
155
|
+
"@avocadostudio-ai/orchestrator-core": "^0.8.0"
|
|
135
156
|
},
|
|
136
157
|
"peerDependenciesMeta": {
|
|
137
158
|
"@avocadostudio-ai/orchestrator-core": {
|
|
@@ -161,7 +182,7 @@
|
|
|
161
182
|
"license": "Apache-2.0",
|
|
162
183
|
"homepage": "https://docs.avocadostudio.dev",
|
|
163
184
|
"bugs": {
|
|
164
|
-
"url": "https://
|
|
185
|
+
"url": "https://docs.avocadostudio.dev"
|
|
165
186
|
},
|
|
166
187
|
"scripts": {
|
|
167
188
|
"build": "tsc -p tsconfig.build.json",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import test from "node:test";
|
|
2
|
-
import assert from "node:assert/strict";
|
|
3
|
-
import { orchestratorMismatchNotice } from "./register-notice.js";
|
|
4
|
-
/*
|
|
5
|
-
* `avocado-register` ends with "the site should appear in the dashboard", and
|
|
6
|
-
* for a library-mode site that is a promise it cannot keep: the registration
|
|
7
|
-
* went to the site's own handler, and the editor reads one
|
|
8
|
-
* `VITE_ORCHESTRATOR_URL` baked in at build time. The dashboard then opens on
|
|
9
|
-
* a different registry's sites and reports no problem, because there is none —
|
|
10
|
-
* there are simply two registries.
|
|
11
|
-
*/
|
|
12
|
-
test("the default target says nothing — that is the path almost everyone is on", () => {
|
|
13
|
-
assert.equal(orchestratorMismatchNotice("http://localhost:4200"), null);
|
|
14
|
-
assert.equal(orchestratorMismatchNotice("http://localhost:4200/"), null, "a trailing slash is the same URL");
|
|
15
|
-
});
|
|
16
|
-
test("a library-mode target names the variable the editor needs", () => {
|
|
17
|
-
const notice = orchestratorMismatchNotice("http://localhost:3002/api/avocado");
|
|
18
|
-
assert.ok(notice);
|
|
19
|
-
assert.match(notice, /VITE_ORCHESTRATOR_URL=http:\/\/localhost:3002\/api\/avocado/);
|
|
20
|
-
assert.match(notice, /sites, or none/, "and names the symptom, which is what the reader is looking at");
|
|
21
|
-
});
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Who may see unpublished content, and which frame may drive the editor.
|
|
3
|
-
*
|
|
4
|
-
* Both questions used to be answered by the query string. `?__editor=1` alone
|
|
5
|
-
* turned on the draft content store — in production, on the customer's own
|
|
6
|
-
* domain — and `editorOrigin` was accepted from the same URL after a check that
|
|
7
|
-
* only asked whether it parsed as http(s). One GET rendered another session's
|
|
8
|
-
* drafts and pointed the postMessage bridge wherever the caller said.
|
|
9
|
-
*/
|
|
10
|
-
export {};
|