@secondlayer/shared 0.7.0 → 0.8.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 +2 -2
- package/dist/src/crypto/hmac.js.map +3 -3
- package/dist/src/db/index.d.ts +18 -3
- package/dist/src/db/index.js +6 -2
- package/dist/src/db/index.js.map +4 -4
- package/dist/src/db/jsonb.js.map +2 -2
- package/dist/src/db/queries/accounts.d.ts +16 -3
- package/dist/src/db/queries/accounts.js +6 -3
- package/dist/src/db/queries/accounts.js.map +3 -3
- package/dist/src/db/queries/integrity.d.ts +15 -2
- package/dist/src/db/queries/integrity.js.map +2 -2
- package/dist/src/db/queries/metrics.d.ts +15 -2
- package/dist/src/db/queries/metrics.js.map +2 -2
- package/dist/src/db/queries/subgraph-gaps.d.ts +305 -0
- package/dist/src/db/queries/subgraph-gaps.js +103 -0
- package/dist/src/db/queries/subgraph-gaps.js.map +10 -0
- package/dist/src/db/queries/subgraphs.d.ts +15 -2
- package/dist/src/db/queries/subgraphs.js +2 -2
- package/dist/src/db/queries/subgraphs.js.map +4 -4
- package/dist/src/db/queries/usage.d.ts +23 -3
- package/dist/src/db/queries/usage.js +35 -3
- package/dist/src/db/queries/usage.js.map +4 -4
- package/dist/src/db/schema.d.ts +18 -3
- package/dist/src/env.js.map +2 -2
- package/dist/src/errors.d.ts +17 -4
- package/dist/src/errors.js +14 -2
- package/dist/src/errors.js.map +3 -3
- package/dist/src/index.d.ts +78 -6
- package/dist/src/index.js +26 -8
- package/dist/src/index.js.map +12 -12
- package/dist/src/lib/plans.js.map +2 -2
- package/dist/src/logger.js.map +3 -3
- package/dist/src/node/archive-client.js +22 -6
- package/dist/src/node/archive-client.js.map +5 -5
- package/dist/src/node/client.js.map +2 -2
- package/dist/src/node/hiro-client.js +47 -11
- package/dist/src/node/hiro-client.js.map +5 -5
- package/dist/src/node/hiro-pg-client.js +131 -26
- package/dist/src/node/hiro-pg-client.js.map +3 -3
- package/dist/src/node/local-client.d.ts +15 -2
- package/dist/src/node/local-client.js.map +2 -2
- package/dist/src/queue/index.js +8 -4
- package/dist/src/queue/index.js.map +5 -5
- package/dist/src/queue/listener.js.map +2 -2
- package/dist/src/queue/recovery.js +6 -2
- package/dist/src/queue/recovery.js.map +5 -5
- package/dist/src/schemas/filters.js +2 -2
- package/dist/src/schemas/filters.js.map +3 -3
- package/dist/src/schemas/index.d.ts +45 -1
- package/dist/src/schemas/index.js +5 -3
- package/dist/src/schemas/index.js.map +5 -5
- package/dist/src/schemas/subgraphs.d.ts +45 -1
- package/dist/src/schemas/subgraphs.js.map +2 -2
- package/dist/src/types.d.ts +1 -1
- package/migrations/0001_initial.ts +295 -159
- package/migrations/0002_api_keys.ts +44 -28
- package/migrations/0003_tenant_isolation.ts +116 -107
- package/migrations/0004_accounts_and_usage.ts +81 -75
- package/migrations/0005_sessions.ts +33 -33
- package/migrations/0006_tx_index.ts +6 -2
- package/migrations/0007_contracts.ts +38 -24
- package/migrations/0008_drop_contracts.ts +33 -19
- package/migrations/0009_waitlist.ts +12 -12
- package/migrations/0010_waitlist_status.ts +5 -5
- package/migrations/0011_account_insights.ts +52 -52
- package/migrations/0012_view_health_snapshots.ts +21 -21
- package/migrations/0013_view_processing_stats.ts +32 -32
- package/migrations/0014_view_table_snapshots.ts +24 -24
- package/migrations/0015_rename_views_to_subgraphs.ts +137 -75
- package/migrations/0016_rename_webhook_to_endpoint.ts +12 -4
- package/migrations/0017_security_hardening.ts +32 -0
- package/migrations/0018_subgraph_gaps.ts +39 -0
- package/package.json +147 -143
package/dist/src/crypto/hmac.js
CHANGED
|
@@ -56,7 +56,7 @@ function verifySignatureHeader(payload, header, secret, toleranceSeconds = 300)
|
|
|
56
56
|
if (!timestamp || !signature) {
|
|
57
57
|
return false;
|
|
58
58
|
}
|
|
59
|
-
const ts = parseInt(timestamp, 10);
|
|
59
|
+
const ts = Number.parseInt(timestamp, 10);
|
|
60
60
|
if (isNaN(ts)) {
|
|
61
61
|
return false;
|
|
62
62
|
}
|
|
@@ -75,5 +75,5 @@ export {
|
|
|
75
75
|
createSignatureHeader
|
|
76
76
|
};
|
|
77
77
|
|
|
78
|
-
//# debugId=
|
|
78
|
+
//# debugId=DCA3A03C2D7DBC2364756E2164756E21
|
|
79
79
|
//# sourceMappingURL=hmac.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/crypto/hmac.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { createHmac, randomBytes } from \"crypto\";\n\n/**\n * Generate a random secret for delivery signing\n * Returns 32 bytes as a 64-character hex string\n */\nexport function generateSecret(): string {\n
|
|
5
|
+
"import { createHmac, randomBytes } from \"crypto\";\n\n/**\n * Generate a random secret for delivery signing\n * Returns 32 bytes as a 64-character hex string\n */\nexport function generateSecret(): string {\n\treturn randomBytes(32).toString(\"hex\");\n}\n\n/**\n * Sign a payload with HMAC-SHA256\n * Returns the signature as a hex string\n */\nexport function signPayload(payload: string, secret: string): string {\n\tconst hmac = createHmac(\"sha256\", secret);\n\thmac.update(payload);\n\treturn hmac.digest(\"hex\");\n}\n\n/**\n * Verify an HMAC signature\n * Uses constant-time comparison to prevent timing attacks\n */\nexport function verifySignature(\n\tpayload: string,\n\tsignature: string,\n\tsecret: string,\n): boolean {\n\tconst expectedSignature = signPayload(payload, secret);\n\n\t// Constant-time comparison\n\tif (signature.length !== expectedSignature.length) {\n\t\treturn false;\n\t}\n\n\tlet result = 0;\n\tfor (let i = 0; i < signature.length; i++) {\n\t\tresult |= signature.charCodeAt(i) ^ expectedSignature.charCodeAt(i);\n\t}\n\n\treturn result === 0;\n}\n\n/**\n * Create a Stripe-style signature header\n * Format: t=timestamp,v1=signature\n */\nexport function createSignatureHeader(\n\tpayload: string,\n\tsecret: string,\n\ttimestamp?: number,\n): string {\n\tconst ts = timestamp ?? Math.floor(Date.now() / 1000);\n\tconst signedPayload = `${ts}.${payload}`;\n\tconst signature = signPayload(signedPayload, secret);\n\n\treturn `t=${ts},v1=${signature}`;\n}\n\n/**\n * Parse and verify a Stripe-style signature header\n * Returns true if valid, false otherwise\n */\nexport function verifySignatureHeader(\n\tpayload: string,\n\theader: string,\n\tsecret: string,\n\ttoleranceSeconds = 300, // 5 minutes\n): boolean {\n\t// Parse header\n\tconst parts = header.split(\",\");\n\tconst timestamp = parts.find((p) => p.startsWith(\"t=\"))?.slice(2);\n\tconst signature = parts.find((p) => p.startsWith(\"v1=\"))?.slice(3);\n\n\tif (!timestamp || !signature) {\n\t\treturn false;\n\t}\n\n\tconst ts = Number.parseInt(timestamp, 10);\n\tif (isNaN(ts)) {\n\t\treturn false;\n\t}\n\n\t// Check timestamp is within tolerance\n\tconst now = Math.floor(Date.now() / 1000);\n\tif (Math.abs(now - ts) > toleranceSeconds) {\n\t\treturn false;\n\t}\n\n\t// Verify signature\n\tconst signedPayload = `${ts}.${payload}`;\n\treturn verifySignature(signedPayload, signature, secret);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAMO,SAAS,cAAc,GAAW;AAAA,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAMO,SAAS,cAAc,GAAW;AAAA,EACxC,OAAO,YAAY,EAAE,EAAE,SAAS,KAAK;AAAA;AAO/B,SAAS,WAAW,CAAC,SAAiB,QAAwB;AAAA,EACpE,MAAM,OAAO,WAAW,UAAU,MAAM;AAAA,EACxC,KAAK,OAAO,OAAO;AAAA,EACnB,OAAO,KAAK,OAAO,KAAK;AAAA;AAOlB,SAAS,eAAe,CAC9B,SACA,WACA,QACU;AAAA,EACV,MAAM,oBAAoB,YAAY,SAAS,MAAM;AAAA,EAGrD,IAAI,UAAU,WAAW,kBAAkB,QAAQ;AAAA,IAClD,OAAO;AAAA,EACR;AAAA,EAEA,IAAI,SAAS;AAAA,EACb,SAAS,IAAI,EAAG,IAAI,UAAU,QAAQ,KAAK;AAAA,IAC1C,UAAU,UAAU,WAAW,CAAC,IAAI,kBAAkB,WAAW,CAAC;AAAA,EACnE;AAAA,EAEA,OAAO,WAAW;AAAA;AAOZ,SAAS,qBAAqB,CACpC,SACA,QACA,WACS;AAAA,EACT,MAAM,KAAK,aAAa,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EACpD,MAAM,gBAAgB,GAAG,MAAM;AAAA,EAC/B,MAAM,YAAY,YAAY,eAAe,MAAM;AAAA,EAEnD,OAAO,KAAK,SAAS;AAAA;AAOf,SAAS,qBAAqB,CACpC,SACA,QACA,QACA,mBAAmB,KACT;AAAA,EAEV,MAAM,QAAQ,OAAO,MAAM,GAAG;AAAA,EAC9B,MAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,IAAI,CAAC,GAAG,MAAM,CAAC;AAAA,EAChE,MAAM,YAAY,MAAM,KAAK,CAAC,MAAM,EAAE,WAAW,KAAK,CAAC,GAAG,MAAM,CAAC;AAAA,EAEjE,IAAI,CAAC,aAAa,CAAC,WAAW;AAAA,IAC7B,OAAO;AAAA,EACR;AAAA,EAEA,MAAM,KAAK,OAAO,SAAS,WAAW,EAAE;AAAA,EACxC,IAAI,MAAM,EAAE,GAAG;AAAA,IACd,OAAO;AAAA,EACR;AAAA,EAGA,MAAM,MAAM,KAAK,MAAM,KAAK,IAAI,IAAI,IAAI;AAAA,EACxC,IAAI,KAAK,IAAI,MAAM,EAAE,IAAI,kBAAkB;AAAA,IAC1C,OAAO;AAAA,EACR;AAAA,EAGA,MAAM,gBAAgB,GAAG,MAAM;AAAA,EAC/B,OAAO,gBAAgB,eAAe,WAAW,MAAM;AAAA;",
|
|
8
|
+
"debugId": "DCA3A03C2D7DBC2364756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
package/dist/src/db/index.d.ts
CHANGED
|
@@ -52,7 +52,7 @@ interface StreamsTable {
|
|
|
52
52
|
options: Generated<unknown>;
|
|
53
53
|
endpoint_url: string;
|
|
54
54
|
signing_secret: string | null;
|
|
55
|
-
api_key_id: string
|
|
55
|
+
api_key_id: string;
|
|
56
56
|
created_at: Generated<Date>;
|
|
57
57
|
updated_at: Generated<Date>;
|
|
58
58
|
}
|
|
@@ -106,15 +106,26 @@ interface SubgraphsTable {
|
|
|
106
106
|
schema_hash: string;
|
|
107
107
|
handler_path: string;
|
|
108
108
|
schema_name: string | null;
|
|
109
|
+
start_block: Generated<number>;
|
|
109
110
|
last_processed_block: Generated<number>;
|
|
110
111
|
last_error: string | null;
|
|
111
112
|
last_error_at: Date | null;
|
|
112
113
|
total_processed: Generated<number>;
|
|
113
114
|
total_errors: Generated<number>;
|
|
114
|
-
api_key_id: string
|
|
115
|
+
api_key_id: string;
|
|
115
116
|
created_at: Generated<Date>;
|
|
116
117
|
updated_at: Generated<Date>;
|
|
117
118
|
}
|
|
119
|
+
interface SubgraphGapsTable {
|
|
120
|
+
id: Generated<string>;
|
|
121
|
+
subgraph_id: string;
|
|
122
|
+
subgraph_name: string;
|
|
123
|
+
gap_start: number;
|
|
124
|
+
gap_end: number;
|
|
125
|
+
reason: string;
|
|
126
|
+
detected_at: Generated<Date>;
|
|
127
|
+
resolved_at: Date | null;
|
|
128
|
+
}
|
|
118
129
|
interface ApiKeysTable {
|
|
119
130
|
id: Generated<string>;
|
|
120
131
|
key_hash: string;
|
|
@@ -151,6 +162,7 @@ interface MagicLinksTable {
|
|
|
151
162
|
token: string;
|
|
152
163
|
expires_at: Date;
|
|
153
164
|
used_at: Date | null;
|
|
165
|
+
failed_attempts: Generated<number>;
|
|
154
166
|
created_at: Generated<Date>;
|
|
155
167
|
}
|
|
156
168
|
interface UsageDailyTable {
|
|
@@ -252,6 +264,7 @@ interface Database {
|
|
|
252
264
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
253
265
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
254
266
|
subgraph_table_snapshots: SubgraphTableSnapshotsTable;
|
|
267
|
+
subgraph_gaps: SubgraphGapsTable;
|
|
255
268
|
}
|
|
256
269
|
type Block = Selectable<BlocksTable>;
|
|
257
270
|
type InsertBlock = Insertable<BlocksTable>;
|
|
@@ -297,10 +310,12 @@ type AccountAgentRun = Selectable<AccountAgentRunsTable>;
|
|
|
297
310
|
type InsertAccountAgentRun = Insertable<AccountAgentRunsTable>;
|
|
298
311
|
type SubgraphHealthSnapshot = Selectable<SubgraphHealthSnapshotsTable>;
|
|
299
312
|
type InsertSubgraphHealthSnapshot = Insertable<SubgraphHealthSnapshotsTable>;
|
|
313
|
+
type SubgraphGap = Selectable<SubgraphGapsTable>;
|
|
314
|
+
type InsertSubgraphGap = Insertable<SubgraphGapsTable>;
|
|
300
315
|
import { sql } from "kysely";
|
|
301
316
|
declare function getDb(connectionString?: string): Kysely<Database>;
|
|
302
317
|
/** Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.) */
|
|
303
318
|
declare function getRawClient(): ReturnType<typeof postgres>;
|
|
304
319
|
/** Close the DB connection pool. Call in CLI commands to allow process exit. */
|
|
305
320
|
declare function closeDb(): Promise<void>;
|
|
306
|
-
export { sql, parseJsonb, jsonb, getRawClient, getDb, closeDb, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateSubgraph, UpdateStreamRow, UpdateStreamMetrics, UpdateJob, UpdateIndexProgress, UpdateEvent, UpdateDelivery, UpdateBlock, UpdateApiKey, TransactionsTable, Transaction, SubgraphsTable, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, Subgraph, StreamsTable, StreamMetricsTable, StreamMetrics, Stream, SessionsTable, Session, MagicLinksTable, MagicLink, JobsTable, Job, InsertTransaction, InsertSubgraphHealthSnapshot, InsertSubgraph, InsertStreamMetrics, InsertStream, InsertSession, InsertMagicLink, InsertJob, InsertIndexProgress, InsertEvent, InsertDelivery, InsertBlock, InsertApiKey, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Delivery, DeliveriesTable, Database, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
|
|
321
|
+
export { sql, parseJsonb, jsonb, getRawClient, getDb, closeDb, WaitlistTable, UsageSnapshotsTable, UsageSnapshot, UsageDailyTable, UsageDaily, UpdateTransaction, UpdateSubgraph, UpdateStreamRow, UpdateStreamMetrics, UpdateJob, UpdateIndexProgress, UpdateEvent, UpdateDelivery, UpdateBlock, UpdateApiKey, TransactionsTable, Transaction, SubgraphsTable, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, StreamsTable, StreamMetricsTable, StreamMetrics, Stream, SessionsTable, Session, MagicLinksTable, MagicLink, JobsTable, Job, InsertTransaction, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertStreamMetrics, InsertStream, InsertSession, InsertMagicLink, InsertJob, InsertIndexProgress, InsertEvent, InsertDelivery, InsertBlock, InsertApiKey, InsertAccountInsight, InsertAccountAgentRun, InsertAccount, IndexProgressTable, IndexProgress, EventsTable, Event, Delivery, DeliveriesTable, Database, BlocksTable, Block, ApiKeysTable, ApiKey, AccountsTable, AccountInsightsTable, AccountInsight, AccountAgentRunsTable, AccountAgentRun, Account };
|
package/dist/src/db/index.js
CHANGED
|
@@ -42,8 +42,12 @@ function getDb(connectionString) {
|
|
|
42
42
|
if (!db) {
|
|
43
43
|
const url = connectionString || process.env.DATABASE_URL || "postgres://postgres:postgres@localhost:5432/streams_dev";
|
|
44
44
|
const isLocal = url.includes("localhost") || url.includes("127.0.0.1") || url.includes("@postgres:");
|
|
45
|
+
const poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? "20", 10);
|
|
45
46
|
rawClient = postgres(url, {
|
|
46
|
-
|
|
47
|
+
max: poolMax,
|
|
48
|
+
ssl: isLocal ? undefined : {
|
|
49
|
+
rejectUnauthorized: process.env.NODE_TLS_REJECT_UNAUTHORIZED !== "0"
|
|
50
|
+
}
|
|
47
51
|
});
|
|
48
52
|
db = new Kysely({
|
|
49
53
|
dialect: new PostgresJSDialect({ postgres: rawClient })
|
|
@@ -75,5 +79,5 @@ export {
|
|
|
75
79
|
closeDb
|
|
76
80
|
};
|
|
77
81
|
|
|
78
|
-
//# debugId=
|
|
82
|
+
//# debugId=AB57A20678E6DFB964756E2164756E21
|
|
79
83
|
//# sourceMappingURL=index.js.map
|
package/dist/src/db/index.js.map
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/jsonb.ts", "../src/db/index.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {
|
|
6
|
-
"import { Kysely } from \"kysely\";\nimport { PostgresJSDialect } from \"kysely-postgres-js\";\nimport postgres from \"postgres\";\nimport type { Database } from \"./types.ts\";\n\nlet db: Kysely<Database> | null = null;\nlet rawClient: ReturnType<typeof postgres> | null = null;\n\nexport function getDb(connectionString?: string): Kysely<Database> {\n
|
|
5
|
+
"import { type RawBuilder, sql } 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\tconst escaped = JSON.stringify(value).replace(/'/g, \"''\");\n\treturn 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\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n",
|
|
6
|
+
"import { Kysely } from \"kysely\";\nimport { PostgresJSDialect } from \"kysely-postgres-js\";\nimport postgres from \"postgres\";\nimport type { Database } from \"./types.ts\";\n\nlet db: Kysely<Database> | null = null;\nlet rawClient: ReturnType<typeof postgres> | null = null;\n\nexport function getDb(connectionString?: string): Kysely<Database> {\n\tif (!db) {\n\t\tconst url =\n\t\t\tconnectionString ||\n\t\t\tprocess.env.DATABASE_URL ||\n\t\t\t\"postgres://postgres:postgres@localhost:5432/streams_dev\";\n\n\t\t// Always use SSL for remote databases, just disable cert verification if needed\n\t\tconst isLocal =\n\t\t\turl.includes(\"localhost\") ||\n\t\t\turl.includes(\"127.0.0.1\") ||\n\t\t\turl.includes(\"@postgres:\");\n\t\tconst poolMax = Number.parseInt(process.env.DATABASE_POOL_MAX ?? \"20\", 10);\n\t\trawClient = postgres(url, {\n\t\t\tmax: poolMax,\n\t\t\tssl: isLocal\n\t\t\t\t? undefined\n\t\t\t\t: {\n\t\t\t\t\t\trejectUnauthorized:\n\t\t\t\t\t\t\tprocess.env.NODE_TLS_REJECT_UNAUTHORIZED !== \"0\",\n\t\t\t\t\t},\n\t\t});\n\t\tdb = new Kysely<Database>({\n\t\t\tdialect: new PostgresJSDialect({ postgres: rawClient }),\n\t\t});\n\t}\n\treturn db;\n}\n\n/** Raw postgres.js client for dynamic schema DDL (CREATE SCHEMA, DROP, etc.) */\nexport function getRawClient(): ReturnType<typeof postgres> {\n\tif (!rawClient) getDb();\n\treturn rawClient!;\n}\n\n/** Close the DB connection pool. Call in CLI commands to allow process exit. */\nexport async function closeDb(): Promise<void> {\n\tif (db) {\n\t\tawait db.destroy();\n\t\tdb = null;\n\t}\n\tif (rawClient) {\n\t\tawait rawClient.end();\n\t\trawClient = null;\n\t}\n}\n\nimport { sql } from \"kysely\";\nexport { sql };\nexport * from \"./types.ts\";\nexport { jsonb, parseJsonb } from \"./jsonb.ts\";\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,EAC1D,MAAM,UAAU,KAAK,UAAU,KAAK,EAAE,QAAQ,MAAM,IAAI;AAAA,EACxD,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;;;ACzBnB;AACA;AACA;AAqDA,gBAAS;AAlDT,IAAI,KAA8B;AAClC,IAAI,YAAgD;AAE7C,SAAS,KAAK,CAAC,kBAA6C;AAAA,EAClE,IAAI,CAAC,IAAI;AAAA,IACR,MAAM,MACL,oBACA,QAAQ,IAAI,gBACZ;AAAA,IAGD,MAAM,UACL,IAAI,SAAS,WAAW,KACxB,IAAI,SAAS,WAAW,KACxB,IAAI,SAAS,YAAY;AAAA,IAC1B,MAAM,UAAU,OAAO,SAAS,QAAQ,IAAI,qBAAqB,MAAM,EAAE;AAAA,IACzE,YAAY,SAAS,KAAK;AAAA,MACzB,KAAK;AAAA,MACL,KAAK,UACF,YACA;AAAA,QACA,oBACC,QAAQ,IAAI,iCAAiC;AAAA,MAC/C;AAAA,IACH,CAAC;AAAA,IACD,KAAK,IAAI,OAAiB;AAAA,MACzB,SAAS,IAAI,kBAAkB,EAAE,UAAU,UAAU,CAAC;AAAA,IACvD,CAAC;AAAA,EACF;AAAA,EACA,OAAO;AAAA;AAID,SAAS,YAAY,GAAgC;AAAA,EAC3D,IAAI,CAAC;AAAA,IAAW,MAAM;AAAA,EACtB,OAAO;AAAA;AAIR,eAAsB,OAAO,GAAkB;AAAA,EAC9C,IAAI,IAAI;AAAA,IACP,MAAM,GAAG,QAAQ;AAAA,IACjB,KAAK;AAAA,EACN;AAAA,EACA,IAAI,WAAW;AAAA,IACd,MAAM,UAAU,IAAI;AAAA,IACpB,YAAY;AAAA,EACb;AAAA;",
|
|
9
|
+
"debugId": "AB57A20678E6DFB964756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
package/dist/src/db/jsonb.js.map
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/jsonb.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {
|
|
5
|
+
"import { type RawBuilder, sql } 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\tconst escaped = JSON.stringify(value).replace(/'/g, \"''\");\n\treturn 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\tif (typeof value === \"string\") {\n\t\ttry {\n\t\t\treturn JSON.parse(value) as T;\n\t\t} catch {\n\t\t\treturn value as T;\n\t\t}\n\t}\n\treturn (value ?? {}) as T;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAOO,SAAS,KAAK,CAAC,OAAqC;AAAA,EAC1D,MAAM,UAAU,KAAK,UAAU,KAAK,EAAE,QAAQ,MAAM,IAAI;AAAA,EACxD,OAAO,MAAM,IAAI,IAAI,IAAI,iBAAiB;AAAA;AAQpC,SAAS,UAAuB,CAAC,OAAmB;AAAA,EAC1D,IAAI,OAAO,UAAU,UAAU;AAAA,IAC9B,IAAI;AAAA,MACH,OAAO,KAAK,MAAM,KAAK;AAAA,MACtB,MAAM;AAAA,MACP,OAAO;AAAA;AAAA,EAET;AAAA,EACA,OAAQ,SAAS,CAAC;AAAA;",
|
|
8
8
|
"debugId": "A6577D9BF54F45FE64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -38,7 +38,7 @@ interface StreamsTable {
|
|
|
38
38
|
options: Generated<unknown>;
|
|
39
39
|
endpoint_url: string;
|
|
40
40
|
signing_secret: string | null;
|
|
41
|
-
api_key_id: string
|
|
41
|
+
api_key_id: string;
|
|
42
42
|
created_at: Generated<Date>;
|
|
43
43
|
updated_at: Generated<Date>;
|
|
44
44
|
}
|
|
@@ -92,15 +92,26 @@ interface SubgraphsTable {
|
|
|
92
92
|
schema_hash: string;
|
|
93
93
|
handler_path: string;
|
|
94
94
|
schema_name: string | null;
|
|
95
|
+
start_block: Generated<number>;
|
|
95
96
|
last_processed_block: Generated<number>;
|
|
96
97
|
last_error: string | null;
|
|
97
98
|
last_error_at: Date | null;
|
|
98
99
|
total_processed: Generated<number>;
|
|
99
100
|
total_errors: Generated<number>;
|
|
100
|
-
api_key_id: string
|
|
101
|
+
api_key_id: string;
|
|
101
102
|
created_at: Generated<Date>;
|
|
102
103
|
updated_at: Generated<Date>;
|
|
103
104
|
}
|
|
105
|
+
interface SubgraphGapsTable {
|
|
106
|
+
id: Generated<string>;
|
|
107
|
+
subgraph_id: string;
|
|
108
|
+
subgraph_name: string;
|
|
109
|
+
gap_start: number;
|
|
110
|
+
gap_end: number;
|
|
111
|
+
reason: string;
|
|
112
|
+
detected_at: Generated<Date>;
|
|
113
|
+
resolved_at: Date | null;
|
|
114
|
+
}
|
|
104
115
|
interface ApiKeysTable {
|
|
105
116
|
id: Generated<string>;
|
|
106
117
|
key_hash: string;
|
|
@@ -137,6 +148,7 @@ interface MagicLinksTable {
|
|
|
137
148
|
token: string;
|
|
138
149
|
expires_at: Date;
|
|
139
150
|
used_at: Date | null;
|
|
151
|
+
failed_attempts: Generated<number>;
|
|
140
152
|
created_at: Generated<Date>;
|
|
141
153
|
}
|
|
142
154
|
interface UsageDailyTable {
|
|
@@ -238,6 +250,7 @@ interface Database {
|
|
|
238
250
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
239
251
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
240
252
|
subgraph_table_snapshots: SubgraphTableSnapshotsTable;
|
|
253
|
+
subgraph_gaps: SubgraphGapsTable;
|
|
241
254
|
}
|
|
242
255
|
type Account = Selectable<AccountsTable>;
|
|
243
256
|
declare function upsertAccount(db: Kysely<Database>, email: string): Promise<Account>;
|
|
@@ -246,7 +259,7 @@ declare function isEmailAllowed(db: Kysely<Database>, email: string): Promise<bo
|
|
|
246
259
|
declare function createMagicLink(db: Kysely<Database>, email: string, token: string, expiresInMs?: number): Promise<void>;
|
|
247
260
|
/**
|
|
248
261
|
* Verify a magic link token. Returns the email if valid, null otherwise.
|
|
249
|
-
* Marks the token as used atomically.
|
|
262
|
+
* Marks the token as used atomically. Rejects after 5 failed attempts.
|
|
250
263
|
*/
|
|
251
264
|
declare function verifyMagicLink(db: Kysely<Database>, token: string): Promise<string | null>;
|
|
252
265
|
export { verifyMagicLink, upsertAccount, isEmailAllowed, getAccountById, createMagicLink };
|
|
@@ -39,8 +39,11 @@ async function createMagicLink(db, email, token, expiresInMs = 15 * 60 * 1000) {
|
|
|
39
39
|
}).execute();
|
|
40
40
|
}
|
|
41
41
|
async function verifyMagicLink(db, token) {
|
|
42
|
-
const result = await db.updateTable("magic_links").set({ used_at: new Date }).where("token", "=", token).where("used_at", "is", null).where("expires_at", ">", new Date).returning("email").executeTakeFirst();
|
|
43
|
-
|
|
42
|
+
const result = await db.updateTable("magic_links").set({ used_at: new Date }).where("token", "=", token).where("used_at", "is", null).where("expires_at", ">", new Date).where("failed_attempts", "<", 5).returning("email").executeTakeFirst();
|
|
43
|
+
if (result?.email)
|
|
44
|
+
return result.email;
|
|
45
|
+
await db.updateTable("magic_links").set({ failed_attempts: sql`failed_attempts + 1` }).where("token", "=", token).where("used_at", "is", null).where("expires_at", ">", new Date).execute();
|
|
46
|
+
return null;
|
|
44
47
|
}
|
|
45
48
|
export {
|
|
46
49
|
verifyMagicLink,
|
|
@@ -50,5 +53,5 @@ export {
|
|
|
50
53
|
createMagicLink
|
|
51
54
|
};
|
|
52
55
|
|
|
53
|
-
//# debugId=
|
|
56
|
+
//# debugId=20A55F621EB14D5064756E2164756E21
|
|
54
57
|
//# sourceMappingURL=accounts.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/queries/accounts.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Account, Database } from \"../types.ts\";\n\nexport async function upsertAccount(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<Account> {\n\treturn await db\n\t\t.insertInto(\"accounts\")\n\t\t.values({ email })\n\t\t.onConflict(\n\t\t\t(oc) => oc.column(\"email\").doUpdateSet({ email }), // no-op update to return existing\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getAccountById(\n\tdb: Kysely<Database>,\n\tid: string,\n): Promise<Account | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"accounts\")\n\t\t\t.selectAll()\n\t\t\t.where(\"id\", \"=\", id)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function isEmailAllowed(\n\tdb: Kysely<Database>,\n\temail: string,\n): Promise<boolean> {\n\tconst result = await sql<{ found: number }>`\n SELECT 1 AS found FROM accounts WHERE email = ${email}\n UNION ALL\n SELECT 1 AS found FROM waitlist WHERE email = ${email} AND status = 'approved'\n LIMIT 1\n `.execute(db);\n\n\treturn result.rows.length > 0;\n}\n\nexport async function createMagicLink(\n\tdb: Kysely<Database>,\n\temail: string,\n\ttoken: string,\n\texpiresInMs: number = 15 * 60 * 1000,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"magic_links\")\n\t\t.values({\n\t\t\temail,\n\t\t\ttoken,\n\t\t\texpires_at: new Date(Date.now() + expiresInMs),\n\t\t})\n\t\t.execute();\n}\n\n/**\n * Verify a magic link token. Returns the email if valid, null otherwise.\n * Marks the token as used atomically. Rejects after 5 failed attempts.\n */\nexport async function verifyMagicLink(\n\tdb: Kysely<Database>,\n\ttoken: string,\n): Promise<string | null> {\n\tconst result = await db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ used_at: new Date() })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.where(\"failed_attempts\", \"<\", 5)\n\t\t.returning(\"email\")\n\t\t.executeTakeFirst();\n\n\tif (result?.email) return result.email;\n\n\t// Increment failed attempts if token exists but didn't verify\n\tawait db\n\t\t.updateTable(\"magic_links\")\n\t\t.set({ failed_attempts: sql`failed_attempts + 1` })\n\t\t.where(\"token\", \"=\", token)\n\t\t.where(\"used_at\", \"is\", null)\n\t\t.where(\"expires_at\", \">\", new Date())\n\t\t.execute();\n\n\treturn null;\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAGA,eAAsB,aAAa,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAGA,eAAsB,aAAa,CAClC,IACA,OACmB;AAAA,EACnB,OAAO,MAAM,GACX,WAAW,UAAU,EACrB,OAAO,EAAE,MAAM,CAAC,EAChB,WACA,CAAC,OAAO,GAAG,OAAO,OAAO,EAAE,YAAY,EAAE,MAAM,CAAC,CACjD,EACC,aAAa,EACb,wBAAwB;AAAA;AAG3B,eAAsB,cAAc,CACnC,IACA,IAC0B;AAAA,EAC1B,OACE,MAAM,GACL,WAAW,UAAU,EACrB,UAAU,EACV,MAAM,MAAM,KAAK,EAAE,EACnB,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,cAAc,CACnC,IACA,OACmB;AAAA,EACnB,MAAM,SAAS,MAAM;AAAA,oDAC8B;AAAA;AAAA,oDAEA;AAAA;AAAA,IAEhD,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,SAAS;AAAA;AAG7B,eAAsB,eAAe,CACpC,IACA,OACA,OACA,cAAsB,KAAK,KAAK,MAChB;AAAA,EAChB,MAAM,GACJ,WAAW,aAAa,EACxB,OAAO;AAAA,IACP;AAAA,IACA;AAAA,IACA,YAAY,IAAI,KAAK,KAAK,IAAI,IAAI,WAAW;AAAA,EAC9C,CAAC,EACA,QAAQ;AAAA;AAOX,eAAsB,eAAe,CACpC,IACA,OACyB;AAAA,EACzB,MAAM,SAAS,MAAM,GACnB,YAAY,aAAa,EACzB,IAAI,EAAE,SAAS,IAAI,KAAO,CAAC,EAC3B,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,MAAM,mBAAmB,KAAK,CAAC,EAC/B,UAAU,OAAO,EACjB,iBAAiB;AAAA,EAEnB,IAAI,QAAQ;AAAA,IAAO,OAAO,OAAO;AAAA,EAGjC,MAAM,GACJ,YAAY,aAAa,EACzB,IAAI,EAAE,iBAAiB,yBAAyB,CAAC,EACjD,MAAM,SAAS,KAAK,KAAK,EACzB,MAAM,WAAW,MAAM,IAAI,EAC3B,MAAM,cAAc,KAAK,IAAI,IAAM,EACnC,QAAQ;AAAA,EAEV,OAAO;AAAA;",
|
|
8
|
+
"debugId": "20A55F621EB14D5064756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -38,7 +38,7 @@ interface StreamsTable {
|
|
|
38
38
|
options: Generated<unknown>;
|
|
39
39
|
endpoint_url: string;
|
|
40
40
|
signing_secret: string | null;
|
|
41
|
-
api_key_id: string
|
|
41
|
+
api_key_id: string;
|
|
42
42
|
created_at: Generated<Date>;
|
|
43
43
|
updated_at: Generated<Date>;
|
|
44
44
|
}
|
|
@@ -92,15 +92,26 @@ interface SubgraphsTable {
|
|
|
92
92
|
schema_hash: string;
|
|
93
93
|
handler_path: string;
|
|
94
94
|
schema_name: string | null;
|
|
95
|
+
start_block: Generated<number>;
|
|
95
96
|
last_processed_block: Generated<number>;
|
|
96
97
|
last_error: string | null;
|
|
97
98
|
last_error_at: Date | null;
|
|
98
99
|
total_processed: Generated<number>;
|
|
99
100
|
total_errors: Generated<number>;
|
|
100
|
-
api_key_id: string
|
|
101
|
+
api_key_id: string;
|
|
101
102
|
created_at: Generated<Date>;
|
|
102
103
|
updated_at: Generated<Date>;
|
|
103
104
|
}
|
|
105
|
+
interface SubgraphGapsTable {
|
|
106
|
+
id: Generated<string>;
|
|
107
|
+
subgraph_id: string;
|
|
108
|
+
subgraph_name: string;
|
|
109
|
+
gap_start: number;
|
|
110
|
+
gap_end: number;
|
|
111
|
+
reason: string;
|
|
112
|
+
detected_at: Generated<Date>;
|
|
113
|
+
resolved_at: Date | null;
|
|
114
|
+
}
|
|
104
115
|
interface ApiKeysTable {
|
|
105
116
|
id: Generated<string>;
|
|
106
117
|
key_hash: string;
|
|
@@ -137,6 +148,7 @@ interface MagicLinksTable {
|
|
|
137
148
|
token: string;
|
|
138
149
|
expires_at: Date;
|
|
139
150
|
used_at: Date | null;
|
|
151
|
+
failed_attempts: Generated<number>;
|
|
140
152
|
created_at: Generated<Date>;
|
|
141
153
|
}
|
|
142
154
|
interface UsageDailyTable {
|
|
@@ -238,6 +250,7 @@ interface Database {
|
|
|
238
250
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
239
251
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
240
252
|
subgraph_table_snapshots: SubgraphTableSnapshotsTable;
|
|
253
|
+
subgraph_gaps: SubgraphGapsTable;
|
|
241
254
|
}
|
|
242
255
|
interface Gap {
|
|
243
256
|
gapStart: number;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/queries/integrity.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../types.ts\";\n\nexport interface Gap {\n\tgapStart: number;\n\tgapEnd: number;\n\tsize: number;\n}\n\nexport async function findGaps(\n\tdb: Kysely<Database>,\n\tlimit?: number,\n): Promise<Gap[]> {\n\tconst limitClause = limit ? sql`LIMIT ${limit}` : sql``;\n\tconst { rows } = await sql<{\n\t\tgap_start: string;\n\t\tgap_end: string;\n\t\tsize: string;\n\t}>`\n SELECT gap_start, gap_end, gap_end - gap_start + 1 AS size\n FROM (\n SELECT height + 1 AS gap_start, next_height - 1 AS gap_end\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n ) gaps\n ORDER BY gap_start\n ${limitClause}\n `.execute(db);\n\n\treturn rows.map((r) => ({\n\t\tgapStart: Number(r.gap_start),\n\t\tgapEnd: Number(r.gap_end),\n\t\tsize: Number(r.size),\n\t}));\n}\n\nexport async function countMissingBlocks(\n\tdb: Kysely<Database>,\n): Promise<number> {\n\tconst { rows } = await sql<{ total: string }>`\n SELECT COALESCE(SUM(next_height - height - 1), 0) AS total\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n `.execute(db);\n\n\treturn Number(rows[0]?.total ?? 0);\n}\n\nexport async function computeContiguousTip(\n\tdb: Kysely<Database>,\n\tfromHeight: number,\n): Promise<number> {\n\tconst { rows } = await sql<{ tip: string }>`\n SELECT COALESCE(MAX(height), ${fromHeight}) AS tip\n FROM (\n SELECT height, height - ROW_NUMBER() OVER (ORDER BY height) AS grp\n FROM blocks WHERE canonical = true AND height >= ${fromHeight}\n ) sub\n WHERE grp = (\n SELECT height - ROW_NUMBER() OVER (ORDER BY height)\n FROM blocks WHERE canonical = true AND height = ${fromHeight}\n )\n `.execute(db);\n\n\treturn Number(rows[0]?.tip ?? fromHeight);\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AASA,eAAsB,QAAQ,
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AASA,eAAsB,QAAQ,CAC7B,IACA,OACiB;AAAA,EACjB,MAAM,cAAc,QAAQ,YAAY,UAAU;AAAA,EAClD,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAelB;AAAA,IACF,QAAQ,EAAE;AAAA,EAEb,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACvB,UAAU,OAAO,EAAE,SAAS;AAAA,IAC5B,QAAQ,OAAO,EAAE,OAAO;AAAA,IACxB,MAAM,OAAO,EAAE,IAAI;AAAA,EACpB,EAAE;AAAA;AAGH,eAAsB,kBAAkB,CACvC,IACkB;AAAA,EAClB,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOpB,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,IAAI,SAAS,CAAC;AAAA;AAGlC,eAAsB,oBAAoB,CACzC,IACA,YACkB;AAAA,EAClB,QAAQ,SAAS,MAAM;AAAA,mCACW;AAAA;AAAA;AAAA,yDAGsB;AAAA;AAAA;AAAA;AAAA,wDAID;AAAA;AAAA,IAEpD,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,IAAI,OAAO,UAAU;AAAA;",
|
|
8
8
|
"debugId": "C8E32B40B73BEACD64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -38,7 +38,7 @@ interface StreamsTable {
|
|
|
38
38
|
options: Generated<unknown>;
|
|
39
39
|
endpoint_url: string;
|
|
40
40
|
signing_secret: string | null;
|
|
41
|
-
api_key_id: string
|
|
41
|
+
api_key_id: string;
|
|
42
42
|
created_at: Generated<Date>;
|
|
43
43
|
updated_at: Generated<Date>;
|
|
44
44
|
}
|
|
@@ -92,15 +92,26 @@ interface SubgraphsTable {
|
|
|
92
92
|
schema_hash: string;
|
|
93
93
|
handler_path: string;
|
|
94
94
|
schema_name: string | null;
|
|
95
|
+
start_block: Generated<number>;
|
|
95
96
|
last_processed_block: Generated<number>;
|
|
96
97
|
last_error: string | null;
|
|
97
98
|
last_error_at: Date | null;
|
|
98
99
|
total_processed: Generated<number>;
|
|
99
100
|
total_errors: Generated<number>;
|
|
100
|
-
api_key_id: string
|
|
101
|
+
api_key_id: string;
|
|
101
102
|
created_at: Generated<Date>;
|
|
102
103
|
updated_at: Generated<Date>;
|
|
103
104
|
}
|
|
105
|
+
interface SubgraphGapsTable {
|
|
106
|
+
id: Generated<string>;
|
|
107
|
+
subgraph_id: string;
|
|
108
|
+
subgraph_name: string;
|
|
109
|
+
gap_start: number;
|
|
110
|
+
gap_end: number;
|
|
111
|
+
reason: string;
|
|
112
|
+
detected_at: Generated<Date>;
|
|
113
|
+
resolved_at: Date | null;
|
|
114
|
+
}
|
|
104
115
|
interface ApiKeysTable {
|
|
105
116
|
id: Generated<string>;
|
|
106
117
|
key_hash: string;
|
|
@@ -137,6 +148,7 @@ interface MagicLinksTable {
|
|
|
137
148
|
token: string;
|
|
138
149
|
expires_at: Date;
|
|
139
150
|
used_at: Date | null;
|
|
151
|
+
failed_attempts: Generated<number>;
|
|
140
152
|
created_at: Generated<Date>;
|
|
141
153
|
}
|
|
142
154
|
interface UsageDailyTable {
|
|
@@ -238,6 +250,7 @@ interface Database {
|
|
|
238
250
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
239
251
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
240
252
|
subgraph_table_snapshots: SubgraphTableSnapshotsTable;
|
|
253
|
+
subgraph_gaps: SubgraphGapsTable;
|
|
241
254
|
}
|
|
242
255
|
type StreamMetrics = Selectable<StreamMetricsTable>;
|
|
243
256
|
declare function getStreamMetrics(db: Kysely<Database>, streamId: string): Promise<StreamMetrics | null>;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/queries/metrics.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import {
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Database, StreamMetrics } from \"../types.ts\";\n\nexport async function getStreamMetrics(\n\tdb: Kysely<Database>,\n\tstreamId: string,\n): Promise<StreamMetrics | null> {\n\treturn (\n\t\t(await db\n\t\t\t.selectFrom(\"stream_metrics\")\n\t\t\t.selectAll()\n\t\t\t.where(\"stream_id\", \"=\", streamId)\n\t\t\t.executeTakeFirst()) ?? null\n\t);\n}\n\nexport async function updateStreamMetrics(\n\tdb: Kysely<Database>,\n\tstreamId: string,\n\tupdates: Partial<{\n\t\tlastTriggeredAt: Date;\n\t\tlastTriggeredBlock: number;\n\t\ttotalDeliveries: number;\n\t\tfailedDeliveries: number;\n\t\terrorMessage: string | null;\n\t}>,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"stream_metrics\")\n\t\t.values({\n\t\t\tstream_id: streamId,\n\t\t\tlast_triggered_at: updates.lastTriggeredAt ?? null,\n\t\t\tlast_triggered_block: updates.lastTriggeredBlock ?? null,\n\t\t\ttotal_deliveries: updates.totalDeliveries ?? 0,\n\t\t\tfailed_deliveries: updates.failedDeliveries ?? 0,\n\t\t\terror_message: updates.errorMessage ?? null,\n\t\t})\n\t\t.onConflict((oc) =>\n\t\t\toc.column(\"stream_id\").doUpdateSet({\n\t\t\t\t...(updates.lastTriggeredAt !== undefined\n\t\t\t\t\t? { last_triggered_at: updates.lastTriggeredAt }\n\t\t\t\t\t: {}),\n\t\t\t\t...(updates.lastTriggeredBlock !== undefined\n\t\t\t\t\t? { last_triggered_block: updates.lastTriggeredBlock }\n\t\t\t\t\t: {}),\n\t\t\t\t...(updates.totalDeliveries !== undefined\n\t\t\t\t\t? { total_deliveries: updates.totalDeliveries }\n\t\t\t\t\t: {}),\n\t\t\t\t...(updates.failedDeliveries !== undefined\n\t\t\t\t\t? { failed_deliveries: updates.failedDeliveries }\n\t\t\t\t\t: {}),\n\t\t\t\t...(updates.errorMessage !== undefined\n\t\t\t\t\t? { error_message: updates.errorMessage }\n\t\t\t\t\t: {}),\n\t\t\t}),\n\t\t)\n\t\t.execute();\n}\n\nexport async function incrementDeliveryCount(\n\tdb: Kysely<Database>,\n\tstreamId: string,\n\tfailed: boolean,\n): Promise<void> {\n\tawait db\n\t\t.insertInto(\"stream_metrics\")\n\t\t.values({\n\t\t\tstream_id: streamId,\n\t\t\ttotal_deliveries: 1,\n\t\t\tfailed_deliveries: failed ? 1 : 0,\n\t\t})\n\t\t.onConflict((oc) =>\n\t\t\toc.column(\"stream_id\").doUpdateSet({\n\t\t\t\ttotal_deliveries: sql`stream_metrics.total_deliveries + 1`,\n\t\t\t\t...(failed\n\t\t\t\t\t? { failed_deliveries: sql`stream_metrics.failed_deliveries + 1` }\n\t\t\t\t\t: {}),\n\t\t\t}),\n\t\t)\n\t\t.execute();\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAGA,eAAsB,gBAAgB,
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAGA,eAAsB,gBAAgB,CACrC,IACA,UACgC;AAAA,EAChC,OACE,MAAM,GACL,WAAW,gBAAgB,EAC3B,UAAU,EACV,MAAM,aAAa,KAAK,QAAQ,EAChC,iBAAiB,KAAM;AAAA;AAI3B,eAAsB,mBAAmB,CACxC,IACA,UACA,SAOgB;AAAA,EAChB,MAAM,GACJ,WAAW,gBAAgB,EAC3B,OAAO;AAAA,IACP,WAAW;AAAA,IACX,mBAAmB,QAAQ,mBAAmB;AAAA,IAC9C,sBAAsB,QAAQ,sBAAsB;AAAA,IACpD,kBAAkB,QAAQ,mBAAmB;AAAA,IAC7C,mBAAmB,QAAQ,oBAAoB;AAAA,IAC/C,eAAe,QAAQ,gBAAgB;AAAA,EACxC,CAAC,EACA,WAAW,CAAC,OACZ,GAAG,OAAO,WAAW,EAAE,YAAY;AAAA,OAC9B,QAAQ,oBAAoB,YAC7B,EAAE,mBAAmB,QAAQ,gBAAgB,IAC7C,CAAC;AAAA,OACA,QAAQ,uBAAuB,YAChC,EAAE,sBAAsB,QAAQ,mBAAmB,IACnD,CAAC;AAAA,OACA,QAAQ,oBAAoB,YAC7B,EAAE,kBAAkB,QAAQ,gBAAgB,IAC5C,CAAC;AAAA,OACA,QAAQ,qBAAqB,YAC9B,EAAE,mBAAmB,QAAQ,iBAAiB,IAC9C,CAAC;AAAA,OACA,QAAQ,iBAAiB,YAC1B,EAAE,eAAe,QAAQ,aAAa,IACtC,CAAC;AAAA,EACL,CAAC,CACF,EACC,QAAQ;AAAA;AAGX,eAAsB,sBAAsB,CAC3C,IACA,UACA,QACgB;AAAA,EAChB,MAAM,GACJ,WAAW,gBAAgB,EAC3B,OAAO;AAAA,IACP,WAAW;AAAA,IACX,kBAAkB;AAAA,IAClB,mBAAmB,SAAS,IAAI;AAAA,EACjC,CAAC,EACA,WAAW,CAAC,OACZ,GAAG,OAAO,WAAW,EAAE,YAAY;AAAA,IAClC,kBAAkB;AAAA,OACd,SACD,EAAE,mBAAmB,0CAA0C,IAC/D,CAAC;AAAA,EACL,CAAC,CACF,EACC,QAAQ;AAAA;",
|
|
8
8
|
"debugId": "B4106C2E1F1C5A3F64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|