@sanity/preview-kit-compat
Advanced tools
Comparing version 1.5.5 to 1.5.6-canary.1
@@ -39,2 +39,16 @@ <!-- markdownlint-disable --><!-- textlint-disable --> | ||
## [1.5.6](https://github.com/sanity-io/visual-editing/compare/preview-kit-compat-v1.5.5...preview-kit-compat-v1.5.6) (2024-08-12) | ||
### Bug Fixes | ||
* **deps:** update dependency @sanity/client to v6.21.2 ([#1749](https://github.com/sanity-io/visual-editing/issues/1749)) ([b9efdd2](https://github.com/sanity-io/visual-editing/commit/b9efdd2a672fdef518bc22a29a25992c938ba1ef)) | ||
### Dependencies | ||
* The following workspace dependencies were updated | ||
* devDependencies | ||
* @repo/visual-editing-helpers bumped to 0.6.20 | ||
## [1.5.5](https://github.com/sanity-io/visual-editing/compare/preview-kit-compat-v1.5.4...preview-kit-compat-v1.5.5) (2024-08-02) | ||
@@ -41,0 +55,0 @@ |
@@ -1,1 +0,322 @@ | ||
import{useState as e,useEffect as n,useMemo as t,useSyncExternalStore as o,useCallback as i}from"react";let r;const s=new Uint8Array(16);function a(){if(!r&&(r=typeof crypto<"u"&&crypto.getRandomValues&&crypto.getRandomValues.bind(crypto),!r))throw new Error("crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported");return r(s)}const c=[];for(let e=0;e<256;++e)c.push((e+256).toString(16).slice(1));var d={randomUUID:typeof crypto<"u"&&crypto.randomUUID&&crypto.randomUUID.bind(crypto)};function u(e,n,t){if(d.randomUUID&&!n&&!e)return d.randomUUID();const o=(e=e||{}).random||(e.rng||a)();return o[6]=15&o[6]|64,o[8]=63&o[8]|128,function(e,n=0){return c[e[n+0]]+c[e[n+1]]+c[e[n+2]]+c[e[n+3]]+"-"+c[e[n+4]]+c[e[n+5]]+"-"+c[e[n+6]]+c[e[n+7]]+"-"+c[e[n+8]]+c[e[n+9]]+"-"+c[e[n+10]]+c[e[n+11]]+c[e[n+12]]+c[e[n+13]]+c[e[n+14]]+c[e[n+15]]}(o)}const f=["channel/disconnect","channel/response","channel/heartbeat"],l=["handshake/syn","handshake/syn-ack","handshake/ack"],p=e=>f.some((n=>n===e)),h=e=>l.some((n=>n===e)),y=({data:e={}})=>"object"==typeof e&&null!==e&&!Array.isArray(e)&&!("domain"in e)&&["id","type","from","to"].every((n=>n in e))&&e.type.startsWith("handshake/");function m(e){const n=window.self!==window.top||window.opener,t={buffer:[],id:null,origin:null,source:null,status:"connecting"};function o(n,o){if(h(n)||p(n)||"connecting"!==t.status&&"reconnecting"!==t.status){if(t.id&&t.origin&&t.source){const i={connectionId:t.id,data:o,domain:"sanity/channels",from:e.id,id:u(),to:e.connectTo,type:n};try{t.source.postMessage(i,{targetOrigin:t.origin})}catch{throw new Error(`Failed to postMessage '${i.id}' on '${e.id}'`)}}}else t.buffer.push({type:n,data:o})}function i(n){if(y(n))console.error("Visual editing package mismatch detected! Please ensure you are using the latest version of Sanity Studio and any packages listed here:\nhttps://github.com/sanity-io/visual-editing");else if(function(n){const{data:t}=n;return"sanity/channels"===t.domain&&t.to===e.id&&t.from===e.connectTo&&"channel/response"!==t.type}(n)){const{data:e}=n;if(t.origin&&n.origin!==t.origin)return;if(n.source&&t.source!==n.source&&(t.source=n.source),h(e.type)&&e.data){if("handshake/syn"===e.type)return t.origin=n.origin,t.id=e.data.id,a("connecting"),void o("handshake/syn-ack",{id:t.id});if("handshake/ack"===e.type&&e.data.id===t.id)return void a("connected")}else if(e.connectionId===t.id&&n.origin===t.origin){if("channel/disconnect"===e.type)return void a("disconnected");{const n=[e.type,e.data];r.forEach((e=>{e(...n)})),o("channel/response",{responseTo:e.id})}return}}}const r=new Set;const s=new Set;function a(e){t.status=e,s.forEach((n=>{n(e)})),"connected"===e&&function(){const e=[...t.buffer];t.buffer.splice(0,t.buffer.length),e.forEach((({type:e,data:n})=>{o(e,n)}))}()}return window.addEventListener("message",i,!1),a("connecting"),{destroy:function(){["disconnected"].includes(t.status)||a("disconnected"),r.clear(),s.clear(),window.removeEventListener("message",i,!1)},inFrame:n,send:function(e,n){o(e,n)},subscribe:function(e){return r.add(e),()=>r.delete(e)},onStatusUpdate:function(e){return s.add(e),()=>s.delete(e)}}}function g(t,o,i){const[r,s]=e(),[a,c]=e(!1);n((()=>{if(window.self===window.top&&!window.opener)return;const e=m({id:"preview-kit",connectTo:"presentation"});e.onStatusUpdate((e=>{"connected"===e?c(!0):"disconnected"===e&&c(!1)}));const n=setTimeout((()=>s(e)),0);return()=>{clearTimeout(n),e.destroy(),s(void 0)}}),[i,o]);const d=JSON.stringify(Array.from(t.keys()));n((()=>{"[]"!==d&&r&&a&&r.send("preview-kit/documents",{projectId:o,dataset:i,perspective:"previewDrafts",documents:Array.from(t.values())})}),[d,r,a,i,t,o])}function w(e){const n=t((()=>JSON.stringify(e||{})),[e]);return t((()=>JSON.parse(n)),[n])}function v(t){const{refreshInterval:r}=t,s=function(){const[t,i]=e(!1);n((()=>{i(navigator.onLine);const e=()=>i(!0),n=()=>i(!1);return window.addEventListener("online",e),window.addEventListener("offline",n),()=>{window.removeEventListener("online",e),window.removeEventListener("offline",n)}}),[]);const r=o(b,(()=>document.visibilityState),(()=>"hidden"));return!t||"hidden"===r}(),[a,c]=e("hit"),d=i((()=>(c("inflight"),()=>c("hit"))),[]);return n((()=>{if(!r||"hit"!==a)return;const e=setTimeout((()=>c("stale")),r);return()=>clearTimeout(e)}),[r,a]),n((()=>{if("hit"!==a)return;const e=()=>c("stale");return window.addEventListener("focus",e),()=>window.removeEventListener("focus",e)}),[r,a]),n((()=>{s&&"hit"===a&&c("stale"),!s&&"stale"===a&&c("refresh")}),[s,a]),[a,d]}function b(e){return document.addEventListener("visibilitychange",e),()=>document.removeEventListener("visibilitychange",e)}export{g as useDocumentsInUse,w as useQueryParams,v as useRevalidate};//# sourceMappingURL=index.js.map | ||
import { useState, useEffect, useMemo, useCallback, useSyncExternalStore } from 'react'; | ||
/** | ||
* Convert array of 16 byte values to UUID string format of the form: | ||
* XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX | ||
*/ | ||
var byteToHex = []; | ||
for (var i = 0; i < 256; ++i) { | ||
byteToHex.push((i + 0x100).toString(16).slice(1)); | ||
} | ||
function unsafeStringify(arr, offset = 0) { | ||
// Note: Be careful editing this code! It's been tuned for performance | ||
// and works in ways you may not expect. See https://github.com/uuidjs/uuid/pull/434 | ||
// | ||
// Note to future-self: No, you can't remove the `toLowerCase()` call. | ||
// REF: https://github.com/uuidjs/uuid/pull/677#issuecomment-1757351351 | ||
return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + '-' + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + '-' + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + '-' + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + '-' + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase(); | ||
} | ||
// Unique ID creation requires a high quality random # generator. In the browser we therefore | ||
// require the crypto API and do not support built-in fallback to lower quality random number | ||
// generators (like Math.random()). | ||
var getRandomValues; | ||
var rnds8 = new Uint8Array(16); | ||
function rng() { | ||
// lazy load so that environments that need to polyfill have a chance to do so | ||
if (!getRandomValues) { | ||
// getRandomValues needs to be invoked in a context where "this" is a Crypto implementation. | ||
getRandomValues = typeof crypto !== 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto); | ||
if (!getRandomValues) { | ||
throw new Error('crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported'); | ||
} | ||
} | ||
return getRandomValues(rnds8); | ||
} | ||
var randomUUID = typeof crypto !== 'undefined' && crypto.randomUUID && crypto.randomUUID.bind(crypto); | ||
var native = { | ||
randomUUID | ||
}; | ||
function v4(options, buf, offset) { | ||
if (native.randomUUID && !buf && !options) { | ||
return native.randomUUID(); | ||
} | ||
options = options || {}; | ||
var rnds = options.random || (options.rng || rng)(); | ||
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved` | ||
rnds[6] = rnds[6] & 0x0f | 0x40; | ||
rnds[8] = rnds[8] & 0x3f | 0x80; | ||
return unsafeStringify(rnds); | ||
} | ||
const INTERNAL_MSG_TYPES = [ | ||
"channel/disconnect", | ||
"channel/response", | ||
"channel/heartbeat" | ||
]; | ||
const HANDSHAKE_MSG_TYPES = [ | ||
"handshake/syn", | ||
"handshake/syn-ack", | ||
"handshake/ack" | ||
]; | ||
const isInternalMessage = (type) => { | ||
return INTERNAL_MSG_TYPES.some((t) => t === type); | ||
}; | ||
const isHandshakeMessage = (type) => { | ||
return HANDSHAKE_MSG_TYPES.some((t) => t === type); | ||
}; | ||
const isLegacyHandshakeMessage = ({ data = {} }) => { | ||
return ( | ||
// Check data is a record type | ||
typeof data === "object" && data !== null && !Array.isArray(data) && // The "domain" key was introduced in commit 4854e7f | ||
!("domain" in data) && // Check the rest of the object shape is present | ||
["id", "type", "from", "to"].every((key) => key in data) && // Prior to 4854e7f only handshake events were emitted prior to an established connection | ||
data.type.startsWith("handshake/") | ||
); | ||
}; | ||
function createChannelsNode(config) { | ||
const inFrame = window.self !== window.top || window.opener; | ||
const channel = { | ||
buffer: [], | ||
id: null, | ||
origin: null, | ||
source: null, | ||
status: "connecting" | ||
}; | ||
function flush() { | ||
const toFlush = [...channel.buffer]; | ||
channel.buffer.splice(0, channel.buffer.length); | ||
toFlush.forEach(({ type, data }) => { | ||
send(type, data); | ||
}); | ||
} | ||
function send(type, data) { | ||
if (!isHandshakeMessage(type) && !isInternalMessage(type) && (channel.status === "connecting" || channel.status === "reconnecting")) { | ||
channel.buffer.push({ type, data }); | ||
return; | ||
} | ||
if (channel.id && channel.origin && channel.source) { | ||
const msg = { | ||
connectionId: channel.id, | ||
data, | ||
domain: "sanity/channels", | ||
from: config.id, | ||
id: v4(), | ||
to: config.connectTo, | ||
type | ||
}; | ||
try { | ||
channel.source.postMessage(msg, { | ||
targetOrigin: channel.origin | ||
}); | ||
} catch (e) { | ||
throw new Error(`Failed to postMessage '${msg.id}' on '${config.id}'`); | ||
} | ||
} | ||
} | ||
function isValidMessageEvent(e) { | ||
const { data } = e; | ||
return data.domain === "sanity/channels" && data.to === config.id && data.from === config.connectTo && data.type !== "channel/response"; | ||
} | ||
function handleEvents(e) { | ||
if (isLegacyHandshakeMessage(e)) { | ||
console.error( | ||
"Visual editing package mismatch detected! Please ensure you are using the latest version of Sanity Studio and any packages listed here:\nhttps://github.com/sanity-io/visual-editing" | ||
); | ||
return; | ||
} | ||
if (isValidMessageEvent(e)) { | ||
const { data } = e; | ||
if (channel.origin && e.origin !== channel.origin) { | ||
return; | ||
} | ||
if (e.source && channel.source !== e.source) { | ||
channel.source = e.source; | ||
} | ||
if (isHandshakeMessage(data.type) && data.data) { | ||
if (data.type === "handshake/syn") { | ||
channel.origin = e.origin; | ||
channel.id = data.data["id"]; | ||
setConnectionStatus("connecting"); | ||
send("handshake/syn-ack", { id: channel.id }); | ||
return; | ||
} | ||
if (data.type === "handshake/ack" && data.data["id"] === channel.id) { | ||
setConnectionStatus("connected"); | ||
return; | ||
} | ||
} else if (data.connectionId === channel.id && e.origin === channel.origin) { | ||
if (data.type === "channel/disconnect") { | ||
setConnectionStatus("disconnected"); | ||
return; | ||
} else { | ||
const args = [data.type, data.data]; | ||
eventSubscribers.forEach((subscriber) => { | ||
subscriber(...args); | ||
}); | ||
send("channel/response", { responseTo: data.id }); | ||
} | ||
return; | ||
} | ||
} | ||
} | ||
const eventSubscribers = /* @__PURE__ */ new Set(); | ||
function subscribeToEvent(subscriber) { | ||
eventSubscribers.add(subscriber); | ||
return () => eventSubscribers.delete(subscriber); | ||
} | ||
function disconnect() { | ||
if (["disconnected"].includes(channel.status)) return; | ||
setConnectionStatus("disconnected"); | ||
} | ||
const statusSubscribers = /* @__PURE__ */ new Set(); | ||
function subscribeToStatus(subscriber) { | ||
statusSubscribers.add(subscriber); | ||
return () => statusSubscribers.delete(subscriber); | ||
} | ||
function setConnectionStatus(next) { | ||
channel.status = next; | ||
statusSubscribers.forEach((subscriber) => { | ||
subscriber(next); | ||
}); | ||
if (next === "connected") { | ||
flush(); | ||
} | ||
} | ||
function destroy() { | ||
disconnect(); | ||
eventSubscribers.clear(); | ||
statusSubscribers.clear(); | ||
window.removeEventListener("message", handleEvents, false); | ||
} | ||
function initialise() { | ||
window.addEventListener("message", handleEvents, false); | ||
setConnectionStatus("connecting"); | ||
} | ||
initialise(); | ||
function sendPublic(type, data) { | ||
send(type, data); | ||
} | ||
return { | ||
destroy, | ||
inFrame, | ||
send: sendPublic, | ||
subscribe: subscribeToEvent, | ||
onStatusUpdate: subscribeToStatus | ||
}; | ||
} | ||
function useDocumentsInUse(documentsInUse, projectId, dataset) { | ||
const [channel, setChannel] = useState(); | ||
const [connected, setConnected] = useState(false); | ||
useEffect(() => { | ||
if (window.self === window.top && !window.opener) { | ||
return; | ||
} | ||
const channel2 = createChannelsNode({ | ||
id: "preview-kit", | ||
connectTo: "presentation" | ||
}); | ||
channel2.onStatusUpdate((status) => { | ||
if (status === "connected") { | ||
setConnected(true); | ||
} else if (status === "disconnected") { | ||
setConnected(false); | ||
} | ||
}); | ||
const timeout = setTimeout(() => setChannel(channel2), 0); | ||
return () => { | ||
clearTimeout(timeout); | ||
channel2.destroy(); | ||
setChannel(void 0); | ||
}; | ||
}, [dataset, projectId]); | ||
const changedKeys = JSON.stringify(Array.from(documentsInUse.keys())); | ||
useEffect(() => { | ||
if (changedKeys !== "[]" && channel && connected) { | ||
channel.send("preview-kit/documents", { | ||
projectId, | ||
dataset, | ||
perspective: "previewDrafts", | ||
documents: Array.from(documentsInUse.values()) | ||
}); | ||
} | ||
}, [changedKeys, channel, connected, dataset, documentsInUse, projectId]); | ||
} | ||
function useQueryParams(params) { | ||
const stringifiedParams = useMemo(() => JSON.stringify(params || {}), [params]); | ||
return useMemo(() => JSON.parse(stringifiedParams), [stringifiedParams]); | ||
} | ||
function useRevalidate(props) { | ||
const { refreshInterval } = props; | ||
const shouldPause = useShouldPause(); | ||
const [state, setState] = useState("hit"); | ||
const startRefresh = useCallback(() => { | ||
setState("inflight"); | ||
return () => setState("hit"); | ||
}, []); | ||
useEffect(() => { | ||
if (!refreshInterval || state !== "hit") { | ||
return; | ||
} | ||
const timeout = setTimeout(() => setState("stale"), refreshInterval); | ||
return () => clearTimeout(timeout); | ||
}, [refreshInterval, state]); | ||
useEffect(() => { | ||
if (state !== "hit") { | ||
return; | ||
} | ||
const onFocus = () => setState("stale"); | ||
window.addEventListener("focus", onFocus); | ||
return () => window.removeEventListener("focus", onFocus); | ||
}, [refreshInterval, state]); | ||
useEffect(() => { | ||
if (shouldPause && state === "hit") { | ||
setState("stale"); | ||
} | ||
if (!shouldPause && state === "stale") { | ||
setState("refresh"); | ||
} | ||
}, [shouldPause, state]); | ||
return [state, startRefresh]; | ||
} | ||
function useShouldPause() { | ||
const [online, setOnline] = useState(false); | ||
useEffect(() => { | ||
setOnline(navigator.onLine); | ||
const online2 = () => setOnline(true); | ||
const offline = () => setOnline(false); | ||
window.addEventListener("online", online2); | ||
window.addEventListener("offline", offline); | ||
return () => { | ||
window.removeEventListener("online", online2); | ||
window.removeEventListener("offline", offline); | ||
}; | ||
}, []); | ||
const visibilityState = useSyncExternalStore( | ||
onVisibilityChange, | ||
() => document.visibilityState, | ||
() => "hidden" | ||
); | ||
if (!online) { | ||
return true; | ||
} | ||
if (visibilityState === "hidden") { | ||
return true; | ||
} | ||
return false; | ||
} | ||
function onVisibilityChange(onStoreChange) { | ||
document.addEventListener("visibilitychange", onStoreChange); | ||
return () => document.removeEventListener("visibilitychange", onStoreChange); | ||
} | ||
export { useDocumentsInUse, useQueryParams, useRevalidate }; | ||
//# sourceMappingURL=index.js.map |
{ | ||
"name": "@sanity/preview-kit-compat", | ||
"version": "1.5.5", | ||
"version": "1.5.6-canary.1", | ||
"homepage": "https://github.com/sanity-io/visual-editing/tree/main/packages/preview-kit-compat#readme", | ||
@@ -91,20 +91,20 @@ "bugs": { | ||
"devDependencies": { | ||
"@repo/channels": "0.4.0", | ||
"@repo/visual-editing-helpers": "0.6.19", | ||
"@sanity/client": "^6.21.1", | ||
"@sanity/pkg-utils": "6.9.3", | ||
"@types/react": "^18.3.3", | ||
"@typescript-eslint/eslint-plugin": "^7.13.1", | ||
"@typescript-eslint/parser": "^7.13.1", | ||
"@sanity/client": "^6.21.3", | ||
"@sanity/pkg-utils": "6.11.1", | ||
"@types/react": "^18.3.5", | ||
"@typescript-eslint/eslint-plugin": "^7.18.0", | ||
"@typescript-eslint/parser": "^7.18.0", | ||
"eslint": "^8.57.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"eslint-plugin-prettier": "^5.1.3", | ||
"eslint-plugin-simple-import-sort": "^12.1.0", | ||
"eslint-plugin-prettier": "^5.2.1", | ||
"eslint-plugin-simple-import-sort": "^12.1.1", | ||
"react": "^18.3.1", | ||
"typescript": "5.4.5", | ||
"vitest": "^1.6.0", | ||
"@repo/package.config": "0.0.0" | ||
"typescript": "5.6.2", | ||
"vitest": "^2.0.5", | ||
"@repo/channels": "0.4.0", | ||
"@repo/package.config": "0.0.0", | ||
"@repo/visual-editing-helpers": "0.6.20" | ||
}, | ||
"peerDependencies": { | ||
"@sanity/client": "^6.21.1", | ||
"@sanity/client": "^6.21.3", | ||
"react": "^18.3 || >=19.0.0-rc" | ||
@@ -120,6 +120,6 @@ }, | ||
"build": "pkg build --strict --check --clean", | ||
"dev": "pkg build --strict", | ||
"lint": "eslint .", | ||
"test": "vitest --pass-with-no-tests --typecheck", | ||
"watch": "pkg watch --strict" | ||
"test": "vitest --pass-with-no-tests --typecheck" | ||
} | ||
} |
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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
104153
719
2