@meetploy/cli 1.20.0 → 1.22.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 +203 -0
- package/dist/dashboard-dist/assets/main-DKimSd85.js +354 -0
- package/dist/dashboard-dist/assets/main-kKaL2zMb.css +1 -0
- package/dist/dashboard-dist/index.html +2 -2
- package/dist/dev.js +246 -41
- package/dist/index.js +320 -62
- package/package.json +1 -1
- package/dist/dashboard-dist/assets/main-5Kt9I_hM.css +0 -1
- package/dist/dashboard-dist/assets/main-CZRtZPnP.js +0 -354
package/dist/dev.js
CHANGED
|
@@ -4,9 +4,9 @@ import { readFile, existsSync, readFileSync, mkdirSync, unlinkSync, writeFileSyn
|
|
|
4
4
|
import { dirname, join } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import 'esbuild';
|
|
7
|
-
import 'chokidar';
|
|
8
7
|
import { promisify } from 'util';
|
|
9
8
|
import { parse } from 'yaml';
|
|
9
|
+
import 'chokidar';
|
|
10
10
|
import { randomUUID, createHmac, pbkdf2Sync, timingSafeEqual, randomBytes } from 'crypto';
|
|
11
11
|
import { serve } from '@hono/node-server';
|
|
12
12
|
import { Hono } from 'hono';
|
|
@@ -15,19 +15,18 @@ import { getCookie, deleteCookie, setCookie } from 'hono/cookie';
|
|
|
15
15
|
import { DatabaseSync, backup } from 'node:sqlite';
|
|
16
16
|
|
|
17
17
|
createRequire(import.meta.url);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
var
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
red: "\x1B[31m"};
|
|
24
|
-
function timestamp() {
|
|
25
|
-
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
18
|
+
promisify(readFile);
|
|
19
|
+
var DEFAULT_WORKFLOW_TIMEOUT_S = 10 * 60;
|
|
20
|
+
var DEFAULT_STEP_TIMEOUT_S = 2 * 60;
|
|
21
|
+
function getWorkflowName(value) {
|
|
22
|
+
return typeof value === "string" ? value : value.name;
|
|
26
23
|
}
|
|
27
|
-
function
|
|
28
|
-
|
|
24
|
+
function getWorkflowTimeout(value) {
|
|
25
|
+
return typeof value === "string" ? void 0 : value.timeout;
|
|
26
|
+
}
|
|
27
|
+
function getWorkflowStepTimeout(value) {
|
|
28
|
+
return typeof value === "string" ? void 0 : value.step_timeout;
|
|
29
29
|
}
|
|
30
|
-
promisify(readFile);
|
|
31
30
|
function readPloyConfigSync(projectDir, configPath) {
|
|
32
31
|
const configFile = configPath ?? "ploy.yaml";
|
|
33
32
|
const fullPath = join(projectDir, configFile);
|
|
@@ -49,6 +48,22 @@ function readPloyConfig(projectDir, configPath) {
|
|
|
49
48
|
}
|
|
50
49
|
return config;
|
|
51
50
|
}
|
|
51
|
+
|
|
52
|
+
// ../emulator/dist/utils/logger.js
|
|
53
|
+
var COLORS = {
|
|
54
|
+
reset: "\x1B[0m",
|
|
55
|
+
dim: "\x1B[2m",
|
|
56
|
+
yellow: "\x1B[33m",
|
|
57
|
+
red: "\x1B[31m"};
|
|
58
|
+
function timestamp() {
|
|
59
|
+
return (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", { hour12: false });
|
|
60
|
+
}
|
|
61
|
+
function warn(message) {
|
|
62
|
+
console.log(`${COLORS.dim}[${timestamp()}]${COLORS.reset} ${COLORS.yellow}[ploy]${COLORS.reset} ${message}`);
|
|
63
|
+
}
|
|
64
|
+
function error(message) {
|
|
65
|
+
console.error(`${COLORS.dim}[${timestamp()}]${COLORS.reset} ${COLORS.red}[ploy]${COLORS.reset} ${message}`);
|
|
66
|
+
}
|
|
52
67
|
function getDataDir(projectDir) {
|
|
53
68
|
return join(projectDir, ".ploy");
|
|
54
69
|
}
|
|
@@ -561,7 +576,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
561
576
|
return c.json({ error: "Query is required" }, 400);
|
|
562
577
|
}
|
|
563
578
|
try {
|
|
564
|
-
const db = dbManager2.
|
|
579
|
+
const db = dbManager2.getDatabase(resourceName);
|
|
565
580
|
const startTime = Date.now();
|
|
566
581
|
const stmt = db.prepare(query);
|
|
567
582
|
const isSelect = query.trim().toUpperCase().startsWith("SELECT");
|
|
@@ -599,7 +614,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
599
614
|
return c.json({ error: `Database binding '${binding}' not found` }, 404);
|
|
600
615
|
}
|
|
601
616
|
try {
|
|
602
|
-
const db = dbManager2.
|
|
617
|
+
const db = dbManager2.getDatabase(resourceName);
|
|
603
618
|
const tables = db.prepare(`SELECT name FROM sqlite_master
|
|
604
619
|
WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_litestream_%'
|
|
605
620
|
ORDER BY name`).all();
|
|
@@ -618,7 +633,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
618
633
|
const limit = parseInt(c.req.query("limit") ?? "50", 10);
|
|
619
634
|
const offset = parseInt(c.req.query("offset") ?? "0", 10);
|
|
620
635
|
try {
|
|
621
|
-
const db = dbManager2.
|
|
636
|
+
const db = dbManager2.getDatabase(resourceName);
|
|
622
637
|
const columnsResult = db.prepare(`PRAGMA table_info("${tableName}")`).all();
|
|
623
638
|
const columns = columnsResult.map((col) => col.name);
|
|
624
639
|
const countResult = db.prepare(`SELECT COUNT(*) as count FROM "${tableName}"`).get();
|
|
@@ -636,7 +651,7 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
636
651
|
return c.json({ error: `Database binding '${binding}' not found` }, 404);
|
|
637
652
|
}
|
|
638
653
|
try {
|
|
639
|
-
const db = dbManager2.
|
|
654
|
+
const db = dbManager2.getDatabase(resourceName);
|
|
640
655
|
const tablesResult = db.prepare(`SELECT name FROM sqlite_master
|
|
641
656
|
WHERE type='table' AND name NOT LIKE 'sqlite_%'
|
|
642
657
|
ORDER BY name`).all();
|
|
@@ -789,13 +804,50 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
789
804
|
app.get("/api/workflow/:binding/executions", (c) => {
|
|
790
805
|
const binding = c.req.param("binding");
|
|
791
806
|
const workflowConfig = config.workflow?.[binding];
|
|
792
|
-
const limit = parseInt(c.req.query("limit") ?? "20", 10);
|
|
807
|
+
const limit = Math.min(Math.max(1, parseInt(c.req.query("limit") ?? "20", 10)), 100);
|
|
808
|
+
const offset = Math.max(0, parseInt(c.req.query("offset") ?? "0", 10));
|
|
809
|
+
const statusFilter = c.req.query("status")?.toLowerCase() ?? null;
|
|
810
|
+
const sinceRaw = c.req.query("since") ?? null;
|
|
793
811
|
if (!workflowConfig) {
|
|
794
812
|
return c.json({ error: "Workflow not found" }, 404);
|
|
795
813
|
}
|
|
814
|
+
const validStatuses = /* @__PURE__ */ new Set([
|
|
815
|
+
"pending",
|
|
816
|
+
"running",
|
|
817
|
+
"completed",
|
|
818
|
+
"failed",
|
|
819
|
+
"cancelled"
|
|
820
|
+
]);
|
|
796
821
|
try {
|
|
797
822
|
const db = dbManager2.emulatorDb;
|
|
798
|
-
const workflowName = workflowConfig;
|
|
823
|
+
const workflowName = getWorkflowName(workflowConfig);
|
|
824
|
+
let sinceTs = null;
|
|
825
|
+
if (sinceRaw) {
|
|
826
|
+
const match = sinceRaw.match(/^(\d+)(s|m|h|d)?$/);
|
|
827
|
+
if (match) {
|
|
828
|
+
const val = parseInt(match[1], 10);
|
|
829
|
+
const unit = match[2] ?? "s";
|
|
830
|
+
const multipliers = {
|
|
831
|
+
s: 1,
|
|
832
|
+
m: 60,
|
|
833
|
+
h: 3600,
|
|
834
|
+
d: 86400
|
|
835
|
+
};
|
|
836
|
+
sinceTs = Math.floor(Date.now() / 1e3) - val * (multipliers[unit] ?? 1);
|
|
837
|
+
}
|
|
838
|
+
}
|
|
839
|
+
const conditions = ["e.workflow_name = ?"];
|
|
840
|
+
const queryArgs = [workflowName];
|
|
841
|
+
if (statusFilter && validStatuses.has(statusFilter)) {
|
|
842
|
+
conditions.push("e.status = ?");
|
|
843
|
+
queryArgs.push(statusFilter);
|
|
844
|
+
}
|
|
845
|
+
if (sinceTs !== null) {
|
|
846
|
+
conditions.push("e.created_at >= ?");
|
|
847
|
+
queryArgs.push(sinceTs);
|
|
848
|
+
}
|
|
849
|
+
const where = conditions.join(" AND ");
|
|
850
|
+
const countRow = db.prepare(`SELECT COUNT(*) as count FROM workflow_executions e WHERE ${where}`).get(...queryArgs);
|
|
799
851
|
const executions = db.prepare(`SELECT
|
|
800
852
|
e.id,
|
|
801
853
|
e.workflow_name,
|
|
@@ -807,9 +859,9 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
807
859
|
(SELECT COUNT(*) FROM workflow_steps WHERE execution_id = e.id) as steps_count,
|
|
808
860
|
(SELECT COUNT(*) FROM workflow_steps WHERE execution_id = e.id AND status = 'completed') as steps_completed
|
|
809
861
|
FROM workflow_executions e
|
|
810
|
-
WHERE
|
|
862
|
+
WHERE ${where}
|
|
811
863
|
ORDER BY e.created_at DESC
|
|
812
|
-
LIMIT ?`).all(
|
|
864
|
+
LIMIT ? OFFSET ?`).all(...queryArgs, limit, offset);
|
|
813
865
|
return c.json({
|
|
814
866
|
executions: executions.map((e) => ({
|
|
815
867
|
id: e.id,
|
|
@@ -821,7 +873,10 @@ function createDashboardRoutes(app, dbManager2, config) {
|
|
|
821
873
|
stepsCompleted: e.steps_completed,
|
|
822
874
|
errorMessage: e.error,
|
|
823
875
|
createdAt: new Date(e.created_at * 1e3).toISOString()
|
|
824
|
-
}))
|
|
876
|
+
})),
|
|
877
|
+
total: countRow.count,
|
|
878
|
+
limit,
|
|
879
|
+
offset
|
|
825
880
|
});
|
|
826
881
|
} catch (err) {
|
|
827
882
|
return c.json({ error: err instanceof Error ? err.message : String(err) }, 500);
|
|
@@ -1558,7 +1613,7 @@ function createWorkflowHandlers(db, workerUrl) {
|
|
|
1558
1613
|
}
|
|
1559
1614
|
const now = Math.floor(Date.now() / 1e3);
|
|
1560
1615
|
if (existing) {
|
|
1561
|
-
db.prepare(`UPDATE workflow_steps SET status = 'running' WHERE execution_id = ? AND step_name = ?`).run(executionId, stepName);
|
|
1616
|
+
db.prepare(`UPDATE workflow_steps SET status = 'running', error = NULL, created_at = ? WHERE execution_id = ? AND step_name = ? AND step_index = ?`).run(now, executionId, stepName, stepIndex);
|
|
1562
1617
|
} else {
|
|
1563
1618
|
db.prepare(`INSERT INTO workflow_steps (execution_id, step_name, step_index, status, created_at)
|
|
1564
1619
|
VALUES (?, ?, ?, 'running', ?)`).run(executionId, stepName, stepIndex, now);
|
|
@@ -1572,10 +1627,16 @@ function createWorkflowHandlers(db, workerUrl) {
|
|
|
1572
1627
|
const stepCompleteHandler = async (c) => {
|
|
1573
1628
|
try {
|
|
1574
1629
|
const body = await c.req.json();
|
|
1575
|
-
const { executionId, stepName, output, durationMs } = body;
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1630
|
+
const { executionId, stepName, stepIndex, output, durationMs } = body;
|
|
1631
|
+
if (stepIndex !== void 0) {
|
|
1632
|
+
db.prepare(`UPDATE workflow_steps
|
|
1633
|
+
SET status = 'completed', output = ?, duration_ms = ?
|
|
1634
|
+
WHERE execution_id = ? AND step_name = ? AND step_index = ?`).run(JSON.stringify(output), durationMs, executionId, stepName, stepIndex);
|
|
1635
|
+
} else {
|
|
1636
|
+
db.prepare(`UPDATE workflow_steps
|
|
1637
|
+
SET status = 'completed', output = ?, duration_ms = ?
|
|
1638
|
+
WHERE execution_id = ? AND step_name = ?`).run(JSON.stringify(output), durationMs, executionId, stepName);
|
|
1639
|
+
}
|
|
1579
1640
|
return c.json({ success: true });
|
|
1580
1641
|
} catch (err) {
|
|
1581
1642
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1585,10 +1646,16 @@ function createWorkflowHandlers(db, workerUrl) {
|
|
|
1585
1646
|
const stepFailHandler = async (c) => {
|
|
1586
1647
|
try {
|
|
1587
1648
|
const body = await c.req.json();
|
|
1588
|
-
const { executionId, stepName, error: error2, durationMs } = body;
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1649
|
+
const { executionId, stepName, stepIndex, error: error2, durationMs } = body;
|
|
1650
|
+
if (stepIndex !== void 0) {
|
|
1651
|
+
db.prepare(`UPDATE workflow_steps
|
|
1652
|
+
SET status = 'failed', error = ?, duration_ms = ?
|
|
1653
|
+
WHERE execution_id = ? AND step_name = ? AND step_index = ?`).run(error2, durationMs, executionId, stepName, stepIndex);
|
|
1654
|
+
} else {
|
|
1655
|
+
db.prepare(`UPDATE workflow_steps
|
|
1656
|
+
SET status = 'failed', error = ?, duration_ms = ?
|
|
1657
|
+
WHERE execution_id = ? AND step_name = ?`).run(error2, durationMs, executionId, stepName);
|
|
1658
|
+
}
|
|
1592
1659
|
return c.json({ success: true });
|
|
1593
1660
|
} catch (err) {
|
|
1594
1661
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -1634,13 +1701,121 @@ function createWorkflowHandlers(db, workerUrl) {
|
|
|
1634
1701
|
failHandler
|
|
1635
1702
|
};
|
|
1636
1703
|
}
|
|
1704
|
+
var DEFAULT_STALL_TIMEOUT_S = 100 * 60;
|
|
1705
|
+
function startWorkflowTimeoutEnforcement(db, intervalMs = 5e3, timeouts) {
|
|
1706
|
+
let isRunning = false;
|
|
1707
|
+
return setInterval(() => void (async () => {
|
|
1708
|
+
if (isRunning) {
|
|
1709
|
+
return;
|
|
1710
|
+
}
|
|
1711
|
+
isRunning = true;
|
|
1712
|
+
try {
|
|
1713
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1714
|
+
const allTimeouts = [
|
|
1715
|
+
timeouts.defaultTimeoutS,
|
|
1716
|
+
...Object.values(timeouts.perWorkflow)
|
|
1717
|
+
];
|
|
1718
|
+
const minTimeoutS = Math.min(...allTimeouts);
|
|
1719
|
+
const queryThreshold = now - minTimeoutS;
|
|
1720
|
+
const timedOutCandidates = db.prepare(`SELECT id, workflow_name, started_at FROM workflow_executions
|
|
1721
|
+
WHERE status = 'running' AND started_at < ?`).all(queryThreshold);
|
|
1722
|
+
for (const execution of timedOutCandidates) {
|
|
1723
|
+
const timeoutS = timeouts.perWorkflow[execution.workflow_name] ?? timeouts.defaultTimeoutS;
|
|
1724
|
+
const threshold = now - timeoutS;
|
|
1725
|
+
if (execution.started_at >= threshold) {
|
|
1726
|
+
continue;
|
|
1727
|
+
}
|
|
1728
|
+
db.prepare(`UPDATE workflow_executions
|
|
1729
|
+
SET status = 'failed', error = ?, completed_at = ?
|
|
1730
|
+
WHERE id = ? AND status = 'running'`).run(`Workflow exceeded timeout of ${timeoutS}s`, now, execution.id);
|
|
1731
|
+
}
|
|
1732
|
+
const allStepTimeouts = [
|
|
1733
|
+
timeouts.defaultStepTimeoutS,
|
|
1734
|
+
...Object.values(timeouts.perWorkflowStep)
|
|
1735
|
+
];
|
|
1736
|
+
const minStepTimeoutS = Math.min(...allStepTimeouts);
|
|
1737
|
+
const stepQueryThreshold = now - minStepTimeoutS;
|
|
1738
|
+
const timedOutSteps = db.prepare(`SELECT ws.id, ws.execution_id, ws.step_name, ws.step_index, ws.created_at, we.workflow_name
|
|
1739
|
+
FROM workflow_steps ws
|
|
1740
|
+
JOIN workflow_executions we ON we.id = ws.execution_id
|
|
1741
|
+
WHERE ws.status = 'running' AND ws.created_at < ?`).all(stepQueryThreshold);
|
|
1742
|
+
for (const step of timedOutSteps) {
|
|
1743
|
+
const stepTimeoutS = timeouts.perWorkflowStep[step.workflow_name] ?? timeouts.defaultStepTimeoutS;
|
|
1744
|
+
const stepThreshold = now - stepTimeoutS;
|
|
1745
|
+
if (step.created_at >= stepThreshold) {
|
|
1746
|
+
continue;
|
|
1747
|
+
}
|
|
1748
|
+
db.transaction(() => {
|
|
1749
|
+
db.prepare(`UPDATE workflow_steps
|
|
1750
|
+
SET status = 'failed', error = ?
|
|
1751
|
+
WHERE id = ? AND status = 'running'`).run(`Step exceeded timeout of ${stepTimeoutS}s`, step.id);
|
|
1752
|
+
db.prepare(`UPDATE workflow_executions
|
|
1753
|
+
SET status = 'failed', error = ?, completed_at = ?
|
|
1754
|
+
WHERE id = ? AND status = 'running'`).run(`Step '${step.step_name}' exceeded timeout of ${stepTimeoutS}s`, now, step.execution_id);
|
|
1755
|
+
})();
|
|
1756
|
+
}
|
|
1757
|
+
} catch (err) {
|
|
1758
|
+
warn(`Failed to enforce workflow timeouts: ${err instanceof Error ? err.message : String(err)}`);
|
|
1759
|
+
} finally {
|
|
1760
|
+
isRunning = false;
|
|
1761
|
+
}
|
|
1762
|
+
})(), intervalMs);
|
|
1763
|
+
}
|
|
1764
|
+
function startStalledWorkflowRecovery(db, workerUrl, intervalMs = 5e3, stallTimeoutS = DEFAULT_STALL_TIMEOUT_S) {
|
|
1765
|
+
let isRunning = false;
|
|
1766
|
+
return setInterval(() => void (async () => {
|
|
1767
|
+
if (isRunning) {
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
isRunning = true;
|
|
1771
|
+
try {
|
|
1772
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
1773
|
+
const queryThreshold = now - stallTimeoutS;
|
|
1774
|
+
const stalledExecutions = db.prepare(`SELECT id, workflow_name, input, started_at FROM workflow_executions
|
|
1775
|
+
WHERE status = 'running' AND started_at < ?`).all(queryThreshold);
|
|
1776
|
+
for (const execution of stalledExecutions) {
|
|
1777
|
+
let input = null;
|
|
1778
|
+
if (execution.input) {
|
|
1779
|
+
try {
|
|
1780
|
+
input = JSON.parse(execution.input);
|
|
1781
|
+
} catch {
|
|
1782
|
+
db.prepare(`UPDATE workflow_executions SET status = 'failed', error = 'Malformed workflow input payload', completed_at = ? WHERE id = ?`).run(now, execution.id);
|
|
1783
|
+
continue;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
db.prepare(`UPDATE workflow_steps SET status = 'pending', error = NULL
|
|
1787
|
+
WHERE execution_id = ? AND status = 'running'`).run(execution.id);
|
|
1788
|
+
try {
|
|
1789
|
+
const response = await fetch(workerUrl, {
|
|
1790
|
+
method: "POST",
|
|
1791
|
+
headers: {
|
|
1792
|
+
"Content-Type": "application/json",
|
|
1793
|
+
"X-Ploy-Workflow-Execution": "true",
|
|
1794
|
+
"X-Ploy-Workflow-Name": execution.workflow_name,
|
|
1795
|
+
"X-Ploy-Execution-Id": execution.id
|
|
1796
|
+
},
|
|
1797
|
+
body: JSON.stringify(input)
|
|
1798
|
+
});
|
|
1799
|
+
if (response.ok) {
|
|
1800
|
+
db.prepare(`UPDATE workflow_executions SET started_at = ? WHERE id = ?`).run(Math.floor(Date.now() / 1e3), execution.id);
|
|
1801
|
+
}
|
|
1802
|
+
} catch {
|
|
1803
|
+
}
|
|
1804
|
+
}
|
|
1805
|
+
} catch (err) {
|
|
1806
|
+
warn(`Failed to recover stalled workflows: ${err instanceof Error ? err.message : String(err)}`);
|
|
1807
|
+
} finally {
|
|
1808
|
+
isRunning = false;
|
|
1809
|
+
}
|
|
1810
|
+
})(), intervalMs);
|
|
1811
|
+
}
|
|
1637
1812
|
|
|
1638
1813
|
// ../emulator/dist/services/mock-server.js
|
|
1639
1814
|
var DEFAULT_MOCK_SERVER_PORT = 4003;
|
|
1640
1815
|
async function startMockServer(dbManager2, config, options = {}) {
|
|
1641
1816
|
const app = new Hono();
|
|
1642
1817
|
if (config.db) {
|
|
1643
|
-
const dbHandler = createDbHandler(dbManager2.
|
|
1818
|
+
const dbHandler = createDbHandler(dbManager2.getDatabase);
|
|
1644
1819
|
app.post("/db", dbHandler);
|
|
1645
1820
|
}
|
|
1646
1821
|
if (config.queue) {
|
|
@@ -1651,6 +1826,8 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1651
1826
|
app.post("/queue/ack", queueHandlers.ackHandler);
|
|
1652
1827
|
app.post("/queue/retry", queueHandlers.retryHandler);
|
|
1653
1828
|
}
|
|
1829
|
+
let stalledRecoveryInterval;
|
|
1830
|
+
let timeoutEnforcementInterval;
|
|
1654
1831
|
if (config.workflow) {
|
|
1655
1832
|
const workflowHandlers = createWorkflowHandlers(dbManager2.emulatorDb, options.workerUrl);
|
|
1656
1833
|
app.post("/workflow/trigger", workflowHandlers.triggerHandler);
|
|
@@ -1661,6 +1838,28 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1661
1838
|
app.post("/workflow/step/fail", workflowHandlers.stepFailHandler);
|
|
1662
1839
|
app.post("/workflow/complete", workflowHandlers.completeHandler);
|
|
1663
1840
|
app.post("/workflow/fail", workflowHandlers.failHandler);
|
|
1841
|
+
const perWorkflow = {};
|
|
1842
|
+
const perWorkflowStep = {};
|
|
1843
|
+
for (const [, value] of Object.entries(config.workflow)) {
|
|
1844
|
+
const workflowName = getWorkflowName(value);
|
|
1845
|
+
const timeout = getWorkflowTimeout(value);
|
|
1846
|
+
const stepTimeout = getWorkflowStepTimeout(value);
|
|
1847
|
+
if (timeout !== void 0) {
|
|
1848
|
+
perWorkflow[workflowName] = timeout;
|
|
1849
|
+
}
|
|
1850
|
+
if (stepTimeout !== void 0) {
|
|
1851
|
+
perWorkflowStep[workflowName] = stepTimeout;
|
|
1852
|
+
}
|
|
1853
|
+
}
|
|
1854
|
+
timeoutEnforcementInterval = startWorkflowTimeoutEnforcement(dbManager2.emulatorDb, 5e3, {
|
|
1855
|
+
perWorkflow,
|
|
1856
|
+
perWorkflowStep,
|
|
1857
|
+
defaultTimeoutS: DEFAULT_WORKFLOW_TIMEOUT_S,
|
|
1858
|
+
defaultStepTimeoutS: DEFAULT_STEP_TIMEOUT_S
|
|
1859
|
+
});
|
|
1860
|
+
if (options.workerUrl) {
|
|
1861
|
+
stalledRecoveryInterval = startStalledWorkflowRecovery(dbManager2.emulatorDb, options.workerUrl, 5e3, DEFAULT_STALL_TIMEOUT_S);
|
|
1862
|
+
}
|
|
1664
1863
|
}
|
|
1665
1864
|
if (config.cache) {
|
|
1666
1865
|
const cacheHandlers = createCacheHandlers(dbManager2.emulatorDb);
|
|
@@ -1708,6 +1907,12 @@ async function startMockServer(dbManager2, config, options = {}) {
|
|
|
1708
1907
|
resolve({
|
|
1709
1908
|
port: info.port,
|
|
1710
1909
|
close: () => new Promise((res) => {
|
|
1910
|
+
if (stalledRecoveryInterval) {
|
|
1911
|
+
clearInterval(stalledRecoveryInterval);
|
|
1912
|
+
}
|
|
1913
|
+
if (timeoutEnforcementInterval) {
|
|
1914
|
+
clearInterval(timeoutEnforcementInterval);
|
|
1915
|
+
}
|
|
1711
1916
|
server.close(() => {
|
|
1712
1917
|
res();
|
|
1713
1918
|
});
|
|
@@ -1943,36 +2148,36 @@ CREATE INDEX IF NOT EXISTS idx_timer_executions_name
|
|
|
1943
2148
|
`;
|
|
1944
2149
|
function initializeDatabases(projectDir) {
|
|
1945
2150
|
const dataDir = ensureDataDir(projectDir);
|
|
1946
|
-
const
|
|
2151
|
+
const databases = /* @__PURE__ */ new Map();
|
|
1947
2152
|
const emulatorDb = openDatabase(join(dataDir, "emulator.db"));
|
|
1948
2153
|
emulatorDb.pragma("journal_mode = WAL");
|
|
1949
2154
|
emulatorDb.exec(EMULATOR_SCHEMA);
|
|
1950
|
-
function
|
|
1951
|
-
let db =
|
|
2155
|
+
function getDatabase(bindingName) {
|
|
2156
|
+
let db = databases.get(bindingName);
|
|
1952
2157
|
if (!db) {
|
|
1953
2158
|
db = openDatabase(join(dataDir, "db", `${bindingName}.db`));
|
|
1954
2159
|
db.pragma("journal_mode = WAL");
|
|
1955
|
-
|
|
2160
|
+
databases.set(bindingName, db);
|
|
1956
2161
|
}
|
|
1957
2162
|
return db;
|
|
1958
2163
|
}
|
|
1959
2164
|
function close() {
|
|
1960
2165
|
emulatorDb.close();
|
|
1961
|
-
for (const db of
|
|
2166
|
+
for (const db of databases.values()) {
|
|
1962
2167
|
db.close();
|
|
1963
2168
|
}
|
|
1964
|
-
|
|
2169
|
+
databases.clear();
|
|
1965
2170
|
}
|
|
1966
2171
|
return {
|
|
1967
2172
|
emulatorDb,
|
|
1968
|
-
|
|
2173
|
+
getDatabase,
|
|
1969
2174
|
close
|
|
1970
2175
|
};
|
|
1971
2176
|
}
|
|
1972
2177
|
|
|
1973
2178
|
// ../emulator/dist/nextjs-dev.js
|
|
1974
2179
|
var PLOY_CONTEXT_SYMBOL = /* @__PURE__ */ Symbol.for("__ploy-context__");
|
|
1975
|
-
function
|
|
2180
|
+
function createDevDB(databaseId, apiUrl) {
|
|
1976
2181
|
return {
|
|
1977
2182
|
async dump() {
|
|
1978
2183
|
const response = await fetch(`${apiUrl}/db`, {
|
|
@@ -2139,7 +2344,7 @@ async function initPloyForDev(config) {
|
|
|
2139
2344
|
const env2 = {};
|
|
2140
2345
|
if (hasDbBindings2 && ployConfig2.db) {
|
|
2141
2346
|
for (const [bindingName, databaseId] of Object.entries(ployConfig2.db)) {
|
|
2142
|
-
env2[bindingName] =
|
|
2347
|
+
env2[bindingName] = createDevDB(databaseId, cliMockServerUrl);
|
|
2143
2348
|
}
|
|
2144
2349
|
}
|
|
2145
2350
|
if (hasAuthConfig2) {
|
|
@@ -2191,7 +2396,7 @@ async function initPloyForDev(config) {
|
|
|
2191
2396
|
const env = {};
|
|
2192
2397
|
if (hasDbBindings && ployConfig.db) {
|
|
2193
2398
|
for (const [bindingName, databaseId] of Object.entries(ployConfig.db)) {
|
|
2194
|
-
env[bindingName] =
|
|
2399
|
+
env[bindingName] = createDevDB(databaseId, apiUrl);
|
|
2195
2400
|
}
|
|
2196
2401
|
}
|
|
2197
2402
|
if (hasAuthConfig) {
|