@avocadostudio-ai/site-sdk 0.24.0 → 0.26.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.
@@ -6,3 +6,30 @@ export declare function resolveEditorContext(searchParams: SearchParamsRecord, o
6
6
  defaultSiteId?: string;
7
7
  defaultEditorOrigin?: string;
8
8
  }): Promise<DraftContext | null>;
9
+ /**
10
+ * The editor context for a preview route, or a 404.
11
+ *
12
+ * A route that exists only to render drafts — `/preview-draft`, `/avocado` —
13
+ * has one legitimate caller: the editor. Call this first, before the route
14
+ * reads anything unpublished:
15
+ *
16
+ * ```tsx
17
+ * export default async function PreviewPage({ params, searchParams }) {
18
+ * const editor = await requireEditorContext(await searchParams)
19
+ * const drafts = await sanity.withConfig({ perspective: "drafts" }).fetch(…) // only now
20
+ * …
21
+ * }
22
+ * ```
23
+ *
24
+ * The order is the point. One integration's preview route fetched every CMS
25
+ * draft with its server read token, then asked whether the request came from
26
+ * the editor — so in production any visitor who guessed the URL saw the
27
+ * site's unreleased pages. `resolveEditorContext` already refused that visitor;
28
+ * nothing stopped the read that came before it.
29
+ *
30
+ * In production a context requires draft mode or a valid `secret` (see
31
+ * `resolveDraftContextCore`); in development, any editor signal. Without one the
32
+ * route answers Next's `notFound()` — the same response a route that does not
33
+ * exist gives, so it confirms nothing.
34
+ */
35
+ export declare function requireEditorContext(searchParams: SearchParamsRecord, options?: Parameters<typeof resolveEditorContext>[1]): Promise<DraftContext>;
@@ -10,3 +10,37 @@ export async function resolveEditorContext(searchParams, options) {
10
10
  };
11
11
  return resolveDraftContextCore(searchParams, adapter, options);
12
12
  }
13
+ /**
14
+ * The editor context for a preview route, or a 404.
15
+ *
16
+ * A route that exists only to render drafts — `/preview-draft`, `/avocado` —
17
+ * has one legitimate caller: the editor. Call this first, before the route
18
+ * reads anything unpublished:
19
+ *
20
+ * ```tsx
21
+ * export default async function PreviewPage({ params, searchParams }) {
22
+ * const editor = await requireEditorContext(await searchParams)
23
+ * const drafts = await sanity.withConfig({ perspective: "drafts" }).fetch(…) // only now
24
+ * …
25
+ * }
26
+ * ```
27
+ *
28
+ * The order is the point. One integration's preview route fetched every CMS
29
+ * draft with its server read token, then asked whether the request came from
30
+ * the editor — so in production any visitor who guessed the URL saw the
31
+ * site's unreleased pages. `resolveEditorContext` already refused that visitor;
32
+ * nothing stopped the read that came before it.
33
+ *
34
+ * In production a context requires draft mode or a valid `secret` (see
35
+ * `resolveDraftContextCore`); in development, any editor signal. Without one the
36
+ * route answers Next's `notFound()` — the same response a route that does not
37
+ * exist gives, so it confirms nothing.
38
+ */
39
+ export async function requireEditorContext(searchParams, options) {
40
+ const context = await resolveEditorContext(searchParams, options);
41
+ if (!context) {
42
+ const { notFound } = await import("next/navigation");
43
+ notFound();
44
+ }
45
+ return context;
46
+ }
package/dist/draft.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { resolveEditorContext, single } from "./draft-context.ts";
1
+ export { requireEditorContext, resolveEditorContext, single } from "./draft-context.ts";
2
2
  export { isEditorRender, isEditorRenderFrom, EDITOR_RENDER_HEADER, getEditorMarkers } from "./editor-render.ts";
3
3
  export { getOrchestratorUrl, fetchEditorPage, fetchEditorSlugs, fetchEditorSiteConfig } from "./draft-fetch.ts";
4
4
  export { DRAFT_SESSION_COOKIE, DRAFT_SITE_COOKIE, EDITOR_ORIGIN_COOKIE } from "./draft-common.ts";
package/dist/draft.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Editor context resolution
2
- export { resolveEditorContext, single } from "./draft-context.js";
2
+ export { requireEditorContext, resolveEditorContext, single } from "./draft-context.js";
3
3
  // Whether this render is the editor's preview — answerable from a layout,
4
4
  // where `resolveEditorContext` cannot reach because layouts get no searchParams.
5
5
  export { isEditorRender, isEditorRenderFrom, EDITOR_RENDER_HEADER, getEditorMarkers } from "./editor-render.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@avocadostudio-ai/site-sdk",
3
- "version": "0.24.0",
3
+ "version": "0.26.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -159,17 +159,17 @@
159
159
  ],
160
160
  "dependencies": {
161
161
  "zod": "^4.3.6",
162
- "@avocadostudio-ai/blocks": "^0.24.0",
163
- "@avocadostudio-ai/preview-adapter": "^0.24.0",
164
- "@avocadostudio-ai/richtext": "^0.24.0",
165
- "@avocadostudio-ai/shared": "^0.24.0"
162
+ "@avocadostudio-ai/blocks": "^0.26.0",
163
+ "@avocadostudio-ai/preview-adapter": "^0.26.0",
164
+ "@avocadostudio-ai/shared": "^0.26.0",
165
+ "@avocadostudio-ai/richtext": "^0.26.0"
166
166
  },
167
167
  "peerDependencies": {
168
168
  "better-sqlite3": ">=12.0.0",
169
169
  "next": ">=15.0.0",
170
170
  "react": ">=19.0.0",
171
171
  "react-dom": ">=19.0.0",
172
- "@avocadostudio-ai/orchestrator-core": "^0.24.0"
172
+ "@avocadostudio-ai/orchestrator-core": "^0.26.0"
173
173
  },
174
174
  "peerDependenciesMeta": {
175
175
  "next": {