tinacms 1.2.1 → 1.2.2

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.
@@ -1,6 +1,3 @@
1
- /**
2
-
3
- */
4
1
  import type { TinaCMS } from '@tinacms/toolkit';
5
2
  import type { TinaSchema } from '@tinacms/schema-tools';
6
3
  import type { Client } from '../internalClient';
@@ -18,6 +15,9 @@ export declare class TinaAdminApi {
18
15
  schema: TinaSchema;
19
16
  constructor(cms: TinaCMS);
20
17
  isAuthenticated(): Promise<boolean>;
18
+ checkGraphqlSchema({ localSchema }: {
19
+ localSchema: any;
20
+ }): Promise<boolean>;
21
21
  fetchCollections(): import("@tinacms/schema-tools").TinaCloudCollection<true>[];
22
22
  renameDocument({ collection, relativePath, newRelativePath }: {
23
23
  collection: any;
@@ -1,8 +1,6 @@
1
- /**
2
-
3
- */
4
1
  /// <reference types="react" />
5
- export declare const TinaAdmin: ({ preview, config, }: {
2
+ export declare const TinaAdmin: ({ preview, config, schemaJson, }: {
3
+ schemaJson?: any;
6
4
  preview?: (props: object) => JSX.Element;
7
5
  config: object;
8
6
  }) => JSX.Element;
package/dist/index.es.js CHANGED
@@ -1,16 +1,18 @@
1
+ import { z } from "zod";
1
2
  import { useCMS, Form, GlobalFormPlugin, EventBus, Modal, ModalPopup, ModalHeader, ModalBody, ModalActions, Button, LoadingDots, useLocalStorage, TinaCMS, BranchSwitcherPlugin, BranchDataProvider, TinaProvider, TinaMediaStore, DummyMediaStore, Nav, BranchBanner, LocalWarning, BillingWarning, Select, Input, ReactDateTimeWithStyles, textFieldClasses, Toggle, OverflowMenu, CursorPaginator, PopupModal, BaseTextField, wrapFieldsWithMeta, FormStatus, FormBuilder } from "@tinacms/toolkit";
2
3
  export * from "@tinacms/toolkit";
3
4
  export { MdxFieldPluginExtendible } from "@tinacms/toolkit";
4
5
  import * as G from "graphql";
5
- import { TypeInfo, visit, visitWithTypeInfo, getNamedType, GraphQLObjectType, isLeafType, GraphQLUnionType, isScalarType as isScalarType$1, getIntrospectionQuery, buildClientSchema, print, parse } from "graphql";
6
+ import { TypeInfo, visit, visitWithTypeInfo, getNamedType, GraphQLObjectType, isLeafType, GraphQLUnionType, isScalarType as isScalarType$1, getIntrospectionQuery, buildClientSchema, print, parse, buildSchema } from "graphql";
6
7
  import set from "lodash.set";
7
8
  import React, { useState, useCallback, useEffect, Fragment, useMemo } from "react";
8
9
  import { getIn, setIn } from "final-form";
9
10
  import { resolveForm, TinaSchema, addNamespaceToSchema, parseURL, validateSchema } from "@tinacms/schema-tools";
10
11
  export { NAMER, resolveForm } from "@tinacms/schema-tools";
11
- import gql$1 from "graphql-tag";
12
12
  import * as yup from "yup";
13
+ import gql$1 from "graphql-tag";
13
14
  import { setEditing, TinaDataContext, EditContext, useEditState } from "@tinacms/sharedctx";
15
+ import { diff } from "@graphql-inspector/core";
14
16
  import { NavLink, useSearchParams, useNavigate, useParams, useLocation, Link, HashRouter, Routes, Route } from "react-router-dom";
15
17
  import { Transition, Menu } from "@headlessui/react";
16
18
  import { useWindowWidth } from "@react-hook/window-size";
@@ -2114,6 +2116,38 @@ const parseRefForBranchName = (ref) => {
2114
2116
  const matches = ref.match(captureBranchName);
2115
2117
  return matches[1];
2116
2118
  };
2119
+ const ListBranchResponse = z.object({
2120
+ name: z.string(),
2121
+ protected: z.boolean(),
2122
+ commit: z.object({ sha: z.string(), url: z.string() })
2123
+ }).array();
2124
+ const IndexStatusResponse = z.object({
2125
+ status: z.union([
2126
+ z.literal("complete"),
2127
+ z.literal("unknown"),
2128
+ z.literal("failed"),
2129
+ z.literal("inprogress")
2130
+ ]).optional(),
2131
+ timestamp: z.number().optional()
2132
+ });
2133
+ async function asyncPoll(fn, pollInterval = 5 * 1e3, pollTimeout = 30 * 1e3) {
2134
+ const endTime = new Date().getTime() + pollTimeout;
2135
+ const checkCondition = (resolve, reject) => {
2136
+ Promise.resolve(fn()).then((result) => {
2137
+ const now = new Date().getTime();
2138
+ if (result.done) {
2139
+ resolve(result.data);
2140
+ } else if (now < endTime) {
2141
+ setTimeout(checkCondition, pollInterval, resolve, reject);
2142
+ } else {
2143
+ reject(new Error("AsyncPoller: reached timeout"));
2144
+ }
2145
+ }).catch((err) => {
2146
+ reject(err);
2147
+ });
2148
+ };
2149
+ return new Promise(checkCondition);
2150
+ }
2117
2151
  class Client {
2118
2152
  constructor({ tokenStorage = "MEMORY", ...options }) {
2119
2153
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P;
@@ -2431,12 +2465,64 @@ mutation addPendingDocumentMutation(
2431
2465
  return null;
2432
2466
  }
2433
2467
  }
2468
+ async waitForIndexStatus({ ref }) {
2469
+ try {
2470
+ const result = await asyncPoll(async () => {
2471
+ try {
2472
+ const result2 = await this.getIndexStatus({ ref });
2473
+ if (!(result2.status === "inprogress" || result2.status === "unknown")) {
2474
+ return Promise.resolve({
2475
+ done: true,
2476
+ data: result2
2477
+ });
2478
+ } else {
2479
+ return Promise.resolve({
2480
+ done: false
2481
+ });
2482
+ }
2483
+ } catch (err) {
2484
+ return Promise.reject(err);
2485
+ }
2486
+ }, 5e3, 9e5);
2487
+ return result;
2488
+ } catch (error) {
2489
+ if (error.message === "AsyncPoller: reached timeout") {
2490
+ console.warn(error);
2491
+ return {
2492
+ status: "timeout"
2493
+ };
2494
+ }
2495
+ throw error;
2496
+ }
2497
+ }
2498
+ async getIndexStatus({ ref }) {
2499
+ const url = `${this.contentApiBase}/db/${this.clientId}/status/${ref}`;
2500
+ const res = await this.fetchWithToken(url);
2501
+ const result = await res.json();
2502
+ const parsedResult = IndexStatusResponse.parse(result);
2503
+ return parsedResult;
2504
+ }
2434
2505
  async listBranches() {
2435
- const url = `${this.contentApiBase}/github/${this.clientId}/list_branches`;
2436
- const res = await this.fetchWithToken(url, {
2437
- method: "GET"
2438
- });
2439
- return res.json();
2506
+ try {
2507
+ const url = `${this.contentApiBase}/github/${this.clientId}/list_branches`;
2508
+ const res = await this.fetchWithToken(url, {
2509
+ method: "GET"
2510
+ });
2511
+ const branches = await res.json();
2512
+ const parsedBranches = ListBranchResponse.parse(branches);
2513
+ const indexStatusPromises = parsedBranches.map(async (branch) => {
2514
+ const indexStatus2 = await this.getIndexStatus({ ref: branch.name });
2515
+ return {
2516
+ ...branch,
2517
+ indexStatus: indexStatus2
2518
+ };
2519
+ });
2520
+ const indexStatus = await Promise.all(indexStatusPromises);
2521
+ return indexStatus;
2522
+ } catch (error) {
2523
+ console.error("There was an error listing branches.", error);
2524
+ throw error;
2525
+ }
2440
2526
  }
2441
2527
  async createBranch({ baseBranch, branchName }) {
2442
2528
  const url = `${this.contentApiBase}/github/${this.clientId}/create_branch`;
@@ -2538,6 +2624,17 @@ class TinaAdminApi {
2538
2624
  async isAuthenticated() {
2539
2625
  return await this.api.isAuthenticated();
2540
2626
  }
2627
+ async checkGraphqlSchema({ localSchema }) {
2628
+ const schemaFromCloud = await this.api.getSchema();
2629
+ const schema1 = schemaFromCloud;
2630
+ const schema2 = buildSchema(print(localSchema));
2631
+ const diffOutput = await diff(schema1, schema2);
2632
+ if (diffOutput.length > 0) {
2633
+ return false;
2634
+ } else {
2635
+ return true;
2636
+ }
2637
+ }
2541
2638
  fetchCollections() {
2542
2639
  return this.schema.getCollections();
2543
2640
  }
@@ -5376,7 +5473,7 @@ const CollectionListPage = () => {
5376
5473
  onChange: (e) => {
5377
5474
  setVars((old) => ({
5378
5475
  ...old,
5379
- after: e.format(),
5476
+ after: typeof e.format === "function" ? e.format() : "",
5380
5477
  booleanEquals: null,
5381
5478
  startsWith: ""
5382
5479
  }));
@@ -5394,7 +5491,7 @@ const CollectionListPage = () => {
5394
5491
  onChange: (e) => {
5395
5492
  setVars((old) => ({
5396
5493
  ...old,
5397
- before: e.format(),
5494
+ before: typeof e.format === "function" ? e.format() : "",
5398
5495
  booleanEquals: null,
5399
5496
  startsWith: ""
5400
5497
  }));
@@ -6052,9 +6149,30 @@ const PreviewInner = ({ preview, config }) => {
6052
6149
  ...config
6053
6150
  });
6054
6151
  };
6152
+ const CheckSchema = ({
6153
+ schemaJson,
6154
+ children
6155
+ }) => {
6156
+ const cms = useCMS();
6157
+ const api = new TinaAdminApi(cms);
6158
+ const url = api.api.contentApiUrl;
6159
+ useEffect(() => {
6160
+ if (schemaJson && cms) {
6161
+ api.checkGraphqlSchema({
6162
+ localSchema: schemaJson
6163
+ }).then((x) => {
6164
+ if (x === false) {
6165
+ cms.alerts.error("GraphQL Schema Mismatch. Editing may not work. If you just switched branches, try going back to the previous branch");
6166
+ }
6167
+ });
6168
+ }
6169
+ }, [cms, JSON.stringify(schemaJson || {}), url]);
6170
+ return children;
6171
+ };
6055
6172
  const TinaAdmin = ({
6056
6173
  preview,
6057
- config
6174
+ config,
6175
+ schemaJson
6058
6176
  }) => {
6059
6177
  const isSSR2 = typeof window === "undefined";
6060
6178
  const { edit } = useEditState();
@@ -6065,9 +6183,18 @@ const TinaAdmin = ({
6065
6183
  return /* @__PURE__ */ React.createElement(Layout, null, /* @__PURE__ */ React.createElement(LoginPage, null));
6066
6184
  }
6067
6185
  return /* @__PURE__ */ React.createElement(GetCMS, null, (cms) => {
6186
+ var _a, _b, _c;
6068
6187
  const isTinaAdminEnabled = cms.flags.get("tina-admin") === false ? false : true;
6069
6188
  if (isTinaAdminEnabled) {
6070
- return /* @__PURE__ */ React.createElement(HashRouter, null, /* @__PURE__ */ React.createElement(SetPreviewFlag, {
6189
+ const tinaClient = (_a = cms.api) == null ? void 0 : _a.tina;
6190
+ const collectionWithRouter = (_c = (_b = tinaClient == null ? void 0 : tinaClient.schema) == null ? void 0 : _b.config) == null ? void 0 : _c.collections.find((x) => {
6191
+ var _a2;
6192
+ return typeof ((_a2 = x == null ? void 0 : x.ui) == null ? void 0 : _a2.router) === "function";
6193
+ });
6194
+ const hasRouter = Boolean(collectionWithRouter);
6195
+ return /* @__PURE__ */ React.createElement(CheckSchema, {
6196
+ schemaJson
6197
+ }, /* @__PURE__ */ React.createElement(HashRouter, null, /* @__PURE__ */ React.createElement(SetPreviewFlag, {
6071
6198
  preview,
6072
6199
  cms
6073
6200
  }), /* @__PURE__ */ React.createElement(Routes, null, preview && /* @__PURE__ */ React.createElement(Route, {
@@ -6109,11 +6236,11 @@ const TinaAdmin = ({
6109
6236
  }), /* @__PURE__ */ React.createElement(Route, {
6110
6237
  path: "/",
6111
6238
  element: /* @__PURE__ */ React.createElement(MaybeRedirectToPreview, {
6112
- redirect: !!preview
6239
+ redirect: !!preview && hasRouter
6113
6240
  }, /* @__PURE__ */ React.createElement(DefaultWrapper, {
6114
6241
  cms
6115
6242
  }, /* @__PURE__ */ React.createElement(DashboardPage, null)))
6116
- })));
6243
+ }))));
6117
6244
  } else {
6118
6245
  return /* @__PURE__ */ React.createElement(Layout, null, /* @__PURE__ */ React.createElement(HashRouter, null, /* @__PURE__ */ React.createElement(Routes, null, /* @__PURE__ */ React.createElement(Route, {
6119
6246
  path: "logout",
@@ -6160,4 +6287,4 @@ const defineStaticConfig = (config) => {
6160
6287
  return config;
6161
6288
  };
6162
6289
  const defineConfig = defineStaticConfig;
6163
- export { AuthWallInner, Client, DEFAULT_LOCAL_TINA_GQL_SERVER_URL, LocalClient, RouteMappingPlugin, TinaAdmin, TinaAdminApi, TinaCMSProvider2, TinaCloudAuthWall, TinaCloudProvider, TinaDataProvider, assertShape, createClient, TinaCMSProvider2 as default, defineConfig, defineLegacyConfig, defineSchema, defineStaticConfig, getStaticPropsForTina, gql, safeAssertShape, staticRequest, useDocumentCreatorPlugin, useGraphqlForms, useTinaAuthRedirect };
6290
+ export { AuthWallInner, Client, DEFAULT_LOCAL_TINA_GQL_SERVER_URL, LocalClient, RouteMappingPlugin, TinaAdmin, TinaAdminApi, TinaCMSProvider2, TinaCloudAuthWall, TinaCloudProvider, TinaDataProvider, assertShape, asyncPoll, createClient, TinaCMSProvider2 as default, defineConfig, defineLegacyConfig, defineSchema, defineStaticConfig, getStaticPropsForTina, gql, safeAssertShape, staticRequest, useDocumentCreatorPlugin, useGraphqlForms, useTinaAuthRedirect };
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  (function(global, factory) {
2
- typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("@tinacms/toolkit"), require("graphql"), require("lodash.set"), require("react"), require("final-form"), require("@tinacms/schema-tools"), require("graphql-tag"), require("yup"), require("@tinacms/sharedctx"), require("react-router-dom"), require("@headlessui/react"), require("@react-hook/window-size")) : typeof define === "function" && define.amd ? define(["exports", "@tinacms/toolkit", "graphql", "lodash.set", "react", "final-form", "@tinacms/schema-tools", "graphql-tag", "yup", "@tinacms/sharedctx", "react-router-dom", "@headlessui/react", "@react-hook/window-size"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.tinacms = {}, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP));
3
- })(this, function(exports2, toolkit, G, set, React, finalForm, schemaTools, gql$1, yup, sharedctx, reactRouterDom, react, windowSize) {
2
+ typeof exports === "object" && typeof module !== "undefined" ? factory(exports, require("zod"), require("@tinacms/toolkit"), require("graphql"), require("lodash.set"), require("react"), require("final-form"), require("@tinacms/schema-tools"), require("yup"), require("graphql-tag"), require("@tinacms/sharedctx"), require("@graphql-inspector/core"), require("react-router-dom"), require("@headlessui/react"), require("@react-hook/window-size")) : typeof define === "function" && define.amd ? define(["exports", "zod", "@tinacms/toolkit", "graphql", "lodash.set", "react", "final-form", "@tinacms/schema-tools", "yup", "graphql-tag", "@tinacms/sharedctx", "@graphql-inspector/core", "react-router-dom", "@headlessui/react", "@react-hook/window-size"], factory) : (global = typeof globalThis !== "undefined" ? globalThis : global || self, factory(global.tinacms = {}, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP, global.NOOP));
3
+ })(this, function(exports2, zod, toolkit, G, set, React, finalForm, schemaTools, yup, gql$1, sharedctx, core, reactRouterDom, react, windowSize) {
4
4
  "use strict";
5
5
  function _interopDefaultLegacy(e) {
6
6
  return e && typeof e === "object" && "default" in e ? e : { "default": e };
@@ -28,8 +28,8 @@
28
28
  var G__namespace = /* @__PURE__ */ _interopNamespace(G);
29
29
  var set__default = /* @__PURE__ */ _interopDefaultLegacy(set);
30
30
  var React__default = /* @__PURE__ */ _interopDefaultLegacy(React);
31
- var gql__default = /* @__PURE__ */ _interopDefaultLegacy(gql$1);
32
31
  var yup__namespace = /* @__PURE__ */ _interopNamespace(yup);
32
+ var gql__default = /* @__PURE__ */ _interopDefaultLegacy(gql$1);
33
33
  function popupWindow(url, title, window2, w, h) {
34
34
  const y = window2.top.outerHeight / 2 + window2.top.screenY - h / 2;
35
35
  const x = window2.top.outerWidth / 2 + window2.top.screenX - w / 2;
@@ -2130,6 +2130,38 @@
2130
2130
  const matches = ref.match(captureBranchName);
2131
2131
  return matches[1];
2132
2132
  };
2133
+ const ListBranchResponse = zod.z.object({
2134
+ name: zod.z.string(),
2135
+ protected: zod.z.boolean(),
2136
+ commit: zod.z.object({ sha: zod.z.string(), url: zod.z.string() })
2137
+ }).array();
2138
+ const IndexStatusResponse = zod.z.object({
2139
+ status: zod.z.union([
2140
+ zod.z.literal("complete"),
2141
+ zod.z.literal("unknown"),
2142
+ zod.z.literal("failed"),
2143
+ zod.z.literal("inprogress")
2144
+ ]).optional(),
2145
+ timestamp: zod.z.number().optional()
2146
+ });
2147
+ async function asyncPoll(fn, pollInterval = 5 * 1e3, pollTimeout = 30 * 1e3) {
2148
+ const endTime = new Date().getTime() + pollTimeout;
2149
+ const checkCondition = (resolve, reject) => {
2150
+ Promise.resolve(fn()).then((result) => {
2151
+ const now = new Date().getTime();
2152
+ if (result.done) {
2153
+ resolve(result.data);
2154
+ } else if (now < endTime) {
2155
+ setTimeout(checkCondition, pollInterval, resolve, reject);
2156
+ } else {
2157
+ reject(new Error("AsyncPoller: reached timeout"));
2158
+ }
2159
+ }).catch((err) => {
2160
+ reject(err);
2161
+ });
2162
+ };
2163
+ return new Promise(checkCondition);
2164
+ }
2133
2165
  class Client {
2134
2166
  constructor({ tokenStorage = "MEMORY", ...options }) {
2135
2167
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _A, _B, _C, _D, _E, _F, _G, _H, _I, _J, _K, _L, _M, _N, _O, _P;
@@ -2447,12 +2479,64 @@ mutation addPendingDocumentMutation(
2447
2479
  return null;
2448
2480
  }
2449
2481
  }
2482
+ async waitForIndexStatus({ ref }) {
2483
+ try {
2484
+ const result = await asyncPoll(async () => {
2485
+ try {
2486
+ const result2 = await this.getIndexStatus({ ref });
2487
+ if (!(result2.status === "inprogress" || result2.status === "unknown")) {
2488
+ return Promise.resolve({
2489
+ done: true,
2490
+ data: result2
2491
+ });
2492
+ } else {
2493
+ return Promise.resolve({
2494
+ done: false
2495
+ });
2496
+ }
2497
+ } catch (err) {
2498
+ return Promise.reject(err);
2499
+ }
2500
+ }, 5e3, 9e5);
2501
+ return result;
2502
+ } catch (error) {
2503
+ if (error.message === "AsyncPoller: reached timeout") {
2504
+ console.warn(error);
2505
+ return {
2506
+ status: "timeout"
2507
+ };
2508
+ }
2509
+ throw error;
2510
+ }
2511
+ }
2512
+ async getIndexStatus({ ref }) {
2513
+ const url = `${this.contentApiBase}/db/${this.clientId}/status/${ref}`;
2514
+ const res = await this.fetchWithToken(url);
2515
+ const result = await res.json();
2516
+ const parsedResult = IndexStatusResponse.parse(result);
2517
+ return parsedResult;
2518
+ }
2450
2519
  async listBranches() {
2451
- const url = `${this.contentApiBase}/github/${this.clientId}/list_branches`;
2452
- const res = await this.fetchWithToken(url, {
2453
- method: "GET"
2454
- });
2455
- return res.json();
2520
+ try {
2521
+ const url = `${this.contentApiBase}/github/${this.clientId}/list_branches`;
2522
+ const res = await this.fetchWithToken(url, {
2523
+ method: "GET"
2524
+ });
2525
+ const branches = await res.json();
2526
+ const parsedBranches = ListBranchResponse.parse(branches);
2527
+ const indexStatusPromises = parsedBranches.map(async (branch) => {
2528
+ const indexStatus2 = await this.getIndexStatus({ ref: branch.name });
2529
+ return {
2530
+ ...branch,
2531
+ indexStatus: indexStatus2
2532
+ };
2533
+ });
2534
+ const indexStatus = await Promise.all(indexStatusPromises);
2535
+ return indexStatus;
2536
+ } catch (error) {
2537
+ console.error("There was an error listing branches.", error);
2538
+ throw error;
2539
+ }
2456
2540
  }
2457
2541
  async createBranch({ baseBranch, branchName }) {
2458
2542
  const url = `${this.contentApiBase}/github/${this.clientId}/create_branch`;
@@ -2554,6 +2638,17 @@ mutation addPendingDocumentMutation(
2554
2638
  async isAuthenticated() {
2555
2639
  return await this.api.isAuthenticated();
2556
2640
  }
2641
+ async checkGraphqlSchema({ localSchema }) {
2642
+ const schemaFromCloud = await this.api.getSchema();
2643
+ const schema1 = schemaFromCloud;
2644
+ const schema2 = G.buildSchema(G.print(localSchema));
2645
+ const diffOutput = await core.diff(schema1, schema2);
2646
+ if (diffOutput.length > 0) {
2647
+ return false;
2648
+ } else {
2649
+ return true;
2650
+ }
2651
+ }
2557
2652
  fetchCollections() {
2558
2653
  return this.schema.getCollections();
2559
2654
  }
@@ -5392,7 +5487,7 @@ This will work when developing locally but NOT when deployed to production.
5392
5487
  onChange: (e) => {
5393
5488
  setVars((old) => ({
5394
5489
  ...old,
5395
- after: e.format(),
5490
+ after: typeof e.format === "function" ? e.format() : "",
5396
5491
  booleanEquals: null,
5397
5492
  startsWith: ""
5398
5493
  }));
@@ -5410,7 +5505,7 @@ This will work when developing locally but NOT when deployed to production.
5410
5505
  onChange: (e) => {
5411
5506
  setVars((old) => ({
5412
5507
  ...old,
5413
- before: e.format(),
5508
+ before: typeof e.format === "function" ? e.format() : "",
5414
5509
  booleanEquals: null,
5415
5510
  startsWith: ""
5416
5511
  }));
@@ -6068,9 +6163,30 @@ This will work when developing locally but NOT when deployed to production.
6068
6163
  ...config
6069
6164
  });
6070
6165
  };
6166
+ const CheckSchema = ({
6167
+ schemaJson,
6168
+ children
6169
+ }) => {
6170
+ const cms = toolkit.useCMS();
6171
+ const api = new TinaAdminApi(cms);
6172
+ const url = api.api.contentApiUrl;
6173
+ React.useEffect(() => {
6174
+ if (schemaJson && cms) {
6175
+ api.checkGraphqlSchema({
6176
+ localSchema: schemaJson
6177
+ }).then((x) => {
6178
+ if (x === false) {
6179
+ cms.alerts.error("GraphQL Schema Mismatch. Editing may not work. If you just switched branches, try going back to the previous branch");
6180
+ }
6181
+ });
6182
+ }
6183
+ }, [cms, JSON.stringify(schemaJson || {}), url]);
6184
+ return children;
6185
+ };
6071
6186
  const TinaAdmin = ({
6072
6187
  preview,
6073
- config
6188
+ config,
6189
+ schemaJson
6074
6190
  }) => {
6075
6191
  const isSSR2 = typeof window === "undefined";
6076
6192
  const { edit } = sharedctx.useEditState();
@@ -6081,9 +6197,18 @@ This will work when developing locally but NOT when deployed to production.
6081
6197
  return /* @__PURE__ */ React__default["default"].createElement(Layout, null, /* @__PURE__ */ React__default["default"].createElement(LoginPage, null));
6082
6198
  }
6083
6199
  return /* @__PURE__ */ React__default["default"].createElement(GetCMS, null, (cms) => {
6200
+ var _a, _b, _c;
6084
6201
  const isTinaAdminEnabled = cms.flags.get("tina-admin") === false ? false : true;
6085
6202
  if (isTinaAdminEnabled) {
6086
- return /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.HashRouter, null, /* @__PURE__ */ React__default["default"].createElement(SetPreviewFlag, {
6203
+ const tinaClient = (_a = cms.api) == null ? void 0 : _a.tina;
6204
+ const collectionWithRouter = (_c = (_b = tinaClient == null ? void 0 : tinaClient.schema) == null ? void 0 : _b.config) == null ? void 0 : _c.collections.find((x) => {
6205
+ var _a2;
6206
+ return typeof ((_a2 = x == null ? void 0 : x.ui) == null ? void 0 : _a2.router) === "function";
6207
+ });
6208
+ const hasRouter = Boolean(collectionWithRouter);
6209
+ return /* @__PURE__ */ React__default["default"].createElement(CheckSchema, {
6210
+ schemaJson
6211
+ }, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.HashRouter, null, /* @__PURE__ */ React__default["default"].createElement(SetPreviewFlag, {
6087
6212
  preview,
6088
6213
  cms
6089
6214
  }), /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Routes, null, preview && /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Route, {
@@ -6125,11 +6250,11 @@ This will work when developing locally but NOT when deployed to production.
6125
6250
  }), /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Route, {
6126
6251
  path: "/",
6127
6252
  element: /* @__PURE__ */ React__default["default"].createElement(MaybeRedirectToPreview, {
6128
- redirect: !!preview
6253
+ redirect: !!preview && hasRouter
6129
6254
  }, /* @__PURE__ */ React__default["default"].createElement(DefaultWrapper, {
6130
6255
  cms
6131
6256
  }, /* @__PURE__ */ React__default["default"].createElement(DashboardPage, null)))
6132
- })));
6257
+ }))));
6133
6258
  } else {
6134
6259
  return /* @__PURE__ */ React__default["default"].createElement(Layout, null, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.HashRouter, null, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Routes, null, /* @__PURE__ */ React__default["default"].createElement(reactRouterDom.Route, {
6135
6260
  path: "logout",
@@ -6206,6 +6331,7 @@ This will work when developing locally but NOT when deployed to production.
6206
6331
  exports2.TinaCloudProvider = TinaCloudProvider;
6207
6332
  exports2.TinaDataProvider = TinaDataProvider;
6208
6333
  exports2.assertShape = assertShape;
6334
+ exports2.asyncPoll = asyncPoll;
6209
6335
  exports2.createClient = createClient;
6210
6336
  exports2["default"] = TinaCMSProvider2;
6211
6337
  exports2.defineConfig = defineConfig;
@@ -1,6 +1,3 @@
1
- /**
2
-
3
- */
4
1
  import { TokenObject } from '../auth/authenticate';
5
2
  import { BranchData, EventBus } from '@tinacms/toolkit';
6
3
  import { DocumentNode, GraphQLSchema } from 'graphql';
@@ -24,6 +21,90 @@ interface ServerOptions {
24
21
  tinaioConfig?: TinaIOConfig;
25
22
  tokenStorage?: 'MEMORY' | 'LOCAL_STORAGE' | 'CUSTOM';
26
23
  }
24
+ /**
25
+ * The function you pass to `asyncPoll` should return a promise
26
+ * that resolves with object that satisfies this interface.
27
+ *
28
+ * The `done` property indicates to the async poller whether to
29
+ * continue polling or not.
30
+ *
31
+ * When done is `true` that means you've got what you need
32
+ * and the poller will resolve with `data`.
33
+ *
34
+ * When done is `false` taht means you don't have what you need
35
+ * and the poller will continue polling.
36
+ */
37
+ export interface AsyncData<T> {
38
+ done: boolean;
39
+ data?: T;
40
+ }
41
+ /**
42
+ * Your custom function you provide to the async poller should
43
+ * satisfy this interface. Your function returns a promise that
44
+ * resolves with `AsyncData` to indicate to the poller whether
45
+ * you have what you need or we should continue polling.
46
+ */
47
+ export interface AsyncFunction<T> extends Function {
48
+ (): PromiseLike<AsyncData<T>>;
49
+ }
50
+ /**
51
+ * How to repeatedly call an async function until get a desired result.
52
+ *
53
+ * Inspired by the following gist:
54
+ * https://gist.github.com/twmbx/2321921670c7e95f6fad164fbdf3170e#gistcomment-3053587
55
+ * https://davidwalsh.name/javascript-polling
56
+ *
57
+ * Usage:
58
+ asyncPoll(
59
+ async (): Promise<AsyncData<any>> => {
60
+ try {
61
+ const result = await getYourAsyncResult();
62
+ if (result.isWhatYouWant) {
63
+ return Promise.resolve({
64
+ done: true,
65
+ data: result,
66
+ });
67
+ } else {
68
+ return Promise.resolve({
69
+ done: false
70
+ });
71
+ }
72
+ } catch (err) {
73
+ return Promise.reject(err);
74
+ }
75
+ },
76
+ 500, // interval
77
+ 15000, // timeout
78
+ );
79
+ */
80
+ export declare function asyncPoll<T>(
81
+ /**
82
+ * Function to call periodically until it resolves or rejects.
83
+ *
84
+ * It should resolve as soon as possible indicating if it found
85
+ * what it was looking for or not. If not then it will be reinvoked
86
+ * after the `pollInterval` if we haven't timed out.
87
+ *
88
+ * Rejections will stop the polling and be propagated.
89
+ */
90
+ fn: AsyncFunction<T>,
91
+ /**
92
+ * Milliseconds to wait before attempting to resolve the promise again.
93
+ * The promise won't be called concurrently. This is the wait period
94
+ * after the promise has resolved/rejected before trying again for a
95
+ * successful resolve so long as we haven't timed out.
96
+ *
97
+ * Default 5 seconds.
98
+ */
99
+ pollInterval?: number,
100
+ /**
101
+ * Max time to keep polling to receive a successful resolved response.
102
+ * If the promise never resolves before the timeout then this method
103
+ * rejects with a timeout error.
104
+ *
105
+ * Default 30 seconds.
106
+ */
107
+ pollTimeout?: number): Promise<T>;
27
108
  export declare class Client {
28
109
  onLogin?: OnLoginFunc;
29
110
  onLogout?: () => Promise<void>;
@@ -121,7 +202,27 @@ export declare class Client {
121
202
  delinquencyDate: number;
122
203
  billingState: 'current' | 'late' | 'delinquent';
123
204
  }>;
124
- listBranches(): Promise<any>;
205
+ waitForIndexStatus({ ref }: {
206
+ ref: string;
207
+ }): Promise<any>;
208
+ getIndexStatus({ ref }: {
209
+ ref: string;
210
+ }): Promise<{
211
+ status?: "unknown" | "complete" | "failed" | "inprogress";
212
+ timestamp?: number;
213
+ }>;
214
+ listBranches(): Promise<{
215
+ indexStatus: {
216
+ status?: "unknown" | "complete" | "failed" | "inprogress";
217
+ timestamp?: number;
218
+ };
219
+ name?: string;
220
+ protected?: boolean;
221
+ commit?: {
222
+ url?: string;
223
+ sha?: string;
224
+ };
225
+ }[]>;
125
226
  createBranch({ baseBranch, branchName }: BranchData): Promise<string>;
126
227
  }
127
228
  export declare const DEFAULT_LOCAL_TINA_GQL_SERVER_URL = "http://localhost:4001/graphql";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tinacms",
3
- "version": "1.2.1",
3
+ "version": "1.2.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "./dist/index.es.js",
6
6
  "exports": {
@@ -51,13 +51,14 @@
51
51
  "typings": "dist/index.d.ts",
52
52
  "license": "Apache-2.0",
53
53
  "dependencies": {
54
+ "@graphql-inspector/core": "^4.0.0",
54
55
  "@graphql-tools/relay-operation-optimizer": "^6.4.1",
55
56
  "@headlessui/react": "^1.5.0",
56
57
  "@heroicons/react": "^1.0.4",
57
58
  "@react-hook/window-size": "^3.0.7",
58
- "@tinacms/schema-tools": "1.3.1",
59
+ "@tinacms/schema-tools": "1.3.2",
59
60
  "@tinacms/sharedctx": "1.0.1",
60
- "@tinacms/toolkit": "1.3.3",
61
+ "@tinacms/toolkit": "1.3.4",
61
62
  "crypto-js": "^4.0.0",
62
63
  "encoding": "0.1.13",
63
64
  "fetch-ponyfill": "^7.1.0",
@@ -68,7 +69,8 @@
68
69
  "prism-react-renderer": "^1.3.5",
69
70
  "react-icons": "^4.3.1",
70
71
  "react-router-dom": "6",
71
- "yup": "^0.32.0"
72
+ "yup": "^0.32.0",
73
+ "zod": "^3.14.3"
72
74
  },
73
75
  "devDependencies": {
74
76
  "@graphql-tools/utils": "^8.6.1",