@hasna/uptime 0.1.8 → 0.1.10
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/CHANGELOG.md +25 -3
- package/README.md +4 -4
- package/SECURITY.md +4 -2
- package/dist/api.js +104 -47
- package/dist/checks.d.ts +2 -1
- package/dist/checks.d.ts.map +1 -1
- package/dist/checks.js +2 -1
- package/dist/cli/index.js +125 -68
- package/dist/cloud-plan.d.ts +6 -6
- package/dist/cloud-plan.d.ts.map +1 -1
- package/dist/cloud-plan.js +17 -17
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -64
- package/dist/mcp/index.js +92 -37
- package/dist/service.d.ts +33 -9
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +92 -37
- package/dist/store.d.ts +13 -3
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +78 -25
- package/dist/types.d.ts +3 -0
- package/dist/types.d.ts.map +1 -1
- package/docs/aws-deployment-runbook.md +229 -14
- package/infra/aws/README.md +12 -1
- package/infra/aws/main.tf +44 -5
- package/infra/aws/terraform.tfvars.example +9 -1
- package/infra/aws/variables.tf +48 -1
- package/package.json +1 -1
package/dist/service.js
CHANGED
|
@@ -844,7 +844,7 @@ var REQUIRED_TABLES = [
|
|
|
844
844
|
];
|
|
845
845
|
var PROBE_TABLES = new Set(["probe_identities", "probe_check_jobs", "probe_submissions"]);
|
|
846
846
|
var REPORT_AUDIT_TABLES = new Set(["report_schedules", "report_runs", "audit_events"]);
|
|
847
|
-
var CURRENT_SCHEMA_VERSION = "
|
|
847
|
+
var CURRENT_SCHEMA_VERSION = "4";
|
|
848
848
|
|
|
849
849
|
class StaleCheckResultError extends Error {
|
|
850
850
|
constructor(message) {
|
|
@@ -905,7 +905,8 @@ class UptimeStore {
|
|
|
905
905
|
this.db.run(`
|
|
906
906
|
CREATE TABLE IF NOT EXISTS monitors (
|
|
907
907
|
id TEXT PRIMARY KEY,
|
|
908
|
-
|
|
908
|
+
workspace_id TEXT NOT NULL DEFAULT 'local',
|
|
909
|
+
name TEXT NOT NULL,
|
|
909
910
|
kind TEXT NOT NULL CHECK (kind IN ('http', 'tcp', 'browser_page')),
|
|
910
911
|
url TEXT,
|
|
911
912
|
host TEXT,
|
|
@@ -920,9 +921,11 @@ class UptimeStore {
|
|
|
920
921
|
last_checked_at TEXT,
|
|
921
922
|
revision INTEGER NOT NULL DEFAULT 1,
|
|
922
923
|
created_at TEXT NOT NULL,
|
|
923
|
-
updated_at TEXT NOT NULL
|
|
924
|
+
updated_at TEXT NOT NULL,
|
|
925
|
+
UNIQUE (workspace_id, name)
|
|
924
926
|
)
|
|
925
927
|
`);
|
|
928
|
+
this.ensureColumn("monitors", "workspace_id", "TEXT NOT NULL DEFAULT 'local'");
|
|
926
929
|
this.ensureColumn("monitors", "revision", "INTEGER NOT NULL DEFAULT 1");
|
|
927
930
|
this.ensureMonitorKindAllowsBrowserPage();
|
|
928
931
|
this.db.run(`
|
|
@@ -1072,6 +1075,7 @@ class UptimeStore {
|
|
|
1072
1075
|
`);
|
|
1073
1076
|
this.db.query("INSERT OR REPLACE INTO schema_migrations (key, value, updated_at) VALUES ('schema_version', ?, ?)").run(CURRENT_SCHEMA_VERSION, new Date().toISOString());
|
|
1074
1077
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_results_monitor_time ON check_results(monitor_id, checked_at DESC)");
|
|
1078
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_monitors_workspace_enabled_name ON monitors(workspace_id, enabled, name)");
|
|
1075
1079
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_incidents_monitor_status ON incidents(monitor_id, status)");
|
|
1076
1080
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_check_leases_until ON check_leases(leased_until)");
|
|
1077
1081
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_monitor_provenance_monitor ON monitor_provenance(monitor_id)");
|
|
@@ -1133,8 +1137,10 @@ class UptimeStore {
|
|
|
1133
1137
|
if (this.mode === "hosted")
|
|
1134
1138
|
assertHostedTargetAllowed(normalized);
|
|
1135
1139
|
const now = new Date().toISOString();
|
|
1140
|
+
const workspaceId = normalizeWorkspaceId(options.workspaceId ?? input.workspaceId ?? "local");
|
|
1136
1141
|
const monitor = {
|
|
1137
1142
|
id: newId("mon"),
|
|
1143
|
+
workspaceId,
|
|
1138
1144
|
name: normalized.name,
|
|
1139
1145
|
kind: normalized.kind,
|
|
1140
1146
|
url: normalized.url ?? null,
|
|
@@ -1153,22 +1159,33 @@ class UptimeStore {
|
|
|
1153
1159
|
updatedAt: now
|
|
1154
1160
|
};
|
|
1155
1161
|
this.db.query(`INSERT INTO monitors (
|
|
1156
|
-
id, name, kind, url, host, port, method, expected_status,
|
|
1162
|
+
id, workspace_id, name, kind, url, host, port, method, expected_status,
|
|
1157
1163
|
interval_seconds, timeout_ms, retry_count, enabled, status,
|
|
1158
1164
|
last_checked_at, revision, created_at, updated_at
|
|
1159
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(monitor.id, monitor.name, monitor.kind, monitor.url, monitor.host, monitor.port, monitor.method, monitor.expectedStatus, monitor.intervalSeconds, monitor.timeoutMs, monitor.retryCount, monitor.enabled ? 1 : 0, monitor.status, monitor.lastCheckedAt, monitor.revision, monitor.createdAt, monitor.updatedAt);
|
|
1165
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(monitor.id, monitor.workspaceId, monitor.name, monitor.kind, monitor.url, monitor.host, monitor.port, monitor.method, monitor.expectedStatus, monitor.intervalSeconds, monitor.timeoutMs, monitor.retryCount, monitor.enabled ? 1 : 0, monitor.status, monitor.lastCheckedAt, monitor.revision, monitor.createdAt, monitor.updatedAt);
|
|
1160
1166
|
return monitor;
|
|
1161
1167
|
}
|
|
1162
1168
|
listMonitors(options = {}) {
|
|
1163
|
-
const
|
|
1169
|
+
const workspaceId = options.workspaceId ? normalizeWorkspaceId(options.workspaceId) : undefined;
|
|
1170
|
+
const clauses = [];
|
|
1171
|
+
const args = [];
|
|
1172
|
+
if (workspaceId) {
|
|
1173
|
+
clauses.push("workspace_id = ?");
|
|
1174
|
+
args.push(workspaceId);
|
|
1175
|
+
}
|
|
1176
|
+
if (!options.includeDisabled)
|
|
1177
|
+
clauses.push("enabled = 1");
|
|
1178
|
+
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
1179
|
+
const rows = this.db.query(`SELECT * FROM monitors ${where} ORDER BY name ASC`).all(...args);
|
|
1164
1180
|
return rows.map(monitorFromRow);
|
|
1165
1181
|
}
|
|
1166
|
-
getMonitor(idOrName) {
|
|
1167
|
-
const
|
|
1182
|
+
getMonitor(idOrName, options = {}) {
|
|
1183
|
+
const workspaceId = options.workspaceId ? normalizeWorkspaceId(options.workspaceId) : undefined;
|
|
1184
|
+
const row = this.db.query(`SELECT * FROM monitors WHERE (id = ? OR name = ?)${workspaceId ? " AND workspace_id = ?" : ""}`).get(...workspaceId ? [idOrName, idOrName, workspaceId] : [idOrName, idOrName]);
|
|
1168
1185
|
return row ? monitorFromRow(row) : null;
|
|
1169
1186
|
}
|
|
1170
1187
|
updateMonitor(idOrName, input, options = {}) {
|
|
1171
|
-
const current = this.getMonitor(idOrName);
|
|
1188
|
+
const current = this.getMonitor(idOrName, { workspaceId: options.workspaceId });
|
|
1172
1189
|
if (!current)
|
|
1173
1190
|
throw new Error(`Monitor not found: ${idOrName}`);
|
|
1174
1191
|
if (this.mode === "hosted") {
|
|
@@ -1192,10 +1209,10 @@ class UptimeStore {
|
|
|
1192
1209
|
if (definitionChanged(current, next)) {
|
|
1193
1210
|
this.closeOpenIncident(current.id, updatedAt);
|
|
1194
1211
|
}
|
|
1195
|
-
return this.getMonitor(current.id);
|
|
1212
|
+
return this.getMonitor(current.id, { workspaceId: options.workspaceId });
|
|
1196
1213
|
}
|
|
1197
|
-
deleteMonitor(idOrName) {
|
|
1198
|
-
const current = this.getMonitor(idOrName);
|
|
1214
|
+
deleteMonitor(idOrName, options = {}) {
|
|
1215
|
+
const current = this.getMonitor(idOrName, { workspaceId: options.workspaceId });
|
|
1199
1216
|
if (!current)
|
|
1200
1217
|
return false;
|
|
1201
1218
|
this.db.query("DELETE FROM monitors WHERE id = ?").run(current.id);
|
|
@@ -1572,7 +1589,20 @@ class UptimeStore {
|
|
|
1572
1589
|
}
|
|
1573
1590
|
listResults(options = {}) {
|
|
1574
1591
|
const limit = clampLimit(options.limit ?? 50);
|
|
1575
|
-
const
|
|
1592
|
+
const workspaceId = options.workspaceId ? normalizeWorkspaceId(options.workspaceId) : undefined;
|
|
1593
|
+
const clauses = [];
|
|
1594
|
+
const args = [];
|
|
1595
|
+
if (options.monitorId) {
|
|
1596
|
+
clauses.push("check_results.monitor_id = ?");
|
|
1597
|
+
args.push(options.monitorId);
|
|
1598
|
+
}
|
|
1599
|
+
if (workspaceId) {
|
|
1600
|
+
clauses.push("monitors.workspace_id = ?");
|
|
1601
|
+
args.push(workspaceId);
|
|
1602
|
+
}
|
|
1603
|
+
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
1604
|
+
args.push(limit);
|
|
1605
|
+
const rows = this.db.query(`SELECT check_results.* FROM check_results JOIN monitors ON monitors.id = check_results.monitor_id ${where} ORDER BY checked_at DESC LIMIT ?`).all(...args);
|
|
1576
1606
|
return rows.map(checkResultFromRow);
|
|
1577
1607
|
}
|
|
1578
1608
|
getProvenance(source, sourceId) {
|
|
@@ -1615,24 +1645,28 @@ class UptimeStore {
|
|
|
1615
1645
|
const clauses = [];
|
|
1616
1646
|
const args = [];
|
|
1617
1647
|
if (options.status) {
|
|
1618
|
-
clauses.push("status = ?");
|
|
1648
|
+
clauses.push("incidents.status = ?");
|
|
1619
1649
|
args.push(options.status);
|
|
1620
1650
|
}
|
|
1621
1651
|
if (options.monitorId) {
|
|
1622
|
-
clauses.push("monitor_id = ?");
|
|
1652
|
+
clauses.push("incidents.monitor_id = ?");
|
|
1623
1653
|
args.push(options.monitorId);
|
|
1624
1654
|
}
|
|
1655
|
+
if (options.workspaceId) {
|
|
1656
|
+
clauses.push("monitors.workspace_id = ?");
|
|
1657
|
+
args.push(normalizeWorkspaceId(options.workspaceId));
|
|
1658
|
+
}
|
|
1625
1659
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
1626
1660
|
args.push(clampLimit(options.limit ?? 50));
|
|
1627
|
-
const rows = this.db.query(`SELECT
|
|
1661
|
+
const rows = this.db.query(`SELECT incidents.* FROM incidents JOIN monitors ON monitors.id = incidents.monitor_id ${where} ORDER BY opened_at DESC LIMIT ?`).all(...args);
|
|
1628
1662
|
return rows.map(incidentFromRow);
|
|
1629
1663
|
}
|
|
1630
1664
|
getOpenIncident(monitorId) {
|
|
1631
1665
|
const row = this.db.query("SELECT * FROM incidents WHERE monitor_id = ? AND status = 'open' ORDER BY opened_at DESC LIMIT 1").get(monitorId);
|
|
1632
1666
|
return row ? incidentFromRow(row) : null;
|
|
1633
1667
|
}
|
|
1634
|
-
summary() {
|
|
1635
|
-
const monitors = this.listMonitors({ includeDisabled: true });
|
|
1668
|
+
summary(options = {}) {
|
|
1669
|
+
const monitors = this.listMonitors({ includeDisabled: true, workspaceId: options.workspaceId });
|
|
1636
1670
|
const summaries = monitors.map((monitor) => this.monitorSummary(monitor));
|
|
1637
1671
|
return {
|
|
1638
1672
|
generatedAt: new Date().toISOString(),
|
|
@@ -1644,11 +1678,15 @@ class UptimeStore {
|
|
|
1644
1678
|
down: monitors.filter((m) => m.status === "down").length,
|
|
1645
1679
|
paused: monitors.filter((m) => !m.enabled || m.status === "paused").length,
|
|
1646
1680
|
unknown: monitors.filter((m) => m.status === "unknown").length,
|
|
1647
|
-
openIncidents: this.countOpenIncidents()
|
|
1681
|
+
openIncidents: this.countOpenIncidents(options.workspaceId)
|
|
1648
1682
|
}
|
|
1649
1683
|
};
|
|
1650
1684
|
}
|
|
1651
|
-
countOpenIncidents() {
|
|
1685
|
+
countOpenIncidents(workspaceId) {
|
|
1686
|
+
if (workspaceId) {
|
|
1687
|
+
const row2 = this.db.query("SELECT COUNT(*) AS count FROM incidents JOIN monitors ON monitors.id = incidents.monitor_id WHERE incidents.status = 'open' AND monitors.workspace_id = ?").get(normalizeWorkspaceId(workspaceId));
|
|
1688
|
+
return Number(row2?.count ?? 0);
|
|
1689
|
+
}
|
|
1652
1690
|
const row = this.db.query("SELECT COUNT(*) AS count FROM incidents WHERE status = 'open'").get();
|
|
1653
1691
|
return Number(row?.count ?? 0);
|
|
1654
1692
|
}
|
|
@@ -1712,7 +1750,9 @@ class UptimeStore {
|
|
|
1712
1750
|
}
|
|
1713
1751
|
ensureMonitorKindAllowsBrowserPage() {
|
|
1714
1752
|
const row = this.db.query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'monitors'").get();
|
|
1715
|
-
|
|
1753
|
+
const needsBrowserPage = !row?.sql?.includes("browser_page");
|
|
1754
|
+
const needsWorkspaceUnique = Boolean(row?.sql?.includes("name TEXT NOT NULL UNIQUE"));
|
|
1755
|
+
if (!row?.sql || !needsBrowserPage && !needsWorkspaceUnique)
|
|
1716
1756
|
return;
|
|
1717
1757
|
this.db.run("PRAGMA foreign_keys = OFF");
|
|
1718
1758
|
this.db.run("PRAGMA legacy_alter_table = ON");
|
|
@@ -1722,7 +1762,8 @@ class UptimeStore {
|
|
|
1722
1762
|
this.db.run(`
|
|
1723
1763
|
CREATE TABLE monitors (
|
|
1724
1764
|
id TEXT PRIMARY KEY,
|
|
1725
|
-
|
|
1765
|
+
workspace_id TEXT NOT NULL DEFAULT 'local',
|
|
1766
|
+
name TEXT NOT NULL,
|
|
1726
1767
|
kind TEXT NOT NULL CHECK (kind IN ('http', 'tcp', 'browser_page')),
|
|
1727
1768
|
url TEXT,
|
|
1728
1769
|
host TEXT,
|
|
@@ -1737,17 +1778,18 @@ class UptimeStore {
|
|
|
1737
1778
|
last_checked_at TEXT,
|
|
1738
1779
|
revision INTEGER NOT NULL DEFAULT 1,
|
|
1739
1780
|
created_at TEXT NOT NULL,
|
|
1740
|
-
updated_at TEXT NOT NULL
|
|
1781
|
+
updated_at TEXT NOT NULL,
|
|
1782
|
+
UNIQUE (workspace_id, name)
|
|
1741
1783
|
)
|
|
1742
1784
|
`);
|
|
1743
1785
|
this.db.run(`
|
|
1744
1786
|
INSERT INTO monitors (
|
|
1745
|
-
id, name, kind, url, host, port, method, expected_status,
|
|
1787
|
+
id, workspace_id, name, kind, url, host, port, method, expected_status,
|
|
1746
1788
|
interval_seconds, timeout_ms, retry_count, enabled, status,
|
|
1747
1789
|
last_checked_at, revision, created_at, updated_at
|
|
1748
1790
|
)
|
|
1749
1791
|
SELECT
|
|
1750
|
-
id, name, kind, url, host, port, method, expected_status,
|
|
1792
|
+
id, workspace_id, name, kind, url, host, port, method, expected_status,
|
|
1751
1793
|
interval_seconds, timeout_ms, retry_count, enabled, status,
|
|
1752
1794
|
last_checked_at, revision, created_at, updated_at
|
|
1753
1795
|
FROM monitors_old_kind
|
|
@@ -1947,6 +1989,16 @@ function rejectControlCharacters2(value, label) {
|
|
|
1947
1989
|
throw new Error(`${label} must not contain control characters`);
|
|
1948
1990
|
}
|
|
1949
1991
|
}
|
|
1992
|
+
function normalizeWorkspaceId(value) {
|
|
1993
|
+
const normalized = value.trim();
|
|
1994
|
+
if (!normalized)
|
|
1995
|
+
throw new Error("Workspace id is required");
|
|
1996
|
+
rejectControlCharacters2(normalized, "Workspace id");
|
|
1997
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/.test(normalized)) {
|
|
1998
|
+
throw new Error("Workspace id contains unsupported characters");
|
|
1999
|
+
}
|
|
2000
|
+
return normalized;
|
|
2001
|
+
}
|
|
1950
2002
|
function normalizeScheduleSlot(value) {
|
|
1951
2003
|
const slot = value.trim();
|
|
1952
2004
|
if (!slot)
|
|
@@ -2133,6 +2185,7 @@ function assertIsoTimestamp(value, label) {
|
|
|
2133
2185
|
function monitorFromRow(row) {
|
|
2134
2186
|
return {
|
|
2135
2187
|
id: row.id,
|
|
2188
|
+
workspaceId: row.workspace_id ?? "local",
|
|
2136
2189
|
name: row.name,
|
|
2137
2190
|
kind: row.kind,
|
|
2138
2191
|
url: row.url,
|
|
@@ -2614,20 +2667,20 @@ class UptimeService {
|
|
|
2614
2667
|
close() {
|
|
2615
2668
|
this.store.close();
|
|
2616
2669
|
}
|
|
2617
|
-
createMonitor(input) {
|
|
2618
|
-
return this.store.createMonitor(input);
|
|
2670
|
+
createMonitor(input, options = {}) {
|
|
2671
|
+
return this.store.createMonitor(input, options);
|
|
2619
2672
|
}
|
|
2620
|
-
updateMonitor(idOrName, input) {
|
|
2621
|
-
return this.store.updateMonitor(idOrName, input);
|
|
2673
|
+
updateMonitor(idOrName, input, options = {}) {
|
|
2674
|
+
return this.store.updateMonitor(idOrName, input, options);
|
|
2622
2675
|
}
|
|
2623
|
-
deleteMonitor(idOrName) {
|
|
2624
|
-
return this.store.deleteMonitor(idOrName);
|
|
2676
|
+
deleteMonitor(idOrName, options = {}) {
|
|
2677
|
+
return this.store.deleteMonitor(idOrName, options);
|
|
2625
2678
|
}
|
|
2626
2679
|
listMonitors(options = {}) {
|
|
2627
2680
|
return this.store.listMonitors(options);
|
|
2628
2681
|
}
|
|
2629
|
-
getMonitor(idOrName) {
|
|
2630
|
-
return this.store.getMonitor(idOrName);
|
|
2682
|
+
getMonitor(idOrName, options = {}) {
|
|
2683
|
+
return this.store.getMonitor(idOrName, options);
|
|
2631
2684
|
}
|
|
2632
2685
|
listResults(options = {}) {
|
|
2633
2686
|
return this.store.listResults(options);
|
|
@@ -2635,8 +2688,8 @@ class UptimeService {
|
|
|
2635
2688
|
listIncidents(options = {}) {
|
|
2636
2689
|
return this.store.listIncidents(options);
|
|
2637
2690
|
}
|
|
2638
|
-
summary() {
|
|
2639
|
-
return this.store.summary();
|
|
2691
|
+
summary(options = {}) {
|
|
2692
|
+
return this.store.summary(options);
|
|
2640
2693
|
}
|
|
2641
2694
|
createProbe(input) {
|
|
2642
2695
|
const store = this.probeStore();
|
|
@@ -2692,7 +2745,8 @@ class UptimeService {
|
|
|
2692
2745
|
return this.store.verifyBackup(backupPath);
|
|
2693
2746
|
}
|
|
2694
2747
|
buildReport(options = {}) {
|
|
2695
|
-
|
|
2748
|
+
const { workspaceId, ...reportOptions } = options;
|
|
2749
|
+
return buildUptimeReport(this.summary({ workspaceId }), reportOptions);
|
|
2696
2750
|
}
|
|
2697
2751
|
async sendReport(options = {}) {
|
|
2698
2752
|
if (this.store.mode === "hosted" && (options.email || options.sms || options.logs)) {
|
|
@@ -3016,6 +3070,7 @@ class UptimeService {
|
|
|
3016
3070
|
throw new Error("Probe job fencing token is invalid");
|
|
3017
3071
|
if (!job.leaseExpiresAt || job.leaseExpiresAt <= new Date().toISOString())
|
|
3018
3072
|
throw new Error("Probe job lease expired");
|
|
3073
|
+
const evidence = input.evidence ? normalizeBrowserEvidence(monitor.url ?? monitor.host ?? "https://example.invalid", input.evidence) : null;
|
|
3019
3074
|
const result = this.store.recordCheckResult({
|
|
3020
3075
|
monitorId: monitor.id,
|
|
3021
3076
|
checkedAt: input.checkedAt,
|
|
@@ -3023,7 +3078,7 @@ class UptimeService {
|
|
|
3023
3078
|
latencyMs: input.latencyMs,
|
|
3024
3079
|
statusCode: input.statusCode ?? null,
|
|
3025
3080
|
error: input.error ?? null,
|
|
3026
|
-
evidence
|
|
3081
|
+
evidence,
|
|
3027
3082
|
attemptCount: input.attemptCount ?? 1,
|
|
3028
3083
|
expectedMonitorRevision: input.monitorRevision
|
|
3029
3084
|
});
|
package/dist/store.d.ts
CHANGED
|
@@ -69,15 +69,22 @@ export declare class UptimeStore {
|
|
|
69
69
|
static restoreBackup(backupPath: string, destinationPath?: string): UptimeBackup;
|
|
70
70
|
createMonitor(input: ImportedMonitorInput, options?: {
|
|
71
71
|
allowBrowserPage?: boolean;
|
|
72
|
+
workspaceId?: string;
|
|
72
73
|
}): Monitor;
|
|
73
74
|
listMonitors(options?: {
|
|
74
75
|
includeDisabled?: boolean;
|
|
76
|
+
workspaceId?: string;
|
|
75
77
|
}): Monitor[];
|
|
76
|
-
getMonitor(idOrName: string
|
|
78
|
+
getMonitor(idOrName: string, options?: {
|
|
79
|
+
workspaceId?: string;
|
|
80
|
+
}): Monitor | null;
|
|
77
81
|
updateMonitor(idOrName: string, input: ImportedUpdateMonitorInput, options?: {
|
|
78
82
|
allowBrowserPage?: boolean;
|
|
83
|
+
workspaceId?: string;
|
|
79
84
|
}): Monitor;
|
|
80
|
-
deleteMonitor(idOrName: string
|
|
85
|
+
deleteMonitor(idOrName: string, options?: {
|
|
86
|
+
workspaceId?: string;
|
|
87
|
+
}): boolean;
|
|
81
88
|
createProbeIdentity(input: {
|
|
82
89
|
name: string;
|
|
83
90
|
publicKeyPem: string;
|
|
@@ -153,10 +160,13 @@ export declare class UptimeStore {
|
|
|
153
160
|
listIncidents(options?: {
|
|
154
161
|
status?: "open" | "closed";
|
|
155
162
|
monitorId?: string;
|
|
163
|
+
workspaceId?: string;
|
|
156
164
|
limit?: number;
|
|
157
165
|
}): Incident[];
|
|
158
166
|
getOpenIncident(monitorId: string): Incident | null;
|
|
159
|
-
summary(
|
|
167
|
+
summary(options?: {
|
|
168
|
+
workspaceId?: string;
|
|
169
|
+
}): UptimeSummary;
|
|
160
170
|
private countOpenIncidents;
|
|
161
171
|
private monitorSummary;
|
|
162
172
|
private reconcileIncidentInTransaction;
|
package/dist/store.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EACV,UAAU,EAEV,WAAW,EACX,yBAAyB,EACzB,QAAQ,EACR,oBAAoB,EACpB,0BAA0B,EAC1B,sBAAsB,EACtB,qBAAqB,EACrB,kBAAkB,EAClB,OAAO,EAGP,aAAa,EAEb,aAAa,EACb,sBAAsB,EACtB,qBAAqB,EACrB,oBAAoB,EACpB,SAAS,EACT,eAAe,EACf,cAAc,EAEd,yBAAyB,EACzB,aAAa,EACd,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,kBAAkB;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,iBAAiB,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,iBAAiB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEnD,eAAO,MAAM,6BAA6B,2BAA2B,CAAC;AAKtE,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,OAAO,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,4BAA4B;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,SAAS,GAAG,aAAa,CAAC;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,EAAE,CAAC;CACpB;
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
|
package/dist/store.js
CHANGED
|
@@ -127,7 +127,7 @@ var REQUIRED_TABLES = [
|
|
|
127
127
|
];
|
|
128
128
|
var PROBE_TABLES = new Set(["probe_identities", "probe_check_jobs", "probe_submissions"]);
|
|
129
129
|
var REPORT_AUDIT_TABLES = new Set(["report_schedules", "report_runs", "audit_events"]);
|
|
130
|
-
var CURRENT_SCHEMA_VERSION = "
|
|
130
|
+
var CURRENT_SCHEMA_VERSION = "4";
|
|
131
131
|
|
|
132
132
|
class StaleCheckResultError extends Error {
|
|
133
133
|
constructor(message) {
|
|
@@ -188,7 +188,8 @@ class UptimeStore {
|
|
|
188
188
|
this.db.run(`
|
|
189
189
|
CREATE TABLE IF NOT EXISTS monitors (
|
|
190
190
|
id TEXT PRIMARY KEY,
|
|
191
|
-
|
|
191
|
+
workspace_id TEXT NOT NULL DEFAULT 'local',
|
|
192
|
+
name TEXT NOT NULL,
|
|
192
193
|
kind TEXT NOT NULL CHECK (kind IN ('http', 'tcp', 'browser_page')),
|
|
193
194
|
url TEXT,
|
|
194
195
|
host TEXT,
|
|
@@ -203,9 +204,11 @@ class UptimeStore {
|
|
|
203
204
|
last_checked_at TEXT,
|
|
204
205
|
revision INTEGER NOT NULL DEFAULT 1,
|
|
205
206
|
created_at TEXT NOT NULL,
|
|
206
|
-
updated_at TEXT NOT NULL
|
|
207
|
+
updated_at TEXT NOT NULL,
|
|
208
|
+
UNIQUE (workspace_id, name)
|
|
207
209
|
)
|
|
208
210
|
`);
|
|
211
|
+
this.ensureColumn("monitors", "workspace_id", "TEXT NOT NULL DEFAULT 'local'");
|
|
209
212
|
this.ensureColumn("monitors", "revision", "INTEGER NOT NULL DEFAULT 1");
|
|
210
213
|
this.ensureMonitorKindAllowsBrowserPage();
|
|
211
214
|
this.db.run(`
|
|
@@ -355,6 +358,7 @@ class UptimeStore {
|
|
|
355
358
|
`);
|
|
356
359
|
this.db.query("INSERT OR REPLACE INTO schema_migrations (key, value, updated_at) VALUES ('schema_version', ?, ?)").run(CURRENT_SCHEMA_VERSION, new Date().toISOString());
|
|
357
360
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_results_monitor_time ON check_results(monitor_id, checked_at DESC)");
|
|
361
|
+
this.db.run("CREATE INDEX IF NOT EXISTS idx_monitors_workspace_enabled_name ON monitors(workspace_id, enabled, name)");
|
|
358
362
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_incidents_monitor_status ON incidents(monitor_id, status)");
|
|
359
363
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_check_leases_until ON check_leases(leased_until)");
|
|
360
364
|
this.db.run("CREATE INDEX IF NOT EXISTS idx_monitor_provenance_monitor ON monitor_provenance(monitor_id)");
|
|
@@ -416,8 +420,10 @@ class UptimeStore {
|
|
|
416
420
|
if (this.mode === "hosted")
|
|
417
421
|
assertHostedTargetAllowed(normalized);
|
|
418
422
|
const now = new Date().toISOString();
|
|
423
|
+
const workspaceId = normalizeWorkspaceId(options.workspaceId ?? input.workspaceId ?? "local");
|
|
419
424
|
const monitor = {
|
|
420
425
|
id: newId("mon"),
|
|
426
|
+
workspaceId,
|
|
421
427
|
name: normalized.name,
|
|
422
428
|
kind: normalized.kind,
|
|
423
429
|
url: normalized.url ?? null,
|
|
@@ -436,22 +442,33 @@ class UptimeStore {
|
|
|
436
442
|
updatedAt: now
|
|
437
443
|
};
|
|
438
444
|
this.db.query(`INSERT INTO monitors (
|
|
439
|
-
id, name, kind, url, host, port, method, expected_status,
|
|
445
|
+
id, workspace_id, name, kind, url, host, port, method, expected_status,
|
|
440
446
|
interval_seconds, timeout_ms, retry_count, enabled, status,
|
|
441
447
|
last_checked_at, revision, created_at, updated_at
|
|
442
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(monitor.id, monitor.name, monitor.kind, monitor.url, monitor.host, monitor.port, monitor.method, monitor.expectedStatus, monitor.intervalSeconds, monitor.timeoutMs, monitor.retryCount, monitor.enabled ? 1 : 0, monitor.status, monitor.lastCheckedAt, monitor.revision, monitor.createdAt, monitor.updatedAt);
|
|
448
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(monitor.id, monitor.workspaceId, monitor.name, monitor.kind, monitor.url, monitor.host, monitor.port, monitor.method, monitor.expectedStatus, monitor.intervalSeconds, monitor.timeoutMs, monitor.retryCount, monitor.enabled ? 1 : 0, monitor.status, monitor.lastCheckedAt, monitor.revision, monitor.createdAt, monitor.updatedAt);
|
|
443
449
|
return monitor;
|
|
444
450
|
}
|
|
445
451
|
listMonitors(options = {}) {
|
|
446
|
-
const
|
|
452
|
+
const workspaceId = options.workspaceId ? normalizeWorkspaceId(options.workspaceId) : undefined;
|
|
453
|
+
const clauses = [];
|
|
454
|
+
const args = [];
|
|
455
|
+
if (workspaceId) {
|
|
456
|
+
clauses.push("workspace_id = ?");
|
|
457
|
+
args.push(workspaceId);
|
|
458
|
+
}
|
|
459
|
+
if (!options.includeDisabled)
|
|
460
|
+
clauses.push("enabled = 1");
|
|
461
|
+
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
462
|
+
const rows = this.db.query(`SELECT * FROM monitors ${where} ORDER BY name ASC`).all(...args);
|
|
447
463
|
return rows.map(monitorFromRow);
|
|
448
464
|
}
|
|
449
|
-
getMonitor(idOrName) {
|
|
450
|
-
const
|
|
465
|
+
getMonitor(idOrName, options = {}) {
|
|
466
|
+
const workspaceId = options.workspaceId ? normalizeWorkspaceId(options.workspaceId) : undefined;
|
|
467
|
+
const row = this.db.query(`SELECT * FROM monitors WHERE (id = ? OR name = ?)${workspaceId ? " AND workspace_id = ?" : ""}`).get(...workspaceId ? [idOrName, idOrName, workspaceId] : [idOrName, idOrName]);
|
|
451
468
|
return row ? monitorFromRow(row) : null;
|
|
452
469
|
}
|
|
453
470
|
updateMonitor(idOrName, input, options = {}) {
|
|
454
|
-
const current = this.getMonitor(idOrName);
|
|
471
|
+
const current = this.getMonitor(idOrName, { workspaceId: options.workspaceId });
|
|
455
472
|
if (!current)
|
|
456
473
|
throw new Error(`Monitor not found: ${idOrName}`);
|
|
457
474
|
if (this.mode === "hosted") {
|
|
@@ -475,10 +492,10 @@ class UptimeStore {
|
|
|
475
492
|
if (definitionChanged(current, next)) {
|
|
476
493
|
this.closeOpenIncident(current.id, updatedAt);
|
|
477
494
|
}
|
|
478
|
-
return this.getMonitor(current.id);
|
|
495
|
+
return this.getMonitor(current.id, { workspaceId: options.workspaceId });
|
|
479
496
|
}
|
|
480
|
-
deleteMonitor(idOrName) {
|
|
481
|
-
const current = this.getMonitor(idOrName);
|
|
497
|
+
deleteMonitor(idOrName, options = {}) {
|
|
498
|
+
const current = this.getMonitor(idOrName, { workspaceId: options.workspaceId });
|
|
482
499
|
if (!current)
|
|
483
500
|
return false;
|
|
484
501
|
this.db.query("DELETE FROM monitors WHERE id = ?").run(current.id);
|
|
@@ -855,7 +872,20 @@ class UptimeStore {
|
|
|
855
872
|
}
|
|
856
873
|
listResults(options = {}) {
|
|
857
874
|
const limit = clampLimit(options.limit ?? 50);
|
|
858
|
-
const
|
|
875
|
+
const workspaceId = options.workspaceId ? normalizeWorkspaceId(options.workspaceId) : undefined;
|
|
876
|
+
const clauses = [];
|
|
877
|
+
const args = [];
|
|
878
|
+
if (options.monitorId) {
|
|
879
|
+
clauses.push("check_results.monitor_id = ?");
|
|
880
|
+
args.push(options.monitorId);
|
|
881
|
+
}
|
|
882
|
+
if (workspaceId) {
|
|
883
|
+
clauses.push("monitors.workspace_id = ?");
|
|
884
|
+
args.push(workspaceId);
|
|
885
|
+
}
|
|
886
|
+
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
887
|
+
args.push(limit);
|
|
888
|
+
const rows = this.db.query(`SELECT check_results.* FROM check_results JOIN monitors ON monitors.id = check_results.monitor_id ${where} ORDER BY checked_at DESC LIMIT ?`).all(...args);
|
|
859
889
|
return rows.map(checkResultFromRow);
|
|
860
890
|
}
|
|
861
891
|
getProvenance(source, sourceId) {
|
|
@@ -898,24 +928,28 @@ class UptimeStore {
|
|
|
898
928
|
const clauses = [];
|
|
899
929
|
const args = [];
|
|
900
930
|
if (options.status) {
|
|
901
|
-
clauses.push("status = ?");
|
|
931
|
+
clauses.push("incidents.status = ?");
|
|
902
932
|
args.push(options.status);
|
|
903
933
|
}
|
|
904
934
|
if (options.monitorId) {
|
|
905
|
-
clauses.push("monitor_id = ?");
|
|
935
|
+
clauses.push("incidents.monitor_id = ?");
|
|
906
936
|
args.push(options.monitorId);
|
|
907
937
|
}
|
|
938
|
+
if (options.workspaceId) {
|
|
939
|
+
clauses.push("monitors.workspace_id = ?");
|
|
940
|
+
args.push(normalizeWorkspaceId(options.workspaceId));
|
|
941
|
+
}
|
|
908
942
|
const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
|
|
909
943
|
args.push(clampLimit(options.limit ?? 50));
|
|
910
|
-
const rows = this.db.query(`SELECT
|
|
944
|
+
const rows = this.db.query(`SELECT incidents.* FROM incidents JOIN monitors ON monitors.id = incidents.monitor_id ${where} ORDER BY opened_at DESC LIMIT ?`).all(...args);
|
|
911
945
|
return rows.map(incidentFromRow);
|
|
912
946
|
}
|
|
913
947
|
getOpenIncident(monitorId) {
|
|
914
948
|
const row = this.db.query("SELECT * FROM incidents WHERE monitor_id = ? AND status = 'open' ORDER BY opened_at DESC LIMIT 1").get(monitorId);
|
|
915
949
|
return row ? incidentFromRow(row) : null;
|
|
916
950
|
}
|
|
917
|
-
summary() {
|
|
918
|
-
const monitors = this.listMonitors({ includeDisabled: true });
|
|
951
|
+
summary(options = {}) {
|
|
952
|
+
const monitors = this.listMonitors({ includeDisabled: true, workspaceId: options.workspaceId });
|
|
919
953
|
const summaries = monitors.map((monitor) => this.monitorSummary(monitor));
|
|
920
954
|
return {
|
|
921
955
|
generatedAt: new Date().toISOString(),
|
|
@@ -927,11 +961,15 @@ class UptimeStore {
|
|
|
927
961
|
down: monitors.filter((m) => m.status === "down").length,
|
|
928
962
|
paused: monitors.filter((m) => !m.enabled || m.status === "paused").length,
|
|
929
963
|
unknown: monitors.filter((m) => m.status === "unknown").length,
|
|
930
|
-
openIncidents: this.countOpenIncidents()
|
|
964
|
+
openIncidents: this.countOpenIncidents(options.workspaceId)
|
|
931
965
|
}
|
|
932
966
|
};
|
|
933
967
|
}
|
|
934
|
-
countOpenIncidents() {
|
|
968
|
+
countOpenIncidents(workspaceId) {
|
|
969
|
+
if (workspaceId) {
|
|
970
|
+
const row2 = this.db.query("SELECT COUNT(*) AS count FROM incidents JOIN monitors ON monitors.id = incidents.monitor_id WHERE incidents.status = 'open' AND monitors.workspace_id = ?").get(normalizeWorkspaceId(workspaceId));
|
|
971
|
+
return Number(row2?.count ?? 0);
|
|
972
|
+
}
|
|
935
973
|
const row = this.db.query("SELECT COUNT(*) AS count FROM incidents WHERE status = 'open'").get();
|
|
936
974
|
return Number(row?.count ?? 0);
|
|
937
975
|
}
|
|
@@ -995,7 +1033,9 @@ class UptimeStore {
|
|
|
995
1033
|
}
|
|
996
1034
|
ensureMonitorKindAllowsBrowserPage() {
|
|
997
1035
|
const row = this.db.query("SELECT sql FROM sqlite_master WHERE type = 'table' AND name = 'monitors'").get();
|
|
998
|
-
|
|
1036
|
+
const needsBrowserPage = !row?.sql?.includes("browser_page");
|
|
1037
|
+
const needsWorkspaceUnique = Boolean(row?.sql?.includes("name TEXT NOT NULL UNIQUE"));
|
|
1038
|
+
if (!row?.sql || !needsBrowserPage && !needsWorkspaceUnique)
|
|
999
1039
|
return;
|
|
1000
1040
|
this.db.run("PRAGMA foreign_keys = OFF");
|
|
1001
1041
|
this.db.run("PRAGMA legacy_alter_table = ON");
|
|
@@ -1005,7 +1045,8 @@ class UptimeStore {
|
|
|
1005
1045
|
this.db.run(`
|
|
1006
1046
|
CREATE TABLE monitors (
|
|
1007
1047
|
id TEXT PRIMARY KEY,
|
|
1008
|
-
|
|
1048
|
+
workspace_id TEXT NOT NULL DEFAULT 'local',
|
|
1049
|
+
name TEXT NOT NULL,
|
|
1009
1050
|
kind TEXT NOT NULL CHECK (kind IN ('http', 'tcp', 'browser_page')),
|
|
1010
1051
|
url TEXT,
|
|
1011
1052
|
host TEXT,
|
|
@@ -1020,17 +1061,18 @@ class UptimeStore {
|
|
|
1020
1061
|
last_checked_at TEXT,
|
|
1021
1062
|
revision INTEGER NOT NULL DEFAULT 1,
|
|
1022
1063
|
created_at TEXT NOT NULL,
|
|
1023
|
-
updated_at TEXT NOT NULL
|
|
1064
|
+
updated_at TEXT NOT NULL,
|
|
1065
|
+
UNIQUE (workspace_id, name)
|
|
1024
1066
|
)
|
|
1025
1067
|
`);
|
|
1026
1068
|
this.db.run(`
|
|
1027
1069
|
INSERT INTO monitors (
|
|
1028
|
-
id, name, kind, url, host, port, method, expected_status,
|
|
1070
|
+
id, workspace_id, name, kind, url, host, port, method, expected_status,
|
|
1029
1071
|
interval_seconds, timeout_ms, retry_count, enabled, status,
|
|
1030
1072
|
last_checked_at, revision, created_at, updated_at
|
|
1031
1073
|
)
|
|
1032
1074
|
SELECT
|
|
1033
|
-
id, name, kind, url, host, port, method, expected_status,
|
|
1075
|
+
id, workspace_id, name, kind, url, host, port, method, expected_status,
|
|
1034
1076
|
interval_seconds, timeout_ms, retry_count, enabled, status,
|
|
1035
1077
|
last_checked_at, revision, created_at, updated_at
|
|
1036
1078
|
FROM monitors_old_kind
|
|
@@ -1230,6 +1272,16 @@ function rejectControlCharacters(value, label) {
|
|
|
1230
1272
|
throw new Error(`${label} must not contain control characters`);
|
|
1231
1273
|
}
|
|
1232
1274
|
}
|
|
1275
|
+
function normalizeWorkspaceId(value) {
|
|
1276
|
+
const normalized = value.trim();
|
|
1277
|
+
if (!normalized)
|
|
1278
|
+
throw new Error("Workspace id is required");
|
|
1279
|
+
rejectControlCharacters(normalized, "Workspace id");
|
|
1280
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9_.:-]{0,127}$/.test(normalized)) {
|
|
1281
|
+
throw new Error("Workspace id contains unsupported characters");
|
|
1282
|
+
}
|
|
1283
|
+
return normalized;
|
|
1284
|
+
}
|
|
1233
1285
|
function normalizeScheduleSlot(value) {
|
|
1234
1286
|
const slot = value.trim();
|
|
1235
1287
|
if (!slot)
|
|
@@ -1416,6 +1468,7 @@ function assertIsoTimestamp(value, label) {
|
|
|
1416
1468
|
function monitorFromRow(row) {
|
|
1417
1469
|
return {
|
|
1418
1470
|
id: row.id,
|
|
1471
|
+
workspaceId: row.workspace_id ?? "local",
|
|
1419
1472
|
name: row.name,
|
|
1420
1473
|
kind: row.kind,
|
|
1421
1474
|
url: row.url,
|
package/dist/types.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type CheckStatus = "up" | "down";
|
|
|
5
5
|
export type IncidentStatus = "open" | "closed";
|
|
6
6
|
export interface Monitor {
|
|
7
7
|
id: string;
|
|
8
|
+
workspaceId: string;
|
|
8
9
|
name: string;
|
|
9
10
|
kind: MonitorKind;
|
|
10
11
|
url: string | null;
|
|
@@ -23,6 +24,7 @@ export interface Monitor {
|
|
|
23
24
|
updatedAt: string;
|
|
24
25
|
}
|
|
25
26
|
export interface CreateMonitorInput {
|
|
27
|
+
workspaceId?: string;
|
|
26
28
|
name: string;
|
|
27
29
|
kind: CreateMonitorKind;
|
|
28
30
|
url?: string;
|
|
@@ -275,6 +277,7 @@ export interface UptimeSummary {
|
|
|
275
277
|
}
|
|
276
278
|
export interface ListResultsOptions {
|
|
277
279
|
monitorId?: string;
|
|
280
|
+
workspaceId?: string;
|
|
278
281
|
limit?: number;
|
|
279
282
|
}
|
|
280
283
|
export interface SchedulerHandle {
|