Files
Richard Osborne ddcb9cd02e feat: Phase 5 BYOB foundation + Phase 3 GitHub integration
Phase 5 - BYOB Backend (TASK-007A/B):
- LocalSQL Adapter with full CloudStore API compatibility
- QueryBuilder translates Parse-style queries to SQL
- SchemaManager with PostgreSQL/Supabase export
- LocalBackendServer with REST endpoints
- BackendManager with IPC handlers for Electron
- In-memory fallback when better-sqlite3 unavailable

Phase 3 - GitHub Panel (GIT-004):
- Issues tab with list/detail views
- Pull Requests tab with list/detail views
- GitHub API client with OAuth support
- Repository info hook integration

Phase 3 - Editor UX Bugfixes (TASK-013):
- Legacy runtime detection banners
- Read-only enforcement for legacy projects
- Code editor modal close improvements
- Property panel stuck state fix
- Blockly node deletion and UI polish

Phase 11 - Cloud Functions Planning:
- Architecture documentation for workflow automation
- Execution history storage schema design
- Canvas overlay concept for debugging

Docs: Updated LEARNINGS.md and COMMON-ISSUES.md
2026-01-15 17:37:15 +01:00

4.3 KiB

CF11-003: Wait/Delay Nodes

Metadata

Field Value
ID CF11-003
Phase Phase 11
Series 1 - Advanced Workflow Nodes
Priority 🟢 Medium
Difficulty 🟢 Low
Estimated Time 4-6 hours
Prerequisites Phase 5 TASK-007C (Workflow Runtime)
Branch feature/cf11-003-delay-nodes

Objective

Create timing-related workflow nodes: Wait for explicit delays, Wait Until for scheduled execution, and debounce utilities - essential for rate limiting and scheduled workflows.

Background

Workflows often need timing control:

  • Wait: Pause execution for a duration (rate limiting APIs)
  • Wait Until: Execute at a specific time (scheduled tasks)
  • Debounce: Prevent rapid repeated execution

Scope

In Scope

  • Wait Node (delay for X milliseconds)
  • Wait Until Node (wait until specific time)
  • Debounce Node (rate limit execution)

Out of Scope

  • Cron scheduling (handled by triggers)
  • Throttle node (future)

Technical Approach

Wait Node

const WaitNode = {
  name: 'Workflow Wait',
  displayName: 'Wait',
  category: 'Workflow Logic',

  inputs: {
    run: { type: 'signal' },
    duration: { type: 'number', displayName: 'Duration (ms)', default: 1000 },
    unit: {
      type: 'enum',
      options: ['milliseconds', 'seconds', 'minutes', 'hours'],
      default: 'milliseconds'
    }
  },

  outputs: {
    done: { type: 'signal', displayName: 'Done' }
  },

  async run(context) {
    let ms = context.inputs.duration || 1000;
    const unit = context.inputs.unit || 'milliseconds';

    // Convert to milliseconds
    const multipliers = {
      milliseconds: 1,
      seconds: 1000,
      minutes: 60 * 1000,
      hours: 60 * 60 * 1000
    };
    ms = ms * (multipliers[unit] || 1);

    await new Promise((resolve) => setTimeout(resolve, ms));
    context.triggerOutput('done');
  }
};

Wait Until Node

const WaitUntilNode = {
  name: 'Workflow Wait Until',
  displayName: 'Wait Until',
  category: 'Workflow Logic',

  inputs: {
    run: { type: 'signal' },
    targetTime: { type: 'string', displayName: 'Target Time (ISO)' },
    targetDate: { type: 'date', displayName: 'Target Date' }
  },

  outputs: {
    done: { type: 'signal' },
    skipped: { type: 'signal', displayName: 'Already Passed' }
  },

  async run(context) {
    const target = context.inputs.targetDate || new Date(context.inputs.targetTime);
    const now = Date.now();
    const targetMs = target.getTime();

    if (targetMs <= now) {
      // Time already passed
      context.triggerOutput('skipped');
      return;
    }

    const waitTime = targetMs - now;
    await new Promise((resolve) => setTimeout(resolve, waitTime));
    context.triggerOutput('done');
  }
};

Debounce Node

const DebounceNode = {
  name: 'Workflow Debounce',
  displayName: 'Debounce',
  category: 'Workflow Logic',

  inputs: {
    run: { type: 'signal' },
    delay: { type: 'number', default: 500 }
  },

  outputs: {
    trigger: { type: 'signal' }
  },

  setup(context) {
    context._debounceTimer = null;
  },

  run(context) {
    if (context._debounceTimer) {
      clearTimeout(context._debounceTimer);
    }

    context._debounceTimer = setTimeout(() => {
      context.triggerOutput('trigger');
      context._debounceTimer = null;
    }, context.inputs.delay || 500);
  }
};

Implementation Steps

  1. Wait Node (2h) - Simple delay, unit conversion
  2. Wait Until Node (2h) - Date parsing, time calculation
  3. Debounce Node (1h) - Timer management
  4. Testing (1h) - Unit tests

Success Criteria

  • Wait delays for specified duration
  • Wait supports multiple time units
  • Wait Until triggers at target time
  • Wait Until handles past times gracefully
  • Debounce prevents rapid triggers

References