ugly-app 0.1.901 → 0.1.902
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/dist/cli/version.d.ts +1 -1
- package/dist/cli/version.js +1 -1
- package/dist/client/components/Image.d.ts.map +1 -1
- package/dist/client/components/Image.js +2 -1
- package/dist/client/components/Image.js.map +1 -1
- package/dist/client/components/UserImage.d.ts.map +1 -1
- package/dist/client/components/UserImage.js +5 -1
- package/dist/client/components/UserImage.js.map +1 -1
- package/dist/client/components/crossOriginProps.d.ts +6 -0
- package/dist/client/components/crossOriginProps.d.ts.map +1 -0
- package/dist/client/components/crossOriginProps.js +44 -0
- package/dist/client/components/crossOriginProps.js.map +1 -0
- package/package.json +1 -1
- package/src/cli/version.ts +1 -1
- package/src/client/components/Image.tsx +2 -0
- package/src/client/components/UserImage.tsx +13 -1
- package/src/client/components/crossOriginProps.test.ts +35 -0
- package/src/client/components/crossOriginProps.ts +43 -0
package/dist/cli/version.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export declare const CLI_VERSION = "0.1.
|
|
1
|
+
export declare const CLI_VERSION = "0.1.902";
|
|
2
2
|
//# sourceMappingURL=version.d.ts.map
|
package/dist/cli/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../../../src/client/components/Image.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"Image.d.ts","sourceRoot":"","sources":["../../../src/client/components/Image.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAGzD,+DAA+D;AAC/D,MAAM,WAAW,WAAW;IAC1B,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,gDAAgD;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,CAAC;AAEpE,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9C,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AASD,wBAAgB,KAAK,CAAC,EACpB,GAAG,EACH,UAAoB,EACpB,GAAQ,EACR,SAAc,EACd,KAAK,GACN,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CAclC"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { crossOriginProps } from './crossOriginProps.js';
|
|
2
3
|
const objectFitMap = {
|
|
3
4
|
contain: 'contain',
|
|
4
5
|
cover: 'cover',
|
|
@@ -11,6 +12,6 @@ export function Image({ src, resizeMode = 'cover', alt = '', className = '', sty
|
|
|
11
12
|
const url = typeof src === 'string' ? src : src?.url;
|
|
12
13
|
if (!url)
|
|
13
14
|
return null;
|
|
14
|
-
return (_jsx("img", { src: url, alt: alt, className: className, style: { objectFit: objectFitMap[resizeMode], ...style } }));
|
|
15
|
+
return (_jsx("img", { src: url, alt: alt, className: className, ...crossOriginProps(url), style: { objectFit: objectFitMap[resizeMode], ...style } }));
|
|
15
16
|
}
|
|
16
17
|
//# sourceMappingURL=Image.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Image.js","sourceRoot":"","sources":["../../../src/client/components/Image.tsx"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"Image.js","sourceRoot":"","sources":["../../../src/client/components/Image.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAsBzD,MAAM,YAAY,GAAmD;IACnE,OAAO,EAAE,SAAS;IAClB,KAAK,EAAE,OAAO;IACd,OAAO,EAAE,MAAM;IACf,MAAM,EAAE,MAAM;CACf,CAAC;AAEF,MAAM,UAAU,KAAK,CAAC,EACpB,GAAG,EACH,UAAU,GAAG,OAAO,EACpB,GAAG,GAAG,EAAE,EACR,SAAS,GAAG,EAAE,EACd,KAAK,GACM;IACX,2EAA2E;IAC3E,oFAAoF;IACpF,MAAM,GAAG,GAAG,OAAO,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,CAAC;IACrD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,OAAO,CACL,cACE,GAAG,EAAE,GAAG,EACR,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,SAAS,KAChB,gBAAgB,CAAC,GAAG,CAAC,EACzB,KAAK,EAAE,EAAE,SAAS,EAAE,YAAY,CAAC,UAAU,CAAC,EAAE,GAAG,KAAK,EAAE,GACxD,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserImage.d.ts","sourceRoot":"","sources":["../../../src/client/components/UserImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,iBAAiB,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"UserImage.d.ts","sourceRoot":"","sources":["../../../src/client/components/UserImage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,iBAAiB,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAI5E,MAAM,WAAW,cAAe,SAAQ,IAAI,CAC1C,iBAAiB,CAAC,gBAAgB,CAAC,EACnC,KAAK,CACN;IACC,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,qCAAqC;IACrC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EACxB,MAAM,EACN,IAAS,EACT,KAAK,EACL,GAAG,IAAI,EACR,EAAE,cAAc,GAAG,YAAY,CAyC/B"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { useCachedDoc } from '../cachedDoc.js';
|
|
3
|
+
import { crossOriginProps } from './crossOriginProps.js';
|
|
3
4
|
/**
|
|
4
5
|
* Renders a user's avatar. Resolves through the framework's getter-backed
|
|
5
6
|
* `userPublic` collection (cached client-side). When no avatar is available,
|
|
@@ -23,6 +24,9 @@ export function UserImage({ userId, size = 32, style, ...rest }) {
|
|
|
23
24
|
display: 'inline-block',
|
|
24
25
|
} }));
|
|
25
26
|
}
|
|
26
|
-
|
|
27
|
+
// crossOriginProps: user avatars live on blob.ugly.bot, which sends CORS but
|
|
28
|
+
// not CORP — without opting into CORS they are blocked outright on any
|
|
29
|
+
// cross-origin-isolated (COEP require-corp) page. See crossOriginProps.ts.
|
|
30
|
+
return (_jsx("img", { src: src, style: boxStyle, alt: "", ...crossOriginProps(src), ...rest }));
|
|
27
31
|
}
|
|
28
32
|
//# sourceMappingURL=UserImage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UserImage.js","sourceRoot":"","sources":["../../../src/client/components/UserImage.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"UserImage.js","sourceRoot":"","sources":["../../../src/client/components/UserImage.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAYzD;;;;GAIG;AACH,MAAM,UAAU,SAAS,CAAC,EACxB,MAAM,EACN,IAAI,GAAG,EAAE,EACT,KAAK,EACL,GAAG,IAAI,EACQ;IACf,MAAM,OAAO,GAAG,YAAY,CAC1B,YAAY,EACZ,MAAM,CACP,CAAC;IACF,MAAM,GAAG,GAAG,OAAO,EAAE,SAAS,IAAI,SAAS,CAAC;IAE5C,MAAM,QAAQ,GAAkB;QAC9B,KAAK,EAAE,IAAI;QACX,MAAM,EAAE,IAAI;QACZ,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,OAAO;QAClB,UAAU,EAAE,CAAC;QACb,GAAG,KAAK;KACT,CAAC;IAEF,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,CACL,6BACc,MAAM,EAClB,KAAK,EAAE;gBACL,GAAG,QAAQ;gBACX,UAAU,EAAE,+BAA+B;gBAC3C,OAAO,EAAE,cAAc;aACxB,GACD,CACH,CAAC;IACJ,CAAC;IAED,6EAA6E;IAC7E,uEAAuE;IACvE,2EAA2E;IAC3E,OAAO,CACL,cACE,GAAG,EAAE,GAAG,EACR,KAAK,EAAE,QAAQ,EACf,GAAG,EAAC,EAAE,KACF,gBAAgB,CAAC,GAAG,CAAC,KACrB,IAAI,GACR,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crossOriginProps.d.ts","sourceRoot":"","sources":["../../../src/client/components/crossOriginProps.ts"],"names":[],"mappings":"AA0BA,mFAAmF;AACnF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG;IAC7C,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,cAAc,CAAC,EAAE,aAAa,CAAC;CAChC,CAYA"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Cross-origin media under COEP `require-corp`.
|
|
2
|
+
//
|
|
3
|
+
// Apps that enable cross-origin isolation (needed for SharedArrayBuffer) send
|
|
4
|
+
// `Cross-Origin-Embedder-Policy: require-corp`. Under that policy the browser
|
|
5
|
+
// BLOCKS every cross-origin subresource unless one of two things is true:
|
|
6
|
+
//
|
|
7
|
+
// 1. the response carries `Cross-Origin-Resource-Policy: cross-origin`, or
|
|
8
|
+
// 2. the element opts into CORS (`crossorigin`) AND the response carries
|
|
9
|
+
// permissive CORS headers.
|
|
10
|
+
//
|
|
11
|
+
// `blob.ugly.bot` (the R2 custom domain serving user avatars and generated
|
|
12
|
+
// media) sends `Access-Control-Allow-Origin: *` but NOT `Cross-Origin-Resource-
|
|
13
|
+
// Policy` — CORP is not expressible through R2's bucket CORS config. So a plain
|
|
14
|
+
// `<img src="https://blob.ugly.bot/…">` on a cross-origin-isolated page is
|
|
15
|
+
// blocked and renders as a broken image. In prod this silently ate user avatars
|
|
16
|
+
// on ugly.chat (reported as "Icon don't appear properly"); the request itself is
|
|
17
|
+
// a perfectly healthy 200 image/webp, which is why it never looked like a server
|
|
18
|
+
// problem.
|
|
19
|
+
//
|
|
20
|
+
// Opting into CORS satisfies condition 2 using the headers blob.ugly.bot
|
|
21
|
+
// already sends, so this needs no infrastructure change.
|
|
22
|
+
//
|
|
23
|
+
// Only applied to cross-origin URLs: adding `crossorigin` to a same-origin image
|
|
24
|
+
// is unnecessary, and on a server that does not emit CORS headers it would turn
|
|
25
|
+
// a working image into a broken one.
|
|
26
|
+
/** `crossOrigin`/`referrerPolicy` props for an <img>, or `{}` when same-origin. */
|
|
27
|
+
export function crossOriginProps(url) {
|
|
28
|
+
// Relative URLs and data:/blob: are same-origin by construction.
|
|
29
|
+
if (!/^https?:\/\//i.test(url))
|
|
30
|
+
return {};
|
|
31
|
+
try {
|
|
32
|
+
if (typeof window === 'undefined')
|
|
33
|
+
return {};
|
|
34
|
+
if (new URL(url).origin === window.location.origin)
|
|
35
|
+
return {};
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
// `no-referrer` keeps the CORS preflight-free request cacheable and avoids
|
|
41
|
+
// leaking the current page path to the media host.
|
|
42
|
+
return { crossOrigin: 'anonymous', referrerPolicy: 'no-referrer' };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=crossOriginProps.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crossOriginProps.js","sourceRoot":"","sources":["../../../src/client/components/crossOriginProps.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,0EAA0E;AAC1E,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,gCAAgC;AAChC,EAAE;AACF,2EAA2E;AAC3E,gFAAgF;AAChF,gFAAgF;AAChF,2EAA2E;AAC3E,gFAAgF;AAChF,iFAAiF;AACjF,iFAAiF;AACjF,WAAW;AACX,EAAE;AACF,yEAAyE;AACzE,yDAAyD;AACzD,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,qCAAqC;AAErC,mFAAmF;AACnF,MAAM,UAAU,gBAAgB,CAAC,GAAW;IAI1C,iEAAiE;IACjE,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IAC1C,IAAI,CAAC;QACH,IAAI,OAAO,MAAM,KAAK,WAAW;YAAE,OAAO,EAAE,CAAC;QAC7C,IAAI,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,MAAM,CAAC,QAAQ,CAAC,MAAM;YAAE,OAAO,EAAE,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,2EAA2E;IAC3E,mDAAmD;IACnD,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;AACrE,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ugly-app",
|
|
3
3
|
"packageManager": "pnpm@10.34.5",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.902",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"comment:files": "Allowlist what ships to npm. dist = runtime; src = sourcemap targets (dist/*.js.map reference ../../src/); templates = CLI scaffold. Everything else at repo root (.pgdata local Postgres, coverage, assets/icons sources, test/, test-results/) is excluded by omission. The !negations strip the scaffold's installed deps + cruft (templates/node_modules is 200MB+ and must never ship). package.json/README/LICENSE ship automatically.",
|
|
7
7
|
"files": [
|
package/src/cli/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by prebuild — do not edit manually
|
|
2
|
-
export const CLI_VERSION = "0.1.
|
|
2
|
+
export const CLI_VERSION = "0.1.902";
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CSSProperties, ReactElement } from 'react';
|
|
2
|
+
import { crossOriginProps } from './crossOriginProps.js';
|
|
2
3
|
|
|
3
4
|
/** Minimal image result — compatible with app's ImageResult */
|
|
4
5
|
export interface ImageSource {
|
|
@@ -43,6 +44,7 @@ export function Image({
|
|
|
43
44
|
src={url}
|
|
44
45
|
alt={alt}
|
|
45
46
|
className={className}
|
|
47
|
+
{...crossOriginProps(url)}
|
|
46
48
|
style={{ objectFit: objectFitMap[resizeMode], ...style }}
|
|
47
49
|
/>
|
|
48
50
|
);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { CSSProperties, ImgHTMLAttributes, ReactElement } from 'react';
|
|
2
2
|
import { useCachedDoc } from '../cachedDoc.js';
|
|
3
|
+
import { crossOriginProps } from './crossOriginProps.js';
|
|
3
4
|
|
|
4
5
|
export interface UserImageProps extends Omit<
|
|
5
6
|
ImgHTMLAttributes<HTMLImageElement>,
|
|
@@ -50,5 +51,16 @@ export function UserImage({
|
|
|
50
51
|
);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
+
// crossOriginProps: user avatars live on blob.ugly.bot, which sends CORS but
|
|
55
|
+
// not CORP — without opting into CORS they are blocked outright on any
|
|
56
|
+
// cross-origin-isolated (COEP require-corp) page. See crossOriginProps.ts.
|
|
57
|
+
return (
|
|
58
|
+
<img
|
|
59
|
+
src={src}
|
|
60
|
+
style={boxStyle}
|
|
61
|
+
alt=""
|
|
62
|
+
{...crossOriginProps(src)}
|
|
63
|
+
{...rest}
|
|
64
|
+
/>
|
|
65
|
+
);
|
|
54
66
|
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// @vitest-environment jsdom
|
|
2
|
+
//
|
|
3
|
+
// Getting this wrong in either direction breaks images:
|
|
4
|
+
// - omitting `crossorigin` on a cross-origin URL leaves it blocked under COEP
|
|
5
|
+
// - adding it to a same-origin URL (or a host without CORS headers) turns a
|
|
6
|
+
// working image into a broken one
|
|
7
|
+
// so both directions are pinned.
|
|
8
|
+
import { describe, it, expect } from 'vitest';
|
|
9
|
+
import { crossOriginProps } from './crossOriginProps.js';
|
|
10
|
+
|
|
11
|
+
describe('crossOriginProps', () => {
|
|
12
|
+
it('opts a cross-origin image into CORS so COEP require-corp allows it', () => {
|
|
13
|
+
expect(crossOriginProps('https://blob.ugly.bot/user/abc/def.webp')).toEqual(
|
|
14
|
+
{ crossOrigin: 'anonymous', referrerPolicy: 'no-referrer' },
|
|
15
|
+
);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('leaves same-origin images alone', () => {
|
|
19
|
+
expect(crossOriginProps(`${window.location.origin}/logo.png`)).toEqual({});
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('leaves relative URLs alone', () => {
|
|
23
|
+
expect(crossOriginProps('/assets/logo.png')).toEqual({});
|
|
24
|
+
expect(crossOriginProps('logo.png')).toEqual({});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('leaves data: and blob: URLs alone', () => {
|
|
28
|
+
expect(crossOriginProps('data:image/png;base64,iVBORw0KGgo=')).toEqual({});
|
|
29
|
+
expect(crossOriginProps('blob:https://example.com/abc-123')).toEqual({});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('does not throw on a malformed URL', () => {
|
|
33
|
+
expect(crossOriginProps('https://')).toEqual({});
|
|
34
|
+
});
|
|
35
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Cross-origin media under COEP `require-corp`.
|
|
2
|
+
//
|
|
3
|
+
// Apps that enable cross-origin isolation (needed for SharedArrayBuffer) send
|
|
4
|
+
// `Cross-Origin-Embedder-Policy: require-corp`. Under that policy the browser
|
|
5
|
+
// BLOCKS every cross-origin subresource unless one of two things is true:
|
|
6
|
+
//
|
|
7
|
+
// 1. the response carries `Cross-Origin-Resource-Policy: cross-origin`, or
|
|
8
|
+
// 2. the element opts into CORS (`crossorigin`) AND the response carries
|
|
9
|
+
// permissive CORS headers.
|
|
10
|
+
//
|
|
11
|
+
// `blob.ugly.bot` (the R2 custom domain serving user avatars and generated
|
|
12
|
+
// media) sends `Access-Control-Allow-Origin: *` but NOT `Cross-Origin-Resource-
|
|
13
|
+
// Policy` — CORP is not expressible through R2's bucket CORS config. So a plain
|
|
14
|
+
// `<img src="https://blob.ugly.bot/…">` on a cross-origin-isolated page is
|
|
15
|
+
// blocked and renders as a broken image. In prod this silently ate user avatars
|
|
16
|
+
// on ugly.chat (reported as "Icon don't appear properly"); the request itself is
|
|
17
|
+
// a perfectly healthy 200 image/webp, which is why it never looked like a server
|
|
18
|
+
// problem.
|
|
19
|
+
//
|
|
20
|
+
// Opting into CORS satisfies condition 2 using the headers blob.ugly.bot
|
|
21
|
+
// already sends, so this needs no infrastructure change.
|
|
22
|
+
//
|
|
23
|
+
// Only applied to cross-origin URLs: adding `crossorigin` to a same-origin image
|
|
24
|
+
// is unnecessary, and on a server that does not emit CORS headers it would turn
|
|
25
|
+
// a working image into a broken one.
|
|
26
|
+
|
|
27
|
+
/** `crossOrigin`/`referrerPolicy` props for an <img>, or `{}` when same-origin. */
|
|
28
|
+
export function crossOriginProps(url: string): {
|
|
29
|
+
crossOrigin?: 'anonymous';
|
|
30
|
+
referrerPolicy?: 'no-referrer';
|
|
31
|
+
} {
|
|
32
|
+
// Relative URLs and data:/blob: are same-origin by construction.
|
|
33
|
+
if (!/^https?:\/\//i.test(url)) return {};
|
|
34
|
+
try {
|
|
35
|
+
if (typeof window === 'undefined') return {};
|
|
36
|
+
if (new URL(url).origin === window.location.origin) return {};
|
|
37
|
+
} catch {
|
|
38
|
+
return {};
|
|
39
|
+
}
|
|
40
|
+
// `no-referrer` keeps the CORS preflight-free request cacheable and avoids
|
|
41
|
+
// leaking the current page path to the media host.
|
|
42
|
+
return { crossOrigin: 'anonymous', referrerPolicy: 'no-referrer' };
|
|
43
|
+
}
|