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
56
Versions
147
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 1.6.20-canary.1 to 1.6.20-canary.3

25

dist/_chunks-es/constants.js

@@ -1,24 +0,1 @@

const schemaType = "sanity.previewUrlSecret";
const schemaIdPrefix = "sanity-preview-url-secret";
const apiVersion = "2023-11-09";
const urlSearchParamPreviewSecret = "sanity-preview-secret";
const urlSearchParamPreviewPathname = "sanity-preview-pathname";
const isDev = process.env.NODE_ENV === "development";
const SECRET_TTL = 60 * 60;
const fetchSecretQuery = (
/* groq */
`*[_type == "${schemaType}" && secret == $secret && dateTime(_updatedAt) > dateTime(now()) - ${SECRET_TTL}][0]{
_id,
_updatedAt,
secret,
studioUrl,
}`
);
const deleteExpiredSecretsQuery = (
/* groq */
`*[_type == "${schemaType}" && dateTime(_updatedAt) <= dateTime(now()) - ${SECRET_TTL}]`
);
const tag = "sanity.preview-url-secret";
export { SECRET_TTL, apiVersion, deleteExpiredSecretsQuery, fetchSecretQuery, isDev, schemaIdPrefix, schemaType, tag, urlSearchParamPreviewPathname, urlSearchParamPreviewSecret };
//# sourceMappingURL=constants.js.map
const e="sanity.previewUrlSecret",t="sanity-preview-url-secret",s="2023-11-09",a="sanity-preview-secret",r="sanity-preview-pathname",i="development"===process.env.NODE_ENV,n=3600,d=`*[_type == "${e}" && secret == $secret && dateTime(_updatedAt) > dateTime(now()) - 3600][0]{\n _id,\n _updatedAt,\n secret,\n studioUrl,\n}`,p=`*[_type == "${e}" && dateTime(_updatedAt) <= dateTime(now()) - 3600]`,c="sanity.preview-url-secret";export{n as S,s as a,r as b,e as c,p as d,d as f,i,t as s,c as t,a as u};//# sourceMappingURL=constants.js.map

@@ -1,33 +0,1 @@

import { uuid } from '@sanity/uuid';
import { apiVersion, SECRET_TTL, schemaIdPrefix, schemaType, tag, deleteExpiredSecretsQuery } from './_chunks-es/constants.js';
function generateUrlSecret() {
if (typeof crypto !== "undefined") {
const array = new Uint8Array(16);
crypto.getRandomValues(array);
let key = "";
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 Math.random().toString(36).slice(2);
}
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 = `${schemaIdPrefix}.${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 };
} finally {
await client.delete({ query: deleteExpiredSecretsQuery });
}
}
export { createPreviewSecret };
//# sourceMappingURL=create-secret.js.map
import{uuid as t}from"@sanity/uuid";import{a as e,S as a,s as r,c as n,t as s,d as o}from"./_chunks-es/constants.js";async function c(c,i,p,u,l=t()){const y=c.withConfig({apiVersion:e});try{const t=new Date(Date.now()+1e3*a),e=`${r}.${l}`,o=function(){if(typeof crypto<"u"){const t=new Uint8Array(16);crypto.getRandomValues(t);let e="";for(let a=0;a<t.length;a++)e+=t[a].toString(16).padStart(2,"0");return e=btoa(e).replace(/\+/g,"-").replace(/\//g,"_").replace(/[=]+$/,""),e}return Math.random().toString(36).slice(2)}(),c=y.patch(e).set({secret:o,source:i,studioUrl:p,userId:u});return await y.transaction().createOrReplace({_id:e,_type:n}).patch(c).commit({tag:s}),{secret:o,expiresAt:t}}finally{await y.delete({query:o})}}export{c as createPreviewSecret};//# sourceMappingURL=create-secret.js.map

@@ -1,49 +0,1 @@

import { urlSearchParamPreviewSecret, urlSearchParamPreviewPathname } from './_chunks-es/constants.js';
function definePreviewUrl(options) {
const {
draftMode,
previewMode,
origin = typeof location === "undefined" ? "https://localhost" : location.origin
} = options;
const enableUrl = previewMode?.enable || draftMode?.enable;
let { preview = "/" } = options;
const productionUrl = new URL(preview, origin);
const enablePreviewModeUrl = enableUrl ? new URL(enableUrl, origin) : void 0;
return async (context) => {
try {
if (context.previewSearchParam) {
const restoredUrl = new URL(context.previewSearchParam, productionUrl);
if (restoredUrl.origin === productionUrl.origin) {
preview = `${restoredUrl.pathname}${restoredUrl.search}`;
}
} else if (context.referrer) {
const referrerUrl = new URL(context.referrer);
if (referrerUrl.origin === productionUrl.origin) {
preview = `${referrerUrl.pathname}${referrerUrl.search}`;
}
}
} catch {
}
if (typeof location !== "undefined" && 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);
if (previewUrl.pathname !== enablePreviewModeRequestUrl.pathname) {
searchParams.set(
urlSearchParamPreviewPathname,
`${previewUrl.pathname}${previewUrl.search}`
);
}
return enablePreviewModeRequestUrl.toString();
}
return previewUrl.toString();
};
}
export { definePreviewUrl };
//# sourceMappingURL=define-preview-url.js.map
import{u as e,b as t}from"./_chunks-es/constants.js";function r(r){const{draftMode:a,previewMode:n,origin:o=(typeof location>"u"?"https://localhost":location.origin)}=r,i=n?.enable||a?.enable;let{preview:s="/"}=r;const c=new URL(s,o),h=i?new URL(i,o):void 0;return async a=>{try{if(a.previewSearchParam){const e=new URL(a.previewSearchParam,c);e.origin===c.origin&&(s=`${e.pathname}${e.search}`)}else if(a.referrer){const e=new URL(a.referrer);e.origin===c.origin&&(s=`${e.pathname}${e.search}`)}}catch{}typeof location<"u"&&location.origin===c.origin&&a.studioBasePath&&(s.startsWith(`${a.studioBasePath}/`)||s===a.studioBasePath)&&(s=r.preview||"/");const n=new URL(s,c);if(h){const r=new URL(h),{searchParams:o}=r;return o.set(e,a.previewUrlSecret),n.pathname!==r.pathname&&o.set(t,`${n.pathname}${n.search}`),r.toString()}return n.toString()}}export{r as definePreviewUrl};//# sourceMappingURL=define-preview-url.js.map

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

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

@@ -1,98 +0,1 @@

import { apiVersion, urlSearchParamPreviewSecret, urlSearchParamPreviewPathname, fetchSecretQuery, tag, isDev } from './_chunks-es/constants.js';
function createClientWithConfig(client) {
if (!client) {
throw new TypeError("`client` is required");
}
if (!client.config().token) {
throw new TypeError("`client` must have a `token` specified");
}
return client.withConfig({
// Userland might be using an API version that's too old to use perspectives
apiVersion,
// We can't use the CDN, the secret is typically validated right after it's created
useCdn: false,
// The documents that hold secrets are never drafts
perspective: "published",
// Don't waste time returning a source map, we don't need it
resultSourceMap: false,
// @ts-expect-error - If stega is enabled, make sure it's disabled
stega: false
});
}
function parsePreviewUrl(unsafeUrl) {
const url = new URL(unsafeUrl, "http://localhost");
const secret = url.searchParams.get(urlSearchParamPreviewSecret);
if (!secret) {
throw new Error("Missing secret");
}
let redirectTo = void 0;
const unsafeRedirectTo = url.searchParams.get(urlSearchParamPreviewPathname);
if (unsafeRedirectTo) {
const { pathname, search, hash } = new URL(unsafeRedirectTo, "http://localhost");
redirectTo = `${pathname}${search}${hash}`;
}
return { secret, redirectTo };
}
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 };
}
const result = await client.fetch(
fetchSecretQuery,
{ secret },
{
tag,
// In CloudFlare Workers we can't pass the cache header
...!disableCacheNoStore ? { cache: "no-store" } : void 0
}
);
if (!result?._id || !result?._updatedAt || !result?.secret) {
return { isValid: false, studioUrl: null };
}
return { isValid: secret === result.secret, studioUrl: result.studioUrl };
}
async function validatePreviewUrl(_client, previewUrl, disableCacheNoStore = globalThis.navigator?.userAgent === "Cloudflare-Workers") {
const client = createClientWithConfig(_client);
let parsedPreviewUrl;
try {
parsedPreviewUrl = parsePreviewUrl(previewUrl);
} catch (error) {
if (isDev) {
console.error("Failed to parse preview URL", error, {
previewUrl,
client
});
}
return { isValid: false };
}
const { isValid, studioUrl } = await validateSecret(
client,
parsedPreviewUrl.secret,
disableCacheNoStore
);
const redirectTo = isValid ? parsedPreviewUrl.redirectTo : void 0;
let studioOrigin;
if (isValid) {
try {
studioOrigin = new URL(studioUrl).origin;
} catch (error) {
if (isDev) {
console.error("Failed to parse studioUrl", error, {
previewUrl,
studioUrl
});
}
}
}
return { isValid, redirectTo, studioOrigin };
}
export { urlSearchParamPreviewPathname, urlSearchParamPreviewSecret, validatePreviewUrl };
//# sourceMappingURL=index.js.map
import{a as e,u as t,b as r,f as i,t as s,i as o}from"./_chunks-es/constants.js";async function n(n,a,c="Cloudflare-Workers"===globalThis.navigator?.userAgent){const l=function(t){if(!t)throw new TypeError("`client` is required");if(!t.config().token)throw new TypeError("`client` must have a `token` specified");return t.withConfig({apiVersion:e,useCdn:!1,perspective:"published",resultSourceMap:!1,stega:!1})}(n);let u;try{u=function(e){const i=new URL(e,"http://localhost"),s=i.searchParams.get(t);if(!s)throw new Error("Missing secret");let o;const n=i.searchParams.get(r);if(n){const{pathname:e,search:t,hash:r}=new URL(n,"http://localhost");o=`${e}${t}${r}`}return{secret:s,redirectTo:o}}(a)}catch(e){return o&&console.error("Failed to parse preview URL",e,{previewUrl:a,client:l}),{isValid:!1}}const{isValid:d,studioUrl:h}=await async function(e,t,r){if(typeof EdgeRuntime<"u"&&await new Promise((e=>setTimeout(e,300))),!t||!t.trim())return{isValid:!1,studioUrl:null};const o=await e.fetch(i,{secret:t},{tag:s,...r?void 0:{cache:"no-store"}});return o?._id&&o?._updatedAt&&o?.secret?{isValid:t===o.secret,studioUrl:o.studioUrl}:{isValid:!1,studioUrl:null}}(l,u.secret,c),p=d?u.redirectTo:void 0;let f;if(d)try{f=new URL(h).origin}catch(e){o&&console.error("Failed to parse studioUrl",e,{previewUrl:a,studioUrl:h})}return{isValid:d,redirectTo:p,studioOrigin:f}}export{r as urlSearchParamPreviewPathname,t as urlSearchParamPreviewSecret,n as validatePreviewUrl};//# sourceMappingURL=index.js.map

@@ -1,84 +0,1 @@

import { defineType, definePlugin } from 'sanity';
import { schemaType, SECRET_TTL } from './_chunks-es/constants.js';
import { LockIcon, CloseCircleIcon, CheckmarkCircleIcon } from '@sanity/icons';
const debugUrlSecretsType = defineType({
type: "document",
icon: LockIcon,
name: schemaType,
title: "@sanity/preview-url-secret",
readOnly: true,
fields: [
{
type: "string",
name: "secret",
title: "Secret"
},
{
type: "string",
name: "source",
title: "Source Tool"
},
{
type: "string",
name: "studioUrl",
title: "Studio URL"
},
{
type: "string",
name: "userId",
title: "Sanity User ID"
}
],
preview: {
select: {
source: "source",
studioUrl: "studioUrl",
updatedAt: "_updatedAt"
},
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;
return {
title: url ? `${url.host}${url.pathname}` : data.source,
subtitle: expired ? "Expired" : `Expires in ${Math.round((expiresAt.getTime() - Date.now()) / (1e3 * 60))} minutes`,
media: icon
};
}
}
});
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 };
//# sourceMappingURL=sanity-plugin-debug-secrets.js.map
import{defineType as e,definePlugin as t}from"sanity";import{c as i,S as s}from"./_chunks-es/constants.js";import{LockIcon as n,CloseCircleIcon as r,CheckmarkCircleIcon as o}from"@sanity/icons";const a=e({type:"document",icon:n,name:i,title:"@sanity/preview-url-secret",readOnly:!0,fields:[{type:"string",name:"secret",title:"Secret"},{type:"string",name:"source",title:"Source Tool"},{type:"string",name:"studioUrl",title:"Studio URL"},{type:"string",name:"userId",title:"Sanity User ID"}],preview:{select:{source:"source",studioUrl:"studioUrl",updatedAt:"_updatedAt"},prepare(e){const t=e.studioUrl?new URL(e.studioUrl,location.origin):void 0,i=new Date(e.updatedAt).getTime(),n=new Date(i+1e3*s),a=n<new Date,c=a?r:o;return{title:t?`${t.host}${t.pathname}`:e.source,subtitle:a?"Expired":`Expires in ${Math.round((n.getTime()-Date.now())/6e4)} minutes`,media:c}}}}),c=t((()=>({name:"sanity-plugin-debug-secrets",schema:{types:[a]},document:{actions:(e,t)=>t.schemaType!==i?e:e.filter((({action:e})=>"delete"===e)),inspectors:(e,t)=>t.documentType!==i?e:[],unstable_fieldActions:(e,t)=>t.schemaType.name!==i?e:[]}})));export{c as debugSecrets};//# sourceMappingURL=sanity-plugin-debug-secrets.js.map

@@ -1,20 +0,1 @@

import { urlSearchParamPreviewPathname, urlSearchParamPreviewSecret } from './_chunks-es/constants.js';
function withoutSecretSearchParams(url) {
const newUrl = new URL(url);
newUrl.searchParams.delete(urlSearchParamPreviewPathname);
newUrl.searchParams.delete(urlSearchParamPreviewSecret);
return newUrl;
}
function hasSecretSearchParams(url) {
return url.searchParams.has(urlSearchParamPreviewSecret);
}
function setSecretSearchParams(url, secret, redirectTo) {
const newUrl = new URL(url);
newUrl.searchParams.set(urlSearchParamPreviewSecret, secret);
newUrl.searchParams.set(urlSearchParamPreviewPathname, redirectTo);
return newUrl;
}
export { hasSecretSearchParams, setSecretSearchParams, withoutSecretSearchParams };
//# sourceMappingURL=without-secret-search-params.js.map
import{b as s,u as e}from"./_chunks-es/constants.js";function a(a){const r=new URL(a);return r.searchParams.delete(s),r.searchParams.delete(e),r}function r(s){return s.searchParams.has(e)}function n(a,r,n){const t=new URL(a);return t.searchParams.set(e,r),t.searchParams.set(s,n),t}export{r as hasSecretSearchParams,n as setSecretSearchParams,a as withoutSecretSearchParams};//# sourceMappingURL=without-secret-search-params.js.map

2

package.json
{
"name": "@sanity/preview-url-secret",
"version": "1.6.20-canary.1",
"version": "1.6.20-canary.3",
"homepage": "https://github.com/sanity-io/visual-editing/tree/main/packages/preview-url-secret#readme",

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

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