@secondlayer/shared 2.1.0 → 3.0.0-alpha.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 +2 -2
- package/dist/src/db/index.d.ts +39 -137
- package/dist/src/db/index.js.map +2 -2
- package/dist/src/db/jsonb.d.ts +5 -1
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/account-spend-caps.d.ts +379 -0
- package/dist/src/db/queries/account-spend-caps.js +60 -0
- package/dist/src/db/queries/account-spend-caps.js.map +10 -0
- package/dist/src/db/queries/account-usage.d.ts +403 -0
- package/dist/src/db/queries/account-usage.js +222 -0
- package/dist/src/db/queries/account-usage.js.map +11 -0
- package/dist/src/db/queries/accounts.d.ts +41 -115
- package/dist/src/db/queries/accounts.js +15 -1
- package/dist/src/db/queries/accounts.js.map +3 -3
- package/dist/src/db/queries/integrity.d.ts +27 -114
- package/dist/src/db/queries/projects.d.ts +27 -114
- package/dist/src/db/queries/provisioning-audit.d.ts +27 -114
- package/dist/src/db/queries/subgraph-gaps.d.ts +27 -114
- package/dist/src/db/queries/subgraphs.d.ts +27 -115
- package/dist/src/db/queries/subgraphs.js +2 -3
- package/dist/src/db/queries/subgraphs.js.map +4 -4
- package/dist/src/db/queries/{workflows.d.ts → tenant-compute-addons.d.ts} +50 -149
- package/dist/src/db/queries/tenant-compute-addons.js +47 -0
- package/dist/src/db/queries/tenant-compute-addons.js.map +10 -0
- package/dist/src/db/queries/tenants.d.ts +40 -117
- package/dist/src/db/queries/tenants.js +9 -6
- package/dist/src/db/queries/tenants.js.map +3 -3
- package/dist/src/db/queries/usage.d.ts +28 -139
- package/dist/src/db/queries/usage.js +5 -64
- package/dist/src/db/queries/usage.js.map +4 -5
- package/dist/src/db/schema.d.ts +34 -136
- package/dist/src/errors.d.ts +8 -7
- package/dist/src/errors.js +11 -12
- package/dist/src/errors.js.map +3 -3
- package/dist/src/index.d.ts +46 -143
- package/dist/src/index.js +11 -12
- package/dist/src/index.js.map +4 -4
- package/dist/src/node/local-client.d.ts +27 -114
- package/dist/src/pricing.d.ts +20 -1
- package/dist/src/pricing.js +58 -1
- package/dist/src/pricing.js.map +3 -3
- package/migrations/0045_drop_marketplace_columns.ts +47 -0
- package/migrations/0046_tenant_activity_signal.ts +47 -0
- package/migrations/0047_usage_daily_tenant_id.ts +73 -0
- package/migrations/0048_tenant_compute_addons.ts +49 -0
- package/migrations/0049_accounts_stripe_customer_id.ts +30 -0
- package/migrations/0050_account_spend_caps.ts +45 -0
- package/migrations/0051_workflow_ai_usage_daily.ts +40 -0
- package/migrations/0052_sentries.ts +61 -0
- package/migrations/0053_workflow_runtime.ts +88 -0
- package/migrations/0054_accounts_plan_hobby.ts +32 -0
- package/migrations/0055_ai_usage_account_scope.ts +108 -0
- package/migrations/0056_drop_workflow_sentry_residuals.ts +23 -0
- package/package.json +26 -14
- package/dist/src/db/queries/workflows.js +0 -260
- package/dist/src/db/queries/workflows.js.map +0 -12
- package/dist/src/lib/plans.d.ts +0 -9
- package/dist/src/lib/plans.js +0 -37
- package/dist/src/lib/plans.js.map +0 -10
- package/dist/src/schemas/workflows.d.ts +0 -70
- package/dist/src/schemas/workflows.js +0 -43
- package/dist/src/schemas/workflows.js.map +0 -10
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Protocol Sentry v1 — packaged monitoring product.
|
|
5
|
+
*
|
|
6
|
+
* `sentries` holds per-account enabled sentries. Worker cron ticks every
|
|
7
|
+
* 60s, loads active rows, runs per-kind detect SQL on the shared indexer
|
|
8
|
+
* DB, AI-triages matches, delivers to `delivery_webhook` (Slack-shape).
|
|
9
|
+
*
|
|
10
|
+
* `sentry_alerts` holds one row per delivered alert. UNIQUE on
|
|
11
|
+
* (sentry_id, idempotency_key) dedupes across ticks — key is
|
|
12
|
+
* sha256(txId:eventIndex) for the large-outflow kind.
|
|
13
|
+
*/
|
|
14
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
15
|
+
await sql`
|
|
16
|
+
CREATE TABLE sentries (
|
|
17
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
18
|
+
account_id uuid NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
19
|
+
kind text NOT NULL,
|
|
20
|
+
name text NOT NULL,
|
|
21
|
+
config jsonb NOT NULL,
|
|
22
|
+
active boolean NOT NULL DEFAULT true,
|
|
23
|
+
last_check_at timestamptz,
|
|
24
|
+
delivery_webhook text NOT NULL,
|
|
25
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
26
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
27
|
+
)
|
|
28
|
+
`.execute(db);
|
|
29
|
+
|
|
30
|
+
await sql`CREATE INDEX sentries_account_idx ON sentries (account_id)`.execute(
|
|
31
|
+
db,
|
|
32
|
+
);
|
|
33
|
+
await sql`
|
|
34
|
+
CREATE INDEX sentries_tick_idx
|
|
35
|
+
ON sentries (active, last_check_at NULLS FIRST)
|
|
36
|
+
WHERE active = true
|
|
37
|
+
`.execute(db);
|
|
38
|
+
|
|
39
|
+
await sql`
|
|
40
|
+
CREATE TABLE sentry_alerts (
|
|
41
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
42
|
+
sentry_id uuid NOT NULL REFERENCES sentries(id) ON DELETE CASCADE,
|
|
43
|
+
idempotency_key text NOT NULL,
|
|
44
|
+
fired_at timestamptz NOT NULL DEFAULT now(),
|
|
45
|
+
payload jsonb NOT NULL,
|
|
46
|
+
delivery_status text NOT NULL DEFAULT 'pending',
|
|
47
|
+
delivery_error text,
|
|
48
|
+
UNIQUE (sentry_id, idempotency_key)
|
|
49
|
+
)
|
|
50
|
+
`.execute(db);
|
|
51
|
+
|
|
52
|
+
await sql`
|
|
53
|
+
CREATE INDEX sentry_alerts_sentry_fired_idx
|
|
54
|
+
ON sentry_alerts (sentry_id, fired_at DESC)
|
|
55
|
+
`.execute(db);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
59
|
+
await sql`DROP TABLE IF EXISTS sentry_alerts`.execute(db);
|
|
60
|
+
await sql`DROP TABLE IF EXISTS sentries`.execute(db);
|
|
61
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Workflow runtime tables (v3).
|
|
5
|
+
*
|
|
6
|
+
* Minimal revival of the runner's execution substrate after migration
|
|
7
|
+
* 0038 dropped the v2 tables. No `workflow_definitions` — v3 is a
|
|
8
|
+
* zero-sugar TS SDK, definitions live in compiled code + a process-
|
|
9
|
+
* local registry, not the DB. No schedules / cursors / signer secrets
|
|
10
|
+
* / budgets — consumers own their own scheduling; broadcast/budget
|
|
11
|
+
* features land in later migrations when a consumer needs them.
|
|
12
|
+
*
|
|
13
|
+
* workflow_runs — one row per enqueued invocation
|
|
14
|
+
* workflow_steps — memoized `step.*` outputs, keyed by
|
|
15
|
+
* `sha256(stepId + canonicalJSON(inputs))`
|
|
16
|
+
* workflow_queue — SKIP LOCKED dispatch table; one or more rows
|
|
17
|
+
* per run (one per scheduling — sleeps re-enqueue)
|
|
18
|
+
*/
|
|
19
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
20
|
+
await sql`
|
|
21
|
+
CREATE TABLE workflow_runs (
|
|
22
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
23
|
+
workflow_name text NOT NULL,
|
|
24
|
+
input jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
25
|
+
status text NOT NULL DEFAULT 'queued',
|
|
26
|
+
output jsonb,
|
|
27
|
+
error text,
|
|
28
|
+
started_at timestamptz,
|
|
29
|
+
completed_at timestamptz,
|
|
30
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
31
|
+
)
|
|
32
|
+
`.execute(db);
|
|
33
|
+
|
|
34
|
+
await sql`
|
|
35
|
+
CREATE INDEX workflow_runs_name_status_idx
|
|
36
|
+
ON workflow_runs (workflow_name, status, created_at DESC)
|
|
37
|
+
`.execute(db);
|
|
38
|
+
|
|
39
|
+
await sql`
|
|
40
|
+
CREATE TABLE workflow_steps (
|
|
41
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
42
|
+
run_id uuid NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,
|
|
43
|
+
step_id text NOT NULL,
|
|
44
|
+
memo_key text NOT NULL,
|
|
45
|
+
status text NOT NULL DEFAULT 'pending',
|
|
46
|
+
output jsonb,
|
|
47
|
+
error text,
|
|
48
|
+
attempts integer NOT NULL DEFAULT 0,
|
|
49
|
+
started_at timestamptz,
|
|
50
|
+
completed_at timestamptz,
|
|
51
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
52
|
+
UNIQUE (run_id, memo_key)
|
|
53
|
+
)
|
|
54
|
+
`.execute(db);
|
|
55
|
+
|
|
56
|
+
await sql`
|
|
57
|
+
CREATE INDEX workflow_steps_run_idx
|
|
58
|
+
ON workflow_steps (run_id, created_at ASC)
|
|
59
|
+
`.execute(db);
|
|
60
|
+
|
|
61
|
+
await sql`
|
|
62
|
+
CREATE TABLE workflow_queue (
|
|
63
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
64
|
+
run_id uuid NOT NULL REFERENCES workflow_runs(id) ON DELETE CASCADE,
|
|
65
|
+
status text NOT NULL DEFAULT 'pending',
|
|
66
|
+
attempts integer NOT NULL DEFAULT 0,
|
|
67
|
+
max_attempts integer NOT NULL DEFAULT 3,
|
|
68
|
+
scheduled_for timestamptz NOT NULL DEFAULT now(),
|
|
69
|
+
locked_at timestamptz,
|
|
70
|
+
locked_by text,
|
|
71
|
+
error text,
|
|
72
|
+
completed_at timestamptz,
|
|
73
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
74
|
+
)
|
|
75
|
+
`.execute(db);
|
|
76
|
+
|
|
77
|
+
await sql`
|
|
78
|
+
CREATE INDEX workflow_queue_dispatch_idx
|
|
79
|
+
ON workflow_queue (status, scheduled_for)
|
|
80
|
+
WHERE status = 'pending'
|
|
81
|
+
`.execute(db);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
85
|
+
await sql`DROP TABLE IF EXISTS workflow_queue CASCADE`.execute(db);
|
|
86
|
+
await sql`DROP TABLE IF EXISTS workflow_steps CASCADE`.execute(db);
|
|
87
|
+
await sql`DROP TABLE IF EXISTS workflow_runs CASCADE`.execute(db);
|
|
88
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Normalize the legacy `free` plan string to `hobby`.
|
|
5
|
+
*
|
|
6
|
+
* Migration 0004 originally set `accounts.plan` default to `"free"` when
|
|
7
|
+
* the tier vocabulary was free/pro/builder. The pricing model later
|
|
8
|
+
* settled on `hobby/launch/grow/scale/enterprise`. Every lookup table
|
|
9
|
+
* (pricing.ts allowances, TIER_META, webhook handlers) is keyed on
|
|
10
|
+
* `hobby` — but existing signups still carry `plan='free'`, breaking
|
|
11
|
+
* the usage + billing pages' tier mapping.
|
|
12
|
+
*
|
|
13
|
+
* This migration:
|
|
14
|
+
* 1. Backfills existing rows: `free` → `hobby`
|
|
15
|
+
* 2. Flips the column default so new signups default to `hobby`
|
|
16
|
+
*
|
|
17
|
+
* No paid accounts are affected (no existing `plan` value maps to a
|
|
18
|
+
* current tier except `hobby` and whatever a webhook has written).
|
|
19
|
+
*/
|
|
20
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
21
|
+
await sql`UPDATE accounts SET plan = 'hobby' WHERE plan = 'free'`.execute(db);
|
|
22
|
+
await sql`ALTER TABLE accounts ALTER COLUMN plan SET DEFAULT 'hobby'`.execute(
|
|
23
|
+
db,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
28
|
+
await sql`ALTER TABLE accounts ALTER COLUMN plan SET DEFAULT 'free'`.execute(
|
|
29
|
+
db,
|
|
30
|
+
);
|
|
31
|
+
await sql`UPDATE accounts SET plan = 'free' WHERE plan = 'hobby'`.execute(db);
|
|
32
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Re-scope workflow AI usage to the account level.
|
|
5
|
+
*
|
|
6
|
+
* Sentries have no tenant (they're account-level), so the original
|
|
7
|
+
* `workflow_ai_usage_daily` schema keyed on `(tenant_id, day)` can't
|
|
8
|
+
* attribute a sentry's AI calls to an account. This migration:
|
|
9
|
+
*
|
|
10
|
+
* 1. Adds `account_id` (NOT NULL) and makes `tenant_id` nullable on
|
|
11
|
+
* `workflow_ai_usage_daily`.
|
|
12
|
+
* 2. Drops the old PK, replaces with a UNIQUE index using
|
|
13
|
+
* `NULLS NOT DISTINCT` so two NULL-tenant rows for the same
|
|
14
|
+
* account+day conflict correctly (requires PG 15+, matches prod).
|
|
15
|
+
* 3. Adds `account_id` + `tenant_id` nullable columns to
|
|
16
|
+
* `workflow_runs` so the processor can set AsyncLocalStorage
|
|
17
|
+
* context before invoking the handler — AI middleware reads this
|
|
18
|
+
* context to attribute usage back to the caller.
|
|
19
|
+
*
|
|
20
|
+
* Backfill for existing rows joins through `tenants.account_id`.
|
|
21
|
+
* On prod the table has 0 rows (AI middleware was a no-op previously),
|
|
22
|
+
* so backfill is a formality.
|
|
23
|
+
*/
|
|
24
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
25
|
+
// ── workflow_ai_usage_daily ──────────────────────────────────────
|
|
26
|
+
await sql`
|
|
27
|
+
ALTER TABLE workflow_ai_usage_daily
|
|
28
|
+
ADD COLUMN account_id uuid REFERENCES accounts(id) ON DELETE CASCADE
|
|
29
|
+
`.execute(db);
|
|
30
|
+
|
|
31
|
+
await sql`
|
|
32
|
+
UPDATE workflow_ai_usage_daily u
|
|
33
|
+
SET account_id = t.account_id
|
|
34
|
+
FROM tenants t
|
|
35
|
+
WHERE u.tenant_id = t.id AND u.account_id IS NULL
|
|
36
|
+
`.execute(db);
|
|
37
|
+
|
|
38
|
+
// Drop any rows we couldn't backfill (orphans — tenant already deleted).
|
|
39
|
+
await sql`DELETE FROM workflow_ai_usage_daily WHERE account_id IS NULL`.execute(
|
|
40
|
+
db,
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Drop the old PK first — it includes tenant_id, which blocks the
|
|
44
|
+
// subsequent DROP NOT NULL.
|
|
45
|
+
await sql`
|
|
46
|
+
ALTER TABLE workflow_ai_usage_daily
|
|
47
|
+
DROP CONSTRAINT workflow_ai_usage_daily_pkey
|
|
48
|
+
`.execute(db);
|
|
49
|
+
|
|
50
|
+
await sql`
|
|
51
|
+
ALTER TABLE workflow_ai_usage_daily
|
|
52
|
+
ALTER COLUMN account_id SET NOT NULL,
|
|
53
|
+
ALTER COLUMN tenant_id DROP NOT NULL
|
|
54
|
+
`.execute(db);
|
|
55
|
+
|
|
56
|
+
await sql`
|
|
57
|
+
CREATE UNIQUE INDEX workflow_ai_usage_daily_account_tenant_day_key
|
|
58
|
+
ON workflow_ai_usage_daily (account_id, tenant_id, day)
|
|
59
|
+
NULLS NOT DISTINCT
|
|
60
|
+
`.execute(db);
|
|
61
|
+
|
|
62
|
+
await sql`DROP INDEX IF EXISTS workflow_ai_usage_daily_lookup_idx`.execute(
|
|
63
|
+
db,
|
|
64
|
+
);
|
|
65
|
+
await sql`
|
|
66
|
+
CREATE INDEX workflow_ai_usage_daily_account_day_idx
|
|
67
|
+
ON workflow_ai_usage_daily (account_id, day DESC)
|
|
68
|
+
`.execute(db);
|
|
69
|
+
|
|
70
|
+
// ── workflow_runs ────────────────────────────────────────────────
|
|
71
|
+
await sql`
|
|
72
|
+
ALTER TABLE workflow_runs
|
|
73
|
+
ADD COLUMN account_id uuid REFERENCES accounts(id) ON DELETE SET NULL,
|
|
74
|
+
ADD COLUMN tenant_id uuid REFERENCES tenants(id) ON DELETE SET NULL
|
|
75
|
+
`.execute(db);
|
|
76
|
+
|
|
77
|
+
await sql`
|
|
78
|
+
CREATE INDEX workflow_runs_account_idx
|
|
79
|
+
ON workflow_runs (account_id, created_at DESC)
|
|
80
|
+
WHERE account_id IS NOT NULL
|
|
81
|
+
`.execute(db);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
85
|
+
await sql`DROP INDEX IF EXISTS workflow_runs_account_idx`.execute(db);
|
|
86
|
+
await sql`
|
|
87
|
+
ALTER TABLE workflow_runs
|
|
88
|
+
DROP COLUMN IF EXISTS account_id,
|
|
89
|
+
DROP COLUMN IF EXISTS tenant_id
|
|
90
|
+
`.execute(db);
|
|
91
|
+
|
|
92
|
+
await sql`DROP INDEX IF EXISTS workflow_ai_usage_daily_account_day_idx`.execute(
|
|
93
|
+
db,
|
|
94
|
+
);
|
|
95
|
+
await sql`DROP INDEX IF EXISTS workflow_ai_usage_daily_account_tenant_day_key`.execute(
|
|
96
|
+
db,
|
|
97
|
+
);
|
|
98
|
+
// Best-effort: rows without tenant_id fill would violate NOT NULL on
|
|
99
|
+
// revert. Assume down is dev-only.
|
|
100
|
+
await sql`DELETE FROM workflow_ai_usage_daily WHERE tenant_id IS NULL`.execute(
|
|
101
|
+
db,
|
|
102
|
+
);
|
|
103
|
+
await sql`
|
|
104
|
+
ALTER TABLE workflow_ai_usage_daily
|
|
105
|
+
ALTER COLUMN tenant_id SET NOT NULL,
|
|
106
|
+
DROP COLUMN account_id
|
|
107
|
+
`.execute(db);
|
|
108
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Drop all residual workflow + sentry tables after the product pivot.
|
|
5
|
+
* Workflows/runner/sentries packages are gone; these tables have no writers.
|
|
6
|
+
*/
|
|
7
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
8
|
+
await sql`DROP TABLE IF EXISTS sentry_alerts CASCADE`.execute(db);
|
|
9
|
+
await sql`DROP TABLE IF EXISTS sentries CASCADE`.execute(db);
|
|
10
|
+
await sql`DROP TABLE IF EXISTS workflow_queue CASCADE`.execute(db);
|
|
11
|
+
await sql`DROP TABLE IF EXISTS workflow_steps CASCADE`.execute(db);
|
|
12
|
+
await sql`DROP TABLE IF EXISTS workflow_runs CASCADE`.execute(db);
|
|
13
|
+
await sql`DROP TABLE IF EXISTS workflow_ai_usage_daily CASCADE`.execute(db);
|
|
14
|
+
|
|
15
|
+
await sql`DROP TRIGGER IF EXISTS tx_confirmed_trigger ON transactions`.execute(
|
|
16
|
+
db,
|
|
17
|
+
);
|
|
18
|
+
await sql`DROP FUNCTION IF EXISTS tx_confirmed_notify() CASCADE`.execute(db);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function down(_db: Kysely<unknown>): Promise<void> {
|
|
22
|
+
throw new Error("0056 is a one-way demolition; restore from backup");
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/shared",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/src/index.js",
|
|
6
6
|
"types": "./dist/src/index.d.ts",
|
|
@@ -41,10 +41,6 @@
|
|
|
41
41
|
"types": "./dist/src/db/schema.d.ts",
|
|
42
42
|
"import": "./dist/src/db/schema.js"
|
|
43
43
|
},
|
|
44
|
-
"./lib/plans": {
|
|
45
|
-
"types": "./dist/src/lib/plans.d.ts",
|
|
46
|
-
"import": "./dist/src/lib/plans.js"
|
|
47
|
-
},
|
|
48
44
|
"./queue/listener": {
|
|
49
45
|
"types": "./dist/src/queue/listener.d.ts",
|
|
50
46
|
"import": "./dist/src/queue/listener.js"
|
|
@@ -65,14 +61,6 @@
|
|
|
65
61
|
"types": "./dist/src/schemas/accounts.d.ts",
|
|
66
62
|
"import": "./dist/src/schemas/accounts.js"
|
|
67
63
|
},
|
|
68
|
-
"./schemas/workflows": {
|
|
69
|
-
"types": "./dist/src/schemas/workflows.d.ts",
|
|
70
|
-
"import": "./dist/src/schemas/workflows.js"
|
|
71
|
-
},
|
|
72
|
-
"./db/queries/workflows": {
|
|
73
|
-
"types": "./dist/src/db/queries/workflows.d.ts",
|
|
74
|
-
"import": "./dist/src/db/queries/workflows.js"
|
|
75
|
-
},
|
|
76
64
|
"./db/queries/tenants": {
|
|
77
65
|
"types": "./dist/src/db/queries/tenants.d.ts",
|
|
78
66
|
"import": "./dist/src/db/queries/tenants.js"
|
|
@@ -85,6 +73,30 @@
|
|
|
85
73
|
"types": "./dist/src/db/queries/provisioning-audit.d.ts",
|
|
86
74
|
"import": "./dist/src/db/queries/provisioning-audit.js"
|
|
87
75
|
},
|
|
76
|
+
"./db/queries/tenant-compute-addons": {
|
|
77
|
+
"types": "./dist/src/db/queries/tenant-compute-addons.d.ts",
|
|
78
|
+
"import": "./dist/src/db/queries/tenant-compute-addons.js"
|
|
79
|
+
},
|
|
80
|
+
"./db/queries/account-spend-caps": {
|
|
81
|
+
"types": "./dist/src/db/queries/account-spend-caps.d.ts",
|
|
82
|
+
"import": "./dist/src/db/queries/account-spend-caps.js"
|
|
83
|
+
},
|
|
84
|
+
"./db/queries/sentries": {
|
|
85
|
+
"types": "./dist/src/db/queries/sentries.d.ts",
|
|
86
|
+
"import": "./dist/src/db/queries/sentries.js"
|
|
87
|
+
},
|
|
88
|
+
"./db/queries/account-usage": {
|
|
89
|
+
"types": "./dist/src/db/queries/account-usage.d.ts",
|
|
90
|
+
"import": "./dist/src/db/queries/account-usage.js"
|
|
91
|
+
},
|
|
92
|
+
"./db/queries/workflow-runs": {
|
|
93
|
+
"types": "./dist/src/db/queries/workflow-runs.d.ts",
|
|
94
|
+
"import": "./dist/src/db/queries/workflow-runs.js"
|
|
95
|
+
},
|
|
96
|
+
"./schemas/sentries": {
|
|
97
|
+
"types": "./dist/src/schemas/sentries.d.ts",
|
|
98
|
+
"import": "./dist/src/schemas/sentries.js"
|
|
99
|
+
},
|
|
88
100
|
"./types": {
|
|
89
101
|
"types": "./dist/src/types.d.ts",
|
|
90
102
|
"import": "./dist/src/types.js"
|
|
@@ -164,7 +176,7 @@
|
|
|
164
176
|
"prepublishOnly": "bun run build"
|
|
165
177
|
},
|
|
166
178
|
"dependencies": {
|
|
167
|
-
"@secondlayer/stacks": "^0.
|
|
179
|
+
"@secondlayer/stacks": "^1.0.0-alpha.0",
|
|
168
180
|
"kysely": "0.28.15",
|
|
169
181
|
"kysely-postgres-js": "3.0.0",
|
|
170
182
|
"postgres": "^3.4.6",
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
var __defProp = Object.defineProperty;
|
|
3
|
-
var __returnValue = (v) => v;
|
|
4
|
-
function __exportSetter(name, newValue) {
|
|
5
|
-
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
-
}
|
|
7
|
-
var __export = (target, all) => {
|
|
8
|
-
for (var name in all)
|
|
9
|
-
__defProp(target, name, {
|
|
10
|
-
get: all[name],
|
|
11
|
-
enumerable: true,
|
|
12
|
-
configurable: true,
|
|
13
|
-
set: __exportSetter.bind(all, name)
|
|
14
|
-
});
|
|
15
|
-
};
|
|
16
|
-
|
|
17
|
-
// src/db/jsonb.ts
|
|
18
|
-
import { sql } from "kysely";
|
|
19
|
-
function jsonb(value) {
|
|
20
|
-
const escaped = JSON.stringify(value, (_k, v) => typeof v === "bigint" ? v.toString() : v).replace(/'/g, "''");
|
|
21
|
-
return sql`${sql.raw(`'${escaped}'::jsonb`)}`;
|
|
22
|
-
}
|
|
23
|
-
function parseJsonb(value) {
|
|
24
|
-
if (typeof value === "string") {
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(value);
|
|
27
|
-
} catch {
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return value ?? {};
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// src/errors.ts
|
|
35
|
-
var ErrorCodes = {
|
|
36
|
-
VALIDATION_ERROR: "VALIDATION_ERROR",
|
|
37
|
-
DATABASE_ERROR: "DATABASE_ERROR",
|
|
38
|
-
AUTHENTICATION_ERROR: "AUTHENTICATION_ERROR",
|
|
39
|
-
AUTHORIZATION_ERROR: "AUTHORIZATION_ERROR",
|
|
40
|
-
RATE_LIMIT_ERROR: "RATE_LIMIT_ERROR",
|
|
41
|
-
FORBIDDEN: "FORBIDDEN",
|
|
42
|
-
VERSION_CONFLICT: "VERSION_CONFLICT",
|
|
43
|
-
NOT_FOUND: "NOT_FOUND",
|
|
44
|
-
KEY_ROTATED: "KEY_ROTATED",
|
|
45
|
-
TRIAL_EXPIRED: "TRIAL_EXPIRED",
|
|
46
|
-
TENANT_SUSPENDED: "TENANT_SUSPENDED",
|
|
47
|
-
NO_TENANT_FOR_PROJECT: "NO_TENANT_FOR_PROJECT",
|
|
48
|
-
INSTANCE_EXISTS: "INSTANCE_EXISTS"
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
class SecondLayerError extends Error {
|
|
52
|
-
code;
|
|
53
|
-
cause;
|
|
54
|
-
constructor(code, message, cause) {
|
|
55
|
-
super(message);
|
|
56
|
-
this.code = code;
|
|
57
|
-
this.cause = cause;
|
|
58
|
-
this.name = this.constructor.name;
|
|
59
|
-
Error.captureStackTrace?.(this, this.constructor);
|
|
60
|
-
}
|
|
61
|
-
toJSON() {
|
|
62
|
-
return {
|
|
63
|
-
name: this.name,
|
|
64
|
-
code: this.code,
|
|
65
|
-
message: this.message,
|
|
66
|
-
stack: this.stack,
|
|
67
|
-
cause: this.cause
|
|
68
|
-
};
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
class NotFoundError extends SecondLayerError {
|
|
73
|
-
constructor(message) {
|
|
74
|
-
super("NOT_FOUND", message);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
class ValidationError extends SecondLayerError {
|
|
79
|
-
constructor(message, cause) {
|
|
80
|
-
super("VALIDATION_ERROR", message, cause);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
class DatabaseError extends SecondLayerError {
|
|
85
|
-
constructor(message, cause) {
|
|
86
|
-
super("DATABASE_ERROR", message, cause);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
class AuthenticationError extends SecondLayerError {
|
|
91
|
-
constructor(message) {
|
|
92
|
-
super("AUTHENTICATION_ERROR", message);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
class AuthorizationError extends SecondLayerError {
|
|
97
|
-
constructor(message) {
|
|
98
|
-
super("AUTHORIZATION_ERROR", message);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
class RateLimitError extends SecondLayerError {
|
|
103
|
-
constructor(message) {
|
|
104
|
-
super("RATE_LIMIT_ERROR", message);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
class ForbiddenError extends SecondLayerError {
|
|
109
|
-
constructor(message = "Forbidden") {
|
|
110
|
-
super("FORBIDDEN", message);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
class VersionConflictError extends SecondLayerError {
|
|
115
|
-
currentVersion;
|
|
116
|
-
expectedVersion;
|
|
117
|
-
constructor(currentVersion, expectedVersion) {
|
|
118
|
-
super("VERSION_CONFLICT", `Version conflict: expected ${expectedVersion}, current ${currentVersion}`);
|
|
119
|
-
this.currentVersion = currentVersion;
|
|
120
|
-
this.expectedVersion = expectedVersion;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
class KeyRotatedError extends SecondLayerError {
|
|
125
|
-
constructor(message = "Token has been rotated") {
|
|
126
|
-
super("KEY_ROTATED", message);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
class TrialExpiredError extends SecondLayerError {
|
|
131
|
-
constructor(message) {
|
|
132
|
-
super("TRIAL_EXPIRED", message);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
class TenantSuspendedError extends SecondLayerError {
|
|
137
|
-
constructor(message = "Instance is suspended") {
|
|
138
|
-
super("TENANT_SUSPENDED", message);
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
var CODE_TO_STATUS = {
|
|
142
|
-
AUTHENTICATION_ERROR: 401,
|
|
143
|
-
AUTHORIZATION_ERROR: 403,
|
|
144
|
-
RATE_LIMIT_ERROR: 429,
|
|
145
|
-
FORBIDDEN: 403,
|
|
146
|
-
NOT_FOUND: 404,
|
|
147
|
-
VALIDATION_ERROR: 400,
|
|
148
|
-
KEY_ROTATED: 401,
|
|
149
|
-
TRIAL_EXPIRED: 402,
|
|
150
|
-
TENANT_SUSPENDED: 423,
|
|
151
|
-
NO_TENANT_FOR_PROJECT: 404,
|
|
152
|
-
INSTANCE_EXISTS: 409
|
|
153
|
-
};
|
|
154
|
-
function getErrorMessage(err) {
|
|
155
|
-
return err instanceof Error ? err.message : String(err);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
// src/db/queries/workflows.ts
|
|
159
|
-
function bumpPatch(version) {
|
|
160
|
-
const parts = version.split(".");
|
|
161
|
-
if (parts.length !== 3)
|
|
162
|
-
return "1.0.1";
|
|
163
|
-
const [major, minor, patch] = parts.map((p) => Number.parseInt(p, 10));
|
|
164
|
-
if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch) || major === undefined || minor === undefined || patch === undefined) {
|
|
165
|
-
return "1.0.1";
|
|
166
|
-
}
|
|
167
|
-
return `${major}.${minor}.${patch + 1}`;
|
|
168
|
-
}
|
|
169
|
-
async function listWorkflowDefinitions(db, apiKeyIds) {
|
|
170
|
-
let query = db.selectFrom("workflow_definitions").selectAll().where("status", "!=", "deleted").orderBy("created_at", "desc");
|
|
171
|
-
if (apiKeyIds?.length) {
|
|
172
|
-
query = query.where("api_key_id", "in", apiKeyIds);
|
|
173
|
-
}
|
|
174
|
-
return await query.execute();
|
|
175
|
-
}
|
|
176
|
-
async function getWorkflowDefinition(db, name, apiKeyIds) {
|
|
177
|
-
let query = db.selectFrom("workflow_definitions").selectAll().where("name", "=", name);
|
|
178
|
-
if (apiKeyIds?.length) {
|
|
179
|
-
query = query.where("api_key_id", "in", apiKeyIds);
|
|
180
|
-
}
|
|
181
|
-
return await query.executeTakeFirst() ?? null;
|
|
182
|
-
}
|
|
183
|
-
async function upsertWorkflowDefinition(db, data) {
|
|
184
|
-
const existing = await db.selectFrom("workflow_definitions").selectAll().where("name", "=", data.name).where("api_key_id", "=", data.apiKeyId).executeTakeFirst();
|
|
185
|
-
if (existing) {
|
|
186
|
-
if (data.expectedVersion !== undefined && existing.version !== data.expectedVersion) {
|
|
187
|
-
throw new VersionConflictError(existing.version, data.expectedVersion);
|
|
188
|
-
}
|
|
189
|
-
const nextVersion = bumpPatch(existing.version);
|
|
190
|
-
return await db.updateTable("workflow_definitions").set({
|
|
191
|
-
trigger_type: data.triggerType,
|
|
192
|
-
trigger_config: jsonb(data.triggerConfig),
|
|
193
|
-
handler_path: data.handlerPath,
|
|
194
|
-
source_code: data.sourceCode ?? existing.source_code,
|
|
195
|
-
retries_config: data.retriesConfig ? jsonb(data.retriesConfig) : null,
|
|
196
|
-
timeout_ms: data.timeoutMs ?? null,
|
|
197
|
-
version: nextVersion,
|
|
198
|
-
status: "active",
|
|
199
|
-
updated_at: new Date
|
|
200
|
-
}).where("id", "=", existing.id).returningAll().executeTakeFirstOrThrow();
|
|
201
|
-
}
|
|
202
|
-
return await db.insertInto("workflow_definitions").values({
|
|
203
|
-
name: data.name,
|
|
204
|
-
trigger_type: data.triggerType,
|
|
205
|
-
trigger_config: jsonb(data.triggerConfig),
|
|
206
|
-
handler_path: data.handlerPath,
|
|
207
|
-
source_code: data.sourceCode ?? null,
|
|
208
|
-
api_key_id: data.apiKeyId,
|
|
209
|
-
project_id: data.projectId ?? null,
|
|
210
|
-
retries_config: data.retriesConfig ? jsonb(data.retriesConfig) : null,
|
|
211
|
-
timeout_ms: data.timeoutMs ?? null,
|
|
212
|
-
version: "1.0.0"
|
|
213
|
-
}).returningAll().executeTakeFirstOrThrow();
|
|
214
|
-
}
|
|
215
|
-
async function updateWorkflowStatus(db, name, apiKeyId, status) {
|
|
216
|
-
await db.updateTable("workflow_definitions").set({ status, updated_at: new Date }).where("name", "=", name).where("api_key_id", "=", apiKeyId).execute();
|
|
217
|
-
}
|
|
218
|
-
async function deleteWorkflowDefinition(db, name, apiKeyId) {
|
|
219
|
-
await db.updateTable("workflow_definitions").set({ status: "deleted", updated_at: new Date }).where("name", "=", name).where("api_key_id", "=", apiKeyId).execute();
|
|
220
|
-
}
|
|
221
|
-
async function createWorkflowRun(db, data) {
|
|
222
|
-
return await db.insertInto("workflow_runs").values({
|
|
223
|
-
definition_id: data.definitionId,
|
|
224
|
-
trigger_type: data.triggerType,
|
|
225
|
-
trigger_data: data.triggerData ? jsonb(data.triggerData) : null,
|
|
226
|
-
dedup_key: data.dedupKey ?? null
|
|
227
|
-
}).returningAll().executeTakeFirstOrThrow();
|
|
228
|
-
}
|
|
229
|
-
async function getWorkflowRun(db, runId) {
|
|
230
|
-
return await db.selectFrom("workflow_runs").selectAll().where("id", "=", runId).executeTakeFirst() ?? null;
|
|
231
|
-
}
|
|
232
|
-
async function listWorkflowRuns(db, definitionId, params) {
|
|
233
|
-
let query = db.selectFrom("workflow_runs").selectAll().where("definition_id", "=", definitionId).orderBy("created_at", "desc");
|
|
234
|
-
if (params?.status) {
|
|
235
|
-
query = query.where("status", "=", params.status);
|
|
236
|
-
}
|
|
237
|
-
query = query.limit(params?.limit ?? 20);
|
|
238
|
-
if (params?.offset) {
|
|
239
|
-
query = query.offset(params.offset);
|
|
240
|
-
}
|
|
241
|
-
return await query.execute();
|
|
242
|
-
}
|
|
243
|
-
async function getWorkflowSteps(db, runId) {
|
|
244
|
-
return await db.selectFrom("workflow_steps").selectAll().where("run_id", "=", runId).orderBy("step_index", "asc").execute();
|
|
245
|
-
}
|
|
246
|
-
export {
|
|
247
|
-
upsertWorkflowDefinition,
|
|
248
|
-
updateWorkflowStatus,
|
|
249
|
-
listWorkflowRuns,
|
|
250
|
-
listWorkflowDefinitions,
|
|
251
|
-
getWorkflowSteps,
|
|
252
|
-
getWorkflowRun,
|
|
253
|
-
getWorkflowDefinition,
|
|
254
|
-
deleteWorkflowDefinition,
|
|
255
|
-
createWorkflowRun,
|
|
256
|
-
bumpPatch
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
//# debugId=E33493756835BCAB64756E2164756E21
|
|
260
|
-
//# sourceMappingURL=workflows.js.map
|