@gengchenchina/chrome-media-publish-extension 1.0.12 → 1.0.13

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 CHANGED
@@ -318,6 +318,10 @@ type PublishStatusRef = {
318
318
  | `cover` | 当前使用第一张图作为封面 | 可选自定义封面,上传到 ImageX |
319
319
  | `content` | `Array<{ url: string }>` 图片 URL 列表 | 视频 URL 字符串 |
320
320
 
321
+ 从 `1.0.13` 开始,视频最终提交由本包原生使用创作者中心 `create_v2` JSON 协议,在 Chrome `MAIN` 页面上下文执行。立即发布的 `item.common.timing` 为数值 `0`,定时发布为 Unix 秒数;无需宿主改写请求或包装共享 Toolkit 对象。
322
+
323
+ 视频最终提交只发起一次。页面返回空正文、网络错误或超时时,不回退旧 `create` 接口、不从扩展后台重复提交;遇到结果不明确时,应先在创作者中心核对作品。每次视频发布使用独立的步骤生命周期与截止时间,超时后阻止尚未开始的后续步骤和最终投稿,并继续清理请求头规则。已发送到服务器的请求仍可能被受理,超时不代表撤回成功。
324
+
321
325
  Douyin `publish()` 成功返回的 `statusRef.url` 是可推导候选:
322
326
 
323
327
  - 图文:`https://www.douyin.com/note/<id>`
package/dist/index.js CHANGED
@@ -434,10 +434,14 @@ function buildVideoTextPayload(media, resolvedTags = []) {
434
434
  resolvedTags
435
435
  });
436
436
  }
437
- function buildVideoFormPayload(media, video, posterUri, cover, resolvedTags = [], scheduleTime) {
437
+ function buildVideoPublishItem(media, video, posterUri, cover, resolvedTags = [], scheduleTime) {
438
438
  const textPayload = buildVideoTextPayload(media, resolvedTags);
439
- const timing = normalizeDouyinTiming(scheduleTime);
440
- const payload = {
439
+ const timing = normalizeDouyinTiming(scheduleTime) ?? 0;
440
+ if (!Number.isSafeInteger(timing) || timing < 0) {
441
+ throw new Error("\u6296\u97F3\u5B9A\u65F6\u53D1\u5E03\u65F6\u95F4\u65E0\u6548");
442
+ }
443
+ if (!video.vid || !posterUri) throw new Error("\u6296\u97F3\u89C6\u9891\u6216\u5C01\u9762\u53C2\u6570\u65E0\u6548");
444
+ const common = {
441
445
  video_id: video.vid,
442
446
  text: textPayload.text,
443
447
  caption: textPayload.bodyText,
@@ -447,25 +451,36 @@ function buildVideoFormPayload(media, video, posterUri, cover, resolvedTags = []
447
451
  challenges: JSON.stringify(textPayload.challenges || []),
448
452
  mentions: JSON.stringify(textPayload.mentions || []),
449
453
  hashtag_source: textPayload.hashtagSource || "",
450
- visibility_type: "0",
451
- upload_source: "1",
452
- is_preview: "0",
453
- is_post_assistant: "0",
454
- title_permission: "1",
454
+ visibility_type: 0,
455
455
  creation_id: createCreationId(),
456
- sync_to_toutiao: "0",
457
- ugc_activity: "{}",
458
- download: "1",
459
- timing: String(timing ?? 0),
460
- poster: posterUri,
461
- poster_delay: "0"
462
- };
463
- if (cover) {
464
- payload.upload_poster = cover.uri;
465
- if (cover.width) payload.custom_cover_image_width = String(cover.width);
466
- if (cover.height) payload.custom_cover_image_height = String(cover.height);
456
+ download: 1,
457
+ timing,
458
+ media_type: 4,
459
+ music_source: 0,
460
+ music_id: null,
461
+ hot_sentence: "",
462
+ interaction_stickers: "[]"
463
+ };
464
+ const videoCover = { poster: posterUri, poster_delay: 0 };
465
+ for (const [source, target] of [["width", "custom_cover_image_width"], ["height", "custom_cover_image_height"]]) {
466
+ if (cover?.[source] !== void 0) {
467
+ if (!Number.isSafeInteger(cover[source]) || cover[source] <= 0) {
468
+ throw new Error("\u6296\u97F3\u89C6\u9891\u5C01\u9762\u5C3A\u5BF8\u65E0\u6548");
469
+ }
470
+ videoCover[target] = cover[source];
471
+ }
467
472
  }
468
- return payload;
473
+ return {
474
+ common,
475
+ cover: videoCover,
476
+ mix: {},
477
+ selected_member: { is_selected_member_video: false },
478
+ chapter: {},
479
+ anchor: {},
480
+ sync: { should_sync: false, sync_to_toutiao: 0 },
481
+ open_platform: {},
482
+ assistant: { is_preview: 0, is_post_assistant: 1 }
483
+ };
469
484
  }
470
485
  function buildTextPayloadResult({ title, text, textExtra, bodyText, resolvedTags }) {
471
486
  const challenges = resolvedTags.map((tag) => tag.id).filter((id) => id !== void 0 && id !== null && id !== "");
@@ -522,6 +537,9 @@ async function getCreatorJson(ctx, url, headers = {}, options = {}) {
522
537
  }, options);
523
538
  }
524
539
  async function postCreatorJson(ctx, url, body, options = {}) {
540
+ if (options.pageContextOnly) {
541
+ return postJsonFromPage(ctx, url, body, options);
542
+ }
525
543
  if (options.preferPageContext) {
526
544
  const result = await postJsonFromPage(ctx, url, body, options).catch((error) => {
527
545
  if (isSecurityCheckError(error)) throw error;
@@ -540,25 +558,6 @@ async function postCreatorJson(ctx, url, body, options = {}) {
540
558
  body: JSON.stringify(body)
541
559
  }, options);
542
560
  }
543
- async function postCreatorForm(ctx, url, body, options = {}) {
544
- if (options.preferPageContext) {
545
- const result = await postFormFromPage(ctx, url, body, options).catch((error) => {
546
- if (isSecurityCheckError(error)) throw error;
547
- ctx.Logger?.warn?.("\u6296\u97F3\u9875\u9762\u4E0A\u4E0B\u6587 FORM POST \u5931\u8D25\uFF0C\u56DE\u9000\u6269\u5C55\u4E0A\u4E0B\u6587", error);
548
- return null;
549
- });
550
- if (result) return result;
551
- }
552
- return requestJson(ctx, url, {
553
- method: "POST",
554
- credentials: "include",
555
- headers: {
556
- "Content-Type": "application/x-www-form-urlencoded",
557
- ...options.headers
558
- },
559
- body: new URLSearchParams(body)
560
- }, options);
561
- }
562
561
  async function getCreatorJsonWithPageFallback(ctx, url, options = {}) {
563
562
  try {
564
563
  return await getJsonFromPage(ctx, url, options);
@@ -587,6 +586,7 @@ async function getJsonFromPage(ctx, url, options = {}) {
587
586
  ok: response.ok,
588
587
  status: response.status,
589
588
  statusText: response.statusText,
589
+ passportDecision: response.headers.get("x-tt-verify-passport-decision"),
590
590
  text: await response.text()
591
591
  };
592
592
  } finally {
@@ -598,66 +598,42 @@ async function getJsonFromPage(ctx, url, options = {}) {
598
598
  ).then((result) => parsePageResponse(result, url)), options);
599
599
  }
600
600
  async function postJsonFromPage(ctx, url, body, options = {}) {
601
- return withCreatorTab(ctx, async (tab) => ctx.Runtime.scripting.execute(
602
- tab.id,
603
- async (requestUrl, requestBody, timeoutMs) => {
604
- const controller = new AbortController();
605
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
606
- try {
607
- const response = await fetch(requestUrl, {
608
- method: "POST",
609
- credentials: "include",
610
- headers: {
611
- "Content-Type": "application/json",
612
- Accept: "application/json, text/plain, */*"
613
- },
614
- body: JSON.stringify(requestBody),
615
- signal: controller.signal
616
- });
617
- return {
618
- ok: response.ok,
619
- status: response.status,
620
- statusText: response.statusText,
621
- text: await response.text()
622
- };
623
- } finally {
624
- clearTimeout(timeoutId);
625
- }
626
- },
627
- [url, body, options.timeoutMs || PAGE_CONTEXT_FETCH_TIMEOUT_MS],
628
- { timeoutMs: options.timeoutMs, world: "MAIN" }
629
- ).then((result) => parsePageResponse(result, url)), options);
630
- }
631
- async function postFormFromPage(ctx, url, body, options = {}) {
632
- return withCreatorTab(ctx, async (tab) => ctx.Runtime.scripting.execute(
633
- tab.id,
634
- async (requestUrl, requestBody, timeoutMs) => {
635
- const controller = new AbortController();
636
- const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
637
- try {
638
- const response = await fetch(requestUrl, {
639
- method: "POST",
640
- credentials: "include",
641
- headers: {
642
- "Content-Type": "application/x-www-form-urlencoded",
643
- Accept: "application/json, text/plain, */*"
644
- },
645
- body: new URLSearchParams(requestBody),
646
- signal: controller.signal
647
- });
648
- return {
649
- ok: response.ok,
650
- status: response.status,
651
- statusText: response.statusText,
652
- text: await response.text()
653
- };
654
- } finally {
655
- clearTimeout(timeoutId);
656
- }
657
- },
658
- [url, body, options.timeoutMs || PAGE_CONTEXT_FETCH_TIMEOUT_MS],
659
- { timeoutMs: options.timeoutMs, world: "MAIN" }
660
- ).then((result) => parsePageResponse(result, url)), options);
601
+ return withCreatorTab(ctx, async (tab) => {
602
+ options.assertActive?.();
603
+ if (options.deadline && Date.now() >= options.deadline) throw new Error("\u6296\u97F3\u89C6\u9891\u63D0\u4EA4\u5DF2\u8D85\u65F6\uFF0C\u5DF2\u963B\u6B62\u8FDF\u5230\u6295\u7A3F");
604
+ return ctx.Runtime.scripting.execute(
605
+ tab.id,
606
+ async (requestUrl, requestBody, timeoutMs, deadline) => {
607
+ if (deadline && Date.now() >= deadline) throw new Error("\u6296\u97F3\u89C6\u9891\u63D0\u4EA4\u5DF2\u8D85\u65F6\uFF0C\u5DF2\u963B\u6B62\u8FDF\u5230\u6295\u7A3F");
608
+ const controller = new AbortController();
609
+ const remaining = deadline ? Math.min(timeoutMs, deadline - Date.now()) : timeoutMs;
610
+ const timeoutId = setTimeout(() => controller.abort(), remaining);
611
+ try {
612
+ const response = await fetch(requestUrl, {
613
+ method: "POST",
614
+ credentials: "include",
615
+ headers: {
616
+ "Content-Type": "application/json",
617
+ Accept: "application/json, text/plain, */*"
618
+ },
619
+ body: JSON.stringify(requestBody),
620
+ signal: controller.signal
621
+ });
622
+ return {
623
+ ok: response.ok,
624
+ status: response.status,
625
+ statusText: response.statusText,
626
+ passportDecision: response.headers.get("x-tt-verify-passport-decision"),
627
+ text: await response.text()
628
+ };
629
+ } finally {
630
+ clearTimeout(timeoutId);
631
+ }
632
+ },
633
+ [url, body, options.timeoutMs || PAGE_CONTEXT_FETCH_TIMEOUT_MS, options.deadline],
634
+ { timeoutMs: options.timeoutMs, world: "MAIN" }
635
+ ).then((result) => parsePageResponse(result, url));
636
+ }, options);
661
637
  }
662
638
  async function requestJson(ctx, url, init = {}, options = {}) {
663
639
  const response = await ctx.Runtime.network.fetch(url, {
@@ -670,6 +646,7 @@ async function requestJson(ctx, url, init = {}, options = {}) {
670
646
  body: init.body,
671
647
  timeoutMs: options.timeoutMs
672
648
  });
649
+ throwIfPassportVerification(response.headers?.get?.("x-tt-verify-passport-decision"));
673
650
  const text = await response.text();
674
651
  if (!response.ok) {
675
652
  if ([401, 403].includes(response.status) || looksLikeSecurityResponse(text)) {
@@ -684,6 +661,7 @@ async function requestJson(ctx, url, init = {}, options = {}) {
684
661
  return parseJsonText(text);
685
662
  }
686
663
  function parsePageResponse(result, url) {
664
+ throwIfPassportVerification(result?.passportDecision);
687
665
  if (!result?.ok) {
688
666
  if ([401, 403].includes(result?.status) || looksLikeSecurityResponse(result?.text)) {
689
667
  throw createSecurityCheckError(`\u6296\u97F3\u9875\u9762\u4E0A\u4E0B\u6587\u8981\u6C42\u5B89\u5168\u6821\u9A8C: HTTP ${result?.status}`);
@@ -696,6 +674,18 @@ function parsePageResponse(result, url) {
696
674
  }
697
675
  return parseJsonText(result.text);
698
676
  }
677
+ function throwIfPassportVerification(value) {
678
+ if (!value) return;
679
+ let decision;
680
+ try {
681
+ decision = JSON.parse(value);
682
+ } catch {
683
+ return;
684
+ }
685
+ if (decision?.account_flow === "verify") {
686
+ throw createSecurityCheckError("\u6296\u97F3\u8981\u6C42\u8D26\u53F7\u8EAB\u4EFD\u9A8C\u8BC1\uFF0C\u8BF7\u5728\u6296\u97F3\u521B\u4F5C\u8005\u4E2D\u5FC3\u5B8C\u6210\u8EAB\u4EFD\u9A8C\u8BC1\u540E\u91CD\u8BD5");
687
+ }
688
+ }
699
689
  function looksLikeSecurityResponse(text) {
700
690
  try {
701
691
  throwIfSecurityCheck(text);
@@ -1514,11 +1504,31 @@ async function withDouyinHeaderRules(ctx, input = {}, callback) {
1514
1504
  return callback();
1515
1505
  }
1516
1506
  const ruleIds = [];
1507
+ let cleaningUp = false;
1508
+ const removeRule = async (ruleId) => {
1509
+ try {
1510
+ await (ctx.cleanupKit || ctx.Kit).runStep(
1511
+ "\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219",
1512
+ () => headerRules.remove(ruleId, { timeoutMs: input.timeoutMs }),
1513
+ {
1514
+ timeoutMs: input.timeoutMs,
1515
+ data: { platform: DOUYIN_PLATFORM, ruleId }
1516
+ }
1517
+ );
1518
+ } catch (error) {
1519
+ ctx.Logger?.warn?.("\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219\u5931\u8D25", error);
1520
+ }
1521
+ };
1517
1522
  try {
1518
1523
  for (const rule of DOUYIN_HEADER_RULES) {
1519
- const ruleId = await ctx.Kit.runStep(
1524
+ await ctx.Kit.runStep(
1520
1525
  "\u6296\u97F3\u6DFB\u52A0\u8BF7\u6C42\u5934\u89C4\u5219",
1521
- () => headerRules.add(rule, { timeoutMs: input.timeoutMs }),
1526
+ async () => {
1527
+ const ruleId = await headerRules.add(rule, { timeoutMs: 0 });
1528
+ if (cleaningUp) await removeRule(ruleId);
1529
+ else ruleIds.push(ruleId);
1530
+ return ruleId;
1531
+ },
1522
1532
  {
1523
1533
  timeoutMs: input.timeoutMs,
1524
1534
  data: {
@@ -1527,26 +1537,12 @@ async function withDouyinHeaderRules(ctx, input = {}, callback) {
1527
1537
  }
1528
1538
  }
1529
1539
  );
1530
- ruleIds.push(ruleId);
1531
1540
  }
1532
1541
  return await callback();
1533
1542
  } finally {
1543
+ cleaningUp = true;
1534
1544
  for (const ruleId of ruleIds.reverse()) {
1535
- try {
1536
- await ctx.Kit.runStep(
1537
- "\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219",
1538
- () => headerRules.remove(ruleId, { timeoutMs: input.timeoutMs }),
1539
- {
1540
- timeoutMs: input.timeoutMs,
1541
- data: {
1542
- platform: DOUYIN_PLATFORM,
1543
- ruleId
1544
- }
1545
- }
1546
- );
1547
- } catch (error) {
1548
- ctx.Logger?.warn?.("\u6296\u97F3\u6E05\u7406\u8BF7\u6C42\u5934\u89C4\u5219\u5931\u8D25", error);
1549
- }
1545
+ await removeRule(ruleId);
1550
1546
  }
1551
1547
  }
1552
1548
  }
@@ -1807,7 +1803,7 @@ async function publishDouyinVideo(ctx, media, input = {}, dependencies = createV
1807
1803
  );
1808
1804
  const payload = await ctx.Kit.runStep(
1809
1805
  "\u6296\u97F3\u6784\u9020\u89C6\u9891\u53D1\u5E03\u53C2\u6570",
1810
- () => buildVideoFormPayload(media, video, posterUri, cover, resolvedTags, input.scheduleTime),
1806
+ () => ({ item: buildVideoPublishItem(media, video, posterUri, cover, resolvedTags, input.scheduleTime) }),
1811
1807
  {
1812
1808
  timeoutMs: input.timeoutMs,
1813
1809
  data: {
@@ -1819,22 +1815,34 @@ async function publishDouyinVideo(ctx, media, input = {}, dependencies = createV
1819
1815
  }
1820
1816
  }
1821
1817
  );
1818
+ let submitStarted = false;
1822
1819
  const response = await ctx.Kit.runStep(
1823
1820
  "\u6296\u97F3\u63D0\u4EA4\u89C6\u9891\u53D1\u5E03",
1824
- () => dependencies.postCreatorForm(
1825
- `${CREATOR_ORIGIN}/web/api/media/aweme/create/?read_aid=${DOUYIN_READ_AID}`,
1826
- payload,
1827
- {
1828
- preferPageContext: true,
1829
- timeoutMs: input.timeoutMs
1830
- }
1831
- ),
1821
+ () => {
1822
+ ctx.publishLifecycle?.assertActive();
1823
+ if (submitStarted) throw new Error("\u6296\u97F3\u89C6\u9891\u5DF2\u63D0\u4EA4\uFF0C\u5DF2\u963B\u6B62\u91CD\u590D\u6295\u7A3F");
1824
+ submitStarted = true;
1825
+ return dependencies.postCreatorJson(
1826
+ `${CREATOR_ORIGIN}/web/api/media/aweme/create_v2/?read_aid=${DOUYIN_READ_AID}`,
1827
+ payload,
1828
+ {
1829
+ preferPageContext: true,
1830
+ pageContextOnly: true,
1831
+ assertActive: ctx.publishLifecycle?.assertActive,
1832
+ deadline: ctx.publishLifecycle?.getDeadline(),
1833
+ timeoutMs: input.timeoutMs
1834
+ }
1835
+ );
1836
+ },
1832
1837
  {
1833
1838
  timeoutMs: input.timeoutMs,
1839
+ retry: { times: 0 },
1834
1840
  data: {
1835
1841
  platform: DOUYIN_PLATFORM,
1842
+ requestPath: "/web/api/media/aweme/create_v2/",
1843
+ contentType: "application/json",
1836
1844
  videoId: video.vid,
1837
- textLength: payload.text?.length ?? 0
1845
+ textLength: payload.item.common.text.length
1838
1846
  }
1839
1847
  }
1840
1848
  );
@@ -1854,7 +1862,7 @@ async function publishDouyinVideo(ctx, media, input = {}, dependencies = createV
1854
1862
  meta: {
1855
1863
  kind: "video",
1856
1864
  videoId: video.vid,
1857
- ...Number(payload.timing) ? { timing: Number(payload.timing) } : {}
1865
+ ...payload.item.common.timing ? { timing: payload.item.common.timing } : {}
1858
1866
  }
1859
1867
  }
1860
1868
  };
@@ -1874,7 +1882,7 @@ function createVideoDependencies(ctx, input) {
1874
1882
  (url) => getCreatorJson(ctx, url, {}, { timeoutMs: input?.timeoutMs }),
1875
1883
  { timeoutMs: input?.timeoutMs }
1876
1884
  ),
1877
- postCreatorForm: (url, body, options) => postCreatorForm(ctx, url, body, options)
1885
+ postCreatorJson: (url, body, options) => postCreatorJson(ctx, url, body, options)
1878
1886
  };
1879
1887
  }
1880
1888
 
@@ -12833,6 +12841,7 @@ async function publishOperation(ctx, adapterLookup, input = {}) {
12833
12841
  platform: platform15
12834
12842
  });
12835
12843
  }
12844
+ let publishContext = ctx;
12836
12845
  try {
12837
12846
  const authInput = withDefaultTimeout(input, OperationTimeoutMs.CheckAuth);
12838
12847
  const publishInput = withDefaultTimeout(input, OperationTimeoutMs.Publish);
@@ -12846,13 +12855,22 @@ async function publishOperation(ctx, adapterLookup, input = {}) {
12846
12855
  platform: platform15
12847
12856
  });
12848
12857
  }
12849
- const publishResult = await ctx.Kit.runStep(
12858
+ const guardedVideo = platform15 === "douyin" && media.type === "video";
12859
+ if (guardedVideo) publishContext = createDouyinVideoPublishContext(ctx, publishInput.timeoutMs);
12860
+ let publishStarted = false;
12861
+ const publishOptions = createStepOptions(publishInput, {
12862
+ platform: platform15,
12863
+ type: media.type
12864
+ }, OperationTimeoutMs.Publish);
12865
+ if (guardedVideo) publishOptions.retry = { times: 0 };
12866
+ const publishResult = await publishContext.Kit.runStep(
12850
12867
  `${platform15}: \u53D1\u5E03\u5185\u5BB9`,
12851
- () => adapter.publish(ctx, media, publishInput),
12852
- createStepOptions(publishInput, {
12853
- platform: platform15,
12854
- type: media.type
12855
- }, OperationTimeoutMs.Publish)
12868
+ () => {
12869
+ if (guardedVideo && publishStarted) throw new Error("\u6296\u97F3\u89C6\u9891\u5DF2\u6267\u884C\uFF0C\u5DF2\u963B\u6B62\u91CD\u590D\u53D1\u5E03");
12870
+ publishStarted = true;
12871
+ return adapter.publish(publishContext, media, publishInput);
12872
+ },
12873
+ publishOptions
12856
12874
  );
12857
12875
  if (publishResult?.securityCheckRequired === true) {
12858
12876
  return createFailure(StatusCode.SecurityCheckRequired, publishResult.reason ?? "\u5E73\u53F0\u8981\u6C42\u5B89\u5168\u6821\u9A8C", {
@@ -12875,7 +12893,57 @@ async function publishOperation(ctx, adapterLookup, input = {}) {
12875
12893
  return mapErrorToResult(error, StatusCode.Unknown, {
12876
12894
  platform: platform15
12877
12895
  });
12896
+ } finally {
12897
+ if (publishContext !== ctx) publishContext.publishLifecycle.close();
12898
+ }
12899
+ }
12900
+ function createDouyinVideoPublishContext(ctx, timeoutMs) {
12901
+ let active = true;
12902
+ const deadline = Date.now() + timeoutMs;
12903
+ const stepDeadlines = /* @__PURE__ */ new Set();
12904
+ const getDeadline = () => Math.min(deadline, ...Array.from(stepDeadlines, (step) => step.deadline));
12905
+ const assertActive = () => {
12906
+ if (!active || Date.now() >= getDeadline()) {
12907
+ active = false;
12908
+ throw Object.assign(new Error("\u6296\u97F3\u89C6\u9891\u53D1\u5E03\u5DF2\u7ED3\u675F\u6216\u8D85\u65F6\uFF0C\u5DF2\u963B\u6B62\u8FDF\u5230\u6B65\u9AA4"), { code: 30000003 });
12909
+ }
12910
+ };
12911
+ const Kit = { ...ctx.Kit };
12912
+ for (const method of ["runStep", "runStepLoose"]) {
12913
+ if (typeof ctx.Kit[method] !== "function") continue;
12914
+ Kit[method] = async (name, action, options = {}) => {
12915
+ assertActive();
12916
+ const step = { deadline: Date.now() + (options.timeoutMs ?? 6e4) };
12917
+ try {
12918
+ return await ctx.Kit[method](name, async (...args) => {
12919
+ stepDeadlines.add(step);
12920
+ try {
12921
+ assertActive();
12922
+ const result = await action(...args);
12923
+ assertActive();
12924
+ return result;
12925
+ } finally {
12926
+ stepDeadlines.delete(step);
12927
+ }
12928
+ }, options);
12929
+ } catch (error) {
12930
+ const seen = /* @__PURE__ */ new Set();
12931
+ for (let cause = error; cause && !seen.has(cause); cause = cause.cause) {
12932
+ seen.add(cause);
12933
+ if ([30000003, 30000008].includes(cause.code) || cause.name === "TimeoutError") active = false;
12934
+ }
12935
+ throw error;
12936
+ }
12937
+ };
12878
12938
  }
12939
+ return {
12940
+ ...ctx,
12941
+ Kit,
12942
+ cleanupKit: ctx.Kit,
12943
+ publishLifecycle: { assertActive, getDeadline, close: () => {
12944
+ active = false;
12945
+ } }
12946
+ };
12879
12947
  }
12880
12948
 
12881
12949
  // src/operations/get-status.js