@iworkflow/core 0.6.1 → 0.7.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/README.md CHANGED
@@ -33,8 +33,8 @@ try {
33
33
  }
34
34
  ```
35
35
 
36
- Ещё: `ctx.startChild` (без ожидания), `ChildOptions.timeoutMs` и `onExisting`, `ctx.startedAt`,
37
- `ctx.parentId`, `AppDefinition.pools`, `handle.describe()` и `handle.terminate()`.
36
+ Ещё: `ctx.startChild` (без ожидания, `reuseClosedId`), `ChildOptions.timeoutMs` и `onExisting`, `ctx.startedAt`,
37
+ `ctx.parentId`, `AppDefinition.pools` и `maxConcurrentSteps`, `StartOptions.onExisting`, `handle.describe()` и `handle.terminate()`.
38
38
 
39
39
  Приоритет: `client.start(..., { priority })` (1 — первым, 5 — последним) → `ctx.priority`; шаги и
40
40
  дети его наследуют, `StepOptions.priority` — свой. Срок повтора из отказа: `new Failure(msg, { type,
package/dist/errors.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { AppDefinition } from "./types";
1
2
  /**
2
3
  * Отказ, который бросает код шага или процесса. `type` — код для разбора (`FlowError.type`),
3
4
  * `nonRetryable` — шаг не повторять. Отказ процесса не повторяется никогда.
@@ -47,3 +48,5 @@ export declare const isFlowError: (e: unknown) => e is FlowError;
47
48
  /** Приоритет порта — целое 1…5 (1 — первым); иное отвергается одинаково на обоих движках. */
48
49
  export declare function checkPriority(p: number | undefined): number | undefined;
49
50
  export declare const DEFAULT_PRIORITY = 3;
51
+ /** Пределы модуля — целые ≥ 1: иначе DBOS молча вешает шаги, а Temporal падает на старте. */
52
+ export declare function checkLimits(def: AppDefinition): void;
package/dist/index.js CHANGED
@@ -55,6 +55,15 @@ function checkPriority(p) {
55
55
  return p;
56
56
  }
57
57
  var DEFAULT_PRIORITY = 3;
58
+ function checkLimits(def) {
59
+ const check = (name, n) => {
60
+ if (n !== undefined && (!Number.isInteger(n) || n < 1))
61
+ throw new Error(`${def.app}: ${name} = ${n}, нужно целое ≥ 1`);
62
+ };
63
+ check("maxConcurrentSteps", def.maxConcurrentSteps);
64
+ for (const [p, n] of Object.entries(def.pools ?? {}))
65
+ check(`pools.${p}`, n);
66
+ }
58
67
  // src/exit.ts
59
68
  function exitSignal() {
60
69
  let d = Promise.withResolvers();
@@ -83,6 +92,7 @@ export {
83
92
  DEFAULT_PRIORITY,
84
93
  Failure,
85
94
  FlowError,
95
+ checkLimits,
86
96
  checkPriority,
87
97
  exitSignal,
88
98
  heartbeat,
package/dist/types.d.ts CHANGED
@@ -34,6 +34,13 @@ export interface ChildOptions {
34
34
  */
35
35
  onExisting?: "reject";
36
36
  }
37
+ export interface StartChildOptions extends Omit<ChildOptions, "onExisting"> {
38
+ /**
39
+ * Id завершённого процесса — новый запуск под ним (окно сбора на чат). Только Temporal: DBOS id не
40
+ * переиспользует — already-started. Идущий процесс с этим id — already-started всегда.
41
+ */
42
+ reuseClosedId?: boolean;
43
+ }
37
44
  /** Дочерний процесс, запущенный без ожидания. */
38
45
  export interface ChildHandle<T> {
39
46
  id: string;
@@ -58,7 +65,7 @@ export interface FlowContext {
58
65
  * Дочерний процесс без ожидания: живёт и после завершения родителя. Id уже идёт или занят —
59
66
  * FlowError reason "already-started".
60
67
  */
61
- startChild<T>(target: FlowRef, args: unknown[], opts: Omit<ChildOptions, "onExisting">): Promise<ChildHandle<T>>;
68
+ startChild<T>(target: FlowRef, args: unknown[], opts: StartChildOptions): Promise<ChildHandle<T>>;
62
69
  sleep(ms: number): Promise<void>;
63
70
  /** Ждать сигнал по теме; null — вышло время. Сигнал, пришедший раньше, не теряется. */
64
71
  recv<T>(topic: string, timeoutMs: number): Promise<T | null>;
@@ -92,6 +99,8 @@ export interface AppDefinition {
92
99
  patches?: string[];
93
100
  /** Пулы шагов: имя → сколько шагов пула идёт одновременно в этом процессе. */
94
101
  pools?: Record<string, number>;
102
+ /** Сколько шагов вне пулов идёт одновременно в этом процессе (бережёт пул БД). Не задано — у DBOS без предела, у Temporal 100. */
103
+ maxConcurrentSteps?: number;
95
104
  }
96
105
  export type FlowStatus = "running" | "completed" | "failed" | "terminated" | "timed-out";
97
106
  export interface FlowDescription {
@@ -124,16 +133,18 @@ export interface StartOptions {
124
133
  timeoutMs?: number;
125
134
  /** 1 — первым, 5 — последним; шаги процесса наследуют его. */
126
135
  priority?: number;
136
+ /** Id уже занят (идёт или завершён; Temporal помнит завершённый в пределах хранения namespace) — FlowError "already-started". */
137
+ onExisting?: "reject";
127
138
  }
128
139
  /** Сторона вызывающего вне процесса (HTTP, CLI, тест). */
129
140
  export interface EngineClient {
130
- /** Id уже идёт — handle к нему. timeoutMs — срок процесса. */
141
+ /** Id уже идёт — handle к нему (с onExisting "reject" — already-started). timeoutMs — срок процесса. */
131
142
  start<T>(target: FlowRef, args: unknown[], opts: StartOptions): Promise<FlowHandle<T>>;
132
143
  /**
133
144
  * Сигнал процессу; нет идущего с этим id — запустить и отдать сигнал ему. Temporal заводит новый
134
145
  * запуск и под id завершённого; DBOS id не переиспользует — сигнал уйдёт завершённому и пропадёт.
135
146
  */
136
- signalWithStart<T>(target: FlowRef, args: unknown[], opts: StartOptions, topic: string, value: unknown): Promise<FlowHandle<T>>;
147
+ signalWithStart<T>(target: FlowRef, args: unknown[], opts: Omit<StartOptions, "onExisting">, topic: string, value: unknown): Promise<FlowHandle<T>>;
137
148
  /** Уже запущенный процесс по id. */
138
149
  handle<T>(id: string): FlowHandle<T>;
139
150
  close(): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@iworkflow/core",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "Engine-neutral durable workflow port: flows, steps, child workflows, signals, queries, patches, heartbeat",
5
5
  "keywords": [
6
6
  "workflow",
package/src/errors.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  // Ошибки порта: одинаковые на обоих движках. Без импортов рантайма — их читает песочница Temporal.
2
+ import type { AppDefinition } from "./types";
2
3
 
3
4
  /**
4
5
  * Отказ, который бросает код шага или процесса. `type` — код для разбора (`FlowError.type`),
@@ -75,3 +76,12 @@ export function checkPriority(p: number | undefined): number | undefined {
75
76
  }
76
77
 
77
78
  export const DEFAULT_PRIORITY = 3;
79
+
80
+ /** Пределы модуля — целые ≥ 1: иначе DBOS молча вешает шаги, а Temporal падает на старте. */
81
+ export function checkLimits(def: AppDefinition): void {
82
+ const check = (name: string, n: number | undefined) => {
83
+ if (n !== undefined && (!Number.isInteger(n) || n < 1)) throw new Error(`${def.app}: ${name} = ${n}, нужно целое ≥ 1`);
84
+ };
85
+ check("maxConcurrentSteps", def.maxConcurrentSteps);
86
+ for (const [p, n] of Object.entries(def.pools ?? {})) check(`pools.${p}`, n);
87
+ }
package/src/types.ts CHANGED
@@ -40,6 +40,14 @@ export interface ChildOptions {
40
40
  onExisting?: "reject";
41
41
  }
42
42
 
43
+ export interface StartChildOptions extends Omit<ChildOptions, "onExisting"> {
44
+ /**
45
+ * Id завершённого процесса — новый запуск под ним (окно сбора на чат). Только Temporal: DBOS id не
46
+ * переиспользует — already-started. Идущий процесс с этим id — already-started всегда.
47
+ */
48
+ reuseClosedId?: boolean;
49
+ }
50
+
43
51
  /** Дочерний процесс, запущенный без ожидания. */
44
52
  export interface ChildHandle<T> {
45
53
  id: string;
@@ -65,7 +73,7 @@ export interface FlowContext {
65
73
  * Дочерний процесс без ожидания: живёт и после завершения родителя. Id уже идёт или занят —
66
74
  * FlowError reason "already-started".
67
75
  */
68
- startChild<T>(target: FlowRef, args: unknown[], opts: Omit<ChildOptions, "onExisting">): Promise<ChildHandle<T>>;
76
+ startChild<T>(target: FlowRef, args: unknown[], opts: StartChildOptions): Promise<ChildHandle<T>>;
69
77
  sleep(ms: number): Promise<void>;
70
78
  /** Ждать сигнал по теме; null — вышло время. Сигнал, пришедший раньше, не теряется. */
71
79
  recv<T>(topic: string, timeoutMs: number): Promise<T | null>;
@@ -102,6 +110,8 @@ export interface AppDefinition {
102
110
  patches?: string[];
103
111
  /** Пулы шагов: имя → сколько шагов пула идёт одновременно в этом процессе. */
104
112
  pools?: Record<string, number>;
113
+ /** Сколько шагов вне пулов идёт одновременно в этом процессе (бережёт пул БД). Не задано — у DBOS без предела, у Temporal 100. */
114
+ maxConcurrentSteps?: number;
105
115
  }
106
116
 
107
117
  export type FlowStatus = "running" | "completed" | "failed" | "terminated" | "timed-out";
@@ -139,17 +149,19 @@ export interface StartOptions {
139
149
  timeoutMs?: number;
140
150
  /** 1 — первым, 5 — последним; шаги процесса наследуют его. */
141
151
  priority?: number;
152
+ /** Id уже занят (идёт или завершён; Temporal помнит завершённый в пределах хранения namespace) — FlowError "already-started". */
153
+ onExisting?: "reject";
142
154
  }
143
155
 
144
156
  /** Сторона вызывающего вне процесса (HTTP, CLI, тест). */
145
157
  export interface EngineClient {
146
- /** Id уже идёт — handle к нему. timeoutMs — срок процесса. */
158
+ /** Id уже идёт — handle к нему (с onExisting "reject" — already-started). timeoutMs — срок процесса. */
147
159
  start<T>(target: FlowRef, args: unknown[], opts: StartOptions): Promise<FlowHandle<T>>;
148
160
  /**
149
161
  * Сигнал процессу; нет идущего с этим id — запустить и отдать сигнал ему. Temporal заводит новый
150
162
  * запуск и под id завершённого; DBOS id не переиспользует — сигнал уйдёт завершённому и пропадёт.
151
163
  */
152
- signalWithStart<T>(target: FlowRef, args: unknown[], opts: StartOptions, topic: string, value: unknown): Promise<FlowHandle<T>>;
164
+ signalWithStart<T>(target: FlowRef, args: unknown[], opts: Omit<StartOptions, "onExisting">, topic: string, value: unknown): Promise<FlowHandle<T>>;
153
165
  /** Уже запущенный процесс по id. */
154
166
  handle<T>(id: string): FlowHandle<T>;
155
167
  close(): Promise<void>;