@tinacms/app 2.5.13 → 2.5.15

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.13",
3
+ "version": "2.5.15",
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.2",
40
- "tinacms": "^3.13.0"
39
+ "tinacms": "^3.14.4",
40
+ "@tinacms/mdx": "^2.3.0"
41
41
  },
42
42
  "repository": {
43
43
  "url": "https://github.com/tinacms/tinacms.git",
package/src/global.css CHANGED
@@ -45,8 +45,8 @@
45
45
  --tina-font-weight-regular: 400;
46
46
  --tina-font-weight-bold: 600;
47
47
 
48
- --tina-shadow-big: 0px 2px 3px rgba(0, 0, 0, 0.05), 0 4px 12px
49
- rgba(0, 0, 0, 0.1);
48
+ --tina-shadow-big:
49
+ 0px 2px 3px rgba(0, 0, 0, 0.05), 0 4px 12px rgba(0, 0, 0, 0.1);
50
50
  --tina-shadow-small: 0px 2px 3px rgba(0, 0, 0, 0.12);
51
51
 
52
52
  --tina-timing-short: 85ms;
package/src/index.css CHANGED
@@ -28,16 +28,16 @@
28
28
  body {
29
29
  margin: 0;
30
30
  padding: 0;
31
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
32
- "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
33
- sans-serif;
31
+ font-family:
32
+ -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
33
+ "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
34
34
  -webkit-font-smoothing: antialiased;
35
35
  -moz-osx-font-smoothing: grayscale;
36
36
  }
37
37
 
38
38
  code {
39
- font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
40
- monospace;
39
+ font-family:
40
+ source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
41
41
  }
42
42
 
43
43
  div.graphiql-explorer-root > div:last-child {
@@ -18,14 +18,14 @@ import {
18
18
  TinaState,
19
19
  isSessionExpiredError,
20
20
  resolveField,
21
+ showErrorModal,
21
22
  useCMS,
22
23
  } from 'tinacms';
23
24
  import { z } from 'zod';
24
25
  import { FormifyCallback, createForm, createGlobalForm } from './build-form';
25
- import { showErrorModal } from './errors';
26
26
  import { expandQuery, isConnectionType, isNodeType } from './expand-query';
27
27
  import {
28
- getExpectedPreviewOrigin,
28
+ getPreviewOrigin,
29
29
  isFromTrustedPreviewOrigin,
30
30
  postMessageToPreview,
31
31
  } from './preview-origin';
@@ -205,12 +205,9 @@ export const useGraphQLReducer = (
205
205
 
206
206
  const activeField = searchParams.get('active-field');
207
207
 
208
- // 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
209
209
  // inbound messages and as the explicit `targetOrigin` for outbound ones.
210
- const expectedOrigin = React.useMemo(
211
- () => getExpectedPreviewOrigin(url),
212
- [url]
213
- );
210
+ const expectedOrigin = getPreviewOrigin();
214
211
 
215
212
  React.useEffect(() => {
216
213
  const run = async () => {
@@ -725,12 +722,13 @@ const onSubmit = async (
725
722
  } catch (e) {
726
723
  // request() already sent the user back to the login modal; no alert over it
727
724
  if (isSessionExpiredError(e)) throw e;
728
- cms.alerts.error(() =>
729
- ErrorDialog({
730
- title: 'There was a problem saving your document',
731
- message: 'Tina caught an error while updating the page',
732
- error: e,
733
- })
725
+ cms.alerts.error(
726
+ () =>
727
+ ErrorDialog({
728
+ message: 'Tina caught an error while updating the page',
729
+ error: e,
730
+ }),
731
+ { title: 'Save Failed' }
734
732
  );
735
733
  console.error(e);
736
734
  }
@@ -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.
@@ -1,27 +0,0 @@
1
- import React from 'react';
2
- import { TinaCMS } from 'tinacms';
3
-
4
- const TROUBLESHOOTING_URL = 'https://tina.io/docs/tinacloud/troubleshooting';
5
-
6
- const ErrorModalContent = (props: { title: string; message: string }) => {
7
- const { title, message } = props;
8
- return (
9
- <>
10
- <div>{title}</div>
11
- <p>{message}</p>
12
- <a href={TROUBLESHOOTING_URL} target='_blank' rel='noopener noreferrer'>
13
- Try these troubleshooting tips
14
- </a>
15
- </>
16
- );
17
- };
18
-
19
- export const showErrorModal = (
20
- title: string,
21
- message: string,
22
- cms: TinaCMS
23
- ) => {
24
- if (cms.alerts.all.some((a: { level: string }) => a.level === 'error'))
25
- return;
26
- cms.alerts.error(() => <ErrorModalContent title={title} message={message} />);
27
- };