sanity-plugin-iframe-pane 2.6.1 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -5
- package/lib/index.cjs +167 -187
- package/lib/index.cjs.js +0 -2
- package/lib/index.cjs.map +1 -1
- package/lib/index.d.ts +28 -26
- package/lib/index.js +172 -191
- package/lib/index.js.map +1 -1
- package/package.json +9 -34
- package/src/DisplayUrl.tsx +5 -10
- package/src/Iframe.tsx +227 -175
- package/src/Toolbar.tsx +11 -21
- package/src/index.ts +1 -8
- package/src/types.ts +0 -6
- package/lib/_chunks/is-valid-secret-VKMJU99B.cjs +0 -64
- package/lib/_chunks/is-valid-secret-VKMJU99B.cjs.map +0 -1
- package/lib/_chunks/is-valid-secret-zi24WaHG.js +0 -58
- package/lib/_chunks/is-valid-secret-zi24WaHG.js.map +0 -1
- package/lib/_chunks/utils-HbzA_NjI.cjs +0 -61
- package/lib/_chunks/utils-HbzA_NjI.cjs.map +0 -1
- package/lib/_chunks/utils-j2CLEOFh.js +0 -56
- package/lib/_chunks/utils-j2CLEOFh.js.map +0 -1
- package/lib/is-valid-secret.cjs +0 -8
- package/lib/is-valid-secret.cjs.map +0 -1
- package/lib/is-valid-secret.d.ts +0 -33
- package/lib/is-valid-secret.js +0 -2
- package/lib/is-valid-secret.js.map +0 -1
- package/lib/preview-url.cjs +0 -56
- package/lib/preview-url.cjs.js +0 -4
- package/lib/preview-url.cjs.map +0 -1
- package/lib/preview-url.d.ts +0 -17
- package/lib/preview-url.js +0 -51
- package/lib/preview-url.js.map +0 -1
- package/src/GetUrlSecret.tsx +0 -80
- package/src/defineUrlResolver.tsx +0 -34
- package/src/is-valid-secret.ts +0 -1
- package/src/isValidSecret.tsx +0 -98
- package/src/preview-url.ts +0 -6
- package/src/previewUrl.ts +0 -62
- package/src/utils.ts +0 -45
package/src/isValidSecret.tsx
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
import {name} from '../package.json'
|
|
2
|
-
|
|
3
|
-
export type UrlSecretId = `${string}.${string}`
|
|
4
|
-
|
|
5
|
-
// updated within the hour, if it's older it'll create a new secret or return null
|
|
6
|
-
export const SECRET_TTL = 60 * 60
|
|
7
|
-
|
|
8
|
-
export const fetchSecretQuery = /* groq */ `*[_id == $id && dateTime(_updatedAt) > dateTime(now()) - ${SECRET_TTL}][0]{secret, _updatedAt}`
|
|
9
|
-
export type FetchSecretResponse = {
|
|
10
|
-
secret: string | null
|
|
11
|
-
_updatedAt: string | null
|
|
12
|
-
} | null
|
|
13
|
-
|
|
14
|
-
export const tag = name
|
|
15
|
-
|
|
16
|
-
export const apiVersion = '2023-08-08'
|
|
17
|
-
|
|
18
|
-
export type SanityClientLike = {
|
|
19
|
-
config(): {token?: string}
|
|
20
|
-
withConfig(config: {
|
|
21
|
-
apiVersion?: string
|
|
22
|
-
useCdn?: boolean
|
|
23
|
-
perspective: 'raw'
|
|
24
|
-
resultSourceMap: boolean
|
|
25
|
-
}): SanityClientLike
|
|
26
|
-
fetch<
|
|
27
|
-
R,
|
|
28
|
-
Q = {
|
|
29
|
-
[key: string]: any
|
|
30
|
-
},
|
|
31
|
-
>(
|
|
32
|
-
query: string,
|
|
33
|
-
params: Q,
|
|
34
|
-
options: {tag?: string},
|
|
35
|
-
): Promise<R>
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const isDev = process.env.NODE_ENV === 'development'
|
|
39
|
-
|
|
40
|
-
export async function isValidSecret(
|
|
41
|
-
client: SanityClientLike,
|
|
42
|
-
urlSecretId: UrlSecretId,
|
|
43
|
-
urlSecret: string,
|
|
44
|
-
): Promise<boolean> {
|
|
45
|
-
if (!urlSecret) {
|
|
46
|
-
throw new TypeError('`urlSecret` is required')
|
|
47
|
-
}
|
|
48
|
-
if (!urlSecretId) {
|
|
49
|
-
throw new TypeError('`urlSecretId` is required')
|
|
50
|
-
}
|
|
51
|
-
if (!urlSecretId.includes('.')) {
|
|
52
|
-
throw new TypeError(
|
|
53
|
-
`\`urlSecretId\` must have a dot prefix, \`${urlSecretId}\` is not secure, add a prefix, for example \`preview.${urlSecretId}\` `,
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
if (!client) {
|
|
57
|
-
throw new TypeError('`client` is required')
|
|
58
|
-
}
|
|
59
|
-
if (!client.config().token) {
|
|
60
|
-
throw new TypeError('`client` must have a `token` specified')
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// If we're in the Edge Runtime it's usually too quick and we need to delay fetching the secret a little bit
|
|
64
|
-
// eslint-disable-next-line no-warning-comments
|
|
65
|
-
// @ts-expect-error -- @TODO add typings for EdgeRuntime
|
|
66
|
-
if (typeof EdgeRuntime !== 'undefined') {
|
|
67
|
-
await new Promise((resolve) => setTimeout(resolve, 300))
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const customClient = client.withConfig({
|
|
71
|
-
apiVersion,
|
|
72
|
-
useCdn: false,
|
|
73
|
-
perspective: 'raw',
|
|
74
|
-
resultSourceMap: false,
|
|
75
|
-
})
|
|
76
|
-
const data = await customClient.fetch<FetchSecretResponse>(
|
|
77
|
-
fetchSecretQuery,
|
|
78
|
-
{id: urlSecretId},
|
|
79
|
-
// @ts-expect-error -- the `cache` option is valid, but not in the types when NextJS typings aren't installed
|
|
80
|
-
{cache: 'no-store', tag},
|
|
81
|
-
)
|
|
82
|
-
// eslint-disable-next-line no-process-env
|
|
83
|
-
if (!data?.secret && isDev) {
|
|
84
|
-
const exists = await client.fetch<null | Record<string, any>>(
|
|
85
|
-
/* groq */ `*[_id == $id][0]`,
|
|
86
|
-
{id: urlSecretId},
|
|
87
|
-
// @ts-expect-error -- the `cache` option is valid, but not in the types when NextJS typings aren't installed
|
|
88
|
-
{cache: 'no-store', tag},
|
|
89
|
-
)
|
|
90
|
-
if (!exists) {
|
|
91
|
-
throw new TypeError(
|
|
92
|
-
`Unable to find a secret in the dataset, with the id \`${urlSecretId}\`. Have you set the \`urlSecretId\` option in your \`Iframe\` and \`previewUrl\` configurations?`,
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return data?.secret === urlSecret
|
|
98
|
-
}
|
package/src/preview-url.ts
DELETED
package/src/previewUrl.ts
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* This plugin sets up the "Open preview (CTRL + ALT + O)" in the dropdown menu that hosts
|
|
3
|
-
* other actions like "Review changes" and "Inspect"
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import {definePlugin, SanityDocument} from 'sanity'
|
|
7
|
-
|
|
8
|
-
import {defineUrlResolver, DefineUrlResolverOptions} from './defineUrlResolver'
|
|
9
|
-
import {
|
|
10
|
-
apiVersion,
|
|
11
|
-
fetchSecretQuery,
|
|
12
|
-
FetchSecretResponse,
|
|
13
|
-
tag,
|
|
14
|
-
type UrlSecretId,
|
|
15
|
-
} from './isValidSecret'
|
|
16
|
-
import {MissingSlug} from './types'
|
|
17
|
-
import {patchUrlSecret} from './utils'
|
|
18
|
-
|
|
19
|
-
export type {DefineUrlResolverOptions, UrlSecretId}
|
|
20
|
-
|
|
21
|
-
export interface ProductionUrlOptions extends DefineUrlResolverOptions {
|
|
22
|
-
matchTypes?: string[]
|
|
23
|
-
urlSecretId?: UrlSecretId
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export const previewUrl = definePlugin<ProductionUrlOptions>(
|
|
27
|
-
({urlSecretId, base, matchTypes, requiresSlug}) => {
|
|
28
|
-
if (!base) {
|
|
29
|
-
throw new TypeError('`base` is required')
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const urlResolver = defineUrlResolver({base, requiresSlug})
|
|
33
|
-
return {
|
|
34
|
-
name: 'previewUrl',
|
|
35
|
-
document: {
|
|
36
|
-
productionUrl: async (prev, {document, getClient}) => {
|
|
37
|
-
if (matchTypes?.length && !matchTypes.includes(document._type)) {
|
|
38
|
-
return prev
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
let urlSecret: string | null = null
|
|
42
|
-
if (urlSecretId) {
|
|
43
|
-
const client = getClient({apiVersion})
|
|
44
|
-
const data = await client.fetch<FetchSecretResponse>(
|
|
45
|
-
fetchSecretQuery,
|
|
46
|
-
{id: urlSecretId},
|
|
47
|
-
{tag},
|
|
48
|
-
)
|
|
49
|
-
urlSecret = data?.secret ? data.secret : await patchUrlSecret(client, urlSecretId)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const url = urlResolver(document as SanityDocument, urlSecret)
|
|
53
|
-
if (url) {
|
|
54
|
-
return url === MissingSlug ? prev : url
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
return prev
|
|
58
|
-
},
|
|
59
|
-
},
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
)
|
package/src/utils.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import type {SanityClient} from 'sanity'
|
|
2
|
-
|
|
3
|
-
import {SECRET_TTL, tag, UrlSecretId} from './isValidSecret'
|
|
4
|
-
|
|
5
|
-
export function getExpiresAt(_updatedAt: Date) {
|
|
6
|
-
return new Date(_updatedAt.getTime() + 1000 * SECRET_TTL)
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
function generateUrlSecret() {
|
|
10
|
-
// Try using WebCrypto if available
|
|
11
|
-
if (typeof crypto !== 'undefined') {
|
|
12
|
-
// Generate a random array of 16 bytes
|
|
13
|
-
const array = new Uint8Array(16)
|
|
14
|
-
crypto.getRandomValues(array)
|
|
15
|
-
|
|
16
|
-
// Convert the array to a URL-safe string
|
|
17
|
-
let key = ''
|
|
18
|
-
for (let i = 0; i < array.length; i++) {
|
|
19
|
-
// Convert each byte to a 2-digit hexadecimal number
|
|
20
|
-
key += array[i].toString(16).padStart(2, '0')
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// Replace '+' and '/' from base64url to '-' and '_'
|
|
24
|
-
key = btoa(key).replace(/\+/g, '-').replace(/\//g, '_').replace(/[=]+$/, '')
|
|
25
|
-
|
|
26
|
-
return key
|
|
27
|
-
}
|
|
28
|
-
// If not fallback to Math.random
|
|
29
|
-
return Math.random().toString(36).slice(2)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export async function patchUrlSecret(
|
|
33
|
-
client: SanityClient,
|
|
34
|
-
urlSecretId: UrlSecretId,
|
|
35
|
-
signal?: AbortSignal,
|
|
36
|
-
): Promise<string> {
|
|
37
|
-
const newSecret = generateUrlSecret()
|
|
38
|
-
const patch = client.patch(urlSecretId).set({secret: newSecret})
|
|
39
|
-
await client
|
|
40
|
-
.transaction()
|
|
41
|
-
.createIfNotExists({_id: urlSecretId, _type: urlSecretId})
|
|
42
|
-
.patch(patch)
|
|
43
|
-
.commit({tag, signal})
|
|
44
|
-
return newSecret
|
|
45
|
-
}
|