@secondlayer/shared 6.4.0 → 6.4.2
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/db/index.d.ts +3 -1
- package/dist/src/db/queries/account-spend-caps.d.ts +3 -1
- package/dist/src/db/queries/account-usage.d.ts +3 -1
- package/dist/src/db/queries/accounts.d.ts +3 -1
- package/dist/src/db/queries/chain-reorgs.d.ts +3 -1
- package/dist/src/db/queries/integrity.d.ts +3 -1
- package/dist/src/db/queries/projects.d.ts +3 -1
- package/dist/src/db/queries/provisioning-audit.d.ts +3 -1
- package/dist/src/db/queries/subgraph-gaps.d.ts +3 -1
- package/dist/src/db/queries/subgraph-operations.d.ts +3 -1
- package/dist/src/db/queries/subgraphs.d.ts +7 -2
- package/dist/src/db/queries/subgraphs.js +8 -3
- package/dist/src/db/queries/subgraphs.js.map +3 -3
- package/dist/src/db/queries/subscriptions.d.ts +3 -1
- package/dist/src/db/queries/tenant-compute-addons.d.ts +3 -1
- package/dist/src/db/queries/usage.d.ts +3 -1
- package/dist/src/db/schema.d.ts +3 -1
- package/dist/src/index.d.ts +4 -2
- package/dist/src/index.js.map +1 -1
- package/dist/src/node/local-client.d.ts +3 -1
- package/dist/src/schemas/index.d.ts +1 -1
- package/dist/src/schemas/index.js.map +1 -1
- package/dist/src/schemas/subgraphs.d.ts +1 -1
- package/dist/src/schemas/subgraphs.js.map +1 -1
- package/migrations/0077_subscription_deliveries_outbox_set_null.ts +50 -0
- package/package.json +1 -1
package/dist/src/db/index.d.ts
CHANGED
|
@@ -789,7 +789,9 @@ type InsertSubscriptionOutbox = Insertable<SubscriptionOutboxTable>;
|
|
|
789
789
|
type UpdateSubscriptionOutbox = Updateable<SubscriptionOutboxTable>;
|
|
790
790
|
interface SubscriptionDeliveriesTable {
|
|
791
791
|
id: Generated<string>;
|
|
792
|
-
|
|
792
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
793
|
+
* delivery telemetry is retained. */
|
|
794
|
+
outbox_id: string | null;
|
|
793
795
|
subscription_id: string;
|
|
794
796
|
attempt: number;
|
|
795
797
|
status_code: number | null;
|
|
@@ -702,7 +702,9 @@ interface SubscriptionOutboxTable {
|
|
|
702
702
|
}
|
|
703
703
|
interface SubscriptionDeliveriesTable {
|
|
704
704
|
id: Generated<string>;
|
|
705
|
-
|
|
705
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
706
|
+
* delivery telemetry is retained. */
|
|
707
|
+
outbox_id: string | null;
|
|
706
708
|
subscription_id: string;
|
|
707
709
|
attempt: number;
|
|
708
710
|
status_code: number | null;
|
|
@@ -700,7 +700,9 @@ interface SubscriptionOutboxTable {
|
|
|
700
700
|
}
|
|
701
701
|
interface SubscriptionDeliveriesTable {
|
|
702
702
|
id: Generated<string>;
|
|
703
|
-
|
|
703
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
704
|
+
* delivery telemetry is retained. */
|
|
705
|
+
outbox_id: string | null;
|
|
704
706
|
subscription_id: string;
|
|
705
707
|
attempt: number;
|
|
706
708
|
status_code: number | null;
|
|
@@ -702,7 +702,9 @@ interface SubscriptionOutboxTable {
|
|
|
702
702
|
}
|
|
703
703
|
interface SubscriptionDeliveriesTable {
|
|
704
704
|
id: Generated<string>;
|
|
705
|
-
|
|
705
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
706
|
+
* delivery telemetry is retained. */
|
|
707
|
+
outbox_id: string | null;
|
|
706
708
|
subscription_id: string;
|
|
707
709
|
attempt: number;
|
|
708
710
|
status_code: number | null;
|
|
@@ -699,7 +699,9 @@ interface SubscriptionOutboxTable {
|
|
|
699
699
|
}
|
|
700
700
|
interface SubscriptionDeliveriesTable {
|
|
701
701
|
id: Generated<string>;
|
|
702
|
-
|
|
702
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
703
|
+
* delivery telemetry is retained. */
|
|
704
|
+
outbox_id: string | null;
|
|
703
705
|
subscription_id: string;
|
|
704
706
|
attempt: number;
|
|
705
707
|
status_code: number | null;
|
|
@@ -700,7 +700,9 @@ interface SubscriptionOutboxTable {
|
|
|
700
700
|
}
|
|
701
701
|
interface SubscriptionDeliveriesTable {
|
|
702
702
|
id: Generated<string>;
|
|
703
|
-
|
|
703
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
704
|
+
* delivery telemetry is retained. */
|
|
705
|
+
outbox_id: string | null;
|
|
704
706
|
subscription_id: string;
|
|
705
707
|
attempt: number;
|
|
706
708
|
status_code: number | null;
|
|
@@ -702,7 +702,9 @@ interface SubscriptionOutboxTable {
|
|
|
702
702
|
}
|
|
703
703
|
interface SubscriptionDeliveriesTable {
|
|
704
704
|
id: Generated<string>;
|
|
705
|
-
|
|
705
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
706
|
+
* delivery telemetry is retained. */
|
|
707
|
+
outbox_id: string | null;
|
|
706
708
|
subscription_id: string;
|
|
707
709
|
attempt: number;
|
|
708
710
|
status_code: number | null;
|
|
@@ -701,7 +701,9 @@ interface SubscriptionOutboxTable {
|
|
|
701
701
|
}
|
|
702
702
|
interface SubscriptionDeliveriesTable {
|
|
703
703
|
id: Generated<string>;
|
|
704
|
-
|
|
704
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
705
|
+
* delivery telemetry is retained. */
|
|
706
|
+
outbox_id: string | null;
|
|
705
707
|
subscription_id: string;
|
|
706
708
|
attempt: number;
|
|
707
709
|
status_code: number | null;
|
|
@@ -700,7 +700,9 @@ interface SubscriptionOutboxTable {
|
|
|
700
700
|
}
|
|
701
701
|
interface SubscriptionDeliveriesTable {
|
|
702
702
|
id: Generated<string>;
|
|
703
|
-
|
|
703
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
704
|
+
* delivery telemetry is retained. */
|
|
705
|
+
outbox_id: string | null;
|
|
704
706
|
subscription_id: string;
|
|
705
707
|
attempt: number;
|
|
706
708
|
status_code: number | null;
|
|
@@ -701,7 +701,9 @@ interface SubscriptionOutboxTable {
|
|
|
701
701
|
}
|
|
702
702
|
interface SubscriptionDeliveriesTable {
|
|
703
703
|
id: Generated<string>;
|
|
704
|
-
|
|
704
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
705
|
+
* delivery telemetry is retained. */
|
|
706
|
+
outbox_id: string | null;
|
|
705
707
|
subscription_id: string;
|
|
706
708
|
attempt: number;
|
|
707
709
|
status_code: number | null;
|
|
@@ -701,7 +701,9 @@ interface SubscriptionOutboxTable {
|
|
|
701
701
|
}
|
|
702
702
|
interface SubscriptionDeliveriesTable {
|
|
703
703
|
id: Generated<string>;
|
|
704
|
-
|
|
704
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
705
|
+
* delivery telemetry is retained. */
|
|
706
|
+
outbox_id: string | null;
|
|
705
707
|
subscription_id: string;
|
|
706
708
|
attempt: number;
|
|
707
709
|
status_code: number | null;
|
|
@@ -741,6 +743,9 @@ declare function getSubgraph(db: Kysely<Database>, name: string, accountId?: str
|
|
|
741
743
|
declare function listSubgraphs(db: Kysely<Database>, accountId?: string): Promise<Subgraph[]>;
|
|
742
744
|
declare function updateSubgraphStatus(db: Kysely<Database>, name: string, status: string, lastProcessedBlock?: number): Promise<void>;
|
|
743
745
|
declare function recordSubgraphProcessed(db: Kysely<Database>, name: string, processed: number, errors: number, lastError?: string): Promise<void>;
|
|
744
|
-
declare function updateSubgraphHandlerPath(db: Kysely<Database>, name: string, handlerPath: string
|
|
746
|
+
declare function updateSubgraphHandlerPath(db: Kysely<Database>, name: string, handlerPath: string, opts?: {
|
|
747
|
+
handlerCode?: string
|
|
748
|
+
sourceCode?: string
|
|
749
|
+
}): Promise<void>;
|
|
745
750
|
declare function deleteSubgraph(db: Kysely<Database>, name: string, accountId?: string): Promise<Subgraph | null>;
|
|
746
751
|
export { updateSubgraphStatus, updateSubgraphHandlerPath, registerSubgraph, recordSubgraphProcessed, pgSchemaNameFor, pgSchemaName, listSubgraphs, getSubgraph, deleteSubgraph };
|
|
@@ -97,8 +97,13 @@ async function recordSubgraphProcessed(db, name, processed, errors, lastError) {
|
|
|
97
97
|
updated_at: new Date
|
|
98
98
|
}).where("name", "=", name).execute();
|
|
99
99
|
}
|
|
100
|
-
async function updateSubgraphHandlerPath(db, name, handlerPath) {
|
|
101
|
-
await db.updateTable("subgraphs").set({
|
|
100
|
+
async function updateSubgraphHandlerPath(db, name, handlerPath, opts) {
|
|
101
|
+
await db.updateTable("subgraphs").set({
|
|
102
|
+
handler_path: handlerPath,
|
|
103
|
+
...opts?.handlerCode != null ? { handler_code: opts.handlerCode } : {},
|
|
104
|
+
...opts?.sourceCode != null ? { source_code: opts.sourceCode } : {},
|
|
105
|
+
updated_at: new Date
|
|
106
|
+
}).where("name", "=", name).execute();
|
|
102
107
|
}
|
|
103
108
|
async function deleteSubgraph(db, name, accountId) {
|
|
104
109
|
const subgraph = await getSubgraph(db, name, accountId);
|
|
@@ -127,5 +132,5 @@ export {
|
|
|
127
132
|
deleteSubgraph
|
|
128
133
|
};
|
|
129
134
|
|
|
130
|
-
//# debugId=
|
|
135
|
+
//# debugId=4B034A879E3C935864756E2164756E21
|
|
131
136
|
//# sourceMappingURL=subgraphs.js.map
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
"sources": ["../src/db/jsonb.ts", "../src/db/queries/subgraphs.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
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 *\n * Generic parameter lets callers set the RawBuilder's output type so they\n * don't need to cast at the insert site. Default is `unknown` — widen at\n * the call site when the column type is narrower, e.g. `jsonb<MyShape>(...)`.\n */\nexport function jsonb<T = unknown>(value: T): RawBuilder<T> {\n\tconst escaped = JSON.stringify(value, (_k, v) =>\n\t\ttypeof v === \"bigint\" ? v.toString() : v,\n\t).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 { type Kysely, sql } from \"kysely\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/**\n * Convert a subgraph name to its PostgreSQL schema name (legacy form).\n * Pre shared-rip every tenant DB had its own schema namespace so disambiguation\n * was implicit. Kept for oss mode (single-tenant) and legacy-row fallback.\n * Platform-mode deploys use `pgSchemaNameFor(accountId, name)`.\n */\nexport function pgSchemaName(subgraphName: string): string {\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${safeName}`;\n}\n\n/**\n * Account-scoped schema name. Matches migration 0028's rename pattern:\n * subgraph_{first8charsOfAccountId, dashes-as-underscores}_{name}\n * Empty accountId falls back to legacy form (oss mode).\n */\nexport function pgSchemaNameFor(\n\taccountId: string,\n\tsubgraphName: string,\n): string {\n\tif (!accountId) return pgSchemaName(subgraphName);\n\tconst accountPrefix = accountId.slice(0, 8).replace(/-/g, \"_\");\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${accountPrefix}_${safeName}`;\n}\n\nexport async function registerSubgraph(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tname: string;\n\t\tversion: string;\n\t\tdefinition: Record<string, unknown>;\n\t\tschemaHash: string;\n\t\thandlerPath: string;\n\t\tapiKeyId?: string;\n\t\taccountId?: string;\n\t\tschemaName?: string;\n\t\tstartBlock?: number;\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t},\n): Promise<Subgraph> {\n\treturn await db\n\t\t.insertInto(\"subgraphs\")\n\t\t.values({\n\t\t\tname: data.name,\n\t\t\tversion: data.version,\n\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\tschema_hash: data.schemaHash,\n\t\t\thandler_path: data.handlerPath,\n\t\t\taccount_id: data.accountId ?? \"\",\n\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\tschema_name: data.schemaName ?? null,\n\t\t\tstart_block: data.startBlock ?? 0,\n\t\t})\n\t\t.onConflict((oc) =>\n\t\t\toc.columns([\"name\", \"account_id\"]).doUpdateSet({\n\t\t\t\tversion: data.version,\n\t\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\t\tschema_hash: data.schemaHash,\n\t\t\t\thandler_path: data.handlerPath,\n\t\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\t\tschema_name: data.schemaName ?? null,\n\t\t\t\tstart_block: data.startBlock ?? 0,\n\t\t\t\tupdated_at: new Date(),\n\t\t\t}),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll().where(\"name\", \"=\", name);\n\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\n\treturn (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listSubgraphs(\n\tdb: Kysely<Database>,\n\taccountId?: string,\n): Promise<Subgraph[]> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll();\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\treturn query.execute();\n}\n\nexport async function updateSubgraphStatus(\n\tdb: Kysely<Database>,\n\tname: string,\n\tstatus: string,\n\tlastProcessedBlock?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\t...(lastProcessedBlock !== undefined\n\t\t\t\t? { last_processed_block: lastProcessedBlock }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function recordSubgraphProcessed(\n\tdb: Kysely<Database>,\n\tname: string,\n\tprocessed: number,\n\terrors: number,\n\tlastError?: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\ttotal_processed: sql`total_processed + ${processed}`,\n\t\t\ttotal_errors: sql`total_errors + ${errors}`,\n\t\t\t...(lastError\n\t\t\t\t? { last_error: lastError, last_error_at: new Date() }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function updateSubgraphHandlerPath(\n\tdb: Kysely<Database>,\n\tname: string,\n\thandlerPath: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({
|
|
6
|
+
"import { type Kysely, sql } from \"kysely\";\nimport { jsonb } from \"../jsonb.ts\";\nimport type { Database, Subgraph } from \"../types.ts\";\n\n/**\n * Convert a subgraph name to its PostgreSQL schema name (legacy form).\n * Pre shared-rip every tenant DB had its own schema namespace so disambiguation\n * was implicit. Kept for oss mode (single-tenant) and legacy-row fallback.\n * Platform-mode deploys use `pgSchemaNameFor(accountId, name)`.\n */\nexport function pgSchemaName(subgraphName: string): string {\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${safeName}`;\n}\n\n/**\n * Account-scoped schema name. Matches migration 0028's rename pattern:\n * subgraph_{first8charsOfAccountId, dashes-as-underscores}_{name}\n * Empty accountId falls back to legacy form (oss mode).\n */\nexport function pgSchemaNameFor(\n\taccountId: string,\n\tsubgraphName: string,\n): string {\n\tif (!accountId) return pgSchemaName(subgraphName);\n\tconst accountPrefix = accountId.slice(0, 8).replace(/-/g, \"_\");\n\tconst safeName = subgraphName.replace(/-/g, \"_\");\n\treturn `subgraph_${accountPrefix}_${safeName}`;\n}\n\nexport async function registerSubgraph(\n\tdb: Kysely<Database>,\n\tdata: {\n\t\tname: string;\n\t\tversion: string;\n\t\tdefinition: Record<string, unknown>;\n\t\tschemaHash: string;\n\t\thandlerPath: string;\n\t\tapiKeyId?: string;\n\t\taccountId?: string;\n\t\tschemaName?: string;\n\t\tstartBlock?: number;\n\t\thandlerCode?: string;\n\t\tsourceCode?: string;\n\t},\n): Promise<Subgraph> {\n\treturn await db\n\t\t.insertInto(\"subgraphs\")\n\t\t.values({\n\t\t\tname: data.name,\n\t\t\tversion: data.version,\n\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\tschema_hash: data.schemaHash,\n\t\t\thandler_path: data.handlerPath,\n\t\t\taccount_id: data.accountId ?? \"\",\n\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\tschema_name: data.schemaName ?? null,\n\t\t\tstart_block: data.startBlock ?? 0,\n\t\t})\n\t\t.onConflict((oc) =>\n\t\t\toc.columns([\"name\", \"account_id\"]).doUpdateSet({\n\t\t\t\tversion: data.version,\n\t\t\t\tdefinition: jsonb<Record<string, unknown>>(data.definition),\n\t\t\t\tschema_hash: data.schemaHash,\n\t\t\t\thandler_path: data.handlerPath,\n\t\t\t\thandler_code: data.handlerCode ?? null,\n\t\t\t\tsource_code: data.sourceCode ?? null,\n\t\t\t\tschema_name: data.schemaName ?? null,\n\t\t\t\tstart_block: data.startBlock ?? 0,\n\t\t\t\tupdated_at: new Date(),\n\t\t\t}),\n\t\t)\n\t\t.returningAll()\n\t\t.executeTakeFirstOrThrow();\n}\n\nexport async function getSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll().where(\"name\", \"=\", name);\n\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\n\treturn (await query.executeTakeFirst()) ?? null;\n}\n\nexport async function listSubgraphs(\n\tdb: Kysely<Database>,\n\taccountId?: string,\n): Promise<Subgraph[]> {\n\tlet query = db.selectFrom(\"subgraphs\").selectAll();\n\tif (accountId !== undefined) {\n\t\tquery = query.where(\"account_id\", \"=\", accountId);\n\t}\n\treturn query.execute();\n}\n\nexport async function updateSubgraphStatus(\n\tdb: Kysely<Database>,\n\tname: string,\n\tstatus: string,\n\tlastProcessedBlock?: number,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\tstatus,\n\t\t\t...(lastProcessedBlock !== undefined\n\t\t\t\t? { last_processed_block: lastProcessedBlock }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function recordSubgraphProcessed(\n\tdb: Kysely<Database>,\n\tname: string,\n\tprocessed: number,\n\terrors: number,\n\tlastError?: string,\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\ttotal_processed: sql`total_processed + ${processed}`,\n\t\t\ttotal_errors: sql`total_errors + ${errors}`,\n\t\t\t...(lastError\n\t\t\t\t? { last_error: lastError, last_error_at: new Date() }\n\t\t\t\t: {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function updateSubgraphHandlerPath(\n\tdb: Kysely<Database>,\n\tname: string,\n\thandlerPath: string,\n\topts?: { handlerCode?: string; sourceCode?: string },\n): Promise<void> {\n\tawait db\n\t\t.updateTable(\"subgraphs\")\n\t\t.set({\n\t\t\thandler_path: handlerPath,\n\t\t\t...(opts?.handlerCode != null ? { handler_code: opts.handlerCode } : {}),\n\t\t\t...(opts?.sourceCode != null ? { source_code: opts.sourceCode } : {}),\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"name\", \"=\", name)\n\t\t.execute();\n}\n\nexport async function deleteSubgraph(\n\tdb: Kysely<Database>,\n\tname: string,\n\taccountId?: string,\n): Promise<Subgraph | null> {\n\tconst subgraph = await getSubgraph(db, name, accountId);\n\tif (!subgraph) return null;\n\n\t// Use stored schema_name if available, otherwise compute\n\tconst schemaName = subgraph.schema_name ?? pgSchemaName(name);\n\n\t// Cascade to subscriptions: a subscription pointing at a deleted\n\t// subgraph + table will throw `relation does not exist` on every\n\t// subsequent emission. Pause active subs and purge any pending outbox\n\t// rows so receivers don't get phantom replays. We don't delete the\n\t// subscriptions themselves — operators may want to repoint them at a\n\t// resurrected subgraph; we just stop them firing.\n\tawait db\n\t\t.updateTable(\"subscriptions\")\n\t\t.set({\n\t\t\tstatus: \"paused\",\n\t\t\tlast_error: `Subgraph \"${name}\" deleted; subscription auto-paused.`,\n\t\t\tupdated_at: new Date(),\n\t\t})\n\t\t.where(\"subgraph_name\", \"=\", name)\n\t\t.execute();\n\tawait db\n\t\t.deleteFrom(\"subscription_outbox\")\n\t\t.where(\"status\", \"=\", \"pending\")\n\t\t.where(\"subscription_id\", \"in\", (qb) =>\n\t\t\tqb\n\t\t\t\t.selectFrom(\"subscriptions\")\n\t\t\t\t.select(\"id\")\n\t\t\t\t.where(\"subgraph_name\", \"=\", name),\n\t\t)\n\t\t.execute();\n\n\t// Drop the subgraph's schema (CASCADE drops all tables within)\n\tawait sql`DROP SCHEMA IF EXISTS ${sql.raw(`\"${schemaName}\"`)} CASCADE`.execute(\n\t\tdb,\n\t);\n\n\t// Remove from registry\n\tawait db.deleteFrom(\"subgraphs\").where(\"id\", \"=\", subgraph.id).execute();\n\n\treturn subgraph;\n}\n"
|
|
7
7
|
],
|
|
8
|
-
"mappings": "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
|
|
9
|
-
"debugId": "
|
|
8
|
+
"mappings": "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",
|
|
9
|
+
"debugId": "4B034A879E3C935864756E2164756E21",
|
|
10
10
|
"names": []
|
|
11
11
|
}
|
|
@@ -701,7 +701,9 @@ interface SubscriptionOutboxTable {
|
|
|
701
701
|
}
|
|
702
702
|
interface SubscriptionDeliveriesTable {
|
|
703
703
|
id: Generated<string>;
|
|
704
|
-
|
|
704
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
705
|
+
* delivery telemetry is retained. */
|
|
706
|
+
outbox_id: string | null;
|
|
705
707
|
subscription_id: string;
|
|
706
708
|
attempt: number;
|
|
707
709
|
status_code: number | null;
|
|
@@ -701,7 +701,9 @@ interface SubscriptionOutboxTable {
|
|
|
701
701
|
}
|
|
702
702
|
interface SubscriptionDeliveriesTable {
|
|
703
703
|
id: Generated<string>;
|
|
704
|
-
|
|
704
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
705
|
+
* delivery telemetry is retained. */
|
|
706
|
+
outbox_id: string | null;
|
|
705
707
|
subscription_id: string;
|
|
706
708
|
attempt: number;
|
|
707
709
|
status_code: number | null;
|
|
@@ -700,7 +700,9 @@ interface SubscriptionOutboxTable {
|
|
|
700
700
|
}
|
|
701
701
|
interface SubscriptionDeliveriesTable {
|
|
702
702
|
id: Generated<string>;
|
|
703
|
-
|
|
703
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
704
|
+
* delivery telemetry is retained. */
|
|
705
|
+
outbox_id: string | null;
|
|
704
706
|
subscription_id: string;
|
|
705
707
|
attempt: number;
|
|
706
708
|
status_code: number | null;
|
package/dist/src/db/schema.d.ts
CHANGED
|
@@ -770,7 +770,9 @@ type InsertSubscriptionOutbox = Insertable<SubscriptionOutboxTable>;
|
|
|
770
770
|
type UpdateSubscriptionOutbox = Updateable<SubscriptionOutboxTable>;
|
|
771
771
|
interface SubscriptionDeliveriesTable {
|
|
772
772
|
id: Generated<string>;
|
|
773
|
-
|
|
773
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
774
|
+
* delivery telemetry is retained. */
|
|
775
|
+
outbox_id: string | null;
|
|
774
776
|
subscription_id: string;
|
|
775
777
|
attempt: number;
|
|
776
778
|
status_code: number | null;
|
package/dist/src/index.d.ts
CHANGED
|
@@ -770,7 +770,9 @@ type InsertSubscriptionOutbox = Insertable<SubscriptionOutboxTable>;
|
|
|
770
770
|
type UpdateSubscriptionOutbox = Updateable<SubscriptionOutboxTable>;
|
|
771
771
|
interface SubscriptionDeliveriesTable {
|
|
772
772
|
id: Generated<string>;
|
|
773
|
-
|
|
773
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
774
|
+
* delivery telemetry is retained. */
|
|
775
|
+
outbox_id: string | null;
|
|
774
776
|
subscription_id: string;
|
|
775
777
|
attempt: number;
|
|
776
778
|
status_code: number | null;
|
|
@@ -1047,7 +1049,7 @@ interface DeploySubgraphRequest {
|
|
|
1047
1049
|
}
|
|
1048
1050
|
declare const DeploySubgraphRequestSchema: z4.ZodType<DeploySubgraphRequest>;
|
|
1049
1051
|
interface DeploySubgraphResponse {
|
|
1050
|
-
action: "created" | "unchanged" | "updated" | "reindexed";
|
|
1052
|
+
action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed";
|
|
1051
1053
|
subgraphId: string;
|
|
1052
1054
|
version: string;
|
|
1053
1055
|
message: string;
|
package/dist/src/index.js.map
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"export const ErrorCodes = {\n\tVALIDATION_ERROR: \"VALIDATION_ERROR\",\n\tDATABASE_ERROR: \"DATABASE_ERROR\",\n\tAUTHENTICATION_ERROR: \"AUTHENTICATION_ERROR\",\n\tAUTHORIZATION_ERROR: \"AUTHORIZATION_ERROR\",\n\tRATE_LIMIT_ERROR: \"RATE_LIMIT_ERROR\",\n\tFORBIDDEN: \"FORBIDDEN\",\n\tVERSION_CONFLICT: \"VERSION_CONFLICT\",\n\tNOT_FOUND: \"NOT_FOUND\",\n\t// Tenant lifecycle (CLI surfaces these verbatim)\n\tKEY_ROTATED: \"KEY_ROTATED\",\n\tTENANT_SUSPENDED: \"TENANT_SUSPENDED\",\n\tNO_TENANT_FOR_PROJECT: \"NO_TENANT_FOR_PROJECT\",\n\tINSTANCE_EXISTS: \"INSTANCE_EXISTS\",\n} as const;\n\nexport type ErrorCode = (typeof ErrorCodes)[keyof typeof ErrorCodes];\n\n/** Base error class for all Secondlayer errors. */\nexport class SecondLayerError extends Error {\n\tpublic code: ErrorCode;\n\tpublic override cause?: unknown;\n\n\tconstructor(code: ErrorCode, message: string, cause?: unknown) {\n\t\tsuper(message);\n\t\tthis.code = code;\n\t\tthis.cause = cause;\n\t\tthis.name = this.constructor.name;\n\t\tError.captureStackTrace?.(this, this.constructor);\n\t}\n\n\ttoJSON(): {\n\t\tname: string;\n\t\tcode: string;\n\t\tmessage: string;\n\t\tstack: string | undefined;\n\t\tcause: unknown;\n\t} {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tcode: this.code,\n\t\t\tmessage: this.message,\n\t\t\tstack: this.stack,\n\t\t\tcause: this.cause,\n\t\t};\n\t}\n}\n\nexport class NotFoundError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"NOT_FOUND\", message);\n\t}\n}\n\nexport class ValidationError extends SecondLayerError {\n\tconstructor(message: string, cause?: unknown) {\n\t\tsuper(\"VALIDATION_ERROR\", message, cause);\n\t}\n}\n\nexport class DatabaseError extends SecondLayerError {\n\tconstructor(message: string, cause?: unknown) {\n\t\tsuper(\"DATABASE_ERROR\", message, cause);\n\t}\n}\n\nexport class AuthenticationError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"AUTHENTICATION_ERROR\", message);\n\t}\n}\n\nexport class AuthorizationError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"AUTHORIZATION_ERROR\", message);\n\t}\n}\n\nexport class RateLimitError extends SecondLayerError {\n\tconstructor(message: string) {\n\t\tsuper(\"RATE_LIMIT_ERROR\", message);\n\t}\n}\n\nexport class ForbiddenError extends SecondLayerError {\n\tconstructor(message = \"Forbidden\") {\n\t\tsuper(\"FORBIDDEN\", message);\n\t}\n}\n\nexport class VersionConflictError extends SecondLayerError {\n\tpublic currentVersion: string;\n\tpublic expectedVersion: string;\n\n\tconstructor(currentVersion: string, expectedVersion: string) {\n\t\tsuper(\n\t\t\t\"VERSION_CONFLICT\",\n\t\t\t`Version conflict: expected ${expectedVersion}, current ${currentVersion}`,\n\t\t);\n\t\tthis.currentVersion = currentVersion;\n\t\tthis.expectedVersion = expectedVersion;\n\t}\n}\n\nexport class KeyRotatedError extends SecondLayerError {\n\tconstructor(message = \"Token has been rotated\") {\n\t\tsuper(\"KEY_ROTATED\", message);\n\t}\n}\n\nexport class TenantSuspendedError extends SecondLayerError {\n\tconstructor(message = \"Instance is suspended\") {\n\t\tsuper(\"TENANT_SUSPENDED\", message);\n\t}\n}\n\n/** Error code → HTTP status. Used by API middleware for code-based matching\n * (avoids cross-bundle instanceof failures from bunup class duplication). */\n// String literal map — codes don't have to be in the central ErrorCode\n// enum (route-local error classes can supply any code; we just map the\n// HTTP status here). This keeps cross-bundle instanceof failures out of\n// the equation.\nexport const CODE_TO_STATUS: Record<\n\tstring,\n\t400 | 401 | 403 | 404 | 409 | 423 | 429\n> = {\n\tAUTHENTICATION_ERROR: 401,\n\tAUTHORIZATION_ERROR: 403,\n\tRATE_LIMIT_ERROR: 429,\n\tFORBIDDEN: 403,\n\tNOT_FOUND: 404,\n\tVALIDATION_ERROR: 400,\n\tKEY_ROTATED: 401,\n\tTENANT_SUSPENDED: 423,\n\tNO_TENANT_FOR_PROJECT: 404,\n\tINSTANCE_EXISTS: 409,\n\tSUBGRAPH_NOT_FOUND: 404,\n} as const;\n\nexport function getErrorMessage(err: unknown): string {\n\treturn err instanceof Error ? err.message : String(err);\n}\n",
|
|
10
10
|
"import { z } from \"zod/v4\";\n\n/**\n * Account profile shapes. Unrelated to marketplace — previously lived in\n * schemas/marketplace.ts alongside public-directory types. Kept here now\n * that marketplace is gone so the profile fields (display_name, bio, slug)\n * have a stable home.\n */\n\nexport interface UpdateProfileRequest {\n\tdisplay_name?: string;\n\tbio?: string;\n\tslug?: string;\n}\n\nexport const UpdateProfileRequestSchema: z.ZodType<UpdateProfileRequest> =\n\tz.object({\n\t\tdisplay_name: z.string().max(50).optional(),\n\t\tbio: z.string().max(300).optional(),\n\t\tslug: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.min(3)\n\t\t\t.max(30)\n\t\t\t.optional(),\n\t});\n",
|
|
11
11
|
"import { isValidAddress as _isValidAddress } from \"@secondlayer/stacks\";\nimport { z } from \"zod/v4\";\n\nconst isValidAddress = _isValidAddress as (addr: string) => boolean;\n\n/** Validate a Stacks principal (standard or contract, e.g. SP2J...ABC or SP2J...ABC.contract-name) */\nconst stacksPrincipal = z.string().refine((val) => {\n\tconst parts = val.split(\".\");\n\tif (parts.length > 2) return false;\n\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\treturn isValidAddress(parts[0]!);\n}, \"Invalid Stacks principal address\");\n\n// Base filter with common fields\nconst baseFilter = {\n\t// Optional: filter by sender\n\tsender: stacksPrincipal.optional(),\n\t// Optional: filter by recipient\n\trecipient: stacksPrincipal.optional(),\n};\n\n// Type exports — defined first so they can annotate schemas\nexport interface StxTransferFilter {\n\ttype: \"stx_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tminAmount?: number;\n\tmaxAmount?: number;\n}\n\nexport interface StxMintFilter {\n\ttype: \"stx_mint\";\n\trecipient?: string;\n\tminAmount?: number;\n}\n\nexport interface StxBurnFilter {\n\ttype: \"stx_burn\";\n\tsender?: string;\n\tminAmount?: number;\n}\n\nexport interface StxLockFilter {\n\ttype: \"stx_lock\";\n\tlockedAddress?: string;\n\tminAmount?: number;\n}\n\nexport interface FtTransferFilter {\n\ttype: \"ft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtMintFilter {\n\ttype: \"ft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtBurnFilter {\n\ttype: \"ft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface NftTransferFilter {\n\ttype: \"nft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftMintFilter {\n\ttype: \"nft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftBurnFilter {\n\ttype: \"nft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface ContractCallFilter {\n\ttype: \"contract_call\";\n\tcontractId?: string;\n\tfunctionName?: string;\n\tcaller?: string;\n}\n\nexport interface ContractDeployFilter {\n\ttype: \"contract_deploy\";\n\tdeployer?: string;\n\tcontractName?: string;\n}\n\nexport interface PrintEventFilter {\n\ttype: \"print_event\";\n\tcontractId?: string;\n\ttopic?: string;\n\tcontains?: string;\n}\n\nexport type EventFilter =\n\t| StxTransferFilter\n\t| StxMintFilter\n\t| StxBurnFilter\n\t| StxLockFilter\n\t| FtTransferFilter\n\t| FtMintFilter\n\t| FtBurnFilter\n\t| NftTransferFilter\n\t| NftMintFilter\n\t| NftBurnFilter\n\t| ContractCallFilter\n\t| ContractDeployFilter\n\t| PrintEventFilter;\n\n// STX Transfer Filter\nexport const StxTransferFilterSchema: z.ZodType<StxTransferFilter> = z.object({\n\ttype: z.literal(\"stx_transfer\"),\n\t...baseFilter,\n\t// Optional: minimum amount in microSTX\n\tminAmount: z.coerce.number().int().positive().optional(),\n\t// Optional: maximum amount in microSTX\n\tmaxAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Mint Filter\nexport const StxMintFilterSchema: z.ZodType<StxMintFilter> = z.object({\n\ttype: z.literal(\"stx_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Burn Filter\nexport const StxBurnFilterSchema: z.ZodType<StxBurnFilter> = z.object({\n\ttype: z.literal(\"stx_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Lock Filter\nexport const StxLockFilterSchema: z.ZodType<StxLockFilter> = z.object({\n\ttype: z.literal(\"stx_lock\"),\n\tlockedAddress: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Transfer Filter\nexport const FtTransferFilterSchema: z.ZodType<FtTransferFilter> = z.object({\n\ttype: z.literal(\"ft_transfer\"),\n\t...baseFilter,\n\t// Contract that defines the token (e.g., SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wstx)\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Mint Filter\nexport const FtMintFilterSchema: z.ZodType<FtMintFilter> = z.object({\n\ttype: z.literal(\"ft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Burn Filter\nexport const FtBurnFilterSchema: z.ZodType<FtBurnFilter> = z.object({\n\ttype: z.literal(\"ft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// NFT Transfer Filter\nexport const NftTransferFilterSchema: z.ZodType<NftTransferFilter> = z.object({\n\ttype: z.literal(\"nft_transfer\"),\n\t...baseFilter,\n\tassetIdentifier: z.string().optional(),\n\t// Optional: filter by specific token ID (Clarity value as hex)\n\ttokenId: z.string().optional(),\n});\n\n// NFT Mint Filter\nexport const NftMintFilterSchema: z.ZodType<NftMintFilter> = z.object({\n\ttype: z.literal(\"nft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// NFT Burn Filter\nexport const NftBurnFilterSchema: z.ZodType<NftBurnFilter> = z.object({\n\ttype: z.literal(\"nft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// Contract Call Filter\nexport const ContractCallFilterSchema: z.ZodType<ContractCallFilter> = z.object(\n\t{\n\t\ttype: z.literal(\"contract_call\"),\n\t\t// Contract being called\n\t\tcontractId: stacksPrincipal.optional(),\n\t\t// Function name (supports wildcards with *)\n\t\tfunctionName: z.string().optional(),\n\t\t// Caller address\n\t\tcaller: stacksPrincipal.optional(),\n\t},\n);\n\n// Contract Deploy Filter\nexport const ContractDeployFilterSchema: z.ZodType<ContractDeployFilter> =\n\tz.object({\n\t\ttype: z.literal(\"contract_deploy\"),\n\t\t// Deployer address\n\t\tdeployer: stacksPrincipal.optional(),\n\t\t// Contract name pattern (supports wildcards)\n\t\tcontractName: z.string().optional(),\n\t});\n\n// Print Event Filter (smart contract events)\nexport const PrintEventFilterSchema: z.ZodType<PrintEventFilter> = z.object({\n\ttype: z.literal(\"print_event\"),\n\t// Contract emitting the event\n\tcontractId: stacksPrincipal.optional(),\n\t// Topic/name of the event\n\ttopic: z.string().optional(),\n\t// Search for substring in event data\n\tcontains: z.string().optional(),\n});\n\n// Union of all filter types\nexport const EventFilterSchema: z.ZodType<EventFilter> = z.discriminatedUnion(\n\t\"type\",\n\t[\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxLockFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractCallFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractDeployFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tPrintEventFilterSchema as any,\n\t],\n);\n",
|
|
12
|
-
"import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: string;\n\tmessage: string;\n\tplan?: string;\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
|
|
12
|
+
"import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"handler_updated\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: string;\n\tmessage: string;\n\tplan?: string;\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
|
|
13
13
|
"import { z } from \"zod/v4\";\n\nexport const SUBSCRIPTION_FORMATS = [\n\t\"standard-webhooks\",\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"cloudevents\",\n\t\"raw\",\n] as const;\n\nexport const SUBSCRIPTION_RUNTIMES = [\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"node\",\n] as const;\n\nexport const SUBSCRIPTION_STATUSES = [\"active\", \"paused\", \"error\"] as const;\n\nexport const SUBSCRIPTION_FILTER_OPERATORS = [\n\t\"eq\",\n\t\"neq\",\n\t\"gt\",\n\t\"gte\",\n\t\"lt\",\n\t\"lte\",\n\t\"in\",\n] as const;\n\nconst webhookUrl = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.refine(\n\t\t(value) => value.startsWith(\"http://\") || value.startsWith(\"https://\"),\n\t\t\"must be an http(s) URL\",\n\t);\n\nconst name = z.string().trim().min(1).max(128);\nconst resourceName = z.string().trim().min(1).max(128);\n\nexport const SubscriptionStatusSchema: z.ZodType<SubscriptionStatus> = z.enum(\n\tSUBSCRIPTION_STATUSES,\n);\nexport const SubscriptionFormatSchema: z.ZodType<SubscriptionFormat> =\n\tz.enum(SUBSCRIPTION_FORMATS);\nexport const SubscriptionRuntimeSchema: z.ZodType<SubscriptionRuntime> = z.enum(\n\tSUBSCRIPTION_RUNTIMES,\n);\n\nexport const SubscriptionFilterPrimitiveSchema: z.ZodType<SubscriptionFilterPrimitive> =\n\tz.union([z.string(), z.number().finite(), z.boolean()]);\n\nexport const SubscriptionFilterOperatorSchema: z.ZodType<SubscriptionFilterOperator> =\n\tz.union([\n\t\tz.object({ eq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ neq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ gt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ gte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz\n\t\t\t.object({\n\t\t\t\tin: z.array(SubscriptionFilterPrimitiveSchema).min(1),\n\t\t\t})\n\t\t\t.strict(),\n\t]);\n\nexport const SubscriptionFilterClauseSchema: z.ZodType<SubscriptionFilterClause> =\n\tz.union([\n\t\tSubscriptionFilterPrimitiveSchema,\n\t\tSubscriptionFilterOperatorSchema,\n\t]);\n\nexport const SubscriptionFilterSchema: z.ZodType<SubscriptionFilter> = z.record(\n\tz.string().min(1),\n\tSubscriptionFilterClauseSchema,\n);\n\nexport const CreateSubscriptionRequestSchema: z.ZodType<ParsedCreateSubscriptionRequest> =\n\tz.object({\n\t\tname,\n\t\tsubgraphName: resourceName,\n\t\ttableName: resourceName,\n\t\turl: webhookUrl,\n\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\tformat: SubscriptionFormatSchema.default(\"standard-webhooks\"),\n\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t});\n\nexport const UpdateSubscriptionRequestSchema: z.ZodType<UpdateSubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tname: name.optional(),\n\t\t\turl: webhookUrl.optional(),\n\t\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\t\tformat: SubscriptionFormatSchema.optional(),\n\t\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t\t})\n\t\t.refine((value) => Object.keys(value).length > 0, {\n\t\t\tmessage: \"At least one field must be provided\",\n\t\t});\n\nexport const ReplaySubscriptionRequestSchema: z.ZodType<ReplaySubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tfromBlock: z.number().int().nonnegative(),\n\t\t\ttoBlock: z.number().int().nonnegative(),\n\t\t\tforce: z.string().trim().min(1).max(64).optional(),\n\t\t})\n\t\t.refine((value) => value.fromBlock <= value.toBlock, {\n\t\t\tmessage: \"fromBlock must be less than or equal to toBlock\",\n\t\t\tpath: [\"toBlock\"],\n\t\t});\n\nexport type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];\nexport type SubscriptionFormat = (typeof SUBSCRIPTION_FORMATS)[number];\nexport type SubscriptionRuntime = (typeof SUBSCRIPTION_RUNTIMES)[number];\nexport type SubscriptionFilterPrimitive = string | number | boolean;\nexport type SubscriptionFilterOperator =\n\t| { eq: SubscriptionFilterPrimitive }\n\t| { neq: SubscriptionFilterPrimitive }\n\t| { gt: string | number }\n\t| { gte: string | number }\n\t| { lt: string | number }\n\t| { lte: string | number }\n\t| { in: SubscriptionFilterPrimitive[] };\nexport type SubscriptionFilterClause =\n\t| SubscriptionFilterPrimitive\n\t| SubscriptionFilterOperator;\nexport type SubscriptionFilter = Record<string, SubscriptionFilterClause>;\n\nexport interface CreateSubscriptionRequest {\n\tname: string;\n\tsubgraphName: string;\n\ttableName: string;\n\turl: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport interface ParsedCreateSubscriptionRequest\n\textends Omit<CreateSubscriptionRequest, \"format\"> {\n\tformat: SubscriptionFormat;\n}\n\nexport interface UpdateSubscriptionRequest {\n\tname?: string;\n\turl?: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport type ParsedUpdateSubscriptionRequest = UpdateSubscriptionRequest;\n\nexport interface ReplaySubscriptionRequest {\n\tfromBlock: number;\n\ttoBlock: number;\n\tforce?: string;\n}\n\nexport type ParsedReplaySubscriptionRequest = ReplaySubscriptionRequest;\n\nexport interface SubscriptionSummary {\n\tid: string;\n\tname: string;\n\tstatus: SubscriptionStatus;\n\tsubgraphName: string;\n\ttableName: string;\n\tformat: SubscriptionFormat;\n\truntime: SubscriptionRuntime | null;\n\turl: string;\n\tlastDeliveryAt: string | null;\n\tlastSuccessAt: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubscriptionDetail extends SubscriptionSummary {\n\tfilter: Record<string, unknown>;\n\tauthConfig: Record<string, unknown>;\n\tmaxRetries: number;\n\ttimeoutMs: number;\n\tconcurrency: number;\n\tcircuitFailures: number;\n\tcircuitOpenedAt: string | null;\n\tlastError: string | null;\n}\n\nexport interface CreateSubscriptionResponse {\n\tsubscription: SubscriptionDetail;\n\t/** Plaintext signing secret — surfaced ONCE. Store it server-side. */\n\tsigningSecret: string;\n}\n\nexport interface RotateSecretResponse {\n\tsubscription: SubscriptionDetail;\n\tsigningSecret: string;\n}\n\nexport interface DeliveryRow {\n\tid: string;\n\tattempt: number;\n\tstatusCode: number | null;\n\terrorMessage: string | null;\n\tdurationMs: number | null;\n\tresponseBody: string | null;\n\tdispatchedAt: string;\n}\n\nexport interface ReplayResult {\n\treplayId: string;\n\tenqueuedCount: number;\n\tscannedCount: number;\n}\n\nexport interface DeadRow {\n\tid: string;\n\teventType: string;\n\tattempt: number;\n\tblockHeight: number;\n\ttxId: string | null;\n\tpayload: Record<string, unknown>;\n\tfailedAt: string | null;\n\tcreatedAt: string;\n}\n\nexport interface SubscriptionSchemaColumn {\n\ttype?: unknown;\n}\n\nexport interface SubscriptionSchemaTable {\n\tcolumns: Record<string, SubscriptionSchemaColumn>;\n}\n\nexport type SubscriptionSchemaTables = Record<string, SubscriptionSchemaTable>;\n\nconst SCALAR_COLUMN_TYPES = new Set([\n\t\"text\",\n\t\"uint\",\n\t\"int\",\n\t\"principal\",\n\t\"boolean\",\n\t\"timestamp\",\n]);\n\nconst COMPARISON_COLUMN_TYPES = new Set([\"uint\", \"int\", \"timestamp\"]);\n\nfunction formatIssuePath(path: PropertyKey[]): string {\n\treturn path.length > 0 ? `${path.map(String).join(\".\")}: ` : \"\";\n}\n\nexport function formatSubscriptionSchemaErrors(error: z.ZodError): string[] {\n\treturn error.issues.map(\n\t\t(issue) => `${formatIssuePath(issue.path)}${issue.message}`,\n\t);\n}\n\nfunction operatorForClause(clause: SubscriptionFilterClause): string {\n\tif (clause === null || typeof clause !== \"object\" || Array.isArray(clause)) {\n\t\treturn \"eq\";\n\t}\n\treturn Object.keys(clause)[0] ?? \"eq\";\n}\n\nexport function validateSubscriptionFilterForTable(input: {\n\tsubgraphName?: string;\n\ttableName: string;\n\tfilter?: unknown;\n\ttables: SubscriptionSchemaTables;\n}): string[] {\n\tconst errors: string[] = [];\n\tconst table = input.tables[input.tableName];\n\tif (!table) {\n\t\tconst names = Object.keys(input.tables);\n\t\terrors.push(\n\t\t\t`Unknown table \"${input.tableName}\"${\n\t\t\t\tinput.subgraphName ? ` in subgraph \"${input.subgraphName}\"` : \"\"\n\t\t\t}.${names.length > 0 ? ` Available tables: ${names.join(\", \")}.` : \"\"}`,\n\t\t);\n\t\treturn errors;\n\t}\n\n\tif (input.filter === undefined) return errors;\n\n\tconst parsed = SubscriptionFilterSchema.safeParse(input.filter);\n\tif (!parsed.success) {\n\t\treturn formatSubscriptionSchemaErrors(parsed.error);\n\t}\n\n\tfor (const [field, clause] of Object.entries(parsed.data)) {\n\t\tconst column = table.columns[field];\n\t\tif (!column) {\n\t\t\terrors.push(\n\t\t\t\t`Unknown filter field \"${field}\" on table \"${input.tableName}\".`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst columnType =\n\t\t\ttypeof column.type === \"string\" ? column.type.toLowerCase() : \"\";\n\t\tif (!SCALAR_COLUMN_TYPES.has(columnType)) {\n\t\t\terrors.push(\n\t\t\t\t`Filter field \"${field}\" has unsupported type \"${columnType || \"unknown\"}\"; subscription filters require scalar columns.`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst operator = operatorForClause(clause);\n\t\tif (\n\t\t\t(operator === \"gt\" ||\n\t\t\t\toperator === \"gte\" ||\n\t\t\t\toperator === \"lt\" ||\n\t\t\t\toperator === \"lte\") &&\n\t\t\t!COMPARISON_COLUMN_TYPES.has(columnType)\n\t\t) {\n\t\t\terrors.push(\n\t\t\t\t`Operator \"${operator}\" is not supported for ${columnType} field \"${field}\".`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n",
|
|
14
14
|
"import type { SubgraphDetail } from \"../schemas/subgraphs.ts\";\n\nexport type SubgraphSpecFormat = \"openapi\" | \"agent\" | \"markdown\";\n\nexport interface SubgraphSpecOptions {\n\tserverUrl?: string;\n\tgeneratedAt?: string;\n}\n\nexport interface SubgraphAgentSchema {\n\tname: string;\n\tversion: string;\n\tdescription?: string;\n\tschemaHash?: string;\n\tgeneratedAt: string;\n\tserverUrl: string;\n\tsources?: Record<string, unknown>;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcountEndpoint: string;\n\t\t\trowCount: number;\n\t\t\tcolumns: SubgraphDetail[\"tables\"][string][\"columns\"];\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t\tquery: {\n\t\t\t\tparameters: string[];\n\t\t\t\tsortable: string[];\n\t\t\t\tselectable: string[];\n\t\t\t\tsearchable: string[];\n\t\t\t\tfilters: string[];\n\t\t\t};\n\t\t\texamples: {\n\t\t\t\tlist: Record<string, unknown>;\n\t\t\t\tcount: { count: number };\n\t\t\t\tcurl: string;\n\t\t\t};\n\t\t}\n\t>;\n}\n\ntype ColumnMeta = SubgraphDetail[\"tables\"][string][\"columns\"][string];\n\nconst SYSTEM_COLUMNS = [\"_id\", \"_block_height\", \"_tx_id\", \"_created_at\"];\nconst BASE_QUERY_PARAMS = [\"_limit\", \"_offset\", \"_sort\", \"_order\", \"_fields\"];\nconst COMPARISON_OPS = [\"neq\", \"gt\", \"gte\", \"lt\", \"lte\"];\n\nfunction generatedAt(options: SubgraphSpecOptions): string {\n\treturn options.generatedAt ?? new Date().toISOString();\n}\n\nfunction normalizeServerUrl(serverUrl?: string): string {\n\treturn (serverUrl ?? \"https://api.secondlayer.tools\").replace(/\\/+$/, \"\");\n}\n\nfunction tablePath(subgraphName: string, tableName: string): string {\n\treturn `/api/subgraphs/${subgraphName}/${tableName}`;\n}\n\nfunction countPath(subgraphName: string, tableName: string): string {\n\treturn `${tablePath(subgraphName, tableName)}/count`;\n}\n\nfunction isTextLike(type: string): boolean {\n\treturn type === \"text\" || type === \"principal\" || type === \"timestamp\";\n}\n\nfunction isComparable(type: string): boolean {\n\treturn (\n\t\ttype === \"uint\" ||\n\t\ttype === \"int\" ||\n\t\ttype === \"bigint\" ||\n\t\ttype === \"serial\" ||\n\t\ttype === \"timestamp\"\n\t);\n}\n\nfunction exampleForColumn(type: string): unknown {\n\tswitch (type) {\n\t\tcase \"uint\":\n\t\tcase \"int\":\n\t\tcase \"bigint\":\n\t\t\treturn \"1000\";\n\t\tcase \"serial\":\n\t\t\treturn 1;\n\t\tcase \"principal\":\n\t\t\treturn \"SP000000000000000000002Q6VF78\";\n\t\tcase \"timestamp\":\n\t\t\treturn \"2026-01-01T00:00:00.000Z\";\n\t\tcase \"boolean\":\n\t\t\treturn true;\n\t\tcase \"jsonb\":\n\t\t\treturn { example: true };\n\t\tdefault:\n\t\t\treturn \"example\";\n\t}\n}\n\nfunction openApiSchemaForColumn(col: ColumnMeta): Record<string, unknown> {\n\tlet schema: Record<string, unknown>;\n\tswitch (col.type) {\n\t\tcase \"uint\":\n\t\tcase \"int\":\n\t\tcase \"bigint\":\n\t\t\tschema = { type: \"string\", pattern: \"^-?\\\\d+(\\\\.\\\\d+)?$\" };\n\t\t\tbreak;\n\t\tcase \"serial\":\n\t\t\tschema = { type: \"integer\" };\n\t\t\tbreak;\n\t\tcase \"principal\":\n\t\tcase \"text\":\n\t\t\tschema = { type: \"string\" };\n\t\t\tbreak;\n\t\tcase \"timestamp\":\n\t\t\tschema = { type: \"string\", format: \"date-time\" };\n\t\t\tbreak;\n\t\tcase \"boolean\":\n\t\t\tschema = { type: \"boolean\" };\n\t\t\tbreak;\n\t\tcase \"jsonb\":\n\t\t\tschema = { type: \"object\", additionalProperties: true };\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tschema = {};\n\t\t\tbreak;\n\t}\n\tif (col.nullable) {\n\t\tconst type = schema.type;\n\t\tif (typeof type === \"string\") schema.type = [type, \"null\"];\n\t}\n\treturn schema;\n}\n\nfunction columnEntries(table: SubgraphDetail[\"tables\"][string]) {\n\treturn Object.entries(table.columns);\n}\n\nfunction selectableColumns(table: SubgraphDetail[\"tables\"][string]): string[] {\n\treturn columnEntries(table).map(([name]) => name);\n}\n\nfunction searchableColumns(table: SubgraphDetail[\"tables\"][string]): string[] {\n\treturn columnEntries(table)\n\t\t.filter(([, col]) => col.searchable)\n\t\t.map(([name]) => name);\n}\n\nfunction filterNames(table: SubgraphDetail[\"tables\"][string]): string[] {\n\tconst result: string[] = [];\n\tfor (const [name, col] of columnEntries(table)) {\n\t\tresult.push(name);\n\t\tresult.push(`${name}.neq`);\n\t\tif (isComparable(col.type)) {\n\t\t\tfor (const op of COMPARISON_OPS.filter((op) => op !== \"neq\")) {\n\t\t\t\tresult.push(`${name}.${op}`);\n\t\t\t}\n\t\t}\n\t\tif (isTextLike(col.type)) result.push(`${name}.like`);\n\t}\n\treturn result;\n}\n\nfunction queryParameters(table: SubgraphDetail[\"tables\"][string]): string[] {\n\tconst params = [...BASE_QUERY_PARAMS];\n\tif (searchableColumns(table).length > 0) params.push(\"_search\");\n\treturn params;\n}\n\nfunction rowExample(table: SubgraphDetail[\"tables\"][string]) {\n\tconst row: Record<string, unknown> = {};\n\tfor (const [name, col] of columnEntries(table)) {\n\t\trow[name] = exampleForColumn(col.type);\n\t}\n\treturn row;\n}\n\nfunction openApiParameter(\n\tname: string,\n\tdescription: string,\n\tschema: Record<string, unknown> = { type: \"string\" },\n) {\n\treturn {\n\t\tname,\n\t\tin: \"query\",\n\t\trequired: false,\n\t\tdescription,\n\t\tschema,\n\t};\n}\n\nfunction tableParameters(table: SubgraphDetail[\"tables\"][string]) {\n\tconst parameters = [\n\t\topenApiParameter(\"_limit\", \"Maximum rows to return.\", {\n\t\t\ttype: \"integer\",\n\t\t\tdefault: 50,\n\t\t\tminimum: 1,\n\t\t\tmaximum: 1000,\n\t\t}),\n\t\topenApiParameter(\"_offset\", \"Rows to skip for pagination.\", {\n\t\t\ttype: \"integer\",\n\t\t\tdefault: 0,\n\t\t\tminimum: 0,\n\t\t}),\n\t\topenApiParameter(\"_sort\", \"Column to sort by.\", {\n\t\t\ttype: \"string\",\n\t\t\tenum: selectableColumns(table),\n\t\t}),\n\t\topenApiParameter(\"_order\", \"Sort direction.\", {\n\t\t\ttype: \"string\",\n\t\t\tenum: [\"asc\", \"desc\"],\n\t\t\tdefault: \"asc\",\n\t\t}),\n\t\topenApiParameter(\"_fields\", \"Comma-separated columns to include.\", {\n\t\t\ttype: \"string\",\n\t\t}),\n\t];\n\tif (searchableColumns(table).length > 0) {\n\t\tparameters.push(\n\t\t\topenApiParameter(\"_search\", \"Search across searchable columns.\", {\n\t\t\t\ttype: \"string\",\n\t\t\t}),\n\t\t);\n\t}\n\tfor (const [name, col] of columnEntries(table)) {\n\t\tparameters.push(\n\t\t\topenApiParameter(name, `Filter ${name} by equality.`, {\n\t\t\t\ttype: \"string\",\n\t\t\t}),\n\t\t);\n\t\tparameters.push(\n\t\t\topenApiParameter(`${name}.neq`, `Filter ${name} by inequality.`, {\n\t\t\t\ttype: \"string\",\n\t\t\t}),\n\t\t);\n\t\tif (isComparable(col.type)) {\n\t\t\tfor (const op of [\"gt\", \"gte\", \"lt\", \"lte\"]) {\n\t\t\t\tparameters.push(\n\t\t\t\t\topenApiParameter(`${name}.${op}`, `Filter ${name} with ${op}.`, {\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tif (isTextLike(col.type)) {\n\t\t\tparameters.push(\n\t\t\t\topenApiParameter(\n\t\t\t\t\t`${name}.like`,\n\t\t\t\t\t`Case-insensitive contains filter for ${name}.`,\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: \"string\",\n\t\t\t\t\t},\n\t\t\t\t),\n\t\t\t);\n\t\t}\n\t}\n\treturn parameters;\n}\n\nexport function generateSubgraphAgentSchema(\n\tdetail: SubgraphDetail,\n\toptions: SubgraphSpecOptions = {},\n): SubgraphAgentSchema {\n\tconst serverUrl = normalizeServerUrl(options.serverUrl);\n\tconst tables: SubgraphAgentSchema[\"tables\"] = {};\n\tfor (const [tableName, table] of Object.entries(detail.tables)) {\n\t\tconst path = tablePath(detail.name, tableName);\n\t\ttables[tableName] = {\n\t\t\tendpoint: `${serverUrl}${path}`,\n\t\t\tcountEndpoint: `${serverUrl}${countPath(detail.name, tableName)}`,\n\t\t\trowCount: table.rowCount,\n\t\t\tcolumns: table.columns,\n\t\t\t...(table.indexes ? { indexes: table.indexes } : {}),\n\t\t\t...(table.uniqueKeys ? { uniqueKeys: table.uniqueKeys } : {}),\n\t\t\tquery: {\n\t\t\t\tparameters: queryParameters(table),\n\t\t\t\tsortable: selectableColumns(table),\n\t\t\t\tselectable: selectableColumns(table),\n\t\t\t\tsearchable: searchableColumns(table),\n\t\t\t\tfilters: filterNames(table),\n\t\t\t},\n\t\t\texamples: {\n\t\t\t\tlist: rowExample(table),\n\t\t\t\tcount: { count: table.rowCount },\n\t\t\t\tcurl: `curl '${serverUrl}${path}?_limit=10&_sort=_block_height&_order=desc'`,\n\t\t\t},\n\t\t};\n\t}\n\treturn {\n\t\tname: detail.name,\n\t\tversion: detail.version,\n\t\t...(detail.description ? { description: detail.description } : {}),\n\t\t...(detail.schemaHash ? { schemaHash: detail.schemaHash } : {}),\n\t\tgeneratedAt: generatedAt(options),\n\t\tserverUrl,\n\t\t...(detail.sources ? { sources: detail.sources } : {}),\n\t\ttables,\n\t};\n}\n\nexport function generateSubgraphOpenApi(\n\tdetail: SubgraphDetail,\n\toptions: SubgraphSpecOptions = {},\n): Record<string, unknown> {\n\tconst serverUrl = normalizeServerUrl(options.serverUrl);\n\tconst paths: Record<string, unknown> = {};\n\tconst schemas: Record<string, unknown> = {};\n\n\tfor (const [tableName, table] of Object.entries(detail.tables)) {\n\t\tconst schemaName = `${tableName}Row`;\n\t\tconst properties: Record<string, unknown> = {};\n\t\tconst required: string[] = [];\n\t\tfor (const [columnName, column] of columnEntries(table)) {\n\t\t\tproperties[columnName] = openApiSchemaForColumn(column);\n\t\t\tif (!column.nullable) required.push(columnName);\n\t\t}\n\t\tschemas[schemaName] = {\n\t\t\ttype: \"object\",\n\t\t\tproperties,\n\t\t\trequired,\n\t\t\texample: rowExample(table),\n\t\t};\n\n\t\tpaths[tablePath(detail.name, tableName)] = {\n\t\t\tget: {\n\t\t\t\tsummary: `Query ${detail.name}.${tableName}`,\n\t\t\t\toperationId: `query_${detail.name.replace(/-/g, \"_\")}_${tableName}`,\n\t\t\t\tparameters: tableParameters(table),\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Rows returned from the subgraph table.\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"array\",\n\t\t\t\t\t\t\t\t\t\t\titems: { $ref: `#/components/schemas/${schemaName}` },\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\tmeta: {\n\t\t\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\t\t\tproperties: {\n\t\t\t\t\t\t\t\t\t\t\t\ttotal: { type: \"integer\" },\n\t\t\t\t\t\t\t\t\t\t\t\tlimit: { type: \"integer\" },\n\t\t\t\t\t\t\t\t\t\t\t\toffset: { type: \"integer\" },\n\t\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\n\t\tpaths[countPath(detail.name, tableName)] = {\n\t\t\tget: {\n\t\t\t\tsummary: `Count ${detail.name}.${tableName}`,\n\t\t\t\toperationId: `count_${detail.name.replace(/-/g, \"_\")}_${tableName}`,\n\t\t\t\tparameters: tableParameters(table),\n\t\t\t\tresponses: {\n\t\t\t\t\t\"200\": {\n\t\t\t\t\t\tdescription: \"Row count for the filtered table query.\",\n\t\t\t\t\t\tcontent: {\n\t\t\t\t\t\t\t\"application/json\": {\n\t\t\t\t\t\t\t\tschema: {\n\t\t\t\t\t\t\t\t\ttype: \"object\",\n\t\t\t\t\t\t\t\t\tproperties: { count: { type: \"integer\" } },\n\t\t\t\t\t\t\t\t\trequired: [\"count\"],\n\t\t\t\t\t\t\t\t\texample: { count: table.rowCount },\n\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t};\n\t}\n\n\treturn {\n\t\topenapi: \"3.1.0\",\n\t\tinfo: {\n\t\t\ttitle: `${detail.name} Subgraph API`,\n\t\t\tversion: detail.version,\n\t\t\t...(detail.description ? { description: detail.description } : {}),\n\t\t},\n\t\tservers: [{ url: serverUrl }],\n\t\tpaths,\n\t\tcomponents: { schemas },\n\t\t\"x-secondlayer-subgraph\": detail.name,\n\t\t\"x-secondlayer-version\": detail.version,\n\t\t\"x-secondlayer-schema-hash\": detail.schemaHash,\n\t\t\"x-secondlayer-generated-at\": generatedAt(options),\n\t\t\"x-secondlayer-sources\": detail.sources ?? {},\n\t\t\"x-secondlayer-tables\": Object.keys(detail.tables),\n\t};\n}\n\nexport function generateSubgraphMarkdown(\n\tdetail: SubgraphDetail,\n\toptions: SubgraphSpecOptions = {},\n): string {\n\tconst agent = generateSubgraphAgentSchema(detail, options);\n\tconst lines = [\n\t\t`# ${detail.name} Subgraph API`,\n\t\t\"\",\n\t\t`Version: ${detail.version}`,\n\t\tdetail.schemaHash ? `Schema hash: ${detail.schemaHash}` : undefined,\n\t\t`Server: ${agent.serverUrl}`,\n\t\t\"\",\n\t\tdetail.description,\n\t].filter((line): line is string => line !== undefined && line !== \"\");\n\n\tfor (const [tableName, table] of Object.entries(agent.tables)) {\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t`## ${tableName}`,\n\t\t\t\"\",\n\t\t\t`GET ${table.endpoint}`,\n\t\t\t`GET ${table.countEndpoint}`,\n\t\t\t\"\",\n\t\t\t`Rows: ${table.rowCount}`,\n\t\t\t\"\",\n\t\t\t\"### Columns\",\n\t\t\t\"\",\n\t\t\t\"| Column | Type | Attributes |\",\n\t\t\t\"| --- | --- | --- |\",\n\t\t);\n\t\tfor (const [columnName, col] of Object.entries(table.columns)) {\n\t\t\tconst attrs = [\n\t\t\t\tSYSTEM_COLUMNS.includes(columnName) ? \"system\" : undefined,\n\t\t\t\tcol.nullable ? \"nullable\" : undefined,\n\t\t\t\tcol.indexed ? \"indexed\" : undefined,\n\t\t\t\tcol.searchable ? \"searchable\" : undefined,\n\t\t\t]\n\t\t\t\t.filter(Boolean)\n\t\t\t\t.join(\", \");\n\t\t\tlines.push(`| \\`${columnName}\\` | \\`${col.type}\\` | ${attrs || \"-\"} |`);\n\t\t}\n\t\tlines.push(\n\t\t\t\"\",\n\t\t\t\"### Query\",\n\t\t\t\"\",\n\t\t\t`Parameters: ${table.query.parameters.map((p) => `\\`${p}\\``).join(\", \")}`,\n\t\t\t`Filters: ${table.query.filters.map((p) => `\\`${p}\\``).join(\", \")}`,\n\t\t\t\"\",\n\t\t\t\"### Example\",\n\t\t\t\"\",\n\t\t\t\"```bash\",\n\t\t\ttable.examples.curl,\n\t\t\t\"```\",\n\t\t);\n\t}\n\treturn `${lines.join(\"\\n\")}\\n`;\n}\n\nexport function generateSubgraphSpec(\n\tdetail: SubgraphDetail,\n\tformat: SubgraphSpecFormat,\n\toptions: SubgraphSpecOptions = {},\n): Record<string, unknown> | SubgraphAgentSchema | string {\n\tif (format === \"openapi\") return generateSubgraphOpenApi(detail, options);\n\tif (format === \"agent\") return generateSubgraphAgentSchema(detail, options);\n\treturn generateSubgraphMarkdown(detail, options);\n}\n",
|
|
15
15
|
"import { createHmac, randomBytes } from \"node: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 (Number.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"
|
|
@@ -700,7 +700,9 @@ interface SubscriptionOutboxTable {
|
|
|
700
700
|
}
|
|
701
701
|
interface SubscriptionDeliveriesTable {
|
|
702
702
|
id: Generated<string>;
|
|
703
|
-
|
|
703
|
+
/** Nullable after migration 0077 — outbox row may be cleaned up while
|
|
704
|
+
* delivery telemetry is retained. */
|
|
705
|
+
outbox_id: string | null;
|
|
704
706
|
subscription_id: string;
|
|
705
707
|
attempt: number;
|
|
706
708
|
status_code: number | null;
|
|
@@ -119,7 +119,7 @@ interface DeploySubgraphRequest {
|
|
|
119
119
|
}
|
|
120
120
|
declare const DeploySubgraphRequestSchema: z3.ZodType<DeploySubgraphRequest>;
|
|
121
121
|
interface DeploySubgraphResponse {
|
|
122
|
-
action: "created" | "unchanged" | "updated" | "reindexed";
|
|
122
|
+
action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed";
|
|
123
123
|
subgraphId: string;
|
|
124
124
|
version: string;
|
|
125
125
|
message: string;
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"sourcesContent": [
|
|
5
5
|
"import { z } from \"zod/v4\";\n\n/**\n * Account profile shapes. Unrelated to marketplace — previously lived in\n * schemas/marketplace.ts alongside public-directory types. Kept here now\n * that marketplace is gone so the profile fields (display_name, bio, slug)\n * have a stable home.\n */\n\nexport interface UpdateProfileRequest {\n\tdisplay_name?: string;\n\tbio?: string;\n\tslug?: string;\n}\n\nexport const UpdateProfileRequestSchema: z.ZodType<UpdateProfileRequest> =\n\tz.object({\n\t\tdisplay_name: z.string().max(50).optional(),\n\t\tbio: z.string().max(300).optional(),\n\t\tslug: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.min(3)\n\t\t\t.max(30)\n\t\t\t.optional(),\n\t});\n",
|
|
6
6
|
"import { isValidAddress as _isValidAddress } from \"@secondlayer/stacks\";\nimport { z } from \"zod/v4\";\n\nconst isValidAddress = _isValidAddress as (addr: string) => boolean;\n\n/** Validate a Stacks principal (standard or contract, e.g. SP2J...ABC or SP2J...ABC.contract-name) */\nconst stacksPrincipal = z.string().refine((val) => {\n\tconst parts = val.split(\".\");\n\tif (parts.length > 2) return false;\n\t// biome-ignore lint/style/noNonNullAssertion: value is non-null after preceding check or by construction; TS narrowing limitation\n\treturn isValidAddress(parts[0]!);\n}, \"Invalid Stacks principal address\");\n\n// Base filter with common fields\nconst baseFilter = {\n\t// Optional: filter by sender\n\tsender: stacksPrincipal.optional(),\n\t// Optional: filter by recipient\n\trecipient: stacksPrincipal.optional(),\n};\n\n// Type exports — defined first so they can annotate schemas\nexport interface StxTransferFilter {\n\ttype: \"stx_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tminAmount?: number;\n\tmaxAmount?: number;\n}\n\nexport interface StxMintFilter {\n\ttype: \"stx_mint\";\n\trecipient?: string;\n\tminAmount?: number;\n}\n\nexport interface StxBurnFilter {\n\ttype: \"stx_burn\";\n\tsender?: string;\n\tminAmount?: number;\n}\n\nexport interface StxLockFilter {\n\ttype: \"stx_lock\";\n\tlockedAddress?: string;\n\tminAmount?: number;\n}\n\nexport interface FtTransferFilter {\n\ttype: \"ft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtMintFilter {\n\ttype: \"ft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface FtBurnFilter {\n\ttype: \"ft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\tminAmount?: number;\n}\n\nexport interface NftTransferFilter {\n\ttype: \"nft_transfer\";\n\tsender?: string;\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftMintFilter {\n\ttype: \"nft_mint\";\n\trecipient?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface NftBurnFilter {\n\ttype: \"nft_burn\";\n\tsender?: string;\n\tassetIdentifier?: string;\n\ttokenId?: string;\n}\n\nexport interface ContractCallFilter {\n\ttype: \"contract_call\";\n\tcontractId?: string;\n\tfunctionName?: string;\n\tcaller?: string;\n}\n\nexport interface ContractDeployFilter {\n\ttype: \"contract_deploy\";\n\tdeployer?: string;\n\tcontractName?: string;\n}\n\nexport interface PrintEventFilter {\n\ttype: \"print_event\";\n\tcontractId?: string;\n\ttopic?: string;\n\tcontains?: string;\n}\n\nexport type EventFilter =\n\t| StxTransferFilter\n\t| StxMintFilter\n\t| StxBurnFilter\n\t| StxLockFilter\n\t| FtTransferFilter\n\t| FtMintFilter\n\t| FtBurnFilter\n\t| NftTransferFilter\n\t| NftMintFilter\n\t| NftBurnFilter\n\t| ContractCallFilter\n\t| ContractDeployFilter\n\t| PrintEventFilter;\n\n// STX Transfer Filter\nexport const StxTransferFilterSchema: z.ZodType<StxTransferFilter> = z.object({\n\ttype: z.literal(\"stx_transfer\"),\n\t...baseFilter,\n\t// Optional: minimum amount in microSTX\n\tminAmount: z.coerce.number().int().positive().optional(),\n\t// Optional: maximum amount in microSTX\n\tmaxAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Mint Filter\nexport const StxMintFilterSchema: z.ZodType<StxMintFilter> = z.object({\n\ttype: z.literal(\"stx_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Burn Filter\nexport const StxBurnFilterSchema: z.ZodType<StxBurnFilter> = z.object({\n\ttype: z.literal(\"stx_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// STX Lock Filter\nexport const StxLockFilterSchema: z.ZodType<StxLockFilter> = z.object({\n\ttype: z.literal(\"stx_lock\"),\n\tlockedAddress: stacksPrincipal.optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Transfer Filter\nexport const FtTransferFilterSchema: z.ZodType<FtTransferFilter> = z.object({\n\ttype: z.literal(\"ft_transfer\"),\n\t...baseFilter,\n\t// Contract that defines the token (e.g., SP3K8BC0PPEVCV7NZ6QSRWPQ2JE9E5B6N3PA0KBR9.token-wstx)\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Mint Filter\nexport const FtMintFilterSchema: z.ZodType<FtMintFilter> = z.object({\n\ttype: z.literal(\"ft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// FT Burn Filter\nexport const FtBurnFilterSchema: z.ZodType<FtBurnFilter> = z.object({\n\ttype: z.literal(\"ft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\tminAmount: z.coerce.number().int().positive().optional(),\n});\n\n// NFT Transfer Filter\nexport const NftTransferFilterSchema: z.ZodType<NftTransferFilter> = z.object({\n\ttype: z.literal(\"nft_transfer\"),\n\t...baseFilter,\n\tassetIdentifier: z.string().optional(),\n\t// Optional: filter by specific token ID (Clarity value as hex)\n\ttokenId: z.string().optional(),\n});\n\n// NFT Mint Filter\nexport const NftMintFilterSchema: z.ZodType<NftMintFilter> = z.object({\n\ttype: z.literal(\"nft_mint\"),\n\trecipient: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// NFT Burn Filter\nexport const NftBurnFilterSchema: z.ZodType<NftBurnFilter> = z.object({\n\ttype: z.literal(\"nft_burn\"),\n\tsender: stacksPrincipal.optional(),\n\tassetIdentifier: z.string().optional(),\n\ttokenId: z.string().optional(),\n});\n\n// Contract Call Filter\nexport const ContractCallFilterSchema: z.ZodType<ContractCallFilter> = z.object(\n\t{\n\t\ttype: z.literal(\"contract_call\"),\n\t\t// Contract being called\n\t\tcontractId: stacksPrincipal.optional(),\n\t\t// Function name (supports wildcards with *)\n\t\tfunctionName: z.string().optional(),\n\t\t// Caller address\n\t\tcaller: stacksPrincipal.optional(),\n\t},\n);\n\n// Contract Deploy Filter\nexport const ContractDeployFilterSchema: z.ZodType<ContractDeployFilter> =\n\tz.object({\n\t\ttype: z.literal(\"contract_deploy\"),\n\t\t// Deployer address\n\t\tdeployer: stacksPrincipal.optional(),\n\t\t// Contract name pattern (supports wildcards)\n\t\tcontractName: z.string().optional(),\n\t});\n\n// Print Event Filter (smart contract events)\nexport const PrintEventFilterSchema: z.ZodType<PrintEventFilter> = z.object({\n\ttype: z.literal(\"print_event\"),\n\t// Contract emitting the event\n\tcontractId: stacksPrincipal.optional(),\n\t// Topic/name of the event\n\ttopic: z.string().optional(),\n\t// Search for substring in event data\n\tcontains: z.string().optional(),\n});\n\n// Union of all filter types\nexport const EventFilterSchema: z.ZodType<EventFilter> = z.discriminatedUnion(\n\t\"type\",\n\t[\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tStxLockFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tFtBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftTransferFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftMintFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tNftBurnFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractCallFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tContractDeployFilterSchema as any,\n\t\t// biome-ignore lint/suspicious/noExplicitAny: interop boundary or dynamic-shape value where typing adds friction without runtime safety\n\t\tPrintEventFilterSchema as any,\n\t],\n);\n",
|
|
7
|
-
"import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: string;\n\tmessage: string;\n\tplan?: string;\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
|
|
7
|
+
"import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"handler_updated\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: string;\n\tmessage: string;\n\tplan?: string;\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n",
|
|
8
8
|
"import { z } from \"zod/v4\";\n\nexport const SUBSCRIPTION_FORMATS = [\n\t\"standard-webhooks\",\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"cloudevents\",\n\t\"raw\",\n] as const;\n\nexport const SUBSCRIPTION_RUNTIMES = [\n\t\"inngest\",\n\t\"trigger\",\n\t\"cloudflare\",\n\t\"node\",\n] as const;\n\nexport const SUBSCRIPTION_STATUSES = [\"active\", \"paused\", \"error\"] as const;\n\nexport const SUBSCRIPTION_FILTER_OPERATORS = [\n\t\"eq\",\n\t\"neq\",\n\t\"gt\",\n\t\"gte\",\n\t\"lt\",\n\t\"lte\",\n\t\"in\",\n] as const;\n\nconst webhookUrl = z\n\t.string()\n\t.trim()\n\t.min(1)\n\t.refine(\n\t\t(value) => value.startsWith(\"http://\") || value.startsWith(\"https://\"),\n\t\t\"must be an http(s) URL\",\n\t);\n\nconst name = z.string().trim().min(1).max(128);\nconst resourceName = z.string().trim().min(1).max(128);\n\nexport const SubscriptionStatusSchema: z.ZodType<SubscriptionStatus> = z.enum(\n\tSUBSCRIPTION_STATUSES,\n);\nexport const SubscriptionFormatSchema: z.ZodType<SubscriptionFormat> =\n\tz.enum(SUBSCRIPTION_FORMATS);\nexport const SubscriptionRuntimeSchema: z.ZodType<SubscriptionRuntime> = z.enum(\n\tSUBSCRIPTION_RUNTIMES,\n);\n\nexport const SubscriptionFilterPrimitiveSchema: z.ZodType<SubscriptionFilterPrimitive> =\n\tz.union([z.string(), z.number().finite(), z.boolean()]);\n\nexport const SubscriptionFilterOperatorSchema: z.ZodType<SubscriptionFilterOperator> =\n\tz.union([\n\t\tz.object({ eq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ neq: SubscriptionFilterPrimitiveSchema }).strict(),\n\t\tz.object({ gt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ gte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lt: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz.object({ lte: z.union([z.string(), z.number().finite()]) }).strict(),\n\t\tz\n\t\t\t.object({\n\t\t\t\tin: z.array(SubscriptionFilterPrimitiveSchema).min(1),\n\t\t\t})\n\t\t\t.strict(),\n\t]);\n\nexport const SubscriptionFilterClauseSchema: z.ZodType<SubscriptionFilterClause> =\n\tz.union([\n\t\tSubscriptionFilterPrimitiveSchema,\n\t\tSubscriptionFilterOperatorSchema,\n\t]);\n\nexport const SubscriptionFilterSchema: z.ZodType<SubscriptionFilter> = z.record(\n\tz.string().min(1),\n\tSubscriptionFilterClauseSchema,\n);\n\nexport const CreateSubscriptionRequestSchema: z.ZodType<ParsedCreateSubscriptionRequest> =\n\tz.object({\n\t\tname,\n\t\tsubgraphName: resourceName,\n\t\ttableName: resourceName,\n\t\turl: webhookUrl,\n\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\tformat: SubscriptionFormatSchema.default(\"standard-webhooks\"),\n\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t});\n\nexport const UpdateSubscriptionRequestSchema: z.ZodType<UpdateSubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tname: name.optional(),\n\t\t\turl: webhookUrl.optional(),\n\t\t\tfilter: SubscriptionFilterSchema.optional(),\n\t\t\tformat: SubscriptionFormatSchema.optional(),\n\t\t\truntime: SubscriptionRuntimeSchema.nullable().optional(),\n\t\t\tauthConfig: z.record(z.string(), z.unknown()).optional(),\n\t\t\tmaxRetries: z.number().int().min(0).max(100).optional(),\n\t\t\ttimeoutMs: z.number().int().min(100).max(300_000).optional(),\n\t\t\tconcurrency: z.number().int().min(1).max(100).optional(),\n\t\t})\n\t\t.refine((value) => Object.keys(value).length > 0, {\n\t\t\tmessage: \"At least one field must be provided\",\n\t\t});\n\nexport const ReplaySubscriptionRequestSchema: z.ZodType<ReplaySubscriptionRequest> =\n\tz\n\t\t.object({\n\t\t\tfromBlock: z.number().int().nonnegative(),\n\t\t\ttoBlock: z.number().int().nonnegative(),\n\t\t\tforce: z.string().trim().min(1).max(64).optional(),\n\t\t})\n\t\t.refine((value) => value.fromBlock <= value.toBlock, {\n\t\t\tmessage: \"fromBlock must be less than or equal to toBlock\",\n\t\t\tpath: [\"toBlock\"],\n\t\t});\n\nexport type SubscriptionStatus = (typeof SUBSCRIPTION_STATUSES)[number];\nexport type SubscriptionFormat = (typeof SUBSCRIPTION_FORMATS)[number];\nexport type SubscriptionRuntime = (typeof SUBSCRIPTION_RUNTIMES)[number];\nexport type SubscriptionFilterPrimitive = string | number | boolean;\nexport type SubscriptionFilterOperator =\n\t| { eq: SubscriptionFilterPrimitive }\n\t| { neq: SubscriptionFilterPrimitive }\n\t| { gt: string | number }\n\t| { gte: string | number }\n\t| { lt: string | number }\n\t| { lte: string | number }\n\t| { in: SubscriptionFilterPrimitive[] };\nexport type SubscriptionFilterClause =\n\t| SubscriptionFilterPrimitive\n\t| SubscriptionFilterOperator;\nexport type SubscriptionFilter = Record<string, SubscriptionFilterClause>;\n\nexport interface CreateSubscriptionRequest {\n\tname: string;\n\tsubgraphName: string;\n\ttableName: string;\n\turl: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport interface ParsedCreateSubscriptionRequest\n\textends Omit<CreateSubscriptionRequest, \"format\"> {\n\tformat: SubscriptionFormat;\n}\n\nexport interface UpdateSubscriptionRequest {\n\tname?: string;\n\turl?: string;\n\tfilter?: SubscriptionFilter;\n\tformat?: SubscriptionFormat;\n\truntime?: SubscriptionRuntime | null;\n\tauthConfig?: Record<string, unknown>;\n\tmaxRetries?: number;\n\ttimeoutMs?: number;\n\tconcurrency?: number;\n}\n\nexport type ParsedUpdateSubscriptionRequest = UpdateSubscriptionRequest;\n\nexport interface ReplaySubscriptionRequest {\n\tfromBlock: number;\n\ttoBlock: number;\n\tforce?: string;\n}\n\nexport type ParsedReplaySubscriptionRequest = ReplaySubscriptionRequest;\n\nexport interface SubscriptionSummary {\n\tid: string;\n\tname: string;\n\tstatus: SubscriptionStatus;\n\tsubgraphName: string;\n\ttableName: string;\n\tformat: SubscriptionFormat;\n\truntime: SubscriptionRuntime | null;\n\turl: string;\n\tlastDeliveryAt: string | null;\n\tlastSuccessAt: string | null;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubscriptionDetail extends SubscriptionSummary {\n\tfilter: Record<string, unknown>;\n\tauthConfig: Record<string, unknown>;\n\tmaxRetries: number;\n\ttimeoutMs: number;\n\tconcurrency: number;\n\tcircuitFailures: number;\n\tcircuitOpenedAt: string | null;\n\tlastError: string | null;\n}\n\nexport interface CreateSubscriptionResponse {\n\tsubscription: SubscriptionDetail;\n\t/** Plaintext signing secret — surfaced ONCE. Store it server-side. */\n\tsigningSecret: string;\n}\n\nexport interface RotateSecretResponse {\n\tsubscription: SubscriptionDetail;\n\tsigningSecret: string;\n}\n\nexport interface DeliveryRow {\n\tid: string;\n\tattempt: number;\n\tstatusCode: number | null;\n\terrorMessage: string | null;\n\tdurationMs: number | null;\n\tresponseBody: string | null;\n\tdispatchedAt: string;\n}\n\nexport interface ReplayResult {\n\treplayId: string;\n\tenqueuedCount: number;\n\tscannedCount: number;\n}\n\nexport interface DeadRow {\n\tid: string;\n\teventType: string;\n\tattempt: number;\n\tblockHeight: number;\n\ttxId: string | null;\n\tpayload: Record<string, unknown>;\n\tfailedAt: string | null;\n\tcreatedAt: string;\n}\n\nexport interface SubscriptionSchemaColumn {\n\ttype?: unknown;\n}\n\nexport interface SubscriptionSchemaTable {\n\tcolumns: Record<string, SubscriptionSchemaColumn>;\n}\n\nexport type SubscriptionSchemaTables = Record<string, SubscriptionSchemaTable>;\n\nconst SCALAR_COLUMN_TYPES = new Set([\n\t\"text\",\n\t\"uint\",\n\t\"int\",\n\t\"principal\",\n\t\"boolean\",\n\t\"timestamp\",\n]);\n\nconst COMPARISON_COLUMN_TYPES = new Set([\"uint\", \"int\", \"timestamp\"]);\n\nfunction formatIssuePath(path: PropertyKey[]): string {\n\treturn path.length > 0 ? `${path.map(String).join(\".\")}: ` : \"\";\n}\n\nexport function formatSubscriptionSchemaErrors(error: z.ZodError): string[] {\n\treturn error.issues.map(\n\t\t(issue) => `${formatIssuePath(issue.path)}${issue.message}`,\n\t);\n}\n\nfunction operatorForClause(clause: SubscriptionFilterClause): string {\n\tif (clause === null || typeof clause !== \"object\" || Array.isArray(clause)) {\n\t\treturn \"eq\";\n\t}\n\treturn Object.keys(clause)[0] ?? \"eq\";\n}\n\nexport function validateSubscriptionFilterForTable(input: {\n\tsubgraphName?: string;\n\ttableName: string;\n\tfilter?: unknown;\n\ttables: SubscriptionSchemaTables;\n}): string[] {\n\tconst errors: string[] = [];\n\tconst table = input.tables[input.tableName];\n\tif (!table) {\n\t\tconst names = Object.keys(input.tables);\n\t\terrors.push(\n\t\t\t`Unknown table \"${input.tableName}\"${\n\t\t\t\tinput.subgraphName ? ` in subgraph \"${input.subgraphName}\"` : \"\"\n\t\t\t}.${names.length > 0 ? ` Available tables: ${names.join(\", \")}.` : \"\"}`,\n\t\t);\n\t\treturn errors;\n\t}\n\n\tif (input.filter === undefined) return errors;\n\n\tconst parsed = SubscriptionFilterSchema.safeParse(input.filter);\n\tif (!parsed.success) {\n\t\treturn formatSubscriptionSchemaErrors(parsed.error);\n\t}\n\n\tfor (const [field, clause] of Object.entries(parsed.data)) {\n\t\tconst column = table.columns[field];\n\t\tif (!column) {\n\t\t\terrors.push(\n\t\t\t\t`Unknown filter field \"${field}\" on table \"${input.tableName}\".`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst columnType =\n\t\t\ttypeof column.type === \"string\" ? column.type.toLowerCase() : \"\";\n\t\tif (!SCALAR_COLUMN_TYPES.has(columnType)) {\n\t\t\terrors.push(\n\t\t\t\t`Filter field \"${field}\" has unsupported type \"${columnType || \"unknown\"}\"; subscription filters require scalar columns.`,\n\t\t\t);\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst operator = operatorForClause(clause);\n\t\tif (\n\t\t\t(operator === \"gt\" ||\n\t\t\t\toperator === \"gte\" ||\n\t\t\t\toperator === \"lt\" ||\n\t\t\t\toperator === \"lte\") &&\n\t\t\t!COMPARISON_COLUMN_TYPES.has(columnType)\n\t\t) {\n\t\t\terrors.push(\n\t\t\t\t`Operator \"${operator}\" is not supported for ${columnType} field \"${field}\".`,\n\t\t\t);\n\t\t}\n\t}\n\n\treturn errors;\n}\n"
|
|
9
9
|
],
|
|
10
10
|
"mappings": "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",
|
|
@@ -13,7 +13,7 @@ interface DeploySubgraphRequest {
|
|
|
13
13
|
}
|
|
14
14
|
declare const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest>;
|
|
15
15
|
interface DeploySubgraphResponse {
|
|
16
|
-
action: "created" | "unchanged" | "updated" | "reindexed";
|
|
16
|
+
action: "created" | "unchanged" | "handler_updated" | "updated" | "reindexed";
|
|
17
17
|
subgraphId: string;
|
|
18
18
|
version: string;
|
|
19
19
|
message: string;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/schemas/subgraphs.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: string;\n\tmessage: string;\n\tplan?: string;\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n"
|
|
5
|
+
"import { z } from \"zod/v4\";\n\n// ── Deploy Subgraph Request ─────────────────────────────────────────────────\n\nexport interface DeploySubgraphRequest {\n\tname: string;\n\tversion?: string;\n\tdescription?: string;\n\tsources: Record<string, Record<string, unknown>>;\n\tschema: Record<string, unknown>;\n\thandlerCode: string;\n\t/** Override the definition's startBlock for this deploy only. */\n\tstartBlock?: number;\n\t/** Original TypeScript source, persisted so chat can read/diff/edit later. */\n\tsourceCode?: string;\n}\n\nexport const DeploySubgraphRequestSchema: z.ZodType<DeploySubgraphRequest> =\n\tz.object({\n\t\tname: z\n\t\t\t.string()\n\t\t\t.regex(/^[a-z0-9-]+$/, \"lowercase alphanumeric + hyphens only\")\n\t\t\t.max(63),\n\t\tversion: z.string().optional(),\n\t\tdescription: z.string().optional(),\n\t\tsources: z\n\t\t\t.record(z.string(), z.record(z.string(), z.unknown()))\n\t\t\t.refine(\n\t\t\t\t(s) => Object.keys(s).length > 0,\n\t\t\t\t\"Must have at least one source\",\n\t\t\t),\n\t\tschema: z.record(z.string(), z.unknown()),\n\t\thandlerCode: z.string().max(1_048_576, \"handler code exceeds 1MB limit\"),\n\t\tstartBlock: z.number().int().nonnegative().optional(),\n\t\tsourceCode: z\n\t\t\t.string()\n\t\t\t.max(1_048_576, \"source code exceeds 1MB limit\")\n\t\t\t.optional(),\n\t});\n\nexport interface DeploySubgraphResponse {\n\taction: \"created\" | \"unchanged\" | \"handler_updated\" | \"updated\" | \"reindexed\";\n\tsubgraphId: string;\n\tversion: string;\n\tmessage: string;\n\toperationId?: string;\n\treindexStarted?: boolean;\n\tdiff?: {\n\t\taddedTables: string[];\n\t\tremovedTables: string[];\n\t\taddedColumns: Record<string, string[]>;\n\t\tbreakingChanges: string[];\n\t};\n}\n\n// Subgraph API response types\n\nexport interface SubgraphSummary {\n\tname: string;\n\tversion: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\ttotalProcessed: number;\n\ttotalErrors: number;\n\ttables: string[];\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tprogress: number;\n\tblocksRemaining?: number;\n\tsyncMode?: \"sync\" | \"reindex\";\n\tresourceWarning?: SubgraphResourceWarning;\n\tgapCount: number;\n\tintegrity: \"complete\" | \"gaps_detected\";\n\tcreatedAt: string;\n}\n\nexport interface SubgraphGapRange {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n}\n\nexport interface SubgraphSyncInfo {\n\tstatus: \"synced\" | \"catching_up\" | \"reindexing\" | \"error\";\n\tmode?: \"sync\" | \"reindex\";\n\tstartBlock: number;\n\tlastProcessedBlock: number;\n\t/**\n\t * Backward-compatible denominator for progress displays. During reindexing,\n\t * this is the reindex target block rather than the live source chain tip.\n\t */\n\tchainTip: number;\n\tsourceChainTip?: number;\n\ttargetBlock?: number;\n\tblocksRemaining: number;\n\tprocessedBlocks?: number;\n\ttotalBlocks?: number;\n\tprogress: number;\n\tresourceWarning?: SubgraphResourceWarning;\n\tgaps: {\n\t\tcount: number;\n\t\ttotalMissingBlocks: number;\n\t\tranges: SubgraphGapRange[];\n\t};\n\tintegrity: \"complete\" | \"gaps_detected\";\n}\n\nexport interface SubgraphResourceWarning {\n\tcode: string;\n\tmessage: string;\n\tplan?: string;\n\tblockRange: number;\n\tprocessorMemoryMb: number;\n\trecommendedPlan: \"launch\";\n}\n\nexport interface SubgraphDetail {\n\tname: string;\n\tversion: string;\n\tschemaHash?: string;\n\tstatus: string;\n\tlastProcessedBlock: number;\n\tdescription?: string;\n\tsources?: Record<string, unknown>;\n\tdefinition?: Record<string, unknown>;\n\thealth: {\n\t\ttotalProcessed: number;\n\t\ttotalErrors: number;\n\t\terrorRate: number;\n\t\tlastError: string | null;\n\t\tlastErrorAt: string | null;\n\t};\n\tsync: SubgraphSyncInfo;\n\ttables: Record<\n\t\tstring,\n\t\t{\n\t\t\tendpoint: string;\n\t\t\tcolumns: Record<\n\t\t\t\tstring,\n\t\t\t\t{\n\t\t\t\t\ttype: string;\n\t\t\t\t\tnullable?: boolean;\n\t\t\t\t\tindexed?: boolean;\n\t\t\t\t\tsearchable?: boolean;\n\t\t\t\t\tdefault?: string | number | boolean;\n\t\t\t\t}\n\t\t\t>;\n\t\t\trowCount: number;\n\t\t\texample: string;\n\t\t\tindexes?: string[][];\n\t\t\tuniqueKeys?: string[][];\n\t\t}\n\t>;\n\tcreatedAt: string;\n\tupdatedAt: string;\n}\n\nexport interface SubgraphGapEntry {\n\tstart: number;\n\tend: number;\n\tsize: number;\n\treason: string;\n\tdetectedAt: string;\n\tresolvedAt: string | null;\n}\n\nexport interface SubgraphGapsResponse {\n\tdata: SubgraphGapEntry[];\n\tmeta: {\n\t\ttotal: number;\n\t\ttotalMissingBlocks: number;\n\t\tlimit: number;\n\t\toffset: number;\n\t};\n}\n\nexport interface ReindexResponse {\n\tmessage: string;\n\tfromBlock: number;\n\ttoBlock: number | string;\n\toperationId?: string;\n\tstatus?: \"queued\" | \"running\" | \"cancel_requested\";\n}\n\nexport interface SubgraphQueryParams {\n\tsort?: string;\n\torder?: string;\n\tlimit?: number;\n\toffset?: number;\n\tfields?: string;\n\tfilters?: Record<string, string>;\n}\n"
|
|
6
6
|
],
|
|
7
7
|
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AAiBO,IAAM,8BACZ,EAAE,OAAO;AAAA,EACR,MAAM,EACJ,OAAO,EACP,MAAM,gBAAgB,uCAAuC,EAC7D,IAAI,EAAE;AAAA,EACR,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAC7B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,EACjC,SAAS,EACP,OAAO,EAAE,OAAO,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC,CAAC,EACpD,OACA,CAAC,MAAM,OAAO,KAAK,CAAC,EAAE,SAAS,GAC/B,+BACD;AAAA,EACD,QAAQ,EAAE,OAAO,EAAE,OAAO,GAAG,EAAE,QAAQ,CAAC;AAAA,EACxC,aAAa,EAAE,OAAO,EAAE,IAAI,SAAW,gCAAgC;AAAA,EACvE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS;AAAA,EACpD,YAAY,EACV,OAAO,EACP,IAAI,SAAW,+BAA+B,EAC9C,SAAS;AACZ,CAAC;",
|
|
8
8
|
"debugId": "AA56811DA75420EF64756E2164756E21",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Loosen `subscription_deliveries.outbox_id` FK from `ON DELETE CASCADE` to
|
|
5
|
+
* `ON DELETE SET NULL` so phantom outbox deletes (cleanup races, manual
|
|
6
|
+
* requeue, subscription delete mid-dispatch) don't 23503 the delivery
|
|
7
|
+
* insert and snowball into auto-paused subscriptions via the circuit
|
|
8
|
+
* breaker.
|
|
9
|
+
*
|
|
10
|
+
* Delivery rows are append-only telemetry; the subscription_id column is
|
|
11
|
+
* the load-bearing reference. Losing the outbox link on a small minority of
|
|
12
|
+
* rows is preferable to losing the entire delivery record.
|
|
13
|
+
*/
|
|
14
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
15
|
+
await sql`
|
|
16
|
+
ALTER TABLE subscription_deliveries
|
|
17
|
+
ALTER COLUMN outbox_id DROP NOT NULL
|
|
18
|
+
`.execute(db);
|
|
19
|
+
|
|
20
|
+
await sql`
|
|
21
|
+
ALTER TABLE subscription_deliveries
|
|
22
|
+
DROP CONSTRAINT IF EXISTS subscription_deliveries_outbox_id_fkey
|
|
23
|
+
`.execute(db);
|
|
24
|
+
|
|
25
|
+
await sql`
|
|
26
|
+
ALTER TABLE subscription_deliveries
|
|
27
|
+
ADD CONSTRAINT subscription_deliveries_outbox_id_fkey
|
|
28
|
+
FOREIGN KEY (outbox_id) REFERENCES subscription_outbox(id)
|
|
29
|
+
ON DELETE SET NULL
|
|
30
|
+
`.execute(db);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
34
|
+
await sql`
|
|
35
|
+
ALTER TABLE subscription_deliveries
|
|
36
|
+
DROP CONSTRAINT IF EXISTS subscription_deliveries_outbox_id_fkey
|
|
37
|
+
`.execute(db);
|
|
38
|
+
|
|
39
|
+
await sql`
|
|
40
|
+
ALTER TABLE subscription_deliveries
|
|
41
|
+
ADD CONSTRAINT subscription_deliveries_outbox_id_fkey
|
|
42
|
+
FOREIGN KEY (outbox_id) REFERENCES subscription_outbox(id)
|
|
43
|
+
ON DELETE CASCADE
|
|
44
|
+
`.execute(db);
|
|
45
|
+
|
|
46
|
+
await sql`
|
|
47
|
+
ALTER TABLE subscription_deliveries
|
|
48
|
+
ALTER COLUMN outbox_id SET NOT NULL
|
|
49
|
+
`.execute(db);
|
|
50
|
+
}
|