@secondlayer/shared 11.4.1 → 11.4.3

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.
@@ -263,6 +263,21 @@ interface MagicLinksTable {
263
263
  failed_attempts: Generated<number>;
264
264
  created_at: Generated<Date>;
265
265
  }
266
+ /** One-shot hashed tokens that attach a play ghost to an email at claim. */
267
+ interface ClaimTokensTable {
268
+ id: Generated<string>;
269
+ account_id: string;
270
+ token_hash: string;
271
+ created_at: Generated<Date>;
272
+ expires_at: Date;
273
+ used_at: Date | null;
274
+ }
275
+ /** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
276
+ interface PlayProvisionsTable {
277
+ ip_hash: string;
278
+ day: string;
279
+ count: Generated<number>;
280
+ }
266
281
  interface SubgraphProcessingStatsTable {
267
282
  id: Generated<string>;
268
283
  subgraph_name: string;
@@ -761,6 +776,8 @@ interface Database {
761
776
  accounts: AccountsTable;
762
777
  sessions: SessionsTable;
763
778
  magic_links: MagicLinksTable;
779
+ claim_tokens: ClaimTokensTable;
780
+ play_provisions: PlayProvisionsTable;
764
781
  subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
765
782
  subgraph_processing_stats: SubgraphProcessingStatsTable;
766
783
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
@@ -770,6 +787,7 @@ interface Database {
770
787
  processed_stripe_events: ProcessedStripeEventsTable;
771
788
  account_spend_caps: AccountSpendCapsTable;
772
789
  account_credits: AccountCreditsTable;
790
+ hosted_meter_days: HostedMeterDaysTable;
773
791
  subscriptions: SubscriptionsTable;
774
792
  subscription_outbox: SubscriptionOutboxTable;
775
793
  subscription_deliveries: SubscriptionDeliveriesTable;
@@ -869,6 +887,15 @@ interface AccountCreditsTable {
869
887
  refill_last_at: Date | null;
870
888
  updated_at: Generated<Date>;
871
889
  }
890
+ /** Daily idempotency ledger for hosted subgraph running + storage meters. */
891
+ interface HostedMeterDaysTable {
892
+ day: string;
893
+ account_id: string;
894
+ subgraph_name: string;
895
+ kind: string;
896
+ usd_micros: string | number | bigint;
897
+ billed_at: Generated<Date>;
898
+ }
872
899
  /**
873
900
  * Archive fetch charge log (design-f089 gate). Append-only: one row per
874
901
  * priced attempt to fetch a partition object. `usd_micros` is 0 for
@@ -263,6 +263,21 @@ interface MagicLinksTable {
263
263
  failed_attempts: Generated<number>;
264
264
  created_at: Generated<Date>;
265
265
  }
266
+ /** One-shot hashed tokens that attach a play ghost to an email at claim. */
267
+ interface ClaimTokensTable {
268
+ id: Generated<string>;
269
+ account_id: string;
270
+ token_hash: string;
271
+ created_at: Generated<Date>;
272
+ expires_at: Date;
273
+ used_at: Date | null;
274
+ }
275
+ /** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
276
+ interface PlayProvisionsTable {
277
+ ip_hash: string;
278
+ day: string;
279
+ count: Generated<number>;
280
+ }
266
281
  interface SubgraphProcessingStatsTable {
267
282
  id: Generated<string>;
268
283
  subgraph_name: string;
@@ -761,6 +776,8 @@ interface Database {
761
776
  accounts: AccountsTable;
762
777
  sessions: SessionsTable;
763
778
  magic_links: MagicLinksTable;
779
+ claim_tokens: ClaimTokensTable;
780
+ play_provisions: PlayProvisionsTable;
764
781
  subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
765
782
  subgraph_processing_stats: SubgraphProcessingStatsTable;
766
783
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
@@ -770,6 +787,7 @@ interface Database {
770
787
  processed_stripe_events: ProcessedStripeEventsTable;
771
788
  account_spend_caps: AccountSpendCapsTable;
772
789
  account_credits: AccountCreditsTable;
790
+ hosted_meter_days: HostedMeterDaysTable;
773
791
  subscriptions: SubscriptionsTable;
774
792
  subscription_outbox: SubscriptionOutboxTable;
775
793
  subscription_deliveries: SubscriptionDeliveriesTable;
@@ -869,6 +887,15 @@ interface AccountCreditsTable {
869
887
  refill_last_at: Date | null;
870
888
  updated_at: Generated<Date>;
871
889
  }
890
+ /** Daily idempotency ledger for hosted subgraph running + storage meters. */
891
+ interface HostedMeterDaysTable {
892
+ day: string;
893
+ account_id: string;
894
+ subgraph_name: string;
895
+ kind: string;
896
+ usd_micros: string | number | bigint;
897
+ billed_at: Generated<Date>;
898
+ }
872
899
  /**
873
900
  * Archive fetch charge log (design-f089 gate). Append-only: one row per
874
901
  * priced attempt to fetch a partition object. `usd_micros` is 0 for
@@ -10,17 +10,25 @@
10
10
  *
11
11
  * The distinction this file exists to protect:
12
12
  *
13
- * lagging → the archive trails the chain tip because it only publishes below
14
- * the finality boundary. This is CORRECT and permanent. An archive
15
- * that reported `stale` for its own finality lag would be crying
16
- * wolf forever, and consumers would learn to ignore it.
17
- * stale → publishing stopped. The lag exceeds what the finality rule can
18
- * explain, so something is broken.
13
+ * lagging → the archive trails the chain tip. This is CORRECT and permanent.
14
+ * Two expected causes: twice-weekly publish (Wednesday and Sunday)
15
+ * and the finality boundary. Cadence is the bulk of the gap;
16
+ * finality is hours, not days. An archive that reported `stale`
17
+ * for its own expected lag would be crying wolf forever, and
18
+ * consumers would learn to ignore it.
19
+ * stale → publishing stopped. The lag exceeds what cadence and the
20
+ * finality rule can explain, so something is broken.
19
21
  *
20
22
  * `state` is derived from measurements, never asserted by the publisher, and
21
23
  * every unhealthy state is reachable — an archive that can only report health
22
24
  * is decoration.
23
25
  */
26
+ /**
27
+ * v1 is additive. `source.decoder_head` joined the object without a bump:
28
+ * it does not change the meaning of any existing field, and readers already
29
+ * ignore unknown keys. Bump only if a field is renamed, removed, or
30
+ * re-interpreted.
31
+ */
24
32
  declare const ARCHIVE_STATUS_SCHEMA_VERSION = 1;
25
33
  type ArchiveState = "fresh" | "lagging" | "stale" | "gap" | "failed-audit" | "source-unavailable";
26
34
  type ArchiveStatus = {
@@ -41,6 +49,12 @@ type ArchiveStatus = {
41
49
  tip_height: number | null
42
50
  /** Highest height eligible to publish under the finality rule. */
43
51
  finalized_height: number | null
52
+ /**
53
+ * Highest height the Index decoder (`decode.ft_transfer.v1`) has
54
+ * committed. Null when the checkpoint cannot be read. Distinct from
55
+ * `archive.coverage_to_block`, which is the last signed publish.
56
+ */
57
+ decoder_head: number | null
44
58
  }
45
59
  lag: {
46
60
  /** Behind the FINALIZED height — the number that indicates a problem. */
@@ -65,6 +79,11 @@ type StatusInputs = {
65
79
  /** Null when the source could not be reached. */
66
80
  sourceTipHeight: number | null
67
81
  finalizedHeight: number | null
82
+ /**
83
+ * Index decoder checkpoint height. Null when unread or unreachable.
84
+ * Reported, never used to derive `state`.
85
+ */
86
+ decoderHead?: number | null
68
87
  audit: {
69
88
  complete: boolean
70
89
  checkedAt: string
@@ -80,6 +99,12 @@ type StatusInputs = {
80
99
  maxSecondsSincePromotion?: number
81
100
  };
82
101
  /**
102
+ * Next twice-weekly publish fire strictly after `now`. Matches
103
+ * `secondlayer-archive-publish.timer`. Export duration means the promotion
104
+ * lands hours later; this is the scheduled start.
105
+ */
106
+ declare function nextScheduledArchivePublish(now: Date): Date;
107
+ /**
83
108
  * Derive status from measurements. Pure, so every state below is reachable in
84
109
  * a test rather than only in production at 3am.
85
110
  *
@@ -89,4 +114,4 @@ type StatusInputs = {
89
114
  declare function deriveArchiveStatus(inputs: StatusInputs): ArchiveStatus;
90
115
  /** States a consumer should refuse to restore from. */
91
116
  declare function isRestorable(state: ArchiveState): boolean;
92
- export { isRestorable, deriveArchiveStatus, StatusInputs, ArchiveStatus, ArchiveState, ARCHIVE_STATUS_SCHEMA_VERSION };
117
+ export { nextScheduledArchivePublish, isRestorable, deriveArchiveStatus, StatusInputs, ArchiveStatus, ArchiveState, ARCHIVE_STATUS_SCHEMA_VERSION };
@@ -18,6 +18,20 @@ var __export = (target, all) => {
18
18
  var ARCHIVE_STATUS_SCHEMA_VERSION = 1;
19
19
  var DEFAULT_MAX_BLOCKS_BEHIND_FINALIZED = 60000;
20
20
  var DEFAULT_MAX_SECONDS_SINCE_PROMOTION = 5 * 24 * 3600;
21
+ var PUBLISH_HOUR_UTC = 8;
22
+ var PUBLISH_WEEKDAYS_UTC = new Set([0, 3]);
23
+ function nextScheduledArchivePublish(now) {
24
+ for (let i = 0;i <= 7; i++) {
25
+ const candidate = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + i, PUBLISH_HOUR_UTC, 0, 0, 0));
26
+ if (PUBLISH_WEEKDAYS_UTC.has(candidate.getUTCDay()) && candidate > now) {
27
+ return candidate;
28
+ }
29
+ }
30
+ throw new Error("nextScheduledArchivePublish: no fire in 7 days");
31
+ }
32
+ function publishWeekdayName(date) {
33
+ return date.getUTCDay() === 0 ? "Sunday" : "Wednesday";
34
+ }
21
35
  function formatAge(seconds) {
22
36
  const hours = seconds / 3600;
23
37
  if (hours < 48)
@@ -44,7 +58,8 @@ function deriveArchiveStatus(inputs) {
44
58
  },
45
59
  source: {
46
60
  tip_height: inputs.sourceTipHeight,
47
- finalized_height: inputs.finalizedHeight
61
+ finalized_height: inputs.finalizedHeight,
62
+ decoder_head: inputs.decoderHead ?? null
48
63
  },
49
64
  lag: {
50
65
  blocks_behind_finalized: behindFinalized,
@@ -89,10 +104,11 @@ function deriveArchiveStatus(inputs) {
89
104
  };
90
105
  }
91
106
  if (behindTip !== null && behindTip > 0) {
107
+ const nextDay = publishWeekdayName(nextScheduledArchivePublish(inputs.now));
92
108
  return {
93
109
  ...base,
94
110
  state: "lagging",
95
- detail: `${behindTip} blocks behind the chain tip, which is expected: only heights below the finality boundary are published`
111
+ detail: `${behindTip} blocks behind the chain tip, which is expected: publishing is twice weekly (Wednesday and Sunday, next ${nextDay}) and stops at the finality boundary`
96
112
  };
97
113
  }
98
114
  return {
@@ -105,10 +121,11 @@ function isRestorable(state) {
105
121
  return state === "fresh" || state === "lagging";
106
122
  }
107
123
  export {
124
+ nextScheduledArchivePublish,
108
125
  isRestorable,
109
126
  deriveArchiveStatus,
110
127
  ARCHIVE_STATUS_SCHEMA_VERSION
111
128
  };
112
129
 
113
- //# debugId=9AB2F7013DDD92C464756E2164756E21
130
+ //# debugId=C18B5116B1FC55FB64756E2164756E21
114
131
  //# sourceMappingURL=status.js.map
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../src/archive/status.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * `status.json` — the archive's operational truth, published short-cache from\n * verified state only.\n *\n * Everything else in the tree is immutable and signed, which makes it durable\n * but silent: a consumer holding a valid manifest cannot tell whether it is\n * current, three days behind, or the last thing published before an incident.\n * This object answers that, and it is the only object allowed to say something\n * unflattering.\n *\n * The distinction this file exists to protect:\n *\n * lagging → the archive trails the chain tip because it only publishes below\n * the finality boundary. This is CORRECT and permanent. An archive\n * that reported `stale` for its own finality lag would be crying\n * wolf forever, and consumers would learn to ignore it.\n * stale → publishing stopped. The lag exceeds what the finality rule can\n * explain, so something is broken.\n *\n * `state` is derived from measurements, never asserted by the publisher, and\n * every unhealthy state is reachable — an archive that can only report health\n * is decoration.\n */\n\nexport const ARCHIVE_STATUS_SCHEMA_VERSION = 1;\n\nexport type ArchiveState =\n\t| \"fresh\"\n\t| \"lagging\"\n\t| \"stale\"\n\t| \"gap\"\n\t| \"failed-audit\"\n\t| \"source-unavailable\";\n\nexport type ArchiveStatus = {\n\tschema_version: typeof ARCHIVE_STATUS_SCHEMA_VERSION;\n\tnetwork: string;\n\tstate: ArchiveState;\n\t/** Human-facing one-liner explaining the state, including WHY. */\n\tdetail: string;\n\tgenerated_at: string;\n\tarchive: {\n\t\tsnapshot_digest: string | null;\n\t\tcoverage_to_block: number | null;\n\t\tpromoted_at: string | null;\n\t\tsigning_key_id: string | null;\n\t};\n\tsource: {\n\t\t/** Chain tip as the publisher last observed it. Null when unreachable. */\n\t\ttip_height: number | null;\n\t\t/** Highest height eligible to publish under the finality rule. */\n\t\tfinalized_height: number | null;\n\t};\n\tlag: {\n\t\t/** Behind the FINALIZED height — the number that indicates a problem. */\n\t\tblocks_behind_finalized: number | null;\n\t\t/** Behind the raw chain tip — expected to be non-zero forever. */\n\t\tblocks_behind_tip: number | null;\n\t\tseconds_since_promotion: number | null;\n\t};\n\taudit: {\n\t\tcomplete: boolean;\n\t\tchecked_at: string;\n\t} | null;\n\tsignature?: string;\n\tkey_id?: string;\n};\n\nexport type StatusInputs = {\n\tnetwork: string;\n\tsnapshotDigest: string | null;\n\tcoverageToBlock: number | null;\n\tpromotedAt: string | null;\n\tsigningKeyId: string | null;\n\t/** Null when the source could not be reached. */\n\tsourceTipHeight: number | null;\n\tfinalizedHeight: number | null;\n\taudit: { complete: boolean; checkedAt: string } | null;\n\tnow: Date;\n\t/**\n\t * How far behind the FINALIZED height the archive may fall before it is\n\t * stale rather than merely lagging. Publishing one partition covers 50k\n\t * blocks, so a healthy archive sits well inside this.\n\t */\n\tmaxBlocksBehindFinalized?: number;\n\t/** How long since the last promotion before staleness, regardless of height. */\n\tmaxSecondsSincePromotion?: number;\n};\n\nconst DEFAULT_MAX_BLOCKS_BEHIND_FINALIZED = 60_000;\n/**\n * Must exceed the publish cadence, or the archive reports `stale` on every\n * healthy cycle and the signal means nothing.\n *\n * The publisher runs Wed + Sun, so the longest gap between promotions in a\n * healthy week is 4 days. Five days leaves a day of grace for a slow export or\n * timer jitter, and still fires two days before the next scheduled attempt\n * whenever a cycle is actually missed.\n *\n * It also lands just inside the height rule: at the observed ~7–9k blocks/day\n * the archive is ~45k blocks behind finalized at the five-day mark, so age\n * trips first and reports the more diagnostic message (\"publishing stopped\")\n * rather than the symptom.\n */\nconst DEFAULT_MAX_SECONDS_SINCE_PROMOTION = 5 * 24 * 3_600;\n\n/**\n * Durations here run to days, and \"beyond the 120h objective\" makes a reader do\n * arithmetic before they can tell whether that is alarming.\n */\nfunction formatAge(seconds: number): string {\n\tconst hours = seconds / 3_600;\n\tif (hours < 48) return `${Math.round(hours)}h`;\n\tconst days = hours / 24;\n\t// One decimal only when it changes the reading: 4d and 4.5d are different\n\t// facts during an incident, 5.0d is noise.\n\tconst rounded = Math.round(days * 10) / 10;\n\treturn `${Number.isInteger(rounded) ? rounded : rounded.toFixed(1)}d`;\n}\n\n/**\n * Derive status from measurements. Pure, so every state below is reachable in\n * a test rather than only in production at 3am.\n *\n * Order matters: a failing audit outranks freshness, because an archive that\n * is current and wrong is worse than one that is behind and correct.\n */\nexport function deriveArchiveStatus(inputs: StatusInputs): ArchiveStatus {\n\tconst maxBehind =\n\t\tinputs.maxBlocksBehindFinalized ?? DEFAULT_MAX_BLOCKS_BEHIND_FINALIZED;\n\tconst maxAge =\n\t\tinputs.maxSecondsSincePromotion ?? DEFAULT_MAX_SECONDS_SINCE_PROMOTION;\n\n\tconst secondsSincePromotion = inputs.promotedAt\n\t\t? Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.round(\n\t\t\t\t\t(inputs.now.getTime() - Date.parse(inputs.promotedAt)) / 1000,\n\t\t\t\t),\n\t\t\t)\n\t\t: null;\n\n\tconst behindFinalized =\n\t\tinputs.finalizedHeight !== null && inputs.coverageToBlock !== null\n\t\t\t? Math.max(0, inputs.finalizedHeight - inputs.coverageToBlock)\n\t\t\t: null;\n\tconst behindTip =\n\t\tinputs.sourceTipHeight !== null && inputs.coverageToBlock !== null\n\t\t\t? Math.max(0, inputs.sourceTipHeight - inputs.coverageToBlock)\n\t\t\t: null;\n\n\tconst base = {\n\t\tschema_version:\n\t\t\tARCHIVE_STATUS_SCHEMA_VERSION as typeof ARCHIVE_STATUS_SCHEMA_VERSION,\n\t\tnetwork: inputs.network,\n\t\tgenerated_at: inputs.now.toISOString(),\n\t\tarchive: {\n\t\t\tsnapshot_digest: inputs.snapshotDigest,\n\t\t\tcoverage_to_block: inputs.coverageToBlock,\n\t\t\tpromoted_at: inputs.promotedAt,\n\t\t\tsigning_key_id: inputs.signingKeyId,\n\t\t},\n\t\tsource: {\n\t\t\ttip_height: inputs.sourceTipHeight,\n\t\t\tfinalized_height: inputs.finalizedHeight,\n\t\t},\n\t\tlag: {\n\t\t\tblocks_behind_finalized: behindFinalized,\n\t\t\tblocks_behind_tip: behindTip,\n\t\t\tseconds_since_promotion: secondsSincePromotion,\n\t\t},\n\t\taudit: inputs.audit\n\t\t\t? { complete: inputs.audit.complete, checked_at: inputs.audit.checkedAt }\n\t\t\t: null,\n\t};\n\n\t// A failed audit is the loudest signal: current-and-wrong beats behind-and-right.\n\tif (inputs.audit && !inputs.audit.complete) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"failed-audit\",\n\t\t\tdetail: `the canonical audit did not pass at ${inputs.audit.checkedAt}; this archive is not safe to restore from`,\n\t\t};\n\t}\n\n\tif (!inputs.snapshotDigest || inputs.coverageToBlock === null) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"gap\",\n\t\t\tdetail: \"no snapshot is currently promoted\",\n\t\t};\n\t}\n\n\t// Source unreachable: report the gap in KNOWLEDGE rather than guessing. The\n\t// archive may be perfectly healthy; we cannot currently say so.\n\tif (inputs.sourceTipHeight === null || inputs.finalizedHeight === null) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"source-unavailable\",\n\t\t\tdetail:\n\t\t\t\t\"the chain source could not be reached, so freshness cannot be determined\",\n\t\t};\n\t}\n\n\tif (secondsSincePromotion !== null && secondsSincePromotion > maxAge) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"stale\",\n\t\t\tdetail: `last promotion was ${formatAge(secondsSincePromotion)} ago, beyond the ${formatAge(maxAge)} freshness objective`,\n\t\t};\n\t}\n\n\tif (behindFinalized !== null && behindFinalized > maxBehind) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"stale\",\n\t\t\tdetail: `${behindFinalized} blocks behind the finalized height, beyond the ${maxBehind} threshold`,\n\t\t};\n\t}\n\n\t// Behind the tip but within the finality boundary — correct, permanent, and\n\t// explicitly NOT staleness.\n\tif (behindTip !== null && behindTip > 0) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"lagging\",\n\t\t\tdetail: `${behindTip} blocks behind the chain tip, which is expected: only heights below the finality boundary are published`,\n\t\t};\n\t}\n\n\treturn {\n\t\t...base,\n\t\tstate: \"fresh\",\n\t\tdetail: \"published through the finalized height\",\n\t};\n}\n\n/** States a consumer should refuse to restore from. */\nexport function isRestorable(state: ArchiveState): boolean {\n\treturn state === \"fresh\" || state === \"lagging\";\n}\n"
5
+ "/**\n * `status.json` — the archive's operational truth, published short-cache from\n * verified state only.\n *\n * Everything else in the tree is immutable and signed, which makes it durable\n * but silent: a consumer holding a valid manifest cannot tell whether it is\n * current, three days behind, or the last thing published before an incident.\n * This object answers that, and it is the only object allowed to say something\n * unflattering.\n *\n * The distinction this file exists to protect:\n *\n * lagging → the archive trails the chain tip. This is CORRECT and permanent.\n * Two expected causes: twice-weekly publish (Wednesday and Sunday)\n * and the finality boundary. Cadence is the bulk of the gap;\n * finality is hours, not days. An archive that reported `stale`\n * for its own expected lag would be crying wolf forever, and\n * consumers would learn to ignore it.\n * stale → publishing stopped. The lag exceeds what cadence and the\n * finality rule can explain, so something is broken.\n *\n * `state` is derived from measurements, never asserted by the publisher, and\n * every unhealthy state is reachable — an archive that can only report health\n * is decoration.\n */\n\n/**\n * v1 is additive. `source.decoder_head` joined the object without a bump:\n * it does not change the meaning of any existing field, and readers already\n * ignore unknown keys. Bump only if a field is renamed, removed, or\n * re-interpreted.\n */\nexport const ARCHIVE_STATUS_SCHEMA_VERSION = 1;\n\nexport type ArchiveState =\n\t| \"fresh\"\n\t| \"lagging\"\n\t| \"stale\"\n\t| \"gap\"\n\t| \"failed-audit\"\n\t| \"source-unavailable\";\n\nexport type ArchiveStatus = {\n\tschema_version: typeof ARCHIVE_STATUS_SCHEMA_VERSION;\n\tnetwork: string;\n\tstate: ArchiveState;\n\t/** Human-facing one-liner explaining the state, including WHY. */\n\tdetail: string;\n\tgenerated_at: string;\n\tarchive: {\n\t\tsnapshot_digest: string | null;\n\t\tcoverage_to_block: number | null;\n\t\tpromoted_at: string | null;\n\t\tsigning_key_id: string | null;\n\t};\n\tsource: {\n\t\t/** Chain tip as the publisher last observed it. Null when unreachable. */\n\t\ttip_height: number | null;\n\t\t/** Highest height eligible to publish under the finality rule. */\n\t\tfinalized_height: number | null;\n\t\t/**\n\t\t * Highest height the Index decoder (`decode.ft_transfer.v1`) has\n\t\t * committed. Null when the checkpoint cannot be read. Distinct from\n\t\t * `archive.coverage_to_block`, which is the last signed publish.\n\t\t */\n\t\tdecoder_head: number | null;\n\t};\n\tlag: {\n\t\t/** Behind the FINALIZED height — the number that indicates a problem. */\n\t\tblocks_behind_finalized: number | null;\n\t\t/** Behind the raw chain tip — expected to be non-zero forever. */\n\t\tblocks_behind_tip: number | null;\n\t\tseconds_since_promotion: number | null;\n\t};\n\taudit: {\n\t\tcomplete: boolean;\n\t\tchecked_at: string;\n\t} | null;\n\tsignature?: string;\n\tkey_id?: string;\n};\n\nexport type StatusInputs = {\n\tnetwork: string;\n\tsnapshotDigest: string | null;\n\tcoverageToBlock: number | null;\n\tpromotedAt: string | null;\n\tsigningKeyId: string | null;\n\t/** Null when the source could not be reached. */\n\tsourceTipHeight: number | null;\n\tfinalizedHeight: number | null;\n\t/**\n\t * Index decoder checkpoint height. Null when unread or unreachable.\n\t * Reported, never used to derive `state`.\n\t */\n\tdecoderHead?: number | null;\n\taudit: { complete: boolean; checkedAt: string } | null;\n\tnow: Date;\n\t/**\n\t * How far behind the FINALIZED height the archive may fall before it is\n\t * stale rather than merely lagging. Publishing one partition covers 50k\n\t * blocks, so a healthy archive sits well inside this.\n\t */\n\tmaxBlocksBehindFinalized?: number;\n\t/** How long since the last promotion before staleness, regardless of height. */\n\tmaxSecondsSincePromotion?: number;\n};\n\nconst DEFAULT_MAX_BLOCKS_BEHIND_FINALIZED = 60_000;\n/**\n * Must exceed the publish cadence, or the archive reports `stale` on every\n * healthy cycle and the signal means nothing.\n *\n * The publisher runs Wed + Sun, so the longest gap between promotions in a\n * healthy week is 4 days. Five days leaves a day of grace for a slow export or\n * timer jitter, and still fires two days before the next scheduled attempt\n * whenever a cycle is actually missed.\n *\n * It also lands just inside the height rule: at the observed ~7–9k blocks/day\n * the archive is ~45k blocks behind finalized at the five-day mark, so age\n * trips first and reports the more diagnostic message (\"publishing stopped\")\n * rather than the symptom.\n */\nconst DEFAULT_MAX_SECONDS_SINCE_PROMOTION = 5 * 24 * 3_600;\n\n/** systemd `OnCalendar=Wed,Sun *-*-* 08:00:00`. Host is UTC. Job start, not promotion landing. */\nconst PUBLISH_HOUR_UTC = 8;\nconst PUBLISH_WEEKDAYS_UTC = new Set([0, 3]);\n\n/**\n * Next twice-weekly publish fire strictly after `now`. Matches\n * `secondlayer-archive-publish.timer`. Export duration means the promotion\n * lands hours later; this is the scheduled start.\n */\nexport function nextScheduledArchivePublish(now: Date): Date {\n\tfor (let i = 0; i <= 7; i++) {\n\t\tconst candidate = new Date(\n\t\t\tDate.UTC(\n\t\t\t\tnow.getUTCFullYear(),\n\t\t\t\tnow.getUTCMonth(),\n\t\t\t\tnow.getUTCDate() + i,\n\t\t\t\tPUBLISH_HOUR_UTC,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t\t0,\n\t\t\t),\n\t\t);\n\t\tif (PUBLISH_WEEKDAYS_UTC.has(candidate.getUTCDay()) && candidate > now) {\n\t\t\treturn candidate;\n\t\t}\n\t}\n\tthrow new Error(\"nextScheduledArchivePublish: no fire in 7 days\");\n}\n\nfunction publishWeekdayName(date: Date): \"Sunday\" | \"Wednesday\" {\n\treturn date.getUTCDay() === 0 ? \"Sunday\" : \"Wednesday\";\n}\n\n/**\n * Durations here run to days, and \"beyond the 120h objective\" makes a reader do\n * arithmetic before they can tell whether that is alarming.\n */\nfunction formatAge(seconds: number): string {\n\tconst hours = seconds / 3_600;\n\tif (hours < 48) return `${Math.round(hours)}h`;\n\tconst days = hours / 24;\n\t// One decimal only when it changes the reading: 4d and 4.5d are different\n\t// facts during an incident, 5.0d is noise.\n\tconst rounded = Math.round(days * 10) / 10;\n\treturn `${Number.isInteger(rounded) ? rounded : rounded.toFixed(1)}d`;\n}\n\n/**\n * Derive status from measurements. Pure, so every state below is reachable in\n * a test rather than only in production at 3am.\n *\n * Order matters: a failing audit outranks freshness, because an archive that\n * is current and wrong is worse than one that is behind and correct.\n */\nexport function deriveArchiveStatus(inputs: StatusInputs): ArchiveStatus {\n\tconst maxBehind =\n\t\tinputs.maxBlocksBehindFinalized ?? DEFAULT_MAX_BLOCKS_BEHIND_FINALIZED;\n\tconst maxAge =\n\t\tinputs.maxSecondsSincePromotion ?? DEFAULT_MAX_SECONDS_SINCE_PROMOTION;\n\n\tconst secondsSincePromotion = inputs.promotedAt\n\t\t? Math.max(\n\t\t\t\t0,\n\t\t\t\tMath.round(\n\t\t\t\t\t(inputs.now.getTime() - Date.parse(inputs.promotedAt)) / 1000,\n\t\t\t\t),\n\t\t\t)\n\t\t: null;\n\n\tconst behindFinalized =\n\t\tinputs.finalizedHeight !== null && inputs.coverageToBlock !== null\n\t\t\t? Math.max(0, inputs.finalizedHeight - inputs.coverageToBlock)\n\t\t\t: null;\n\tconst behindTip =\n\t\tinputs.sourceTipHeight !== null && inputs.coverageToBlock !== null\n\t\t\t? Math.max(0, inputs.sourceTipHeight - inputs.coverageToBlock)\n\t\t\t: null;\n\n\tconst base = {\n\t\tschema_version:\n\t\t\tARCHIVE_STATUS_SCHEMA_VERSION as typeof ARCHIVE_STATUS_SCHEMA_VERSION,\n\t\tnetwork: inputs.network,\n\t\tgenerated_at: inputs.now.toISOString(),\n\t\tarchive: {\n\t\t\tsnapshot_digest: inputs.snapshotDigest,\n\t\t\tcoverage_to_block: inputs.coverageToBlock,\n\t\t\tpromoted_at: inputs.promotedAt,\n\t\t\tsigning_key_id: inputs.signingKeyId,\n\t\t},\n\t\tsource: {\n\t\t\ttip_height: inputs.sourceTipHeight,\n\t\t\tfinalized_height: inputs.finalizedHeight,\n\t\t\tdecoder_head: inputs.decoderHead ?? null,\n\t\t},\n\t\tlag: {\n\t\t\tblocks_behind_finalized: behindFinalized,\n\t\t\tblocks_behind_tip: behindTip,\n\t\t\tseconds_since_promotion: secondsSincePromotion,\n\t\t},\n\t\taudit: inputs.audit\n\t\t\t? { complete: inputs.audit.complete, checked_at: inputs.audit.checkedAt }\n\t\t\t: null,\n\t};\n\n\t// A failed audit is the loudest signal: current-and-wrong beats behind-and-right.\n\tif (inputs.audit && !inputs.audit.complete) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"failed-audit\",\n\t\t\tdetail: `the canonical audit did not pass at ${inputs.audit.checkedAt}; this archive is not safe to restore from`,\n\t\t};\n\t}\n\n\tif (!inputs.snapshotDigest || inputs.coverageToBlock === null) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"gap\",\n\t\t\tdetail: \"no snapshot is currently promoted\",\n\t\t};\n\t}\n\n\t// Source unreachable: report the gap in KNOWLEDGE rather than guessing. The\n\t// archive may be perfectly healthy; we cannot currently say so.\n\tif (inputs.sourceTipHeight === null || inputs.finalizedHeight === null) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"source-unavailable\",\n\t\t\tdetail:\n\t\t\t\t\"the chain source could not be reached, so freshness cannot be determined\",\n\t\t};\n\t}\n\n\tif (secondsSincePromotion !== null && secondsSincePromotion > maxAge) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"stale\",\n\t\t\tdetail: `last promotion was ${formatAge(secondsSincePromotion)} ago, beyond the ${formatAge(maxAge)} freshness objective`,\n\t\t};\n\t}\n\n\tif (behindFinalized !== null && behindFinalized > maxBehind) {\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"stale\",\n\t\t\tdetail: `${behindFinalized} blocks behind the finalized height, beyond the ${maxBehind} threshold`,\n\t\t};\n\t}\n\n\t// Behind the tip. CORRECT and permanent: twice-weekly publish plus the\n\t// finality boundary. Cadence is the bulk of the gap. Do not call this stale.\n\tif (behindTip !== null && behindTip > 0) {\n\t\tconst nextDay = publishWeekdayName(nextScheduledArchivePublish(inputs.now));\n\t\treturn {\n\t\t\t...base,\n\t\t\tstate: \"lagging\",\n\t\t\tdetail: `${behindTip} blocks behind the chain tip, which is expected: publishing is twice weekly (Wednesday and Sunday, next ${nextDay}) and stops at the finality boundary`,\n\t\t};\n\t}\n\n\treturn {\n\t\t...base,\n\t\tstate: \"fresh\",\n\t\tdetail: \"published through the finalized height\",\n\t};\n}\n\n/** States a consumer should refuse to restore from. */\nexport function isRestorable(state: ArchiveState): boolean {\n\treturn state === \"fresh\" || state === \"lagging\";\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "9AB2F7013DDD92C464756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "C18B5116B1FC55FB64756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -581,6 +581,21 @@ interface MagicLinksTable {
581
581
  failed_attempts: Generated<number>;
582
582
  created_at: Generated<Date>;
583
583
  }
584
+ /** One-shot hashed tokens that attach a play ghost to an email at claim. */
585
+ interface ClaimTokensTable {
586
+ id: Generated<string>;
587
+ account_id: string;
588
+ token_hash: string;
589
+ created_at: Generated<Date>;
590
+ expires_at: Date;
591
+ used_at: Date | null;
592
+ }
593
+ /** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
594
+ interface PlayProvisionsTable {
595
+ ip_hash: string;
596
+ day: string;
597
+ count: Generated<number>;
598
+ }
584
599
  interface SubgraphProcessingStatsTable {
585
600
  id: Generated<string>;
586
601
  subgraph_name: string;
@@ -1079,6 +1094,8 @@ interface Database {
1079
1094
  accounts: AccountsTable;
1080
1095
  sessions: SessionsTable;
1081
1096
  magic_links: MagicLinksTable;
1097
+ claim_tokens: ClaimTokensTable;
1098
+ play_provisions: PlayProvisionsTable;
1082
1099
  subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
1083
1100
  subgraph_processing_stats: SubgraphProcessingStatsTable;
1084
1101
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
@@ -1088,6 +1105,7 @@ interface Database {
1088
1105
  processed_stripe_events: ProcessedStripeEventsTable;
1089
1106
  account_spend_caps: AccountSpendCapsTable;
1090
1107
  account_credits: AccountCreditsTable;
1108
+ hosted_meter_days: HostedMeterDaysTable;
1091
1109
  subscriptions: SubscriptionsTable;
1092
1110
  subscription_outbox: SubscriptionOutboxTable;
1093
1111
  subscription_deliveries: SubscriptionDeliveriesTable;
@@ -1187,6 +1205,15 @@ interface AccountCreditsTable {
1187
1205
  refill_last_at: Date | null;
1188
1206
  updated_at: Generated<Date>;
1189
1207
  }
1208
+ /** Daily idempotency ledger for hosted subgraph running + storage meters. */
1209
+ interface HostedMeterDaysTable {
1210
+ day: string;
1211
+ account_id: string;
1212
+ subgraph_name: string;
1213
+ kind: string;
1214
+ usd_micros: string | number | bigint;
1215
+ billed_at: Generated<Date>;
1216
+ }
1190
1217
  /**
1191
1218
  * Archive fetch charge log (design-f089 gate). Append-only: one row per
1192
1219
  * priced attempt to fetch a partition object. `usd_micros` is 0 for
@@ -282,6 +282,21 @@ interface MagicLinksTable {
282
282
  failed_attempts: Generated<number>;
283
283
  created_at: Generated<Date>;
284
284
  }
285
+ /** One-shot hashed tokens that attach a play ghost to an email at claim. */
286
+ interface ClaimTokensTable {
287
+ id: Generated<string>;
288
+ account_id: string;
289
+ token_hash: string;
290
+ created_at: Generated<Date>;
291
+ expires_at: Date;
292
+ used_at: Date | null;
293
+ }
294
+ /** 3 play provisions per IP per UTC day. PK (ip_hash, day); `count` caps at 3. */
295
+ interface PlayProvisionsTable {
296
+ ip_hash: string;
297
+ day: string;
298
+ count: Generated<number>;
299
+ }
285
300
  interface SubgraphProcessingStatsTable {
286
301
  id: Generated<string>;
287
302
  subgraph_name: string;
@@ -780,6 +795,8 @@ interface Database {
780
795
  accounts: AccountsTable;
781
796
  sessions: SessionsTable;
782
797
  magic_links: MagicLinksTable;
798
+ claim_tokens: ClaimTokensTable;
799
+ play_provisions: PlayProvisionsTable;
783
800
  subgraph_health_snapshots: SubgraphHealthSnapshotsTable;
784
801
  subgraph_processing_stats: SubgraphProcessingStatsTable;
785
802
  subgraph_table_snapshots: SubgraphTableSnapshotsTable;
@@ -789,6 +806,7 @@ interface Database {
789
806
  processed_stripe_events: ProcessedStripeEventsTable;
790
807
  account_spend_caps: AccountSpendCapsTable;
791
808
  account_credits: AccountCreditsTable;
809
+ hosted_meter_days: HostedMeterDaysTable;
792
810
  subscriptions: SubscriptionsTable;
793
811
  subscription_outbox: SubscriptionOutboxTable;
794
812
  subscription_deliveries: SubscriptionDeliveriesTable;
@@ -894,6 +912,18 @@ interface AccountCreditsTable {
894
912
  type AccountCredits = Selectable<AccountCreditsTable>;
895
913
  type InsertAccountCredits = Insertable<AccountCreditsTable>;
896
914
  type UpdateAccountCredits = Updateable<AccountCreditsTable>;
915
+ /** Daily idempotency ledger for hosted subgraph running + storage meters. */
916
+ interface HostedMeterDaysTable {
917
+ day: string;
918
+ account_id: string;
919
+ subgraph_name: string;
920
+ kind: string;
921
+ usd_micros: string | number | bigint;
922
+ billed_at: Generated<Date>;
923
+ }
924
+ type HostedMeterDay = Selectable<HostedMeterDaysTable>;
925
+ type InsertHostedMeterDay = Insertable<HostedMeterDaysTable>;
926
+ type UpdateHostedMeterDay = Updateable<HostedMeterDaysTable>;
897
927
  /**
898
928
  * Archive fetch charge log (design-f089 gate). Append-only: one row per
899
929
  * priced attempt to fetch a partition object. `usd_micros` is 0 for
@@ -941,6 +971,11 @@ type Account = Selectable<AccountsTable>;
941
971
  type InsertAccount = Insertable<AccountsTable>;
942
972
  type MagicLink = Selectable<MagicLinksTable>;
943
973
  type InsertMagicLink = Insertable<MagicLinksTable>;
974
+ type ClaimToken = Selectable<ClaimTokensTable>;
975
+ type InsertClaimToken = Insertable<ClaimTokensTable>;
976
+ type UpdateClaimToken = Updateable<ClaimTokensTable>;
977
+ type PlayProvision = Selectable<PlayProvisionsTable>;
978
+ type InsertPlayProvision = Insertable<PlayProvisionsTable>;
944
979
  type Session = Selectable<SessionsTable>;
945
980
  type InsertSession = Insertable<SessionsTable>;
946
981
  type SubgraphHealthSnapshot = Selectable<SubgraphHealthSnapshotsTable>;
@@ -1968,6 +2003,8 @@ declare const TABLE_TO_DB: {
1968
2003
  api_keys: string
1969
2004
  sessions: string
1970
2005
  magic_links: string
2006
+ claim_tokens: string
2007
+ play_provisions: string
1971
2008
  account_spend_caps: string
1972
2009
  processed_stripe_events: string
1973
2010
  subscriptions: string
@@ -1984,6 +2021,7 @@ declare const TABLE_TO_DB: {
1984
2021
  x402_payments: string
1985
2022
  x402_balances: string
1986
2023
  account_credits: string
2024
+ hosted_meter_days: string
1987
2025
  archive_fetches: string
1988
2026
  service_heartbeats: string
1989
2027
  };
@@ -2056,4 +2094,4 @@ declare function getRawClient(role?: "source" | "target"): ReturnType<typeof pos
2056
2094
  declare function getRawClientFor(url: string): ReturnType<typeof postgres>;
2057
2095
  /** Close all DB connection pools. Call in CLI commands to allow process exit. */
2058
2096
  declare function closeDb(): Promise<void>;
2059
- export { sql, setMigrationRole, parseJsonb, onControlPlane, onChainPlane, jsonb, getTargetDb, getSourceDb, getRawClientFor, getRawClient, getMigrationRole, getDbSplitStatus, getDb, closeDb, assertDbSplit, applicationDatabaseUrl, X402PaymentsTable, X402BalancesTable, UpdateTransaction, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateIndexProgress, UpdateEvent, UpdateContract, UpdateBlock, UpdateArchiveFetch, UpdateApiKey, UpdateAccountSpendCap, UpdateAccountCredits, TriggerEvaluatorStateTable, TriggerEvaluatorState, TransactionsTable, TransactionsArchiveTable, Transaction, TABLE_TO_DB, SyncScopesTable, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionKind, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphViolationsTable, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, StageRunsTable, StageRegistryTable, StageKind, StageFailuresTable, StageBlockReceiptsTable, SessionsTable, Session, ServiceHeartbeatsTable, SbtcTokenEventsTable, SbtcTokenEventType, SbtcSupplySnapshotsTable, SbtcSettlementsTable, SbtcEventsTable, SbtcEventTopic, SOURCE_READ_TYPES, SOURCE_READ_PKS, SOURCE_READ_COLUMNS, RunStatus, RetryState, RepairMode, ProcessedStripeEventsTable, Pox5EventsTable, Pox5EventTopic, Pox4SignersDailyTable, Pox4FunctionName, Pox4CyclesDailyTable, Pox4CallsTable, PendingForkBlocksTable, OutboxStatus, ObserverJournalTable, ObserverJournalStatus, NumericAsText, NativeClock, MigrationRole, MempoolTransactionsTable, MempoolTransaction, MagicLinksTable, MagicLink, InstancesTable, InsertTransaction, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertMempoolTransaction, InsertMagicLink, InsertIndexProgress, InsertEvent, InsertContract, InsertBlock, InsertArchiveFetch, InsertApiKey, InsertAccountSpendCap, InsertAccountCredits, InsertAccount, IndexProgressTable, IndexProgress, IndexPrimaryKey, IndexColumnType, IndexColumn, FailureUnit, FailureClass, EventsTable, EventsArchiveTable, Event, DecoderCheckpointsTable, DecodedEventsTable, DeadLetterEventsTable, DbSplitStatus, DbReadRow, DbPlane, Database, CoverageSegmentsTable, ContractsTable, Contract, ChainReorgsTable, ChainReadCacheTable, BurnBlockRewardsTable, BurnBlockRewardSlotsTable, BnsNamespacesTable, BnsNamespaceEventsTable, BnsNamespaceEventStatus, BnsNamesTable, BnsNameEventsTable, BnsNameEventTopic, BnsMarketplaceEventsTable, BnsMarketplaceAction, BlocksTable, Block, ArchiveFetchesTable, ArchiveFetch, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountCreditsTable, AccountCredits, Account };
2097
+ export { sql, setMigrationRole, parseJsonb, onControlPlane, onChainPlane, jsonb, getTargetDb, getSourceDb, getRawClientFor, getRawClient, getMigrationRole, getDbSplitStatus, getDb, closeDb, assertDbSplit, applicationDatabaseUrl, X402PaymentsTable, X402BalancesTable, UpdateTransaction, UpdateSubscriptionOutbox, UpdateSubscription, UpdateSubgraphOperation, UpdateSubgraph, UpdateIndexProgress, UpdateHostedMeterDay, UpdateEvent, UpdateContract, UpdateClaimToken, UpdateBlock, UpdateArchiveFetch, UpdateApiKey, UpdateAccountSpendCap, UpdateAccountCredits, TriggerEvaluatorStateTable, TriggerEvaluatorState, TransactionsTable, TransactionsArchiveTable, Transaction, TABLE_TO_DB, SyncScopesTable, SubscriptionsTable, SubscriptionStatus, SubscriptionRuntime, SubscriptionOutboxTable, SubscriptionOutbox, SubscriptionKind, SubscriptionFormat, SubscriptionDelivery, SubscriptionDeliveriesTable, Subscription, SubgraphsTable, SubgraphViolationsTable, SubgraphTableSnapshotsTable, SubgraphProcessingStatsTable, SubgraphOperationsTable, SubgraphOperationStatus, SubgraphOperationKind, SubgraphOperation, SubgraphHealthSnapshotsTable, SubgraphHealthSnapshot, SubgraphGapsTable, SubgraphGap, Subgraph, StageRunsTable, StageRegistryTable, StageKind, StageFailuresTable, StageBlockReceiptsTable, SessionsTable, Session, ServiceHeartbeatsTable, SbtcTokenEventsTable, SbtcTokenEventType, SbtcSupplySnapshotsTable, SbtcSettlementsTable, SbtcEventsTable, SbtcEventTopic, SOURCE_READ_TYPES, SOURCE_READ_PKS, SOURCE_READ_COLUMNS, RunStatus, RetryState, RepairMode, ProcessedStripeEventsTable, Pox5EventsTable, Pox5EventTopic, Pox4SignersDailyTable, Pox4FunctionName, Pox4CyclesDailyTable, Pox4CallsTable, PlayProvisionsTable, PlayProvision, PendingForkBlocksTable, OutboxStatus, ObserverJournalTable, ObserverJournalStatus, NumericAsText, NativeClock, MigrationRole, MempoolTransactionsTable, MempoolTransaction, MagicLinksTable, MagicLink, InstancesTable, InsertTransaction, InsertSubscriptionOutbox, InsertSubscriptionDelivery, InsertSubscription, InsertSubgraphOperation, InsertSubgraphHealthSnapshot, InsertSubgraphGap, InsertSubgraph, InsertSession, InsertPlayProvision, InsertMempoolTransaction, InsertMagicLink, InsertIndexProgress, InsertHostedMeterDay, InsertEvent, InsertContract, InsertClaimToken, InsertBlock, InsertArchiveFetch, InsertApiKey, InsertAccountSpendCap, InsertAccountCredits, InsertAccount, IndexProgressTable, IndexProgress, IndexPrimaryKey, IndexColumnType, IndexColumn, HostedMeterDaysTable, HostedMeterDay, FailureUnit, FailureClass, EventsTable, EventsArchiveTable, Event, DecoderCheckpointsTable, DecodedEventsTable, DeadLetterEventsTable, DbSplitStatus, DbReadRow, DbPlane, Database, CoverageSegmentsTable, ContractsTable, Contract, ClaimTokensTable, ClaimToken, ChainReorgsTable, ChainReadCacheTable, BurnBlockRewardsTable, BurnBlockRewardSlotsTable, BnsNamespacesTable, BnsNamespaceEventsTable, BnsNamespaceEventStatus, BnsNamesTable, BnsNameEventsTable, BnsNameEventTopic, BnsMarketplaceEventsTable, BnsMarketplaceAction, BlocksTable, Block, ArchiveFetchesTable, ArchiveFetch, ApiKeysTable, ApiKey, AccountsTable, AccountSpendCapsTable, AccountSpendCap, AccountCreditsTable, AccountCredits, Account };
@@ -775,6 +775,8 @@ var TABLE_TO_DB = {
775
775
  api_keys: "target",
776
776
  sessions: "target",
777
777
  magic_links: "target",
778
+ claim_tokens: "target",
779
+ play_provisions: "target",
778
780
  account_spend_caps: "target",
779
781
  processed_stripe_events: "target",
780
782
  subscriptions: "target",
@@ -791,6 +793,7 @@ var TABLE_TO_DB = {
791
793
  x402_payments: "target",
792
794
  x402_balances: "target",
793
795
  account_credits: "target",
796
+ hosted_meter_days: "target",
794
797
  archive_fetches: "target",
795
798
  service_heartbeats: "both"
796
799
  };
@@ -994,5 +997,5 @@ export {
994
997
  SOURCE_READ_COLUMNS
995
998
  };
996
999
 
997
- //# debugId=CD731059E79DAACA64756E2164756E21
1000
+ //# debugId=AF41DD34BD57EF3464756E2164756E21
998
1001
  //# sourceMappingURL=index.js.map