@tinacms/app 2.5.12 → 2.5.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tinacms/app",
3
- "version": "2.5.12",
3
+ "version": "2.5.14",
4
4
  "main": "src/main.tsx",
5
5
  "files": [
6
6
  "src",
@@ -28,7 +28,7 @@
28
28
  "@graphiql/toolkit": "^0.11.3",
29
29
  "@monaco-editor/loader": "^1.7.0",
30
30
  "@radix-ui/react-popover": "^1.1.18",
31
- "final-form": "^5.0.1",
31
+ "final-form": "4.20.10",
32
32
  "graphiql": "^4.1.2",
33
33
  "graphql": "15.8.0",
34
34
  "lucide-react": "^0.424.0",
@@ -36,8 +36,8 @@
36
36
  "react-dom": "^18.3.1",
37
37
  "react-router-dom": "^6.30.3",
38
38
  "zod": "^3.24.2",
39
- "@tinacms/mdx": "^2.2.1",
40
- "tinacms": "^3.12.1"
39
+ "@tinacms/mdx": "^2.2.3",
40
+ "tinacms": "^3.14.0"
41
41
  },
42
42
  "repository": {
43
43
  "url": "https://github.com/tinacms/tinacms.git",
@@ -16,6 +16,7 @@ import {
16
16
  TinaField,
17
17
  TinaSchema,
18
18
  TinaState,
19
+ isSessionExpiredError,
19
20
  resolveField,
20
21
  useCMS,
21
22
  } from 'tinacms';
@@ -24,7 +25,7 @@ import { FormifyCallback, createForm, createGlobalForm } from './build-form';
24
25
  import { showErrorModal } from './errors';
25
26
  import { expandQuery, isConnectionType, isNodeType } from './expand-query';
26
27
  import {
27
- getExpectedPreviewOrigin,
28
+ getPreviewOrigin,
28
29
  isFromTrustedPreviewOrigin,
29
30
  postMessageToPreview,
30
31
  } from './preview-origin';
@@ -204,12 +205,9 @@ export const useGraphQLReducer = (
204
205
 
205
206
  const activeField = searchParams.get('active-field');
206
207
 
207
- // Origin of the preview document we load in the iframe. Used to validate
208
+ // The preview is always loaded from the admin's own origin. Used to validate
208
209
  // inbound messages and as the explicit `targetOrigin` for outbound ones.
209
- const expectedOrigin = React.useMemo(
210
- () => getExpectedPreviewOrigin(url),
211
- [url]
212
- );
210
+ const expectedOrigin = getPreviewOrigin();
213
211
 
214
212
  React.useEffect(() => {
215
213
  const run = async () => {
@@ -722,6 +720,8 @@ const onSubmit = async (
722
720
  });
723
721
  cms.alerts.success('Document saved!');
724
722
  } catch (e) {
723
+ // request() already sent the user back to the login modal; no alert over it
724
+ if (isSessionExpiredError(e)) throw e;
725
725
  cms.alerts.error(() =>
726
726
  ErrorDialog({
727
727
  title: 'There was a problem saving your document',
@@ -1,11 +1,10 @@
1
1
  import { describe, expect, it, vi } from 'vitest';
2
2
  import {
3
- getExpectedPreviewOrigin,
3
+ getPreviewOrigin,
4
4
  isFromTrustedPreviewOrigin,
5
5
  postMessageToPreview,
6
6
  } from './preview-origin';
7
7
 
8
- const ADMIN = 'https://admin.example';
9
8
  const PREVIEW = 'https://preview.example';
10
9
 
11
10
  // A stand-in for the iframe's content window; we only need an identity to
@@ -15,15 +14,23 @@ const makePeerWindow = () =>
15
14
  postMessage: ReturnType<typeof vi.fn>;
16
15
  };
17
16
 
18
- describe('getExpectedPreviewOrigin', () => {
19
- it('resolves a relative admin URL to the admin origin', () => {
20
- expect(getExpectedPreviewOrigin('/posts/hello', ADMIN)).toBe(ADMIN);
17
+ describe('getPreviewOrigin', () => {
18
+ it('is the origin the admin itself is served from', () => {
19
+ expect(getPreviewOrigin()).toBe(window.location.origin);
21
20
  });
22
21
 
23
- it('keeps the origin of an absolute preview URL', () => {
24
- expect(getExpectedPreviewOrigin(`${PREVIEW}/posts/hello`, ADMIN)).toBe(
25
- PREVIEW
26
- );
22
+ /**
23
+ * The origin used to be resolved from the preview URL, so a URL naming
24
+ * another origin moved the trust anchor to it. The preview is always loaded
25
+ * from the admin's own origin, so nothing it carries is consulted here.
26
+ */
27
+ it('takes no argument that could name another origin', () => {
28
+ expect(getPreviewOrigin.length).toBe(0);
29
+ expect(
30
+ (getPreviewOrigin as (...args: unknown[]) => string)(
31
+ `${PREVIEW}/posts/hello`
32
+ )
33
+ ).toBe(window.location.origin);
27
34
  });
28
35
  });
29
36
 
@@ -7,23 +7,15 @@
7
7
  */
8
8
 
9
9
  /**
10
- * Returns the origin expected for the preview iframe.
10
+ * Returns the origin the preview iframe is allowed to be on.
11
11
  *
12
- * Relative URLs resolve against `baseOrigin`; absolute URLs keep their own
13
- * origin.
12
+ * The admin only ever loads the preview from its own origin, so this is that
13
+ * origin and nothing else. It is deliberately not derived from the preview URL:
14
+ * reading the trust anchor out of a value the URL supplies is what allowed a
15
+ * crafted link to nominate the origin it would then be trusted from.
14
16
  */
15
- export const getExpectedPreviewOrigin = (
16
- url: string,
17
- baseOrigin: string = typeof window !== 'undefined'
18
- ? window.location.origin
19
- : ''
20
- ): string => {
21
- try {
22
- return new URL(url, baseOrigin || undefined).origin;
23
- } catch {
24
- return baseOrigin;
25
- }
26
- };
17
+ export const getPreviewOrigin = (): string =>
18
+ typeof window !== 'undefined' ? window.location.origin : '';
27
19
 
28
20
  /**
29
21
  * Checks whether a MessageEvent came from the trusted preview iframe.