@sanity/client 7.24.0 → 7.25.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 CHANGED
@@ -77,6 +77,7 @@ export async function updateDocumentTitle(_id, title) {
77
77
  - [`raw`](#raw)
78
78
  - [Fetching Content Source Maps](#fetching-content-source-maps)
79
79
  - [Using Visual editing with steganography](#using-visual-editing-with-steganography)
80
+ - [Catching unsafe string comparisons at compile time](#catching-unsafe-string-comparisons-at-compile-time)
80
81
  - [Creating Studio edit intent links](#creating-studio-edit-intent-links)
81
82
  - [Listening to queries](#listening-to-queries)
82
83
  - [Fetch a single document](#fetch-a-single-document)
@@ -189,7 +190,7 @@ pnpm install @sanity/client
189
190
 
190
191
  `const client = createClient(options)`
191
192
 
192
- Initializes a new Sanity Client. Required options are `projectId`, `dataset`, and `apiVersion`. [We encourage setting `useCdn` to either `true` or `false`.](https://www.sanity.io/help/js-client-cdn-configuration) The default is `true`. If you're not sure which option to choose we recommend starting with `true` and revise later if you find that you require uncached content. [Our awesome Slack community can help guide you on how to avoid stale data tailored to your tech stack and architecture.](https://slack.sanity.io/)
193
+ Initializes a new Sanity Client. Required options are `projectId`, `dataset`, and `apiVersion`. [We encourage setting `useCdn` to either `true` or `false`.](https://www.sanity.io/help/js-client-cdn-configuration) The default is `true`. If you're not sure which option to choose we recommend starting with `true` and revise later if you find that you require uncached content. [Our awesome Discord community can help guide you on how to avoid stale data tailored to your tech stack and architecture.](https://snty.link/community)
193
194
 
194
195
  #### [ESM](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/)
195
196
 
@@ -892,6 +893,46 @@ const result = await client.fetch('*[_type == "video"][0]')
892
893
  const videoAsset = stegaClean(result.videoAsset)
893
894
  ```
894
895
 
896
+ #### Catching unsafe string comparisons at compile time
897
+
898
+ Strings with stega payloads contain invisible characters, so comparing them against string literals fails in surprising ways: `imageLocation === 'left'` is `false` even though `imageLocation` looks exactly like `'left'` when logged. The branded types available on `@sanity/client/stega` turn these bugs into compile errors.
899
+
900
+ If you use [Sanity TypeGen](https://www.sanity.io/docs/sanity-typegen), pass `ClientReturnStega` as the first generic to `client.fetch` and every string in the result that may contain stega payloads is branded as `StegaString`:
901
+
902
+ ```ts
903
+ import {createClient} from '@sanity/client'
904
+ import {type ClientReturnStega, stegaClean} from '@sanity/client/stega'
905
+
906
+ const query = `*[_type == "post"][0]{title, imageLocation, "slug": slug.current}`
907
+ const post = await client.fetch<ClientReturnStega<typeof query>>(query)
908
+
909
+ // Rendering strings is allowed, stega payloads are meant to end up in text nodes
910
+ document.querySelector('h1')!.textContent = post.title
911
+
912
+ // Comparing against literals without cleaning is now a compile error:
913
+ // "This comparison appears to be unintentional because the types have no overlap"
914
+ if (post.imageLocation === 'left') {
915
+ }
916
+
917
+ // Cleaning the value removes the brand and recovers the original type, e.g. 'left' | 'right'
918
+ if (stegaClean(post.imageLocation) === 'left') {
919
+ }
920
+ ```
921
+
922
+ Properties that are never stega encoded keep their plain string types: keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref` and friends, so narrowing unions on `_type` keeps working), `slug.current` patterns, and Portable Text properties like `style`, `listItem` and `marks`. Everything else is assumed to be "poisoned", even if the stega `filter` skips it at runtime (URLs, dates and such), as a redundant `stegaClean` is harmless while a missing one is a bug.
923
+
924
+ If you type your query results manually, brand them with the `StegaBranded` helper type, or re-type already fetched data with the `stegaBrand` identity function:
925
+
926
+ ```ts
927
+ import {type StegaBranded, stegaBrand} from '@sanity/client/stega'
928
+
929
+ const post = await client.fetch<StegaBranded<Post>>(query)
930
+ // or
931
+ const branded = stegaBrand(await loadQuery<Post>(query))
932
+ ```
933
+
934
+ Note that branded strings stay assignable to `string`, that's what keeps JSX text nodes, template literals and string methods working, so props typed as plain `string` (like `className` or `href`) still accept them. The brand catches comparisons, `switch` cases and assignments to literal unions.
935
+
895
936
  #### Creating Studio edit intent links
896
937
 
897
938
  If you want to create an edit link to something that isn't a string, or a field that isn't rendered directly, like a `slug` used in a URL but not rendered on the page, you can use the `resolveEditUrl` function.
@@ -1 +1 @@
1
- {"version":3,"file":"stegaClean.cjs","sources":["../../src/util/isRecord.ts","../../node_modules/@vercel/stega/dist/index.mjs","../../src/stega/stegaClean.ts"],"sourcesContent":["/** @internal */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","var p={0:8203,1:8204,2:8205,3:8290,4:8291,5:8288,6:65279,7:8289,8:119155,9:119156,a:119157,b:119158,c:119159,d:119160,e:119161,f:119162},l={0:8203,1:8204,2:8205,3:65279},d={0:String.fromCodePoint(l[0]),1:String.fromCodePoint(l[1]),2:String.fromCodePoint(l[2]),3:String.fromCodePoint(l[3])},u=new Array(4).fill(String.fromCodePoint(l[0])).join(\"\"),g=String.fromCharCode(0);function A(e){let r=JSON.stringify(e),t=new TextEncoder().encode(r),i=\"\";for(let c=0;c<t.length;c++){let n=t[c];i+=d[n>>6&3]+d[n>>4&3]+d[n>>2&3]+d[n&3]}return u+i}function C(e){let r=JSON.stringify(e);return Array.from(r).map(t=>{let i=t.charCodeAt(0);if(i>255)throw new Error(`Only ASCII edit info can be encoded. Error attempting to encode ${r} on character ${t} (${i})`);return Array.from(i.toString(16).padStart(2,\"0\")).map(c=>String.fromCodePoint(p[c])).join(\"\")}).join(\"\")}function I(e){return!Number.isNaN(Number(e))||/[a-z]/i.test(e)&&!/\\d+(?:[-:\\/]\\d+){2}(?:T\\d+(?:[-:\\/]\\d+){1,2}(\\.\\d+)?Z?)?/.test(e)?!1:!!Date.parse(e)}function S(e){try{new URL(e,e.startsWith(\"/\")?\"https://acme.com\":void 0)}catch{return!1}return!0}function y(e,r,t=\"auto\"){return t===!0||t===\"auto\"&&(I(e)||S(e))?e:`${e}${A(r)}`}var m=Object.fromEntries(Object.entries(d).map(e=>[e[1],+e[0]])),T=Object.fromEntries(Object.entries(p).map(e=>e.reverse())),h=`${Object.values(p).map(e=>`\\\\u{${e.toString(16)}}`).join(\"\")}`,x=new RegExp(`[${h}]{4,}`,\"gu\");function X(e){let r=e.match(x);if(r)return E(r[0],!0)[0]}function M(e){let r=e.match(x);if(r)return r.map(t=>E(t)).flat()}function E(e,r=!1){let t=Array.from(e),i=1/0,c=-1;for(let n=0;n<t.length;++n)t[n]===u[0]&&t[n+1]===u[1]&&t[n+2]===u[2]&&t[n+3]===u[3]&&(i=Math.min(i,n),c=Math.max(c,n));if(c===-1)return _(t,r);for(let n=i;n<=c;++n)if(!((t.length-n)%4))try{let f=t.slice(n+4),s=new Uint8Array(f.length/4);for(let o=0;o<s.length;o++)s[o]=m[f[o*4]]<<6|m[f[o*4+1]]<<4|m[f[o*4+2]]<<2|m[f[o*4+3]];let a=new TextDecoder().decode(s);if(r){let o=a.indexOf(g);return o===-1&&(o=a.length),[JSON.parse(a.slice(0,o))]}return a.split(g).filter(Boolean).map(o=>JSON.parse(o))}catch{}return[]}function _(e,r){var f;let t=[];for(let s=e.length*.5;s--;){let a=`${T[e[s*2].codePointAt(0)]}${T[e[s*2+1].codePointAt(0)]}`;t.unshift(String.fromCharCode(parseInt(a,16)))}let i=[],c=[t.join(\"\")],n=10;for(;c.length;){let s=c.shift();try{if(i.push(JSON.parse(s)),r)return i}catch(a){if(!n--)throw a;let o=+((f=a.message.match(/\\sposition\\s(\\d+)$/))==null?void 0:f[1]);if(!o)throw a;c.unshift(s.substring(0,o),s.substring(o))}}return i}function P(e){var r;return{cleaned:e.replace(x,\"\"),encoded:((r=e.match(x))==null?void 0:r[0])||\"\"}}function w(e){return e&&JSON.parse(P(JSON.stringify(e)).cleaned)}export{x as VERCEL_STEGA_REGEX,C as legacyStegaEncode,w as vercelStegaClean,y as vercelStegaCombine,X as vercelStegaDecode,M as vercelStegaDecodeAll,A as vercelStegaEncode,P as vercelStegaSplit};\n","import {vercelStegaClean} from '@vercel/stega'\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * @public\n */\nexport function stegaClean<Result = unknown>(result: Result): Result {\n return vercelStegaClean<Result>(result)\n}\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * @alpha\n * @deprecated Use `stegaClean` instead\n */\nexport const vercelStegaCleanAll = stegaClean\n"],"names":["vercelStegaClean"],"mappings":";AACO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,SAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;ACHG,IAAC,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,OAAM,GAAE,MAAK,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,OAAM,GAAE,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,IAAE,EAAC,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,EAAC,GAAE,IAAE,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;AAA2B,SAAS,EAAE,GAAE;AAAC,MAAI,IAAE,KAAK,UAAU,CAAC,GAAE,IAAE,IAAI,YAAW,EAAG,OAAO,CAAC,GAAE,IAAE;AAAG,WAAQ,IAAE,GAAE,IAAE,EAAE,QAAO,KAAI;AAAC,QAAI,IAAE,EAAE,CAAC;AAAE,SAAG,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,IAAE,CAAC;AAAA,EAAC;AAAC,SAAO,IAAE;AAAC;AAA6T,SAAS,EAAE,GAAE;AAAC,SAAM,CAAC,OAAO,MAAM,OAAO,CAAC,CAAC,KAAG,SAAS,KAAK,CAAC,KAAG,CAAC,2DAA2D,KAAK,CAAC,IAAE,KAAG,CAAC,CAAC,KAAK,MAAM,CAAC;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,MAAG;AAAC,QAAI,IAAI,GAAE,EAAE,WAAW,GAAG,IAAE,qBAAmB,MAAM;AAAA,EAAC,QAAM;AAAC,WAAM;AAAA,EAAE;AAAC;AAAQ;AAAC,SAAS,EAAE,GAAE,GAAE,IAAE,QAAO;AAAC,SAAO,MAAI,MAAI,MAAI,WAAS,EAAE,CAAC,KAAG,EAAE,CAAC,KAAG,IAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;AAAE;AAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,CAAC,EAAE,CAAC,GAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAAI,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,EAAE,QAAO,CAAE,CAAC;AAAC,IAAC,IAAE,GAAG,OAAO,OAAO,CAAC,EAAE,IAAI,OAAG,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,IAAG,IAAE,IAAI,OAAO,IAAI,CAAC,SAAQ,IAAI;AAA6lC,SAAS,EAAE,GAAE;AAAC,MAAI;AAAE,SAAM,EAAC,SAAQ,EAAE,QAAQ,GAAE,EAAE,GAAE,WAAU,IAAE,EAAE,MAAM,CAAC,MAAI,OAAK,SAAO,EAAE,CAAC,MAAI,GAAE;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,SAAO,KAAG,KAAK,MAAM,EAAE,KAAK,UAAU,CAAC,CAAC,EAAE,OAAO;AAAC;ACOlnF,SAAS,WAA6B,QAAwB;AACnE,SAAOA,EAAyB,MAAM;AACxC;AAQO,MAAM,sBAAsB;;;;;","x_google_ignoreList":[1]}
1
+ {"version":3,"file":"stegaClean.cjs","sources":["../../src/util/isRecord.ts","../../node_modules/@vercel/stega/dist/index.mjs","../../src/stega/stegaClean.ts"],"sourcesContent":["/** @internal */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","var p={0:8203,1:8204,2:8205,3:8290,4:8291,5:8288,6:65279,7:8289,8:119155,9:119156,a:119157,b:119158,c:119159,d:119160,e:119161,f:119162},l={0:8203,1:8204,2:8205,3:65279},d={0:String.fromCodePoint(l[0]),1:String.fromCodePoint(l[1]),2:String.fromCodePoint(l[2]),3:String.fromCodePoint(l[3])},u=new Array(4).fill(String.fromCodePoint(l[0])).join(\"\"),g=String.fromCharCode(0);function A(e){let r=JSON.stringify(e),t=new TextEncoder().encode(r),i=\"\";for(let c=0;c<t.length;c++){let n=t[c];i+=d[n>>6&3]+d[n>>4&3]+d[n>>2&3]+d[n&3]}return u+i}function C(e){let r=JSON.stringify(e);return Array.from(r).map(t=>{let i=t.charCodeAt(0);if(i>255)throw new Error(`Only ASCII edit info can be encoded. Error attempting to encode ${r} on character ${t} (${i})`);return Array.from(i.toString(16).padStart(2,\"0\")).map(c=>String.fromCodePoint(p[c])).join(\"\")}).join(\"\")}function I(e){return!Number.isNaN(Number(e))||/[a-z]/i.test(e)&&!/\\d+(?:[-:\\/]\\d+){2}(?:T\\d+(?:[-:\\/]\\d+){1,2}(\\.\\d+)?Z?)?/.test(e)?!1:!!Date.parse(e)}function S(e){try{new URL(e,e.startsWith(\"/\")?\"https://acme.com\":void 0)}catch{return!1}return!0}function y(e,r,t=\"auto\"){return t===!0||t===\"auto\"&&(I(e)||S(e))?e:`${e}${A(r)}`}var m=Object.fromEntries(Object.entries(d).map(e=>[e[1],+e[0]])),T=Object.fromEntries(Object.entries(p).map(e=>e.reverse())),h=`${Object.values(p).map(e=>`\\\\u{${e.toString(16)}}`).join(\"\")}`,x=new RegExp(`[${h}]{4,}`,\"gu\");function X(e){let r=e.match(x);if(r)return E(r[0],!0)[0]}function M(e){let r=e.match(x);if(r)return r.map(t=>E(t)).flat()}function E(e,r=!1){let t=Array.from(e),i=1/0,c=-1;for(let n=0;n<t.length;++n)t[n]===u[0]&&t[n+1]===u[1]&&t[n+2]===u[2]&&t[n+3]===u[3]&&(i=Math.min(i,n),c=Math.max(c,n));if(c===-1)return _(t,r);for(let n=i;n<=c;++n)if(!((t.length-n)%4))try{let f=t.slice(n+4),s=new Uint8Array(f.length/4);for(let o=0;o<s.length;o++)s[o]=m[f[o*4]]<<6|m[f[o*4+1]]<<4|m[f[o*4+2]]<<2|m[f[o*4+3]];let a=new TextDecoder().decode(s);if(r){let o=a.indexOf(g);return o===-1&&(o=a.length),[JSON.parse(a.slice(0,o))]}return a.split(g).filter(Boolean).map(o=>JSON.parse(o))}catch{}return[]}function _(e,r){var f;let t=[];for(let s=e.length*.5;s--;){let a=`${T[e[s*2].codePointAt(0)]}${T[e[s*2+1].codePointAt(0)]}`;t.unshift(String.fromCharCode(parseInt(a,16)))}let i=[],c=[t.join(\"\")],n=10;for(;c.length;){let s=c.shift();try{if(i.push(JSON.parse(s)),r)return i}catch(a){if(!n--)throw a;let o=+((f=a.message.match(/\\sposition\\s(\\d+)$/))==null?void 0:f[1]);if(!o)throw a;c.unshift(s.substring(0,o),s.substring(o))}}return i}function P(e){var r;return{cleaned:e.replace(x,\"\"),encoded:((r=e.match(x))==null?void 0:r[0])||\"\"}}function w(e){return e&&JSON.parse(P(JSON.stringify(e)).cleaned)}export{x as VERCEL_STEGA_REGEX,C as legacyStegaEncode,w as vercelStegaClean,y as vercelStegaCombine,X as vercelStegaDecode,M as vercelStegaDecodeAll,A as vercelStegaEncode,P as vercelStegaSplit};\n","import {vercelStegaClean} from '@vercel/stega'\n\n/**\n * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were\n * branded as `StegaString` are returned to their original type, everything else is left as-is.\n * @public\n */\nexport type StegaCleaned<T> = 0 extends 1 & T\n ? T\n : T extends {readonly ' stegaBrand': infer Original extends string}\n ? Original\n : T extends string | number | bigint | boolean | null | undefined\n ? T\n : T extends Date | RegExp | ((...args: never[]) => unknown)\n ? T\n : T extends readonly unknown[]\n ? {[Index in keyof T]: StegaCleaned<T[Index]>}\n : T extends object\n ? {[K in keyof T]: StegaCleaned<T[K]>}\n : T\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),\n * the brand is stripped and the original string type is restored.\n * @public\n */\nexport function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result> {\n return vercelStegaClean(result) as StegaCleaned<Result>\n}\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * @alpha\n * @deprecated Use `stegaClean` instead\n */\nexport const vercelStegaCleanAll = stegaClean\n"],"names":["vercelStegaClean"],"mappings":";AACO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,SAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;ACHG,IAAC,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,OAAM,GAAE,MAAK,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,OAAM,GAAE,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,IAAE,EAAC,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,EAAC,GAAE,IAAE,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;AAA2B,SAAS,EAAE,GAAE;AAAC,MAAI,IAAE,KAAK,UAAU,CAAC,GAAE,IAAE,IAAI,YAAW,EAAG,OAAO,CAAC,GAAE,IAAE;AAAG,WAAQ,IAAE,GAAE,IAAE,EAAE,QAAO,KAAI;AAAC,QAAI,IAAE,EAAE,CAAC;AAAE,SAAG,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,IAAE,CAAC;AAAA,EAAC;AAAC,SAAO,IAAE;AAAC;AAA6T,SAAS,EAAE,GAAE;AAAC,SAAM,CAAC,OAAO,MAAM,OAAO,CAAC,CAAC,KAAG,SAAS,KAAK,CAAC,KAAG,CAAC,2DAA2D,KAAK,CAAC,IAAE,KAAG,CAAC,CAAC,KAAK,MAAM,CAAC;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,MAAG;AAAC,QAAI,IAAI,GAAE,EAAE,WAAW,GAAG,IAAE,qBAAmB,MAAM;AAAA,EAAC,QAAM;AAAC,WAAM;AAAA,EAAE;AAAC;AAAQ;AAAC,SAAS,EAAE,GAAE,GAAE,IAAE,QAAO;AAAC,SAAO,MAAI,MAAI,MAAI,WAAS,EAAE,CAAC,KAAG,EAAE,CAAC,KAAG,IAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;AAAE;AAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,CAAC,EAAE,CAAC,GAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAAI,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,EAAE,QAAO,CAAE,CAAC;AAAC,IAAC,IAAE,GAAG,OAAO,OAAO,CAAC,EAAE,IAAI,OAAG,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,IAAG,IAAE,IAAI,OAAO,IAAI,CAAC,SAAQ,IAAI;AAA6lC,SAAS,EAAE,GAAE;AAAC,MAAI;AAAE,SAAM,EAAC,SAAQ,EAAE,QAAQ,GAAE,EAAE,GAAE,WAAU,IAAE,EAAE,MAAM,CAAC,MAAI,OAAK,SAAO,EAAE,CAAC,MAAI,GAAE;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,SAAO,KAAG,KAAK,MAAM,EAAE,KAAK,UAAU,CAAC,CAAC,EAAE,OAAO;AAAC;AC4BlnF,SAAS,WAA6B,QAAsC;AACjF,SAAOA,EAAiB,MAAM;AAChC;AAQO,MAAM,sBAAsB;;;;;","x_google_ignoreList":[1]}
@@ -1 +1 @@
1
- {"version":3,"file":"stegaClean.js","sources":["../../src/util/isRecord.ts","../../node_modules/@vercel/stega/dist/index.mjs","../../src/stega/stegaClean.ts"],"sourcesContent":["/** @internal */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","var p={0:8203,1:8204,2:8205,3:8290,4:8291,5:8288,6:65279,7:8289,8:119155,9:119156,a:119157,b:119158,c:119159,d:119160,e:119161,f:119162},l={0:8203,1:8204,2:8205,3:65279},d={0:String.fromCodePoint(l[0]),1:String.fromCodePoint(l[1]),2:String.fromCodePoint(l[2]),3:String.fromCodePoint(l[3])},u=new Array(4).fill(String.fromCodePoint(l[0])).join(\"\"),g=String.fromCharCode(0);function A(e){let r=JSON.stringify(e),t=new TextEncoder().encode(r),i=\"\";for(let c=0;c<t.length;c++){let n=t[c];i+=d[n>>6&3]+d[n>>4&3]+d[n>>2&3]+d[n&3]}return u+i}function C(e){let r=JSON.stringify(e);return Array.from(r).map(t=>{let i=t.charCodeAt(0);if(i>255)throw new Error(`Only ASCII edit info can be encoded. Error attempting to encode ${r} on character ${t} (${i})`);return Array.from(i.toString(16).padStart(2,\"0\")).map(c=>String.fromCodePoint(p[c])).join(\"\")}).join(\"\")}function I(e){return!Number.isNaN(Number(e))||/[a-z]/i.test(e)&&!/\\d+(?:[-:\\/]\\d+){2}(?:T\\d+(?:[-:\\/]\\d+){1,2}(\\.\\d+)?Z?)?/.test(e)?!1:!!Date.parse(e)}function S(e){try{new URL(e,e.startsWith(\"/\")?\"https://acme.com\":void 0)}catch{return!1}return!0}function y(e,r,t=\"auto\"){return t===!0||t===\"auto\"&&(I(e)||S(e))?e:`${e}${A(r)}`}var m=Object.fromEntries(Object.entries(d).map(e=>[e[1],+e[0]])),T=Object.fromEntries(Object.entries(p).map(e=>e.reverse())),h=`${Object.values(p).map(e=>`\\\\u{${e.toString(16)}}`).join(\"\")}`,x=new RegExp(`[${h}]{4,}`,\"gu\");function X(e){let r=e.match(x);if(r)return E(r[0],!0)[0]}function M(e){let r=e.match(x);if(r)return r.map(t=>E(t)).flat()}function E(e,r=!1){let t=Array.from(e),i=1/0,c=-1;for(let n=0;n<t.length;++n)t[n]===u[0]&&t[n+1]===u[1]&&t[n+2]===u[2]&&t[n+3]===u[3]&&(i=Math.min(i,n),c=Math.max(c,n));if(c===-1)return _(t,r);for(let n=i;n<=c;++n)if(!((t.length-n)%4))try{let f=t.slice(n+4),s=new Uint8Array(f.length/4);for(let o=0;o<s.length;o++)s[o]=m[f[o*4]]<<6|m[f[o*4+1]]<<4|m[f[o*4+2]]<<2|m[f[o*4+3]];let a=new TextDecoder().decode(s);if(r){let o=a.indexOf(g);return o===-1&&(o=a.length),[JSON.parse(a.slice(0,o))]}return a.split(g).filter(Boolean).map(o=>JSON.parse(o))}catch{}return[]}function _(e,r){var f;let t=[];for(let s=e.length*.5;s--;){let a=`${T[e[s*2].codePointAt(0)]}${T[e[s*2+1].codePointAt(0)]}`;t.unshift(String.fromCharCode(parseInt(a,16)))}let i=[],c=[t.join(\"\")],n=10;for(;c.length;){let s=c.shift();try{if(i.push(JSON.parse(s)),r)return i}catch(a){if(!n--)throw a;let o=+((f=a.message.match(/\\sposition\\s(\\d+)$/))==null?void 0:f[1]);if(!o)throw a;c.unshift(s.substring(0,o),s.substring(o))}}return i}function P(e){var r;return{cleaned:e.replace(x,\"\"),encoded:((r=e.match(x))==null?void 0:r[0])||\"\"}}function w(e){return e&&JSON.parse(P(JSON.stringify(e)).cleaned)}export{x as VERCEL_STEGA_REGEX,C as legacyStegaEncode,w as vercelStegaClean,y as vercelStegaCombine,X as vercelStegaDecode,M as vercelStegaDecodeAll,A as vercelStegaEncode,P as vercelStegaSplit};\n","import {vercelStegaClean} from '@vercel/stega'\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * @public\n */\nexport function stegaClean<Result = unknown>(result: Result): Result {\n return vercelStegaClean<Result>(result)\n}\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * @alpha\n * @deprecated Use `stegaClean` instead\n */\nexport const vercelStegaCleanAll = stegaClean\n"],"names":["vercelStegaClean"],"mappings":"AACO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,SAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;ACHG,IAAC,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,OAAM,GAAE,MAAK,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,OAAM,GAAE,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,IAAE,EAAC,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,EAAC,GAAE,IAAE,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;AAA2B,SAAS,EAAE,GAAE;AAAC,MAAI,IAAE,KAAK,UAAU,CAAC,GAAE,IAAE,IAAI,YAAW,EAAG,OAAO,CAAC,GAAE,IAAE;AAAG,WAAQ,IAAE,GAAE,IAAE,EAAE,QAAO,KAAI;AAAC,QAAI,IAAE,EAAE,CAAC;AAAE,SAAG,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,IAAE,CAAC;AAAA,EAAC;AAAC,SAAO,IAAE;AAAC;AAA6T,SAAS,EAAE,GAAE;AAAC,SAAM,CAAC,OAAO,MAAM,OAAO,CAAC,CAAC,KAAG,SAAS,KAAK,CAAC,KAAG,CAAC,2DAA2D,KAAK,CAAC,IAAE,KAAG,CAAC,CAAC,KAAK,MAAM,CAAC;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,MAAG;AAAC,QAAI,IAAI,GAAE,EAAE,WAAW,GAAG,IAAE,qBAAmB,MAAM;AAAA,EAAC,QAAM;AAAC,WAAM;AAAA,EAAE;AAAC;AAAQ;AAAC,SAAS,EAAE,GAAE,GAAE,IAAE,QAAO;AAAC,SAAO,MAAI,MAAI,MAAI,WAAS,EAAE,CAAC,KAAG,EAAE,CAAC,KAAG,IAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;AAAE;AAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,CAAC,EAAE,CAAC,GAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAAI,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,EAAE,QAAO,CAAE,CAAC;AAAC,IAAC,IAAE,GAAG,OAAO,OAAO,CAAC,EAAE,IAAI,OAAG,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,IAAG,IAAE,IAAI,OAAO,IAAI,CAAC,SAAQ,IAAI;AAA6lC,SAAS,EAAE,GAAE;AAAC,MAAI;AAAE,SAAM,EAAC,SAAQ,EAAE,QAAQ,GAAE,EAAE,GAAE,WAAU,IAAE,EAAE,MAAM,CAAC,MAAI,OAAK,SAAO,EAAE,CAAC,MAAI,GAAE;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,SAAO,KAAG,KAAK,MAAM,EAAE,KAAK,UAAU,CAAC,CAAC,EAAE,OAAO;AAAC;ACOlnF,SAAS,WAA6B,QAAwB;AACnE,SAAOA,EAAyB,MAAM;AACxC;AAQO,MAAM,sBAAsB;","x_google_ignoreList":[1]}
1
+ {"version":3,"file":"stegaClean.js","sources":["../../src/util/isRecord.ts","../../node_modules/@vercel/stega/dist/index.mjs","../../src/stega/stegaClean.ts"],"sourcesContent":["/** @internal */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n}\n","var p={0:8203,1:8204,2:8205,3:8290,4:8291,5:8288,6:65279,7:8289,8:119155,9:119156,a:119157,b:119158,c:119159,d:119160,e:119161,f:119162},l={0:8203,1:8204,2:8205,3:65279},d={0:String.fromCodePoint(l[0]),1:String.fromCodePoint(l[1]),2:String.fromCodePoint(l[2]),3:String.fromCodePoint(l[3])},u=new Array(4).fill(String.fromCodePoint(l[0])).join(\"\"),g=String.fromCharCode(0);function A(e){let r=JSON.stringify(e),t=new TextEncoder().encode(r),i=\"\";for(let c=0;c<t.length;c++){let n=t[c];i+=d[n>>6&3]+d[n>>4&3]+d[n>>2&3]+d[n&3]}return u+i}function C(e){let r=JSON.stringify(e);return Array.from(r).map(t=>{let i=t.charCodeAt(0);if(i>255)throw new Error(`Only ASCII edit info can be encoded. Error attempting to encode ${r} on character ${t} (${i})`);return Array.from(i.toString(16).padStart(2,\"0\")).map(c=>String.fromCodePoint(p[c])).join(\"\")}).join(\"\")}function I(e){return!Number.isNaN(Number(e))||/[a-z]/i.test(e)&&!/\\d+(?:[-:\\/]\\d+){2}(?:T\\d+(?:[-:\\/]\\d+){1,2}(\\.\\d+)?Z?)?/.test(e)?!1:!!Date.parse(e)}function S(e){try{new URL(e,e.startsWith(\"/\")?\"https://acme.com\":void 0)}catch{return!1}return!0}function y(e,r,t=\"auto\"){return t===!0||t===\"auto\"&&(I(e)||S(e))?e:`${e}${A(r)}`}var m=Object.fromEntries(Object.entries(d).map(e=>[e[1],+e[0]])),T=Object.fromEntries(Object.entries(p).map(e=>e.reverse())),h=`${Object.values(p).map(e=>`\\\\u{${e.toString(16)}}`).join(\"\")}`,x=new RegExp(`[${h}]{4,}`,\"gu\");function X(e){let r=e.match(x);if(r)return E(r[0],!0)[0]}function M(e){let r=e.match(x);if(r)return r.map(t=>E(t)).flat()}function E(e,r=!1){let t=Array.from(e),i=1/0,c=-1;for(let n=0;n<t.length;++n)t[n]===u[0]&&t[n+1]===u[1]&&t[n+2]===u[2]&&t[n+3]===u[3]&&(i=Math.min(i,n),c=Math.max(c,n));if(c===-1)return _(t,r);for(let n=i;n<=c;++n)if(!((t.length-n)%4))try{let f=t.slice(n+4),s=new Uint8Array(f.length/4);for(let o=0;o<s.length;o++)s[o]=m[f[o*4]]<<6|m[f[o*4+1]]<<4|m[f[o*4+2]]<<2|m[f[o*4+3]];let a=new TextDecoder().decode(s);if(r){let o=a.indexOf(g);return o===-1&&(o=a.length),[JSON.parse(a.slice(0,o))]}return a.split(g).filter(Boolean).map(o=>JSON.parse(o))}catch{}return[]}function _(e,r){var f;let t=[];for(let s=e.length*.5;s--;){let a=`${T[e[s*2].codePointAt(0)]}${T[e[s*2+1].codePointAt(0)]}`;t.unshift(String.fromCharCode(parseInt(a,16)))}let i=[],c=[t.join(\"\")],n=10;for(;c.length;){let s=c.shift();try{if(i.push(JSON.parse(s)),r)return i}catch(a){if(!n--)throw a;let o=+((f=a.message.match(/\\sposition\\s(\\d+)$/))==null?void 0:f[1]);if(!o)throw a;c.unshift(s.substring(0,o),s.substring(o))}}return i}function P(e){var r;return{cleaned:e.replace(x,\"\"),encoded:((r=e.match(x))==null?void 0:r[0])||\"\"}}function w(e){return e&&JSON.parse(P(JSON.stringify(e)).cleaned)}export{x as VERCEL_STEGA_REGEX,C as legacyStegaEncode,w as vercelStegaClean,y as vercelStegaCombine,X as vercelStegaDecode,M as vercelStegaDecodeAll,A as vercelStegaEncode,P as vercelStegaSplit};\n","import {vercelStegaClean} from '@vercel/stega'\n\n/**\n * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were\n * branded as `StegaString` are returned to their original type, everything else is left as-is.\n * @public\n */\nexport type StegaCleaned<T> = 0 extends 1 & T\n ? T\n : T extends {readonly ' stegaBrand': infer Original extends string}\n ? Original\n : T extends string | number | bigint | boolean | null | undefined\n ? T\n : T extends Date | RegExp | ((...args: never[]) => unknown)\n ? T\n : T extends readonly unknown[]\n ? {[Index in keyof T]: StegaCleaned<T[Index]>}\n : T extends object\n ? {[K in keyof T]: StegaCleaned<T[K]>}\n : T\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),\n * the brand is stripped and the original string type is restored.\n * @public\n */\nexport function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result> {\n return vercelStegaClean(result) as StegaCleaned<Result>\n}\n\n/**\n * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`\n * and remove all stega-encoded data from it.\n * @alpha\n * @deprecated Use `stegaClean` instead\n */\nexport const vercelStegaCleanAll = stegaClean\n"],"names":["vercelStegaClean"],"mappings":"AACO,SAAS,SAAS,OAAkD;AACzE,SAAO,OAAO,SAAU,YAAY,UAAU,QAAQ,CAAC,MAAM,QAAQ,KAAK;AAC5E;ACHG,IAAC,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,OAAM,GAAE,MAAK,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,QAAO,GAAE,OAAM,GAAE,IAAE,EAAC,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,MAAK,GAAE,IAAE,EAAC,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,GAAE,GAAE,OAAO,cAAc,EAAE,CAAC,CAAC,EAAC,GAAE,IAAE,IAAI,MAAM,CAAC,EAAE,KAAK,OAAO,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE;AAA2B,SAAS,EAAE,GAAE;AAAC,MAAI,IAAE,KAAK,UAAU,CAAC,GAAE,IAAE,IAAI,YAAW,EAAG,OAAO,CAAC,GAAE,IAAE;AAAG,WAAQ,IAAE,GAAE,IAAE,EAAE,QAAO,KAAI;AAAC,QAAI,IAAE,EAAE,CAAC;AAAE,SAAG,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,KAAG,IAAE,CAAC,IAAE,EAAE,IAAE,CAAC;AAAA,EAAC;AAAC,SAAO,IAAE;AAAC;AAA6T,SAAS,EAAE,GAAE;AAAC,SAAM,CAAC,OAAO,MAAM,OAAO,CAAC,CAAC,KAAG,SAAS,KAAK,CAAC,KAAG,CAAC,2DAA2D,KAAK,CAAC,IAAE,KAAG,CAAC,CAAC,KAAK,MAAM,CAAC;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,MAAG;AAAC,QAAI,IAAI,GAAE,EAAE,WAAW,GAAG,IAAE,qBAAmB,MAAM;AAAA,EAAC,QAAM;AAAC,WAAM;AAAA,EAAE;AAAC;AAAQ;AAAC,SAAS,EAAE,GAAE,GAAE,IAAE,QAAO;AAAC,SAAO,MAAI,MAAI,MAAI,WAAS,EAAE,CAAC,KAAG,EAAE,CAAC,KAAG,IAAE,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC;AAAE;AAAO,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,CAAC,EAAE,CAAC,GAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAAI,OAAO,YAAY,OAAO,QAAQ,CAAC,EAAE,IAAI,OAAG,EAAE,QAAO,CAAE,CAAC;AAAC,IAAC,IAAE,GAAG,OAAO,OAAO,CAAC,EAAE,IAAI,OAAG,OAAO,EAAE,SAAS,EAAE,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,IAAG,IAAE,IAAI,OAAO,IAAI,CAAC,SAAQ,IAAI;AAA6lC,SAAS,EAAE,GAAE;AAAC,MAAI;AAAE,SAAM,EAAC,SAAQ,EAAE,QAAQ,GAAE,EAAE,GAAE,WAAU,IAAE,EAAE,MAAM,CAAC,MAAI,OAAK,SAAO,EAAE,CAAC,MAAI,GAAE;AAAC;AAAC,SAAS,EAAE,GAAE;AAAC,SAAO,KAAG,KAAK,MAAM,EAAE,KAAK,UAAU,CAAC,CAAC,EAAE,OAAO;AAAC;AC4BlnF,SAAS,WAA6B,QAAsC;AACjF,SAAOA,EAAiB,MAAM;AAChC;AAQO,MAAM,sBAAsB;","x_google_ignoreList":[1]}
package/dist/index.cjs CHANGED
@@ -2893,7 +2893,7 @@ function defineDeprecatedCreateClient(createClient2) {
2893
2893
  return config.printNoDefaultExport(), createClient2(config$1);
2894
2894
  };
2895
2895
  }
2896
- var name = "@sanity/client", version = "7.24.0";
2896
+ var name = "@sanity/client", version = "7.25.0";
2897
2897
  const middleware = [
2898
2898
  middleware$1.debug({ verbose: !0, namespace: "sanity:client" }),
2899
2899
  middleware$1.headers({ "User-Agent": `${name} ${version}` }),
package/dist/index.js CHANGED
@@ -2879,7 +2879,7 @@ function defineDeprecatedCreateClient(createClient2) {
2879
2879
  return printNoDefaultExport(), createClient2(config);
2880
2880
  };
2881
2881
  }
2882
- var name = "@sanity/client", version = "7.24.0";
2882
+ var name = "@sanity/client", version = "7.25.0";
2883
2883
  const middleware = [
2884
2884
  debug({ verbose: !0, namespace: "sanity:client" }),
2885
2885
  headers({ "User-Agent": `${name} ${version}` }),
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
3
  var client = require("@sanity/client"), stegaEncodeSourceMap = require("./_chunks-cjs/stegaEncodeSourceMap.cjs"), stegaClean = require("./_chunks-cjs/stegaClean.cjs");
4
+ function stegaBrand(result) {
5
+ return result;
6
+ }
4
7
  class SanityStegaClient extends client.SanityClient {
5
8
  }
6
9
  class ObservableSanityStegaClient extends client.ObservableSanityClient {
@@ -14,6 +17,7 @@ exports.ObservableSanityStegaClient = ObservableSanityStegaClient;
14
17
  exports.SanityStegaClient = SanityStegaClient;
15
18
  exports.createClient = createClient;
16
19
  exports.requester = requester;
20
+ exports.stegaBrand = stegaBrand;
17
21
  Object.keys(client).forEach(function(k) {
18
22
  k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k) && Object.defineProperty(exports, k, {
19
23
  enumerable: !0,
@@ -1 +1 @@
1
- {"version":3,"file":"stega.browser.cjs","sources":["../src/stega/index.ts"],"sourcesContent":["export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {stegaClean, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["SanityClient","ObservableSanityClient","originalRequester","originalCreateClient"],"mappings":";;;AAiBO,MAAM,0BAA0BA,OAAAA,aAAa;AAAC;AAM9C,MAAM,oCAAoCC,OAAAA,uBAAuB;AAAC;AAMlE,MAAM,YAAYC,OAAAA,WAMZ,eAAeC,OAAAA;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"stega.browser.cjs","sources":["../src/stega/stegaBrand.ts","../src/stega/index.ts"],"sourcesContent":["import type {Any, ClientReturn} from '@sanity/client'\n\n/**\n * A string that might contain stega-encoded data, and is unsafe to compare against string literals\n * until it's been cleaned with `stegaClean()`.\n *\n * The type intersects a template literal that has a human readable suffix, with a brand property\n * that holds the original string type:\n * - The suffix makes TypeScript report \"This comparison appears to be unintentional\" (TS2367) when\n * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to\n * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,\n * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime\n * value never ends with that text.\n * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's\n * a string-keyed property rather than a `unique symbol` so that branded types stay structurally\n * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in\n * the type system and is never present at runtime.\n *\n * @beta\n */\nexport type StegaString<T extends string = string> =\n `${T} (may contain hidden stega characters)` & {\n readonly ' stegaBrand': T\n }\n\n/**\n * Deeply brands the string properties of a query result as `StegaString`, marking them as\n * potentially containing stega-encoded data.\n *\n * String properties that the stega encoder is guaranteed to never encode keep their plain type:\n * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and\n * so on), which also preserves discriminated union narrowing on `_type`\n * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued\n * `slug` keys (covering `\"slug\": slug.current` projections)\n * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and\n * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay\n * plain\n *\n * Every other string is assumed to be \"poisoned\", even if the runtime `filter` happens to skip it\n * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case\n * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.\n *\n * The `Key` and `ParentKey` type parameters track the key the current value is found under, and\n * the key of the object containing it, they're only used internally during recursion.\n *\n * @beta\n */\nexport type StegaBranded<\n T,\n Key extends PropertyKey = number,\n ParentKey extends PropertyKey = number,\n> = 0 extends 1 & T\n ? T\n : T extends string\n ? T extends {readonly ' stegaBrand': string}\n ? T\n : Key extends `_${string}` | 'slug'\n ? T\n : Key extends 'current'\n ? ParentKey extends 'slug'\n ? T\n : StegaString<T>\n : StegaString<T>\n : T extends number | bigint | boolean | null | undefined\n ? T\n : T extends Date | RegExp | ((...args: never[]) => unknown)\n ? T\n : T extends readonly unknown[]\n ? {[Index in keyof T]: StegaBranded<T[Index], number, number>}\n : T extends {_type: 'block'}\n ? {[K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends {_type: 'span'}\n ? {[K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends object\n ? {[K in keyof T]: StegaBranded<T[K], K, Key>}\n : T\n\n/**\n * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use\n * as the first generic of `client.fetch` when stega is enabled:\n * ```ts\n * import {createClient} from '@sanity/client'\n * import type {ClientReturnStega} from '@sanity/client/stega'\n *\n * const query = '*[_type == \"post\"][0]'\n * const post = await client.fetch<ClientReturnStega<typeof query>>(query)\n * ```\n * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),\n * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to\n * `Fallback`, which defaults to `any`, just like `ClientReturn`.\n *\n * @beta\n */\nexport type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<\n ClientReturn<GroqString, Fallback>\n>\n\n/**\n * Marks strings in an already fetched query result as potentially containing stega-encoded data,\n * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error\n * until they're cleaned with `stegaClean()`, which recovers the original type.\n *\n * This is an identity function, it only changes the type of the input, not its value.\n * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use\n * this function for data that has already been fetched.\n *\n * @beta\n */\nexport function stegaBrand<Result>(result: Result): StegaBranded<Result> {\n return result as StegaBranded<Result>\n}\n","export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {type ClientReturnStega, stegaBrand, type StegaBranded, type StegaString} from './stegaBrand'\nexport {stegaClean, type StegaCleaned, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["SanityClient","ObservableSanityClient","originalRequester","originalCreateClient"],"mappings":";;;AA4GO,SAAS,WAAmB,QAAsC;AACvE,SAAO;AACT;AC5FO,MAAM,0BAA0BA,OAAAA,aAAa;AAAC;AAM9C,MAAM,oCAAoCC,OAAAA,uBAAuB;AAAC;AAMlE,MAAM,YAAYC,OAAAA,WAMZ,eAAeC,OAAAA;;;;;;;;;;;;;;;;;;"}
@@ -878,6 +878,26 @@ export declare type ClientReturn<
878
878
  Fallback = Any,
879
879
  > = GroqString extends keyof SanityQueries ? SanityQueries[GroqString] : Fallback
880
880
 
881
+ /**
882
+ * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use
883
+ * as the first generic of `client.fetch` when stega is enabled:
884
+ * ```ts
885
+ * import {createClient} from '@sanity/client'
886
+ * import type {ClientReturnStega} from '@sanity/client/stega'
887
+ *
888
+ * const query = '*[_type == "post"][0]'
889
+ * const post = await client.fetch<ClientReturnStega<typeof query>>(query)
890
+ * ```
891
+ * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),
892
+ * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to
893
+ * `Fallback`, which defaults to `any`, just like `ClientReturn`.
894
+ *
895
+ * @beta
896
+ */
897
+ export declare type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<
898
+ ClientReturn<GroqString, Fallback>
899
+ >
900
+
881
901
  /**
882
902
  * @public
883
903
  * @deprecated -- use `ClientConfig` instead
@@ -6168,12 +6188,118 @@ export declare interface SingleMutationResult {
6168
6188
  /** @public */
6169
6189
  export declare type StackablePerspective = ('published' | 'drafts' | string) & {}
6170
6190
 
6191
+ /**
6192
+ * Marks strings in an already fetched query result as potentially containing stega-encoded data,
6193
+ * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error
6194
+ * until they're cleaned with `stegaClean()`, which recovers the original type.
6195
+ *
6196
+ * This is an identity function, it only changes the type of the input, not its value.
6197
+ * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use
6198
+ * this function for data that has already been fetched.
6199
+ *
6200
+ * @beta
6201
+ */
6202
+ export declare function stegaBrand<Result>(result: Result): StegaBranded<Result>
6203
+
6204
+ /**
6205
+ * Deeply brands the string properties of a query result as `StegaString`, marking them as
6206
+ * potentially containing stega-encoded data.
6207
+ *
6208
+ * String properties that the stega encoder is guaranteed to never encode keep their plain type:
6209
+ * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and
6210
+ * so on), which also preserves discriminated union narrowing on `_type`
6211
+ * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued
6212
+ * `slug` keys (covering `"slug": slug.current` projections)
6213
+ * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and
6214
+ * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay
6215
+ * plain
6216
+ *
6217
+ * Every other string is assumed to be "poisoned", even if the runtime `filter` happens to skip it
6218
+ * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case
6219
+ * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.
6220
+ *
6221
+ * The `Key` and `ParentKey` type parameters track the key the current value is found under, and
6222
+ * the key of the object containing it, they're only used internally during recursion.
6223
+ *
6224
+ * @beta
6225
+ */
6226
+ export declare type StegaBranded<
6227
+ T,
6228
+ Key extends PropertyKey = number,
6229
+ ParentKey extends PropertyKey = number,
6230
+ > = 0 extends 1 & T
6231
+ ? T
6232
+ : T extends string
6233
+ ? T extends {
6234
+ readonly ' stegaBrand': string
6235
+ }
6236
+ ? T
6237
+ : Key extends `_${string}` | 'slug'
6238
+ ? T
6239
+ : Key extends 'current'
6240
+ ? ParentKey extends 'slug'
6241
+ ? T
6242
+ : StegaString<T>
6243
+ : StegaString<T>
6244
+ : T extends number | bigint | boolean | null | undefined
6245
+ ? T
6246
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6247
+ ? T
6248
+ : T extends readonly unknown[]
6249
+ ? {
6250
+ [Index in keyof T]: StegaBranded<T[Index], number, number>
6251
+ }
6252
+ : T extends {
6253
+ _type: 'block'
6254
+ }
6255
+ ? {
6256
+ [K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]
6257
+ }
6258
+ : T extends {
6259
+ _type: 'span'
6260
+ }
6261
+ ? {
6262
+ [K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]
6263
+ }
6264
+ : T extends object
6265
+ ? {
6266
+ [K in keyof T]: StegaBranded<T[K], K, Key>
6267
+ }
6268
+ : T
6269
+
6171
6270
  /**
6172
6271
  * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`
6173
6272
  * and remove all stega-encoded data from it.
6273
+ * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),
6274
+ * the brand is stripped and the original string type is restored.
6174
6275
  * @public
6175
6276
  */
6176
- export declare function stegaClean<Result = unknown>(result: Result): Result
6277
+ export declare function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result>
6278
+
6279
+ /**
6280
+ * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were
6281
+ * branded as `StegaString` are returned to their original type, everything else is left as-is.
6282
+ * @public
6283
+ */
6284
+ export declare type StegaCleaned<T> = 0 extends 1 & T
6285
+ ? T
6286
+ : T extends {
6287
+ readonly ' stegaBrand': infer Original extends string
6288
+ }
6289
+ ? Original
6290
+ : T extends string | number | bigint | boolean | null | undefined
6291
+ ? T
6292
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6293
+ ? T
6294
+ : T extends readonly unknown[]
6295
+ ? {
6296
+ [Index in keyof T]: StegaCleaned<T[Index]>
6297
+ }
6298
+ : T extends object
6299
+ ? {
6300
+ [K in keyof T]: StegaCleaned<T[K]>
6301
+ }
6302
+ : T
6177
6303
 
6178
6304
  /** @public */
6179
6305
  export declare interface StegaConfig {
@@ -6253,6 +6379,29 @@ export declare function stegaEncodeSourceMap<Result = unknown>(
6253
6379
  config: InitializedStegaConfig_2,
6254
6380
  ): Result
6255
6381
 
6382
+ /**
6383
+ * A string that might contain stega-encoded data, and is unsafe to compare against string literals
6384
+ * until it's been cleaned with `stegaClean()`.
6385
+ *
6386
+ * The type intersects a template literal that has a human readable suffix, with a brand property
6387
+ * that holds the original string type:
6388
+ * - The suffix makes TypeScript report "This comparison appears to be unintentional" (TS2367) when
6389
+ * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to
6390
+ * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,
6391
+ * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime
6392
+ * value never ends with that text.
6393
+ * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's
6394
+ * a string-keyed property rather than a `unique symbol` so that branded types stay structurally
6395
+ * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in
6396
+ * the type system and is never present at runtime.
6397
+ *
6398
+ * @beta
6399
+ */
6400
+ export declare type StegaString<T extends string = string> =
6401
+ `${T} (may contain hidden stega characters)` & {
6402
+ readonly ' stegaBrand': T
6403
+ }
6404
+
6256
6405
  /**
6257
6406
  * Allowed still image formats (thumbnail + storyboard).
6258
6407
  * @public
@@ -878,6 +878,26 @@ export declare type ClientReturn<
878
878
  Fallback = Any,
879
879
  > = GroqString extends keyof SanityQueries ? SanityQueries[GroqString] : Fallback
880
880
 
881
+ /**
882
+ * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use
883
+ * as the first generic of `client.fetch` when stega is enabled:
884
+ * ```ts
885
+ * import {createClient} from '@sanity/client'
886
+ * import type {ClientReturnStega} from '@sanity/client/stega'
887
+ *
888
+ * const query = '*[_type == "post"][0]'
889
+ * const post = await client.fetch<ClientReturnStega<typeof query>>(query)
890
+ * ```
891
+ * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),
892
+ * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to
893
+ * `Fallback`, which defaults to `any`, just like `ClientReturn`.
894
+ *
895
+ * @beta
896
+ */
897
+ export declare type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<
898
+ ClientReturn<GroqString, Fallback>
899
+ >
900
+
881
901
  /**
882
902
  * @public
883
903
  * @deprecated -- use `ClientConfig` instead
@@ -6168,12 +6188,118 @@ export declare interface SingleMutationResult {
6168
6188
  /** @public */
6169
6189
  export declare type StackablePerspective = ('published' | 'drafts' | string) & {}
6170
6190
 
6191
+ /**
6192
+ * Marks strings in an already fetched query result as potentially containing stega-encoded data,
6193
+ * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error
6194
+ * until they're cleaned with `stegaClean()`, which recovers the original type.
6195
+ *
6196
+ * This is an identity function, it only changes the type of the input, not its value.
6197
+ * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use
6198
+ * this function for data that has already been fetched.
6199
+ *
6200
+ * @beta
6201
+ */
6202
+ export declare function stegaBrand<Result>(result: Result): StegaBranded<Result>
6203
+
6204
+ /**
6205
+ * Deeply brands the string properties of a query result as `StegaString`, marking them as
6206
+ * potentially containing stega-encoded data.
6207
+ *
6208
+ * String properties that the stega encoder is guaranteed to never encode keep their plain type:
6209
+ * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and
6210
+ * so on), which also preserves discriminated union narrowing on `_type`
6211
+ * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued
6212
+ * `slug` keys (covering `"slug": slug.current` projections)
6213
+ * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and
6214
+ * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay
6215
+ * plain
6216
+ *
6217
+ * Every other string is assumed to be "poisoned", even if the runtime `filter` happens to skip it
6218
+ * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case
6219
+ * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.
6220
+ *
6221
+ * The `Key` and `ParentKey` type parameters track the key the current value is found under, and
6222
+ * the key of the object containing it, they're only used internally during recursion.
6223
+ *
6224
+ * @beta
6225
+ */
6226
+ export declare type StegaBranded<
6227
+ T,
6228
+ Key extends PropertyKey = number,
6229
+ ParentKey extends PropertyKey = number,
6230
+ > = 0 extends 1 & T
6231
+ ? T
6232
+ : T extends string
6233
+ ? T extends {
6234
+ readonly ' stegaBrand': string
6235
+ }
6236
+ ? T
6237
+ : Key extends `_${string}` | 'slug'
6238
+ ? T
6239
+ : Key extends 'current'
6240
+ ? ParentKey extends 'slug'
6241
+ ? T
6242
+ : StegaString<T>
6243
+ : StegaString<T>
6244
+ : T extends number | bigint | boolean | null | undefined
6245
+ ? T
6246
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6247
+ ? T
6248
+ : T extends readonly unknown[]
6249
+ ? {
6250
+ [Index in keyof T]: StegaBranded<T[Index], number, number>
6251
+ }
6252
+ : T extends {
6253
+ _type: 'block'
6254
+ }
6255
+ ? {
6256
+ [K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]
6257
+ }
6258
+ : T extends {
6259
+ _type: 'span'
6260
+ }
6261
+ ? {
6262
+ [K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]
6263
+ }
6264
+ : T extends object
6265
+ ? {
6266
+ [K in keyof T]: StegaBranded<T[K], K, Key>
6267
+ }
6268
+ : T
6269
+
6171
6270
  /**
6172
6271
  * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`
6173
6272
  * and remove all stega-encoded data from it.
6273
+ * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),
6274
+ * the brand is stripped and the original string type is restored.
6174
6275
  * @public
6175
6276
  */
6176
- export declare function stegaClean<Result = unknown>(result: Result): Result
6277
+ export declare function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result>
6278
+
6279
+ /**
6280
+ * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were
6281
+ * branded as `StegaString` are returned to their original type, everything else is left as-is.
6282
+ * @public
6283
+ */
6284
+ export declare type StegaCleaned<T> = 0 extends 1 & T
6285
+ ? T
6286
+ : T extends {
6287
+ readonly ' stegaBrand': infer Original extends string
6288
+ }
6289
+ ? Original
6290
+ : T extends string | number | bigint | boolean | null | undefined
6291
+ ? T
6292
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6293
+ ? T
6294
+ : T extends readonly unknown[]
6295
+ ? {
6296
+ [Index in keyof T]: StegaCleaned<T[Index]>
6297
+ }
6298
+ : T extends object
6299
+ ? {
6300
+ [K in keyof T]: StegaCleaned<T[K]>
6301
+ }
6302
+ : T
6177
6303
 
6178
6304
  /** @public */
6179
6305
  export declare interface StegaConfig {
@@ -6253,6 +6379,29 @@ export declare function stegaEncodeSourceMap<Result = unknown>(
6253
6379
  config: InitializedStegaConfig_2,
6254
6380
  ): Result
6255
6381
 
6382
+ /**
6383
+ * A string that might contain stega-encoded data, and is unsafe to compare against string literals
6384
+ * until it's been cleaned with `stegaClean()`.
6385
+ *
6386
+ * The type intersects a template literal that has a human readable suffix, with a brand property
6387
+ * that holds the original string type:
6388
+ * - The suffix makes TypeScript report "This comparison appears to be unintentional" (TS2367) when
6389
+ * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to
6390
+ * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,
6391
+ * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime
6392
+ * value never ends with that text.
6393
+ * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's
6394
+ * a string-keyed property rather than a `unique symbol` so that branded types stay structurally
6395
+ * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in
6396
+ * the type system and is never present at runtime.
6397
+ *
6398
+ * @beta
6399
+ */
6400
+ export declare type StegaString<T extends string = string> =
6401
+ `${T} (may contain hidden stega characters)` & {
6402
+ readonly ' stegaBrand': T
6403
+ }
6404
+
6256
6405
  /**
6257
6406
  * Allowed still image formats (thumbnail + storyboard).
6258
6407
  * @public
@@ -2,6 +2,9 @@ import { createClient as createClient$1, requester as requester$1, ObservableSan
2
2
  export * from "@sanity/client";
3
3
  import { encodeIntoResult, stegaEncodeSourceMap } from "./_chunks-es/stegaEncodeSourceMap.js";
4
4
  import { stegaClean, vercelStegaCleanAll } from "./_chunks-es/stegaClean.js";
5
+ function stegaBrand(result) {
6
+ return result;
7
+ }
5
8
  class SanityStegaClient extends SanityClient {
6
9
  }
7
10
  class ObservableSanityStegaClient extends ObservableSanityClient {
@@ -13,6 +16,7 @@ export {
13
16
  createClient,
14
17
  encodeIntoResult,
15
18
  requester,
19
+ stegaBrand,
16
20
  stegaClean,
17
21
  stegaEncodeSourceMap,
18
22
  vercelStegaCleanAll
@@ -1 +1 @@
1
- {"version":3,"file":"stega.browser.js","sources":["../src/stega/index.ts"],"sourcesContent":["export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {stegaClean, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["originalRequester","originalCreateClient"],"mappings":";;;;AAiBO,MAAM,0BAA0B,aAAa;AAAC;AAM9C,MAAM,oCAAoC,uBAAuB;AAAC;AAMlE,MAAM,YAAYA,aAMZ,eAAeC;"}
1
+ {"version":3,"file":"stega.browser.js","sources":["../src/stega/stegaBrand.ts","../src/stega/index.ts"],"sourcesContent":["import type {Any, ClientReturn} from '@sanity/client'\n\n/**\n * A string that might contain stega-encoded data, and is unsafe to compare against string literals\n * until it's been cleaned with `stegaClean()`.\n *\n * The type intersects a template literal that has a human readable suffix, with a brand property\n * that holds the original string type:\n * - The suffix makes TypeScript report \"This comparison appears to be unintentional\" (TS2367) when\n * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to\n * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,\n * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime\n * value never ends with that text.\n * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's\n * a string-keyed property rather than a `unique symbol` so that branded types stay structurally\n * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in\n * the type system and is never present at runtime.\n *\n * @beta\n */\nexport type StegaString<T extends string = string> =\n `${T} (may contain hidden stega characters)` & {\n readonly ' stegaBrand': T\n }\n\n/**\n * Deeply brands the string properties of a query result as `StegaString`, marking them as\n * potentially containing stega-encoded data.\n *\n * String properties that the stega encoder is guaranteed to never encode keep their plain type:\n * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and\n * so on), which also preserves discriminated union narrowing on `_type`\n * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued\n * `slug` keys (covering `\"slug\": slug.current` projections)\n * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and\n * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay\n * plain\n *\n * Every other string is assumed to be \"poisoned\", even if the runtime `filter` happens to skip it\n * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case\n * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.\n *\n * The `Key` and `ParentKey` type parameters track the key the current value is found under, and\n * the key of the object containing it, they're only used internally during recursion.\n *\n * @beta\n */\nexport type StegaBranded<\n T,\n Key extends PropertyKey = number,\n ParentKey extends PropertyKey = number,\n> = 0 extends 1 & T\n ? T\n : T extends string\n ? T extends {readonly ' stegaBrand': string}\n ? T\n : Key extends `_${string}` | 'slug'\n ? T\n : Key extends 'current'\n ? ParentKey extends 'slug'\n ? T\n : StegaString<T>\n : StegaString<T>\n : T extends number | bigint | boolean | null | undefined\n ? T\n : T extends Date | RegExp | ((...args: never[]) => unknown)\n ? T\n : T extends readonly unknown[]\n ? {[Index in keyof T]: StegaBranded<T[Index], number, number>}\n : T extends {_type: 'block'}\n ? {[K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends {_type: 'span'}\n ? {[K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends object\n ? {[K in keyof T]: StegaBranded<T[K], K, Key>}\n : T\n\n/**\n * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use\n * as the first generic of `client.fetch` when stega is enabled:\n * ```ts\n * import {createClient} from '@sanity/client'\n * import type {ClientReturnStega} from '@sanity/client/stega'\n *\n * const query = '*[_type == \"post\"][0]'\n * const post = await client.fetch<ClientReturnStega<typeof query>>(query)\n * ```\n * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),\n * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to\n * `Fallback`, which defaults to `any`, just like `ClientReturn`.\n *\n * @beta\n */\nexport type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<\n ClientReturn<GroqString, Fallback>\n>\n\n/**\n * Marks strings in an already fetched query result as potentially containing stega-encoded data,\n * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error\n * until they're cleaned with `stegaClean()`, which recovers the original type.\n *\n * This is an identity function, it only changes the type of the input, not its value.\n * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use\n * this function for data that has already been fetched.\n *\n * @beta\n */\nexport function stegaBrand<Result>(result: Result): StegaBranded<Result> {\n return result as StegaBranded<Result>\n}\n","export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {type ClientReturnStega, stegaBrand, type StegaBranded, type StegaString} from './stegaBrand'\nexport {stegaClean, type StegaCleaned, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["originalRequester","originalCreateClient"],"mappings":";;;;AA4GO,SAAS,WAAmB,QAAsC;AACvE,SAAO;AACT;AC5FO,MAAM,0BAA0B,aAAa;AAAC;AAM9C,MAAM,oCAAoC,uBAAuB;AAAC;AAMlE,MAAM,YAAYA,aAMZ,eAAeC;"}
package/dist/stega.cjs CHANGED
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
3
  var client = require("@sanity/client"), stegaEncodeSourceMap = require("./_chunks-cjs/stegaEncodeSourceMap.cjs"), stegaClean = require("./_chunks-cjs/stegaClean.cjs");
4
+ function stegaBrand(result) {
5
+ return result;
6
+ }
4
7
  class SanityStegaClient extends client.SanityClient {
5
8
  }
6
9
  class ObservableSanityStegaClient extends client.ObservableSanityClient {
@@ -14,6 +17,7 @@ exports.ObservableSanityStegaClient = ObservableSanityStegaClient;
14
17
  exports.SanityStegaClient = SanityStegaClient;
15
18
  exports.createClient = createClient;
16
19
  exports.requester = requester;
20
+ exports.stegaBrand = stegaBrand;
17
21
  Object.keys(client).forEach(function(k) {
18
22
  k !== "default" && !Object.prototype.hasOwnProperty.call(exports, k) && Object.defineProperty(exports, k, {
19
23
  enumerable: !0,
@@ -1 +1 @@
1
- {"version":3,"file":"stega.cjs","sources":["../src/stega/index.ts"],"sourcesContent":["export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {stegaClean, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["SanityClient","ObservableSanityClient","originalRequester","originalCreateClient"],"mappings":";;;AAiBO,MAAM,0BAA0BA,OAAAA,aAAa;AAAC;AAM9C,MAAM,oCAAoCC,OAAAA,uBAAuB;AAAC;AAMlE,MAAM,YAAYC,OAAAA,WAMZ,eAAeC,OAAAA;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"stega.cjs","sources":["../src/stega/stegaBrand.ts","../src/stega/index.ts"],"sourcesContent":["import type {Any, ClientReturn} from '@sanity/client'\n\n/**\n * A string that might contain stega-encoded data, and is unsafe to compare against string literals\n * until it's been cleaned with `stegaClean()`.\n *\n * The type intersects a template literal that has a human readable suffix, with a brand property\n * that holds the original string type:\n * - The suffix makes TypeScript report \"This comparison appears to be unintentional\" (TS2367) when\n * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to\n * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,\n * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime\n * value never ends with that text.\n * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's\n * a string-keyed property rather than a `unique symbol` so that branded types stay structurally\n * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in\n * the type system and is never present at runtime.\n *\n * @beta\n */\nexport type StegaString<T extends string = string> =\n `${T} (may contain hidden stega characters)` & {\n readonly ' stegaBrand': T\n }\n\n/**\n * Deeply brands the string properties of a query result as `StegaString`, marking them as\n * potentially containing stega-encoded data.\n *\n * String properties that the stega encoder is guaranteed to never encode keep their plain type:\n * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and\n * so on), which also preserves discriminated union narrowing on `_type`\n * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued\n * `slug` keys (covering `\"slug\": slug.current` projections)\n * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and\n * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay\n * plain\n *\n * Every other string is assumed to be \"poisoned\", even if the runtime `filter` happens to skip it\n * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case\n * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.\n *\n * The `Key` and `ParentKey` type parameters track the key the current value is found under, and\n * the key of the object containing it, they're only used internally during recursion.\n *\n * @beta\n */\nexport type StegaBranded<\n T,\n Key extends PropertyKey = number,\n ParentKey extends PropertyKey = number,\n> = 0 extends 1 & T\n ? T\n : T extends string\n ? T extends {readonly ' stegaBrand': string}\n ? T\n : Key extends `_${string}` | 'slug'\n ? T\n : Key extends 'current'\n ? ParentKey extends 'slug'\n ? T\n : StegaString<T>\n : StegaString<T>\n : T extends number | bigint | boolean | null | undefined\n ? T\n : T extends Date | RegExp | ((...args: never[]) => unknown)\n ? T\n : T extends readonly unknown[]\n ? {[Index in keyof T]: StegaBranded<T[Index], number, number>}\n : T extends {_type: 'block'}\n ? {[K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends {_type: 'span'}\n ? {[K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends object\n ? {[K in keyof T]: StegaBranded<T[K], K, Key>}\n : T\n\n/**\n * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use\n * as the first generic of `client.fetch` when stega is enabled:\n * ```ts\n * import {createClient} from '@sanity/client'\n * import type {ClientReturnStega} from '@sanity/client/stega'\n *\n * const query = '*[_type == \"post\"][0]'\n * const post = await client.fetch<ClientReturnStega<typeof query>>(query)\n * ```\n * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),\n * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to\n * `Fallback`, which defaults to `any`, just like `ClientReturn`.\n *\n * @beta\n */\nexport type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<\n ClientReturn<GroqString, Fallback>\n>\n\n/**\n * Marks strings in an already fetched query result as potentially containing stega-encoded data,\n * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error\n * until they're cleaned with `stegaClean()`, which recovers the original type.\n *\n * This is an identity function, it only changes the type of the input, not its value.\n * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use\n * this function for data that has already been fetched.\n *\n * @beta\n */\nexport function stegaBrand<Result>(result: Result): StegaBranded<Result> {\n return result as StegaBranded<Result>\n}\n","export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {type ClientReturnStega, stegaBrand, type StegaBranded, type StegaString} from './stegaBrand'\nexport {stegaClean, type StegaCleaned, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["SanityClient","ObservableSanityClient","originalRequester","originalCreateClient"],"mappings":";;;AA4GO,SAAS,WAAmB,QAAsC;AACvE,SAAO;AACT;AC5FO,MAAM,0BAA0BA,OAAAA,aAAa;AAAC;AAM9C,MAAM,oCAAoCC,OAAAA,uBAAuB;AAAC;AAMlE,MAAM,YAAYC,OAAAA,WAMZ,eAAeC,OAAAA;;;;;;;;;;;;;;;;;;"}
package/dist/stega.d.cts CHANGED
@@ -878,6 +878,26 @@ export declare type ClientReturn<
878
878
  Fallback = Any,
879
879
  > = GroqString extends keyof SanityQueries ? SanityQueries[GroqString] : Fallback
880
880
 
881
+ /**
882
+ * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use
883
+ * as the first generic of `client.fetch` when stega is enabled:
884
+ * ```ts
885
+ * import {createClient} from '@sanity/client'
886
+ * import type {ClientReturnStega} from '@sanity/client/stega'
887
+ *
888
+ * const query = '*[_type == "post"][0]'
889
+ * const post = await client.fetch<ClientReturnStega<typeof query>>(query)
890
+ * ```
891
+ * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),
892
+ * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to
893
+ * `Fallback`, which defaults to `any`, just like `ClientReturn`.
894
+ *
895
+ * @beta
896
+ */
897
+ export declare type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<
898
+ ClientReturn<GroqString, Fallback>
899
+ >
900
+
881
901
  /**
882
902
  * @public
883
903
  * @deprecated -- use `ClientConfig` instead
@@ -6168,12 +6188,118 @@ export declare interface SingleMutationResult {
6168
6188
  /** @public */
6169
6189
  export declare type StackablePerspective = ('published' | 'drafts' | string) & {}
6170
6190
 
6191
+ /**
6192
+ * Marks strings in an already fetched query result as potentially containing stega-encoded data,
6193
+ * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error
6194
+ * until they're cleaned with `stegaClean()`, which recovers the original type.
6195
+ *
6196
+ * This is an identity function, it only changes the type of the input, not its value.
6197
+ * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use
6198
+ * this function for data that has already been fetched.
6199
+ *
6200
+ * @beta
6201
+ */
6202
+ export declare function stegaBrand<Result>(result: Result): StegaBranded<Result>
6203
+
6204
+ /**
6205
+ * Deeply brands the string properties of a query result as `StegaString`, marking them as
6206
+ * potentially containing stega-encoded data.
6207
+ *
6208
+ * String properties that the stega encoder is guaranteed to never encode keep their plain type:
6209
+ * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and
6210
+ * so on), which also preserves discriminated union narrowing on `_type`
6211
+ * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued
6212
+ * `slug` keys (covering `"slug": slug.current` projections)
6213
+ * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and
6214
+ * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay
6215
+ * plain
6216
+ *
6217
+ * Every other string is assumed to be "poisoned", even if the runtime `filter` happens to skip it
6218
+ * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case
6219
+ * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.
6220
+ *
6221
+ * The `Key` and `ParentKey` type parameters track the key the current value is found under, and
6222
+ * the key of the object containing it, they're only used internally during recursion.
6223
+ *
6224
+ * @beta
6225
+ */
6226
+ export declare type StegaBranded<
6227
+ T,
6228
+ Key extends PropertyKey = number,
6229
+ ParentKey extends PropertyKey = number,
6230
+ > = 0 extends 1 & T
6231
+ ? T
6232
+ : T extends string
6233
+ ? T extends {
6234
+ readonly ' stegaBrand': string
6235
+ }
6236
+ ? T
6237
+ : Key extends `_${string}` | 'slug'
6238
+ ? T
6239
+ : Key extends 'current'
6240
+ ? ParentKey extends 'slug'
6241
+ ? T
6242
+ : StegaString<T>
6243
+ : StegaString<T>
6244
+ : T extends number | bigint | boolean | null | undefined
6245
+ ? T
6246
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6247
+ ? T
6248
+ : T extends readonly unknown[]
6249
+ ? {
6250
+ [Index in keyof T]: StegaBranded<T[Index], number, number>
6251
+ }
6252
+ : T extends {
6253
+ _type: 'block'
6254
+ }
6255
+ ? {
6256
+ [K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]
6257
+ }
6258
+ : T extends {
6259
+ _type: 'span'
6260
+ }
6261
+ ? {
6262
+ [K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]
6263
+ }
6264
+ : T extends object
6265
+ ? {
6266
+ [K in keyof T]: StegaBranded<T[K], K, Key>
6267
+ }
6268
+ : T
6269
+
6171
6270
  /**
6172
6271
  * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`
6173
6272
  * and remove all stega-encoded data from it.
6273
+ * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),
6274
+ * the brand is stripped and the original string type is restored.
6174
6275
  * @public
6175
6276
  */
6176
- export declare function stegaClean<Result = unknown>(result: Result): Result
6277
+ export declare function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result>
6278
+
6279
+ /**
6280
+ * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were
6281
+ * branded as `StegaString` are returned to their original type, everything else is left as-is.
6282
+ * @public
6283
+ */
6284
+ export declare type StegaCleaned<T> = 0 extends 1 & T
6285
+ ? T
6286
+ : T extends {
6287
+ readonly ' stegaBrand': infer Original extends string
6288
+ }
6289
+ ? Original
6290
+ : T extends string | number | bigint | boolean | null | undefined
6291
+ ? T
6292
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6293
+ ? T
6294
+ : T extends readonly unknown[]
6295
+ ? {
6296
+ [Index in keyof T]: StegaCleaned<T[Index]>
6297
+ }
6298
+ : T extends object
6299
+ ? {
6300
+ [K in keyof T]: StegaCleaned<T[K]>
6301
+ }
6302
+ : T
6177
6303
 
6178
6304
  /** @public */
6179
6305
  export declare interface StegaConfig {
@@ -6253,6 +6379,29 @@ export declare function stegaEncodeSourceMap<Result = unknown>(
6253
6379
  config: InitializedStegaConfig_2,
6254
6380
  ): Result
6255
6381
 
6382
+ /**
6383
+ * A string that might contain stega-encoded data, and is unsafe to compare against string literals
6384
+ * until it's been cleaned with `stegaClean()`.
6385
+ *
6386
+ * The type intersects a template literal that has a human readable suffix, with a brand property
6387
+ * that holds the original string type:
6388
+ * - The suffix makes TypeScript report "This comparison appears to be unintentional" (TS2367) when
6389
+ * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to
6390
+ * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,
6391
+ * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime
6392
+ * value never ends with that text.
6393
+ * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's
6394
+ * a string-keyed property rather than a `unique symbol` so that branded types stay structurally
6395
+ * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in
6396
+ * the type system and is never present at runtime.
6397
+ *
6398
+ * @beta
6399
+ */
6400
+ export declare type StegaString<T extends string = string> =
6401
+ `${T} (may contain hidden stega characters)` & {
6402
+ readonly ' stegaBrand': T
6403
+ }
6404
+
6256
6405
  /**
6257
6406
  * Allowed still image formats (thumbnail + storyboard).
6258
6407
  * @public
package/dist/stega.d.ts CHANGED
@@ -878,6 +878,26 @@ export declare type ClientReturn<
878
878
  Fallback = Any,
879
879
  > = GroqString extends keyof SanityQueries ? SanityQueries[GroqString] : Fallback
880
880
 
881
+ /**
882
+ * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use
883
+ * as the first generic of `client.fetch` when stega is enabled:
884
+ * ```ts
885
+ * import {createClient} from '@sanity/client'
886
+ * import type {ClientReturnStega} from '@sanity/client/stega'
887
+ *
888
+ * const query = '*[_type == "post"][0]'
889
+ * const post = await client.fetch<ClientReturnStega<typeof query>>(query)
890
+ * ```
891
+ * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),
892
+ * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to
893
+ * `Fallback`, which defaults to `any`, just like `ClientReturn`.
894
+ *
895
+ * @beta
896
+ */
897
+ export declare type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<
898
+ ClientReturn<GroqString, Fallback>
899
+ >
900
+
881
901
  /**
882
902
  * @public
883
903
  * @deprecated -- use `ClientConfig` instead
@@ -6168,12 +6188,118 @@ export declare interface SingleMutationResult {
6168
6188
  /** @public */
6169
6189
  export declare type StackablePerspective = ('published' | 'drafts' | string) & {}
6170
6190
 
6191
+ /**
6192
+ * Marks strings in an already fetched query result as potentially containing stega-encoded data,
6193
+ * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error
6194
+ * until they're cleaned with `stegaClean()`, which recovers the original type.
6195
+ *
6196
+ * This is an identity function, it only changes the type of the input, not its value.
6197
+ * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use
6198
+ * this function for data that has already been fetched.
6199
+ *
6200
+ * @beta
6201
+ */
6202
+ export declare function stegaBrand<Result>(result: Result): StegaBranded<Result>
6203
+
6204
+ /**
6205
+ * Deeply brands the string properties of a query result as `StegaString`, marking them as
6206
+ * potentially containing stega-encoded data.
6207
+ *
6208
+ * String properties that the stega encoder is guaranteed to never encode keep their plain type:
6209
+ * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and
6210
+ * so on), which also preserves discriminated union narrowing on `_type`
6211
+ * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued
6212
+ * `slug` keys (covering `"slug": slug.current` projections)
6213
+ * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and
6214
+ * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay
6215
+ * plain
6216
+ *
6217
+ * Every other string is assumed to be "poisoned", even if the runtime `filter` happens to skip it
6218
+ * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case
6219
+ * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.
6220
+ *
6221
+ * The `Key` and `ParentKey` type parameters track the key the current value is found under, and
6222
+ * the key of the object containing it, they're only used internally during recursion.
6223
+ *
6224
+ * @beta
6225
+ */
6226
+ export declare type StegaBranded<
6227
+ T,
6228
+ Key extends PropertyKey = number,
6229
+ ParentKey extends PropertyKey = number,
6230
+ > = 0 extends 1 & T
6231
+ ? T
6232
+ : T extends string
6233
+ ? T extends {
6234
+ readonly ' stegaBrand': string
6235
+ }
6236
+ ? T
6237
+ : Key extends `_${string}` | 'slug'
6238
+ ? T
6239
+ : Key extends 'current'
6240
+ ? ParentKey extends 'slug'
6241
+ ? T
6242
+ : StegaString<T>
6243
+ : StegaString<T>
6244
+ : T extends number | bigint | boolean | null | undefined
6245
+ ? T
6246
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6247
+ ? T
6248
+ : T extends readonly unknown[]
6249
+ ? {
6250
+ [Index in keyof T]: StegaBranded<T[Index], number, number>
6251
+ }
6252
+ : T extends {
6253
+ _type: 'block'
6254
+ }
6255
+ ? {
6256
+ [K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]
6257
+ }
6258
+ : T extends {
6259
+ _type: 'span'
6260
+ }
6261
+ ? {
6262
+ [K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]
6263
+ }
6264
+ : T extends object
6265
+ ? {
6266
+ [K in keyof T]: StegaBranded<T[K], K, Key>
6267
+ }
6268
+ : T
6269
+
6171
6270
  /**
6172
6271
  * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`
6173
6272
  * and remove all stega-encoded data from it.
6273
+ * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),
6274
+ * the brand is stripped and the original string type is restored.
6174
6275
  * @public
6175
6276
  */
6176
- export declare function stegaClean<Result = unknown>(result: Result): Result
6277
+ export declare function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result>
6278
+
6279
+ /**
6280
+ * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were
6281
+ * branded as `StegaString` are returned to their original type, everything else is left as-is.
6282
+ * @public
6283
+ */
6284
+ export declare type StegaCleaned<T> = 0 extends 1 & T
6285
+ ? T
6286
+ : T extends {
6287
+ readonly ' stegaBrand': infer Original extends string
6288
+ }
6289
+ ? Original
6290
+ : T extends string | number | bigint | boolean | null | undefined
6291
+ ? T
6292
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
6293
+ ? T
6294
+ : T extends readonly unknown[]
6295
+ ? {
6296
+ [Index in keyof T]: StegaCleaned<T[Index]>
6297
+ }
6298
+ : T extends object
6299
+ ? {
6300
+ [K in keyof T]: StegaCleaned<T[K]>
6301
+ }
6302
+ : T
6177
6303
 
6178
6304
  /** @public */
6179
6305
  export declare interface StegaConfig {
@@ -6253,6 +6379,29 @@ export declare function stegaEncodeSourceMap<Result = unknown>(
6253
6379
  config: InitializedStegaConfig_2,
6254
6380
  ): Result
6255
6381
 
6382
+ /**
6383
+ * A string that might contain stega-encoded data, and is unsafe to compare against string literals
6384
+ * until it's been cleaned with `stegaClean()`.
6385
+ *
6386
+ * The type intersects a template literal that has a human readable suffix, with a brand property
6387
+ * that holds the original string type:
6388
+ * - The suffix makes TypeScript report "This comparison appears to be unintentional" (TS2367) when
6389
+ * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to
6390
+ * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,
6391
+ * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime
6392
+ * value never ends with that text.
6393
+ * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's
6394
+ * a string-keyed property rather than a `unique symbol` so that branded types stay structurally
6395
+ * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in
6396
+ * the type system and is never present at runtime.
6397
+ *
6398
+ * @beta
6399
+ */
6400
+ export declare type StegaString<T extends string = string> =
6401
+ `${T} (may contain hidden stega characters)` & {
6402
+ readonly ' stegaBrand': T
6403
+ }
6404
+
6256
6405
  /**
6257
6406
  * Allowed still image formats (thumbnail + storyboard).
6258
6407
  * @public
package/dist/stega.js CHANGED
@@ -2,6 +2,9 @@ import { createClient as createClient$1, requester as requester$1, ObservableSan
2
2
  export * from "@sanity/client";
3
3
  import { encodeIntoResult, stegaEncodeSourceMap } from "./_chunks-es/stegaEncodeSourceMap.js";
4
4
  import { stegaClean, vercelStegaCleanAll } from "./_chunks-es/stegaClean.js";
5
+ function stegaBrand(result) {
6
+ return result;
7
+ }
5
8
  class SanityStegaClient extends SanityClient {
6
9
  }
7
10
  class ObservableSanityStegaClient extends ObservableSanityClient {
@@ -13,6 +16,7 @@ export {
13
16
  createClient,
14
17
  encodeIntoResult,
15
18
  requester,
19
+ stegaBrand,
16
20
  stegaClean,
17
21
  stegaEncodeSourceMap,
18
22
  vercelStegaCleanAll
package/dist/stega.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"stega.js","sources":["../src/stega/index.ts"],"sourcesContent":["export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {stegaClean, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["originalRequester","originalCreateClient"],"mappings":";;;;AAiBO,MAAM,0BAA0B,aAAa;AAAC;AAM9C,MAAM,oCAAoC,uBAAuB;AAAC;AAMlE,MAAM,YAAYA,aAMZ,eAAeC;"}
1
+ {"version":3,"file":"stega.js","sources":["../src/stega/stegaBrand.ts","../src/stega/index.ts"],"sourcesContent":["import type {Any, ClientReturn} from '@sanity/client'\n\n/**\n * A string that might contain stega-encoded data, and is unsafe to compare against string literals\n * until it's been cleaned with `stegaClean()`.\n *\n * The type intersects a template literal that has a human readable suffix, with a brand property\n * that holds the original string type:\n * - The suffix makes TypeScript report \"This comparison appears to be unintentional\" (TS2367) when\n * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to\n * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,\n * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime\n * value never ends with that text.\n * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's\n * a string-keyed property rather than a `unique symbol` so that branded types stay structurally\n * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in\n * the type system and is never present at runtime.\n *\n * @beta\n */\nexport type StegaString<T extends string = string> =\n `${T} (may contain hidden stega characters)` & {\n readonly ' stegaBrand': T\n }\n\n/**\n * Deeply brands the string properties of a query result as `StegaString`, marking them as\n * potentially containing stega-encoded data.\n *\n * String properties that the stega encoder is guaranteed to never encode keep their plain type:\n * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and\n * so on), which also preserves discriminated union narrowing on `_type`\n * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued\n * `slug` keys (covering `\"slug\": slug.current` projections)\n * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and\n * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay\n * plain\n *\n * Every other string is assumed to be \"poisoned\", even if the runtime `filter` happens to skip it\n * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case\n * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.\n *\n * The `Key` and `ParentKey` type parameters track the key the current value is found under, and\n * the key of the object containing it, they're only used internally during recursion.\n *\n * @beta\n */\nexport type StegaBranded<\n T,\n Key extends PropertyKey = number,\n ParentKey extends PropertyKey = number,\n> = 0 extends 1 & T\n ? T\n : T extends string\n ? T extends {readonly ' stegaBrand': string}\n ? T\n : Key extends `_${string}` | 'slug'\n ? T\n : Key extends 'current'\n ? ParentKey extends 'slug'\n ? T\n : StegaString<T>\n : StegaString<T>\n : T extends number | bigint | boolean | null | undefined\n ? T\n : T extends Date | RegExp | ((...args: never[]) => unknown)\n ? T\n : T extends readonly unknown[]\n ? {[Index in keyof T]: StegaBranded<T[Index], number, number>}\n : T extends {_type: 'block'}\n ? {[K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends {_type: 'span'}\n ? {[K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]}\n : T extends object\n ? {[K in keyof T]: StegaBranded<T[K], K, Key>}\n : T\n\n/**\n * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use\n * as the first generic of `client.fetch` when stega is enabled:\n * ```ts\n * import {createClient} from '@sanity/client'\n * import type {ClientReturnStega} from '@sanity/client/stega'\n *\n * const query = '*[_type == \"post\"][0]'\n * const post = await client.fetch<ClientReturnStega<typeof query>>(query)\n * ```\n * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),\n * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to\n * `Fallback`, which defaults to `any`, just like `ClientReturn`.\n *\n * @beta\n */\nexport type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<\n ClientReturn<GroqString, Fallback>\n>\n\n/**\n * Marks strings in an already fetched query result as potentially containing stega-encoded data,\n * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error\n * until they're cleaned with `stegaClean()`, which recovers the original type.\n *\n * This is an identity function, it only changes the type of the input, not its value.\n * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use\n * this function for data that has already been fetched.\n *\n * @beta\n */\nexport function stegaBrand<Result>(result: Result): StegaBranded<Result> {\n return result as StegaBranded<Result>\n}\n","export * from '@sanity/client'\nimport {\n createClient as originalCreateClient,\n ObservableSanityClient,\n requester as originalRequester,\n SanityClient,\n} from '@sanity/client'\n\nexport {encodeIntoResult} from './encodeIntoResult'\nexport {type ClientReturnStega, stegaBrand, type StegaBranded, type StegaString} from './stegaBrand'\nexport {stegaClean, type StegaCleaned, vercelStegaCleanAll} from './stegaClean'\nexport {stegaEncodeSourceMap} from './stegaEncodeSourceMap'\nexport * from './types'\n\n/**\n * @deprecated -- Use `import {SanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class SanityStegaClient extends SanityClient {}\n\n/**\n * @deprecated -- Use `import {ObservableSanityClient} from '@sanity/client'` instead\n * @public\n */\nexport class ObservableSanityStegaClient extends ObservableSanityClient {}\n\n/**\n * @deprecated -- Use `import {requester} from '@sanity/client'` instead\n * @public\n */\nexport const requester = originalRequester\n\n/**\n * @deprecated -- Use `import {createClient} from '@sanity/client'` instead\n * @public\n */\nexport const createClient = originalCreateClient\n"],"names":["originalRequester","originalCreateClient"],"mappings":";;;;AA4GO,SAAS,WAAmB,QAAsC;AACvE,SAAO;AACT;AC5FO,MAAM,0BAA0B,aAAa;AAAC;AAM9C,MAAM,oCAAoC,uBAAuB;AAAC;AAMlE,MAAM,YAAYA,aAMZ,eAAeC;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sanity/client",
3
- "version": "7.24.0",
3
+ "version": "7.25.0",
4
4
  "description": "Client for retrieving, creating and patching data from Sanity.io",
5
5
  "keywords": [
6
6
  "sanity",
@@ -145,6 +145,7 @@
145
145
  "@sanity/pkg-utils": "^7.2.2",
146
146
  "@types/json-diff": "^1.0.3",
147
147
  "@types/node": "^22.9.0",
148
+ "@types/react": "^19.2.17",
148
149
  "@typescript-eslint/eslint-plugin": "^8.29.1",
149
150
  "@typescript-eslint/parser": "^8.29.1",
150
151
  "@vercel/stega": "1.1.0",
@@ -7,7 +7,8 @@ import {
7
7
  } from '@sanity/client'
8
8
 
9
9
  export {encodeIntoResult} from './encodeIntoResult'
10
- export {stegaClean, vercelStegaCleanAll} from './stegaClean'
10
+ export {type ClientReturnStega, stegaBrand, type StegaBranded, type StegaString} from './stegaBrand'
11
+ export {stegaClean, type StegaCleaned, vercelStegaCleanAll} from './stegaClean'
11
12
  export {stegaEncodeSourceMap} from './stegaEncodeSourceMap'
12
13
  export * from './types'
13
14
 
@@ -0,0 +1,111 @@
1
+ import type {Any, ClientReturn} from '@sanity/client'
2
+
3
+ /**
4
+ * A string that might contain stega-encoded data, and is unsafe to compare against string literals
5
+ * until it's been cleaned with `stegaClean()`.
6
+ *
7
+ * The type intersects a template literal that has a human readable suffix, with a brand property
8
+ * that holds the original string type:
9
+ * - The suffix makes TypeScript report "This comparison appears to be unintentional" (TS2367) when
10
+ * the string is compared to a literal (`===`, `switch` cases), and makes it unassignable to
11
+ * literal unions like `'left' | 'right'`, while keeping it assignable to `string` so rendering,
12
+ * interpolation and string methods keep working. The suffix is a type-level fiction, the runtime
13
+ * value never ends with that text.
14
+ * - The brand property preserves the original type so `stegaClean()` can recover it exactly. It's
15
+ * a string-keyed property rather than a `unique symbol` so that branded types stay structurally
16
+ * compatible across duplicated copies of `@sanity/client` in `node_modules`. It only exists in
17
+ * the type system and is never present at runtime.
18
+ *
19
+ * @beta
20
+ */
21
+ export type StegaString<T extends string = string> =
22
+ `${T} (may contain hidden stega characters)` & {
23
+ readonly ' stegaBrand': T
24
+ }
25
+
26
+ /**
27
+ * Deeply brands the string properties of a query result as `StegaString`, marking them as
28
+ * potentially containing stega-encoded data.
29
+ *
30
+ * String properties that the stega encoder is guaranteed to never encode keep their plain type:
31
+ * - keys starting with `_` (`_id`, `_type`, `_createdAt`, `_updatedAt`, `_key`, `_ref`, `_rev` and
32
+ * so on), which also preserves discriminated union narrowing on `_type`
33
+ * - `slug.current` patterns: a `current` key directly under a `slug` key, as well as string valued
34
+ * `slug` keys (covering `"slug": slug.current` projections)
35
+ * - Portable Text internals: the encoder only visits `children` of `_type: 'block'` objects and
36
+ * `text` of `_type: 'span'` objects, so properties like `style`, `listItem` and `marks` stay
37
+ * plain
38
+ *
39
+ * Every other string is assumed to be "poisoned", even if the runtime `filter` happens to skip it
40
+ * (URLs, dates, keys ending in `Id`, denylisted keys). Being conservative is safe: the worst case
41
+ * is a redundant `stegaClean()` call, whereas under-branding would hide real bugs.
42
+ *
43
+ * The `Key` and `ParentKey` type parameters track the key the current value is found under, and
44
+ * the key of the object containing it, they're only used internally during recursion.
45
+ *
46
+ * @beta
47
+ */
48
+ export type StegaBranded<
49
+ T,
50
+ Key extends PropertyKey = number,
51
+ ParentKey extends PropertyKey = number,
52
+ > = 0 extends 1 & T
53
+ ? T
54
+ : T extends string
55
+ ? T extends {readonly ' stegaBrand': string}
56
+ ? T
57
+ : Key extends `_${string}` | 'slug'
58
+ ? T
59
+ : Key extends 'current'
60
+ ? ParentKey extends 'slug'
61
+ ? T
62
+ : StegaString<T>
63
+ : StegaString<T>
64
+ : T extends number | bigint | boolean | null | undefined
65
+ ? T
66
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
67
+ ? T
68
+ : T extends readonly unknown[]
69
+ ? {[Index in keyof T]: StegaBranded<T[Index], number, number>}
70
+ : T extends {_type: 'block'}
71
+ ? {[K in keyof T]: K extends 'children' ? StegaBranded<T[K], K, Key> : T[K]}
72
+ : T extends {_type: 'span'}
73
+ ? {[K in keyof T]: K extends 'text' ? StegaBranded<T[K], K, Key> : T[K]}
74
+ : T extends object
75
+ ? {[K in keyof T]: StegaBranded<T[K], K, Key>}
76
+ : T
77
+
78
+ /**
79
+ * Drop-in replacement for `ClientReturn` that brands the result strings as `StegaString`, for use
80
+ * as the first generic of `client.fetch` when stega is enabled:
81
+ * ```ts
82
+ * import {createClient} from '@sanity/client'
83
+ * import type {ClientReturnStega} from '@sanity/client/stega'
84
+ *
85
+ * const query = '*[_type == "post"][0]'
86
+ * const post = await client.fetch<ClientReturnStega<typeof query>>(query)
87
+ * ```
88
+ * When the query is registered in the `SanityQueries` interface (for example by `sanity typegen`),
89
+ * the result type is looked up and deeply branded with `StegaBranded`. Otherwise it falls back to
90
+ * `Fallback`, which defaults to `any`, just like `ClientReturn`.
91
+ *
92
+ * @beta
93
+ */
94
+ export type ClientReturnStega<GroqString extends string, Fallback = Any> = StegaBranded<
95
+ ClientReturn<GroqString, Fallback>
96
+ >
97
+
98
+ /**
99
+ * Marks strings in an already fetched query result as potentially containing stega-encoded data,
100
+ * by re-typing them as `StegaString`. Comparing branded strings to string literals is a type error
101
+ * until they're cleaned with `stegaClean()`, which recovers the original type.
102
+ *
103
+ * This is an identity function, it only changes the type of the input, not its value.
104
+ * Prefer passing `ClientReturnStega` as the first generic to `client.fetch` when possible, and use
105
+ * this function for data that has already been fetched.
106
+ *
107
+ * @beta
108
+ */
109
+ export function stegaBrand<Result>(result: Result): StegaBranded<Result> {
110
+ return result as StegaBranded<Result>
111
+ }
@@ -1,12 +1,33 @@
1
1
  import {vercelStegaClean} from '@vercel/stega'
2
2
 
3
+ /**
4
+ * The result of removing stega-encoded data from a value with `stegaClean()`: strings that were
5
+ * branded as `StegaString` are returned to their original type, everything else is left as-is.
6
+ * @public
7
+ */
8
+ export type StegaCleaned<T> = 0 extends 1 & T
9
+ ? T
10
+ : T extends {readonly ' stegaBrand': infer Original extends string}
11
+ ? Original
12
+ : T extends string | number | bigint | boolean | null | undefined
13
+ ? T
14
+ : T extends Date | RegExp | ((...args: never[]) => unknown)
15
+ ? T
16
+ : T extends readonly unknown[]
17
+ ? {[Index in keyof T]: StegaCleaned<T[Index]>}
18
+ : T extends object
19
+ ? {[K in keyof T]: StegaCleaned<T[K]>}
20
+ : T
21
+
3
22
  /**
4
23
  * Can take a `result` JSON from a `const {result} = client.fetch(query, params, {filterResponse: false})`
5
24
  * and remove all stega-encoded data from it.
25
+ * If the result type has strings branded as `StegaString` (by `ClientReturnStega` or `stegaBrand()`),
26
+ * the brand is stripped and the original string type is restored.
6
27
  * @public
7
28
  */
8
- export function stegaClean<Result = unknown>(result: Result): Result {
9
- return vercelStegaClean<Result>(result)
29
+ export function stegaClean<Result = unknown>(result: Result): StegaCleaned<Result> {
30
+ return vercelStegaClean(result) as StegaCleaned<Result>
10
31
  }
11
32
 
12
33
  /**