Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@sanity/preview-url-secret

Package Overview
Dependencies
Maintainers
61
Versions
143
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@sanity/preview-url-secret - npm Package Compare versions

Comparing version 2.0.0 to 2.0.1-canary.0

13

dist/_chunks-es/generateSecret.js
function generateUrlSecret() {
if (typeof crypto !== "undefined") {
if (typeof crypto < "u") {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
let key = "";
for (let i = 0; i < array.length; i++) {
for (let i = 0; i < array.length; i++)
key += array[i].toString(16).padStart(2, "0");
}
key = btoa(key).replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]+$/, "");
return key;
return key = btoa(key).replace(/\+/g, "-").replace(/\//g, "_").replace(/[=]+$/, ""), key;
}
return Math.random().toString(36).slice(2);
}
export { generateUrlSecret };
export {
generateUrlSecret
};
//# sourceMappingURL=generateSecret.js.map

@@ -1,12 +0,2 @@

const schemaType = "sanity.previewUrlSecret";
const schemaIdPrefix = "sanity-preview-url-secret";
const schemaIdSingleton = `${schemaIdPrefix}.share-access`;
const schemaTypeSingleton = "sanity.previewUrlShareAccess";
const apiVersion = "2023-11-09";
const urlSearchParamPreviewSecret = "sanity-preview-secret";
const urlSearchParamPreviewPathname = "sanity-preview-pathname";
const urlSearchParamPreviewPerspective = "sanity-preview-perspective";
const isDev = process.env.NODE_ENV === "development";
const SECRET_TTL = 60 * 60;
const fetchSecretQuery = (
const schemaType = "sanity.previewUrlSecret", schemaIdPrefix = "sanity-preview-url-secret", schemaIdSingleton = `${schemaIdPrefix}.share-access`, schemaTypeSingleton = "sanity.previewUrlShareAccess", apiVersion = "2023-11-09", urlSearchParamPreviewSecret = "sanity-preview-secret", urlSearchParamPreviewPathname = "sanity-preview-pathname", urlSearchParamPreviewPerspective = "sanity-preview-perspective", isDev = process.env.NODE_ENV === "development", SECRET_TTL = 60 * 60, fetchSecretQuery = (
/* groq */

@@ -19,8 +9,6 @@ `*[_type == "${schemaType}" && secret == $secret && dateTime(_updatedAt) > dateTime(now()) - ${SECRET_TTL}][0]{

}`
);
const fetchSharedAccessQuery = (
), fetchSharedAccessQuery = (
/* groq */
`*[_id == "${schemaIdSingleton}" && _type == "${schemaTypeSingleton}"][0].secret`
);
const fetchSharedAccessSecretQuery = (
), fetchSharedAccessSecretQuery = (
/* groq */

@@ -31,11 +19,24 @@ `*[_id == "${schemaIdSingleton}" && _type == "${schemaTypeSingleton}" && secret == $secret][0]{

}`
);
const deleteExpiredSecretsQuery = (
), deleteExpiredSecretsQuery = (
/* groq */
`*[_type == "${schemaType}" && dateTime(_updatedAt) <= dateTime(now()) - ${SECRET_TTL}]`
);
const tag = "sanity.preview-url-secret";
const perspectiveCookieName = "sanity-preview-perspective";
export { SECRET_TTL, apiVersion, deleteExpiredSecretsQuery, fetchSecretQuery, fetchSharedAccessQuery, fetchSharedAccessSecretQuery, isDev, perspectiveCookieName, schemaIdPrefix, schemaIdSingleton, schemaType, schemaTypeSingleton, tag, urlSearchParamPreviewPathname, urlSearchParamPreviewPerspective, urlSearchParamPreviewSecret };
), tag = "sanity.preview-url-secret", perspectiveCookieName = "sanity-preview-perspective";
export {
SECRET_TTL,
apiVersion,
deleteExpiredSecretsQuery,
fetchSecretQuery,
fetchSharedAccessQuery,
fetchSharedAccessSecretQuery,
isDev,
perspectiveCookieName,
schemaIdPrefix,
schemaIdSingleton,
schemaType,
schemaTypeSingleton,
tag,
urlSearchParamPreviewPathname,
urlSearchParamPreviewPerspective,
urlSearchParamPreviewSecret
};
//# sourceMappingURL=constants.js.map

@@ -1,14 +0,9 @@

import { uuid } from '@sanity/uuid';
import { apiVersion, SECRET_TTL, schemaType, tag, deleteExpiredSecretsQuery } from './constants.js';
import { generateUrlSecret } from './_chunks-es/generateSecret.js';
import { uuid } from "@sanity/uuid";
import { apiVersion, SECRET_TTL, schemaType, tag, deleteExpiredSecretsQuery } from "./constants.js";
import { generateUrlSecret } from "./_chunks-es/generateSecret.js";
async function createPreviewSecret(_client, source, studioUrl, userId, id = uuid()) {
const client = _client.withConfig({ apiVersion });
try {
const expiresAt = new Date(Date.now() + 1e3 * SECRET_TTL);
const _id = `drafts.${id}`;
const newSecret = generateUrlSecret();
const patch = client.patch(_id).set({ secret: newSecret, source, studioUrl, userId });
await client.transaction().createOrReplace({ _id, _type: schemaType }).patch(patch).commit({ tag });
return { secret: newSecret, expiresAt };
const expiresAt = new Date(Date.now() + 1e3 * SECRET_TTL), _id = `drafts.${id}`, newSecret = generateUrlSecret(), patch = client.patch(_id).set({ secret: newSecret, source, studioUrl, userId });
return await client.transaction().createOrReplace({ _id, _type: schemaType }).patch(patch).commit({ tag }), { secret: newSecret, expiresAt };
} finally {

@@ -22,4 +17,5 @@ try {

}
export { createPreviewSecret };
export {
createPreviewSecret
};
//# sourceMappingURL=create-secret.js.map

@@ -1,3 +0,2 @@

import { urlSearchParamPreviewSecret, urlSearchParamPreviewPerspective, urlSearchParamPreviewPathname } from './constants.js';
import { urlSearchParamPreviewSecret, urlSearchParamPreviewPerspective, urlSearchParamPreviewPathname } from "./constants.js";
function definePreviewUrl(options) {

@@ -7,8 +6,6 @@ const {

previewMode,
origin = typeof location === "undefined" ? "https://localhost" : location.origin
} = options;
const enableUrl = previewMode?.enable || draftMode?.enable;
origin = typeof location > "u" ? "https://localhost" : location.origin
} = options, enableUrl = previewMode?.enable || draftMode?.enable;
let { preview = "/" } = options;
const productionUrl = new URL(preview, origin);
const enablePreviewModeUrl = enableUrl ? new URL(enableUrl, origin) : void 0;
const productionUrl = new URL(preview, origin), enablePreviewModeUrl = enableUrl ? new URL(enableUrl, origin) : void 0;
return async (context) => {

@@ -18,24 +15,14 @@ try {

const restoredUrl = new URL(context.previewSearchParam, productionUrl);
if (restoredUrl.origin === productionUrl.origin) {
preview = `${restoredUrl.pathname}${restoredUrl.search}`;
}
restoredUrl.origin === productionUrl.origin && (preview = `${restoredUrl.pathname}${restoredUrl.search}`);
}
} catch {
}
if (typeof location !== "undefined" && location.origin === productionUrl.origin && context.studioBasePath && (preview.startsWith(`${context.studioBasePath}/`) || preview === context.studioBasePath)) {
preview = options.preview || "/";
}
typeof location < "u" && location.origin === productionUrl.origin && context.studioBasePath && (preview.startsWith(`${context.studioBasePath}/`) || preview === context.studioBasePath) && (preview = options.preview || "/");
const previewUrl = new URL(preview, productionUrl);
if (enablePreviewModeUrl) {
const enablePreviewModeRequestUrl = new URL(enablePreviewModeUrl);
const { searchParams } = enablePreviewModeRequestUrl;
searchParams.set(urlSearchParamPreviewSecret, context.previewUrlSecret);
searchParams.set(urlSearchParamPreviewPerspective, context.studioPreviewPerspective);
if (previewUrl.pathname !== enablePreviewModeRequestUrl.pathname) {
searchParams.set(
urlSearchParamPreviewPathname,
`${previewUrl.pathname}${previewUrl.search}`
);
}
return enablePreviewModeRequestUrl.toString();
const enablePreviewModeRequestUrl = new URL(enablePreviewModeUrl), { searchParams } = enablePreviewModeRequestUrl;
return searchParams.set(urlSearchParamPreviewSecret, context.previewUrlSecret), searchParams.set(urlSearchParamPreviewPerspective, context.studioPreviewPerspective), previewUrl.pathname !== enablePreviewModeRequestUrl.pathname && searchParams.set(
urlSearchParamPreviewPathname,
`${previewUrl.pathname}${previewUrl.search}`
), enablePreviewModeRequestUrl.toString();
}

@@ -45,4 +32,5 @@ return previewUrl.toString();

}
export { definePreviewUrl };
export {
definePreviewUrl
};
//# sourceMappingURL=define-preview-url.js.map

@@ -1,11 +0,8 @@

import { urlSearchParamPreviewPathname } from './constants.js';
import { urlSearchParamPreviewPathname } from "./constants.js";
function getRedirectTo(url) {
if (url.searchParams.has(urlSearchParamPreviewPathname)) {
return new URL(url.searchParams.get(urlSearchParamPreviewPathname), url.origin);
}
return url;
return url.searchParams.has(urlSearchParamPreviewPathname) ? new URL(url.searchParams.get(urlSearchParamPreviewPathname), url.origin) : url;
}
export { getRedirectTo };
export {
getRedirectTo
};
//# sourceMappingURL=get-redirect-to.js.map

@@ -1,10 +0,7 @@

import { apiVersion, urlSearchParamPreviewSecret, urlSearchParamPreviewPerspective, urlSearchParamPreviewPathname, fetchSecretQuery, fetchSharedAccessSecretQuery, tag, isDev } from './constants.js';
import { apiVersion, urlSearchParamPreviewSecret, urlSearchParamPreviewPerspective, urlSearchParamPreviewPathname, fetchSecretQuery, fetchSharedAccessSecretQuery, tag, isDev } from "./constants.js";
function createClientWithConfig(client) {
if (!client) {
if (!client)
throw new TypeError("`client` is required");
}
if (!client.config().token) {
if (!client.config().token)
throw new TypeError("`client` must have a `token` specified");
}
return client.withConfig({

@@ -15,18 +12,15 @@ perspective: "raw",

// We can't use the CDN, the secret is typically validated right after it's created
useCdn: false,
useCdn: !1,
// Don't waste time returning a source map, we don't need it
resultSourceMap: false,
resultSourceMap: !1,
// @ts-expect-error - If stega is enabled, make sure it's disabled
stega: false
stega: !1
});
}
function parsePreviewUrl(unsafeUrl) {
const url = new URL(unsafeUrl, "http://localhost");
const secret = url.searchParams.get(urlSearchParamPreviewSecret);
if (!secret) {
const url = new URL(unsafeUrl, "http://localhost"), secret = url.searchParams.get(urlSearchParamPreviewSecret);
if (!secret)
throw new Error("Missing secret");
}
const studioPreviewPerspective = url.searchParams.get(urlSearchParamPreviewPerspective);
let redirectTo = void 0;
let redirectTo;
const unsafeRedirectTo = url.searchParams.get(urlSearchParamPreviewPathname);

@@ -39,10 +33,5 @@ if (unsafeRedirectTo) {

}
async function validateSecret(client, secret, disableCacheNoStore) {
if (typeof EdgeRuntime !== "undefined") {
await new Promise((resolve) => setTimeout(resolve, 300));
}
if (!secret || !secret.trim()) {
return { isValid: false, studioUrl: null };
}
if (typeof EdgeRuntime < "u" && await new Promise((resolve) => setTimeout(resolve, 300)), !secret || !secret.trim())
return { isValid: !1, studioUrl: null };
const { private: privateSecret, public: publicSecret } = await client.fetch(

@@ -57,17 +46,7 @@ `{

// In CloudFlare Workers we can't pass the cache header
...!disableCacheNoStore ? { cache: "no-store" } : void 0
...disableCacheNoStore ? void 0 : { cache: "no-store" }
}
);
if (privateSecret) {
if (!privateSecret?._id || !privateSecret?._updatedAt || !privateSecret?.secret) {
return { isValid: false, studioUrl: null };
}
return { isValid: secret === privateSecret.secret, studioUrl: privateSecret.studioUrl };
}
if (!publicSecret?.secret) {
return { isValid: false, studioUrl: null };
}
return { isValid: secret === publicSecret.secret, studioUrl: publicSecret.studioUrl };
return privateSecret ? !privateSecret?._id || !privateSecret?._updatedAt || !privateSecret?.secret ? { isValid: !1, studioUrl: null } : { isValid: secret === privateSecret.secret, studioUrl: privateSecret.studioUrl } : publicSecret?.secret ? { isValid: secret === publicSecret.secret, studioUrl: publicSecret.studioUrl } : { isValid: !1, studioUrl: null };
}
async function validatePreviewUrl(_client, previewUrl, disableCacheNoStore = globalThis.navigator?.userAgent === "Cloudflare-Workers") {

@@ -79,9 +58,6 @@ const client = createClientWithConfig(_client);

} catch (error) {
if (isDev) {
console.error("Failed to parse preview URL", error, {
previewUrl,
client
});
}
return { isValid: false };
return isDev && console.error("Failed to parse preview URL", error, {
previewUrl,
client
}), { isValid: !1 };
}

@@ -92,22 +68,20 @@ const { isValid, studioUrl } = await validateSecret(

disableCacheNoStore
);
const redirectTo = isValid ? parsedPreviewUrl.redirectTo : void 0;
const studioPreviewPerspective = isValid ? parsedPreviewUrl.studioPreviewPerspective : void 0;
), redirectTo = isValid ? parsedPreviewUrl.redirectTo : void 0, studioPreviewPerspective = isValid ? parsedPreviewUrl.studioPreviewPerspective : void 0;
let studioOrigin;
if (isValid) {
if (isValid)
try {
studioOrigin = new URL(studioUrl).origin;
} catch (error) {
if (isDev) {
console.error("Failed to parse studioUrl", error, {
previewUrl,
studioUrl
});
}
isDev && console.error("Failed to parse studioUrl", error, {
previewUrl,
studioUrl
});
}
}
return { isValid, redirectTo, studioOrigin, studioPreviewPerspective };
}
export { urlSearchParamPreviewPathname, urlSearchParamPreviewSecret, validatePreviewUrl };
export {
urlSearchParamPreviewPathname,
urlSearchParamPreviewSecret,
validatePreviewUrl
};
//# sourceMappingURL=index.js.map

@@ -1,5 +0,4 @@

import { defineType, definePlugin } from 'sanity';
import { schemaType, SECRET_TTL } from './constants.js';
import { LockIcon, CloseCircleIcon, CheckmarkCircleIcon } from '@sanity/icons';
import { defineType, definePlugin } from "sanity";
import { schemaType, SECRET_TTL } from "./constants.js";
import { LockIcon, CloseCircleIcon, CheckmarkCircleIcon } from "@sanity/icons";
const debugUrlSecretsType = defineType({

@@ -10,3 +9,3 @@ type: "document",

title: "@sanity/preview-url-secret",
readOnly: true,
readOnly: !0,
fields: [

@@ -41,7 +40,3 @@ {

prepare(data) {
const url = data.studioUrl ? new URL(data.studioUrl, location.origin) : void 0;
const updatedAt = new Date(data.updatedAt).getTime();
const expiresAt = new Date(updatedAt + 1e3 * SECRET_TTL);
const expired = expiresAt < /* @__PURE__ */ new Date();
const icon = expired ? CloseCircleIcon : CheckmarkCircleIcon;
const url = data.studioUrl ? new URL(data.studioUrl, location.origin) : void 0, updatedAt = new Date(data.updatedAt).getTime(), expiresAt = new Date(updatedAt + 1e3 * SECRET_TTL), expired = expiresAt < /* @__PURE__ */ new Date(), icon = expired ? CloseCircleIcon : CheckmarkCircleIcon;
return {

@@ -54,34 +49,16 @@ title: url ? `${url.host}${url.pathname}` : data.source,

}
});
const debugSecrets = definePlugin(() => {
return {
name: "sanity-plugin-debug-secrets",
schema: {
types: [debugUrlSecretsType]
},
document: {
actions: (prev, context) => {
if (context.schemaType !== schemaType) {
return prev;
}
return prev.filter(({ action }) => action === "delete");
},
inspectors: (prev, context) => {
if (context.documentType !== schemaType) {
return prev;
}
return [];
},
unstable_fieldActions: (prev, context) => {
if (context.schemaType.name !== schemaType) {
return prev;
}
return [];
}
}
};
});
export { debugSecrets };
}), debugSecrets = definePlugin(() => ({
name: "sanity-plugin-debug-secrets",
schema: {
types: [debugUrlSecretsType]
},
document: {
actions: (prev, context) => context.schemaType !== schemaType ? prev : prev.filter(({ action }) => action === "delete"),
inspectors: (prev, context) => context.documentType !== schemaType ? prev : [],
unstable_fieldActions: (prev, context) => context.schemaType.name !== schemaType ? prev : []
}
}));
export {
debugSecrets
};
//# sourceMappingURL=sanity-plugin-debug-secrets.js.map

@@ -1,18 +0,15 @@

import { apiVersion, schemaIdSingleton, schemaTypeSingleton, tag } from './constants.js';
import { generateUrlSecret } from './_chunks-es/generateSecret.js';
import { apiVersion, schemaIdSingleton, schemaTypeSingleton, tag } from "./constants.js";
import { generateUrlSecret } from "./_chunks-es/generateSecret.js";
async function enablePreviewAccessSharing(_client, source, studioUrl, userId) {
const client = _client.withConfig({ apiVersion });
const newSecret = generateUrlSecret();
const patch = client.patch(schemaIdSingleton).set({ secret: newSecret, studioUrl, userId });
await client.transaction().createIfNotExists({ _id: schemaIdSingleton, _type: schemaTypeSingleton, source, studioUrl, userId }).patch(patch).commit({ tag });
return { secret: newSecret };
const client = _client.withConfig({ apiVersion }), newSecret = generateUrlSecret(), patch = client.patch(schemaIdSingleton).set({ secret: newSecret, studioUrl, userId });
return await client.transaction().createIfNotExists({ _id: schemaIdSingleton, _type: schemaTypeSingleton, source, studioUrl, userId }).patch(patch).commit({ tag }), { secret: newSecret };
}
async function disablePreviewAccessSharing(_client, source, studioUrl, userId) {
const client = _client.withConfig({ apiVersion });
const patch = client.patch(schemaIdSingleton).set({ secret: null, studioUrl, userId });
const client = _client.withConfig({ apiVersion }), patch = client.patch(schemaIdSingleton).set({ secret: null, studioUrl, userId });
await client.transaction().createIfNotExists({ _id: schemaIdSingleton, _type: schemaTypeSingleton, source, studioUrl, userId }).patch(patch).commit({ tag });
}
export { disablePreviewAccessSharing, enablePreviewAccessSharing };
export {
disablePreviewAccessSharing,
enablePreviewAccessSharing
};
//# sourceMappingURL=toggle-preview-access-sharing.js.map

@@ -1,10 +0,5 @@

import { urlSearchParamPreviewPathname, urlSearchParamPreviewSecret, urlSearchParamPreviewPerspective } from './constants.js';
import { urlSearchParamPreviewPathname, urlSearchParamPreviewSecret, urlSearchParamPreviewPerspective } from "./constants.js";
function withoutSecretSearchParams(url) {
const newUrl = new URL(url);
const { searchParams } = newUrl;
searchParams.delete(urlSearchParamPreviewPathname);
searchParams.delete(urlSearchParamPreviewSecret);
searchParams.delete(urlSearchParamPreviewPerspective);
return newUrl;
const newUrl = new URL(url), { searchParams } = newUrl;
return searchParams.delete(urlSearchParamPreviewPathname), searchParams.delete(urlSearchParamPreviewSecret), searchParams.delete(urlSearchParamPreviewPerspective), newUrl;
}

@@ -15,11 +10,10 @@ function hasSecretSearchParams(url) {

function setSecretSearchParams(url, secret, redirectTo, perspective) {
const newUrl = new URL(url);
const { searchParams } = newUrl;
searchParams.set(urlSearchParamPreviewSecret, secret);
searchParams.set(urlSearchParamPreviewPathname, redirectTo);
searchParams.set(urlSearchParamPreviewPerspective, perspective);
return newUrl;
const newUrl = new URL(url), { searchParams } = newUrl;
return searchParams.set(urlSearchParamPreviewSecret, secret), searchParams.set(urlSearchParamPreviewPathname, redirectTo), searchParams.set(urlSearchParamPreviewPerspective, perspective), newUrl;
}
export { hasSecretSearchParams, setSecretSearchParams, withoutSecretSearchParams };
export {
hasSecretSearchParams,
setSecretSearchParams,
withoutSecretSearchParams
};
//# sourceMappingURL=without-secret-search-params.js.map
{
"name": "@sanity/preview-url-secret",
"version": "2.0.0",
"version": "2.0.1-canary.0",
"homepage": "https://github.com/sanity-io/visual-editing/tree/main/packages/preview-url-secret#readme",

@@ -122,5 +122,5 @@ "bugs": {

"vitest": "^2.1.3",
"@repo/eslint-config": "0.0.0",
"@repo/package.config": "0.0.0",
"@repo/prettier-config": "0.0.0"
"@repo/prettier-config": "0.0.0",
"@repo/eslint-config": "0.0.0"
},

@@ -127,0 +127,0 @@ "peerDependencies": {

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc