@kubun/plugin-workflow 0.13.0 → 0.14.1

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.
@@ -0,0 +1,9 @@
1
+ import type { AdaptivePolicy, WorkflowOutcome } from '@kubun/plugin-workflow-api';
2
+ export declare function validateAdaptivePolicy(policy: AdaptivePolicy): void;
3
+ export declare function computeNextDelay(policy: AdaptivePolicy, outcome: WorkflowOutcome, streak: {
4
+ lastOutcome: WorkflowOutcome | null;
5
+ count: number;
6
+ }): {
7
+ delayMs: number;
8
+ count: number;
9
+ };
@@ -0,0 +1,44 @@
1
+ // Adaptive schedule policy: the pure core that maps a recurring sync's last
2
+ // outcome to the delay before its next run. No DB, no engine state — the store
3
+ // persists the streak, the engine calls this to advance it. The policy types
4
+ // are the cross-plugin contract; this module keeps only the pure math.
5
+ const DEFAULT_BACKOFF = {
6
+ idle: 'fixed',
7
+ error: 'exponential',
8
+ offline: 'exponential'
9
+ };
10
+ function assertPositive(value, label) {
11
+ if (!(value > 0)) throw new Error(`adaptive policy: ${label} must be > 0, got ${value}`);
12
+ }
13
+ function validateBranch(branch, label) {
14
+ assertPositive(branch.base, `${label}.base`);
15
+ assertPositive(branch.max, `${label}.max`);
16
+ if (branch.base > branch.max) {
17
+ throw new Error(`adaptive policy: ${label}.base (${branch.base}) exceeds max (${branch.max})`);
18
+ }
19
+ }
20
+ export function validateAdaptivePolicy(policy) {
21
+ assertPositive(policy.changed, 'changed');
22
+ validateBranch(policy.idle, 'idle');
23
+ validateBranch(policy.error, 'error');
24
+ validateBranch(policy.offline, 'offline');
25
+ }
26
+ export function computeNextDelay(policy, outcome, streak) {
27
+ if (outcome === 'changed') {
28
+ return {
29
+ delayMs: policy.changed,
30
+ count: 0
31
+ };
32
+ }
33
+ // A different outcome class (a productive run's stored count of 0 included)
34
+ // starts a fresh streak at 1; same class grows it.
35
+ const count = streak.lastOutcome === outcome ? streak.count + 1 : 1;
36
+ const branch = policy[outcome];
37
+ const kind = branch.backoff ?? DEFAULT_BACKOFF[outcome];
38
+ const raw = kind === 'fixed' ? branch.base : branch.base * 2 ** (count - 1);
39
+ // Clamp is the runtime backstop even if validation was bypassed.
40
+ return {
41
+ delayMs: Math.min(branch.max, raw),
42
+ count
43
+ };
44
+ }
@@ -1,25 +1,3 @@
1
- import type { FlowAction, HandlersRecord } from '@sozai/flow';
2
- import type { Validator } from '@sozai/schema';
3
- export type RetryPolicy = {
4
- maxAttempts?: number;
5
- backoff?: 'exponential' | 'fixed';
6
- baseMs?: number;
7
- maxMs?: number;
8
- };
9
- export type TerminalStatus = 'completed' | 'failed' | 'cancelled';
10
- export type TerminalInfo = {
11
- instanceID: string;
12
- name: string;
13
- status: TerminalStatus;
14
- error?: string;
15
- };
16
- export type WorkflowDefinition<State extends Record<string, unknown> = Record<string, unknown>, Handlers extends HandlersRecord<State> = HandlersRecord<State>> = {
17
- name: string;
18
- handlers: Handlers;
19
- initialAction: FlowAction<State, Handlers>;
20
- stateValidator?: Validator<State>;
21
- queue?: string;
22
- retry?: RetryPolicy;
23
- onTerminal?: (info: TerminalInfo) => void;
24
- };
1
+ import type { WorkflowDefinition } from '@kubun/plugin-workflow-api';
2
+ import type { HandlersRecord } from '@sozai/flow';
25
3
  export declare function defineWorkflow<State extends Record<string, unknown>, Handlers extends HandlersRecord<State> = HandlersRecord<State>>(def: WorkflowDefinition<State, Handlers>): WorkflowDefinition<State, Handlers>;
package/lib/engine.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import type { Logger } from '@kubun/logger';
2
+ import type { RetryPolicy, WorkflowAPI } from '@kubun/plugin-workflow-api';
2
3
  import type { WorkflowStoreAPI } from '@kubun/store-workflow';
3
4
  import type { GetRandomID } from '@sozai/runtime';
4
- import type { RetryPolicy } from './definition.js';
5
- import type { WorkflowAPI } from './types.js';
6
5
  export declare function backoff(attempts: number, policy?: RetryPolicy): number;
7
6
  export declare function parseEvery(spec: string): number;
8
7
  export type WorkflowEngineDeps = {
package/lib/engine.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { EventEmitter } from '@sozai/event';
2
2
  import { createFlow } from '@sozai/flow';
3
+ import { computeNextDelay, validateAdaptivePolicy } from './adaptive.js';
3
4
  // One runner may hold an instance's lease for this long before another boot may
4
5
  // reclaim it as stranded.
5
6
  const LEASE_MS = 30_000;
@@ -43,6 +44,35 @@ function toStatus(row) {
43
44
  updatedAt: row.updated_at
44
45
  };
45
46
  }
47
+ // The active (pending/running) instance for (name, singletonKey) if one exists,
48
+ // else the most recent terminal by updated_at, else null. Instances are promoted
49
+ // with singleton_key = subject, so the subject keys the lookup.
50
+ async function currentInstanceRow(s, name, singletonKey) {
51
+ const rows = await s.list({
52
+ name
53
+ });
54
+ const forSubject = rows.filter((r)=>r.singleton_key === singletonKey);
55
+ const active = forSubject.find((r)=>r.status === 'pending' || r.status === 'running');
56
+ return active ?? forSubject.reduce((best, r)=>best == null || r.updated_at > best.updated_at ? r : best, null);
57
+ }
58
+ // Project an adaptive schedule row as a PeriodicSync. `currentStatus` is the
59
+ // schedule subject's current instance (see `currentInstanceRow`), null if never
60
+ // run.
61
+ async function buildPeriodicSync(s, sched) {
62
+ const subject = sched.id.slice(sched.workflow_name.length + 1);
63
+ const chosen = await currentInstanceRow(s, sched.workflow_name, subject);
64
+ return {
65
+ scheduleID: sched.id,
66
+ subject,
67
+ workflowName: sched.workflow_name,
68
+ enabled: sched.enabled,
69
+ nextRun: sched.next_run,
70
+ lastOutcome: sched.last_outcome,
71
+ consecutiveCount: sched.consecutive_count,
72
+ policy: sched.policy,
73
+ currentStatus: chosen != null ? toStatus(chosen) : null
74
+ };
75
+ }
46
76
  export function createWorkflowEngine(deps) {
47
77
  const now = deps.now ?? Date.now;
48
78
  const logger = deps.logger;
@@ -65,7 +95,7 @@ export function createWorkflowEngine(deps) {
65
95
  inflight.add(promise);
66
96
  void promise.finally(()=>inflight.delete(promise));
67
97
  }
68
- function fireTerminal(def, instanceID, status, error) {
98
+ function fireTerminal(def, instanceID, status, error, outcome) {
69
99
  events.fire(status, {
70
100
  instanceID,
71
101
  name: def.name
@@ -76,7 +106,8 @@ export function createWorkflowEngine(deps) {
76
106
  instanceID,
77
107
  name: def.name,
78
108
  status,
79
- error
109
+ error,
110
+ outcome
80
111
  });
81
112
  } catch (cause) {
82
113
  logger.warn('workflow onTerminal threw', {
@@ -87,6 +118,24 @@ export function createWorkflowEngine(deps) {
87
118
  }
88
119
  }
89
120
  }
121
+ // Advance the adaptive schedule that owns this instance, if any. Keyed by the
122
+ // instance's singleton_key: an interval-promoted or one-shot run has none and
123
+ // is a no-op. Called only after a fenced terminal write succeeded.
124
+ async function advanceAdaptive(row, outcome, at) {
125
+ if (row.singleton_key == null) return;
126
+ const s = await store();
127
+ const sched = await s.getSchedule(`${row.name}:${row.singleton_key}`);
128
+ if (sched == null || sched.kind !== 'adaptive') return;
129
+ const { delayMs, count } = computeNextDelay(sched.policy, outcome, {
130
+ lastOutcome: sched.last_outcome,
131
+ count: sched.consecutive_count
132
+ });
133
+ await s.advanceScheduleAdaptive(sched.id, {
134
+ nextRun: at + delayMs,
135
+ lastOutcome: outcome,
136
+ consecutiveCount: count
137
+ }, at);
138
+ }
90
139
  // Runs an already-claimed, already-leased row through the flow loop. The tick's
91
140
  // `claimDue` does the claiming, so this path never re-claims.
92
141
  async function run(row) {
@@ -153,7 +202,10 @@ export function createWorkflowEngine(deps) {
153
202
  state: flow.getState()
154
203
  }, now(), bootID);
155
204
  if (!wrote) return;
156
- fireTerminal(def, instanceID, 'completed');
205
+ // A run reports its outcome via the flow `end` value; absent = idle.
206
+ const outcome = value.outcome ?? 'idle';
207
+ fireTerminal(def, instanceID, 'completed', undefined, outcome);
208
+ await advanceAdaptive(row, outcome, now());
157
209
  return;
158
210
  }
159
211
  if (value.status === 'aborted') {
@@ -191,7 +243,8 @@ export function createWorkflowEngine(deps) {
191
243
  status: 'failed'
192
244
  }, now(), bootID);
193
245
  if (!wrote) return;
194
- fireTerminal(def, instanceID, 'failed', error);
246
+ fireTerminal(def, instanceID, 'failed', error, 'error');
247
+ await advanceAdaptive(row, 'error', now());
195
248
  return;
196
249
  }
197
250
  } catch (cause) {
@@ -255,8 +308,21 @@ export function createWorkflowEngine(deps) {
255
308
  const due = await s.listDueSchedules(at);
256
309
  for (const sched of due){
257
310
  try {
311
+ if (sched.kind === 'adaptive') {
312
+ // subjectKey may itself contain ':', so slice by the known prefix
313
+ // rather than splitting.
314
+ const subjectKey = sched.id.slice(sched.workflow_name.length + 1);
315
+ // next_run is NOT advanced here: it stays stable until the instance
316
+ // terminates, so the fire-time idempotency key blocks re-enqueue and
317
+ // the singleton key interlocks any manual sync of the same subject.
318
+ await api.enqueue(sched.workflow_name, sched.params, {
319
+ idempotencyKey: `schedule:${sched.id}:${sched.next_run}`,
320
+ singletonKey: subjectKey
321
+ });
322
+ continue;
323
+ }
258
324
  if (sched.every == null) {
259
- // Only interval schedules exist today; a cron-only row can't be armed.
325
+ // A cron-only interval row can't be armed (cron is deferred).
260
326
  logger.warn('workflow schedule "{id}" has no interval, skipping', {
261
327
  id: sched.id
262
328
  });
@@ -332,6 +398,9 @@ export function createWorkflowEngine(deps) {
332
398
  }
333
399
  registry.set(def.name, erased);
334
400
  },
401
+ isManagedWorkflow (name) {
402
+ return registry.get(name)?.managed === true;
403
+ },
335
404
  defineQueue,
336
405
  async schedule (name, params, when) {
337
406
  const s = await store();
@@ -369,6 +438,48 @@ export function createWorkflowEngine(deps) {
369
438
  };
370
439
  return handle;
371
440
  },
441
+ async scheduleAdaptive (name, params, opts) {
442
+ validateAdaptivePolicy(opts.policy);
443
+ const s = await store();
444
+ // Deterministic id so a re-arm across restarts targets the same row.
445
+ const id = `${name}:${opts.subjectKey}`;
446
+ const at = now();
447
+ // Insert-or-arm in one atomic write: a fresh subject is created armed, an
448
+ // existing one is re-armed eagerly with its outcome streak preserved.
449
+ // Concurrent enables of the same subject converge instead of racing to a
450
+ // PK conflict.
451
+ await s.upsertAdaptiveSchedule({
452
+ id,
453
+ workflowName: name,
454
+ params,
455
+ policy: opts.policy,
456
+ nextRun: at + opts.policy.changed,
457
+ enabled: true,
458
+ reactivate: opts.reactivate
459
+ }, at);
460
+ const handle = {
461
+ id,
462
+ async disable () {
463
+ await (await store()).setScheduleEnabled(id, false, now());
464
+ },
465
+ async enable () {
466
+ // Re-arm eagerly (now + changed), mirroring reactivation above.
467
+ const s2 = await store();
468
+ const reAt = now();
469
+ const cur = await s2.getSchedule(id);
470
+ await s2.advanceScheduleAdaptive(id, {
471
+ nextRun: reAt + opts.policy.changed,
472
+ lastOutcome: cur?.last_outcome ?? null,
473
+ consecutiveCount: cur?.consecutive_count ?? 0
474
+ }, reAt);
475
+ await s2.setScheduleEnabled(id, true, reAt);
476
+ },
477
+ async cancel () {
478
+ await (await store()).setScheduleEnabled(id, false, now());
479
+ }
480
+ };
481
+ return handle;
482
+ },
372
483
  async enqueue (name, params, opts) {
373
484
  const def = registry.get(name);
374
485
  if (def == null) {
@@ -453,6 +564,39 @@ export function createWorkflowEngine(deps) {
453
564
  const rows = await s.list(filter);
454
565
  return rows.map(toStatus);
455
566
  },
567
+ async getCurrentInstance (name, singletonKey) {
568
+ const s = await store();
569
+ const row = await currentInstanceRow(s, name, singletonKey);
570
+ return row != null ? toStatus(row) : null;
571
+ },
572
+ async getPeriodicSync (scheduleID) {
573
+ const s = await store();
574
+ const sched = await s.getSchedule(scheduleID);
575
+ if (sched == null || sched.kind !== 'adaptive') return null;
576
+ return buildPeriodicSync(s, sched);
577
+ },
578
+ async listPeriodicSyncs (workflowName) {
579
+ const s = await store();
580
+ const scheds = (await s.listSchedules()).filter((x)=>x.kind === 'adaptive' && (workflowName == null || x.workflow_name === workflowName));
581
+ return Promise.all(scheds.map((x)=>buildPeriodicSync(s, x)));
582
+ },
583
+ async setPeriodicSyncEnabled (scheduleID, enabled) {
584
+ const s = await store();
585
+ const sched = await s.getSchedule(scheduleID);
586
+ if (sched == null || sched.kind !== 'adaptive') return null;
587
+ await s.setScheduleEnabled(scheduleID, enabled, now());
588
+ return buildPeriodicSync(s, await s.getSchedule(scheduleID) ?? sched);
589
+ },
590
+ async setPeriodicSyncPolicy (scheduleID, policy) {
591
+ // Validate before touching the store: a bad policy throws loudly and
592
+ // nothing is written.
593
+ validateAdaptivePolicy(policy);
594
+ const s = await store();
595
+ const sched = await s.getSchedule(scheduleID);
596
+ if (sched == null || sched.kind !== 'adaptive') return null;
597
+ await s.setSchedulePolicy(scheduleID, policy, now());
598
+ return buildPeriodicSync(s, await s.getSchedule(scheduleID) ?? sched);
599
+ },
456
600
  events
457
601
  };
458
602
  return {
package/lib/index.d.ts CHANGED
@@ -1,10 +1,9 @@
1
1
  import type { KubunPlugin, PluginFactoryParams } from '@kubun/engine';
2
2
  export type WorkflowPluginOptions = Record<string, never>;
3
3
  export declare function createWorkflowPlugin(_options?: WorkflowPluginOptions): (params: PluginFactoryParams) => KubunPlugin;
4
- export type { RetryPolicy, TerminalInfo, TerminalStatus, WorkflowDefinition, } from './definition.js';
4
+ export { computeNextDelay, validateAdaptivePolicy } from './adaptive.js';
5
5
  export { defineWorkflow } from './definition.js';
6
6
  export type { WorkflowEngine, WorkflowEngineDeps } from './engine.js';
7
7
  export { createWorkflowEngine, parseEvery } from './engine.js';
8
8
  export type { WorkflowContext, WorkflowEventResult } from './schema.js';
9
9
  export { createWorkflowContextFactory, createWorkflowSchemaExtension, subscribeToWorkflowEvents, } from './schema.js';
10
- export type { EnqueueOptions, InstanceHandle, InstanceStatus, ListFilter, QueueOptions, ScheduleHandle, ScheduleSpec, WorkflowAPI, WorkflowEvents, } from './types.js';
package/lib/index.js CHANGED
@@ -25,6 +25,7 @@ export function createWorkflowPlugin(_options = {}) {
25
25
  };
26
26
  };
27
27
  }
28
+ export { computeNextDelay, validateAdaptivePolicy } from './adaptive.js';
28
29
  export { defineWorkflow } from './definition.js';
29
30
  export { createWorkflowEngine, parseEvery } from './engine.js';
30
31
  export { createWorkflowContextFactory, createWorkflowSchemaExtension, subscribeToWorkflowEvents } from './schema.js';
package/lib/schema.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { ContextFactory, SchemaExtension } from '@kubun/engine';
2
+ import type { AdaptivePolicy, InstanceStatus, ListFilter, PeriodicSync, WorkflowAPI, WorkflowEvents } from '@kubun/plugin-workflow-api';
2
3
  import type { EventsSource } from '@sozai/event';
3
- import type { InstanceStatus, ListFilter, WorkflowAPI, WorkflowEvents } from './types.js';
4
4
  export type WorkflowEventResult = {
5
5
  type: keyof WorkflowEvents;
6
6
  instanceID: string;
@@ -31,6 +31,10 @@ export type WorkflowContext = {
31
31
  get(instanceID: string): Promise<InstanceStatus | null>;
32
32
  list(filter: ListFilter): Promise<Array<InstanceStatus>>;
33
33
  subscribeToWorkflowEvents(instanceID?: string | null): AsyncIterableIterator<WorkflowEventResult>;
34
+ getPeriodicSync(scheduleID: string): Promise<PeriodicSync | null>;
35
+ listPeriodicSyncs(workflowName?: string): Promise<Array<PeriodicSync>>;
36
+ setPeriodicSyncEnabled(scheduleID: string, enabled: boolean): Promise<PeriodicSync | null>;
37
+ setPeriodicSyncPolicy(scheduleID: string, policy: AdaptivePolicy): Promise<PeriodicSync | null>;
34
38
  };
35
39
  declare module '@kubun/graphql' {
36
40
  interface PluginContextMap {
package/lib/schema.js CHANGED
@@ -1,3 +1,7 @@
1
+ import { PluginNodeID } from '@kubun/id';
2
+ // The plugin identity and Node type name in every PeriodicSync global id.
3
+ const PLUGIN_NAME = 'workflow';
4
+ const PERIODIC_SYNC_TYPE = 'PeriodicSync';
1
5
  // Every lifecycle key. The source must listen to all of them and tag each event
2
6
  // with its `type`, since one subscription surfaces the whole lifecycle.
3
7
  const EVENT_TYPES = [
@@ -95,11 +99,43 @@ function requireWorkflow(ctx) {
95
99
  }
96
100
  return ctx.workflow;
97
101
  }
102
+ // A PeriodicSync's GraphQL `id` is a global PluginNodeID; the schedule id is its
103
+ // localID. `node(id)` decodes centrally, but the typed fields receive the global
104
+ // id and must decode it here.
105
+ function decodeScheduleID(id) {
106
+ return PluginNodeID.fromString(id).localID;
107
+ }
108
+ // Refusal for a managed schedule reached through the generic control surface.
109
+ // A managed schedule is owned by its workflow's plugin and controlled only
110
+ // through that plugin's own owner-scoped path.
111
+ const MANAGED_SCHEDULE_ERROR = 'schedule is managed by its owning plugin';
98
112
  export function createWorkflowContextFactory(api) {
113
+ // A managed schedule is excluded from the generic controls: queries omit it and
114
+ // mutations refuse it. The connector reaches its own schedules through the
115
+ // engine WorkflowAPI directly (not this context), so its owner-scoped path is
116
+ // untouched.
117
+ const isManaged = (sync)=>sync != null && api.isManagedWorkflow(sync.workflowName);
99
118
  return ()=>({
100
119
  get: (instanceID)=>api.get(instanceID),
101
120
  list: (filter)=>api.list(filter),
102
- subscribeToWorkflowEvents: (instanceID)=>subscribeToWorkflowEvents(api.events, instanceID)
121
+ subscribeToWorkflowEvents: (instanceID)=>subscribeToWorkflowEvents(api.events, instanceID),
122
+ getPeriodicSync: async (scheduleID)=>{
123
+ const sync = await api.getPeriodicSync(scheduleID);
124
+ return isManaged(sync) ? null : sync;
125
+ },
126
+ listPeriodicSyncs: async (workflowName)=>(await api.listPeriodicSyncs(workflowName)).filter((sync)=>!api.isManagedWorkflow(sync.workflowName)),
127
+ setPeriodicSyncEnabled: async (scheduleID, enabled)=>{
128
+ if (isManaged(await api.getPeriodicSync(scheduleID))) {
129
+ throw new Error(MANAGED_SCHEDULE_ERROR);
130
+ }
131
+ return api.setPeriodicSyncEnabled(scheduleID, enabled);
132
+ },
133
+ setPeriodicSyncPolicy: async (scheduleID, policy)=>{
134
+ if (isManaged(await api.getPeriodicSync(scheduleID))) {
135
+ throw new Error(MANAGED_SCHEDULE_ERROR);
136
+ }
137
+ return api.setPeriodicSyncPolicy(scheduleID, policy);
138
+ }
103
139
  });
104
140
  }
105
141
  // ms-epoch timestamps are exposed as `Float` (JS number), matching how the
@@ -124,9 +160,28 @@ type WorkflowEvent {
124
160
  name: String!
125
161
  }
126
162
 
163
+ type PeriodicSync implements Node {
164
+ id: ID!
165
+ subject: String!
166
+ workflowName: String!
167
+ enabled: Boolean!
168
+ nextRun: Float!
169
+ lastOutcome: String
170
+ consecutiveCount: Int!
171
+ policy: JSON!
172
+ currentStatus: WorkflowInstanceStatus
173
+ }
174
+
127
175
  extend type Query {
128
176
  workflowInstance(instanceID: ID!): WorkflowInstanceStatus
129
177
  workflowInstances(name: String, queue: String, status: String): [WorkflowInstanceStatus!]!
178
+ periodicSync(id: ID!): PeriodicSync
179
+ periodicSyncs(workflowName: String): [PeriodicSync!]!
180
+ }
181
+
182
+ extend type Mutation {
183
+ setPeriodicSyncEnabled(id: ID!, enabled: Boolean!): PeriodicSync!
184
+ setPeriodicSyncPolicy(id: ID!, policy: JSON!): PeriodicSync!
130
185
  }
131
186
 
132
187
  extend type Subscription {
@@ -143,7 +198,23 @@ export function createWorkflowSchemaExtension() {
143
198
  name: args.name,
144
199
  queue: args.queue,
145
200
  status: args.status
146
- })
201
+ }),
202
+ periodicSync: (_source, args, context)=>requireWorkflow(context).getPeriodicSync(decodeScheduleID(args.id)),
203
+ periodicSyncs: (_source, args, context)=>requireWorkflow(context).listPeriodicSyncs(args.workflowName)
204
+ },
205
+ mutationFields: {
206
+ setPeriodicSyncEnabled: (_source, args, context)=>requireWorkflow(context).setPeriodicSyncEnabled(decodeScheduleID(args.id), args.enabled),
207
+ setPeriodicSyncPolicy: (_source, args, context)=>requireWorkflow(context).setPeriodicSyncPolicy(decodeScheduleID(args.id), args.policy)
208
+ },
209
+ typeFields: {
210
+ PeriodicSync: {
211
+ id: (source)=>PluginNodeID.create(PLUGIN_NAME, PERIODIC_SYNC_TYPE, source.scheduleID).toString()
212
+ }
213
+ },
214
+ nodeResolvers: {
215
+ PeriodicSync: {
216
+ resolve: (localID, ctx)=>requireWorkflow(ctx).getPeriodicSync(localID)
217
+ }
147
218
  },
148
219
  subscriptionFields: {
149
220
  workflowEvents: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/plugin-workflow",
3
- "version": "0.13.0",
3
+ "version": "0.14.1",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,20 +15,22 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
+ "@kubun/db": "^0.14.0",
19
+ "@kubun/engine": "^0.14.1",
20
+ "@kubun/graphql": "^0.14.1",
21
+ "@kubun/id": "^0.14.0",
22
+ "@kubun/logger": "^0.14.0",
23
+ "@kubun/plugin-workflow-api": "^0.14.1",
24
+ "@kubun/store-workflow": "^0.14.1",
18
25
  "@sozai/event": "^0.1.3",
19
- "@sozai/flow": "^0.3.0",
26
+ "@sozai/flow": "^0.3.1",
20
27
  "@sozai/runtime": "^0.1.0",
21
- "@sozai/schema": "^0.1.1",
22
- "@kubun/engine": "^0.13.0",
23
- "@kubun/logger": "^0.13.0",
24
- "@kubun/store-workflow": "^0.13.0",
25
- "@kubun/graphql": "^0.13.0",
26
- "@kubun/db": "^0.13.0"
28
+ "@sozai/schema": "^0.1.2"
27
29
  },
28
30
  "devDependencies": {
29
- "@testcontainers/postgresql": "^12.1.0",
30
- "@kubun/db-postgres": "^0.13.0",
31
- "@kubun/db-better-sqlite": "^0.13.0"
31
+ "@kubun/db-better-sqlite": "^0.14.0",
32
+ "@kubun/db-postgres": "^0.14.0",
33
+ "@testcontainers/postgresql": "^12.1.0"
32
34
  },
33
35
  "publishConfig": {
34
36
  "access": "public"
package/lib/types.d.ts DELETED
@@ -1,73 +0,0 @@
1
- import type { WorkflowStatus } from '@kubun/store-workflow';
2
- import type { EventsSource } from '@sozai/event';
3
- import type { HandlersRecord } from '@sozai/flow';
4
- import type { WorkflowDefinition } from './definition.js';
5
- export type WorkflowEvents = {
6
- started: {
7
- instanceID: string;
8
- name: string;
9
- };
10
- checkpoint: {
11
- instanceID: string;
12
- name: string;
13
- };
14
- failed: {
15
- instanceID: string;
16
- name: string;
17
- };
18
- completed: {
19
- instanceID: string;
20
- name: string;
21
- };
22
- cancelled: {
23
- instanceID: string;
24
- name: string;
25
- };
26
- };
27
- export type InstanceStatus = {
28
- instanceID: string;
29
- name: string;
30
- queue: string;
31
- status: WorkflowStatus;
32
- attempts: number;
33
- lastError: string | null;
34
- createdAt: number;
35
- updatedAt: number;
36
- };
37
- export type InstanceHandle = {
38
- instanceID: string;
39
- };
40
- export type EnqueueOptions = {
41
- queue?: string;
42
- priority?: number;
43
- idempotencyKey?: string;
44
- singletonKey?: string;
45
- };
46
- export type ListFilter = {
47
- name?: string;
48
- queue?: string;
49
- status?: WorkflowStatus;
50
- };
51
- export type QueueOptions = {
52
- concurrency: number;
53
- ratePerMinute?: number;
54
- };
55
- export type ScheduleSpec = {
56
- every: string;
57
- };
58
- export type ScheduleHandle = {
59
- id: string;
60
- disable(): Promise<void>;
61
- enable(): Promise<void>;
62
- cancel(): Promise<void>;
63
- };
64
- export type WorkflowAPI = {
65
- register<State extends Record<string, unknown>, Handlers extends HandlersRecord<State> = HandlersRecord<State>>(def: WorkflowDefinition<State, Handlers>): void;
66
- defineQueue(name: string, opts: QueueOptions): void;
67
- enqueue(name: string, params?: unknown, opts?: EnqueueOptions): Promise<InstanceHandle>;
68
- schedule(name: string, params: unknown, when: ScheduleSpec): Promise<ScheduleHandle>;
69
- cancel(instanceID: string): Promise<void>;
70
- get(instanceID: string): Promise<InstanceStatus | null>;
71
- list(filter?: ListFilter): Promise<Array<InstanceStatus>>;
72
- events: EventsSource<WorkflowEvents>;
73
- };
package/lib/types.js DELETED
@@ -1 +0,0 @@
1
- export { };