@iworkflow/temporal 0.7.0 → 0.8.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.js CHANGED
@@ -141,7 +141,15 @@ var wrap = (h, ready = async () => {}) => ({
141
141
  var TEMPORAL_STEP = {
142
142
  heartbeat: async (details) => Context.current().heartbeat(details),
143
143
  lastHeartbeat: async () => Context.current().info.heartbeatDetails,
144
- attempt: () => Context.current().info.attempt
144
+ attempt: () => Context.current().info.attempt,
145
+ info: () => {
146
+ const { workflowExecution, activityId } = Context.current().info;
147
+ const { workflowId, runId } = workflowExecution ?? {};
148
+ if (!workflowId || !runId)
149
+ throw new Error("stepInfo: шаг вне процесса Temporal");
150
+ return { workflowId, runId, stepId: activityId };
151
+ },
152
+ signal: () => Context.current().cancellationSignal
145
153
  };
146
154
  var temporalStep = (fn) => stepScope(alsScope).run(TEMPORAL_STEP, fn);
147
155
  function temporalActivities(activities) {
@@ -284,7 +292,10 @@ async function temporalClient(opts = {}) {
284
292
  await ready();
285
293
  const h = await client.workflow.start(target.flow, {
286
294
  ...startOptions(target, args, o),
287
- ...o.onExisting === "reject" ? { workflowIdConflictPolicy: "FAIL", workflowIdReusePolicy: "REJECT_DUPLICATE" } : { workflowIdConflictPolicy: "USE_EXISTING" }
295
+ ...o.onExisting === "reject" || o.reuseClosedId ? {
296
+ workflowIdConflictPolicy: "FAIL",
297
+ workflowIdReusePolicy: o.reuseClosedId ? "ALLOW_DUPLICATE" : "REJECT_DUPLICATE"
298
+ } : { workflowIdConflictPolicy: "USE_EXISTING" }
288
299
  }).catch((e) => {
289
300
  throw fromTemporal(e);
290
301
  });
package/dist/workflow.js CHANGED
@@ -6,6 +6,7 @@ import {
6
6
  deprecatePatch,
7
7
  executeChild,
8
8
  getExternalWorkflowHandle,
9
+ log,
9
10
  ParentClosePolicy,
10
11
  patched,
11
12
  proxyActivities,
@@ -15,7 +16,7 @@ import {
15
16
  TemporalFailure,
16
17
  workflowInfo
17
18
  } from "@temporalio/workflow";
18
- import { checkPriority, DEFAULT_PRIORITY, FlowError as FlowError2 } from "@iworkflow/core";
19
+ import { checkPriority, DEFAULT_PRIORITY, FlowError as FlowError2, plainAttrs } from "@iworkflow/core";
19
20
 
20
21
  // src/failure.ts
21
22
  import {
@@ -144,6 +145,7 @@ function temporalFlow(flow, patches = []) {
144
145
  throw e;
145
146
  throw new FlowError2(`процесса ${workflowId} нет или он завершён`, { reason: "not-found" });
146
147
  }),
148
+ log: async (level, message, attrs) => log[level](message, plainAttrs(attrs)),
147
149
  async setState(key, value) {
148
150
  state.set(key, value);
149
151
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iworkflow/temporal",
3
- "version": "0.7.0",
3
+ "version": "0.8.0",
4
4
  "description": "Temporal adapter for @iworkflow/core",
5
5
  "keywords": [
6
6
  "workflow",
@@ -41,7 +41,7 @@
41
41
  "prepack": "bun run build"
42
42
  },
43
43
  "dependencies": {
44
- "@iworkflow/core": "^0.7.0"
44
+ "@iworkflow/core": "^0.8.0"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@temporalio/activity": "^1.24.0",
package/src/index.ts CHANGED
@@ -78,6 +78,14 @@ const TEMPORAL_STEP: StepRuntime = {
78
78
  heartbeat: async (details) => Context.current().heartbeat(details),
79
79
  lastHeartbeat: async () => Context.current().info.heartbeatDetails,
80
80
  attempt: () => Context.current().info.attempt,
81
+ info: () => {
82
+ // Пустой id как ключ идемпотентности склеил бы записи разных процессов — отказ.
83
+ const { workflowExecution, activityId } = Context.current().info;
84
+ const { workflowId, runId } = workflowExecution ?? {};
85
+ if (!workflowId || !runId) throw new Error("stepInfo: шаг вне процесса Temporal");
86
+ return { workflowId, runId, stepId: activityId };
87
+ },
88
+ signal: () => Context.current().cancellationSignal,
81
89
  };
82
90
 
83
91
  /** Один вызов шага в рантайме Temporal без обёртки отказов — для тестов в MockActivityEnvironment. */
@@ -250,8 +258,11 @@ export async function temporalClient(opts: TemporalOptions & { lazy?: boolean }
250
258
  const h = await client.workflow
251
259
  .start(target.flow, {
252
260
  ...startOptions(target, args, o),
253
- ...(o.onExisting === "reject"
254
- ? { workflowIdConflictPolicy: "FAIL" as const, workflowIdReusePolicy: "REJECT_DUPLICATE" as const }
261
+ ...(o.onExisting === "reject" || o.reuseClosedId
262
+ ? {
263
+ workflowIdConflictPolicy: "FAIL" as const,
264
+ workflowIdReusePolicy: o.reuseClosedId ? ("ALLOW_DUPLICATE" as const) : ("REJECT_DUPLICATE" as const),
265
+ }
255
266
  : { workflowIdConflictPolicy: "USE_EXISTING" as const }),
256
267
  })
257
268
  .catch((e) => {
package/src/workflow.ts CHANGED
@@ -8,6 +8,7 @@ import {
8
8
  deprecatePatch,
9
9
  executeChild,
10
10
  getExternalWorkflowHandle,
11
+ log,
11
12
  ParentClosePolicy,
12
13
  patched,
13
14
  proxyActivities,
@@ -18,7 +19,7 @@ import {
18
19
  workflowInfo,
19
20
  } from "@temporalio/workflow";
20
21
 
21
- import { checkPriority, DEFAULT_PRIORITY, FlowError, type ChildOptions, type Flow, type FlowContext, type StepOptions } from "@iworkflow/core";
22
+ import { checkPriority, DEFAULT_PRIORITY, FlowError, plainAttrs, type ChildOptions, type Flow, type FlowContext, type StepOptions } from "@iworkflow/core";
22
23
  import { fromTemporal, toTemporal } from "./failure";
23
24
 
24
25
  export const SIGNAL = "iwf.signal";
@@ -105,6 +106,7 @@ export function temporalFlow(flow: Flow, patches: string[] = []) {
105
106
  },
106
107
  sleep: (ms) => sleep(ms),
107
108
  async recv<T>(topic: string, timeoutMs: number) {
109
+ // 0 и меньше — таймер 1 мс (так было и до 0.8): отдаёт активацию, цикл опроса не виснет.
108
110
  const ready = await condition(() => (inbox.get(topic)?.length ?? 0) > 0, timeoutMs);
109
111
  return ready ? (inbox.get(topic)!.shift() as T) : null;
110
112
  },
@@ -115,6 +117,9 @@ export function temporalFlow(flow: Flow, patches: string[] = []) {
115
117
  if (!(e instanceof TemporalFailure)) throw e;
116
118
  throw new FlowError(`процесса ${workflowId} нет или он завершён`, { reason: "not-found" });
117
119
  }),
120
+ // log песочницы молчит при переигрывании.
121
+ // Неклонируемые attrs (функция, BigInt) уронили бы активацию при отправке из песочницы.
122
+ log: async (level, message, attrs) => log[level](message, plainAttrs(attrs)),
118
123
  async setState(key, value) {
119
124
  state.set(key, value);
120
125
  },