@secondlayer/shared 0.4.0 → 0.5.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/dist/src/crypto/hmac.js +6 -2
- package/dist/src/crypto/hmac.js.map +2 -2
- package/dist/src/db/index.d.ts +83 -6
- package/dist/src/db/index.js +6 -2
- package/dist/src/db/index.js.map +2 -2
- package/dist/src/db/jsonb.js +6 -2
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/accounts.d.ts +75 -3
- package/dist/src/db/queries/accounts.js +17 -2
- package/dist/src/db/queries/accounts.js.map +3 -3
- package/dist/src/db/queries/integrity.d.ts +73 -2
- package/dist/src/db/queries/integrity.js +6 -2
- package/dist/src/db/queries/integrity.js.map +2 -2
- package/dist/src/db/queries/metrics.d.ts +73 -2
- package/dist/src/db/queries/metrics.js +6 -2
- package/dist/src/db/queries/metrics.js.map +2 -2
- package/dist/src/db/queries/{views.d.ts → subgraphs.d.ts} +87 -16
- package/dist/src/db/queries/{views.js → subgraphs.js} +37 -33
- package/dist/src/db/queries/subgraphs.js.map +11 -0
- package/dist/src/db/queries/usage.d.ts +76 -5
- package/dist/src/db/queries/usage.js +13 -9
- package/dist/src/db/queries/usage.js.map +4 -4
- package/dist/src/db/schema.d.ts +83 -6
- package/dist/src/env.js +6 -2
- package/dist/src/env.js.map +2 -2
- package/dist/src/errors.js +6 -2
- package/dist/src/errors.js.map +2 -2
- package/dist/src/index.d.ts +90 -13
- package/dist/src/index.js +9 -5
- package/dist/src/index.js.map +4 -4
- package/dist/src/lib/plans.d.ts +1 -1
- package/dist/src/lib/plans.js +7 -3
- package/dist/src/lib/plans.js.map +3 -3
- package/dist/src/logger.js +6 -2
- package/dist/src/logger.js.map +2 -2
- package/dist/src/node/client.js +6 -2
- package/dist/src/node/client.js.map +2 -2
- package/dist/src/node/hiro-client.js +6 -2
- package/dist/src/node/hiro-client.js.map +2 -2
- package/dist/src/node/local-client.d.ts +73 -2
- package/dist/src/node/local-client.js +6 -2
- package/dist/src/node/local-client.js.map +2 -2
- package/dist/src/queue/index.js +6 -2
- package/dist/src/queue/index.js.map +2 -2
- package/dist/src/queue/listener.js +6 -2
- package/dist/src/queue/listener.js.map +2 -2
- package/dist/src/queue/recovery.js +6 -2
- package/dist/src/queue/recovery.js.map +2 -2
- package/dist/src/schemas/filters.js +6 -2
- package/dist/src/schemas/filters.js.map +2 -2
- package/dist/src/schemas/index.d.ts +8 -8
- package/dist/src/schemas/index.js +9 -5
- package/dist/src/schemas/index.js.map +4 -4
- package/dist/src/schemas/{views.d.ts → subgraphs.d.ts} +8 -8
- package/dist/src/schemas/{views.js → subgraphs.js} +10 -6
- package/dist/src/schemas/subgraphs.js.map +10 -0
- package/dist/src/types.js +1 -12
- package/dist/src/types.js.map +1 -1
- package/migrations/0009_waitlist.ts +19 -0
- package/migrations/0010_waitlist_status.ts +12 -0
- package/migrations/0011_account_insights.ts +63 -0
- package/migrations/0012_view_health_snapshots.ts +29 -0
- package/migrations/0013_view_processing_stats.ts +42 -0
- package/migrations/0014_view_table_snapshots.ts +33 -0
- package/migrations/0015_rename_views_to_subgraphs.ts +136 -0
- package/package.json +7 -7
- package/dist/src/db/queries/views.js.map +0 -11
- package/dist/src/schemas/views.js.map +0 -10
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { Kysely } from "kysely";
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable("view_health_snapshots")
|
|
6
|
+
.addColumn("id", "uuid", (col) =>
|
|
7
|
+
col.primaryKey().defaultTo(db.fn("gen_random_uuid")),
|
|
8
|
+
)
|
|
9
|
+
.addColumn("view_id", "uuid", (col) =>
|
|
10
|
+
col.notNull().references("views.id").onDelete("cascade"),
|
|
11
|
+
)
|
|
12
|
+
.addColumn("total_processed", "bigint", (col) => col.notNull())
|
|
13
|
+
.addColumn("total_errors", "bigint", (col) => col.notNull())
|
|
14
|
+
.addColumn("last_processed_block", "integer")
|
|
15
|
+
.addColumn("captured_at", "timestamptz", (col) =>
|
|
16
|
+
col.notNull().defaultTo(db.fn("now")),
|
|
17
|
+
)
|
|
18
|
+
.execute();
|
|
19
|
+
|
|
20
|
+
await db.schema
|
|
21
|
+
.createIndex("idx_view_health_snapshots_view_captured")
|
|
22
|
+
.on("view_health_snapshots")
|
|
23
|
+
.columns(["view_id", "captured_at"])
|
|
24
|
+
.execute();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function down(db: Kysely<any>): Promise<void> {
|
|
28
|
+
await db.schema.dropTable("view_health_snapshots").execute();
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { Kysely } from "kysely";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
|
|
4
|
+
export async function up(db: Kysely<any>): Promise<void> {
|
|
5
|
+
await db.schema
|
|
6
|
+
.createTable("view_processing_stats")
|
|
7
|
+
.addColumn("id", "uuid", (col) =>
|
|
8
|
+
col.primaryKey().defaultTo(db.fn("gen_random_uuid")),
|
|
9
|
+
)
|
|
10
|
+
.addColumn("view_name", "text", (col) => col.notNull())
|
|
11
|
+
.addColumn("api_key_id", "text")
|
|
12
|
+
.addColumn("bucket_start", "timestamptz")
|
|
13
|
+
.addColumn("bucket_end", "timestamptz")
|
|
14
|
+
.addColumn("blocks_processed", "integer")
|
|
15
|
+
.addColumn("total_time_ms", "integer")
|
|
16
|
+
.addColumn("handler_time_ms", "integer")
|
|
17
|
+
.addColumn("flush_time_ms", "integer")
|
|
18
|
+
.addColumn("max_block_time_ms", "integer")
|
|
19
|
+
.addColumn("max_handler_time_ms", "integer")
|
|
20
|
+
.addColumn("avg_ops_per_block", sql`real`)
|
|
21
|
+
.addColumn("is_catchup", "boolean", (col) => col.defaultTo(false))
|
|
22
|
+
.addColumn("created_at", "timestamptz", (col) =>
|
|
23
|
+
col.notNull().defaultTo(db.fn("now")),
|
|
24
|
+
)
|
|
25
|
+
.execute();
|
|
26
|
+
|
|
27
|
+
await db.schema
|
|
28
|
+
.createIndex("idx_view_processing_stats_view_bucket")
|
|
29
|
+
.on("view_processing_stats")
|
|
30
|
+
.columns(["view_name", "bucket_start"])
|
|
31
|
+
.execute();
|
|
32
|
+
|
|
33
|
+
await db.schema
|
|
34
|
+
.createIndex("idx_view_processing_stats_api_key")
|
|
35
|
+
.on("view_processing_stats")
|
|
36
|
+
.column("api_key_id")
|
|
37
|
+
.execute();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function down(db: Kysely<any>): Promise<void> {
|
|
41
|
+
await db.schema.dropTable("view_processing_stats").execute();
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Kysely } from "kysely";
|
|
2
|
+
|
|
3
|
+
export async function up(db: Kysely<any>): Promise<void> {
|
|
4
|
+
await db.schema
|
|
5
|
+
.createTable("view_table_snapshots")
|
|
6
|
+
.addColumn("id", "uuid", (col) =>
|
|
7
|
+
col.primaryKey().defaultTo(db.fn("gen_random_uuid")),
|
|
8
|
+
)
|
|
9
|
+
.addColumn("view_name", "text", (col) => col.notNull())
|
|
10
|
+
.addColumn("api_key_id", "text")
|
|
11
|
+
.addColumn("table_name", "text", (col) => col.notNull())
|
|
12
|
+
.addColumn("row_count", "bigint")
|
|
13
|
+
.addColumn("created_at", "timestamptz", (col) =>
|
|
14
|
+
col.notNull().defaultTo(db.fn("now")),
|
|
15
|
+
)
|
|
16
|
+
.execute();
|
|
17
|
+
|
|
18
|
+
await db.schema
|
|
19
|
+
.createIndex("idx_view_table_snapshots_view_table_created")
|
|
20
|
+
.on("view_table_snapshots")
|
|
21
|
+
.columns(["view_name", "table_name", "created_at"])
|
|
22
|
+
.execute();
|
|
23
|
+
|
|
24
|
+
await db.schema
|
|
25
|
+
.createIndex("idx_view_table_snapshots_api_key")
|
|
26
|
+
.on("view_table_snapshots")
|
|
27
|
+
.column("api_key_id")
|
|
28
|
+
.execute();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function down(db: Kysely<any>): Promise<void> {
|
|
32
|
+
await db.schema.dropTable("view_table_snapshots").execute();
|
|
33
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { Kysely } from "kysely";
|
|
2
|
+
import { sql } from "kysely";
|
|
3
|
+
|
|
4
|
+
export async function up(db: Kysely<any>): Promise<void> {
|
|
5
|
+
// Drop old trigger + function
|
|
6
|
+
await sql`DROP TRIGGER IF EXISTS views_notify_trigger ON "views"`.execute(db);
|
|
7
|
+
await sql`DROP FUNCTION IF EXISTS notify_view_changes()`.execute(db);
|
|
8
|
+
|
|
9
|
+
// Rename tables
|
|
10
|
+
await sql`ALTER TABLE "views" RENAME TO "subgraphs"`.execute(db);
|
|
11
|
+
await sql`ALTER TABLE "view_health_snapshots" RENAME TO "subgraph_health_snapshots"`.execute(db);
|
|
12
|
+
await sql`ALTER TABLE "view_processing_stats" RENAME TO "subgraph_processing_stats"`.execute(db);
|
|
13
|
+
await sql`ALTER TABLE "view_table_snapshots" RENAME TO "subgraph_table_snapshots"`.execute(db);
|
|
14
|
+
|
|
15
|
+
// Rename indexes on subgraphs (formerly views)
|
|
16
|
+
await sql`ALTER INDEX "views_name_idx" RENAME TO "subgraphs_name_idx"`.execute(db);
|
|
17
|
+
await sql`ALTER INDEX "views_status_idx" RENAME TO "subgraphs_status_idx"`.execute(db);
|
|
18
|
+
|
|
19
|
+
// Rename indexes on subgraph_health_snapshots
|
|
20
|
+
await sql`ALTER INDEX "idx_view_health_snapshots_view_captured" RENAME TO "idx_subgraph_health_snapshots_subgraph_captured"`.execute(db);
|
|
21
|
+
|
|
22
|
+
// Rename indexes on subgraph_processing_stats
|
|
23
|
+
await sql`ALTER INDEX "idx_view_processing_stats_view_bucket" RENAME TO "idx_subgraph_processing_stats_subgraph_bucket"`.execute(db);
|
|
24
|
+
await sql`ALTER INDEX "idx_view_processing_stats_api_key" RENAME TO "idx_subgraph_processing_stats_api_key"`.execute(db);
|
|
25
|
+
|
|
26
|
+
// Rename indexes on subgraph_table_snapshots
|
|
27
|
+
await sql`ALTER INDEX "idx_view_table_snapshots_view_table_created" RENAME TO "idx_subgraph_table_snapshots_subgraph_table_created"`.execute(db);
|
|
28
|
+
await sql`ALTER INDEX "idx_view_table_snapshots_api_key" RENAME TO "idx_subgraph_table_snapshots_api_key"`.execute(db);
|
|
29
|
+
|
|
30
|
+
// Rename column view_id → subgraph_id in health snapshots
|
|
31
|
+
await sql`ALTER TABLE "subgraph_health_snapshots" RENAME COLUMN "view_id" TO "subgraph_id"`.execute(db);
|
|
32
|
+
|
|
33
|
+
// Rename column view_name → subgraph_name in processing stats and table snapshots
|
|
34
|
+
await sql`ALTER TABLE "subgraph_processing_stats" RENAME COLUMN "view_name" TO "subgraph_name"`.execute(db);
|
|
35
|
+
await sql`ALTER TABLE "subgraph_table_snapshots" RENAME COLUMN "view_name" TO "subgraph_name"`.execute(db);
|
|
36
|
+
|
|
37
|
+
// Rename FK constraint (Postgres auto-names it based on original table/column)
|
|
38
|
+
await sql`ALTER TABLE "subgraph_health_snapshots" RENAME CONSTRAINT "view_health_snapshots_view_id_fkey" TO "subgraph_health_snapshots_subgraph_id_fkey"`.execute(db);
|
|
39
|
+
|
|
40
|
+
// Recreate notify trigger with new names
|
|
41
|
+
await sql`
|
|
42
|
+
CREATE OR REPLACE FUNCTION notify_subgraph_changes() RETURNS trigger AS $$
|
|
43
|
+
BEGIN
|
|
44
|
+
PERFORM pg_notify('subgraph_changes', json_build_object(
|
|
45
|
+
'operation', TG_OP,
|
|
46
|
+
'name', COALESCE(NEW.name, OLD.name)
|
|
47
|
+
)::text);
|
|
48
|
+
RETURN COALESCE(NEW, OLD);
|
|
49
|
+
END;
|
|
50
|
+
$$ LANGUAGE plpgsql
|
|
51
|
+
`.execute(db);
|
|
52
|
+
|
|
53
|
+
await sql`
|
|
54
|
+
CREATE TRIGGER subgraphs_notify_trigger
|
|
55
|
+
AFTER INSERT OR UPDATE OR DELETE ON "subgraphs"
|
|
56
|
+
FOR EACH ROW EXECUTE FUNCTION notify_subgraph_changes()
|
|
57
|
+
`.execute(db);
|
|
58
|
+
|
|
59
|
+
// Rename any existing tenant schemas from view_* to subgraph_*
|
|
60
|
+
await sql`
|
|
61
|
+
DO $$
|
|
62
|
+
DECLARE
|
|
63
|
+
s record;
|
|
64
|
+
BEGIN
|
|
65
|
+
FOR s IN SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'view_%'
|
|
66
|
+
LOOP
|
|
67
|
+
EXECUTE format('ALTER SCHEMA %I RENAME TO %I', s.schema_name, 'subgraph_' || substring(s.schema_name from 6));
|
|
68
|
+
END LOOP;
|
|
69
|
+
END $$
|
|
70
|
+
`.execute(db);
|
|
71
|
+
|
|
72
|
+
// Update schema_name column in subgraphs table to match new prefix
|
|
73
|
+
await sql`UPDATE "subgraphs" SET schema_name = 'subgraph_' || substring(schema_name from 6) WHERE schema_name LIKE 'view_%'`.execute(db);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export async function down(db: Kysely<any>): Promise<void> {
|
|
77
|
+
// Drop new trigger + function
|
|
78
|
+
await sql`DROP TRIGGER IF EXISTS subgraphs_notify_trigger ON "subgraphs"`.execute(db);
|
|
79
|
+
await sql`DROP FUNCTION IF EXISTS notify_subgraph_changes()`.execute(db);
|
|
80
|
+
|
|
81
|
+
// Revert schema_name column
|
|
82
|
+
await sql`UPDATE "subgraphs" SET schema_name = 'view_' || substring(schema_name from 10) WHERE schema_name LIKE 'subgraph_%'`.execute(db);
|
|
83
|
+
|
|
84
|
+
// Revert tenant schemas
|
|
85
|
+
await sql`
|
|
86
|
+
DO $$
|
|
87
|
+
DECLARE
|
|
88
|
+
s record;
|
|
89
|
+
BEGIN
|
|
90
|
+
FOR s IN SELECT schema_name FROM information_schema.schemata WHERE schema_name LIKE 'subgraph_%'
|
|
91
|
+
LOOP
|
|
92
|
+
EXECUTE format('ALTER SCHEMA %I RENAME TO %I', s.schema_name, 'view_' || substring(s.schema_name from 10));
|
|
93
|
+
END LOOP;
|
|
94
|
+
END $$
|
|
95
|
+
`.execute(db);
|
|
96
|
+
|
|
97
|
+
// Rename columns back
|
|
98
|
+
await sql`ALTER TABLE "subgraph_health_snapshots" RENAME CONSTRAINT "subgraph_health_snapshots_subgraph_id_fkey" TO "view_health_snapshots_view_id_fkey"`.execute(db);
|
|
99
|
+
await sql`ALTER TABLE "subgraph_table_snapshots" RENAME COLUMN "subgraph_name" TO "view_name"`.execute(db);
|
|
100
|
+
await sql`ALTER TABLE "subgraph_processing_stats" RENAME COLUMN "subgraph_name" TO "view_name"`.execute(db);
|
|
101
|
+
await sql`ALTER TABLE "subgraph_health_snapshots" RENAME COLUMN "subgraph_id" TO "view_id"`.execute(db);
|
|
102
|
+
|
|
103
|
+
// Rename indexes back
|
|
104
|
+
await sql`ALTER INDEX "idx_subgraph_table_snapshots_api_key" RENAME TO "idx_view_table_snapshots_api_key"`.execute(db);
|
|
105
|
+
await sql`ALTER INDEX "idx_subgraph_table_snapshots_subgraph_table_created" RENAME TO "idx_view_table_snapshots_view_table_created"`.execute(db);
|
|
106
|
+
await sql`ALTER INDEX "idx_subgraph_processing_stats_api_key" RENAME TO "idx_view_processing_stats_api_key"`.execute(db);
|
|
107
|
+
await sql`ALTER INDEX "idx_subgraph_processing_stats_subgraph_bucket" RENAME TO "idx_view_processing_stats_view_bucket"`.execute(db);
|
|
108
|
+
await sql`ALTER INDEX "idx_subgraph_health_snapshots_subgraph_captured" RENAME TO "idx_view_health_snapshots_view_captured"`.execute(db);
|
|
109
|
+
await sql`ALTER INDEX "subgraphs_status_idx" RENAME TO "views_status_idx"`.execute(db);
|
|
110
|
+
await sql`ALTER INDEX "subgraphs_name_idx" RENAME TO "views_name_idx"`.execute(db);
|
|
111
|
+
|
|
112
|
+
// Rename tables back
|
|
113
|
+
await sql`ALTER TABLE "subgraph_table_snapshots" RENAME TO "view_table_snapshots"`.execute(db);
|
|
114
|
+
await sql`ALTER TABLE "subgraph_processing_stats" RENAME TO "view_processing_stats"`.execute(db);
|
|
115
|
+
await sql`ALTER TABLE "subgraph_health_snapshots" RENAME TO "view_health_snapshots"`.execute(db);
|
|
116
|
+
await sql`ALTER TABLE "subgraphs" RENAME TO "views"`.execute(db);
|
|
117
|
+
|
|
118
|
+
// Recreate old trigger
|
|
119
|
+
await sql`
|
|
120
|
+
CREATE OR REPLACE FUNCTION notify_view_changes() RETURNS trigger AS $$
|
|
121
|
+
BEGIN
|
|
122
|
+
PERFORM pg_notify('view_changes', json_build_object(
|
|
123
|
+
'operation', TG_OP,
|
|
124
|
+
'name', COALESCE(NEW.name, OLD.name)
|
|
125
|
+
)::text);
|
|
126
|
+
RETURN COALESCE(NEW, OLD);
|
|
127
|
+
END;
|
|
128
|
+
$$ LANGUAGE plpgsql
|
|
129
|
+
`.execute(db);
|
|
130
|
+
|
|
131
|
+
await sql`
|
|
132
|
+
CREATE TRIGGER views_notify_trigger
|
|
133
|
+
AFTER INSERT OR UPDATE OR DELETE ON "views"
|
|
134
|
+
FOR EACH ROW EXECUTE FUNCTION notify_view_changes()
|
|
135
|
+
`.execute(db);
|
|
136
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/shared",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/src/index.js",
|
|
6
6
|
"types": "./dist/src/index.d.ts",
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
"types": "./dist/src/db/queries/usage.d.ts",
|
|
30
30
|
"import": "./dist/src/db/queries/usage.js"
|
|
31
31
|
},
|
|
32
|
-
"./db/queries/
|
|
33
|
-
"types": "./dist/src/db/queries/
|
|
34
|
-
"import": "./dist/src/db/queries/
|
|
32
|
+
"./db/queries/subgraphs": {
|
|
33
|
+
"types": "./dist/src/db/queries/subgraphs.d.ts",
|
|
34
|
+
"import": "./dist/src/db/queries/subgraphs.js"
|
|
35
35
|
},
|
|
36
36
|
"./db/queries/contracts": {
|
|
37
37
|
"types": "./dist/src/db/queries/contracts.d.ts",
|
|
@@ -69,9 +69,9 @@
|
|
|
69
69
|
"types": "./dist/src/schemas/filters.d.ts",
|
|
70
70
|
"import": "./dist/src/schemas/filters.js"
|
|
71
71
|
},
|
|
72
|
-
"./schemas/
|
|
73
|
-
"types": "./dist/src/schemas/
|
|
74
|
-
"import": "./dist/src/schemas/
|
|
72
|
+
"./schemas/subgraphs": {
|
|
73
|
+
"types": "./dist/src/schemas/subgraphs.d.ts",
|
|
74
|
+
"import": "./dist/src/schemas/subgraphs.js"
|
|
75
75
|
},
|
|
76
76
|
"./types": {
|
|
77
77
|
"types": "./dist/src/types.d.ts",
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/db/jsonb.ts", "../src/db/queries/views.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import { sql, type RawBuilder } from \"kysely\";\n\n/**\n * Safely encode a JS value as a JSONB literal for Kysely inserts/updates.\n * Kysely + postgres.js double-encodes JSON when using parameterized queries\n * with ::jsonb casts. This uses sql.raw to inline a properly escaped literal.\n */\nexport function jsonb(value: unknown): RawBuilder<unknown> {\n const escaped = JSON.stringify(value).replace(/'/g, \"''\");\n return sql`${sql.raw(`'${escaped}'::jsonb`)}`;\n}\n\n/**\n * Safely parse a JSONB value from the database.\n * Handles double-encoded strings where postgres.js returns a JSON string\n * instead of a parsed object.\n */\nexport function parseJsonb<T = unknown>(value: unknown): T {\n if (typeof value === \"string\") {\n try {\n return JSON.parse(value) as T;\n } catch {\n return value as T;\n }\n }\n return (value ?? {}) as T;\n}\n",
|
|
6
|
-
"import { sql, type Kysely } from \"kysely\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, View } from \"../types.ts\";\n\n/**\n * Convert a view name to its PostgreSQL schema name.\n * With keyPrefix: \"view_{prefix}_{name}\" (tenant-isolated)\n * Without keyPrefix: \"view_{name}\" (backward compat)\n */\nexport function pgSchemaName(viewName: string, keyPrefix?: string): string {\n const safeName = viewName.replace(/-/g, \"_\");\n if (!keyPrefix) {\n return `view_${safeName}`;\n }\n const safePrefix = keyPrefix.replace(/^sk-sl_/, \"\").replace(/-/g, \"_\");\n return `view_${safePrefix}_${safeName}`;\n}\n\nexport async function registerView(\n db: Kysely<Database>,\n data: {\n name: string;\n version: string;\n definition: Record<string, unknown>;\n schemaHash: string;\n handlerPath: string;\n apiKeyId?: string;\n schemaName?: string;\n },\n): Promise<View> {\n return await db\n .insertInto(\"views\")\n .values({\n name: data.name,\n version: data.version,\n definition: jsonb(data.definition) as any,\n schema_hash: data.schemaHash,\n handler_path: data.handlerPath,\n api_key_id: data.apiKeyId ?? null,\n schema_name: data.schemaName ?? null,\n })\n .onConflict((oc) =>\n oc.columns([\"name\", \"api_key_id\"]).doUpdateSet({\n version: data.version,\n definition: jsonb(data.definition) as any,\n schema_hash: data.schemaHash,\n handler_path: data.handlerPath,\n schema_name: data.schemaName ?? null,\n updated_at: new Date(),\n }),\n )\n .returningAll()\n .executeTakeFirstOrThrow();\n}\n\nexport async function getView(db: Kysely<Database>, name: string, apiKeyId?: string): Promise<View | null> {\n let query = db\n .selectFrom(\"views\")\n .selectAll()\n .where(\"name\", \"=\", name);\n\n if (apiKeyId) {\n query = query.where(\"api_key_id\", \"=\", apiKeyId);\n }\n\n return (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listViews(db: Kysely<Database>, apiKeyId?: string): Promise<View[]> {\n let query = db.selectFrom(\"views\").selectAll();\n if (apiKeyId) {\n query = query.where(\"api_key_id\", \"=\", apiKeyId);\n }\n return query.execute();\n}\n\nexport async function updateViewStatus(\n db: Kysely<Database>,\n name: string,\n status: string,\n lastProcessedBlock?: number,\n): Promise<void> {\n await db\n .updateTable(\"views\")\n .set({\n status,\n ...(lastProcessedBlock !== undefined ? { last_processed_block: lastProcessedBlock } : {}),\n updated_at: new Date(),\n })\n .where(\"name\", \"=\", name)\n .execute();\n}\n\nexport async function recordViewProcessed(\n db: Kysely<Database>,\n name: string,\n processed: number,\n errors: number,\n lastError?: string,\n): Promise<void> {\n await db\n .updateTable(\"views\")\n .set({\n total_processed: sql`total_processed + ${processed}`,\n total_errors: sql`total_errors + ${errors}`,\n ...(lastError\n ? { last_error: lastError, last_error_at: new Date() }\n : {}),\n updated_at: new Date(),\n })\n .where(\"name\", \"=\", name)\n .execute();\n}\n\nexport async function updateViewHandlerPath(\n db: Kysely<Database>,\n name: string,\n handlerPath: string,\n): Promise<void> {\n await db\n .updateTable(\"views\")\n .set({ handler_path: handlerPath, updated_at: new Date() })\n .where(\"name\", \"=\", name)\n .execute();\n}\n\nexport async function deleteView(db: Kysely<Database>, name: string, apiKeyId?: string): Promise<View | null> {\n const view = await getView(db, name, apiKeyId);\n if (!view) return null;\n\n // Use stored schema_name if available, otherwise compute\n const schemaName = view.schema_name ?? pgSchemaName(name);\n\n // Drop the view's schema (CASCADE drops all tables within)\n await sql`DROP SCHEMA IF EXISTS ${sql.raw(`\"${schemaName}\"`)} CASCADE`.execute(db);\n\n // Remove from registry\n await db.deleteFrom(\"views\").where(\"id\", \"=\", view.id).execute();\n\n return view;\n}\n"
|
|
7
|
-
],
|
|
8
|
-
"mappings": "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",
|
|
9
|
-
"debugId": "C19949368802BB4964756E2164756E21",
|
|
10
|
-
"names": []
|
|
11
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../src/schemas/views.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"import { z } from \"zod\";\n\n// ── Deploy View Request ─────────────────────────────────────────────────\n\nexport interface DeployViewRequest {\n name: string;\n version?: string;\n description?: string;\n sources: string[];\n schema: Record<string, unknown>;\n handlerCode: string;\n reindex?: boolean;\n}\n\nexport const DeployViewRequestSchema: z.ZodType<DeployViewRequest> = z.object({\n name: z.string().regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\").max(63),\n version: z.string().optional(),\n description: z.string().optional(),\n sources: z.array(z.string()).min(1),\n schema: z.record(z.unknown()),\n handlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n reindex: z.boolean().optional(),\n});\n\nexport interface DeployViewResponse {\n action: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n viewId: string;\n message: string;\n}\n\n// View API response types\n\nexport interface ViewSummary {\n name: string;\n version: string;\n status: string;\n lastProcessedBlock: number;\n tables: string[];\n createdAt: string;\n}\n\nexport interface ViewDetail {\n name: string;\n version: string;\n status: string;\n lastProcessedBlock: number;\n health: {\n totalProcessed: number;\n totalErrors: number;\n errorRate: number;\n lastError: string | null;\n lastErrorAt: string | null;\n };\n tables: Record<string, {\n endpoint: string;\n columns: Record<string, { type: string; nullable?: boolean }>;\n rowCount: number;\n example: string;\n }>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ReindexResponse {\n message: string;\n fromBlock: number;\n toBlock: number | string;\n}\n\nexport interface ViewQueryParams {\n sort?: string;\n order?: string;\n limit?: number;\n offset?: number;\n fields?: string;\n filters?: Record<string, string>;\n}\n"
|
|
6
|
-
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;AAAA;AAcO,IAAM,0BAAwD,EAAE,OAAO;AAAA,EAC5E,MAAM,EAAE,OAAO,EAAE,MAAM,gBAAgB,uCAAuC,EAAE,IAAI,EAAE;AAAA,EACtF,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC;AAAA,EAClC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC;AAAA,EAC5B,aAAa,EAAE,OAAO,EAAE,IAAI,SAAW,gCAAgC;AAAA,EACvE,SAAS,EAAE,QAAQ,EAAE,SAAS;AAChC,CAAC;",
|
|
8
|
-
"debugId": "F7D47497A7667C0064756E2164756E21",
|
|
9
|
-
"names": []
|
|
10
|
-
}
|