tinacms 3.8.4 → 3.9.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.
@@ -2,16 +2,20 @@ import { BranchData, EventBus } from '@tinacms/toolkit';
2
2
  import { DocumentNode, GraphQLSchema } from 'graphql';
3
3
  import { TokenObject } from '../auth/authenticate';
4
4
  import { AuthProvider, Schema, TinaSchema } from '@tinacms/schema-tools';
5
- import { SearchClient, SearchOptions, SearchQueryResponse, IndexableDocument, FuzzySearchOptions } from '@tinacms/search/index-client';
5
+ import { FuzzySearchOptions, IndexableDocument, SearchClient, SearchOptions, SearchQueryResponse } from '@tinacms/search/index-client';
6
+ import gql from 'graphql-tag';
7
+ import { EditorialWorkflowResult } from '../toolkit/form-builder/editorial-workflow-constants';
8
+ import { TinaCloudProject } from './types';
9
+ export * from './authProvider';
6
10
  interface TinaSearchConfig {
7
11
  stopwordLanguages?: string[];
8
12
  fuzzyEnabled?: boolean;
9
13
  fuzzyOptions?: FuzzySearchOptions;
10
14
  }
11
- import gql from 'graphql-tag';
12
- import { EditorialWorkflowResult } from '../toolkit/form-builder/editorial-workflow-constants';
13
- import { TinaCloudProject } from './types';
14
- export * from './authProvider';
15
+ type EditorialWorkflowStatusUpdate = {
16
+ status: string;
17
+ message?: string;
18
+ };
15
19
  export type OnLoginFunc = (args: {
16
20
  token?: TokenObject;
17
21
  }) => Promise<void>;
@@ -124,11 +128,12 @@ export declare class Client {
124
128
  getIndexStatus({ ref }: {
125
129
  ref: string;
126
130
  }): Promise<{
127
- status?: "unknown" | "complete" | "failed" | "inprogress";
131
+ status?: "complete" | "unknown" | "failed" | "inprogress";
128
132
  timestamp?: number;
129
133
  }>;
130
134
  listBranches(args?: {
131
135
  includeIndexStatus?: boolean;
136
+ signal?: AbortSignal;
132
137
  }): Promise<[{
133
138
  name?: string;
134
139
  protected?: boolean;
@@ -139,7 +144,7 @@ export declare class Client {
139
144
  githubPullRequestUrl?: string;
140
145
  }[]] | {
141
146
  indexStatus: {
142
- status?: "unknown" | "complete" | "failed" | "inprogress";
147
+ status?: "complete" | "unknown" | "failed" | "inprogress";
143
148
  timestamp?: number;
144
149
  };
145
150
  name?: string;
@@ -147,9 +152,14 @@ export declare class Client {
147
152
  githubPullRequestUrl?: string;
148
153
  }[]>;
149
154
  usingProtectedBranch(): boolean;
150
- branchExists(branchName: string): Promise<boolean>;
155
+ branchExists(branchName: string, args?: {
156
+ signal?: AbortSignal;
157
+ }): Promise<boolean>;
151
158
  createBranch({ baseBranch, branchName }: BranchData): Promise<string>;
152
159
  getLatestVersion(): Promise<LatestVersionResponse>;
160
+ private pollEditorialWorkflowStatus;
161
+ private toEditorialWorkflowError;
162
+ private postEditorialWorkflow;
153
163
  /**
154
164
  * Initiate and poll for the results of an editorial workflow operation
155
165
  *
@@ -169,6 +179,18 @@ export declare class Client {
169
179
  message?: string;
170
180
  }) => void;
171
181
  }): Promise<EditorialWorkflowResult>;
182
+ startMediaEditorialWorkflow(options: {
183
+ branchName: string;
184
+ baseBranch: string;
185
+ prTitle?: string;
186
+ operation: 'upload' | 'delete';
187
+ repoPath: string;
188
+ }): Promise<{
189
+ branchName: string;
190
+ requestId: string;
191
+ status?: string;
192
+ }>;
193
+ waitForEditorialWorkflowStatus(requestId: string, onStatusUpdate?: (status: EditorialWorkflowStatusUpdate) => void): Promise<EditorialWorkflowResult>;
172
194
  }
173
195
  export declare const DEFAULT_LOCAL_TINA_GQL_SERVER_URL = "http://localhost:4001/graphql";
174
196
  export declare class LocalClient extends Client {
@@ -0,0 +1,2 @@
1
+ import * as React from 'react';
2
+ export declare const MediaWorkflowOverlay: () => React.JSX.Element;
@@ -6,12 +6,26 @@ export interface CMSEvent {
6
6
  export declare class EventBus {
7
7
  private listeners;
8
8
  subscribe<E extends CMSEvent = CMSEvent>(event: E['type'] | E['type'][], callback: Callback<E>): () => void;
9
- dispatch<E extends CMSEvent = CMSEvent>(event: E): void;
9
+ dispatch<E extends CMSEvent = CMSEvent>(event: E): boolean;
10
+ /**
11
+ * Whether any listener *explicitly* targets `eventType`. The catch-all `'*'`
12
+ * pattern does not count — callers use this to detect a purpose-built
13
+ * subscriber (e.g. a mounted UI overlay) without being misled by ambient
14
+ * `'*'` listeners such as the alerts bridge, which would otherwise make
15
+ * {@link dispatch} report every event as "handled".
16
+ */
17
+ hasExplicitListenerFor<E extends CMSEvent = CMSEvent>(eventType: E['type']): boolean;
10
18
  }
11
19
  export declare class Listener<E extends CMSEvent = CMSEvent> {
12
20
  private eventPattern;
13
21
  private callback;
14
22
  constructor(eventPattern: E['type'], callback: Callback<E>);
15
23
  handleEvent(event: E): boolean;
24
+ /**
25
+ * Whether this listener explicitly targets `eventType`. Unlike
26
+ * {@link watchesEvent}, the catch-all `'*'` pattern does NOT match, so this
27
+ * reflects a purpose-built subscription rather than an ambient one.
28
+ */
29
+ isExplicitListenerFor(eventType: E['type']): boolean;
16
30
  watchesEvent(currentEvent: E): boolean;
17
31
  }
@@ -1,4 +1,4 @@
1
- import { CMS } from './cms';
1
+ import type { TinaCMS } from '../tina-cms';
2
2
  import { Media, MediaList, MediaListOptions, MediaStore, MediaUploadOptions } from './media';
3
3
  export declare class DummyMediaStore implements MediaStore {
4
4
  accept: string;
@@ -30,14 +30,16 @@ export declare class TinaMediaStore implements MediaStore {
30
30
  private url;
31
31
  private staticMedia;
32
32
  isStatic?: boolean;
33
- constructor(cms: CMS, staticMedia?: StaticMedia);
33
+ private workflowBranchOverride;
34
+ private mediaWorkflowInProgress;
35
+ constructor(cms: TinaCMS, staticMedia?: StaticMedia);
34
36
  setup(): void;
35
37
  isAuthenticated(): Promise<boolean>;
36
38
  accept: string;
37
39
  maxSize: number;
38
40
  /**
39
- * Returns the current branch as a single-encoded query-param value, or
40
- * an empty string when no branch is set.
41
+ * Returns the workflow branch override or current branch as a single-encoded
42
+ * query-param value, or an empty string when no branch is set.
41
43
  *
42
44
  * `this.api.branch` is already URL-encoded by `Client.setBranch()`, so we
43
45
  * decode then re-encode here to defend against double-encoding when this
@@ -50,7 +52,32 @@ export declare class TinaMediaStore implements MediaStore {
50
52
  * assets-api (which would route the call to a non-existent staging path).
51
53
  */
52
54
  private encodedBranchParam;
55
+ private shortStableHash;
56
+ private trimEdges;
57
+ private branchSlugForMediaPath;
58
+ /** Joins a directory and filename into a normalized `dir/file` repo path. */
59
+ private joinMediaPath;
60
+ private branchQueryParam;
61
+ private requestMediaBranchChoice;
62
+ private prepareMediaBranch;
63
+ private resetWorkflowState;
64
+ private finalizeMediaWorkflow;
65
+ private dispatchMediaWorkflowError;
66
+ private runMediaOpWithWorkflow;
67
+ private prepareProtectedMediaBranch;
68
+ private waitForRequestStatus;
53
69
  private persist_cloud;
70
+ /**
71
+ * Uploads assets through the editorial workflow. The asset is staged, the
72
+ * server-side workflow catalogues it in the new branch's media index, and
73
+ * only then do we list it — while the branch override still routes list
74
+ * calls to the workflow branch, so the freshly uploaded item is returned to
75
+ * the caller (and added to the media manager) without needing a manual
76
+ * refresh.
77
+ */
78
+ private persistCloudViaWorkflow;
79
+ private uploadCloudMediaItem;
80
+ private groupMediaByDirectory;
54
81
  /**
55
82
  * Resolves the just-uploaded items to canonical `Media` entries by hitting
56
83
  * the assets-api `list` endpoint, which is the source of truth for the
@@ -6,7 +6,7 @@ export declare const Command: React.ForwardRefExoticComponent<Omit<Omit<{
6
6
  ref?: React.Ref<HTMLDivElement>;
7
7
  } & {
8
8
  asChild?: boolean;
9
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
9
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
10
10
  label?: string;
11
11
  shouldFilter?: boolean;
12
12
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -22,7 +22,7 @@ export declare const Command: React.ForwardRefExoticComponent<Omit<Omit<{
22
22
  ref?: React.Ref<HTMLDivElement>;
23
23
  } & {
24
24
  asChild?: boolean;
25
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
25
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
26
26
  label?: string;
27
27
  shouldFilter?: boolean;
28
28
  filter?: (value: string, search: string, keywords?: string[]) => number;
@@ -58,7 +58,7 @@ export declare const InputCommand: React.ForwardRefExoticComponent<Omit<Omit<Omi
58
58
  }, "key" | keyof React.InputHTMLAttributes<HTMLInputElement> | "asChild">, "value" | "type" | "onChange"> & {
59
59
  value?: string;
60
60
  onValueChange?: (search: string) => void;
61
- } & React.RefAttributes<HTMLInputElement>, "h" | "variant"> & import("class-variance-authority").VariantProps<(props?: {
61
+ } & React.RefAttributes<HTMLInputElement>, "variant" | "h"> & import("class-variance-authority").VariantProps<(props?: {
62
62
  h?: "sm" | "md";
63
63
  variant?: "default" | "ghost";
64
64
  } & import("class-variance-authority/types").ClassProp) => string>, "ref"> & React.RefAttributes<HTMLInputElement>>;
@@ -68,7 +68,7 @@ export declare const CommandList: React.ForwardRefExoticComponent<Omit<{
68
68
  ref?: React.Ref<HTMLDivElement>;
69
69
  } & {
70
70
  asChild?: boolean;
71
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
71
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
72
72
  label?: string;
73
73
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
74
74
  export declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
@@ -77,14 +77,14 @@ export declare const CommandEmpty: React.ForwardRefExoticComponent<Omit<{
77
77
  ref?: React.Ref<HTMLDivElement>;
78
78
  } & {
79
79
  asChild?: boolean;
80
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
80
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
81
81
  export declare const CommandGroup: React.ForwardRefExoticComponent<Omit<{
82
82
  children?: React.ReactNode;
83
83
  } & Omit<Pick<Pick<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "key" | keyof React.HTMLAttributes<HTMLDivElement>> & {
84
84
  ref?: React.Ref<HTMLDivElement>;
85
85
  } & {
86
86
  asChild?: boolean;
87
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "value" | "heading"> & {
87
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>>, "value" | "heading"> & {
88
88
  heading?: React.ReactNode;
89
89
  value?: string;
90
90
  forceMount?: boolean;
@@ -93,7 +93,7 @@ export declare const CommandSeparator: React.ForwardRefExoticComponent<Omit<Pick
93
93
  ref?: React.Ref<HTMLDivElement>;
94
94
  } & {
95
95
  asChild?: boolean;
96
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild"> & {
96
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>> & {
97
97
  alwaysRender?: boolean;
98
98
  } & React.RefAttributes<HTMLDivElement>, "ref"> & React.RefAttributes<HTMLDivElement>>;
99
99
  export declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
@@ -102,7 +102,7 @@ export declare const CommandItem: React.ForwardRefExoticComponent<Omit<{
102
102
  ref?: React.Ref<HTMLDivElement>;
103
103
  } & {
104
104
  asChild?: boolean;
105
- }, "key" | keyof React.HTMLAttributes<HTMLDivElement> | "asChild">, "value" | "disabled" | "onSelect"> & {
105
+ }, "key" | "asChild" | keyof React.HTMLAttributes<HTMLDivElement>>, "value" | "disabled" | "onSelect"> & {
106
106
  disabled?: boolean;
107
107
  onSelect?: (value: string) => void;
108
108
  value?: string;
@@ -2,7 +2,7 @@ export declare const inputVariants: (props?: {
2
2
  h?: "sm" | "md";
3
3
  variant?: "default" | "ghost";
4
4
  } & import("class-variance-authority/types").ClassProp) => string;
5
- export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "h" | "variant"> & import("class-variance-authority").VariantProps<(props?: {
5
+ export declare const Input: import("react").ForwardRefExoticComponent<Omit<Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "ref"> & Omit<import("react").DetailedHTMLProps<import("react").InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>, "variant" | "h"> & import("class-variance-authority").VariantProps<(props?: {
6
6
  h?: "sm" | "md";
7
7
  variant?: "default" | "ghost";
8
8
  } & import("class-variance-authority/types").ClassProp) => string>, "ref"> & import("react").RefAttributes<HTMLInputElement>>;
@@ -9,6 +9,15 @@ export declare const CreateBranchModal: ({ close, safeSubmit, path, values, crud
9
9
  tinaForm?: Form;
10
10
  onBaseBranchDeleted?: () => void;
11
11
  }) => React.JSX.Element;
12
+ export declare const CreateBranchPromptModal: ({ branchName, close, disabled, errorMessage, onBranchNameChange, onCreateBranch, onSaveToProtectedBranch, }: {
13
+ branchName: string;
14
+ close: () => void;
15
+ disabled?: boolean;
16
+ errorMessage?: string;
17
+ onBranchNameChange: (value: string) => void;
18
+ onCreateBranch: () => void;
19
+ onSaveToProtectedBranch: () => void;
20
+ }) => React.JSX.Element;
12
21
  export declare const PrefixedTextField: ({ label, prefix, ...props }: {
13
22
  [x: string]: any;
14
23
  label?: any;
@@ -0,0 +1,15 @@
1
+ import * as React from 'react';
2
+ interface EditorialWorkflowProgressModalProps {
3
+ title: string;
4
+ currentStep: number;
5
+ elapsedTime: number;
6
+ }
7
+ /**
8
+ * Shared modal shell for any "we're creating a branch + PR right now"
9
+ * state. Both the content-save flow (`CreateBranchModal`) and the
10
+ * media-store flow (`MediaWorkflowOverlay`) render this while the
11
+ * workflow is in flight so the editor sees the same indicator regardless
12
+ * of what triggered it.
13
+ */
14
+ export declare const EditorialWorkflowProgressModal: ({ title, currentStep, elapsedTime, }: EditorialWorkflowProgressModalProps) => React.JSX.Element;
15
+ export {};
@@ -0,0 +1,20 @@
1
+ export interface MediaWorkflowConfirmBranchEvent {
2
+ type: 'media:workflow:confirm-branch';
3
+ branchName: string;
4
+ baseBranch: string;
5
+ onConfirm: (branchName: string) => Promise<void>;
6
+ onCancel: () => void;
7
+ onSaveToProtectedBranch: () => void;
8
+ }
9
+ export declare const getEditorialWorkflowPrTitle: (branchName: string) => string;
10
+ export declare const TARGET_BRANCH_EXISTS_ERROR = "A branch with this name already exists";
11
+ export declare const checkBaseBranchExists: (tinaApi: {
12
+ branchExists: (branchName: string, args?: {
13
+ signal?: AbortSignal;
14
+ }) => Promise<boolean>;
15
+ }, baseBranch: string, debugLabel: string, signal?: AbortSignal) => Promise<boolean>;
16
+ export declare const checkTargetBranchExists: (tinaApi: {
17
+ branchExists: (branchName: string, args?: {
18
+ signal?: AbortSignal;
19
+ }) => Promise<boolean>;
20
+ }, targetBranch: string, debugLabel: string, signal?: AbortSignal) => Promise<boolean>;
@@ -12,6 +12,7 @@ export interface ExecuteWorkflowOptions {
12
12
  values: Record<string, unknown>;
13
13
  crudType: string;
14
14
  tinaForm?: Form;
15
+ signal?: AbortSignal;
15
16
  }
16
17
  export interface UseEditorialWorkflowResult {
17
18
  isExecuting: boolean;
@@ -23,4 +24,5 @@ export interface UseEditorialWorkflowResult {
23
24
  /** Reset error/executing state so the form can be retried */
24
25
  reset: () => void;
25
26
  }
27
+ export declare const getEditorialWorkflowErrorMessage: (e: unknown) => string;
26
28
  export declare function useEditorialWorkflow(): UseEditorialWorkflowResult;
@@ -5,12 +5,6 @@
5
5
  export declare class GitClient {
6
6
  private baseUrl;
7
7
  constructor(baseUrl: string);
8
- /**
9
- * An alias to `commit`
10
- *
11
- * @deprecated
12
- */
13
- onSubmit(data: any): Promise<any>;
14
8
  /**
15
9
  * An alias to `writeToDisk`
16
10
  *
@@ -18,7 +18,7 @@ export declare class GitFile {
18
18
  /**
19
19
  * @deprecated
20
20
  */
21
- commit: () => Promise<void>;
21
+ commit: () => Promise<boolean>;
22
22
  /**
23
23
  * @deprecated
24
24
  */
@@ -38,3 +38,4 @@ export type { TinaUIProps } from './components/tina-ui';
38
38
  export { useLocalStorage } from './hooks/use-local-storage';
39
39
  export { CursorPaginator } from './components/media/pagination';
40
40
  export { DEFAULT_MEDIA_UPLOAD_TYPES, sanitizeFilename, } from './components/media';
41
+ export { MediaWorkflowOverlay } from './components/media/media-workflow-overlay';
@@ -1,7 +1,8 @@
1
1
  import * as React from 'react';
2
2
  import { Branch, BranchSwitcherProps } from './types';
3
3
  export declare const tableHeadingStyle = "px-3 py-3 text-left text-xs font-bold text-gray-700 tracking-wider sticky top-0 bg-gray-100 z-20 border-b-2 border-gray-200 ";
4
- export declare function formatBranchName(str: string): string;
4
+ import { formatBranchName } from './format-branch-name';
5
+ export { formatBranchName };
5
6
  export declare const BranchSwitcher: (props: BranchSwitcherProps) => React.JSX.Element;
6
7
  export declare const EditoralBranchSwitcher: ({ listBranches, createBranch, chooseBranch, setModalTitle, }: BranchSwitcherProps) => React.JSX.Element;
7
8
  export declare const getFilteredBranchList: (branchList: Branch[], search: string, currentBranchName: string, filter?: "content" | "all") => Branch[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Normalizes user-supplied strings into git-ref-safe branch segments.
3
+ * Kept in its own file (no React or workspace-internal imports) so
4
+ * non-React callers like `core/media-store.default.ts` can import it
5
+ * without dragging in the rest of `plugin-branch-switcher` and creating
6
+ * a cycle through `@toolkit/core`.
7
+ */
8
+ export declare function formatBranchName(str: string): string;
@@ -3,6 +3,6 @@ import { Callback, CMSEvent } from '../core';
3
3
  export declare function useCMSEvent<E extends CMSEvent = CMSEvent>(event: E['type'] | E['type'][], callback: Callback<E>, deps: React.DependencyList): void;
4
4
  export declare const useEventSubscription: typeof useCMSEvent;
5
5
  export declare function useEvent<E extends CMSEvent = CMSEvent>(eventType: E['type']): {
6
- dispatch: (event: Omit<E, "type">) => void;
6
+ dispatch: (event: Omit<E, "type">) => boolean;
7
7
  subscribe: (callback: (event: E) => any) => () => void;
8
8
  };
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "tinacms",
3
3
  "type": "module",
4
4
  "typings": "dist/index.d.ts",
5
- "version": "3.8.4",
5
+ "version": "3.9.0",
6
6
  "main": "dist/index.js",
7
7
  "module": "./dist/index.js",
8
8
  "exports": {
@@ -117,9 +117,9 @@
117
117
  "webfontloader": "1.6.28",
118
118
  "yup": "^1.6.1",
119
119
  "zod": "^3.24.2",
120
+ "@tinacms/schema-tools": "2.8.1",
120
121
  "@tinacms/bridge": "0.3.0",
121
122
  "@tinacms/mdx": "2.1.6",
122
- "@tinacms/schema-tools": "2.8.1",
123
123
  "@tinacms/search": "1.2.17"
124
124
  },
125
125
  "devDependencies": {