comfy-pr 0.2.24 → 0.2.26
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/README.md +247 -225
- package/index.ts +6 -0
- package/next-env.d.ts +5 -5
- package/package.json +52 -25
- package/src/Authors.ts +42 -48
- package/src/CMNodes.ts +60 -60
- package/src/CNRepos.ts +78 -74
- package/src/CRNodes.ts +27 -27
- package/src/CRPulls.ts +4 -4
- package/src/EmailTasks.ts +101 -0
- package/src/FORK_OWNER.ts +5 -5
- package/src/FORK_PREFIX.ts +5 -5
- package/src/FollowRules.ts +25 -25
- package/src/GIT_USEREMAIL.ts +5 -5
- package/src/GIT_USERNAME.ts +9 -9
- package/src/GithubIssueComments.ts +3 -3
- package/src/PushedBranch.ts +3 -3
- package/src/Totals.ts +24 -9
- package/src/TrackingPRs.ts +12 -12
- package/src/WorkerInstances.ts +89 -89
- package/src/addCommentAction.ts +73 -65
- package/src/analyzePullsStatus.ts +219 -170
- package/src/analyzeTotals.test.ts +5 -5
- package/src/analyzeTotals.ts +118 -118
- package/src/bypassRepos.spec.ts +10 -0
- package/src/bypassRepos.ts +8 -0
- package/src/checkComfyActivated.ts +39 -39
- package/src/checkPRsFailures.ts +18 -0
- package/src/cli.ts +40 -40
- package/src/clone_modify_push_Branches.ts +21 -21
- package/src/createComfyRegistryPRsFromCandidates.ts +55 -55
- package/src/createComfyRegistryPullRequests.ts +32 -22
- package/src/createGithubForkForRepo.ts +54 -37
- package/src/createGithubPullRequest.ts +157 -94
- package/src/createIssueComment.ts +34 -29
- package/src/fetchCMNodes.ts +22 -22
- package/src/fetchComfyRegistryNodes.ts +11 -11
- package/src/fetchCurrentGeoInfo.ts +6 -6
- package/src/fetchRelatedPullWithComments.ts +15 -15
- package/src/fetchRepoDescriptionMap.ts +15 -15
- package/src/followRuleSchema.ts +79 -76
- package/src/getActivatedShell.ts +18 -18
- package/src/getBranchWorkingDir.ts +16 -16
- package/src/getRepoWorkingDir.ts +5 -5
- package/src/ghUser.ts +9 -6
- package/src/index.ts +26 -22
- package/src/initializeFollowRules.ts +26 -26
- package/src/makePublishBranch.ts +58 -58
- package/src/makeTomlBranch.ts +53 -53
- package/src/makeUpdateTomlLicenseBranch.ts +245 -0
- package/src/matchRelatedPulls.ts +47 -60
- package/src/muteInstances.ts +16 -16
- package/src/parseIssueUrl.ts +6 -6
- package/src/parseOwnerRepo.ts +33 -33
- package/src/parsePullUrl.ts +6 -6
- package/src/parsePullsState.ts +6 -6
- package/src/parseTitleBodyOfMarkdown.ts +8 -8
- package/src/postSlackMessage.ts +21 -21
- package/src/preload.ts +30 -30
- package/src/pullStatusFollowSchema.ts +12 -12
- package/src/readTemplateTitle.ts +11 -11
- package/src/sendEmailAction.ts +56 -0
- package/src/sendGmail.ts +105 -0
- package/src/showFollowRuleSet.ts +12 -12
- package/src/summaryLastPullComment.ts +8 -8
- package/src/tomlFillDescription.ts +17 -17
- package/src/updateAuthors.ts +7 -7
- package/src/updateAuthorsForGithub.ts +54 -50
- package/src/updateAuthorsFromCNRepo.ts +30 -30
- package/src/updateCMNodesDuplicationWarnings.ts +56 -56
- package/src/updateCMRepos.ts +27 -27
- package/src/updateCNRepos.ts +58 -58
- package/src/updateCNReposCRPullsComments.ts +40 -40
- package/src/updateCNReposInfo.ts +58 -57
- package/src/updateCNReposPRCandidate.ts +85 -85
- package/src/updateCNReposPulls.ts +31 -28
- package/src/updateCNReposPullsDashboard.ts +49 -49
- package/src/updateCNReposRelatedPulls.ts +25 -25
- package/src/updateCRNodes.ts +50 -50
- package/src/updateCRRepos.ts +26 -26
- package/src/updateComfyTotals.ts +43 -43
- package/src/updateFollowRuleSet.ts +136 -127
- package/src/updateOutdatedPullsTemplates.ts +165 -160
- package/src/updateSlackMessages.ts +43 -43
- package/tailwind.config.ts +75 -20
package/src/PushedBranch.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { makePublishcrBranch } from "./makePublishBranch";
|
|
2
|
-
|
|
3
|
-
export type PushedBranch = Awaited<ReturnType<typeof makePublishcrBranch>>;
|
|
1
|
+
import { makePublishcrBranch } from "./makePublishBranch";
|
|
2
|
+
|
|
3
|
+
export type PushedBranch = Awaited<ReturnType<typeof makePublishcrBranch>>;
|
package/src/Totals.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { type
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
|
|
2
|
+
import { flatten } from "flat";
|
|
3
|
+
import { sf } from "snoflow";
|
|
4
|
+
import { type Task } from "../packages/mongodb-pipeline-ts/Task";
|
|
5
|
+
import { analyzeTotals } from "./analyzeTotals";
|
|
6
|
+
import { db } from "./db";
|
|
7
|
+
import { type AwaitedReturnType } from "./types/AwaitedReturnType";
|
|
8
|
+
export type Totals = AwaitedReturnType<typeof analyzeTotals>;
|
|
9
|
+
export const Totals = db.collection<{
|
|
10
|
+
/** @deprecated use totals.mtime */
|
|
11
|
+
today?: string;
|
|
12
|
+
totals?: Task<Totals>;
|
|
13
|
+
}>("Totals");
|
|
14
|
+
if (import.meta.main) {
|
|
15
|
+
sf(
|
|
16
|
+
$pipeline(Totals)
|
|
17
|
+
.match({ "totals.data": { $exists: true } })
|
|
18
|
+
.set({ "totals.data.date": "$totals.mtime" })
|
|
19
|
+
.replaceRoot({ newRoot: "$totals.data" })
|
|
20
|
+
.aggregate(),
|
|
21
|
+
)
|
|
22
|
+
.map((e) => flatten(e) as { date: string } & Record<string, number>)
|
|
23
|
+
.toLog();
|
|
24
|
+
}
|
package/src/TrackingPRs.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { db } from "./db";
|
|
2
|
-
|
|
3
|
-
// 2) tracking the statuses of the PRs we've sent out (edited)
|
|
4
|
-
// "status [open, closed, merged]",
|
|
5
|
-
// "comments [int]",
|
|
6
|
-
// "last active [date]",
|
|
7
|
-
// "last updated [date]",
|
|
8
|
-
|
|
9
|
-
export const TrackingPulls = db.collection("TrackingPulls");
|
|
10
|
-
// bun --env-file .env.production.local src/dump.ts > dump.log
|
|
11
|
-
if (import.meta.main) {
|
|
12
|
-
}
|
|
1
|
+
import { db } from "./db";
|
|
2
|
+
|
|
3
|
+
// 2) tracking the statuses of the PRs we've sent out (edited)
|
|
4
|
+
// "status [open, closed, merged]",
|
|
5
|
+
// "comments [int]",
|
|
6
|
+
// "last active [date]",
|
|
7
|
+
// "last updated [date]",
|
|
8
|
+
|
|
9
|
+
export const TrackingPulls = db.collection("TrackingPulls");
|
|
10
|
+
// bun --env-file .env.production.local src/dump.ts > dump.log
|
|
11
|
+
if (import.meta.main) {
|
|
12
|
+
}
|
package/src/WorkerInstances.ts
CHANGED
|
@@ -1,89 +1,89 @@
|
|
|
1
|
-
import { getMAC } from "@ctrl/mac-address";
|
|
2
|
-
import { defer } from "lodash-es";
|
|
3
|
-
import md5 from "md5";
|
|
4
|
-
import type { WithId } from "mongodb";
|
|
5
|
-
import { db } from "./db";
|
|
6
|
-
import { fetchCurrentGeoInfo } from "./fetchCurrentGeoInfo";
|
|
7
|
-
import { createInstanceId } from "./utils/createInstanceId";
|
|
8
|
-
export type GeoInfo = Awaited<ReturnType<typeof fetchCurrentGeoInfo>>;
|
|
9
|
-
export type WorkerInstance = {
|
|
10
|
-
/** id: rand */
|
|
11
|
-
id: string;
|
|
12
|
-
up: Date;
|
|
13
|
-
active: Date;
|
|
14
|
-
geo: GeoInfo;
|
|
15
|
-
workerId: string;
|
|
16
|
-
task?: string;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
const k = "COMFY_PR_WorkerInstanceKey";
|
|
20
|
-
type g = typeof globalThis & { [k]: any };
|
|
21
|
-
function getWorkerInstanceId() {
|
|
22
|
-
// ensure only one instance
|
|
23
|
-
if (!(global as any as g)[k])
|
|
24
|
-
defer(async function () {
|
|
25
|
-
await Promise.all([postWorkerHeartBeatLoop(), watchWorkerInstancesLoop()]);
|
|
26
|
-
});
|
|
27
|
-
const instanceId = ((global as any as g)[k] ??= createInstanceId());
|
|
28
|
-
return instanceId;
|
|
29
|
-
}
|
|
30
|
-
export const WorkerInstances = db.collection<WorkerInstance>("WorkerInstances");
|
|
31
|
-
await WorkerInstances.createIndex({ id: 1 }, { unique: true });
|
|
32
|
-
await WorkerInstances.createIndex({ ip: 1 });
|
|
33
|
-
export const _geoPromise = fetchCurrentGeoInfo(); // in background
|
|
34
|
-
|
|
35
|
-
if (import.meta.main) {
|
|
36
|
-
console.log(await getWorkerInstance());
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function postWorkerHeartBeatLoop() {
|
|
40
|
-
// 30s heartbeat
|
|
41
|
-
while (true) {
|
|
42
|
-
await new Promise((r) => setTimeout(r, 30e3));
|
|
43
|
-
await getWorkerInstance();
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
async function watchWorkerInstancesLoop() {
|
|
48
|
-
const me = await getWorkerInstance();
|
|
49
|
-
console.log("[INIT] Worker instance " + me.id + " is up.");
|
|
50
|
-
for await (const event of WorkerInstances.watch([], {
|
|
51
|
-
fullDocument: "whenAvailable",
|
|
52
|
-
})) {
|
|
53
|
-
const { fullDocument: updated } = event as typeof event & {
|
|
54
|
-
fullDocument?: WithId<WorkerInstance>;
|
|
55
|
-
};
|
|
56
|
-
if (updated && updated.id !== me.id) {
|
|
57
|
-
console.log("Another worker is updated", updated);
|
|
58
|
-
if (+updated.up > +me.up && updated.task === me.task) {
|
|
59
|
-
console.log("[EXIT] I'm outdated, new instance is: " + updated.id);
|
|
60
|
-
process.exit(0);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export async function getWorkerInstance(task?: string) {
|
|
67
|
-
const id = getWorkerInstanceId();
|
|
68
|
-
if (task) {
|
|
69
|
-
console.log("Working on task: ", task);
|
|
70
|
-
}
|
|
71
|
-
return (await WorkerInstances.findOneAndUpdate(
|
|
72
|
-
{ id },
|
|
73
|
-
{
|
|
74
|
-
$set: {
|
|
75
|
-
id,
|
|
76
|
-
active: new Date(),
|
|
77
|
-
workerId: getWorkerId(),
|
|
78
|
-
geo: await _geoPromise,
|
|
79
|
-
...(task && { task }),
|
|
80
|
-
},
|
|
81
|
-
$setOnInsert: { up: new Date() },
|
|
82
|
-
},
|
|
83
|
-
{ upsert: true, returnDocument: "after" },
|
|
84
|
-
))!;
|
|
85
|
-
}
|
|
86
|
-
function getWorkerId() {
|
|
87
|
-
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME;
|
|
88
|
-
return md5(`SALT=v9yJQouMC22do66t ${hostname} ${getMAC()}`).slice(0, 8);
|
|
89
|
-
}
|
|
1
|
+
import { getMAC } from "@ctrl/mac-address";
|
|
2
|
+
import { defer } from "lodash-es";
|
|
3
|
+
import md5 from "md5";
|
|
4
|
+
import type { WithId } from "mongodb";
|
|
5
|
+
import { db } from "./db";
|
|
6
|
+
import { fetchCurrentGeoInfo } from "./fetchCurrentGeoInfo";
|
|
7
|
+
import { createInstanceId } from "./utils/createInstanceId";
|
|
8
|
+
export type GeoInfo = Awaited<ReturnType<typeof fetchCurrentGeoInfo>>;
|
|
9
|
+
export type WorkerInstance = {
|
|
10
|
+
/** id: rand */
|
|
11
|
+
id: string;
|
|
12
|
+
up: Date;
|
|
13
|
+
active: Date;
|
|
14
|
+
geo: GeoInfo;
|
|
15
|
+
workerId: string;
|
|
16
|
+
task?: string;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const k = "COMFY_PR_WorkerInstanceKey";
|
|
20
|
+
type g = typeof globalThis & { [k]: any };
|
|
21
|
+
function getWorkerInstanceId() {
|
|
22
|
+
// ensure only one instance
|
|
23
|
+
if (!(global as any as g)[k])
|
|
24
|
+
defer(async function () {
|
|
25
|
+
await Promise.all([postWorkerHeartBeatLoop(), watchWorkerInstancesLoop()]);
|
|
26
|
+
});
|
|
27
|
+
const instanceId = ((global as any as g)[k] ??= createInstanceId());
|
|
28
|
+
return instanceId;
|
|
29
|
+
}
|
|
30
|
+
export const WorkerInstances = db.collection<WorkerInstance>("WorkerInstances");
|
|
31
|
+
await WorkerInstances.createIndex({ id: 1 }, { unique: true });
|
|
32
|
+
await WorkerInstances.createIndex({ ip: 1 });
|
|
33
|
+
export const _geoPromise = fetchCurrentGeoInfo(); // in background
|
|
34
|
+
|
|
35
|
+
if (import.meta.main) {
|
|
36
|
+
console.log(await getWorkerInstance());
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function postWorkerHeartBeatLoop() {
|
|
40
|
+
// 30s heartbeat
|
|
41
|
+
while (true) {
|
|
42
|
+
await new Promise((r) => setTimeout(r, 30e3));
|
|
43
|
+
await getWorkerInstance();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function watchWorkerInstancesLoop() {
|
|
48
|
+
const me = await getWorkerInstance();
|
|
49
|
+
console.log("[INIT] Worker instance " + me.id + " is up.");
|
|
50
|
+
for await (const event of WorkerInstances.watch([], {
|
|
51
|
+
fullDocument: "whenAvailable",
|
|
52
|
+
})) {
|
|
53
|
+
const { fullDocument: updated } = event as typeof event & {
|
|
54
|
+
fullDocument?: WithId<WorkerInstance>;
|
|
55
|
+
};
|
|
56
|
+
if (updated && updated.id !== me.id) {
|
|
57
|
+
console.log("Another worker is updated", updated);
|
|
58
|
+
if (+updated.up > +me.up && updated.task === me.task) {
|
|
59
|
+
console.log("[EXIT] I'm outdated, new instance is: " + updated.id);
|
|
60
|
+
process.exit(0);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export async function getWorkerInstance(task?: string) {
|
|
67
|
+
const id = getWorkerInstanceId();
|
|
68
|
+
if (task) {
|
|
69
|
+
console.log("Working on task: ", task);
|
|
70
|
+
}
|
|
71
|
+
return (await WorkerInstances.findOneAndUpdate(
|
|
72
|
+
{ id },
|
|
73
|
+
{
|
|
74
|
+
$set: {
|
|
75
|
+
id,
|
|
76
|
+
active: new Date(),
|
|
77
|
+
workerId: getWorkerId(),
|
|
78
|
+
geo: await _geoPromise,
|
|
79
|
+
...(task && { task }),
|
|
80
|
+
},
|
|
81
|
+
$setOnInsert: { up: new Date() },
|
|
82
|
+
},
|
|
83
|
+
{ upsert: true, returnDocument: "after" },
|
|
84
|
+
))!;
|
|
85
|
+
}
|
|
86
|
+
function getWorkerId() {
|
|
87
|
+
const hostname = process.env.HOSTNAME || process.env.COMPUTERNAME;
|
|
88
|
+
return md5(`SALT=v9yJQouMC22do66t ${hostname} ${getMAC()}`).slice(0, 8);
|
|
89
|
+
}
|
package/src/addCommentAction.ts
CHANGED
|
@@ -1,65 +1,73 @@
|
|
|
1
|
-
"use server";
|
|
2
|
-
import { $elemMatch } from "@/packages/mongodb-pipeline-ts/$elemMatch";
|
|
3
|
-
import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
|
|
4
|
-
import { TaskDataOrNull, type Task } from "@/packages/mongodb-pipeline-ts/Task";
|
|
5
|
-
import DIE from "@snomiao/die";
|
|
6
|
-
import pMap from "p-map";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
loadedAction.url
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
1
|
+
"use server";
|
|
2
|
+
import { $elemMatch } from "@/packages/mongodb-pipeline-ts/$elemMatch";
|
|
3
|
+
import { $pipeline } from "@/packages/mongodb-pipeline-ts/$pipeline";
|
|
4
|
+
import { TaskDataOrNull, type Task } from "@/packages/mongodb-pipeline-ts/Task";
|
|
5
|
+
import DIE from "@snomiao/die";
|
|
6
|
+
import pMap from "p-map";
|
|
7
|
+
import type { z } from "zod";
|
|
8
|
+
import type { PullStatusShown } from "./analyzePullsStatus";
|
|
9
|
+
import { CNRepos, type CRPull } from "./CNRepos";
|
|
10
|
+
import { createIssueComment } from "./createIssueComment";
|
|
11
|
+
import { $filaten } from "./db";
|
|
12
|
+
import type { zAddCommentAction } from "./followRuleSchema";
|
|
13
|
+
import { ghUser } from "./ghUser";
|
|
14
|
+
import type { GithubIssueComment } from "./GithubIssueComments";
|
|
15
|
+
import { notifySlackLinks } from "./slack/notifySlackLinks";
|
|
16
|
+
|
|
17
|
+
export async function addCommentAction({
|
|
18
|
+
matched,
|
|
19
|
+
action,
|
|
20
|
+
runAction,
|
|
21
|
+
rule,
|
|
22
|
+
}: {
|
|
23
|
+
matched: Task<PullStatusShown[]>;
|
|
24
|
+
action: z.infer<typeof zAddCommentAction>;
|
|
25
|
+
runAction: boolean;
|
|
26
|
+
rule: { name: string };
|
|
27
|
+
}) {
|
|
28
|
+
return await pMap(
|
|
29
|
+
TaskDataOrNull(matched) ?? DIE("NO-PAYLOAD-AVAILABLE"),
|
|
30
|
+
async (payload) => {
|
|
31
|
+
const loadedAction = {
|
|
32
|
+
action: "add-comment",
|
|
33
|
+
url: payload.url,
|
|
34
|
+
by: action.by,
|
|
35
|
+
body: action.body.replace(
|
|
36
|
+
/{{\$([_A-Za-z0-9]+)}}/g,
|
|
37
|
+
(_, key: string) =>
|
|
38
|
+
(payload as any)[key] || DIE("Missing key: " + key + " in payload: " + JSON.stringify(payload)),
|
|
39
|
+
),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (runAction && loadedAction.by === ghUser.login) {
|
|
43
|
+
const existedCommentsTask =
|
|
44
|
+
(await $pipeline(CNRepos)
|
|
45
|
+
.unwind("$crPulls.data")
|
|
46
|
+
.match({ "crPulls.data.pull.html_url": loadedAction.url })
|
|
47
|
+
.with<{ "crPulls.data": CRPull }>()
|
|
48
|
+
.replaceRoot({ newRoot: "$crPulls.data.comments" })
|
|
49
|
+
.as<Task<GithubIssueComment[]>>()
|
|
50
|
+
.aggregate()
|
|
51
|
+
.next()) ?? DIE("comments is not fetched before, plz check " + loadedAction.url + " in CNRepos");
|
|
52
|
+
|
|
53
|
+
const existedComments =
|
|
54
|
+
TaskDataOrNull(existedCommentsTask) ??
|
|
55
|
+
DIE("NO-COMMENTS-FOUND should never happen here, bcz pipeline filtered at first");
|
|
56
|
+
const existedComment = existedComments.find((e) => e.body === loadedAction.body);
|
|
57
|
+
|
|
58
|
+
if (!existedComment) {
|
|
59
|
+
const { comments, comment } = await createIssueComment(loadedAction.url, loadedAction.body, loadedAction.by);
|
|
60
|
+
const updateResult = await CNRepos.updateOne(
|
|
61
|
+
$filaten({ crPulls: { data: $elemMatch({ pull: { html_url: loadedAction.url } }) } }),
|
|
62
|
+
{ $set: { "crPulls.data.$.comments": comments } },
|
|
63
|
+
);
|
|
64
|
+
if (!updateResult.matchedCount) DIE("created issue not matched");
|
|
65
|
+
await notifySlackLinks("A New issue comment are created from rule " + rule.name, [comment.html_url]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return loadedAction;
|
|
70
|
+
},
|
|
71
|
+
{ concurrency: 1 },
|
|
72
|
+
);
|
|
73
|
+
}
|