next-sanity 11.5.7-canary.2 → 11.5.7
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/experimental/live.d.ts +15 -8
- package/dist/experimental/live.js +38 -24
- package/dist/experimental/live.js.map +1 -1
- package/dist/live/server-actions/index.js +2 -2
- package/dist/live/server-actions/index.js.map +1 -1
- package/package.json +4 -4
- package/src/experimental/live.tsx +66 -39
- package/src/live/server-actions/index.ts +2 -4
|
@@ -8,7 +8,7 @@ import { ClientPerspective, ClientReturn, ContentSourceMap, LiveEventGoAway, Que
|
|
|
8
8
|
/**
|
|
9
9
|
* @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.
|
|
10
10
|
*/
|
|
11
|
-
declare function
|
|
11
|
+
declare function resolvePerspectiveFromCookie({
|
|
12
12
|
cookies: jar
|
|
13
13
|
}: {
|
|
14
14
|
cookies: Awaited<ReturnType<typeof cookies>>;
|
|
@@ -19,13 +19,11 @@ declare function resolvePerspectiveFromCookies({
|
|
|
19
19
|
type DefinedSanityFetchType = <const QueryString extends string>(options: {
|
|
20
20
|
query: QueryString;
|
|
21
21
|
params?: QueryParams | Promise<QueryParams>;
|
|
22
|
-
/**
|
|
23
|
-
* @defaultValue 'published'
|
|
24
|
-
*/
|
|
25
22
|
perspective?: Exclude<ClientPerspective, "raw">;
|
|
26
23
|
/**
|
|
27
|
-
* Enables stega encoding of the data, this is typically only used in draft mode
|
|
28
|
-
*
|
|
24
|
+
* Enables stega encoding of the data, this is typically only used in draft mode.
|
|
25
|
+
* If `defineLive({..., stega: true})` is provided, then it defaults to `true` in Draft Mode.
|
|
26
|
+
* If `defineLive({..., stega: false})` then it defaults to `false`.
|
|
29
27
|
*/
|
|
30
28
|
stega?: boolean;
|
|
31
29
|
/**
|
|
@@ -35,7 +33,7 @@ type DefinedSanityFetchType = <const QueryString extends string>(options: {
|
|
|
35
33
|
*/
|
|
36
34
|
requestTag?: string;
|
|
37
35
|
/**
|
|
38
|
-
* Custom cache tags that can be used with next's `revalidateTag`
|
|
36
|
+
* Custom cache tags that can be used with next's `revalidateTag` function for custom webhook on-demand revalidation.
|
|
39
37
|
*/
|
|
40
38
|
tags?: string[];
|
|
41
39
|
}) => Promise<{
|
|
@@ -46,6 +44,10 @@ type DefinedSanityFetchType = <const QueryString extends string>(options: {
|
|
|
46
44
|
*/
|
|
47
45
|
sourceMap: ContentSourceMap | null;
|
|
48
46
|
/**
|
|
47
|
+
* The perspective used to fetch the data, useful for debugging.
|
|
48
|
+
*/
|
|
49
|
+
perspective: Exclude<ClientPerspective, "raw">;
|
|
50
|
+
/**
|
|
49
51
|
* The cache tags used with `next/cache`, useful for debugging.
|
|
50
52
|
*/
|
|
51
53
|
tags: string[];
|
|
@@ -123,6 +125,11 @@ interface DefineSanityLiveOptions {
|
|
|
123
125
|
* It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.
|
|
124
126
|
*/
|
|
125
127
|
browserToken?: string | false;
|
|
128
|
+
/**
|
|
129
|
+
* Optional. Include stega encoding when draft mode is enabled.
|
|
130
|
+
* @defaultValue `true` if the client configuration has the `stega.studioUrl` property set, otherwise `false`
|
|
131
|
+
*/
|
|
132
|
+
stega?: boolean;
|
|
126
133
|
}
|
|
127
134
|
/**
|
|
128
135
|
* @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.
|
|
@@ -138,5 +145,5 @@ declare function defineLive(config: DefineSanityLiveOptions): {
|
|
|
138
145
|
SanityLive: React.ComponentType<DefinedSanityLiveProps>;
|
|
139
146
|
};
|
|
140
147
|
//#endregion
|
|
141
|
-
export { DefineSanityLiveOptions, DefinedSanityFetchType, DefinedSanityLiveProps, defineLive,
|
|
148
|
+
export { DefineSanityLiveOptions, DefinedSanityFetchType, DefinedSanityLiveProps, defineLive, resolvePerspectiveFromCookie };
|
|
142
149
|
//# sourceMappingURL=live.d.ts.map
|
|
@@ -1,37 +1,36 @@
|
|
|
1
1
|
import { t as sanitizePerspective } from "../utils.js";
|
|
2
2
|
import { n as PUBLISHED_SYNC_TAG_PREFIX, t as DRAFT_SYNC_TAG_PREFIX } from "../constants.js";
|
|
3
|
+
import { stegaEncodeSourceMap } from "@sanity/client/stega";
|
|
3
4
|
import { preconnect } from "react-dom";
|
|
4
5
|
import { cookies, draftMode } from "next/headers";
|
|
5
6
|
import { perspectiveCookieName } from "@sanity/preview-url-secret/constants";
|
|
6
7
|
import { jsx } from "react/jsx-runtime";
|
|
7
8
|
import "server-only";
|
|
8
|
-
import "next-sanity";
|
|
9
|
+
import { createClient } from "next-sanity";
|
|
9
10
|
import SanityLiveClientComponent from "next-sanity/experimental/client-components/live";
|
|
10
|
-
import {
|
|
11
|
-
async function
|
|
11
|
+
import { cacheTag, updateTag } from "next/cache";
|
|
12
|
+
async function resolvePerspectiveFromCookie({ cookies: jar }) {
|
|
12
13
|
return jar.has(perspectiveCookieName) ? sanitizePerspective(jar.get(perspectiveCookieName)?.value, "drafts") : "drafts";
|
|
13
14
|
}
|
|
14
|
-
async function sanityCachedFetch(
|
|
15
|
-
"use cache
|
|
16
|
-
const client =
|
|
15
|
+
async function sanityCachedFetch(config, { query, params = {}, perspective, stega, requestTag, draftToken, customCacheTags = [] }) {
|
|
16
|
+
"use cache";
|
|
17
|
+
const client = createClient({
|
|
17
18
|
...config,
|
|
18
|
-
useCdn: true
|
|
19
|
-
allowReconfigure: false
|
|
19
|
+
useCdn: true
|
|
20
20
|
});
|
|
21
21
|
const useCdn = perspective === "published";
|
|
22
|
-
const { result, resultSourceMap, syncTags } = await client.fetch(query,
|
|
22
|
+
const { result, resultSourceMap, syncTags } = await client.fetch(query, params, {
|
|
23
23
|
filterResponse: false,
|
|
24
24
|
returnQuery: false,
|
|
25
25
|
perspective,
|
|
26
26
|
useCdn,
|
|
27
|
-
stega,
|
|
27
|
+
resultSourceMap: stega ? "withKeyArraySelector" : void 0,
|
|
28
28
|
cacheMode: useCdn ? "noStale" : void 0,
|
|
29
29
|
tag: requestTag,
|
|
30
30
|
token: perspective === "published" ? config.token : draftToken || config.token
|
|
31
31
|
});
|
|
32
32
|
const tags = [...customCacheTags, ...(syncTags || []).map((tag) => `${perspective === "published" ? PUBLISHED_SYNC_TAG_PREFIX : DRAFT_SYNC_TAG_PREFIX}${tag}`)];
|
|
33
33
|
cacheTag(...tags);
|
|
34
|
-
cacheLife({ revalidate: 3600 * 24 * 90 });
|
|
35
34
|
return {
|
|
36
35
|
data: result,
|
|
37
36
|
sourceMap: resultSourceMap || null,
|
|
@@ -47,10 +46,15 @@ function defineLive(config) {
|
|
|
47
46
|
allowReconfigure: false,
|
|
48
47
|
useCdn: false
|
|
49
48
|
});
|
|
50
|
-
const { token: originalToken,
|
|
49
|
+
const { token: originalToken, stega: stegaConfig } = client.config();
|
|
50
|
+
const studioUrlDefined = typeof client.config().stega.studioUrl !== "undefined";
|
|
51
|
+
const { stega: stegaEnabled = typeof client.config().stega.studioUrl !== "undefined" } = config;
|
|
51
52
|
return {
|
|
52
|
-
sanityFetch: function sanityFetch({ query, params = {}, stega
|
|
53
|
-
|
|
53
|
+
sanityFetch: async function sanityFetch({ query, params = {}, stega: _stega, tags: customCacheTags = [], perspective: _perspective, requestTag = "next-loader.fetch" }) {
|
|
54
|
+
const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled);
|
|
55
|
+
const perspective = _perspective ?? ((await draftMode()).isEnabled ? "drafts" : "published");
|
|
56
|
+
const { apiHost, apiVersion, useProjectHostname, dataset, projectId, requestTagPrefix } = client.config();
|
|
57
|
+
const { data: _data, sourceMap, tags } = await sanityCachedFetch({
|
|
54
58
|
apiHost,
|
|
55
59
|
apiVersion,
|
|
56
60
|
useProjectHostname,
|
|
@@ -60,27 +64,36 @@ function defineLive(config) {
|
|
|
60
64
|
token: originalToken
|
|
61
65
|
}, {
|
|
62
66
|
query,
|
|
63
|
-
params,
|
|
67
|
+
params: await params,
|
|
64
68
|
perspective,
|
|
65
69
|
stega,
|
|
66
70
|
requestTag,
|
|
67
71
|
draftToken: serverToken,
|
|
68
72
|
customCacheTags
|
|
69
73
|
});
|
|
74
|
+
return {
|
|
75
|
+
data: stega && sourceMap ? stegaEncodeSourceMap(_data, sourceMap, {
|
|
76
|
+
...stegaConfig,
|
|
77
|
+
enabled: true
|
|
78
|
+
}) : _data,
|
|
79
|
+
sourceMap,
|
|
80
|
+
tags,
|
|
81
|
+
perspective
|
|
82
|
+
};
|
|
70
83
|
},
|
|
71
84
|
SanityLive: function SanityLive(props) {
|
|
72
85
|
const { refreshOnMount = false, refreshOnFocus = false, refreshOnReconnect = false, requestTag, onError, onGoAway, intervalOnGoAway, revalidateSyncTags = expireTags } = props;
|
|
73
|
-
const { projectId
|
|
86
|
+
const { projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix } = client.config();
|
|
74
87
|
const { origin } = new URL(client.getUrl("", false));
|
|
75
88
|
preconnect(origin);
|
|
76
89
|
return /* @__PURE__ */ jsx(SanityLiveServerComponent, {
|
|
77
90
|
config: {
|
|
78
|
-
projectId
|
|
79
|
-
dataset
|
|
80
|
-
apiHost
|
|
81
|
-
apiVersion
|
|
82
|
-
useProjectHostname
|
|
83
|
-
requestTagPrefix
|
|
91
|
+
projectId,
|
|
92
|
+
dataset,
|
|
93
|
+
apiHost,
|
|
94
|
+
apiVersion,
|
|
95
|
+
useProjectHostname,
|
|
96
|
+
requestTagPrefix
|
|
84
97
|
},
|
|
85
98
|
requestTag,
|
|
86
99
|
browserToken,
|
|
@@ -97,6 +110,7 @@ function defineLive(config) {
|
|
|
97
110
|
};
|
|
98
111
|
}
|
|
99
112
|
const SanityLiveServerComponent = async function SanityLiveServerComponent$1(props) {
|
|
113
|
+
"use cache";
|
|
100
114
|
const { config, requestTag, intervalOnGoAway, onError, onGoAway, refreshOnFocus, refreshOnMount, refreshOnReconnect, revalidateSyncTags, browserToken, resolveDraftModePerspective: resolveDraftModePerspective$1 } = props;
|
|
101
115
|
const { isEnabled: isDraftModeEnabled } = await draftMode();
|
|
102
116
|
return /* @__PURE__ */ jsx(SanityLiveClientComponent, {
|
|
@@ -132,9 +146,9 @@ async function expireTags(_tags) {
|
|
|
132
146
|
}
|
|
133
147
|
async function resolveDraftModePerspective() {
|
|
134
148
|
"use server";
|
|
135
|
-
if ((await draftMode()).isEnabled) return
|
|
149
|
+
if ((await draftMode()).isEnabled) return resolvePerspectiveFromCookie({ cookies: await cookies() });
|
|
136
150
|
return "published";
|
|
137
151
|
}
|
|
138
|
-
export { defineLive,
|
|
152
|
+
export { defineLive, resolvePerspectiveFromCookie };
|
|
139
153
|
|
|
140
154
|
//# sourceMappingURL=live.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"live.js","names":["SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps>","SanityLiveServerComponent","resolveDraftModePerspective"],"sources":["../../src/experimental/live.tsx"],"sourcesContent":["// oxlint-disable-next-line no-unassigned-import\nimport 'server-only'\nimport {\n type ClientPerspective,\n type ClientReturn,\n type ContentSourceMap,\n type LiveEventGoAway,\n type QueryParams,\n type SanityClient,\n type SyncTag,\n} from 'next-sanity'\nimport SanityLiveClientComponent, {\n type SanityLiveProps,\n} from 'next-sanity/experimental/client-components/live'\nimport {cacheTag, cacheLife, updateTag} from 'next/cache'\nimport {draftMode, cookies} from 'next/headers'\nimport {preconnect} from 'react-dom'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {sanitizePerspective} from '../live/utils'\nimport type {SanityClientConfig} from './types'\nimport {DRAFT_SYNC_TAG_PREFIX, PUBLISHED_SYNC_TAG_PREFIX} from './constants'\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport async function resolvePerspectiveFromCookies({\n cookies: jar,\n}: {\n cookies: Awaited<ReturnType<typeof cookies>>\n}): Promise<Exclude<ClientPerspective, 'raw'>> {\n return jar.has(perspectiveCookieName)\n ? sanitizePerspective(jar.get(perspectiveCookieName)?.value, 'drafts')\n : 'drafts'\n}\n\nasync function sanityCachedFetch<const QueryString extends string>(\n // opaque reference to the client, allows passing stega.filter and other non-serializable client options that won't be used as cache keys\n _client: SanityClient,\n // The parts of the client config that should be used as cache keys\n config: SanityClientConfig,\n {\n query,\n params = {},\n perspective,\n stega,\n requestTag,\n draftToken,\n customCacheTags = [],\n }: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n perspective: Exclude<ClientPerspective, 'raw'>\n stega: boolean\n requestTag: string\n draftToken?: string | false | undefined\n customCacheTags?: string[]\n },\n): Promise<{\n data: ClientReturn<QueryString, unknown>\n sourceMap: ContentSourceMap | null\n tags: string[]\n}> {\n 'use cache: remote'\n\n const client = _client.withConfig({...config, useCdn: true, allowReconfigure: false})\n const useCdn = perspective === 'published'\n\n const {result, resultSourceMap, syncTags} = await client.fetch(query, await params, {\n filterResponse: false,\n returnQuery: false,\n perspective,\n useCdn,\n // resultSourceMap: stega ? 'withKeyArraySelector' : undefined, // @TODO allow passing csm for non-stega use\n stega,\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: requestTag,\n token: perspective === 'published' ? config.token : draftToken || config.token, // @TODO can pass undefined instead of config.token here?\n })\n const tags = [\n ...customCacheTags,\n ...(syncTags || []).map(\n (tag) =>\n `${perspective === 'published' ? PUBLISHED_SYNC_TAG_PREFIX : DRAFT_SYNC_TAG_PREFIX}${tag}`,\n ),\n ]\n /**\n * The tags used here, are expired later on in the `expireTags` Server Action with the `expireTag` function from `next/cache`\n */\n cacheTag(...tags)\n /**\n * Sanity Live handles on-demand revalidation, so the default 15min time based revalidation is too short\n */\n cacheLife({revalidate: 60 * 60 * 24 * 90})\n\n return {data: result, sourceMap: resultSourceMap || null, tags}\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport type DefinedSanityFetchType = <const QueryString extends string>(options: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n /**\n * @defaultValue 'published'\n */\n perspective?: Exclude<ClientPerspective, 'raw'>\n /**\n * Enables stega encoding of the data, this is typically only used in draft mode in conjunction with `perspective: 'drafts'` and with `@sanity/visual-editing` setup.\n * @defaultValue `false`\n */\n stega?: boolean\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.fetch'\n */\n requestTag?: string\n /**\n * Custom cache tags that can be used with next's `revalidateTag` and `updateTag` functions for custom webhook on-demand revalidation.\n */\n tags?: string[]\n}) => Promise<{\n data: ClientReturn<QueryString, unknown>\n /**\n * The Content Source Map can be used for custom setups like `encodeSourceMap` for `data-sanity` attributes, or `stegaEncodeSourceMap` for stega encoding in your own way.\n * The Content Source Map is only fetched by default in draft mode, if `stega` is `true`. Otherwise your client configuration will need to have `resultSourceMap: 'withKeyArraySelector' | true`\n */\n sourceMap: ContentSourceMap | null\n /**\n * The cache tags used with `next/cache`, useful for debugging.\n */\n tags: string[]\n}>\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefinedSanityLiveProps {\n /**\n * Automatic refresh of RSC when the component <SanityLive /> is mounted.\n * @defaultValue `false`\n */\n refreshOnMount?: boolean\n /**\n * Automatically refresh when window gets focused\n * @defaultValue `false`\n */\n refreshOnFocus?: boolean\n /**\n * Automatically refresh when the browser regains a network connection (via navigator.onLine)\n * @defaultValue `false`\n */\n refreshOnReconnect?: boolean\n /**\n * Automatically refresh on an interval when the Live Event API emits a `goaway` event, which indicates that the connection is rejected or closed.\n * This typically happens if the connection limit is reached, or if the connection is idle for too long.\n * To disable this long polling fallback behavior set `intervalOnGoAway` to `false` or `0`.\n * You can also use `onGoAway` to handle the `goaway` event in your own way, and read the reason why the event was emitted.\n * @defaultValue `30_000` 30 seconds interval\n */\n intervalOnGoAway?: number | false\n\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live'\n */\n requestTag?: string\n\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n\n /**\n * Handle the `goaway` event if the connection is rejected/closed.\n * `event.reason` will be a string of why the event was emitted, for example `'connection limit reached'`.\n * When this happens the `<SanityLive />` will fallback to long polling with a default interval of 30 seconds, providing your own `onGoAway` handler does not change this behavior.\n * If you want to disable long polling set `intervalOnGoAway` to `false` or `0`.\n */\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n\n /**\n * Override how cache tags are invalidated, you need to pass a server action here.\n * You can also pass a `use client` function here, and have `router.refresh()` be called if the promise resolves to `'refresh'`.\n */\n // @TODO remove, replace with onLiveEvent\n revalidateSyncTags?: (\n tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],\n ) => Promise<void | 'refresh'>\n\n // @TODO add\n // decide how to handle a live event coming in\n // onLiveEvent?: (event: LiveEvent, mode: 'production' | 'preview) => void\n\n /**\n * Control how the draft mode perspective is resolved, by default it resolves from the `sanity-preview-perspective` cookie.\n */\n resolveDraftModePerspective?: () => Promise<ClientPerspective>\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefineSanityLiveOptions {\n /**\n * Required for `sanityFetch` and `SanityLive` to work\n */\n client: SanityClient\n /**\n * Optional. If provided then the token needs to have permissions to query documents with `drafts.` prefixes in order for `perspective: 'drafts'` to work.\n * This token is not shared with the browser.\n */\n serverToken?: string | false\n /**\n * Optional. This token is shared with the browser, and should only have access to query published documents.\n * It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.\n */\n browserToken?: string | false\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport function defineLive(config: DefineSanityLiveOptions): {\n /**\n * Use this function to fetch data from Sanity in your React Server Components.\n */\n sanityFetch: DefinedSanityFetchType\n /**\n * Render this in your root layout.tsx to make your page revalidate on new content live, automatically.\n */\n SanityLive: React.ComponentType<DefinedSanityLiveProps>\n} {\n const {client: _client, serverToken, browserToken} = config\n\n if (!_client) {\n throw new Error('`client` is required for `defineLive` to function')\n }\n\n if (process.env.NODE_ENV !== 'production' && !serverToken && serverToken !== false) {\n // eslint-disable-next-line no-console\n console.warn(\n 'No `serverToken` provided to `defineLive`. This means that only published content will be fetched and respond to live events. You can silence this warning by setting `serverToken: false`.',\n )\n }\n\n if (process.env.NODE_ENV !== 'production' && !browserToken && browserToken !== false) {\n // eslint-disable-next-line no-console\n console.warn(\n 'No `browserToken` provided to `defineLive`. This means that live previewing drafts will only work when using the Presentation Tool in your Sanity Studio. To support live previewing drafts stand-alone, provide a `browserToken`. It is shared with the browser so it should only have Viewer rights or lower. You can silence this warning by setting `browserToken: false`.',\n )\n }\n\n const client = _client.withConfig({allowReconfigure: false, useCdn: false})\n const {\n token: originalToken,\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n } = client.config()\n\n const sanityFetch: DefinedSanityFetchType = function sanityFetch<\n const QueryString extends string,\n >({\n query,\n params = {},\n stega = false,\n perspective = 'published',\n tags: customCacheTags = [],\n requestTag = 'next-loader.fetch',\n }: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n stega?: boolean\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n requestTag?: string\n }) {\n return sanityCachedFetch(\n client,\n {\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n token: originalToken,\n },\n {\n query,\n params,\n perspective,\n stega,\n requestTag,\n draftToken: serverToken,\n customCacheTags,\n },\n )\n }\n\n const SanityLive: React.ComponentType<DefinedSanityLiveProps> = function SanityLive(props) {\n const {\n // perspective,\n refreshOnMount = false,\n refreshOnFocus = false,\n refreshOnReconnect = false,\n requestTag,\n onError,\n onGoAway,\n intervalOnGoAway,\n revalidateSyncTags = expireTags,\n } = props\n\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix} =\n client.config()\n const {origin} = new URL(client.getUrl('', false))\n\n // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n preconnect(origin)\n\n return (\n <SanityLiveServerComponent\n config={{projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix}}\n requestTag={requestTag}\n browserToken={browserToken}\n // origin={origin}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={\n props.resolveDraftModePerspective ?? resolveDraftModePerspective\n }\n />\n )\n }\n\n return {sanityFetch, SanityLive}\n}\n\ninterface SanityLiveServerComponentProps\n extends Omit<SanityLiveProps, 'draftModeEnabled' | 'token' | 'draftModePerspective'> {\n browserToken: string | false | undefined\n // origin: string\n // perspective?: Exclude<ClientPerspective, 'raw'>\n}\n\nconst SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps> =\n async function SanityLiveServerComponent(props) {\n // 'use cache'\n // @TODO should this be 'max' instead?, or configured by changing the default cache profile?\n // cacheLife({\n // stale: Infinity,\n // revalidate: Infinity,\n // expire: Infinity,\n // })\n const {\n config,\n requestTag,\n intervalOnGoAway,\n onError,\n onGoAway,\n refreshOnFocus,\n refreshOnMount,\n refreshOnReconnect,\n revalidateSyncTags,\n browserToken,\n // origin,\n // perspective,\n resolveDraftModePerspective,\n } = props\n\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n // // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n // preconnect(origin)\n\n return (\n <SanityLiveClientComponent\n config={{\n ...config,\n token: typeof browserToken === 'string' && isDraftModeEnabled ? browserToken : undefined,\n }}\n requestTag={requestTag}\n draftModeEnabled={isDraftModeEnabled}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={resolveDraftModePerspective}\n />\n )\n }\n\n// @TODO expose parseTags function that returns the correct array of tags\n// we already have s1: prefixes, but they could change\n// use sp: for prod, sd: for draft, keep em short\nasync function expireTags(_tags: unknown): Promise<void> {\n 'use server'\n // @TODO Draft Mode bypasses cache anyway so we don't bother with expiring tags for draft content\n // const isDraftMode = (await draftMode()).isEnabled\n // const tags = _tags.map((tag) => `${isDraftMode ? 'drafts' : 'sanity'}:${tag}`)\n if (!Array.isArray(_tags)) {\n console.warn('<SanityLive /> `expireTags` called with non-array tags', _tags)\n return undefined\n }\n const tags = _tags.filter(\n (tag) => typeof tag === 'string' && tag.startsWith(PUBLISHED_SYNC_TAG_PREFIX),\n )\n if (!tags.length) {\n console.warn('<SanityLive /> `expireTags` called with no valid tags', _tags)\n return undefined\n }\n for (const tag of tags) {\n updateTag(tag)\n }\n console.log(`<SanityLive /> updated tags: ${tags.join(', ')}`)\n}\n\nasync function resolveDraftModePerspective(): Promise<ClientPerspective> {\n 'use server'\n if ((await draftMode()).isEnabled) {\n const jar = await cookies()\n return resolvePerspectiveFromCookies({cookies: jar})\n }\n return 'published'\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"live.js","names":["SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps>","SanityLiveServerComponent","resolveDraftModePerspective"],"sources":["../../src/experimental/live.tsx"],"sourcesContent":["// oxlint-disable-next-line no-unassigned-import\nimport 'server-only'\nimport {\n createClient,\n type ClientPerspective,\n type ClientReturn,\n type ContentSourceMap,\n type LiveEventGoAway,\n type QueryParams,\n type SanityClient,\n type SyncTag,\n} from 'next-sanity'\nimport {stegaEncodeSourceMap} from '@sanity/client/stega'\nimport SanityLiveClientComponent, {\n type SanityLiveProps,\n} from 'next-sanity/experimental/client-components/live'\nimport {cacheTag, updateTag} from 'next/cache'\nimport {draftMode, cookies} from 'next/headers'\nimport {preconnect} from 'react-dom'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {sanitizePerspective} from '../live/utils'\nimport type {SanityClientConfig} from './types'\nimport {DRAFT_SYNC_TAG_PREFIX, PUBLISHED_SYNC_TAG_PREFIX} from './constants'\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport async function resolvePerspectiveFromCookie({\n cookies: jar,\n}: {\n cookies: Awaited<ReturnType<typeof cookies>>\n}): Promise<Exclude<ClientPerspective, 'raw'>> {\n return jar.has(perspectiveCookieName)\n ? sanitizePerspective(jar.get(perspectiveCookieName)?.value, 'drafts')\n : 'drafts'\n}\n\nasync function sanityCachedFetch<const QueryString extends string>(\n config: SanityClientConfig,\n {\n query,\n params = {},\n perspective,\n stega,\n requestTag,\n draftToken,\n customCacheTags = [],\n }: {\n query: QueryString\n params?: QueryParams\n perspective: Exclude<ClientPerspective, 'raw'>\n stega: boolean\n requestTag: string\n draftToken?: string | false | undefined\n customCacheTags?: string[]\n },\n): Promise<{\n data: ClientReturn<QueryString, unknown>\n sourceMap: ContentSourceMap | null\n tags: string[]\n}> {\n 'use cache'\n\n const client = createClient({...config, useCdn: true})\n const useCdn = perspective === 'published'\n /**\n * The default cache profile isn't ideal for live content, as it has unnecessary time based background validation, as well as a too lazy client stale value\n * https://github.com/vercel/next.js/blob/8dd358002baf4244c0b2e38b5bda496daf60dacb/packages/next/cache.d.ts#L14-L26\n */\n // cacheLife({\n // stale: Infinity,\n // revalidate: Infinity,\n // expire: Infinity,\n // })\n\n const {result, resultSourceMap, syncTags} = await client.fetch(query, params, {\n filterResponse: false,\n returnQuery: false,\n perspective,\n useCdn,\n resultSourceMap: stega ? 'withKeyArraySelector' : undefined, // @TODO allow passing csm for non-stega use\n cacheMode: useCdn ? 'noStale' : undefined,\n tag: requestTag,\n token: perspective === 'published' ? config.token : draftToken || config.token, // @TODO can pass undefined instead of config.token here?\n })\n const tags = [\n ...customCacheTags,\n ...(syncTags || []).map(\n (tag) =>\n `${perspective === 'published' ? PUBLISHED_SYNC_TAG_PREFIX : DRAFT_SYNC_TAG_PREFIX}${tag}`,\n ),\n ]\n /**\n * The tags used here, are expired later on in the `expireTags` Server Action with the `expireTag` function from `next/cache`\n */\n cacheTag(...tags)\n\n return {data: result, sourceMap: resultSourceMap || null, tags}\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport type DefinedSanityFetchType = <const QueryString extends string>(options: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n perspective?: Exclude<ClientPerspective, 'raw'>\n /**\n * Enables stega encoding of the data, this is typically only used in draft mode.\n * If `defineLive({..., stega: true})` is provided, then it defaults to `true` in Draft Mode.\n * If `defineLive({..., stega: false})` then it defaults to `false`.\n */\n stega?: boolean\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.fetch'\n */\n requestTag?: string\n /**\n * Custom cache tags that can be used with next's `revalidateTag` function for custom webhook on-demand revalidation.\n */\n tags?: string[]\n}) => Promise<{\n data: ClientReturn<QueryString, unknown>\n /**\n * The Content Source Map can be used for custom setups like `encodeSourceMap` for `data-sanity` attributes, or `stegaEncodeSourceMap` for stega encoding in your own way.\n * The Content Source Map is only fetched by default in draft mode, if `stega` is `true`. Otherwise your client configuration will need to have `resultSourceMap: 'withKeyArraySelector' | true`\n */\n sourceMap: ContentSourceMap | null\n /**\n * The perspective used to fetch the data, useful for debugging.\n */\n perspective: Exclude<ClientPerspective, 'raw'>\n /**\n * The cache tags used with `next/cache`, useful for debugging.\n */\n tags: string[]\n}>\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefinedSanityLiveProps {\n /**\n * Automatic refresh of RSC when the component <SanityLive /> is mounted.\n * @defaultValue `false`\n */\n refreshOnMount?: boolean\n /**\n * Automatically refresh when window gets focused\n * @defaultValue `false`\n */\n refreshOnFocus?: boolean\n /**\n * Automatically refresh when the browser regains a network connection (via navigator.onLine)\n * @defaultValue `false`\n */\n refreshOnReconnect?: boolean\n /**\n * Automatically refresh on an interval when the Live Event API emits a `goaway` event, which indicates that the connection is rejected or closed.\n * This typically happens if the connection limit is reached, or if the connection is idle for too long.\n * To disable this long polling fallback behavior set `intervalOnGoAway` to `false` or `0`.\n * You can also use `onGoAway` to handle the `goaway` event in your own way, and read the reason why the event was emitted.\n * @defaultValue `30_000` 30 seconds interval\n */\n intervalOnGoAway?: number | false\n\n /**\n * This request tag is used to identify the request when viewing request logs from your Sanity Content Lake.\n * @see https://www.sanity.io/docs/reference-api-request-tags\n * @defaultValue 'next-loader.live'\n */\n requestTag?: string\n\n /**\n * Handle errors from the Live Events subscription.\n * By default it's reported using `console.error`, you can override this prop to handle it in your own way.\n */\n onError?: (error: unknown) => void\n\n /**\n * Handle the `goaway` event if the connection is rejected/closed.\n * `event.reason` will be a string of why the event was emitted, for example `'connection limit reached'`.\n * When this happens the `<SanityLive />` will fallback to long polling with a default interval of 30 seconds, providing your own `onGoAway` handler does not change this behavior.\n * If you want to disable long polling set `intervalOnGoAway` to `false` or `0`.\n */\n onGoAway?: (event: LiveEventGoAway, intervalOnGoAway: number | false) => void\n\n /**\n * Override how cache tags are invalidated, you need to pass a server action here.\n * You can also pass a `use client` function here, and have `router.refresh()` be called if the promise resolves to `'refresh'`.\n */\n // @TODO remove, replace with onLiveEvent\n revalidateSyncTags?: (\n tags: `${typeof PUBLISHED_SYNC_TAG_PREFIX | typeof DRAFT_SYNC_TAG_PREFIX}${SyncTag}`[],\n ) => Promise<void | 'refresh'>\n\n // @TODO add\n // decide how to handle a live event coming in\n // onLiveEvent?: (event: LiveEvent, mode: 'production' | 'preview) => void\n\n /**\n * Control how the draft mode perspective is resolved, by default it resolves from the `sanity-preview-perspective` cookie.\n */\n resolveDraftModePerspective?: () => Promise<ClientPerspective>\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport interface DefineSanityLiveOptions {\n /**\n * Required for `sanityFetch` and `SanityLive` to work\n */\n client: SanityClient\n /**\n * Optional. If provided then the token needs to have permissions to query documents with `drafts.` prefixes in order for `perspective: 'drafts'` to work.\n * This token is not shared with the browser.\n */\n serverToken?: string | false\n /**\n * Optional. This token is shared with the browser, and should only have access to query published documents.\n * It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.\n */\n browserToken?: string | false\n /**\n * Optional. Include stega encoding when draft mode is enabled.\n * @defaultValue `true` if the client configuration has the `stega.studioUrl` property set, otherwise `false`\n */\n stega?: boolean\n}\n\n/**\n * @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.\n */\nexport function defineLive(config: DefineSanityLiveOptions): {\n /**\n * Use this function to fetch data from Sanity in your React Server Components.\n */\n sanityFetch: DefinedSanityFetchType\n /**\n * Render this in your root layout.tsx to make your page revalidate on new content live, automatically.\n */\n SanityLive: React.ComponentType<DefinedSanityLiveProps>\n} {\n const {client: _client, serverToken, browserToken} = config\n\n if (!_client) {\n throw new Error('`client` is required for `defineLive` to function')\n }\n\n if (process.env.NODE_ENV !== 'production' && !serverToken && serverToken !== false) {\n // eslint-disable-next-line no-console\n console.warn(\n 'No `serverToken` provided to `defineLive`. This means that only published content will be fetched and respond to live events. You can silence this warning by setting `serverToken: false`.',\n )\n }\n\n if (process.env.NODE_ENV !== 'production' && !browserToken && browserToken !== false) {\n // eslint-disable-next-line no-console\n console.warn(\n 'No `browserToken` provided to `defineLive`. This means that live previewing drafts will only work when using the Presentation Tool in your Sanity Studio. To support live previewing drafts stand-alone, provide a `browserToken`. It is shared with the browser so it should only have Viewer rights or lower. You can silence this warning by setting `browserToken: false`.',\n )\n }\n\n const client = _client.withConfig({allowReconfigure: false, useCdn: false})\n const {token: originalToken, stega: stegaConfig} = client.config()\n const studioUrlDefined = typeof client.config().stega.studioUrl !== 'undefined'\n const {stega: stegaEnabled = typeof client.config().stega.studioUrl !== 'undefined'} = config\n\n const sanityFetch: DefinedSanityFetchType = async function sanityFetch<\n const QueryString extends string,\n >({\n query,\n params = {},\n stega: _stega,\n tags: customCacheTags = [],\n perspective: _perspective,\n requestTag = 'next-loader.fetch',\n }: {\n query: QueryString\n params?: QueryParams | Promise<QueryParams>\n stega?: boolean\n tags?: string[]\n perspective?: Exclude<ClientPerspective, 'raw'>\n requestTag?: string\n }) {\n const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled)\n const perspective = _perspective ?? ((await draftMode()).isEnabled ? 'drafts' : 'published')\n\n const {apiHost, apiVersion, useProjectHostname, dataset, projectId, requestTagPrefix} =\n client.config()\n const {\n data: _data,\n sourceMap,\n tags,\n } = await sanityCachedFetch(\n {\n apiHost,\n apiVersion,\n useProjectHostname,\n dataset,\n projectId,\n requestTagPrefix,\n token: originalToken,\n },\n {\n query,\n params: await params,\n perspective,\n stega,\n requestTag,\n draftToken: serverToken,\n customCacheTags,\n },\n )\n\n const data =\n stega && sourceMap\n ? stegaEncodeSourceMap(_data, sourceMap, {...stegaConfig, enabled: true})\n : _data\n\n return {data, sourceMap, tags, perspective}\n }\n\n const SanityLive: React.ComponentType<DefinedSanityLiveProps> = function SanityLive(props) {\n const {\n // perspective,\n refreshOnMount = false,\n refreshOnFocus = false,\n refreshOnReconnect = false,\n requestTag,\n onError,\n onGoAway,\n intervalOnGoAway,\n revalidateSyncTags = expireTags,\n } = props\n\n const {projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix} =\n client.config()\n const {origin} = new URL(client.getUrl('', false))\n\n // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n preconnect(origin)\n\n return (\n <SanityLiveServerComponent\n config={{projectId, dataset, apiHost, apiVersion, useProjectHostname, requestTagPrefix}}\n requestTag={requestTag}\n browserToken={browserToken}\n // origin={origin}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={\n props.resolveDraftModePerspective ?? resolveDraftModePerspective\n }\n />\n )\n }\n\n return {sanityFetch, SanityLive}\n}\n\ninterface SanityLiveServerComponentProps\n extends Omit<SanityLiveProps, 'draftModeEnabled' | 'token' | 'draftModePerspective'> {\n browserToken: string | false | undefined\n // origin: string\n // perspective?: Exclude<ClientPerspective, 'raw'>\n}\n\nconst SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps> =\n async function SanityLiveServerComponent(props) {\n 'use cache'\n // @TODO should this be 'max' instead?, or configured by changing the default cache profile?\n // cacheLife({\n // stale: Infinity,\n // revalidate: Infinity,\n // expire: Infinity,\n // })\n const {\n config,\n requestTag,\n intervalOnGoAway,\n onError,\n onGoAway,\n refreshOnFocus,\n refreshOnMount,\n refreshOnReconnect,\n revalidateSyncTags,\n browserToken,\n // origin,\n // perspective,\n resolveDraftModePerspective,\n } = props\n\n const {isEnabled: isDraftModeEnabled} = await draftMode()\n\n // // Preconnect to the Live Event API origin early, as the Sanity API is almost always on a different origin than the app\n // preconnect(origin)\n\n return (\n <SanityLiveClientComponent\n config={{\n ...config,\n token: typeof browserToken === 'string' && isDraftModeEnabled ? browserToken : undefined,\n }}\n requestTag={requestTag}\n draftModeEnabled={isDraftModeEnabled}\n refreshOnMount={refreshOnMount}\n refreshOnFocus={refreshOnFocus}\n refreshOnReconnect={refreshOnReconnect}\n onError={onError}\n onGoAway={onGoAway}\n intervalOnGoAway={intervalOnGoAway}\n revalidateSyncTags={revalidateSyncTags}\n resolveDraftModePerspective={resolveDraftModePerspective}\n />\n )\n }\n\n// @TODO expose parseTags function that returns the correct array of tags\n// we already have s1: prefixes, but they could change\n// use sp: for prod, sd: for draft, keep em short\nasync function expireTags(_tags: unknown): Promise<void> {\n 'use server'\n // @TODO Draft Mode bypasses cache anyway so we don't bother with expiring tags for draft content\n // const isDraftMode = (await draftMode()).isEnabled\n // const tags = _tags.map((tag) => `${isDraftMode ? 'drafts' : 'sanity'}:${tag}`)\n if (!Array.isArray(_tags)) {\n console.warn('<SanityLive /> `expireTags` called with non-array tags', _tags)\n return undefined\n }\n const tags = _tags.filter(\n (tag) => typeof tag === 'string' && tag.startsWith(PUBLISHED_SYNC_TAG_PREFIX),\n )\n if (!tags.length) {\n console.warn('<SanityLive /> `expireTags` called with no valid tags', _tags)\n return undefined\n }\n for (const tag of tags) {\n updateTag(tag)\n }\n console.log(`<SanityLive /> updated tags: ${tags.join(', ')}`)\n}\n\nasync function resolveDraftModePerspective(): Promise<ClientPerspective> {\n 'use server'\n if ((await draftMode()).isEnabled) {\n const jar = await cookies()\n return resolvePerspectiveFromCookie({cookies: jar})\n }\n return 'published'\n}\n\n/**\n * Add more stuff:\n * - sanityFetchMetadata: sanityFetch({query, params, stega: false, perspective: 'auto'})\n * - sanityFetchStaticParams: sanityFetch({query, params, stega: false, perspective: 'published', cacheMode: undefined})\n * - sanityFetchCached: sanityFetch({query, params, stega: 'opt-in',perspective: 'opt-in'}) useful for 'use cache' components, no unexpected magic, maybe this will be `sanityFetch` instead\n * - sanityFetchDynamic: sanityFetch({query, params, stega: 'auto', perspective: 'auto'}) just like sanityFetch of old, since `sanityFetch` will likely become opt-in\n */\n"],"mappings":";;;;;;;;;;;AA2BA,eAAsB,6BAA6B,EACjD,SAAS,OAGoC;AAC7C,QAAO,IAAI,IAAI,sBAAsB,GACjC,oBAAoB,IAAI,IAAI,sBAAsB,EAAE,OAAO,SAAS,GACpE;;AAGN,eAAe,kBACb,QACA,EACE,OACA,SAAS,EAAE,EACX,aACA,OACA,YACA,YACA,kBAAkB,EAAE,IAcrB;AACD;CAEA,MAAM,SAAS,aAAa;EAAC,GAAG;EAAQ,QAAQ;EAAK,CAAC;CACtD,MAAM,SAAS,gBAAgB;CAW/B,MAAM,EAAC,QAAQ,iBAAiB,aAAY,MAAM,OAAO,MAAM,OAAO,QAAQ;EAC5E,gBAAgB;EAChB,aAAa;EACb;EACA;EACA,iBAAiB,QAAQ,yBAAyB,KAAA;EAClD,WAAW,SAAS,YAAY,KAAA;EAChC,KAAK;EACL,OAAO,gBAAgB,cAAc,OAAO,QAAQ,cAAc,OAAO;EAC1E,CAAC;CACF,MAAM,OAAO,CACX,GAAG,iBACH,IAAI,YAAY,EAAE,EAAE,KACjB,QACC,GAAG,gBAAgB,cAAc,4BAA4B,wBAAwB,MACxF,CACF;AAID,UAAS,GAAG,KAAK;AAEjB,QAAO;EAAC,MAAM;EAAQ,WAAW,mBAAmB;EAAM;EAAK;;AA2IjE,SAAgB,WAAW,QASzB;CACA,MAAM,EAAC,QAAQ,SAAS,aAAa,iBAAgB;AAErD,KAAI,CAAC,QACH,OAAM,IAAI,MAAM,oDAAoD;AAGtE,KAAI,QAAQ,IAAI,aAAa,gBAAgB,CAAC,eAAe,gBAAgB,MAE3E,SAAQ,KACN,8LACD;AAGH,KAAI,QAAQ,IAAI,aAAa,gBAAgB,CAAC,gBAAgB,iBAAiB,MAE7E,SAAQ,KACN,iXACD;CAGH,MAAM,SAAS,QAAQ,WAAW;EAAC,kBAAkB;EAAO,QAAQ;EAAM,CAAC;CAC3E,MAAM,EAAC,OAAO,eAAe,OAAO,gBAAe,OAAO,QAAQ;CAClE,MAAM,mBAAmB,OAAO,OAAO,QAAQ,CAAC,MAAM,cAAc;CACpE,MAAM,EAAC,OAAO,eAAe,OAAO,OAAO,QAAQ,CAAC,MAAM,cAAc,gBAAe;AAiGvF,QAAO;EAAC,aA/FoC,eAAe,YAEzD,EACA,OACA,SAAS,EAAE,EACX,OAAO,QACP,MAAM,kBAAkB,EAAE,EAC1B,aAAa,cACb,aAAa,uBAQZ;GACD,MAAM,QAAQ,WAAW,gBAAgB,qBAAqB,MAAM,WAAW,EAAE;GACjF,MAAM,cAAc,kBAAkB,MAAM,WAAW,EAAE,YAAY,WAAW;GAEhF,MAAM,EAAC,SAAS,YAAY,oBAAoB,SAAS,WAAW,qBAClE,OAAO,QAAQ;GACjB,MAAM,EACJ,MAAM,OACN,WACA,SACE,MAAM,kBACR;IACE;IACA;IACA;IACA;IACA;IACA;IACA,OAAO;IACR,EACD;IACE;IACA,QAAQ,MAAM;IACd;IACA;IACA;IACA,YAAY;IACZ;IACD,CACF;AAOD,UAAO;IAAC,MAJN,SAAS,YACL,qBAAqB,OAAO,WAAW;KAAC,GAAG;KAAa,SAAS;KAAK,CAAC,GACvE;IAEQ;IAAW;IAAM;IAAY;;EA2CxB,YAxC2C,SAAS,WAAW,OAAO;GACzF,MAAM,EAEJ,iBAAiB,OACjB,iBAAiB,OACjB,qBAAqB,OACrB,YACA,SACA,UACA,kBACA,qBAAqB,eACnB;GAEJ,MAAM,EAAC,WAAW,SAAS,SAAS,YAAY,oBAAoB,qBAClE,OAAO,QAAQ;GACjB,MAAM,EAAC,WAAU,IAAI,IAAI,OAAO,OAAO,IAAI,MAAM,CAAC;AAGlD,cAAW,OAAO;AAElB,UACE,oBAAC,2BAAA;IACC,QAAQ;KAAC;KAAW;KAAS;KAAS;KAAY;KAAoB;KAAiB;IAC3E;IACE;IAEE;IACA;IACI;IACX;IACC;IACQ;IACE;IACpB,6BACE,MAAM,+BAA+B;KAEvC;;EAI0B;;AAUlC,MAAMA,4BACJ,eAAeC,4BAA0B,OAAO;AAC9C;CAOA,MAAM,EACJ,QACA,YACA,kBACA,SACA,UACA,gBACA,gBACA,oBACA,oBACA,cAGA,6BAAA,kCACE;CAEJ,MAAM,EAAC,WAAW,uBAAsB,MAAM,WAAW;AAKzD,QACE,oBAAC,2BAAA;EACC,QAAQ;GACN,GAAG;GACH,OAAO,OAAO,iBAAiB,YAAY,qBAAqB,eAAe,KAAA;GAChF;EACW;EACZ,kBAAkB;EACF;EACA;EACI;EACX;EACC;EACQ;EACE;EACpB,6BAA6BC;GAC7B;;AAOR,eAAe,WAAW,OAA+B;AACvD;AAIA,KAAI,CAAC,MAAM,QAAQ,MAAM,EAAE;AACzB,UAAQ,KAAK,0DAA0D,MAAM;AAC7E;;CAEF,MAAM,OAAO,MAAM,QAChB,QAAQ,OAAO,QAAQ,YAAY,IAAI,WAAW,0BAA0B,CAC9E;AACD,KAAI,CAAC,KAAK,QAAQ;AAChB,UAAQ,KAAK,yDAAyD,MAAM;AAC5E;;AAEF,MAAK,MAAM,OAAO,KAChB,WAAU,IAAI;AAEhB,SAAQ,IAAI,gCAAgC,KAAK,KAAK,KAAK,GAAG;;AAGhE,eAAe,8BAA0D;AACvE;AACA,MAAK,MAAM,WAAW,EAAE,UAEtB,QAAO,6BAA6B,EAAC,SADzB,MAAM,SAAS,EACuB,CAAC;AAErD,QAAO"}
|
|
@@ -4,10 +4,10 @@ import { cookies, draftMode } from "next/headers";
|
|
|
4
4
|
import { perspectiveCookieName } from "@sanity/preview-url-secret/constants";
|
|
5
5
|
import { revalidateTag } from "next/cache";
|
|
6
6
|
async function revalidateSyncTags(tags) {
|
|
7
|
-
await revalidateTag("sanity:fetch-sync-tags");
|
|
7
|
+
await revalidateTag("sanity:fetch-sync-tags", "max");
|
|
8
8
|
for (const _tag of tags) {
|
|
9
9
|
const tag = `sanity:${_tag}`;
|
|
10
|
-
revalidateTag(tag);
|
|
10
|
+
revalidateTag(tag, { expire: 0 });
|
|
11
11
|
console.log(`<SanityLive /> revalidated tag: ${tag}`);
|
|
12
12
|
}
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/live/server-actions/index.ts"],"sourcesContent":["'use server'\n\nimport type {ClientPerspective, SyncTag} from '@sanity/client'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {revalidateTag} from 'next/cache'\nimport {cookies, draftMode} from 'next/headers'\nimport {sanitizePerspective} from '../utils'\n\nexport async function revalidateSyncTags(tags: SyncTag[]): Promise<void> {\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/live/server-actions/index.ts"],"sourcesContent":["'use server'\n\nimport type {ClientPerspective, SyncTag} from '@sanity/client'\nimport {perspectiveCookieName} from '@sanity/preview-url-secret/constants'\nimport {revalidateTag} from 'next/cache'\nimport {cookies, draftMode} from 'next/headers'\nimport {sanitizePerspective} from '../utils'\n\nexport async function revalidateSyncTags(tags: SyncTag[]): Promise<void> {\n await revalidateTag('sanity:fetch-sync-tags', 'max')\n\n for (const _tag of tags) {\n const tag = `sanity:${_tag}`\n revalidateTag(tag, {expire: 0})\n console.log(`<SanityLive /> revalidated tag: ${tag}`)\n }\n}\n\nexport async function setPerspectiveCookie(perspective: ClientPerspective): Promise<void> {\n if (!(await draftMode()).isEnabled) {\n // throw new Error('Draft mode is not enabled, setting perspective cookie is not allowed')\n return\n }\n const sanitizedPerspective = sanitizePerspective(perspective, 'drafts')\n if (perspective !== sanitizedPerspective) {\n throw new Error(`Invalid perspective: ${perspective}`)\n }\n\n ;(await cookies()).set(\n perspectiveCookieName,\n Array.isArray(sanitizedPerspective) ? sanitizedPerspective.join(',') : sanitizedPerspective,\n {\n httpOnly: true,\n path: '/',\n secure: true,\n sameSite: 'none',\n },\n )\n}\n"],"mappings":";;;;;AAQA,eAAsB,mBAAmB,MAAgC;AACvE,OAAM,cAAc,0BAA0B,MAAM;AAEpD,MAAK,MAAM,QAAQ,MAAM;EACvB,MAAM,MAAM,UAAU;AACtB,gBAAc,KAAK,EAAC,QAAQ,GAAE,CAAC;AAC/B,UAAQ,IAAI,mCAAmC,MAAM;;;AAIzD,eAAsB,qBAAqB,aAA+C;AACxF,KAAI,EAAE,MAAM,WAAW,EAAE,UAEvB;CAEF,MAAM,uBAAuB,oBAAoB,aAAa,SAAS;AACvE,KAAI,gBAAgB,qBAClB,OAAM,IAAI,MAAM,wBAAwB,cAAc;AAGvD,EAAC,MAAM,SAAS,EAAE,IACjB,uBACA,MAAM,QAAQ,qBAAqB,GAAG,qBAAqB,KAAK,IAAI,GAAG,sBACvE;EACE,UAAU;EACV,MAAM;EACN,QAAQ;EACR,UAAU;EACX,CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-sanity",
|
|
3
|
-
"version": "11.5.7
|
|
3
|
+
"version": "11.5.7",
|
|
4
4
|
"description": "Sanity.io toolkit for Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -115,12 +115,12 @@
|
|
|
115
115
|
"@types/react": "^19.1.8",
|
|
116
116
|
"@types/react-dom": "^19.1.6",
|
|
117
117
|
"@vitest/coverage-v8": "^3.2.4",
|
|
118
|
-
"next": "^
|
|
119
|
-
"publint": "^0.3.
|
|
118
|
+
"next": "^15.3.5",
|
|
119
|
+
"publint": "^0.3.14",
|
|
120
120
|
"react": "^19.1.0",
|
|
121
121
|
"react-dom": "^19.1.0",
|
|
122
122
|
"styled-components": "^6.1.19",
|
|
123
|
-
"tsdown": "0.15.
|
|
123
|
+
"tsdown": "0.15.7",
|
|
124
124
|
"typescript": "5.9.3",
|
|
125
125
|
"vite-tsconfig-paths": "^5.1.4",
|
|
126
126
|
"vitest": "^3.2.4"
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// oxlint-disable-next-line no-unassigned-import
|
|
2
2
|
import 'server-only'
|
|
3
3
|
import {
|
|
4
|
+
createClient,
|
|
4
5
|
type ClientPerspective,
|
|
5
6
|
type ClientReturn,
|
|
6
7
|
type ContentSourceMap,
|
|
@@ -9,10 +10,11 @@ import {
|
|
|
9
10
|
type SanityClient,
|
|
10
11
|
type SyncTag,
|
|
11
12
|
} from 'next-sanity'
|
|
13
|
+
import {stegaEncodeSourceMap} from '@sanity/client/stega'
|
|
12
14
|
import SanityLiveClientComponent, {
|
|
13
15
|
type SanityLiveProps,
|
|
14
16
|
} from 'next-sanity/experimental/client-components/live'
|
|
15
|
-
import {cacheTag,
|
|
17
|
+
import {cacheTag, updateTag} from 'next/cache'
|
|
16
18
|
import {draftMode, cookies} from 'next/headers'
|
|
17
19
|
import {preconnect} from 'react-dom'
|
|
18
20
|
import {perspectiveCookieName} from '@sanity/preview-url-secret/constants'
|
|
@@ -23,7 +25,7 @@ import {DRAFT_SYNC_TAG_PREFIX, PUBLISHED_SYNC_TAG_PREFIX} from './constants'
|
|
|
23
25
|
/**
|
|
24
26
|
* @alpha CAUTION: This API does not follow semver and could have breaking changes in future minor releases.
|
|
25
27
|
*/
|
|
26
|
-
export async function
|
|
28
|
+
export async function resolvePerspectiveFromCookie({
|
|
27
29
|
cookies: jar,
|
|
28
30
|
}: {
|
|
29
31
|
cookies: Awaited<ReturnType<typeof cookies>>
|
|
@@ -34,9 +36,6 @@ export async function resolvePerspectiveFromCookies({
|
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
async function sanityCachedFetch<const QueryString extends string>(
|
|
37
|
-
// opaque reference to the client, allows passing stega.filter and other non-serializable client options that won't be used as cache keys
|
|
38
|
-
_client: SanityClient,
|
|
39
|
-
// The parts of the client config that should be used as cache keys
|
|
40
39
|
config: SanityClientConfig,
|
|
41
40
|
{
|
|
42
41
|
query,
|
|
@@ -48,7 +47,7 @@ async function sanityCachedFetch<const QueryString extends string>(
|
|
|
48
47
|
customCacheTags = [],
|
|
49
48
|
}: {
|
|
50
49
|
query: QueryString
|
|
51
|
-
params?: QueryParams
|
|
50
|
+
params?: QueryParams
|
|
52
51
|
perspective: Exclude<ClientPerspective, 'raw'>
|
|
53
52
|
stega: boolean
|
|
54
53
|
requestTag: string
|
|
@@ -60,18 +59,26 @@ async function sanityCachedFetch<const QueryString extends string>(
|
|
|
60
59
|
sourceMap: ContentSourceMap | null
|
|
61
60
|
tags: string[]
|
|
62
61
|
}> {
|
|
63
|
-
'use cache
|
|
62
|
+
'use cache'
|
|
64
63
|
|
|
65
|
-
const client =
|
|
64
|
+
const client = createClient({...config, useCdn: true})
|
|
66
65
|
const useCdn = perspective === 'published'
|
|
66
|
+
/**
|
|
67
|
+
* The default cache profile isn't ideal for live content, as it has unnecessary time based background validation, as well as a too lazy client stale value
|
|
68
|
+
* https://github.com/vercel/next.js/blob/8dd358002baf4244c0b2e38b5bda496daf60dacb/packages/next/cache.d.ts#L14-L26
|
|
69
|
+
*/
|
|
70
|
+
// cacheLife({
|
|
71
|
+
// stale: Infinity,
|
|
72
|
+
// revalidate: Infinity,
|
|
73
|
+
// expire: Infinity,
|
|
74
|
+
// })
|
|
67
75
|
|
|
68
|
-
const {result, resultSourceMap, syncTags} = await client.fetch(query,
|
|
76
|
+
const {result, resultSourceMap, syncTags} = await client.fetch(query, params, {
|
|
69
77
|
filterResponse: false,
|
|
70
78
|
returnQuery: false,
|
|
71
79
|
perspective,
|
|
72
80
|
useCdn,
|
|
73
|
-
|
|
74
|
-
stega,
|
|
81
|
+
resultSourceMap: stega ? 'withKeyArraySelector' : undefined, // @TODO allow passing csm for non-stega use
|
|
75
82
|
cacheMode: useCdn ? 'noStale' : undefined,
|
|
76
83
|
tag: requestTag,
|
|
77
84
|
token: perspective === 'published' ? config.token : draftToken || config.token, // @TODO can pass undefined instead of config.token here?
|
|
@@ -87,10 +94,6 @@ async function sanityCachedFetch<const QueryString extends string>(
|
|
|
87
94
|
* The tags used here, are expired later on in the `expireTags` Server Action with the `expireTag` function from `next/cache`
|
|
88
95
|
*/
|
|
89
96
|
cacheTag(...tags)
|
|
90
|
-
/**
|
|
91
|
-
* Sanity Live handles on-demand revalidation, so the default 15min time based revalidation is too short
|
|
92
|
-
*/
|
|
93
|
-
cacheLife({revalidate: 60 * 60 * 24 * 90})
|
|
94
97
|
|
|
95
98
|
return {data: result, sourceMap: resultSourceMap || null, tags}
|
|
96
99
|
}
|
|
@@ -101,13 +104,11 @@ async function sanityCachedFetch<const QueryString extends string>(
|
|
|
101
104
|
export type DefinedSanityFetchType = <const QueryString extends string>(options: {
|
|
102
105
|
query: QueryString
|
|
103
106
|
params?: QueryParams | Promise<QueryParams>
|
|
104
|
-
/**
|
|
105
|
-
* @defaultValue 'published'
|
|
106
|
-
*/
|
|
107
107
|
perspective?: Exclude<ClientPerspective, 'raw'>
|
|
108
108
|
/**
|
|
109
|
-
* Enables stega encoding of the data, this is typically only used in draft mode
|
|
110
|
-
*
|
|
109
|
+
* Enables stega encoding of the data, this is typically only used in draft mode.
|
|
110
|
+
* If `defineLive({..., stega: true})` is provided, then it defaults to `true` in Draft Mode.
|
|
111
|
+
* If `defineLive({..., stega: false})` then it defaults to `false`.
|
|
111
112
|
*/
|
|
112
113
|
stega?: boolean
|
|
113
114
|
/**
|
|
@@ -117,7 +118,7 @@ export type DefinedSanityFetchType = <const QueryString extends string>(options:
|
|
|
117
118
|
*/
|
|
118
119
|
requestTag?: string
|
|
119
120
|
/**
|
|
120
|
-
* Custom cache tags that can be used with next's `revalidateTag`
|
|
121
|
+
* Custom cache tags that can be used with next's `revalidateTag` function for custom webhook on-demand revalidation.
|
|
121
122
|
*/
|
|
122
123
|
tags?: string[]
|
|
123
124
|
}) => Promise<{
|
|
@@ -127,6 +128,10 @@ export type DefinedSanityFetchType = <const QueryString extends string>(options:
|
|
|
127
128
|
* The Content Source Map is only fetched by default in draft mode, if `stega` is `true`. Otherwise your client configuration will need to have `resultSourceMap: 'withKeyArraySelector' | true`
|
|
128
129
|
*/
|
|
129
130
|
sourceMap: ContentSourceMap | null
|
|
131
|
+
/**
|
|
132
|
+
* The perspective used to fetch the data, useful for debugging.
|
|
133
|
+
*/
|
|
134
|
+
perspective: Exclude<ClientPerspective, 'raw'>
|
|
130
135
|
/**
|
|
131
136
|
* The cache tags used with `next/cache`, useful for debugging.
|
|
132
137
|
*/
|
|
@@ -219,6 +224,11 @@ export interface DefineSanityLiveOptions {
|
|
|
219
224
|
* It is used to setup a `Live Draft Content` EventSource connection, and enables live previewing drafts stand-alone, outside of Presentation Tool.
|
|
220
225
|
*/
|
|
221
226
|
browserToken?: string | false
|
|
227
|
+
/**
|
|
228
|
+
* Optional. Include stega encoding when draft mode is enabled.
|
|
229
|
+
* @defaultValue `true` if the client configuration has the `stega.studioUrl` property set, otherwise `false`
|
|
230
|
+
*/
|
|
231
|
+
stega?: boolean
|
|
222
232
|
}
|
|
223
233
|
|
|
224
234
|
/**
|
|
@@ -255,24 +265,18 @@ export function defineLive(config: DefineSanityLiveOptions): {
|
|
|
255
265
|
}
|
|
256
266
|
|
|
257
267
|
const client = _client.withConfig({allowReconfigure: false, useCdn: false})
|
|
258
|
-
const {
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
dataset,
|
|
264
|
-
projectId,
|
|
265
|
-
requestTagPrefix,
|
|
266
|
-
} = client.config()
|
|
267
|
-
|
|
268
|
-
const sanityFetch: DefinedSanityFetchType = function sanityFetch<
|
|
268
|
+
const {token: originalToken, stega: stegaConfig} = client.config()
|
|
269
|
+
const studioUrlDefined = typeof client.config().stega.studioUrl !== 'undefined'
|
|
270
|
+
const {stega: stegaEnabled = typeof client.config().stega.studioUrl !== 'undefined'} = config
|
|
271
|
+
|
|
272
|
+
const sanityFetch: DefinedSanityFetchType = async function sanityFetch<
|
|
269
273
|
const QueryString extends string,
|
|
270
274
|
>({
|
|
271
275
|
query,
|
|
272
276
|
params = {},
|
|
273
|
-
stega
|
|
274
|
-
perspective = 'published',
|
|
277
|
+
stega: _stega,
|
|
275
278
|
tags: customCacheTags = [],
|
|
279
|
+
perspective: _perspective,
|
|
276
280
|
requestTag = 'next-loader.fetch',
|
|
277
281
|
}: {
|
|
278
282
|
query: QueryString
|
|
@@ -282,8 +286,16 @@ export function defineLive(config: DefineSanityLiveOptions): {
|
|
|
282
286
|
perspective?: Exclude<ClientPerspective, 'raw'>
|
|
283
287
|
requestTag?: string
|
|
284
288
|
}) {
|
|
285
|
-
|
|
286
|
-
|
|
289
|
+
const stega = _stega ?? (stegaEnabled && studioUrlDefined && (await draftMode()).isEnabled)
|
|
290
|
+
const perspective = _perspective ?? ((await draftMode()).isEnabled ? 'drafts' : 'published')
|
|
291
|
+
|
|
292
|
+
const {apiHost, apiVersion, useProjectHostname, dataset, projectId, requestTagPrefix} =
|
|
293
|
+
client.config()
|
|
294
|
+
const {
|
|
295
|
+
data: _data,
|
|
296
|
+
sourceMap,
|
|
297
|
+
tags,
|
|
298
|
+
} = await sanityCachedFetch(
|
|
287
299
|
{
|
|
288
300
|
apiHost,
|
|
289
301
|
apiVersion,
|
|
@@ -295,7 +307,7 @@ export function defineLive(config: DefineSanityLiveOptions): {
|
|
|
295
307
|
},
|
|
296
308
|
{
|
|
297
309
|
query,
|
|
298
|
-
params,
|
|
310
|
+
params: await params,
|
|
299
311
|
perspective,
|
|
300
312
|
stega,
|
|
301
313
|
requestTag,
|
|
@@ -303,6 +315,13 @@ export function defineLive(config: DefineSanityLiveOptions): {
|
|
|
303
315
|
customCacheTags,
|
|
304
316
|
},
|
|
305
317
|
)
|
|
318
|
+
|
|
319
|
+
const data =
|
|
320
|
+
stega && sourceMap
|
|
321
|
+
? stegaEncodeSourceMap(_data, sourceMap, {...stegaConfig, enabled: true})
|
|
322
|
+
: _data
|
|
323
|
+
|
|
324
|
+
return {data, sourceMap, tags, perspective}
|
|
306
325
|
}
|
|
307
326
|
|
|
308
327
|
const SanityLive: React.ComponentType<DefinedSanityLiveProps> = function SanityLive(props) {
|
|
@@ -357,7 +376,7 @@ interface SanityLiveServerComponentProps
|
|
|
357
376
|
|
|
358
377
|
const SanityLiveServerComponent: React.ComponentType<SanityLiveServerComponentProps> =
|
|
359
378
|
async function SanityLiveServerComponent(props) {
|
|
360
|
-
|
|
379
|
+
'use cache'
|
|
361
380
|
// @TODO should this be 'max' instead?, or configured by changing the default cache profile?
|
|
362
381
|
// cacheLife({
|
|
363
382
|
// stale: Infinity,
|
|
@@ -434,7 +453,15 @@ async function resolveDraftModePerspective(): Promise<ClientPerspective> {
|
|
|
434
453
|
'use server'
|
|
435
454
|
if ((await draftMode()).isEnabled) {
|
|
436
455
|
const jar = await cookies()
|
|
437
|
-
return
|
|
456
|
+
return resolvePerspectiveFromCookie({cookies: jar})
|
|
438
457
|
}
|
|
439
458
|
return 'published'
|
|
440
459
|
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Add more stuff:
|
|
463
|
+
* - sanityFetchMetadata: sanityFetch({query, params, stega: false, perspective: 'auto'})
|
|
464
|
+
* - sanityFetchStaticParams: sanityFetch({query, params, stega: false, perspective: 'published', cacheMode: undefined})
|
|
465
|
+
* - sanityFetchCached: sanityFetch({query, params, stega: 'opt-in',perspective: 'opt-in'}) useful for 'use cache' components, no unexpected magic, maybe this will be `sanityFetch` instead
|
|
466
|
+
* - sanityFetchDynamic: sanityFetch({query, params, stega: 'auto', perspective: 'auto'}) just like sanityFetch of old, since `sanityFetch` will likely become opt-in
|
|
467
|
+
*/
|
|
@@ -7,13 +7,11 @@ import {cookies, draftMode} from 'next/headers'
|
|
|
7
7
|
import {sanitizePerspective} from '../utils'
|
|
8
8
|
|
|
9
9
|
export async function revalidateSyncTags(tags: SyncTag[]): Promise<void> {
|
|
10
|
-
|
|
11
|
-
await revalidateTag('sanity:fetch-sync-tags')
|
|
10
|
+
await revalidateTag('sanity:fetch-sync-tags', 'max')
|
|
12
11
|
|
|
13
12
|
for (const _tag of tags) {
|
|
14
13
|
const tag = `sanity:${_tag}`
|
|
15
|
-
|
|
16
|
-
revalidateTag(tag)
|
|
14
|
+
revalidateTag(tag, {expire: 0})
|
|
17
15
|
console.log(`<SanityLive /> revalidated tag: ${tag}`)
|
|
18
16
|
}
|
|
19
17
|
}
|