Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions plugins/Vercel/v1/configValidation.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"steps": [
{
"displayName": "Authenticate",
"dataStream": { "name": "currentUser" },
"required": true,
"error": "Could not authenticate with Vercel. Check that your API Token is valid and has not expired.",
"success": "Connected to Vercel successfully."
}
]
}
16 changes: 16 additions & 0 deletions plugins/Vercel/v1/custom_types.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[
{
"name": "Project",
"sourceType": "Project",
"icon": "rocket",
"singular": "Project",
"plural": "Projects"
},
{
"name": "Domain",
"sourceType": "Domain",
"icon": "globe",
"singular": "Domain",
"plural": "Domains"
}
]
Comment thread
claude[bot] marked this conversation as resolved.
36 changes: 36 additions & 0 deletions plugins/Vercel/v1/dataStreams/activity.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "activity",
"displayName": "Activity",
"description": "Vercel account or team activity feed, one row per audit-style event. Backs activity and audit log tiles",
"tags": ["Activity"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v3/events",
"pathToData": "events",
"expandInnerObjects": true,
"getArgs": [
{ "key": "since", "value": "{{timeframe.start}}" },
{ "key": "until", "value": "{{timeframe.end}}" },
{ "key": "limit", "value": "100" }
],
"paging": { "mode": "none" }
},
"matches": "none",
"metadata": [
{ "name": "id", "displayName": "ID", "role": "id", "visible": false },
{ "name": "type", "displayName": "Type" },
{ "name": "text", "displayName": "Summary", "role": "label" },
{
"name": "actor",
"displayName": "Actor",
"computed": true,
"valueExpression": "{{ $['user.username'] || $['user.email'] || $['userId'] }}"
},
{ "name": "user.username", "displayName": "User", "visible": false },
{ "name": "user.email", "displayName": "Email", "visible": false },
{ "name": "userId", "displayName": "User ID", "visible": false },
{ "name": "createdAt", "displayName": "Created", "shape": "date", "role": "timestamp" }
],
"timeframes": true
}
71 changes: 71 additions & 0 deletions plugins/Vercel/v1/dataStreams/cost.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"name": "cost",
"displayName": "Cost",
"description": "Vercel usage cost and consumption from the FOCUS billing endpoint, one row per daily charge",
"tags": ["Cost", "Billing"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v1/billing/charges",
"postRequestScript": "cost.js",
"getArgs": [
{ "key": "from", "value": "{{timeframe.start}}" },
{ "key": "to", "value": "{{timeframe.end}}" }
],
"headers": [
{
"key": "Accept-Encoding",
"value": "gzip"
}
]
},
"matches": "none",
"metadata": [
{
"name": "service",
"displayName": "Service",
"shape": "string",
"role": "label"
},
{
"name": "billedCost",
"displayName": "Billed Cost ($)",
"shape": [
"currency",
{
"code": "usd",
"decimalPlaces": 2,
"thousandsSeparator": true
}
],
"role": "value"
},
{
"name": "effectiveCost",
"displayName": "Effective Cost ($)",
"shape": [
"currency",
{
"code": "usd",
"decimalPlaces": 2,
"thousandsSeparator": true
}
]
},
{ "name": "quantity", "displayName": "Quantity", "shape": "number" },
{ "name": "unit", "displayName": "Unit", "shape": "string" },
{
"name": "projectName",
"displayName": "Project",
"shape": "string",
"role": "label"
},
{
"name": "periodStart",
"displayName": "Period Start",
"shape": "date",
"role": "timestamp"
}
],
"timeframes": ["last24hours", "last7days", "last30days", "thisMonth"]

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 The cost data stream declares "timeframes": ["last24hours", "last7days", "last30days", "thisMonth"] at cost.json:70, but docs/README.md:53 and the PR description's Known limitations explicitly state the cost stream is restricted to Last 24 hours and Last 7 days, noting that even within that window users "may get time outs or lambda response size errors". A user picking last30days or thisMonth from the timeframe picker will silently walk into the documented timeout/lambda response-size failures with no warning. Fix by narrowing the array to ["last24hours", "last7days"] to match the README (preferred — the docs explain the underlying constraint), or update the README/known-limitations to reflect the wider allowed set.

Extended reasoning...

What the bug is

plugins/Vercel/v1/dataStreams/cost.json:70 declares:

"timeframes": ["last24hours", "last7days", "last30days", "thisMonth"]

— four selectable timeframes. But plugins/Vercel/v1/docs/README.md:53 explicitly tells users:

Cost data is daily granularity and the billing endpoint returns very large datasets, so the cost stream is restricted to the Last 24 hours and Last 7 days timeframes.

And this PR's own Known limitations section in the description goes further:

Cost data is daily granularity and the billing endpoint returns very large datasets, so the cost stream is restricted to the Last 24 hours and Last 7 days timeframes, even then we may get time outs or lambda response size errors.

The config and the documentation contradict each other. The four-entry list is what the UI will actually show in the timeframe picker; the README is what the user will read before deciding what to pick.

How it manifests

  1. User opens a tile bound to the Cost stream (e.g. one of the Cost dashboard tiles, or a new tile they author).
  2. The SquaredUp UI renders the timeframe picker from the stream's timeframes array — last24hours, last7days, last30days, and thisMonth are all selectable.
  3. User selects last30days (or thisMonth) because it's available — there is no in-product hint that it's off-limits.
  4. The cost stream calls GET /v1/billing/charges?from={timeframe.start}&to={timeframe.end} — a 30-day FOCUS billing window for a non-trivial account.
  5. Per the author's own documented experience, the request returns either a Lambda timeout or a Lambda response-size error (or both). The tile fails to render; the user has no idea this is an expected limitation.

Why existing code does not prevent it

There is no runtime guard in dataStreams/scripts/cost.js to detect or short-circuit oversized requests — the script only parses the JSONL response after it returns. The timeframes array in the data stream JSON is the only user-facing gate, and it currently allows the two timeframes the author has explicitly flagged as broken. The OOB Cost dashboard tiles all pin timeframe: "last7days", so OOB rendering is safe — but the moment any user customizes the timeframe or browses the data stream, the footgun is exposed.

Step-by-step proof of the inconsistency

  1. Open plugins/Vercel/v1/dataStreams/cost.json line 70 → array has 4 entries: last24hours, last7days, last30days, thisMonth.
  2. Open plugins/Vercel/v1/docs/README.md line 53 → "the cost stream is restricted to the Last 24 hours and Last 7 days timeframes." Two values, explicitly.
  3. Read the PR description's Known limitations bullet for cost → same two values, with the additional admission that even those can time out.
  4. Compare: 4 ≠ 2. The config offers two timeframes the docs say are not supported.
  5. Concrete trigger: in the SquaredUp UI, edit any cost-bound tile, open the timeframe dropdown, pick Last 30 days or This Month. The request fires; on any account with non-trivial billing volume, the documented timeout/response-size failure occurs.

How to fix

Preferred fix (matches the author's documented intent):

-    "timeframes": ["last24hours", "last7days", "last30days", "thisMonth"]
+    "timeframes": ["last24hours", "last7days"]

Alternative: if last30days / thisMonth were intentionally exposed despite the documented risk, update docs/README.md:53 and the PR description's Known limitations bullet to reflect that those timeframes are available but may time out, so users are not surprised. The first option is cleaner — it removes a known footgun rather than documenting it.

}
21 changes: 21 additions & 0 deletions plugins/Vercel/v1/dataStreams/currentUser.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"name": "currentUser",
"displayName": "Current User",
"description": "Returns the authenticated Vercel user. Used to validate the connection.",
"tags": [],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v2/user",
"postRequestScript": "currentUser.js"
},
"matches": "none",
"metadata": [
{ "name": "id", "displayName": "ID", "visible": false },
{ "name": "username", "displayName": "Username", "role": "label" },
{ "name": "name", "displayName": "Name" },
{ "name": "email", "displayName": "Email" }
],
"timeframes": false,
"visibility": { "type": "hidden" }
}
95 changes: 95 additions & 0 deletions plugins/Vercel/v1/dataStreams/deployments.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
{
"name": "deployments",
"displayName": "Deployments",
"description": "Vercel deployments across the account or a selected project, one row per deployment. Backs deployment health and history tiles",
"tags": ["Deployments"],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v7/deployments",
"postRequestScript": "deployments.js",
"getArgs": [
{ "key": "since", "value": "{{timeframe.unixStart * 1000}}" }
],
"paging": {
"mode": "token",
"pageSize": {
"realm": "queryArg",
"path": "limit",
"value": "100"
},
"in": { "realm": "payload", "path": "pagination.next" },
"out": { "realm": "queryArg", "path": "until" }
}
},
"matches": "none",
"ui": [
{
"type": "objects",
"name": "project",
"label": "Project (optional)",
"matches": {
"sourceType": { "type": "equals", "value": "Project" }
}
}
],
"metadata": [
{
"name": "uid",
"displayName": "ID",
"role": "value",
"visible": false
},
{ "name": "name", "displayName": "Name", "role": "label" },
{
"name": "state",
"displayName": "State",
"shape": [
"state",
{
"map": {
"success": ["READY"],
"error": ["ERROR", "CANCELED"],
"warning": ["BUILDING", "QUEUED", "INITIALIZING"],
"unknown": ["DELETED"]
}
}
]
},
{ "name": "target", "displayName": "Target" },
{ "name": "projectId", "displayName": "Project ID", "visible": false },
{
"name": "created",
"displayName": "Created",
"shape": "date",
"role": "timestamp"
},
{ "name": "url", "displayName": "Url", "shape": "url" },
{
"name": "inspectorUrl",
"displayName": "Inspector Url",
"shape": "url"
},
{ "name": "creator", "displayName": "Creator" },
{
"name": "ready",
"displayName": "Ready",
"shape": "date",
"role": "timestamp"
},
{
"name": "buildingAt",
"displayName": "Building At",
"shape": "date",
"role": "timestamp"
},
{
"name": "createdAt",
"displayName": "Created At",
"shape": "date",
"role": "timestamp"
},
{ "pattern": ".*" }
],
"timeframes": true
}
30 changes: 30 additions & 0 deletions plugins/Vercel/v1/dataStreams/domainConfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "domainConfig",
"displayName": "Domain Config",
"description": "Configuration health for a single Vercel domain — whether DNS/nameservers are misconfigured, the service type, and who configured it",
"tags": ["Domain"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "v6/domains/{{object.name}}/config"
},
"matches": { "sourceType": { "type": "equals", "value": "Domain" } },
"metadata": [
{
"name": "misconfigured",
"displayName": "Misconfigured",
"shape": "boolean"
},
{
"name": "serviceType",
"displayName": "Service Type",
"shape": "string"
},
{
"name": "configuredBy",
"displayName": "Configured By",
"shape": "string"
}
],
"timeframes": false
}
31 changes: 31 additions & 0 deletions plugins/Vercel/v1/dataStreams/domains.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
{
"name": "domains",
"displayName": "Domains",
"description": "Lists Vercel custom domains in the configured account or team. Backs the Vercel Domain import and domain inventory tiles.",
"tags": [],
"baseDataSourceName": "httpRequestUnscoped",
"config": {
"httpMethod": "get",
"endpointPath": "v5/domains",
"pathToData": "domains",
"paging": {
"mode": "token",
"pageSize": { "realm": "queryArg", "path": "limit", "value": "100" },
"in": { "realm": "payload", "path": "pagination.next" },
"out": { "realm": "queryArg", "path": "until" }
}
},
"matches": "none",
"metadata": [
{ "name": "id", "displayName": "ID", "visible": false },
{ "name": "sourceType", "computed": true, "valueExpression": "Vercel Domain", "visible": false },
{ "name": "name", "displayName": "Domain", "role": "label" },
{ "name": "verified", "displayName": "Verified" },
{ "name": "serviceType", "displayName": "Service Type" },
{ "name": "expiresAt", "displayName": "Expires", "shape": "date" },
{ "name": "boughtAt", "displayName": "Bought", "shape": "date" },
{ "name": "renew", "displayName": "Auto-renew" },
{ "name": "createdAt", "displayName": "Created", "shape": "date" }
],
"timeframes": false
}
69 changes: 69 additions & 0 deletions plugins/Vercel/v1/dataStreams/firewallEvents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
{
"name": "firewallEvents",
"displayName": "Firewall Events",
"description": "Per-action firewall event counts over the timeframe for a single Vercel project, one row per time-bucket and action type",
"tags": ["Security", "Firewall"],
"baseDataSourceName": "httpRequestScopedSingle",
"config": {
"httpMethod": "get",
"endpointPath": "v1/security/firewall/events",
"getArgs": [
{ "key": "projectId", "value": "{{object.rawId}}" },
{
"key": "startTimestamp",
"value": "{{timeframe.unixStart * 1000}}"
},
{ "key": "endTimestamp", "value": "{{timeframe.unixEnd * 1000}}" }
],
"pathToData": "actions"
},
"matches": {
"sourceType": { "type": "equals", "value": "Project" }
},
"metadata": [
{
"name": "startTime",
"displayName": "Time",
"shape": "date",
"role": "timestamp"
},
{
"name": "action",
"displayName": "Action",
"shape": "string",
"role": "label"
},
{
"name": "count",
"displayName": "Count",
"shape": "number",
"role": "value"
},
{
"name": "host",
"displayName": "Host",
"shape": "string"
},
{
"name": "public_ip",
"displayName": "Public IP",
"shape": "string"
},
{
"name": "action_type",
"displayName": "Action Category",
"shape": "string",
"visible": false
},
{
"name": "isActive",
"displayName": "Active",
"shape": "boolean",
"visible": false
},
{
"pattern": ".*"
}
],
"timeframes": true
}
Loading
Loading