@secondlayer/shared 11.7.0 → 11.7.1
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/archive/range-digest.d.ts +23 -27
- package/dist/src/archive/semantic-digest-builder.d.ts +23 -27
- package/dist/src/coverage/index.d.ts +23 -27
- package/dist/src/db/index.d.ts +29 -39
- package/dist/src/db/index.js +3 -4
- package/dist/src/db/index.js.map +3 -3
- package/dist/src/db/queries/chain-reorgs.d.ts +23 -27
- package/dist/src/db/queries/chain-reorgs.js +3 -4
- package/dist/src/db/queries/chain-reorgs.js.map +3 -3
- package/dist/src/db/queries/contracts.d.ts +23 -27
- package/dist/src/db/queries/instance.d.ts +23 -27
- package/dist/src/db/queries/integrity.d.ts +24 -28
- package/dist/src/db/queries/integrity.js +4 -3
- package/dist/src/db/queries/integrity.js.map +3 -3
- package/dist/src/db/queries/subgraph-gaps.d.ts +23 -27
- package/dist/src/db/queries/subgraph-operations.d.ts +23 -27
- package/dist/src/db/queries/subgraphs.d.ts +24 -33
- package/dist/src/db/queries/subgraphs.js +1 -10
- package/dist/src/db/queries/subgraphs.js.map +3 -3
- package/dist/src/db/queries/sync-scope.d.ts +23 -27
- package/dist/src/db/queries/webhooks.d.ts +23 -27
- package/dist/src/db/schema.d.ts +27 -36
- package/dist/src/index.d.ts +29 -39
- package/dist/src/index.js +3 -4
- package/dist/src/index.js.map +3 -3
- package/dist/src/node/local-client.d.ts +23 -27
- package/migrations/0134_waitlists.ts +64 -0
- package/migrations/0135_drop_hosted_play_and_meters.ts +58 -0
- package/package.json +1 -1
|
@@ -284,21 +284,6 @@ interface MagicLinksTable {
|
|
|
284
284
|
failed_attempts: Generated<number>;
|
|
285
285
|
created_at: Generated<Date>;
|
|
286
286
|
}
|
|
287
|
-
/** One-shot hashed tokens that attach a play ghost to an email at claim. */
|
|
288
|
-
interface ClaimTokensTable {
|
|
289
|
-
id: Generated<string>;
|
|
290
|
-
account_id: string;
|
|
291
|
-
token_hash: string;
|
|
292
|
-
created_at: Generated<Date>;
|
|
293
|
-
expires_at: Date;
|
|
294
|
-
used_at: Date | null;
|
|
295
|
-
}
|
|
296
|
-
/** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
|
|
297
|
-
interface PlayProvisionsTable {
|
|
298
|
-
ip_hash: string;
|
|
299
|
-
day: string;
|
|
300
|
-
count: Generated<number>;
|
|
301
|
-
}
|
|
302
287
|
interface SubgraphProcessingStatsTable {
|
|
303
288
|
id: Generated<string>;
|
|
304
289
|
subgraph_name: string;
|
|
@@ -760,8 +745,6 @@ interface Database {
|
|
|
760
745
|
accounts: AccountsTable;
|
|
761
746
|
sessions: SessionsTable;
|
|
762
747
|
magic_links: MagicLinksTable;
|
|
763
|
-
claim_tokens: ClaimTokensTable;
|
|
764
|
-
play_provisions: PlayProvisionsTable;
|
|
765
748
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
766
749
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
767
750
|
subgraph_gaps: SubgraphGapsTable;
|
|
@@ -770,7 +753,6 @@ interface Database {
|
|
|
770
753
|
processed_stripe_events: ProcessedStripeEventsTable;
|
|
771
754
|
account_spend_caps: AccountSpendCapsTable;
|
|
772
755
|
account_credits: AccountCreditsTable;
|
|
773
|
-
hosted_meter_days: HostedMeterDaysTable;
|
|
774
756
|
webhooks: WebhooksTable;
|
|
775
757
|
webhook_outbox: WebhookOutboxTable;
|
|
776
758
|
webhook_deliveries: WebhookDeliveriesTable;
|
|
@@ -801,6 +783,8 @@ interface Database {
|
|
|
801
783
|
coverage_segments: CoverageSegmentsTable;
|
|
802
784
|
stage_failures: StageFailuresTable;
|
|
803
785
|
archive_fetches: ArchiveFetchesTable;
|
|
786
|
+
waitlists: WaitlistsTable;
|
|
787
|
+
waitlist_signups: WaitlistSignupsTable;
|
|
804
788
|
}
|
|
805
789
|
interface ServiceHeartbeatsTable {
|
|
806
790
|
name: string;
|
|
@@ -829,15 +813,6 @@ interface AccountCreditsTable {
|
|
|
829
813
|
refill_last_at: Date | null;
|
|
830
814
|
updated_at: Generated<Date>;
|
|
831
815
|
}
|
|
832
|
-
/** Daily idempotency ledger for hosted subgraph running + storage meters. */
|
|
833
|
-
interface HostedMeterDaysTable {
|
|
834
|
-
day: string;
|
|
835
|
-
account_id: string;
|
|
836
|
-
subgraph_name: string;
|
|
837
|
-
kind: string;
|
|
838
|
-
usd_micros: string | number | bigint;
|
|
839
|
-
billed_at: Generated<Date>;
|
|
840
|
-
}
|
|
841
816
|
/**
|
|
842
817
|
* Archive fetch charge log (design-f089 gate). Append-only: one row per
|
|
843
818
|
* priced attempt to fetch a partition object. `usd_micros` is 0 for
|
|
@@ -941,6 +916,27 @@ interface TriggerEvaluatorStateTable {
|
|
|
941
916
|
last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
942
917
|
updated_at: Generated<Date>;
|
|
943
918
|
}
|
|
919
|
+
/** A public waitlist page: what it is for and whether it takes signups. */
|
|
920
|
+
interface WaitlistsTable {
|
|
921
|
+
slug: string;
|
|
922
|
+
title: string;
|
|
923
|
+
description: string;
|
|
924
|
+
url: string;
|
|
925
|
+
opened_at: Generated<Date>;
|
|
926
|
+
/** Null while the list is open. */
|
|
927
|
+
closed_at: Date | null;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* One signup per (list, contact, token). `answers` holds the list's own
|
|
931
|
+
* questions, validated per list by the API before insert.
|
|
932
|
+
*/
|
|
933
|
+
interface WaitlistSignupsTable {
|
|
934
|
+
id: Generated<string>;
|
|
935
|
+
list: string;
|
|
936
|
+
contact: string;
|
|
937
|
+
answers: Record<string, unknown>;
|
|
938
|
+
created_at: Generated<Date>;
|
|
939
|
+
}
|
|
944
940
|
/**
|
|
945
941
|
* Contract registry queries — backing for trait-based discovery. Populated from
|
|
946
942
|
* contract deploys; ABI fetched async; standards inferred by static analysis.
|
|
@@ -284,21 +284,6 @@ interface MagicLinksTable {
|
|
|
284
284
|
failed_attempts: Generated<number>;
|
|
285
285
|
created_at: Generated<Date>;
|
|
286
286
|
}
|
|
287
|
-
/** One-shot hashed tokens that attach a play ghost to an email at claim. */
|
|
288
|
-
interface ClaimTokensTable {
|
|
289
|
-
id: Generated<string>;
|
|
290
|
-
account_id: string;
|
|
291
|
-
token_hash: string;
|
|
292
|
-
created_at: Generated<Date>;
|
|
293
|
-
expires_at: Date;
|
|
294
|
-
used_at: Date | null;
|
|
295
|
-
}
|
|
296
|
-
/** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
|
|
297
|
-
interface PlayProvisionsTable {
|
|
298
|
-
ip_hash: string;
|
|
299
|
-
day: string;
|
|
300
|
-
count: Generated<number>;
|
|
301
|
-
}
|
|
302
287
|
interface SubgraphProcessingStatsTable {
|
|
303
288
|
id: Generated<string>;
|
|
304
289
|
subgraph_name: string;
|
|
@@ -760,8 +745,6 @@ interface Database {
|
|
|
760
745
|
accounts: AccountsTable;
|
|
761
746
|
sessions: SessionsTable;
|
|
762
747
|
magic_links: MagicLinksTable;
|
|
763
|
-
claim_tokens: ClaimTokensTable;
|
|
764
|
-
play_provisions: PlayProvisionsTable;
|
|
765
748
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
766
749
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
767
750
|
subgraph_gaps: SubgraphGapsTable;
|
|
@@ -770,7 +753,6 @@ interface Database {
|
|
|
770
753
|
processed_stripe_events: ProcessedStripeEventsTable;
|
|
771
754
|
account_spend_caps: AccountSpendCapsTable;
|
|
772
755
|
account_credits: AccountCreditsTable;
|
|
773
|
-
hosted_meter_days: HostedMeterDaysTable;
|
|
774
756
|
webhooks: WebhooksTable;
|
|
775
757
|
webhook_outbox: WebhookOutboxTable;
|
|
776
758
|
webhook_deliveries: WebhookDeliveriesTable;
|
|
@@ -801,6 +783,8 @@ interface Database {
|
|
|
801
783
|
coverage_segments: CoverageSegmentsTable;
|
|
802
784
|
stage_failures: StageFailuresTable;
|
|
803
785
|
archive_fetches: ArchiveFetchesTable;
|
|
786
|
+
waitlists: WaitlistsTable;
|
|
787
|
+
waitlist_signups: WaitlistSignupsTable;
|
|
804
788
|
}
|
|
805
789
|
interface ServiceHeartbeatsTable {
|
|
806
790
|
name: string;
|
|
@@ -829,15 +813,6 @@ interface AccountCreditsTable {
|
|
|
829
813
|
refill_last_at: Date | null;
|
|
830
814
|
updated_at: Generated<Date>;
|
|
831
815
|
}
|
|
832
|
-
/** Daily idempotency ledger for hosted subgraph running + storage meters. */
|
|
833
|
-
interface HostedMeterDaysTable {
|
|
834
|
-
day: string;
|
|
835
|
-
account_id: string;
|
|
836
|
-
subgraph_name: string;
|
|
837
|
-
kind: string;
|
|
838
|
-
usd_micros: string | number | bigint;
|
|
839
|
-
billed_at: Generated<Date>;
|
|
840
|
-
}
|
|
841
816
|
/**
|
|
842
817
|
* Archive fetch charge log (design-f089 gate). Append-only: one row per
|
|
843
818
|
* priced attempt to fetch a partition object. `usd_micros` is 0 for
|
|
@@ -940,6 +915,27 @@ interface TriggerEvaluatorStateTable {
|
|
|
940
915
|
last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
941
916
|
updated_at: Generated<Date>;
|
|
942
917
|
}
|
|
918
|
+
/** A public waitlist page: what it is for and whether it takes signups. */
|
|
919
|
+
interface WaitlistsTable {
|
|
920
|
+
slug: string;
|
|
921
|
+
title: string;
|
|
922
|
+
description: string;
|
|
923
|
+
url: string;
|
|
924
|
+
opened_at: Generated<Date>;
|
|
925
|
+
/** Null while the list is open. */
|
|
926
|
+
closed_at: Date | null;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* One signup per (list, contact, token). `answers` holds the list's own
|
|
930
|
+
* questions, validated per list by the API before insert.
|
|
931
|
+
*/
|
|
932
|
+
interface WaitlistSignupsTable {
|
|
933
|
+
id: Generated<string>;
|
|
934
|
+
list: string;
|
|
935
|
+
contact: string;
|
|
936
|
+
answers: Record<string, unknown>;
|
|
937
|
+
created_at: Generated<Date>;
|
|
938
|
+
}
|
|
943
939
|
declare const INSTANCE_NETWORKS: readonly ["mainnet", "testnet", "devnet"];
|
|
944
940
|
type InstanceNetwork = (typeof INSTANCE_NETWORKS)[number];
|
|
945
941
|
type InstanceRow = {
|
|
@@ -284,21 +284,6 @@ interface MagicLinksTable {
|
|
|
284
284
|
failed_attempts: Generated<number>;
|
|
285
285
|
created_at: Generated<Date>;
|
|
286
286
|
}
|
|
287
|
-
/** One-shot hashed tokens that attach a play ghost to an email at claim. */
|
|
288
|
-
interface ClaimTokensTable {
|
|
289
|
-
id: Generated<string>;
|
|
290
|
-
account_id: string;
|
|
291
|
-
token_hash: string;
|
|
292
|
-
created_at: Generated<Date>;
|
|
293
|
-
expires_at: Date;
|
|
294
|
-
used_at: Date | null;
|
|
295
|
-
}
|
|
296
|
-
/** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
|
|
297
|
-
interface PlayProvisionsTable {
|
|
298
|
-
ip_hash: string;
|
|
299
|
-
day: string;
|
|
300
|
-
count: Generated<number>;
|
|
301
|
-
}
|
|
302
287
|
interface SubgraphProcessingStatsTable {
|
|
303
288
|
id: Generated<string>;
|
|
304
289
|
subgraph_name: string;
|
|
@@ -760,8 +745,6 @@ interface Database {
|
|
|
760
745
|
accounts: AccountsTable;
|
|
761
746
|
sessions: SessionsTable;
|
|
762
747
|
magic_links: MagicLinksTable;
|
|
763
|
-
claim_tokens: ClaimTokensTable;
|
|
764
|
-
play_provisions: PlayProvisionsTable;
|
|
765
748
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
766
749
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
767
750
|
subgraph_gaps: SubgraphGapsTable;
|
|
@@ -770,7 +753,6 @@ interface Database {
|
|
|
770
753
|
processed_stripe_events: ProcessedStripeEventsTable;
|
|
771
754
|
account_spend_caps: AccountSpendCapsTable;
|
|
772
755
|
account_credits: AccountCreditsTable;
|
|
773
|
-
hosted_meter_days: HostedMeterDaysTable;
|
|
774
756
|
webhooks: WebhooksTable;
|
|
775
757
|
webhook_outbox: WebhookOutboxTable;
|
|
776
758
|
webhook_deliveries: WebhookDeliveriesTable;
|
|
@@ -801,6 +783,8 @@ interface Database {
|
|
|
801
783
|
coverage_segments: CoverageSegmentsTable;
|
|
802
784
|
stage_failures: StageFailuresTable;
|
|
803
785
|
archive_fetches: ArchiveFetchesTable;
|
|
786
|
+
waitlists: WaitlistsTable;
|
|
787
|
+
waitlist_signups: WaitlistSignupsTable;
|
|
804
788
|
}
|
|
805
789
|
interface ServiceHeartbeatsTable {
|
|
806
790
|
name: string;
|
|
@@ -829,15 +813,6 @@ interface AccountCreditsTable {
|
|
|
829
813
|
refill_last_at: Date | null;
|
|
830
814
|
updated_at: Generated<Date>;
|
|
831
815
|
}
|
|
832
|
-
/** Daily idempotency ledger for hosted subgraph running + storage meters. */
|
|
833
|
-
interface HostedMeterDaysTable {
|
|
834
|
-
day: string;
|
|
835
|
-
account_id: string;
|
|
836
|
-
subgraph_name: string;
|
|
837
|
-
kind: string;
|
|
838
|
-
usd_micros: string | number | bigint;
|
|
839
|
-
billed_at: Generated<Date>;
|
|
840
|
-
}
|
|
841
816
|
/**
|
|
842
817
|
* Archive fetch charge log (design-f089 gate). Append-only: one row per
|
|
843
818
|
* priced attempt to fetch a partition object. `usd_micros` is 0 for
|
|
@@ -940,6 +915,27 @@ interface TriggerEvaluatorStateTable {
|
|
|
940
915
|
last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
941
916
|
updated_at: Generated<Date>;
|
|
942
917
|
}
|
|
918
|
+
/** A public waitlist page: what it is for and whether it takes signups. */
|
|
919
|
+
interface WaitlistsTable {
|
|
920
|
+
slug: string;
|
|
921
|
+
title: string;
|
|
922
|
+
description: string;
|
|
923
|
+
url: string;
|
|
924
|
+
opened_at: Generated<Date>;
|
|
925
|
+
/** Null while the list is open. */
|
|
926
|
+
closed_at: Date | null;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* One signup per (list, contact, token). `answers` holds the list's own
|
|
930
|
+
* questions, validated per list by the API before insert.
|
|
931
|
+
*/
|
|
932
|
+
interface WaitlistSignupsTable {
|
|
933
|
+
id: Generated<string>;
|
|
934
|
+
list: string;
|
|
935
|
+
contact: string;
|
|
936
|
+
answers: Record<string, unknown>;
|
|
937
|
+
created_at: Generated<Date>;
|
|
938
|
+
}
|
|
943
939
|
interface Gap {
|
|
944
940
|
gapStart: number;
|
|
945
941
|
gapEnd: number;
|
|
@@ -991,7 +987,7 @@ type BrokenLink = {
|
|
|
991
987
|
* concern, and scanning eight million rows every cycle is not.
|
|
992
988
|
*/
|
|
993
989
|
declare function findBrokenLinks(db: Kysely<Database>, opts?: {
|
|
994
|
-
window?: number
|
|
990
|
+
window?: number | null
|
|
995
991
|
limit?: number
|
|
996
992
|
}): Promise<BrokenLink[]>;
|
|
997
993
|
export { findGaps, findBrokenLinks, countMissingBlocks, computeContiguousTip, checkChainDataIntegrity, Gap, ChainDataIntegrity, BrokenLink };
|
|
@@ -93,14 +93,15 @@ async function computeContiguousTip(db, fromHeight) {
|
|
|
93
93
|
return Number(rows[0]?.tip ?? fromHeight);
|
|
94
94
|
}
|
|
95
95
|
async function findBrokenLinks(db, opts = {}) {
|
|
96
|
-
const windowSize = opts.window
|
|
96
|
+
const windowSize = opts.window === undefined ? 1e4 : opts.window;
|
|
97
97
|
const limitClause = opts.limit ? sql`LIMIT ${opts.limit}` : sql``;
|
|
98
|
+
const windowClause = windowSize === null ? sql`` : sql`AND height > (SELECT MAX(height) - ${windowSize} FROM blocks WHERE canonical = true)`;
|
|
98
99
|
const { rows } = await sql`
|
|
99
100
|
WITH recent AS (
|
|
100
101
|
SELECT height, hash, parent_hash
|
|
101
102
|
FROM blocks
|
|
102
103
|
WHERE canonical = true
|
|
103
|
-
|
|
104
|
+
${windowClause}
|
|
104
105
|
)
|
|
105
106
|
SELECT b.height, b.hash, p.hash AS expected_parent, b.parent_hash AS stored_parent
|
|
106
107
|
FROM recent b
|
|
@@ -124,5 +125,5 @@ export {
|
|
|
124
125
|
checkChainDataIntegrity
|
|
125
126
|
};
|
|
126
127
|
|
|
127
|
-
//# debugId=
|
|
128
|
+
//# debugId=956E0AF2AC54818A64756E2164756E21
|
|
128
129
|
//# sourceMappingURL=integrity.js.map
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/db/queries/integrity.ts"],
|
|
4
4
|
"sourcesContent": [
|
|
5
|
-
"import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../types.ts\";\n\nexport interface Gap {\n\tgapStart: number;\n\tgapEnd: number;\n\tsize: number;\n}\n\nexport async function findGaps(\n\tdb: Kysely<Database>,\n\tlimit?: number,\n): Promise<Gap[]> {\n\tconst limitClause = limit ? sql`LIMIT ${limit}` : sql``;\n\tconst { rows } = await sql<{\n\t\tgap_start: string;\n\t\tgap_end: string;\n\t\tsize: string;\n\t}>`\n SELECT gap_start, gap_end, gap_end - gap_start + 1 AS size\n FROM (\n SELECT height + 1 AS gap_start, next_height - 1 AS gap_end\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n ) gaps\n ORDER BY gap_start\n ${limitClause}\n `.execute(db);\n\n\treturn rows.map((r) => ({\n\t\tgapStart: Number(r.gap_start),\n\t\tgapEnd: Number(r.gap_end),\n\t\tsize: Number(r.size),\n\t}));\n}\n\nexport async function countMissingBlocks(\n\tdb: Kysely<Database>,\n): Promise<number> {\n\tconst { rows } = await sql<{ total: string }>`\n SELECT COALESCE(SUM(next_height - height - 1), 0) AS total\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n `.execute(db);\n\n\treturn Number(rows[0]?.total ?? 0);\n}\n\nexport interface ChainDataIntegrity {\n\tok: boolean;\n\tmaxHeight: number;\n\tsampleHeight: number | null;\n\tsampleBlocks: number;\n\treason: string | null;\n}\n\n/**\n * Cheap sanity check that the `blocks` table actually holds the history its tip\n * implies. Catches a wrong/empty Postgres volume — e.g. a container recreated\n * against a fresh volume — being silently served: if the tip is high but a\n * window of blocks well below it is missing, the data isn't what the tip claims.\n *\n * Below `checkFloor` we can't distinguish a fresh install from an empty volume,\n * so we report ok — a genuinely new DB legitimately has little history. Two\n * indexed lookups, safe to run on every startup / health poll.\n */\nexport async function checkChainDataIntegrity(\n\tdb: Kysely<Database>,\n\topts?: { checkFloor?: number; lookback?: number },\n): Promise<ChainDataIntegrity> {\n\tconst checkFloor = opts?.checkFloor ?? 1_000_000;\n\tconst lookback = opts?.lookback ?? 500_000;\n\n\tconst { rows: tipRows } = await sql<{ max: string | null }>`\n\t\tSELECT MAX(height) AS max FROM blocks WHERE canonical = true\n\t`.execute(db);\n\tconst maxHeight = Number(tipRows[0]?.max ?? 0);\n\n\tif (maxHeight < checkFloor) {\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmaxHeight,\n\t\t\tsampleHeight: null,\n\t\t\tsampleBlocks: 0,\n\t\t\treason: null,\n\t\t};\n\t}\n\n\tconst sampleHeight = maxHeight - lookback;\n\tconst { rows: sampleRows } = await sql<{ n: string }>`\n\t\tSELECT count(*) AS n FROM blocks\n\t\tWHERE canonical = true AND height >= ${sampleHeight} AND height < ${sampleHeight + 1000}\n\t`.execute(db);\n\tconst sampleBlocks = Number(sampleRows[0]?.n ?? 0);\n\n\treturn {\n\t\tok: sampleBlocks > 0,\n\t\tmaxHeight,\n\t\tsampleHeight,\n\t\tsampleBlocks,\n\t\treason:\n\t\t\tsampleBlocks > 0\n\t\t\t\t? null\n\t\t\t\t: `tip is ${maxHeight} but no canonical blocks near ${sampleHeight} — wrong or empty volume?`,\n\t};\n}\n\nexport async function computeContiguousTip(\n\tdb: Kysely<Database>,\n\tfromHeight: number,\n): Promise<number> {\n\tconst { rows } = await sql<{ tip: string }>`\n SELECT COALESCE(MAX(height), ${fromHeight}) AS tip\n FROM (\n SELECT height, height - ROW_NUMBER() OVER (ORDER BY height) AS grp\n FROM blocks WHERE canonical = true AND height >= ${fromHeight}\n ) sub\n WHERE grp = (\n SELECT height - ROW_NUMBER() OVER (ORDER BY height)\n FROM blocks WHERE canonical = true AND height = ${fromHeight}\n )\n `.execute(db);\n\n\treturn Number(rows[0]?.tip ?? fromHeight);\n}\n\nexport type BrokenLink = {\n\theight: number;\n\thash: string;\n\texpectedParent: string;\n\tstoredParent: string;\n};\n\n/**\n * Canonical heights whose block does not descend from the canonical block\n * below it.\n *\n * `findGaps` asks \"is every height present?\", which a chain sitting on a losing\n * fork answers yes to. On 2026-07-30 a reorg at 8,663,166 was resolved the\n * wrong way: we adopted a block that mainnet orphaned, its true successors were\n * swept non-canonical, and later blocks were inserted canonical on top. Every\n * height existed. Nothing was \"missing\". The chain simply did not join up, and\n * no check asked that question — so we served a broken chain for seventeen\n * hours and only noticed because subgraphs stopped moving.\n *\n * Scoped to a recent window by default: an unlinked height is an active-repair\n * concern, and scanning eight million rows every cycle is not.\n */\nexport async function findBrokenLinks(\n\tdb: Kysely<Database>,\n\topts: { window?: number; limit?: number } = {},\n): Promise<BrokenLink[]> {\n\tconst windowSize = opts.window
|
|
5
|
+
"import { type Kysely, sql } from \"kysely\";\nimport type { Database } from \"../types.ts\";\n\nexport interface Gap {\n\tgapStart: number;\n\tgapEnd: number;\n\tsize: number;\n}\n\nexport async function findGaps(\n\tdb: Kysely<Database>,\n\tlimit?: number,\n): Promise<Gap[]> {\n\tconst limitClause = limit ? sql`LIMIT ${limit}` : sql``;\n\tconst { rows } = await sql<{\n\t\tgap_start: string;\n\t\tgap_end: string;\n\t\tsize: string;\n\t}>`\n SELECT gap_start, gap_end, gap_end - gap_start + 1 AS size\n FROM (\n SELECT height + 1 AS gap_start, next_height - 1 AS gap_end\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n ) gaps\n ORDER BY gap_start\n ${limitClause}\n `.execute(db);\n\n\treturn rows.map((r) => ({\n\t\tgapStart: Number(r.gap_start),\n\t\tgapEnd: Number(r.gap_end),\n\t\tsize: Number(r.size),\n\t}));\n}\n\nexport async function countMissingBlocks(\n\tdb: Kysely<Database>,\n): Promise<number> {\n\tconst { rows } = await sql<{ total: string }>`\n SELECT COALESCE(SUM(next_height - height - 1), 0) AS total\n FROM (\n SELECT height, LEAD(height) OVER (ORDER BY height) AS next_height\n FROM blocks WHERE canonical = true\n ) sub\n WHERE next_height - height > 1\n `.execute(db);\n\n\treturn Number(rows[0]?.total ?? 0);\n}\n\nexport interface ChainDataIntegrity {\n\tok: boolean;\n\tmaxHeight: number;\n\tsampleHeight: number | null;\n\tsampleBlocks: number;\n\treason: string | null;\n}\n\n/**\n * Cheap sanity check that the `blocks` table actually holds the history its tip\n * implies. Catches a wrong/empty Postgres volume — e.g. a container recreated\n * against a fresh volume — being silently served: if the tip is high but a\n * window of blocks well below it is missing, the data isn't what the tip claims.\n *\n * Below `checkFloor` we can't distinguish a fresh install from an empty volume,\n * so we report ok — a genuinely new DB legitimately has little history. Two\n * indexed lookups, safe to run on every startup / health poll.\n */\nexport async function checkChainDataIntegrity(\n\tdb: Kysely<Database>,\n\topts?: { checkFloor?: number; lookback?: number },\n): Promise<ChainDataIntegrity> {\n\tconst checkFloor = opts?.checkFloor ?? 1_000_000;\n\tconst lookback = opts?.lookback ?? 500_000;\n\n\tconst { rows: tipRows } = await sql<{ max: string | null }>`\n\t\tSELECT MAX(height) AS max FROM blocks WHERE canonical = true\n\t`.execute(db);\n\tconst maxHeight = Number(tipRows[0]?.max ?? 0);\n\n\tif (maxHeight < checkFloor) {\n\t\treturn {\n\t\t\tok: true,\n\t\t\tmaxHeight,\n\t\t\tsampleHeight: null,\n\t\t\tsampleBlocks: 0,\n\t\t\treason: null,\n\t\t};\n\t}\n\n\tconst sampleHeight = maxHeight - lookback;\n\tconst { rows: sampleRows } = await sql<{ n: string }>`\n\t\tSELECT count(*) AS n FROM blocks\n\t\tWHERE canonical = true AND height >= ${sampleHeight} AND height < ${sampleHeight + 1000}\n\t`.execute(db);\n\tconst sampleBlocks = Number(sampleRows[0]?.n ?? 0);\n\n\treturn {\n\t\tok: sampleBlocks > 0,\n\t\tmaxHeight,\n\t\tsampleHeight,\n\t\tsampleBlocks,\n\t\treason:\n\t\t\tsampleBlocks > 0\n\t\t\t\t? null\n\t\t\t\t: `tip is ${maxHeight} but no canonical blocks near ${sampleHeight} — wrong or empty volume?`,\n\t};\n}\n\nexport async function computeContiguousTip(\n\tdb: Kysely<Database>,\n\tfromHeight: number,\n): Promise<number> {\n\tconst { rows } = await sql<{ tip: string }>`\n SELECT COALESCE(MAX(height), ${fromHeight}) AS tip\n FROM (\n SELECT height, height - ROW_NUMBER() OVER (ORDER BY height) AS grp\n FROM blocks WHERE canonical = true AND height >= ${fromHeight}\n ) sub\n WHERE grp = (\n SELECT height - ROW_NUMBER() OVER (ORDER BY height)\n FROM blocks WHERE canonical = true AND height = ${fromHeight}\n )\n `.execute(db);\n\n\treturn Number(rows[0]?.tip ?? fromHeight);\n}\n\nexport type BrokenLink = {\n\theight: number;\n\thash: string;\n\texpectedParent: string;\n\tstoredParent: string;\n};\n\n/**\n * Canonical heights whose block does not descend from the canonical block\n * below it.\n *\n * `findGaps` asks \"is every height present?\", which a chain sitting on a losing\n * fork answers yes to. On 2026-07-30 a reorg at 8,663,166 was resolved the\n * wrong way: we adopted a block that mainnet orphaned, its true successors were\n * swept non-canonical, and later blocks were inserted canonical on top. Every\n * height existed. Nothing was \"missing\". The chain simply did not join up, and\n * no check asked that question — so we served a broken chain for seventeen\n * hours and only noticed because subgraphs stopped moving.\n *\n * Scoped to a recent window by default: an unlinked height is an active-repair\n * concern, and scanning eight million rows every cycle is not.\n */\nexport async function findBrokenLinks(\n\tdb: Kysely<Database>,\n\t/** Heights below the tip to scan; `null` scans the whole chain. */\n\topts: { window?: number | null; limit?: number } = {},\n): Promise<BrokenLink[]> {\n\tconst windowSize = opts.window === undefined ? 10_000 : opts.window;\n\tconst limitClause = opts.limit ? sql`LIMIT ${opts.limit}` : sql``;\n\tconst windowClause =\n\t\twindowSize === null\n\t\t\t? sql``\n\t\t\t: sql`AND height > (SELECT MAX(height) - ${windowSize} FROM blocks WHERE canonical = true)`;\n\tconst { rows } = await sql<{\n\t\theight: string | number;\n\t\thash: string;\n\t\texpected_parent: string;\n\t\tstored_parent: string;\n\t}>`\n\t\tWITH recent AS (\n\t\t\tSELECT height, hash, parent_hash\n\t\t\tFROM blocks\n\t\t\tWHERE canonical = true\n\t\t\t\t${windowClause}\n\t\t)\n\t\tSELECT b.height, b.hash, p.hash AS expected_parent, b.parent_hash AS stored_parent\n\t\tFROM recent b\n\t\tJOIN recent p ON p.height = b.height - 1\n\t\tWHERE b.parent_hash <> p.hash\n\t\tORDER BY b.height\n\t\t${limitClause}\n\t`.execute(db);\n\treturn rows.map((r) => ({\n\t\theight: Number(r.height),\n\t\thash: r.hash,\n\t\texpectedParent: r.expected_parent,\n\t\tstoredParent: r.stored_parent,\n\t}));\n}\n"
|
|
6
6
|
],
|
|
7
|
-
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AASA,eAAsB,QAAQ,CAC7B,IACA,OACiB;AAAA,EACjB,MAAM,cAAc,QAAQ,YAAY,UAAU;AAAA,EAClD,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAelB;AAAA,IACF,QAAQ,EAAE;AAAA,EAEb,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACvB,UAAU,OAAO,EAAE,SAAS;AAAA,IAC5B,QAAQ,OAAO,EAAE,OAAO;AAAA,IACxB,MAAM,OAAO,EAAE,IAAI;AAAA,EACpB,EAAE;AAAA;AAGH,eAAsB,kBAAkB,CACvC,IACkB;AAAA,EAClB,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOpB,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,IAAI,SAAS,CAAC;AAAA;AAqBlC,eAAsB,uBAAuB,CAC5C,IACA,MAC8B;AAAA,EAC9B,MAAM,aAAa,MAAM,cAAc;AAAA,EACvC,MAAM,WAAW,MAAM,YAAY;AAAA,EAEnC,QAAQ,MAAM,YAAY,MAAM;AAAA;AAAA,GAE9B,QAAQ,EAAE;AAAA,EACZ,MAAM,YAAY,OAAO,QAAQ,IAAI,OAAO,CAAC;AAAA,EAE7C,IAAI,YAAY,YAAY;AAAA,IAC3B,OAAO;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,MACA,cAAc;AAAA,MACd,cAAc;AAAA,MACd,QAAQ;AAAA,IACT;AAAA,EACD;AAAA,EAEA,MAAM,eAAe,YAAY;AAAA,EACjC,QAAQ,MAAM,eAAe,MAAM;AAAA;AAAA,yCAEK,6BAA6B,eAAe;AAAA,GAClF,QAAQ,EAAE;AAAA,EACZ,MAAM,eAAe,OAAO,WAAW,IAAI,KAAK,CAAC;AAAA,EAEjD,OAAO;AAAA,IACN,IAAI,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,QACC,eAAe,IACZ,OACA,UAAU,0CAA0C;AAAA,EACzD;AAAA;AAGD,eAAsB,oBAAoB,CACzC,IACA,YACkB;AAAA,EAClB,QAAQ,SAAS,MAAM;AAAA,mCACW;AAAA;AAAA;AAAA,yDAGsB;AAAA;AAAA;AAAA;AAAA,wDAID;AAAA;AAAA,IAEpD,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,IAAI,OAAO,UAAU;AAAA;AAyBzC,eAAsB,eAAe,CACpC,
|
|
8
|
-
"debugId": "
|
|
7
|
+
"mappings": ";;;;;;;;;;;;;;;;;AAAA;AASA,eAAsB,QAAQ,CAC7B,IACA,OACiB;AAAA,EACjB,MAAM,cAAc,QAAQ,YAAY,UAAU;AAAA,EAClD,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MAelB;AAAA,IACF,QAAQ,EAAE;AAAA,EAEb,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACvB,UAAU,OAAO,EAAE,SAAS;AAAA,IAC5B,QAAQ,OAAO,EAAE,OAAO;AAAA,IACxB,MAAM,OAAO,EAAE,IAAI;AAAA,EACpB,EAAE;AAAA;AAGH,eAAsB,kBAAkB,CACvC,IACkB;AAAA,EAClB,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOpB,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,IAAI,SAAS,CAAC;AAAA;AAqBlC,eAAsB,uBAAuB,CAC5C,IACA,MAC8B;AAAA,EAC9B,MAAM,aAAa,MAAM,cAAc;AAAA,EACvC,MAAM,WAAW,MAAM,YAAY;AAAA,EAEnC,QAAQ,MAAM,YAAY,MAAM;AAAA;AAAA,GAE9B,QAAQ,EAAE;AAAA,EACZ,MAAM,YAAY,OAAO,QAAQ,IAAI,OAAO,CAAC;AAAA,EAE7C,IAAI,YAAY,YAAY;AAAA,IAC3B,OAAO;AAAA,MACN,IAAI;AAAA,MACJ;AAAA,MACA,cAAc;AAAA,MACd,cAAc;AAAA,MACd,QAAQ;AAAA,IACT;AAAA,EACD;AAAA,EAEA,MAAM,eAAe,YAAY;AAAA,EACjC,QAAQ,MAAM,eAAe,MAAM;AAAA;AAAA,yCAEK,6BAA6B,eAAe;AAAA,GAClF,QAAQ,EAAE;AAAA,EACZ,MAAM,eAAe,OAAO,WAAW,IAAI,KAAK,CAAC;AAAA,EAEjD,OAAO;AAAA,IACN,IAAI,eAAe;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,IACA,QACC,eAAe,IACZ,OACA,UAAU,0CAA0C;AAAA,EACzD;AAAA;AAGD,eAAsB,oBAAoB,CACzC,IACA,YACkB;AAAA,EAClB,QAAQ,SAAS,MAAM;AAAA,mCACW;AAAA;AAAA;AAAA,yDAGsB;AAAA;AAAA;AAAA;AAAA,wDAID;AAAA;AAAA,IAEpD,QAAQ,EAAE;AAAA,EAEb,OAAO,OAAO,KAAK,IAAI,OAAO,UAAU;AAAA;AAyBzC,eAAsB,eAAe,CACpC,IAEA,OAAmD,CAAC,GAC5B;AAAA,EACxB,MAAM,aAAa,KAAK,WAAW,YAAY,MAAS,KAAK;AAAA,EAC7D,MAAM,cAAc,KAAK,QAAQ,YAAY,KAAK,UAAU;AAAA,EAC5D,MAAM,eACL,eAAe,OACZ,QACA,yCAAyC;AAAA,EAC7C,QAAQ,SAAS,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA,MAUlB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOF;AAAA,GACD,QAAQ,EAAE;AAAA,EACZ,OAAO,KAAK,IAAI,CAAC,OAAO;AAAA,IACvB,QAAQ,OAAO,EAAE,MAAM;AAAA,IACvB,MAAM,EAAE;AAAA,IACR,gBAAgB,EAAE;AAAA,IAClB,cAAc,EAAE;AAAA,EACjB,EAAE;AAAA;",
|
|
8
|
+
"debugId": "956E0AF2AC54818A64756E2164756E21",
|
|
9
9
|
"names": []
|
|
10
10
|
}
|
|
@@ -284,21 +284,6 @@ interface MagicLinksTable {
|
|
|
284
284
|
failed_attempts: Generated<number>;
|
|
285
285
|
created_at: Generated<Date>;
|
|
286
286
|
}
|
|
287
|
-
/** One-shot hashed tokens that attach a play ghost to an email at claim. */
|
|
288
|
-
interface ClaimTokensTable {
|
|
289
|
-
id: Generated<string>;
|
|
290
|
-
account_id: string;
|
|
291
|
-
token_hash: string;
|
|
292
|
-
created_at: Generated<Date>;
|
|
293
|
-
expires_at: Date;
|
|
294
|
-
used_at: Date | null;
|
|
295
|
-
}
|
|
296
|
-
/** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
|
|
297
|
-
interface PlayProvisionsTable {
|
|
298
|
-
ip_hash: string;
|
|
299
|
-
day: string;
|
|
300
|
-
count: Generated<number>;
|
|
301
|
-
}
|
|
302
287
|
interface SubgraphProcessingStatsTable {
|
|
303
288
|
id: Generated<string>;
|
|
304
289
|
subgraph_name: string;
|
|
@@ -760,8 +745,6 @@ interface Database {
|
|
|
760
745
|
accounts: AccountsTable;
|
|
761
746
|
sessions: SessionsTable;
|
|
762
747
|
magic_links: MagicLinksTable;
|
|
763
|
-
claim_tokens: ClaimTokensTable;
|
|
764
|
-
play_provisions: PlayProvisionsTable;
|
|
765
748
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
766
749
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
767
750
|
subgraph_gaps: SubgraphGapsTable;
|
|
@@ -770,7 +753,6 @@ interface Database {
|
|
|
770
753
|
processed_stripe_events: ProcessedStripeEventsTable;
|
|
771
754
|
account_spend_caps: AccountSpendCapsTable;
|
|
772
755
|
account_credits: AccountCreditsTable;
|
|
773
|
-
hosted_meter_days: HostedMeterDaysTable;
|
|
774
756
|
webhooks: WebhooksTable;
|
|
775
757
|
webhook_outbox: WebhookOutboxTable;
|
|
776
758
|
webhook_deliveries: WebhookDeliveriesTable;
|
|
@@ -801,6 +783,8 @@ interface Database {
|
|
|
801
783
|
coverage_segments: CoverageSegmentsTable;
|
|
802
784
|
stage_failures: StageFailuresTable;
|
|
803
785
|
archive_fetches: ArchiveFetchesTable;
|
|
786
|
+
waitlists: WaitlistsTable;
|
|
787
|
+
waitlist_signups: WaitlistSignupsTable;
|
|
804
788
|
}
|
|
805
789
|
interface ServiceHeartbeatsTable {
|
|
806
790
|
name: string;
|
|
@@ -829,15 +813,6 @@ interface AccountCreditsTable {
|
|
|
829
813
|
refill_last_at: Date | null;
|
|
830
814
|
updated_at: Generated<Date>;
|
|
831
815
|
}
|
|
832
|
-
/** Daily idempotency ledger for hosted subgraph running + storage meters. */
|
|
833
|
-
interface HostedMeterDaysTable {
|
|
834
|
-
day: string;
|
|
835
|
-
account_id: string;
|
|
836
|
-
subgraph_name: string;
|
|
837
|
-
kind: string;
|
|
838
|
-
usd_micros: string | number | bigint;
|
|
839
|
-
billed_at: Generated<Date>;
|
|
840
|
-
}
|
|
841
816
|
/**
|
|
842
817
|
* Archive fetch charge log (design-f089 gate). Append-only: one row per
|
|
843
818
|
* priced attempt to fetch a partition object. `usd_micros` is 0 for
|
|
@@ -940,6 +915,27 @@ interface TriggerEvaluatorStateTable {
|
|
|
940
915
|
last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
941
916
|
updated_at: Generated<Date>;
|
|
942
917
|
}
|
|
918
|
+
/** A public waitlist page: what it is for and whether it takes signups. */
|
|
919
|
+
interface WaitlistsTable {
|
|
920
|
+
slug: string;
|
|
921
|
+
title: string;
|
|
922
|
+
description: string;
|
|
923
|
+
url: string;
|
|
924
|
+
opened_at: Generated<Date>;
|
|
925
|
+
/** Null while the list is open. */
|
|
926
|
+
closed_at: Date | null;
|
|
927
|
+
}
|
|
928
|
+
/**
|
|
929
|
+
* One signup per (list, contact, token). `answers` holds the list's own
|
|
930
|
+
* questions, validated per list by the API before insert.
|
|
931
|
+
*/
|
|
932
|
+
interface WaitlistSignupsTable {
|
|
933
|
+
id: Generated<string>;
|
|
934
|
+
list: string;
|
|
935
|
+
contact: string;
|
|
936
|
+
answers: Record<string, unknown>;
|
|
937
|
+
created_at: Generated<Date>;
|
|
938
|
+
}
|
|
943
939
|
interface GapRange {
|
|
944
940
|
start: number;
|
|
945
941
|
end: number;
|
|
@@ -284,21 +284,6 @@ interface MagicLinksTable {
|
|
|
284
284
|
failed_attempts: Generated<number>;
|
|
285
285
|
created_at: Generated<Date>;
|
|
286
286
|
}
|
|
287
|
-
/** One-shot hashed tokens that attach a play ghost to an email at claim. */
|
|
288
|
-
interface ClaimTokensTable {
|
|
289
|
-
id: Generated<string>;
|
|
290
|
-
account_id: string;
|
|
291
|
-
token_hash: string;
|
|
292
|
-
created_at: Generated<Date>;
|
|
293
|
-
expires_at: Date;
|
|
294
|
-
used_at: Date | null;
|
|
295
|
-
}
|
|
296
|
-
/** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
|
|
297
|
-
interface PlayProvisionsTable {
|
|
298
|
-
ip_hash: string;
|
|
299
|
-
day: string;
|
|
300
|
-
count: Generated<number>;
|
|
301
|
-
}
|
|
302
287
|
interface SubgraphProcessingStatsTable {
|
|
303
288
|
id: Generated<string>;
|
|
304
289
|
subgraph_name: string;
|
|
@@ -760,8 +745,6 @@ interface Database {
|
|
|
760
745
|
accounts: AccountsTable;
|
|
761
746
|
sessions: SessionsTable;
|
|
762
747
|
magic_links: MagicLinksTable;
|
|
763
|
-
claim_tokens: ClaimTokensTable;
|
|
764
|
-
play_provisions: PlayProvisionsTable;
|
|
765
748
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
766
749
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
767
750
|
subgraph_gaps: SubgraphGapsTable;
|
|
@@ -770,7 +753,6 @@ interface Database {
|
|
|
770
753
|
processed_stripe_events: ProcessedStripeEventsTable;
|
|
771
754
|
account_spend_caps: AccountSpendCapsTable;
|
|
772
755
|
account_credits: AccountCreditsTable;
|
|
773
|
-
hosted_meter_days: HostedMeterDaysTable;
|
|
774
756
|
webhooks: WebhooksTable;
|
|
775
757
|
webhook_outbox: WebhookOutboxTable;
|
|
776
758
|
webhook_deliveries: WebhookDeliveriesTable;
|
|
@@ -801,6 +783,8 @@ interface Database {
|
|
|
801
783
|
coverage_segments: CoverageSegmentsTable;
|
|
802
784
|
stage_failures: StageFailuresTable;
|
|
803
785
|
archive_fetches: ArchiveFetchesTable;
|
|
786
|
+
waitlists: WaitlistsTable;
|
|
787
|
+
waitlist_signups: WaitlistSignupsTable;
|
|
804
788
|
}
|
|
805
789
|
interface ServiceHeartbeatsTable {
|
|
806
790
|
name: string;
|
|
@@ -829,15 +813,6 @@ interface AccountCreditsTable {
|
|
|
829
813
|
refill_last_at: Date | null;
|
|
830
814
|
updated_at: Generated<Date>;
|
|
831
815
|
}
|
|
832
|
-
/** Daily idempotency ledger for hosted subgraph running + storage meters. */
|
|
833
|
-
interface HostedMeterDaysTable {
|
|
834
|
-
day: string;
|
|
835
|
-
account_id: string;
|
|
836
|
-
subgraph_name: string;
|
|
837
|
-
kind: string;
|
|
838
|
-
usd_micros: string | number | bigint;
|
|
839
|
-
billed_at: Generated<Date>;
|
|
840
|
-
}
|
|
841
816
|
/**
|
|
842
817
|
* Archive fetch charge log (design-f089 gate). Append-only: one row per
|
|
843
818
|
* priced attempt to fetch a partition object. `usd_micros` is 0 for
|
|
@@ -941,6 +916,27 @@ interface TriggerEvaluatorStateTable {
|
|
|
941
916
|
last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
942
917
|
updated_at: Generated<Date>;
|
|
943
918
|
}
|
|
919
|
+
/** A public waitlist page: what it is for and whether it takes signups. */
|
|
920
|
+
interface WaitlistsTable {
|
|
921
|
+
slug: string;
|
|
922
|
+
title: string;
|
|
923
|
+
description: string;
|
|
924
|
+
url: string;
|
|
925
|
+
opened_at: Generated<Date>;
|
|
926
|
+
/** Null while the list is open. */
|
|
927
|
+
closed_at: Date | null;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* One signup per (list, contact, token). `answers` holds the list's own
|
|
931
|
+
* questions, validated per list by the API before insert.
|
|
932
|
+
*/
|
|
933
|
+
interface WaitlistSignupsTable {
|
|
934
|
+
id: Generated<string>;
|
|
935
|
+
list: string;
|
|
936
|
+
contact: string;
|
|
937
|
+
answers: Record<string, unknown>;
|
|
938
|
+
created_at: Generated<Date>;
|
|
939
|
+
}
|
|
944
940
|
declare function isActiveSubgraphOperationConflict(err: unknown): boolean;
|
|
945
941
|
declare function createSubgraphOperation(db: Kysely<Database>, data: {
|
|
946
942
|
subgraphId: string
|
|
@@ -284,21 +284,6 @@ interface MagicLinksTable {
|
|
|
284
284
|
failed_attempts: Generated<number>;
|
|
285
285
|
created_at: Generated<Date>;
|
|
286
286
|
}
|
|
287
|
-
/** One-shot hashed tokens that attach a play ghost to an email at claim. */
|
|
288
|
-
interface ClaimTokensTable {
|
|
289
|
-
id: Generated<string>;
|
|
290
|
-
account_id: string;
|
|
291
|
-
token_hash: string;
|
|
292
|
-
created_at: Generated<Date>;
|
|
293
|
-
expires_at: Date;
|
|
294
|
-
used_at: Date | null;
|
|
295
|
-
}
|
|
296
|
-
/** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
|
|
297
|
-
interface PlayProvisionsTable {
|
|
298
|
-
ip_hash: string;
|
|
299
|
-
day: string;
|
|
300
|
-
count: Generated<number>;
|
|
301
|
-
}
|
|
302
287
|
interface SubgraphProcessingStatsTable {
|
|
303
288
|
id: Generated<string>;
|
|
304
289
|
subgraph_name: string;
|
|
@@ -760,8 +745,6 @@ interface Database {
|
|
|
760
745
|
accounts: AccountsTable;
|
|
761
746
|
sessions: SessionsTable;
|
|
762
747
|
magic_links: MagicLinksTable;
|
|
763
|
-
claim_tokens: ClaimTokensTable;
|
|
764
|
-
play_provisions: PlayProvisionsTable;
|
|
765
748
|
subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
|
|
766
749
|
subgraph_processing_stats: SubgraphProcessingStatsTable;
|
|
767
750
|
subgraph_gaps: SubgraphGapsTable;
|
|
@@ -770,7 +753,6 @@ interface Database {
|
|
|
770
753
|
processed_stripe_events: ProcessedStripeEventsTable;
|
|
771
754
|
account_spend_caps: AccountSpendCapsTable;
|
|
772
755
|
account_credits: AccountCreditsTable;
|
|
773
|
-
hosted_meter_days: HostedMeterDaysTable;
|
|
774
756
|
webhooks: WebhooksTable;
|
|
775
757
|
webhook_outbox: WebhookOutboxTable;
|
|
776
758
|
webhook_deliveries: WebhookDeliveriesTable;
|
|
@@ -801,6 +783,8 @@ interface Database {
|
|
|
801
783
|
coverage_segments: CoverageSegmentsTable;
|
|
802
784
|
stage_failures: StageFailuresTable;
|
|
803
785
|
archive_fetches: ArchiveFetchesTable;
|
|
786
|
+
waitlists: WaitlistsTable;
|
|
787
|
+
waitlist_signups: WaitlistSignupsTable;
|
|
804
788
|
}
|
|
805
789
|
interface ServiceHeartbeatsTable {
|
|
806
790
|
name: string;
|
|
@@ -829,15 +813,6 @@ interface AccountCreditsTable {
|
|
|
829
813
|
refill_last_at: Date | null;
|
|
830
814
|
updated_at: Generated<Date>;
|
|
831
815
|
}
|
|
832
|
-
/** Daily idempotency ledger for hosted subgraph running + storage meters. */
|
|
833
|
-
interface HostedMeterDaysTable {
|
|
834
|
-
day: string;
|
|
835
|
-
account_id: string;
|
|
836
|
-
subgraph_name: string;
|
|
837
|
-
kind: string;
|
|
838
|
-
usd_micros: string | number | bigint;
|
|
839
|
-
billed_at: Generated<Date>;
|
|
840
|
-
}
|
|
841
816
|
/**
|
|
842
817
|
* Archive fetch charge log (design-f089 gate). Append-only: one row per
|
|
843
818
|
* priced attempt to fetch a partition object. `usd_micros` is 0 for
|
|
@@ -941,6 +916,27 @@ interface TriggerEvaluatorStateTable {
|
|
|
941
916
|
last_settlement_scan_at: ColumnType<Date | null, Date | null | undefined, Date | null>;
|
|
942
917
|
updated_at: Generated<Date>;
|
|
943
918
|
}
|
|
919
|
+
/** A public waitlist page: what it is for and whether it takes signups. */
|
|
920
|
+
interface WaitlistsTable {
|
|
921
|
+
slug: string;
|
|
922
|
+
title: string;
|
|
923
|
+
description: string;
|
|
924
|
+
url: string;
|
|
925
|
+
opened_at: Generated<Date>;
|
|
926
|
+
/** Null while the list is open. */
|
|
927
|
+
closed_at: Date | null;
|
|
928
|
+
}
|
|
929
|
+
/**
|
|
930
|
+
* One signup per (list, contact, token). `answers` holds the list's own
|
|
931
|
+
* questions, validated per list by the API before insert.
|
|
932
|
+
*/
|
|
933
|
+
interface WaitlistSignupsTable {
|
|
934
|
+
id: Generated<string>;
|
|
935
|
+
list: string;
|
|
936
|
+
contact: string;
|
|
937
|
+
answers: Record<string, unknown>;
|
|
938
|
+
created_at: Generated<Date>;
|
|
939
|
+
}
|
|
944
940
|
/**
|
|
945
941
|
* Convert a subgraph name to its PostgreSQL schema name (legacy form).
|
|
946
942
|
* Pre shared-rip every tenant DB had its own schema namespace so disambiguation
|
|
@@ -972,11 +968,6 @@ declare function registerSubgraph(db: Kysely<Database>, data: {
|
|
|
972
968
|
declare function getSubgraph(db: Kysely<Database>, name: string, accountId?: string): Promise<Subgraph | null>;
|
|
973
969
|
declare function listSubgraphs(db: Kysely<Database>, accountId?: string): Promise<Subgraph[]>;
|
|
974
970
|
declare function updateSubgraphStatus(db: Kysely<Database>, name: string, status: string, lastProcessedBlock?: number): Promise<void>;
|
|
975
|
-
/** Account-scoped pause. Only active/reindexing rows flip; name-only
|
|
976
|
-
* {@link updateSubgraphStatus} is unsafe on platform (name, account_id). */
|
|
977
|
-
declare function pauseSubgraph(db: Kysely<Database>, name: string, accountId: string): Promise<void>;
|
|
978
|
-
/** Resume every paused subgraph for an account (claim / Stripe top-up). */
|
|
979
|
-
declare function resumePausedSubgraphs(db: Kysely<Database>, accountId: string): Promise<number>;
|
|
980
971
|
/** Live-walk ERROR write (f069): a block whose handlers ALL failed
|
|
981
972
|
* (`processed === 0 && errors > 0`, `block-processor.ts`'s `applyProgress`)
|
|
982
973
|
* is stamped 'error' and its height is recorded as processed, same as
|
|
@@ -1052,4 +1043,4 @@ declare function recordLiveProgress(db: Kysely<Database>, name: string, lastProc
|
|
|
1052
1043
|
* forward write between the rewind and the fork block's reprocess. Do not
|
|
1053
1044
|
* call this from the live catch-up path. */
|
|
1054
1045
|
declare function rewindLiveProgress(db: Kysely<Database>, name: string, lastProcessedBlock: number): Promise<void>;
|
|
1055
|
-
export { updateSubgraphStatus, updateSubgraphHandlerPath, updateSubgraphExpiry, rewindLiveProgress,
|
|
1046
|
+
export { updateSubgraphStatus, updateSubgraphHandlerPath, updateSubgraphExpiry, rewindLiveProgress, registerSubgraph, recordSubgraphProcessed, recordLiveProgress, recordLiveError, pgSchemaNameFor, pgSchemaName, localSubgraphAccountId, listSubgraphs, getSubgraph, deleteSubgraph };
|
|
@@ -139,13 +139,6 @@ async function updateSubgraphStatus(db, name, status, lastProcessedBlock) {
|
|
|
139
139
|
updated_at: new Date
|
|
140
140
|
}).where("name", "=", name).execute();
|
|
141
141
|
}
|
|
142
|
-
async function pauseSubgraph(db, name, accountId) {
|
|
143
|
-
await db.updateTable("subgraphs").set({ status: "paused", updated_at: new Date }).where("name", "=", name).where("account_id", "=", accountId).where("status", "in", ["active", "reindexing"]).execute();
|
|
144
|
-
}
|
|
145
|
-
async function resumePausedSubgraphs(db, accountId) {
|
|
146
|
-
const res = await db.updateTable("subgraphs").set({ status: "active", updated_at: new Date }).where("account_id", "=", accountId).where("status", "=", "paused").executeTakeFirst();
|
|
147
|
-
return Number(res.numUpdatedRows ?? 0);
|
|
148
|
-
}
|
|
149
142
|
async function recordLiveError(db, name, blockHeight) {
|
|
150
143
|
const result = await db.updateTable("subgraphs").set({
|
|
151
144
|
status: "error",
|
|
@@ -208,19 +201,17 @@ export {
|
|
|
208
201
|
updateSubgraphHandlerPath,
|
|
209
202
|
updateSubgraphExpiry,
|
|
210
203
|
rewindLiveProgress,
|
|
211
|
-
resumePausedSubgraphs,
|
|
212
204
|
registerSubgraph,
|
|
213
205
|
recordSubgraphProcessed,
|
|
214
206
|
recordLiveProgress,
|
|
215
207
|
recordLiveError,
|
|
216
208
|
pgSchemaNameFor,
|
|
217
209
|
pgSchemaName,
|
|
218
|
-
pauseSubgraph,
|
|
219
210
|
localSubgraphAccountId,
|
|
220
211
|
listSubgraphs,
|
|
221
212
|
getSubgraph,
|
|
222
213
|
deleteSubgraph
|
|
223
214
|
};
|
|
224
215
|
|
|
225
|
-
//# debugId=
|
|
216
|
+
//# debugId=EEA4A11D75AFDFDE64756E2164756E21
|
|
226
217
|
//# sourceMappingURL=subgraphs.js.map
|