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.
- package/dist/index.d.ts +0 -4
- package/dist/index.js +886 -293
- package/dist/internalClient/index.d.ts +30 -8
- package/dist/toolkit/components/media/media-workflow-overlay.d.ts +2 -0
- package/dist/toolkit/core/event.d.ts +15 -1
- package/dist/toolkit/core/media-store.default.d.ts +31 -4
- package/dist/toolkit/fields/plugins/mdx-field-plugin/plate/components/plate-ui/command.d.ts +8 -8
- package/dist/toolkit/fields/plugins/mdx-field-plugin/plate/components/plate-ui/input.d.ts +1 -1
- package/dist/toolkit/form-builder/create-branch-modal.d.ts +9 -0
- package/dist/toolkit/form-builder/editorial-workflow-progress-modal.d.ts +15 -0
- package/dist/toolkit/form-builder/editorial-workflow-utils.d.ts +20 -0
- package/dist/toolkit/form-builder/use-editorial-workflow.d.ts +2 -0
- package/dist/toolkit/git-client/git-client.d.ts +0 -6
- package/dist/toolkit/git-client/git-file.d.ts +1 -1
- package/dist/toolkit/index.d.ts +1 -0
- package/dist/toolkit/plugin-branch-switcher/branch-switcher.d.ts +2 -1
- package/dist/toolkit/plugin-branch-switcher/format-branch-name.d.ts +8 -0
- package/dist/toolkit/react-core/use-cms-event.d.ts +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -58,10 +58,10 @@ import { useSensors, useSensor, PointerSensor, KeyboardSensor, DndContext, close
|
|
|
58
58
|
import { sortableKeyboardCoordinates, useSortable, SortableContext, verticalListSortingStrategy, defaultAnimateLayoutChanges } from "@dnd-kit/sortable";
|
|
59
59
|
import { CSS } from "@dnd-kit/utilities";
|
|
60
60
|
import { FaSpinner, FaCircle, FaFolder, FaFile, FaLock, FaUnlock } from "react-icons/fa";
|
|
61
|
+
import { AiFillWarning, AiOutlineLoading } from "react-icons/ai";
|
|
61
62
|
import { buildSchema, print, getIntrospectionQuery, buildClientSchema, parse as parse$3 } from "graphql";
|
|
62
63
|
import { diff as diff$1 } from "@graphql-inspector/core";
|
|
63
64
|
import { processDocumentForIndexing, queryToSearchIndexQuery, optionsToSearchIndexOptions, parseSearchIndexResponse } from "@tinacms/search/index-client";
|
|
64
|
-
import { AiFillWarning, AiOutlineLoading } from "react-icons/ai";
|
|
65
65
|
import { HexColorPicker } from "react-colorful";
|
|
66
66
|
import { MdKeyboardArrowDown, MdOutlineClear, MdArrowForward, MdAccessTime, MdOutlineDataSaverOff, MdCheckCircle, MdWifiOff, MdOutlineSettings, MdOutlinePhotoLibrary, MdVpnKey, MdImage, MdOutlineCloud, MdWarning, MdInfo, MdError, MdSyncProblem, MdOutlinePerson, MdOutlineHelpOutline } from "react-icons/md";
|
|
67
67
|
import * as dropzone from "react-dropzone";
|
|
@@ -9306,12 +9306,23 @@ const PullRequestCell = ({
|
|
|
9306
9306
|
}
|
|
9307
9307
|
return null;
|
|
9308
9308
|
};
|
|
9309
|
-
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 ";
|
|
9310
9309
|
function formatBranchName(str) {
|
|
9311
|
-
|
|
9312
|
-
|
|
9313
|
-
|
|
9310
|
+
let result = "";
|
|
9311
|
+
let replacingInvalidChars = false;
|
|
9312
|
+
for (const char of str.toLowerCase()) {
|
|
9313
|
+
const code = char.charCodeAt(0);
|
|
9314
|
+
const isValid = char === "/" || char === "-" || char === "_" || code >= 48 && code <= 57 || code >= 97 && code <= 122;
|
|
9315
|
+
if (isValid) {
|
|
9316
|
+
result += char;
|
|
9317
|
+
replacingInvalidChars = false;
|
|
9318
|
+
} else if (!replacingInvalidChars) {
|
|
9319
|
+
result += "-";
|
|
9320
|
+
replacingInvalidChars = true;
|
|
9321
|
+
}
|
|
9322
|
+
}
|
|
9323
|
+
return result;
|
|
9314
9324
|
}
|
|
9325
|
+
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 ";
|
|
9315
9326
|
const BranchSwitcher = (props) => {
|
|
9316
9327
|
const cms = useCMS$1();
|
|
9317
9328
|
const usingEditorialWorkflow = cms.api.tina.usingEditorialWorkflow;
|
|
@@ -10014,9 +10025,28 @@ class EventBus {
|
|
|
10014
10025
|
}
|
|
10015
10026
|
dispatch(event) {
|
|
10016
10027
|
if (!this.listeners)
|
|
10017
|
-
return;
|
|
10028
|
+
return false;
|
|
10018
10029
|
const listenerSnapshot = Array.from(this.listeners.values());
|
|
10019
|
-
listenerSnapshot.
|
|
10030
|
+
return listenerSnapshot.reduce(
|
|
10031
|
+
(handled, listener) => listener.handleEvent(event) || handled,
|
|
10032
|
+
false
|
|
10033
|
+
);
|
|
10034
|
+
}
|
|
10035
|
+
/**
|
|
10036
|
+
* Whether any listener *explicitly* targets `eventType`. The catch-all `'*'`
|
|
10037
|
+
* pattern does not count — callers use this to detect a purpose-built
|
|
10038
|
+
* subscriber (e.g. a mounted UI overlay) without being misled by ambient
|
|
10039
|
+
* `'*'` listeners such as the alerts bridge, which would otherwise make
|
|
10040
|
+
* {@link dispatch} report every event as "handled".
|
|
10041
|
+
*/
|
|
10042
|
+
hasExplicitListenerFor(eventType) {
|
|
10043
|
+
if (!this.listeners)
|
|
10044
|
+
return false;
|
|
10045
|
+
for (const listener of this.listeners) {
|
|
10046
|
+
if (listener.isExplicitListenerFor(eventType))
|
|
10047
|
+
return true;
|
|
10048
|
+
}
|
|
10049
|
+
return false;
|
|
10020
10050
|
}
|
|
10021
10051
|
}
|
|
10022
10052
|
class Listener {
|
|
@@ -10031,6 +10061,16 @@ class Listener {
|
|
|
10031
10061
|
}
|
|
10032
10062
|
return false;
|
|
10033
10063
|
}
|
|
10064
|
+
/**
|
|
10065
|
+
* Whether this listener explicitly targets `eventType`. Unlike
|
|
10066
|
+
* {@link watchesEvent}, the catch-all `'*'` pattern does NOT match, so this
|
|
10067
|
+
* reflects a purpose-built subscription rather than an ambient one.
|
|
10068
|
+
*/
|
|
10069
|
+
isExplicitListenerFor(eventType) {
|
|
10070
|
+
if (this.eventPattern === "*")
|
|
10071
|
+
return false;
|
|
10072
|
+
return this.watchesEvent({ type: eventType });
|
|
10073
|
+
}
|
|
10034
10074
|
watchesEvent(currentEvent) {
|
|
10035
10075
|
if (this.eventPattern === "*")
|
|
10036
10076
|
return true;
|
|
@@ -10047,6 +10087,63 @@ class Listener {
|
|
|
10047
10087
|
return !ignoresEvent;
|
|
10048
10088
|
}
|
|
10049
10089
|
}
|
|
10090
|
+
const EDITORIAL_WORKFLOW_STATUS = {
|
|
10091
|
+
QUEUED: "queued",
|
|
10092
|
+
PROCESSING: "processing",
|
|
10093
|
+
SETTING_UP: "setting_up",
|
|
10094
|
+
CREATING_BRANCH: "creating_branch",
|
|
10095
|
+
INDEXING: "indexing",
|
|
10096
|
+
CONTENT_GENERATION: "content_generation",
|
|
10097
|
+
CREATING_PR: "creating_pr",
|
|
10098
|
+
COMPLETE: "complete",
|
|
10099
|
+
ERROR: "error",
|
|
10100
|
+
TIMEOUT: "timeout"
|
|
10101
|
+
};
|
|
10102
|
+
const EDITORIAL_WORKFLOW_ERROR = {
|
|
10103
|
+
BRANCH_EXISTS: "BRANCH_EXISTS",
|
|
10104
|
+
BRANCH_HIERARCHY_CONFLICT: "BRANCH_HIERARCHY_CONFLICT",
|
|
10105
|
+
VALIDATION_FAILED: "VALIDATION_FAILED"
|
|
10106
|
+
};
|
|
10107
|
+
const getEditorialWorkflowPrTitle = (branchName) => `${branchName.replace("tina/", "").replaceAll("-", " ")} (PR from TinaCMS)`;
|
|
10108
|
+
const TARGET_BRANCH_EXISTS_ERROR = "A branch with this name already exists";
|
|
10109
|
+
const checkBranchExists = async (tinaApi, branchName, debugLabel, branchType, fallback, signal) => {
|
|
10110
|
+
try {
|
|
10111
|
+
console.debug(
|
|
10112
|
+
`[tina:branch-guard] ${debugLabel}: checking ${branchType} branch:`,
|
|
10113
|
+
branchName
|
|
10114
|
+
);
|
|
10115
|
+
const exists = await tinaApi.branchExists(branchName, { signal });
|
|
10116
|
+
console.debug(
|
|
10117
|
+
`[tina:branch-guard] ${debugLabel}: ${branchType} branch exists?`,
|
|
10118
|
+
exists
|
|
10119
|
+
);
|
|
10120
|
+
return exists;
|
|
10121
|
+
} catch (err) {
|
|
10122
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
10123
|
+
return fallback;
|
|
10124
|
+
console.error(
|
|
10125
|
+
`[tina:branch-guard] ${debugLabel}: branchExists threw, failing open:`,
|
|
10126
|
+
err
|
|
10127
|
+
);
|
|
10128
|
+
return fallback;
|
|
10129
|
+
}
|
|
10130
|
+
};
|
|
10131
|
+
const checkBaseBranchExists = async (tinaApi, baseBranch, debugLabel, signal) => checkBranchExists(tinaApi, baseBranch, debugLabel, "base", true, signal);
|
|
10132
|
+
const checkTargetBranchExists = async (tinaApi, targetBranch, debugLabel, signal) => checkBranchExists(tinaApi, targetBranch, debugLabel, "target", false, signal);
|
|
10133
|
+
const MEDIA_WORKFLOW_STEP = {
|
|
10134
|
+
BRANCH: 1,
|
|
10135
|
+
CONTENT: 2,
|
|
10136
|
+
PR: 3,
|
|
10137
|
+
COMPLETE: 4
|
|
10138
|
+
};
|
|
10139
|
+
const MEDIA_WORKFLOW_STATUS_TO_STEP = {
|
|
10140
|
+
[EDITORIAL_WORKFLOW_STATUS.SETTING_UP]: MEDIA_WORKFLOW_STEP.BRANCH,
|
|
10141
|
+
[EDITORIAL_WORKFLOW_STATUS.CREATING_BRANCH]: MEDIA_WORKFLOW_STEP.BRANCH,
|
|
10142
|
+
[EDITORIAL_WORKFLOW_STATUS.INDEXING]: MEDIA_WORKFLOW_STEP.CONTENT,
|
|
10143
|
+
[EDITORIAL_WORKFLOW_STATUS.CONTENT_GENERATION]: MEDIA_WORKFLOW_STEP.CONTENT,
|
|
10144
|
+
[EDITORIAL_WORKFLOW_STATUS.CREATING_PR]: MEDIA_WORKFLOW_STEP.PR,
|
|
10145
|
+
[EDITORIAL_WORKFLOW_STATUS.COMPLETE]: MEDIA_WORKFLOW_STEP.COMPLETE
|
|
10146
|
+
};
|
|
10050
10147
|
const s3ErrorRegex = /<Error>.*<Code>(.+)<\/Code>.*<Message>(.+)<\/Message>.*/;
|
|
10051
10148
|
class DummyMediaStore {
|
|
10052
10149
|
constructor() {
|
|
@@ -10084,6 +10181,9 @@ class TinaMediaStore {
|
|
|
10084
10181
|
__publicField(this, "url");
|
|
10085
10182
|
__publicField(this, "staticMedia");
|
|
10086
10183
|
__publicField(this, "isStatic");
|
|
10184
|
+
// Route assets-api calls through the created branch until indexing makes it safe to switch React branch state.
|
|
10185
|
+
__publicField(this, "workflowBranchOverride");
|
|
10186
|
+
__publicField(this, "mediaWorkflowInProgress", false);
|
|
10087
10187
|
__publicField(this, "accept", DEFAULT_MEDIA_UPLOAD_TYPES);
|
|
10088
10188
|
// allow up to 100MB uploads
|
|
10089
10189
|
__publicField(this, "maxSize", 100 * 1024 * 1024);
|
|
@@ -10123,8 +10223,8 @@ class TinaMediaStore {
|
|
|
10123
10223
|
return await this.api.authProvider.isAuthenticated();
|
|
10124
10224
|
}
|
|
10125
10225
|
/**
|
|
10126
|
-
* Returns the current branch as a single-encoded
|
|
10127
|
-
* an empty string when no branch is set.
|
|
10226
|
+
* Returns the workflow branch override or current branch as a single-encoded
|
|
10227
|
+
* query-param value, or an empty string when no branch is set.
|
|
10128
10228
|
*
|
|
10129
10229
|
* `this.api.branch` is already URL-encoded by `Client.setBranch()`, so we
|
|
10130
10230
|
* decode then re-encode here to defend against double-encoding when this
|
|
@@ -10137,6 +10237,9 @@ class TinaMediaStore {
|
|
|
10137
10237
|
* assets-api (which would route the call to a non-existent staging path).
|
|
10138
10238
|
*/
|
|
10139
10239
|
encodedBranchParam() {
|
|
10240
|
+
if (this.workflowBranchOverride) {
|
|
10241
|
+
return encodeURIComponent(this.workflowBranchOverride);
|
|
10242
|
+
}
|
|
10140
10243
|
if (!this.api.branch)
|
|
10141
10244
|
return "";
|
|
10142
10245
|
const decoded = decodeURIComponent(this.api.branch);
|
|
@@ -10144,67 +10247,297 @@ class TinaMediaStore {
|
|
|
10144
10247
|
return "";
|
|
10145
10248
|
return encodeURIComponent(decoded);
|
|
10146
10249
|
}
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10250
|
+
shortStableHash(input) {
|
|
10251
|
+
let hash = 2166136261;
|
|
10252
|
+
for (let index = 0; index < input.length; index++) {
|
|
10253
|
+
hash ^= input.charCodeAt(index);
|
|
10254
|
+
hash = Math.imul(hash, 16777619);
|
|
10150
10255
|
}
|
|
10256
|
+
return (hash >>> 0).toString(36);
|
|
10257
|
+
}
|
|
10258
|
+
trimEdges(value, char) {
|
|
10259
|
+
let start = 0;
|
|
10260
|
+
let end = value.length;
|
|
10261
|
+
while (start < end && value[start] === char) {
|
|
10262
|
+
start++;
|
|
10263
|
+
}
|
|
10264
|
+
while (end > start && value[end - 1] === char) {
|
|
10265
|
+
end--;
|
|
10266
|
+
}
|
|
10267
|
+
return value.slice(start, end);
|
|
10268
|
+
}
|
|
10269
|
+
branchSlugForMediaPath(directory, filename) {
|
|
10270
|
+
const trimmedDirectory = this.trimEdges(directory ?? "", "/");
|
|
10271
|
+
const rawPath = [trimmedDirectory, filename].filter(Boolean).join("/");
|
|
10272
|
+
const flattened = rawPath.replaceAll("/", "-");
|
|
10273
|
+
const slug = this.trimEdges(formatBranchName(flattened), "-");
|
|
10274
|
+
if (!slug)
|
|
10275
|
+
return `asset-${this.shortStableHash(rawPath || "root")}`;
|
|
10276
|
+
return rawPath !== rawPath.toLowerCase() ? `${slug}-${this.shortStableHash(rawPath)}` : slug;
|
|
10277
|
+
}
|
|
10278
|
+
/** Joins a directory and filename into a normalized `dir/file` repo path. */
|
|
10279
|
+
joinMediaPath(directory, filename) {
|
|
10280
|
+
const dir = this.trimEdges(directory ?? "", "/");
|
|
10281
|
+
return dir && dir !== "/" ? `${dir}/${filename ?? ""}` : filename ?? "";
|
|
10282
|
+
}
|
|
10283
|
+
branchQueryParam() {
|
|
10151
10284
|
const encodedBranch = this.encodedBranchParam();
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10158
|
-
const safeName = sanitizeFilename(item.file.name);
|
|
10159
|
-
const path = `${directory && directory !== "/" ? `${directory}/${safeName}` : safeName}`;
|
|
10160
|
-
const res = await this.api.authProvider.fetchWithToken(
|
|
10161
|
-
`${this.url}/upload_url/${path}${branchQuery}`,
|
|
10162
|
-
{ method: "GET" }
|
|
10285
|
+
return encodedBranch ? `?branch=${encodedBranch}` : "";
|
|
10286
|
+
}
|
|
10287
|
+
requestMediaBranchChoice(branchName, baseBranch, opType, repoPath) {
|
|
10288
|
+
if (!this.cms.events.hasExplicitListenerFor("media:workflow:confirm-branch")) {
|
|
10289
|
+
throw new Error(
|
|
10290
|
+
"Cannot start a media editorial workflow: no branch prompt is mounted. Ensure <MediaWorkflowOverlay /> is rendered (TinaCloudProvider mounts it automatically)."
|
|
10163
10291
|
);
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10292
|
+
}
|
|
10293
|
+
return new Promise((resolve) => {
|
|
10294
|
+
this.cms.events.dispatch({
|
|
10295
|
+
type: "media:workflow:confirm-branch",
|
|
10296
|
+
branchName,
|
|
10297
|
+
baseBranch,
|
|
10298
|
+
onConfirm: async (selectedBranchName) => {
|
|
10299
|
+
const context = await this.prepareMediaBranch(
|
|
10300
|
+
selectedBranchName,
|
|
10301
|
+
baseBranch,
|
|
10302
|
+
opType,
|
|
10303
|
+
repoPath
|
|
10304
|
+
);
|
|
10305
|
+
resolve({
|
|
10306
|
+
kind: "workflow",
|
|
10307
|
+
context
|
|
10308
|
+
});
|
|
10309
|
+
},
|
|
10310
|
+
onCancel: () => resolve({ kind: "cancelled" }),
|
|
10311
|
+
onSaveToProtectedBranch: () => resolve({ kind: "direct" })
|
|
10312
|
+
});
|
|
10313
|
+
});
|
|
10314
|
+
}
|
|
10315
|
+
async prepareMediaBranch(branchName, baseBranch, opType, repoPath) {
|
|
10316
|
+
if (this.mediaWorkflowInProgress) {
|
|
10317
|
+
throw new Error("A media workflow is already in progress.");
|
|
10318
|
+
}
|
|
10319
|
+
this.mediaWorkflowInProgress = true;
|
|
10320
|
+
try {
|
|
10321
|
+
this.cms.events.dispatch({
|
|
10322
|
+
type: "media:workflow:start",
|
|
10323
|
+
branchName,
|
|
10324
|
+
baseBranch
|
|
10325
|
+
});
|
|
10326
|
+
const workflow = await this.api.startMediaEditorialWorkflow({
|
|
10327
|
+
branchName,
|
|
10328
|
+
baseBranch,
|
|
10329
|
+
prTitle: getEditorialWorkflowPrTitle(branchName),
|
|
10330
|
+
operation: opType,
|
|
10331
|
+
repoPath
|
|
10332
|
+
});
|
|
10333
|
+
const branchContext = {
|
|
10334
|
+
branchName: workflow.branchName || branchName,
|
|
10335
|
+
baseBranch,
|
|
10336
|
+
requestId: workflow.requestId
|
|
10337
|
+
};
|
|
10338
|
+
this.workflowBranchOverride = branchContext.branchName;
|
|
10339
|
+
return branchContext;
|
|
10340
|
+
} catch (err) {
|
|
10341
|
+
this.resetWorkflowState();
|
|
10342
|
+
throw err;
|
|
10343
|
+
}
|
|
10344
|
+
}
|
|
10345
|
+
resetWorkflowState() {
|
|
10346
|
+
this.workflowBranchOverride = void 0;
|
|
10347
|
+
this.mediaWorkflowInProgress = false;
|
|
10348
|
+
}
|
|
10349
|
+
async finalizeMediaWorkflow(branchContext, onCatalogued) {
|
|
10350
|
+
try {
|
|
10351
|
+
const result = await this.api.waitForEditorialWorkflowStatus(
|
|
10352
|
+
branchContext.requestId,
|
|
10353
|
+
(status) => {
|
|
10354
|
+
const step = MEDIA_WORKFLOW_STATUS_TO_STEP[status.status];
|
|
10355
|
+
if (step) {
|
|
10356
|
+
this.cms.events.dispatch({
|
|
10357
|
+
type: "media:workflow:step",
|
|
10358
|
+
step
|
|
10359
|
+
});
|
|
10360
|
+
}
|
|
10178
10361
|
}
|
|
10362
|
+
);
|
|
10363
|
+
if (onCatalogued)
|
|
10364
|
+
await onCatalogued();
|
|
10365
|
+
this.resetWorkflowState();
|
|
10366
|
+
this.cms.events.dispatch({
|
|
10367
|
+
type: "media:workflow:complete",
|
|
10368
|
+
branchName: result.branchName || branchContext.branchName
|
|
10179
10369
|
});
|
|
10180
|
-
if (
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
|
|
10185
|
-
|
|
10370
|
+
if (result.warning) {
|
|
10371
|
+
this.cms.alerts.warn(result.warning, 0);
|
|
10372
|
+
}
|
|
10373
|
+
this.cms.alerts.success(
|
|
10374
|
+
`Branch created successfully - Pull Request at ${(result == null ? void 0 : result.pullRequestUrl) || ""}`,
|
|
10375
|
+
0
|
|
10376
|
+
);
|
|
10377
|
+
this.cms.events.dispatch({ type: "media:workflow:finish" });
|
|
10378
|
+
} catch (err) {
|
|
10379
|
+
this.resetWorkflowState();
|
|
10380
|
+
this.dispatchMediaWorkflowError(err);
|
|
10381
|
+
}
|
|
10382
|
+
}
|
|
10383
|
+
dispatchMediaWorkflowError(err) {
|
|
10384
|
+
this.cms.events.dispatch({
|
|
10385
|
+
type: "media:workflow:error",
|
|
10386
|
+
message: err instanceof Error ? err.message : String(err)
|
|
10387
|
+
});
|
|
10388
|
+
}
|
|
10389
|
+
async runMediaOpWithWorkflow(decision, op) {
|
|
10390
|
+
if (decision.kind !== "workflow")
|
|
10391
|
+
return op();
|
|
10392
|
+
try {
|
|
10393
|
+
const result = await op();
|
|
10394
|
+
await this.finalizeMediaWorkflow(decision.context);
|
|
10395
|
+
return result;
|
|
10396
|
+
} catch (err) {
|
|
10397
|
+
this.resetWorkflowState();
|
|
10398
|
+
this.cms.events.dispatch({ type: "media:workflow:finish" });
|
|
10399
|
+
throw err;
|
|
10400
|
+
}
|
|
10401
|
+
}
|
|
10402
|
+
async prepareProtectedMediaBranch(opType, directory, filename) {
|
|
10403
|
+
if (!this.api.usingProtectedBranch())
|
|
10404
|
+
return { kind: "direct" };
|
|
10405
|
+
const baseBranch = decodeURIComponent(this.api.branch || "");
|
|
10406
|
+
const mediaSlug = this.branchSlugForMediaPath(directory, filename);
|
|
10407
|
+
const branchName = `media-${opType}-${mediaSlug}`;
|
|
10408
|
+
const repoFilename = opType === "upload" && filename ? sanitizeFilename(filename) : filename;
|
|
10409
|
+
const repoPath = this.joinMediaPath(directory, repoFilename);
|
|
10410
|
+
return this.requestMediaBranchChoice(
|
|
10411
|
+
branchName,
|
|
10412
|
+
baseBranch,
|
|
10413
|
+
opType,
|
|
10414
|
+
repoPath
|
|
10415
|
+
);
|
|
10416
|
+
}
|
|
10417
|
+
async waitForRequestStatus(requestId, timeoutMessage) {
|
|
10418
|
+
const startTime = Date.now();
|
|
10419
|
+
while (true) {
|
|
10420
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
10421
|
+
const { error: error2, message } = await this.api.getRequestStatus(requestId);
|
|
10422
|
+
if (error2 !== void 0) {
|
|
10423
|
+
if (error2) {
|
|
10424
|
+
throw new Error(message);
|
|
10186
10425
|
} else {
|
|
10187
|
-
|
|
10426
|
+
return;
|
|
10188
10427
|
}
|
|
10189
10428
|
}
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
10193
|
-
const { error: error2, message } = await this.api.getRequestStatus(requestId);
|
|
10194
|
-
if (error2 !== void 0) {
|
|
10195
|
-
if (error2) {
|
|
10196
|
-
throw new Error(message);
|
|
10197
|
-
} else {
|
|
10198
|
-
break;
|
|
10199
|
-
}
|
|
10200
|
-
}
|
|
10201
|
-
if (Date.now() - updateStartTime > 3e4) {
|
|
10202
|
-
throw new Error("Time out waiting for upload to complete");
|
|
10203
|
-
}
|
|
10429
|
+
if (Date.now() - startTime > 3e4) {
|
|
10430
|
+
throw new Error(timeoutMessage);
|
|
10204
10431
|
}
|
|
10205
10432
|
}
|
|
10433
|
+
}
|
|
10434
|
+
async persist_cloud(media) {
|
|
10435
|
+
if (media.length === 0) {
|
|
10436
|
+
return [];
|
|
10437
|
+
}
|
|
10438
|
+
if (!await this.isAuthenticated()) {
|
|
10439
|
+
return [];
|
|
10440
|
+
}
|
|
10441
|
+
const firstItem = media[0];
|
|
10442
|
+
const decision = await this.prepareProtectedMediaBranch(
|
|
10443
|
+
"upload",
|
|
10444
|
+
firstItem.directory,
|
|
10445
|
+
firstItem.file.name
|
|
10446
|
+
);
|
|
10447
|
+
if (decision.kind === "cancelled")
|
|
10448
|
+
return [];
|
|
10449
|
+
if (decision.kind === "workflow") {
|
|
10450
|
+
return this.persistCloudViaWorkflow(media, decision.context);
|
|
10451
|
+
}
|
|
10452
|
+
const branchQuery = this.branchQueryParam();
|
|
10453
|
+
for (const item of media) {
|
|
10454
|
+
await this.uploadCloudMediaItem(item, branchQuery, {
|
|
10455
|
+
waitForStatus: true
|
|
10456
|
+
});
|
|
10457
|
+
}
|
|
10206
10458
|
return this.fetchUploadedEntries(media);
|
|
10207
10459
|
}
|
|
10460
|
+
/**
|
|
10461
|
+
* Uploads assets through the editorial workflow. The asset is staged, the
|
|
10462
|
+
* server-side workflow catalogues it in the new branch's media index, and
|
|
10463
|
+
* only then do we list it — while the branch override still routes list
|
|
10464
|
+
* calls to the workflow branch, so the freshly uploaded item is returned to
|
|
10465
|
+
* the caller (and added to the media manager) without needing a manual
|
|
10466
|
+
* refresh.
|
|
10467
|
+
*/
|
|
10468
|
+
async persistCloudViaWorkflow(media, branchContext) {
|
|
10469
|
+
try {
|
|
10470
|
+
const branchQuery = this.branchQueryParam();
|
|
10471
|
+
for (const item of media) {
|
|
10472
|
+
await this.uploadCloudMediaItem(item, branchQuery, {
|
|
10473
|
+
waitForStatus: false
|
|
10474
|
+
});
|
|
10475
|
+
}
|
|
10476
|
+
let entries = [];
|
|
10477
|
+
await this.finalizeMediaWorkflow(branchContext, async () => {
|
|
10478
|
+
entries = await this.fetchUploadedEntries(media);
|
|
10479
|
+
});
|
|
10480
|
+
return entries;
|
|
10481
|
+
} catch (err) {
|
|
10482
|
+
this.resetWorkflowState();
|
|
10483
|
+
this.cms.events.dispatch({ type: "media:workflow:finish" });
|
|
10484
|
+
throw err;
|
|
10485
|
+
}
|
|
10486
|
+
}
|
|
10487
|
+
async uploadCloudMediaItem(item, branchQuery, { waitForStatus = true } = {}) {
|
|
10488
|
+
const path = this.joinMediaPath(
|
|
10489
|
+
item.directory,
|
|
10490
|
+
sanitizeFilename(item.file.name)
|
|
10491
|
+
);
|
|
10492
|
+
const res = await this.api.authProvider.fetchWithToken(
|
|
10493
|
+
`${this.url}/upload_url/${path}${branchQuery}`,
|
|
10494
|
+
{ method: "GET" }
|
|
10495
|
+
);
|
|
10496
|
+
if (res.status === 412) {
|
|
10497
|
+
const { message = "Unexpected error generating upload url" } = await res.json();
|
|
10498
|
+
throw new Error(message);
|
|
10499
|
+
}
|
|
10500
|
+
const { signedUrl, requestId } = await res.json();
|
|
10501
|
+
if (!signedUrl) {
|
|
10502
|
+
throw new Error("Unexpected error generating upload url");
|
|
10503
|
+
}
|
|
10504
|
+
const uploadRes = await this.fetchFunction(signedUrl, {
|
|
10505
|
+
method: "PUT",
|
|
10506
|
+
body: item.file,
|
|
10507
|
+
headers: {
|
|
10508
|
+
"Content-Type": item.file.type || "application/octet-stream",
|
|
10509
|
+
"Content-Length": String(item.file.size)
|
|
10510
|
+
}
|
|
10511
|
+
});
|
|
10512
|
+
if (!uploadRes.ok) {
|
|
10513
|
+
const xmlRes = await uploadRes.text();
|
|
10514
|
+
const matches = s3ErrorRegex.exec(xmlRes);
|
|
10515
|
+
console.error(xmlRes);
|
|
10516
|
+
if (!matches) {
|
|
10517
|
+
throw new Error("Unexpected error uploading media asset");
|
|
10518
|
+
} else {
|
|
10519
|
+
throw new Error(`Upload error: '${matches[2]}'`);
|
|
10520
|
+
}
|
|
10521
|
+
}
|
|
10522
|
+
if (waitForStatus) {
|
|
10523
|
+
await this.waitForRequestStatus(
|
|
10524
|
+
requestId,
|
|
10525
|
+
"Time out waiting for upload to complete"
|
|
10526
|
+
);
|
|
10527
|
+
}
|
|
10528
|
+
}
|
|
10529
|
+
groupMediaByDirectory(media) {
|
|
10530
|
+
const byDirectory = /* @__PURE__ */ new Map();
|
|
10531
|
+
for (const item of media) {
|
|
10532
|
+
let dir = item.directory || "";
|
|
10533
|
+
while (dir.endsWith("/"))
|
|
10534
|
+
dir = dir.slice(0, -1);
|
|
10535
|
+
const bucket = byDirectory.get(dir) ?? [];
|
|
10536
|
+
bucket.push(item);
|
|
10537
|
+
byDirectory.set(dir, bucket);
|
|
10538
|
+
}
|
|
10539
|
+
return byDirectory;
|
|
10540
|
+
}
|
|
10208
10541
|
/**
|
|
10209
10542
|
* Resolves the just-uploaded items to canonical `Media` entries by hitting
|
|
10210
10543
|
* the assets-api `list` endpoint, which is the source of truth for the
|
|
@@ -10218,15 +10551,7 @@ class TinaMediaStore {
|
|
|
10218
10551
|
* throwing — the upload itself already succeeded.
|
|
10219
10552
|
*/
|
|
10220
10553
|
async fetchUploadedEntries(media) {
|
|
10221
|
-
const byDirectory =
|
|
10222
|
-
for (const item of media) {
|
|
10223
|
-
let dir = item.directory || "";
|
|
10224
|
-
while (dir.endsWith("/"))
|
|
10225
|
-
dir = dir.slice(0, -1);
|
|
10226
|
-
const bucket = byDirectory.get(dir) ?? [];
|
|
10227
|
-
bucket.push(item);
|
|
10228
|
-
byDirectory.set(dir, bucket);
|
|
10229
|
-
}
|
|
10554
|
+
const byDirectory = this.groupMediaByDirectory(media);
|
|
10230
10555
|
const thumbnailSizes = [
|
|
10231
10556
|
{ w: 75, h: 75 },
|
|
10232
10557
|
{ w: 400, h: 400 },
|
|
@@ -10422,37 +10747,33 @@ class TinaMediaStore {
|
|
|
10422
10747
|
};
|
|
10423
10748
|
}
|
|
10424
10749
|
async delete(media) {
|
|
10425
|
-
const path =
|
|
10750
|
+
const path = this.joinMediaPath(media.directory, media.filename);
|
|
10426
10751
|
if (!this.isLocal) {
|
|
10427
10752
|
if (await this.isAuthenticated()) {
|
|
10428
|
-
const
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
{
|
|
10433
|
-
method: "DELETE"
|
|
10434
|
-
}
|
|
10753
|
+
const decision = await this.prepareProtectedMediaBranch(
|
|
10754
|
+
"delete",
|
|
10755
|
+
media.directory,
|
|
10756
|
+
media.filename
|
|
10435
10757
|
);
|
|
10436
|
-
if (
|
|
10758
|
+
if (decision.kind === "cancelled")
|
|
10759
|
+
return;
|
|
10760
|
+
await this.runMediaOpWithWorkflow(decision, async () => {
|
|
10761
|
+
const branchQuery = this.branchQueryParam();
|
|
10762
|
+
const res = await this.api.authProvider.fetchWithToken(
|
|
10763
|
+
`${this.url}/${path}${branchQuery}`,
|
|
10764
|
+
{ method: "DELETE" }
|
|
10765
|
+
);
|
|
10766
|
+
if (res.status !== 200) {
|
|
10767
|
+
throw new Error("Unexpected error deleting media asset");
|
|
10768
|
+
}
|
|
10437
10769
|
const { requestId } = await res.json();
|
|
10438
|
-
|
|
10439
|
-
|
|
10440
|
-
|
|
10441
|
-
|
|
10442
|
-
|
|
10443
|
-
if (error2) {
|
|
10444
|
-
throw new Error(message);
|
|
10445
|
-
} else {
|
|
10446
|
-
break;
|
|
10447
|
-
}
|
|
10448
|
-
}
|
|
10449
|
-
if (Date.now() - deleteStartTime > 3e4) {
|
|
10450
|
-
throw new Error("Time out waiting for delete to complete");
|
|
10451
|
-
}
|
|
10770
|
+
if (decision.kind !== "workflow") {
|
|
10771
|
+
await this.waitForRequestStatus(
|
|
10772
|
+
requestId,
|
|
10773
|
+
"Time out waiting for delete to complete"
|
|
10774
|
+
);
|
|
10452
10775
|
}
|
|
10453
|
-
}
|
|
10454
|
-
throw new Error("Unexpected error deleting media asset");
|
|
10455
|
-
}
|
|
10776
|
+
});
|
|
10456
10777
|
} else {
|
|
10457
10778
|
throw E_UNAUTHORIZED;
|
|
10458
10779
|
}
|
|
@@ -12865,7 +13186,7 @@ const NavProvider = ({
|
|
|
12865
13186
|
const name = "tinacms";
|
|
12866
13187
|
const type = "module";
|
|
12867
13188
|
const typings = "dist/index.d.ts";
|
|
12868
|
-
const version$1 = "3.
|
|
13189
|
+
const version$1 = "3.9.0";
|
|
12869
13190
|
const main = "dist/index.js";
|
|
12870
13191
|
const module = "./dist/index.js";
|
|
12871
13192
|
const exports = {
|
|
@@ -15454,23 +15775,6 @@ const BranchPreviewButton = (props) => {
|
|
|
15454
15775
|
/* @__PURE__ */ React.createElement(BiLinkExternal, { className: "h-5 w-auto" })
|
|
15455
15776
|
);
|
|
15456
15777
|
};
|
|
15457
|
-
const EDITORIAL_WORKFLOW_STATUS = {
|
|
15458
|
-
QUEUED: "queued",
|
|
15459
|
-
PROCESSING: "processing",
|
|
15460
|
-
SETTING_UP: "setting_up",
|
|
15461
|
-
CREATING_BRANCH: "creating_branch",
|
|
15462
|
-
INDEXING: "indexing",
|
|
15463
|
-
CONTENT_GENERATION: "content_generation",
|
|
15464
|
-
CREATING_PR: "creating_pr",
|
|
15465
|
-
COMPLETE: "complete",
|
|
15466
|
-
ERROR: "error",
|
|
15467
|
-
TIMEOUT: "timeout"
|
|
15468
|
-
};
|
|
15469
|
-
const EDITORIAL_WORKFLOW_ERROR = {
|
|
15470
|
-
BRANCH_EXISTS: "BRANCH_EXISTS",
|
|
15471
|
-
BRANCH_HIERARCHY_CONFLICT: "BRANCH_HIERARCHY_CONFLICT",
|
|
15472
|
-
VALIDATION_FAILED: "VALIDATION_FAILED"
|
|
15473
|
-
};
|
|
15474
15778
|
const CREATE_DOCUMENT_GQL = `#graphql
|
|
15475
15779
|
mutation($collection: String!, $relativePath: String!, $params: DocumentMutation!) {
|
|
15476
15780
|
createDocument(
|
|
@@ -15828,6 +16132,18 @@ const pathRelativeToCollection = (collectionPath, fullPath) => {
|
|
|
15828
16132
|
`Path ${fullPath} not within collection path ${collectionPath}`
|
|
15829
16133
|
);
|
|
15830
16134
|
};
|
|
16135
|
+
const getEditorialWorkflowMutation = (crudType) => {
|
|
16136
|
+
if (crudType === "create") {
|
|
16137
|
+
return CREATE_DOCUMENT_GQL;
|
|
16138
|
+
}
|
|
16139
|
+
if (crudType === "delete") {
|
|
16140
|
+
return DELETE_DOCUMENT_GQL;
|
|
16141
|
+
}
|
|
16142
|
+
if (crudType !== "view") {
|
|
16143
|
+
return UPDATE_DOCUMENT_GQL;
|
|
16144
|
+
}
|
|
16145
|
+
return "";
|
|
16146
|
+
};
|
|
15831
16147
|
const WORKFLOW_STEPS = [
|
|
15832
16148
|
{ id: 1, name: "Creating branch", description: "Setting up workspace" },
|
|
15833
16149
|
{ id: 2, name: "Updating branch", description: "Syncing content to branch" },
|
|
@@ -15838,6 +16154,33 @@ const formatTime = (seconds) => {
|
|
|
15838
16154
|
const secs = seconds % 60;
|
|
15839
16155
|
return `${mins}:${secs.toString().padStart(2, "0")}`;
|
|
15840
16156
|
};
|
|
16157
|
+
const getEditorialWorkflowErrorMessage = (e) => {
|
|
16158
|
+
let errMessage = "Branch operation failed. Talking to GitHub was unsuccessful, please try again. If the problem persists please contact support at https://tina.io/support 🦙";
|
|
16159
|
+
const err = e;
|
|
16160
|
+
if (err.errorCode) {
|
|
16161
|
+
switch (err.errorCode) {
|
|
16162
|
+
case EDITORIAL_WORKFLOW_ERROR.BRANCH_EXISTS:
|
|
16163
|
+
errMessage = "A branch with this name already exists";
|
|
16164
|
+
break;
|
|
16165
|
+
case EDITORIAL_WORKFLOW_ERROR.BRANCH_HIERARCHY_CONFLICT:
|
|
16166
|
+
errMessage = err.message || "Branch name conflicts with an existing branch";
|
|
16167
|
+
break;
|
|
16168
|
+
case EDITORIAL_WORKFLOW_ERROR.VALIDATION_FAILED:
|
|
16169
|
+
errMessage = err.message || "Invalid branch name";
|
|
16170
|
+
break;
|
|
16171
|
+
default:
|
|
16172
|
+
errMessage = err.message || errMessage;
|
|
16173
|
+
break;
|
|
16174
|
+
}
|
|
16175
|
+
} else if (err.message) {
|
|
16176
|
+
if (err.message.toLowerCase().includes("already exists")) {
|
|
16177
|
+
errMessage = "A branch with this name already exists";
|
|
16178
|
+
} else if (err.message.toLowerCase().includes("conflict")) {
|
|
16179
|
+
errMessage = err.message;
|
|
16180
|
+
}
|
|
16181
|
+
}
|
|
16182
|
+
return errMessage;
|
|
16183
|
+
};
|
|
15841
16184
|
function useEditorialWorkflow() {
|
|
15842
16185
|
const cms = useCMS$1();
|
|
15843
16186
|
const tinaApi = cms.api.tina;
|
|
@@ -15871,20 +16214,30 @@ function useEditorialWorkflow() {
|
|
|
15871
16214
|
path,
|
|
15872
16215
|
values,
|
|
15873
16216
|
crudType,
|
|
15874
|
-
tinaForm
|
|
16217
|
+
tinaForm,
|
|
16218
|
+
signal
|
|
15875
16219
|
}) => {
|
|
15876
16220
|
var _a;
|
|
15877
16221
|
try {
|
|
16222
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
16223
|
+
return false;
|
|
16224
|
+
const targetBranchExists = await checkTargetBranchExists(
|
|
16225
|
+
tinaApi,
|
|
16226
|
+
branchName,
|
|
16227
|
+
"executeEditorialWorkflow",
|
|
16228
|
+
signal
|
|
16229
|
+
);
|
|
16230
|
+
if (signal == null ? void 0 : signal.aborted)
|
|
16231
|
+
return false;
|
|
16232
|
+
if (targetBranchExists) {
|
|
16233
|
+
setErrorMessage(TARGET_BRANCH_EXISTS_ERROR);
|
|
16234
|
+
setIsExecuting(false);
|
|
16235
|
+
setCurrentStep(0);
|
|
16236
|
+
return false;
|
|
16237
|
+
}
|
|
15878
16238
|
setIsExecuting(true);
|
|
15879
16239
|
setCurrentStep(1);
|
|
15880
|
-
|
|
15881
|
-
if (crudType === "create") {
|
|
15882
|
-
graphql2 = CREATE_DOCUMENT_GQL;
|
|
15883
|
-
} else if (crudType === "delete") {
|
|
15884
|
-
graphql2 = DELETE_DOCUMENT_GQL;
|
|
15885
|
-
} else if (crudType !== "view") {
|
|
15886
|
-
graphql2 = UPDATE_DOCUMENT_GQL;
|
|
15887
|
-
}
|
|
16240
|
+
const graphql2 = getEditorialWorkflowMutation(crudType);
|
|
15888
16241
|
const collection = tinaApi.schema.getCollectionByFullPath(path);
|
|
15889
16242
|
let submittedValues = values;
|
|
15890
16243
|
if ((_a = collection == null ? void 0 : collection.ui) == null ? void 0 : _a.beforeSubmit) {
|
|
@@ -15905,7 +16258,7 @@ function useEditorialWorkflow() {
|
|
|
15905
16258
|
const result = await tinaApi.executeEditorialWorkflow({
|
|
15906
16259
|
branchName,
|
|
15907
16260
|
baseBranch,
|
|
15908
|
-
prTitle:
|
|
16261
|
+
prTitle: getEditorialWorkflowPrTitle(branchName),
|
|
15909
16262
|
graphQLContentOp: {
|
|
15910
16263
|
query: graphql2,
|
|
15911
16264
|
variables: {
|
|
@@ -15954,30 +16307,7 @@ function useEditorialWorkflow() {
|
|
|
15954
16307
|
return true;
|
|
15955
16308
|
} catch (e) {
|
|
15956
16309
|
console.error(e);
|
|
15957
|
-
|
|
15958
|
-
const err = e;
|
|
15959
|
-
if (err.errorCode) {
|
|
15960
|
-
switch (err.errorCode) {
|
|
15961
|
-
case EDITORIAL_WORKFLOW_ERROR.BRANCH_EXISTS:
|
|
15962
|
-
errMessage = "A branch with this name already exists";
|
|
15963
|
-
break;
|
|
15964
|
-
case EDITORIAL_WORKFLOW_ERROR.BRANCH_HIERARCHY_CONFLICT:
|
|
15965
|
-
errMessage = err.message || "Branch name conflicts with an existing branch";
|
|
15966
|
-
break;
|
|
15967
|
-
case EDITORIAL_WORKFLOW_ERROR.VALIDATION_FAILED:
|
|
15968
|
-
errMessage = err.message || "Invalid branch name";
|
|
15969
|
-
break;
|
|
15970
|
-
default:
|
|
15971
|
-
errMessage = err.message || errMessage;
|
|
15972
|
-
break;
|
|
15973
|
-
}
|
|
15974
|
-
} else if (err.message) {
|
|
15975
|
-
if (err.message.toLowerCase().includes("already exists")) {
|
|
15976
|
-
errMessage = "A branch with this name already exists";
|
|
15977
|
-
} else if (err.message.toLowerCase().includes("conflict")) {
|
|
15978
|
-
errMessage = err.message;
|
|
15979
|
-
}
|
|
15980
|
-
}
|
|
16310
|
+
const errMessage = getEditorialWorkflowErrorMessage(e);
|
|
15981
16311
|
setErrorMessage(errMessage);
|
|
15982
16312
|
setIsExecuting(false);
|
|
15983
16313
|
setCurrentStep(0);
|
|
@@ -16065,6 +16395,18 @@ const WorkflowProgressIndicator = ({
|
|
|
16065
16395
|
"Learn more about Editorial Workflow"
|
|
16066
16396
|
));
|
|
16067
16397
|
};
|
|
16398
|
+
const EditorialWorkflowProgressModal = ({
|
|
16399
|
+
title,
|
|
16400
|
+
currentStep,
|
|
16401
|
+
elapsedTime
|
|
16402
|
+
}) => /* @__PURE__ */ React.createElement(Modal, { className: "flex" }, /* @__PURE__ */ React.createElement(PopupModal, { className: "w-auto" }, /* @__PURE__ */ React.createElement(ModalHeader, null, title), /* @__PURE__ */ React.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React.createElement(
|
|
16403
|
+
WorkflowProgressIndicator,
|
|
16404
|
+
{
|
|
16405
|
+
currentStep,
|
|
16406
|
+
isExecuting: true,
|
|
16407
|
+
elapsedTime
|
|
16408
|
+
}
|
|
16409
|
+
))));
|
|
16068
16410
|
const formatDefaultBranchName = (filePath, crudType) => {
|
|
16069
16411
|
let result = filePath;
|
|
16070
16412
|
const contentPrefix = "content/";
|
|
@@ -16096,6 +16438,7 @@ const CreateBranchModal = ({
|
|
|
16096
16438
|
formatDefaultBranchName(path, crudType)
|
|
16097
16439
|
);
|
|
16098
16440
|
const [isBranchGuardChecking, setIsBranchGuardChecking] = React.useState(false);
|
|
16441
|
+
const branchGuardAbortRef = React.useRef(null);
|
|
16099
16442
|
const {
|
|
16100
16443
|
isExecuting,
|
|
16101
16444
|
errorMessage,
|
|
@@ -16104,27 +16447,35 @@ const CreateBranchModal = ({
|
|
|
16104
16447
|
executeWorkflow,
|
|
16105
16448
|
reset
|
|
16106
16449
|
} = useEditorialWorkflow();
|
|
16450
|
+
const abortBranchGuard = React.useCallback(() => {
|
|
16451
|
+
var _a;
|
|
16452
|
+
(_a = branchGuardAbortRef.current) == null ? void 0 : _a.abort();
|
|
16453
|
+
branchGuardAbortRef.current = null;
|
|
16454
|
+
setIsBranchGuardChecking(false);
|
|
16455
|
+
}, []);
|
|
16456
|
+
React.useEffect(() => {
|
|
16457
|
+
return () => {
|
|
16458
|
+
var _a;
|
|
16459
|
+
(_a = branchGuardAbortRef.current) == null ? void 0 : _a.abort();
|
|
16460
|
+
};
|
|
16461
|
+
}, []);
|
|
16107
16462
|
const executeEditorialWorkflow = async () => {
|
|
16463
|
+
abortBranchGuard();
|
|
16464
|
+
const abortController = new AbortController();
|
|
16465
|
+
branchGuardAbortRef.current = abortController;
|
|
16108
16466
|
setIsBranchGuardChecking(true);
|
|
16109
16467
|
const baseBranch = decodeURIComponent(tinaApi.branch);
|
|
16110
|
-
|
|
16111
|
-
|
|
16112
|
-
|
|
16113
|
-
|
|
16114
|
-
|
|
16115
|
-
|
|
16116
|
-
baseBranchExists = await tinaApi.branchExists(baseBranch);
|
|
16117
|
-
} catch (err) {
|
|
16118
|
-
console.error(
|
|
16119
|
-
"[tina:branch-guard] executeEditorialWorkflow: branchExists threw, failing open:",
|
|
16120
|
-
err
|
|
16121
|
-
);
|
|
16122
|
-
}
|
|
16123
|
-
console.debug(
|
|
16124
|
-
"[tina:branch-guard] executeEditorialWorkflow: base branch exists?",
|
|
16125
|
-
baseBranchExists
|
|
16468
|
+
const targetBranch = `tina/${newBranchName}`;
|
|
16469
|
+
const baseBranchExists = await checkBaseBranchExists(
|
|
16470
|
+
tinaApi,
|
|
16471
|
+
baseBranch,
|
|
16472
|
+
"executeEditorialWorkflow",
|
|
16473
|
+
abortController.signal
|
|
16126
16474
|
);
|
|
16475
|
+
if (abortController.signal.aborted)
|
|
16476
|
+
return;
|
|
16127
16477
|
if (!baseBranchExists) {
|
|
16478
|
+
abortBranchGuard();
|
|
16128
16479
|
console.debug(
|
|
16129
16480
|
"[tina:branch-guard] executeEditorialWorkflow: base branch deleted — handing off"
|
|
16130
16481
|
);
|
|
@@ -16133,52 +16484,84 @@ const CreateBranchModal = ({
|
|
|
16133
16484
|
}
|
|
16134
16485
|
setIsBranchGuardChecking(false);
|
|
16135
16486
|
const success = await executeWorkflow({
|
|
16136
|
-
branchName:
|
|
16487
|
+
branchName: targetBranch,
|
|
16137
16488
|
baseBranch,
|
|
16138
16489
|
path,
|
|
16139
16490
|
values,
|
|
16140
16491
|
crudType,
|
|
16141
|
-
tinaForm
|
|
16492
|
+
tinaForm,
|
|
16493
|
+
signal: abortController.signal
|
|
16142
16494
|
});
|
|
16495
|
+
if (branchGuardAbortRef.current === abortController) {
|
|
16496
|
+
branchGuardAbortRef.current = null;
|
|
16497
|
+
}
|
|
16143
16498
|
if (success) {
|
|
16144
16499
|
close2();
|
|
16145
16500
|
}
|
|
16146
16501
|
};
|
|
16147
|
-
|
|
16148
|
-
|
|
16149
|
-
|
|
16150
|
-
|
|
16151
|
-
|
|
16152
|
-
|
|
16153
|
-
|
|
16154
|
-
|
|
16155
|
-
|
|
16156
|
-
|
|
16157
|
-
|
|
16158
|
-
|
|
16159
|
-
|
|
16160
|
-
|
|
16161
|
-
|
|
16162
|
-
|
|
16163
|
-
|
|
16164
|
-
|
|
16165
|
-
|
|
16166
|
-
|
|
16167
|
-
|
|
16168
|
-
|
|
16169
|
-
|
|
16170
|
-
|
|
16171
|
-
|
|
16172
|
-
|
|
16173
|
-
|
|
16174
|
-
|
|
16175
|
-
|
|
16176
|
-
|
|
16177
|
-
|
|
16178
|
-
));
|
|
16502
|
+
if (isExecuting) {
|
|
16503
|
+
return /* @__PURE__ */ React.createElement(
|
|
16504
|
+
EditorialWorkflowProgressModal,
|
|
16505
|
+
{
|
|
16506
|
+
title: "Save changes to new branch",
|
|
16507
|
+
currentStep,
|
|
16508
|
+
elapsedTime
|
|
16509
|
+
}
|
|
16510
|
+
);
|
|
16511
|
+
}
|
|
16512
|
+
return /* @__PURE__ */ React.createElement(
|
|
16513
|
+
CreateBranchPromptModal,
|
|
16514
|
+
{
|
|
16515
|
+
branchName: newBranchName,
|
|
16516
|
+
close: () => {
|
|
16517
|
+
abortBranchGuard();
|
|
16518
|
+
close2();
|
|
16519
|
+
},
|
|
16520
|
+
errorMessage,
|
|
16521
|
+
disabled: newBranchName === "" || isBranchGuardChecking,
|
|
16522
|
+
onBranchNameChange: (value) => {
|
|
16523
|
+
abortBranchGuard();
|
|
16524
|
+
reset();
|
|
16525
|
+
setNewBranchName(value);
|
|
16526
|
+
},
|
|
16527
|
+
onCreateBranch: executeEditorialWorkflow,
|
|
16528
|
+
onSaveToProtectedBranch: () => {
|
|
16529
|
+
abortBranchGuard();
|
|
16530
|
+
close2();
|
|
16531
|
+
safeSubmit();
|
|
16532
|
+
}
|
|
16179
16533
|
}
|
|
16180
|
-
|
|
16181
|
-
|
|
16534
|
+
);
|
|
16535
|
+
};
|
|
16536
|
+
const CreateBranchPromptModal = ({
|
|
16537
|
+
branchName,
|
|
16538
|
+
close: close2,
|
|
16539
|
+
disabled,
|
|
16540
|
+
errorMessage,
|
|
16541
|
+
onBranchNameChange,
|
|
16542
|
+
onCreateBranch,
|
|
16543
|
+
onSaveToProtectedBranch
|
|
16544
|
+
}) => {
|
|
16545
|
+
return /* @__PURE__ */ React.createElement(Modal, { className: "flex" }, /* @__PURE__ */ React.createElement(PopupModal, { className: "w-auto" }, /* @__PURE__ */ React.createElement(ModalHeader, { close: close2 }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center justify-between w-full" }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center" }, "Save changes to new branch"))), /* @__PURE__ */ React.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React.createElement("div", { className: "max-w-sm" }, errorMessage && /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1 text-red-700 py-2 px-3 mb-4 bg-red-50 border border-red-200 rounded" }, /* @__PURE__ */ React.createElement(BiError, { className: "w-5 h-auto text-red-400 flex-shrink-0" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm" }, /* @__PURE__ */ React.createElement("b", null, "Error:"), " ", errorMessage)), /* @__PURE__ */ React.createElement("p", { className: "text-lg text-gray-700 font-bold mb-2" }, "First, let's create a copy"), /* @__PURE__ */ React.createElement("p", { className: "text-sm text-gray-700 mb-4 max-w-sm" }, "To make changes, you need to create a copy then get it approved and merged for it to go live.", /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("br", null), /* @__PURE__ */ React.createElement("span", { className: "text-gray-500" }, "Learn more about "), /* @__PURE__ */ React.createElement(
|
|
16546
|
+
"a",
|
|
16547
|
+
{
|
|
16548
|
+
className: "underline text-tina-orange-dark font-medium",
|
|
16549
|
+
href: "https://tina.io/docs/r/editorial-workflow",
|
|
16550
|
+
target: "_blank"
|
|
16551
|
+
},
|
|
16552
|
+
"Editorial Workflow"
|
|
16553
|
+
), "."), /* @__PURE__ */ React.createElement(
|
|
16554
|
+
PrefixedTextField,
|
|
16555
|
+
{
|
|
16556
|
+
name: "new-branch-name",
|
|
16557
|
+
label: "Branch Name",
|
|
16558
|
+
placeholder: "e.g. {{PAGE-NAME}}-updates",
|
|
16559
|
+
value: branchName,
|
|
16560
|
+
onChange: (e) => {
|
|
16561
|
+
onBranchNameChange(e.target.value);
|
|
16562
|
+
}
|
|
16563
|
+
}
|
|
16564
|
+
))), /* @__PURE__ */ React.createElement(ModalActions, { align: "end" }, /* @__PURE__ */ React.createElement(
|
|
16182
16565
|
Button$2,
|
|
16183
16566
|
{
|
|
16184
16567
|
variant: "secondary",
|
|
@@ -16192,26 +16575,17 @@ const CreateBranchModal = ({
|
|
|
16192
16575
|
variant: "primary",
|
|
16193
16576
|
align: "start",
|
|
16194
16577
|
className: "w-full sm:w-auto",
|
|
16195
|
-
disabled
|
|
16196
|
-
onMainAction:
|
|
16578
|
+
disabled,
|
|
16579
|
+
onMainAction: onCreateBranch,
|
|
16197
16580
|
items: [
|
|
16198
16581
|
{
|
|
16199
16582
|
label: "Save to Protected Branch",
|
|
16200
|
-
onClick:
|
|
16201
|
-
close2();
|
|
16202
|
-
safeSubmit();
|
|
16203
|
-
},
|
|
16583
|
+
onClick: onSaveToProtectedBranch,
|
|
16204
16584
|
icon: /* @__PURE__ */ React.createElement(TriangleAlert, { className: "w-4 h-4" })
|
|
16205
16585
|
}
|
|
16206
16586
|
]
|
|
16207
16587
|
},
|
|
16208
|
-
/* @__PURE__ */ React.createElement(
|
|
16209
|
-
GitBranchIcon,
|
|
16210
|
-
{
|
|
16211
|
-
className: "w-4 h-4 mr-1",
|
|
16212
|
-
style: { fill: "none" }
|
|
16213
|
-
}
|
|
16214
|
-
),
|
|
16588
|
+
/* @__PURE__ */ React.createElement(GitBranchIcon, { className: "w-4 h-4 mr-1", style: { fill: "none" } }),
|
|
16215
16589
|
"Save to a new branch"
|
|
16216
16590
|
))));
|
|
16217
16591
|
};
|
|
@@ -69994,6 +70368,191 @@ function useLocalStorage(key, initialValue) {
|
|
|
69994
70368
|
};
|
|
69995
70369
|
return [storedValue, setValue];
|
|
69996
70370
|
}
|
|
70371
|
+
const MediaWorkflowOverlay = () => {
|
|
70372
|
+
const cms = useCMS$1();
|
|
70373
|
+
const { setCurrentBranch } = useBranchData();
|
|
70374
|
+
const [state, setState] = React.useState({ phase: "idle" });
|
|
70375
|
+
const preflightAbortRef = React.useRef(null);
|
|
70376
|
+
const abortPreflight = React.useCallback(() => {
|
|
70377
|
+
var _a;
|
|
70378
|
+
(_a = preflightAbortRef.current) == null ? void 0 : _a.abort();
|
|
70379
|
+
preflightAbortRef.current = null;
|
|
70380
|
+
}, []);
|
|
70381
|
+
React.useEffect(() => {
|
|
70382
|
+
const offConfirm = cms.events.subscribe(
|
|
70383
|
+
"media:workflow:confirm-branch",
|
|
70384
|
+
(event) => {
|
|
70385
|
+
abortPreflight();
|
|
70386
|
+
setState({
|
|
70387
|
+
phase: "confirming",
|
|
70388
|
+
branchName: event.branchName,
|
|
70389
|
+
baseBranch: event.baseBranch,
|
|
70390
|
+
onConfirm: event.onConfirm,
|
|
70391
|
+
onCancel: event.onCancel,
|
|
70392
|
+
onSaveToProtectedBranch: event.onSaveToProtectedBranch
|
|
70393
|
+
});
|
|
70394
|
+
}
|
|
70395
|
+
);
|
|
70396
|
+
const offStart = cms.events.subscribe("media:workflow:start", () => {
|
|
70397
|
+
setState({ phase: "executing", step: 1, elapsed: 0 });
|
|
70398
|
+
});
|
|
70399
|
+
const offStep = cms.events.subscribe(
|
|
70400
|
+
"media:workflow:step",
|
|
70401
|
+
(event) => {
|
|
70402
|
+
setState(
|
|
70403
|
+
(prev) => prev.phase === "executing" ? { ...prev, step: event.step } : { phase: "executing", step: event.step, elapsed: 0 }
|
|
70404
|
+
);
|
|
70405
|
+
}
|
|
70406
|
+
);
|
|
70407
|
+
const offComplete = cms.events.subscribe("media:workflow:complete", (event) => {
|
|
70408
|
+
setCurrentBranch(event.branchName);
|
|
70409
|
+
});
|
|
70410
|
+
const offError = cms.events.subscribe(
|
|
70411
|
+
"media:workflow:error",
|
|
70412
|
+
(event) => {
|
|
70413
|
+
setState({ phase: "error", message: event.message });
|
|
70414
|
+
}
|
|
70415
|
+
);
|
|
70416
|
+
const offFinish = cms.events.subscribe("media:workflow:finish", () => {
|
|
70417
|
+
setState({ phase: "idle" });
|
|
70418
|
+
});
|
|
70419
|
+
return () => {
|
|
70420
|
+
offConfirm();
|
|
70421
|
+
offStart();
|
|
70422
|
+
offStep();
|
|
70423
|
+
offComplete();
|
|
70424
|
+
offError();
|
|
70425
|
+
offFinish();
|
|
70426
|
+
abortPreflight();
|
|
70427
|
+
};
|
|
70428
|
+
}, [abortPreflight, cms, setCurrentBranch]);
|
|
70429
|
+
React.useEffect(() => {
|
|
70430
|
+
if (state.phase !== "executing")
|
|
70431
|
+
return;
|
|
70432
|
+
const interval = setInterval(() => {
|
|
70433
|
+
setState(
|
|
70434
|
+
(prev) => prev.phase === "executing" ? { ...prev, elapsed: prev.elapsed + 1 } : prev
|
|
70435
|
+
);
|
|
70436
|
+
}, 1e3);
|
|
70437
|
+
return () => clearInterval(interval);
|
|
70438
|
+
}, [state.phase]);
|
|
70439
|
+
const handleCreateBranch = async () => {
|
|
70440
|
+
if (state.phase !== "confirming")
|
|
70441
|
+
return;
|
|
70442
|
+
const confirmState = state;
|
|
70443
|
+
const branchName = confirmState.branchName;
|
|
70444
|
+
const targetBranch = `tina/${branchName}`;
|
|
70445
|
+
abortPreflight();
|
|
70446
|
+
const abortController = new AbortController();
|
|
70447
|
+
preflightAbortRef.current = abortController;
|
|
70448
|
+
setState({
|
|
70449
|
+
...confirmState,
|
|
70450
|
+
isChecking: true,
|
|
70451
|
+
errorMessage: ""
|
|
70452
|
+
});
|
|
70453
|
+
const baseBranchExists = await checkBaseBranchExists(
|
|
70454
|
+
cms.api.tina,
|
|
70455
|
+
confirmState.baseBranch,
|
|
70456
|
+
"media workflow",
|
|
70457
|
+
abortController.signal
|
|
70458
|
+
);
|
|
70459
|
+
if (abortController.signal.aborted)
|
|
70460
|
+
return;
|
|
70461
|
+
if (!baseBranchExists) {
|
|
70462
|
+
if (preflightAbortRef.current === abortController) {
|
|
70463
|
+
preflightAbortRef.current = null;
|
|
70464
|
+
}
|
|
70465
|
+
setState({
|
|
70466
|
+
...confirmState,
|
|
70467
|
+
branchName,
|
|
70468
|
+
isChecking: false,
|
|
70469
|
+
errorMessage: `The branch ${confirmState.baseBranch} no longer exists. It may have been merged or deleted. Your changes cannot be pushed to it.`
|
|
70470
|
+
});
|
|
70471
|
+
return;
|
|
70472
|
+
}
|
|
70473
|
+
const targetBranchExists = await checkTargetBranchExists(
|
|
70474
|
+
cms.api.tina,
|
|
70475
|
+
targetBranch,
|
|
70476
|
+
"media workflow",
|
|
70477
|
+
abortController.signal
|
|
70478
|
+
);
|
|
70479
|
+
if (abortController.signal.aborted)
|
|
70480
|
+
return;
|
|
70481
|
+
if (targetBranchExists) {
|
|
70482
|
+
if (preflightAbortRef.current === abortController) {
|
|
70483
|
+
preflightAbortRef.current = null;
|
|
70484
|
+
}
|
|
70485
|
+
setState({
|
|
70486
|
+
...confirmState,
|
|
70487
|
+
branchName,
|
|
70488
|
+
isChecking: false,
|
|
70489
|
+
errorMessage: TARGET_BRANCH_EXISTS_ERROR
|
|
70490
|
+
});
|
|
70491
|
+
return;
|
|
70492
|
+
}
|
|
70493
|
+
try {
|
|
70494
|
+
if (preflightAbortRef.current === abortController) {
|
|
70495
|
+
preflightAbortRef.current = null;
|
|
70496
|
+
}
|
|
70497
|
+
setState({ phase: "executing", step: 1, elapsed: 0 });
|
|
70498
|
+
await confirmState.onConfirm(targetBranch);
|
|
70499
|
+
} catch (e) {
|
|
70500
|
+
console.error(e);
|
|
70501
|
+
setState({
|
|
70502
|
+
...confirmState,
|
|
70503
|
+
branchName,
|
|
70504
|
+
isChecking: false,
|
|
70505
|
+
errorMessage: getEditorialWorkflowErrorMessage(e)
|
|
70506
|
+
});
|
|
70507
|
+
}
|
|
70508
|
+
};
|
|
70509
|
+
if (state.phase === "idle")
|
|
70510
|
+
return null;
|
|
70511
|
+
if (state.phase === "confirming") {
|
|
70512
|
+
return /* @__PURE__ */ React.createElement(
|
|
70513
|
+
CreateBranchPromptModal,
|
|
70514
|
+
{
|
|
70515
|
+
branchName: state.branchName,
|
|
70516
|
+
close: () => {
|
|
70517
|
+
abortPreflight();
|
|
70518
|
+
state.onCancel();
|
|
70519
|
+
setState({ phase: "idle" });
|
|
70520
|
+
},
|
|
70521
|
+
disabled: state.branchName === "" || state.isChecking,
|
|
70522
|
+
errorMessage: state.errorMessage,
|
|
70523
|
+
onBranchNameChange: (branchName) => {
|
|
70524
|
+
abortPreflight();
|
|
70525
|
+
setState(
|
|
70526
|
+
(prev) => prev.phase === "confirming" ? {
|
|
70527
|
+
...prev,
|
|
70528
|
+
branchName,
|
|
70529
|
+
errorMessage: void 0,
|
|
70530
|
+
isChecking: false
|
|
70531
|
+
} : prev
|
|
70532
|
+
);
|
|
70533
|
+
},
|
|
70534
|
+
onCreateBranch: handleCreateBranch,
|
|
70535
|
+
onSaveToProtectedBranch: () => {
|
|
70536
|
+
abortPreflight();
|
|
70537
|
+
state.onSaveToProtectedBranch();
|
|
70538
|
+
setState({ phase: "idle" });
|
|
70539
|
+
}
|
|
70540
|
+
}
|
|
70541
|
+
);
|
|
70542
|
+
}
|
|
70543
|
+
if (state.phase === "executing") {
|
|
70544
|
+
return /* @__PURE__ */ React.createElement(
|
|
70545
|
+
EditorialWorkflowProgressModal,
|
|
70546
|
+
{
|
|
70547
|
+
title: "Save changes to new branch",
|
|
70548
|
+
currentStep: state.step,
|
|
70549
|
+
elapsedTime: state.elapsed
|
|
70550
|
+
}
|
|
70551
|
+
);
|
|
70552
|
+
}
|
|
70553
|
+
const dismissError = () => setState({ phase: "idle" });
|
|
70554
|
+
return /* @__PURE__ */ React.createElement(Modal, { className: "flex" }, /* @__PURE__ */ React.createElement(PopupModal, { className: "w-auto" }, /* @__PURE__ */ React.createElement(ModalHeader, { close: dismissError }, "Branch creation failed"), /* @__PURE__ */ React.createElement(ModalBody, { padded: true }, /* @__PURE__ */ React.createElement("div", { className: "flex items-center gap-1 text-red-700 py-2 px-3 bg-red-50 border border-red-200 rounded max-w-sm" }, /* @__PURE__ */ React.createElement(BiError, { className: "w-5 h-auto text-red-400 flex-shrink-0" }), /* @__PURE__ */ React.createElement("span", { className: "text-sm" }, /* @__PURE__ */ React.createElement("b", null, "Error:"), " ", state.message)))));
|
|
70555
|
+
};
|
|
69997
70556
|
function asyncPoll(fn, pollInterval = 5 * 1e3, pollTimeout = 30 * 1e3) {
|
|
69998
70557
|
const endTime = (/* @__PURE__ */ new Date()).getTime() + pollTimeout;
|
|
69999
70558
|
let stop = false;
|
|
@@ -70553,7 +71112,7 @@ mutation addPendingDocumentMutation(
|
|
|
70553
71112
|
}
|
|
70554
71113
|
});
|
|
70555
71114
|
if (!res.ok) {
|
|
70556
|
-
let errorMessage = `There was an error creating a
|
|
71115
|
+
let errorMessage = `There was an error creating a pull request. ${res.statusText}`;
|
|
70557
71116
|
if (res.status === 422) {
|
|
70558
71117
|
errorMessage = `Please make sure you have made changes on ${branch} before creating a pull request.`;
|
|
70559
71118
|
}
|
|
@@ -70562,7 +71121,7 @@ mutation addPendingDocumentMutation(
|
|
|
70562
71121
|
const values = await res.json();
|
|
70563
71122
|
return values;
|
|
70564
71123
|
} catch (error2) {
|
|
70565
|
-
console.error("There was an error creating a
|
|
71124
|
+
console.error("There was an error creating a pull request.", error2);
|
|
70566
71125
|
throw error2;
|
|
70567
71126
|
}
|
|
70568
71127
|
}
|
|
@@ -70659,7 +71218,8 @@ mutation addPendingDocumentMutation(
|
|
|
70659
71218
|
try {
|
|
70660
71219
|
const url = `${this.contentApiBase}/github/${this.clientId}/list_branches`;
|
|
70661
71220
|
const res = await this.authProvider.fetchWithToken(url, {
|
|
70662
|
-
method: "GET"
|
|
71221
|
+
method: "GET",
|
|
71222
|
+
signal: args == null ? void 0 : args.signal
|
|
70663
71223
|
});
|
|
70664
71224
|
const branches = await res.json();
|
|
70665
71225
|
const parsedBranches = await ListBranchResponse.parseAsync(branches);
|
|
@@ -70685,10 +71245,13 @@ mutation addPendingDocumentMutation(
|
|
|
70685
71245
|
var _a;
|
|
70686
71246
|
return this.usingEditorialWorkflow && ((_a = this.protectedBranches) == null ? void 0 : _a.includes(decodeURIComponent(this.branch)));
|
|
70687
71247
|
}
|
|
70688
|
-
async branchExists(branchName) {
|
|
71248
|
+
async branchExists(branchName, args) {
|
|
70689
71249
|
if (this.isLocalMode)
|
|
70690
71250
|
return true;
|
|
70691
|
-
const branches = await this.listBranches({
|
|
71251
|
+
const branches = await this.listBranches({
|
|
71252
|
+
includeIndexStatus: false,
|
|
71253
|
+
signal: args == null ? void 0 : args.signal
|
|
71254
|
+
});
|
|
70692
71255
|
return branches.some((b) => b.name === branchName);
|
|
70693
71256
|
}
|
|
70694
71257
|
async createBranch({ baseBranch, branchName }) {
|
|
@@ -70732,6 +71295,89 @@ mutation addPendingDocumentMutation(
|
|
|
70732
71295
|
throw error2;
|
|
70733
71296
|
}
|
|
70734
71297
|
}
|
|
71298
|
+
async pollEditorialWorkflowStatus(requestId, onStatusUpdate) {
|
|
71299
|
+
const pollInterval = 5e3;
|
|
71300
|
+
const maxAttempts = 180;
|
|
71301
|
+
let attempts = 0;
|
|
71302
|
+
while (attempts < maxAttempts) {
|
|
71303
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
71304
|
+
attempts++;
|
|
71305
|
+
try {
|
|
71306
|
+
const statusUrl = `${this.contentApiBase}/editorial-workflow/${this.clientId}/status/${requestId}`;
|
|
71307
|
+
const statusResponse = await this.authProvider.fetchWithToken(statusUrl);
|
|
71308
|
+
const statusResponseBody = await statusResponse.json();
|
|
71309
|
+
onStatusUpdate == null ? void 0 : onStatusUpdate({
|
|
71310
|
+
status: statusResponseBody.status,
|
|
71311
|
+
message: statusResponseBody.message || `Status: ${statusResponseBody.status}`
|
|
71312
|
+
});
|
|
71313
|
+
if (statusResponseBody.status === EDITORIAL_WORKFLOW_STATUS.ERROR || statusResponse.status === 500) {
|
|
71314
|
+
if (statusResponseBody.pullRequestUrl) {
|
|
71315
|
+
return {
|
|
71316
|
+
branchName: statusResponseBody.branchName,
|
|
71317
|
+
pullRequestUrl: statusResponseBody.pullRequestUrl,
|
|
71318
|
+
warning: statusResponseBody.message
|
|
71319
|
+
};
|
|
71320
|
+
}
|
|
71321
|
+
const error2 = new Error(
|
|
71322
|
+
statusResponseBody.message || "Editorial workflow failed"
|
|
71323
|
+
);
|
|
71324
|
+
error2.errorCode = statusResponseBody.errorCode || "WORKFLOW_FAILED";
|
|
71325
|
+
throw error2;
|
|
71326
|
+
}
|
|
71327
|
+
if (statusResponse.status === 200) {
|
|
71328
|
+
return {
|
|
71329
|
+
branchName: statusResponseBody.branchName,
|
|
71330
|
+
pullRequestUrl: statusResponseBody.pullRequestUrl
|
|
71331
|
+
};
|
|
71332
|
+
}
|
|
71333
|
+
if (statusResponse.status !== 202) {
|
|
71334
|
+
const error2 = new Error(
|
|
71335
|
+
statusResponseBody.message || `Failed to check workflow status: ${statusResponse.statusText}`
|
|
71336
|
+
);
|
|
71337
|
+
error2.errorCode = "WORKFLOW_STATUS_FAILED";
|
|
71338
|
+
throw error2;
|
|
71339
|
+
}
|
|
71340
|
+
} catch (error2) {
|
|
71341
|
+
if (error2.errorCode) {
|
|
71342
|
+
throw error2;
|
|
71343
|
+
}
|
|
71344
|
+
console.warn(
|
|
71345
|
+
`Editorial workflow status poll failed (attempt ${attempts}/${maxAttempts}), retrying...`,
|
|
71346
|
+
error2
|
|
71347
|
+
);
|
|
71348
|
+
}
|
|
71349
|
+
}
|
|
71350
|
+
const timeoutMinutes = Math.round(maxAttempts * pollInterval / 6e4);
|
|
71351
|
+
throw new Error(
|
|
71352
|
+
`Editorial workflow timed out after ${timeoutMinutes} minutes. It may still be completing in the background — please wait before retrying.`
|
|
71353
|
+
);
|
|
71354
|
+
}
|
|
71355
|
+
toEditorialWorkflowError(responseBody, fallbackMessage) {
|
|
71356
|
+
const error2 = new Error(
|
|
71357
|
+
(responseBody == null ? void 0 : responseBody.message) || fallbackMessage
|
|
71358
|
+
);
|
|
71359
|
+
if (responseBody == null ? void 0 : responseBody.errorCode) {
|
|
71360
|
+
error2.errorCode = responseBody.errorCode;
|
|
71361
|
+
}
|
|
71362
|
+
if (responseBody == null ? void 0 : responseBody.conflictingBranch) {
|
|
71363
|
+
error2.conflictingBranch = responseBody.conflictingBranch;
|
|
71364
|
+
}
|
|
71365
|
+
return error2;
|
|
71366
|
+
}
|
|
71367
|
+
async postEditorialWorkflow(url, body, errorFallback) {
|
|
71368
|
+
const res = await this.authProvider.fetchWithToken(url, {
|
|
71369
|
+
method: "POST",
|
|
71370
|
+
body: JSON.stringify(body),
|
|
71371
|
+
headers: {
|
|
71372
|
+
"Content-Type": "application/json"
|
|
71373
|
+
}
|
|
71374
|
+
});
|
|
71375
|
+
const responseBody = await res.json();
|
|
71376
|
+
if (!res.ok) {
|
|
71377
|
+
throw this.toEditorialWorkflowError(responseBody, errorFallback);
|
|
71378
|
+
}
|
|
71379
|
+
return responseBody;
|
|
71380
|
+
}
|
|
70735
71381
|
/**
|
|
70736
71382
|
* Initiate and poll for the results of an editorial workflow operation
|
|
70737
71383
|
*
|
|
@@ -70741,32 +71387,16 @@ mutation addPendingDocumentMutation(
|
|
|
70741
71387
|
async executeEditorialWorkflow(options) {
|
|
70742
71388
|
const url = `${this.contentApiBase}/editorial-workflow/${this.clientId}`;
|
|
70743
71389
|
try {
|
|
70744
|
-
const
|
|
70745
|
-
|
|
70746
|
-
|
|
71390
|
+
const responseBody = await this.postEditorialWorkflow(
|
|
71391
|
+
url,
|
|
71392
|
+
{
|
|
70747
71393
|
branchName: options.branchName,
|
|
70748
71394
|
baseBranch: options.baseBranch,
|
|
70749
71395
|
prTitle: options.prTitle,
|
|
70750
71396
|
graphQLContentOp: options.graphQLContentOp
|
|
70751
|
-
}
|
|
70752
|
-
|
|
70753
|
-
|
|
70754
|
-
}
|
|
70755
|
-
});
|
|
70756
|
-
const responseBody = await res.json();
|
|
70757
|
-
if (!res.ok) {
|
|
70758
|
-
console.error("There was an error starting editorial workflow.");
|
|
70759
|
-
const error2 = new Error(
|
|
70760
|
-
(responseBody == null ? void 0 : responseBody.message) || "Failed to start editorial workflow"
|
|
70761
|
-
);
|
|
70762
|
-
if (responseBody == null ? void 0 : responseBody.errorCode) {
|
|
70763
|
-
error2.errorCode = responseBody.errorCode;
|
|
70764
|
-
}
|
|
70765
|
-
if (responseBody == null ? void 0 : responseBody.conflictingBranch) {
|
|
70766
|
-
error2.conflictingBranch = responseBody.conflictingBranch;
|
|
70767
|
-
}
|
|
70768
|
-
throw error2;
|
|
70769
|
-
}
|
|
71397
|
+
},
|
|
71398
|
+
"Failed to start editorial workflow"
|
|
71399
|
+
);
|
|
70770
71400
|
const requestId = responseBody.requestId;
|
|
70771
71401
|
if (!requestId) {
|
|
70772
71402
|
return responseBody;
|
|
@@ -70777,60 +71407,10 @@ mutation addPendingDocumentMutation(
|
|
|
70777
71407
|
message: "Workflow queued, starting..."
|
|
70778
71408
|
});
|
|
70779
71409
|
}
|
|
70780
|
-
|
|
70781
|
-
|
|
70782
|
-
|
|
70783
|
-
|
|
70784
|
-
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
70785
|
-
attempts++;
|
|
70786
|
-
try {
|
|
70787
|
-
const statusUrl = `${this.contentApiBase}/editorial-workflow/${this.clientId}/status/${requestId}`;
|
|
70788
|
-
const statusResponse = await this.authProvider.fetchWithToken(statusUrl);
|
|
70789
|
-
const statusResponseBody = await statusResponse.json();
|
|
70790
|
-
if (options.onStatusUpdate) {
|
|
70791
|
-
options.onStatusUpdate({
|
|
70792
|
-
status: statusResponseBody.status,
|
|
70793
|
-
message: statusResponseBody.message || `Status: ${statusResponseBody.status}`
|
|
70794
|
-
});
|
|
70795
|
-
}
|
|
70796
|
-
if (statusResponseBody.status === EDITORIAL_WORKFLOW_STATUS.ERROR || statusResponse.status === 500) {
|
|
70797
|
-
if (statusResponseBody.pullRequestUrl) {
|
|
70798
|
-
return {
|
|
70799
|
-
branchName: statusResponseBody.branchName,
|
|
70800
|
-
pullRequestUrl: statusResponseBody.pullRequestUrl,
|
|
70801
|
-
warning: statusResponseBody.message
|
|
70802
|
-
};
|
|
70803
|
-
}
|
|
70804
|
-
const error2 = new Error(
|
|
70805
|
-
statusResponseBody.message || "Editorial workflow failed"
|
|
70806
|
-
);
|
|
70807
|
-
error2.errorCode = statusResponseBody.errorCode || "WORKFLOW_FAILED";
|
|
70808
|
-
throw error2;
|
|
70809
|
-
}
|
|
70810
|
-
if (statusResponse.status === 200) {
|
|
70811
|
-
return {
|
|
70812
|
-
branchName: statusResponseBody.branchName,
|
|
70813
|
-
pullRequestUrl: statusResponseBody.pullRequestUrl
|
|
70814
|
-
};
|
|
70815
|
-
}
|
|
70816
|
-
if (statusResponse.status !== 202) {
|
|
70817
|
-
const error2 = new Error(
|
|
70818
|
-
statusResponseBody.message || `Failed to check workflow status: ${statusResponse.statusText}`
|
|
70819
|
-
);
|
|
70820
|
-
error2.errorCode = "WORKFLOW_STATUS_FAILED";
|
|
70821
|
-
throw error2;
|
|
70822
|
-
}
|
|
70823
|
-
} catch (error2) {
|
|
70824
|
-
if (error2.errorCode) {
|
|
70825
|
-
throw error2;
|
|
70826
|
-
}
|
|
70827
|
-
console.warn(
|
|
70828
|
-
`Editorial workflow status poll failed (attempt ${attempts}/${maxAttempts}), retrying...`,
|
|
70829
|
-
error2
|
|
70830
|
-
);
|
|
70831
|
-
}
|
|
70832
|
-
}
|
|
70833
|
-
throw new Error("Editorial workflow timed out after 5 minutes");
|
|
71410
|
+
return await this.pollEditorialWorkflowStatus(
|
|
71411
|
+
requestId,
|
|
71412
|
+
options.onStatusUpdate
|
|
71413
|
+
);
|
|
70834
71414
|
} catch (error2) {
|
|
70835
71415
|
console.error(
|
|
70836
71416
|
"There was an error with editorial workflow operation.",
|
|
@@ -70839,6 +71419,17 @@ mutation addPendingDocumentMutation(
|
|
|
70839
71419
|
throw error2;
|
|
70840
71420
|
}
|
|
70841
71421
|
}
|
|
71422
|
+
async startMediaEditorialWorkflow(options) {
|
|
71423
|
+
const url = `${this.contentApiBase}/editorial-workflow/${this.clientId}/media`;
|
|
71424
|
+
return await this.postEditorialWorkflow(
|
|
71425
|
+
url,
|
|
71426
|
+
options,
|
|
71427
|
+
"Failed to start media editorial workflow"
|
|
71428
|
+
);
|
|
71429
|
+
}
|
|
71430
|
+
async waitForEditorialWorkflowStatus(requestId, onStatusUpdate) {
|
|
71431
|
+
return await this.pollEditorialWorkflowStatus(requestId, onStatusUpdate);
|
|
71432
|
+
}
|
|
70842
71433
|
}
|
|
70843
71434
|
const DEFAULT_LOCAL_TINA_GQL_SERVER_URL = "http://localhost:4001/graphql";
|
|
70844
71435
|
class LocalClient extends Client {
|
|
@@ -71392,7 +71983,7 @@ const TinaCloudProvider = (props) => {
|
|
|
71392
71983
|
}, []);
|
|
71393
71984
|
React__default.useEffect(() => {
|
|
71394
71985
|
const setupEditorialWorkflow = () => {
|
|
71395
|
-
client.getProject().then((project) => {
|
|
71986
|
+
client.getProject().then(async (project) => {
|
|
71396
71987
|
var _a2;
|
|
71397
71988
|
if ((_a2 = project == null ? void 0 : project.features) == null ? void 0 : _a2.includes("editorial-workflow")) {
|
|
71398
71989
|
cms.flags.set("branch-switcher", true);
|
|
@@ -71422,7 +72013,7 @@ const TinaCloudProvider = (props) => {
|
|
|
71422
72013
|
setCurrentBranch(b);
|
|
71423
72014
|
}
|
|
71424
72015
|
},
|
|
71425
|
-
/* @__PURE__ */ React__default.createElement(TinaProvider, { cms }, /* @__PURE__ */ React__default.createElement(AuthWallInner, { ...props, cms }))
|
|
72016
|
+
/* @__PURE__ */ React__default.createElement(TinaProvider, { cms }, /* @__PURE__ */ React__default.createElement(MediaWorkflowOverlay, null), /* @__PURE__ */ React__default.createElement(AuthWallInner, { ...props, cms }))
|
|
71426
72017
|
));
|
|
71427
72018
|
};
|
|
71428
72019
|
const TinaCloudAuthWall = TinaCloudProvider;
|
|
@@ -74457,6 +75048,7 @@ export {
|
|
|
74457
75048
|
ColorPicker,
|
|
74458
75049
|
CreateBranchModal,
|
|
74459
75050
|
CreateBranchModel,
|
|
75051
|
+
CreateBranchPromptModal,
|
|
74460
75052
|
CursorPaginator,
|
|
74461
75053
|
DEFAULT_LOCAL_TINA_GQL_SERVER_URL,
|
|
74462
75054
|
DEFAULT_MEDIA_UPLOAD_TYPES,
|
|
@@ -74535,6 +75127,7 @@ export {
|
|
|
74535
75127
|
MediaIcon,
|
|
74536
75128
|
MediaListError,
|
|
74537
75129
|
MediaManager$1 as MediaManager,
|
|
75130
|
+
MediaWorkflowOverlay,
|
|
74538
75131
|
Message,
|
|
74539
75132
|
Modal,
|
|
74540
75133
|
ModalActions,
|