@kubun/plugin-workflow 0.14.0 → 0.15.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.
Files changed (3) hide show
  1. package/lib/engine.js +16 -22
  2. package/lib/schema.js +26 -4
  3. package/package.json +11 -11
package/lib/engine.js CHANGED
@@ -398,6 +398,9 @@ export function createWorkflowEngine(deps) {
398
398
  }
399
399
  registry.set(def.name, erased);
400
400
  },
401
+ isManagedWorkflow (name) {
402
+ return registry.get(name)?.managed === true;
403
+ },
401
404
  defineQueue,
402
405
  async schedule (name, params, when) {
403
406
  const s = await store();
@@ -441,28 +444,19 @@ export function createWorkflowEngine(deps) {
441
444
  // Deterministic id so a re-arm across restarts targets the same row.
442
445
  const id = `${name}:${opts.subjectKey}`;
443
446
  const at = now();
444
- const existing = await s.getSchedule(id);
445
- if (existing == null) {
446
- await s.insertSchedule({
447
- id,
448
- workflowName: name,
449
- params,
450
- cron: null,
451
- every: null,
452
- kind: 'adaptive',
453
- policy: opts.policy,
454
- nextRun: at + opts.policy.changed,
455
- enabled: true
456
- }, at);
457
- } else {
458
- // Reactivation: re-arm eagerly, preserving the stored streak.
459
- await s.advanceScheduleAdaptive(id, {
460
- nextRun: at + opts.policy.changed,
461
- lastOutcome: existing.last_outcome,
462
- consecutiveCount: existing.consecutive_count
463
- }, at);
464
- await s.setScheduleEnabled(id, true, at);
465
- }
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);
466
460
  const handle = {
467
461
  id,
468
462
  async disable () {
package/lib/schema.js CHANGED
@@ -105,15 +105,37 @@ function requireWorkflow(ctx) {
105
105
  function decodeScheduleID(id) {
106
106
  return PluginNodeID.fromString(id).localID;
107
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';
108
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);
109
118
  return ()=>({
110
119
  get: (instanceID)=>api.get(instanceID),
111
120
  list: (filter)=>api.list(filter),
112
121
  subscribeToWorkflowEvents: (instanceID)=>subscribeToWorkflowEvents(api.events, instanceID),
113
- getPeriodicSync: (scheduleID)=>api.getPeriodicSync(scheduleID),
114
- listPeriodicSyncs: (workflowName)=>api.listPeriodicSyncs(workflowName),
115
- setPeriodicSyncEnabled: (scheduleID, enabled)=>api.setPeriodicSyncEnabled(scheduleID, enabled),
116
- setPeriodicSyncPolicy: (scheduleID, policy)=>api.setPeriodicSyncPolicy(scheduleID, policy)
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
+ }
117
139
  });
118
140
  }
119
141
  // ms-epoch timestamps are exposed as `Float` (JS number), matching how the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kubun/plugin-workflow",
3
- "version": "0.14.0",
3
+ "version": "0.15.0",
4
4
  "keywords": [],
5
5
  "license": "see LICENSE.md",
6
6
  "sideEffects": false,
@@ -15,21 +15,21 @@
15
15
  "LICENSE.md"
16
16
  ],
17
17
  "dependencies": {
18
- "@kubun/db": "^0.14.0",
19
- "@kubun/engine": "^0.14.0",
20
- "@kubun/graphql": "^0.14.0",
21
- "@kubun/id": "^0.14.0",
22
- "@kubun/logger": "^0.14.0",
23
- "@kubun/plugin-workflow-api": "^0.14.0",
24
- "@kubun/store-workflow": "^0.14.0",
18
+ "@kubun/db": "^0.15.0",
19
+ "@kubun/engine": "^0.15.0",
20
+ "@kubun/graphql": "^0.15.0",
21
+ "@kubun/id": "^0.15.0",
22
+ "@kubun/logger": "^0.15.0",
23
+ "@kubun/plugin-workflow-api": "^0.15.0",
24
+ "@kubun/store-workflow": "^0.15.0",
25
25
  "@sozai/event": "^0.1.3",
26
26
  "@sozai/flow": "^0.3.1",
27
27
  "@sozai/runtime": "^0.1.0",
28
- "@sozai/schema": "^0.1.1"
28
+ "@sozai/schema": "^0.1.2"
29
29
  },
30
30
  "devDependencies": {
31
- "@kubun/db-better-sqlite": "^0.14.0",
32
- "@kubun/db-postgres": "^0.14.0",
31
+ "@kubun/db-better-sqlite": "^0.15.0",
32
+ "@kubun/db-postgres": "^0.15.0",
33
33
  "@testcontainers/postgresql": "^12.1.0"
34
34
  },
35
35
  "publishConfig": {