@secondlayer/shared 6.14.0 → 6.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ type IndexBlockRow = {
2
+ block_height: number
3
+ block_hash: string
4
+ parent_hash: string
5
+ burn_block_height: number
6
+ burn_block_hash: string | null
7
+ block_time: string | null
8
+ };
9
+ type IndexEventCommon = {
10
+ block_height: number
11
+ tx_id: string
12
+ event_index: number
13
+ contract_id: string | null
14
+ };
15
+ type IndexEventRow = IndexEventCommon & ({
16
+ event_type: "ft_transfer" | "ft_mint" | "ft_burn"
17
+ asset_identifier: string
18
+ sender?: string
19
+ recipient?: string
20
+ amount: string
21
+ } | {
22
+ event_type: "nft_transfer" | "nft_mint" | "nft_burn"
23
+ asset_identifier: string
24
+ sender?: string
25
+ recipient?: string
26
+ value: string
27
+ } | {
28
+ event_type: "stx_transfer" | "stx_mint" | "stx_burn"
29
+ sender?: string
30
+ recipient?: string
31
+ amount: string
32
+ memo?: string | null
33
+ } | {
34
+ event_type: "stx_lock"
35
+ sender: string
36
+ amount: string
37
+ payload: {
38
+ unlock_height: string | null
39
+ }
40
+ } | {
41
+ event_type: "print"
42
+ payload: {
43
+ topic: string | null
44
+ value: unknown
45
+ raw_value: string | null
46
+ }
47
+ });
48
+ type IndexTransactionRow = {
49
+ tx_id: string
50
+ block_height: number
51
+ tx_index: number
52
+ tx_type: string
53
+ sender: string
54
+ status: string
55
+ contract_call?: {
56
+ contract_id: string
57
+ function_name: string
58
+ function_args_hex?: string[] | null
59
+ result_hex?: string | null
60
+ } | null
61
+ smart_contract?: {
62
+ contract_id: string | null
63
+ } | null
64
+ };
65
+ type StreamsReorgRow = {
66
+ detected_at: string
67
+ fork_point_height: number
68
+ orphaned_range: {
69
+ from: string
70
+ to: string
71
+ }
72
+ new_canonical_tip: string
73
+ };
74
+ type IndexHttpOptions = {
75
+ /** Base URL for /v1/index (the decoded data plane). */
76
+ indexBaseUrl: string
77
+ /** Bearer for /v1/index. Defaults to the internal enterprise key. */
78
+ indexApiKey?: string
79
+ /** Base URL for /v1/streams (the canonical clock). */
80
+ streamsBaseUrl: string
81
+ /** Bearer for /v1/streams (internal enterprise key). */
82
+ streamsApiKey: string
83
+ };
84
+ declare class IndexHttpClient {
85
+ private readonly indexBaseUrl;
86
+ private readonly indexApiKey;
87
+ private readonly streamsBaseUrl;
88
+ private readonly streamsApiKey;
89
+ constructor(opts: IndexHttpOptions);
90
+ private get;
91
+ /** Drain a cursor-paginated Index collection over [fromHeight, toHeight]. */
92
+ private walk;
93
+ walkBlocks(fromHeight: number, toHeight: number): Promise<IndexBlockRow[]>;
94
+ walkEvents(eventType: string, fromHeight: number, toHeight: number): Promise<IndexEventRow[]>;
95
+ walkTransactions(fromHeight: number, toHeight: number): Promise<IndexTransactionRow[]>;
96
+ /** Canonical tip height from the Streams clock. */
97
+ getStreamsTip(): Promise<number>;
98
+ /**
99
+ * Highest block height the Index data plane can serve (tip is inline in every
100
+ * envelope). This is the data-availability bound — a consumer must not
101
+ * process past it, even if the Streams clock is ahead.
102
+ */
103
+ getIndexTip(): Promise<number>;
104
+ /** Reorgs since a resume token (wall-clock `detected_at`-keyed). */
105
+ listReorgs(since: string): Promise<{
106
+ reorgs: StreamsReorgRow[]
107
+ next_since: string | null
108
+ }>;
109
+ }
110
+ export { StreamsReorgRow, IndexTransactionRow, IndexHttpOptions, IndexHttpClient, IndexEventRow, IndexBlockRow };
@@ -0,0 +1,93 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/index-internal-auth.ts
18
+ var INDEX_INTERNAL_TENANT_ID = "tenant_index_internal";
19
+ var DEFAULT_INDEX_INTERNAL_API_KEY = "sk-sl_index_internal";
20
+ function defaultInternalIndexApiKey() {
21
+ return process.env.INDEX_INTERNAL_API_KEY || DEFAULT_INDEX_INTERNAL_API_KEY;
22
+ }
23
+
24
+ // src/index-http.ts
25
+ var PAGE_LIMIT = 1000;
26
+
27
+ class IndexHttpClient {
28
+ indexBaseUrl;
29
+ indexApiKey;
30
+ streamsBaseUrl;
31
+ streamsApiKey;
32
+ constructor(opts) {
33
+ this.indexBaseUrl = opts.indexBaseUrl.replace(/\/+$/, "");
34
+ this.indexApiKey = opts.indexApiKey ?? defaultInternalIndexApiKey();
35
+ this.streamsBaseUrl = opts.streamsBaseUrl.replace(/\/+$/, "");
36
+ this.streamsApiKey = opts.streamsApiKey;
37
+ }
38
+ async get(url, apiKey) {
39
+ const res = await fetch(url, {
40
+ headers: apiKey ? { authorization: `Bearer ${apiKey}` } : {}
41
+ });
42
+ if (!res.ok) {
43
+ throw new Error(`GET ${url} → ${res.status} ${await res.text()}`);
44
+ }
45
+ return await res.json();
46
+ }
47
+ async walk(path, key, fromHeight, toHeight, extraParams = {}) {
48
+ const out = [];
49
+ let cursor = null;
50
+ do {
51
+ const params = new URLSearchParams({
52
+ to_height: String(toHeight),
53
+ limit: String(PAGE_LIMIT),
54
+ ...extraParams
55
+ });
56
+ if (cursor)
57
+ params.set("cursor", cursor);
58
+ else
59
+ params.set("from_height", String(fromHeight));
60
+ const env = await this.get(`${this.indexBaseUrl}${path}?${params}`, this.indexApiKey);
61
+ out.push(...env[key]);
62
+ cursor = env.next_cursor;
63
+ } while (cursor);
64
+ return out;
65
+ }
66
+ walkBlocks(fromHeight, toHeight) {
67
+ return this.walk("/v1/index/blocks", "blocks", fromHeight, toHeight);
68
+ }
69
+ walkEvents(eventType, fromHeight, toHeight) {
70
+ return this.walk("/v1/index/events", "events", fromHeight, toHeight, { event_type: eventType });
71
+ }
72
+ walkTransactions(fromHeight, toHeight) {
73
+ return this.walk("/v1/index/transactions", "transactions", fromHeight, toHeight);
74
+ }
75
+ async getStreamsTip() {
76
+ const tip = await this.get(`${this.streamsBaseUrl}/v1/streams/tip`, this.streamsApiKey);
77
+ return Number(tip.block_height) || 0;
78
+ }
79
+ async getIndexTip() {
80
+ const env = await this.get(`${this.indexBaseUrl}/v1/index/blocks?limit=1`, this.indexApiKey);
81
+ return Number(env.tip?.block_height) || 0;
82
+ }
83
+ async listReorgs(since) {
84
+ const params = new URLSearchParams({ since });
85
+ return this.get(`${this.streamsBaseUrl}/v1/streams/reorgs?${params}`, this.streamsApiKey);
86
+ }
87
+ }
88
+ export {
89
+ IndexHttpClient
90
+ };
91
+
92
+ //# debugId=E1C3D9E21DD5904564756E2164756E21
93
+ //# sourceMappingURL=index-http.js.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index-internal-auth.ts", "../src/index-http.ts"],
4
+ "sourcesContent": [
5
+ "// Internal service credential for first-party consumers of /v1/index over HTTP\n// (e.g. the subgraph processor's PublicApiBlockSource). Seeded into the Index\n// token store as an enterprise tenant with NO account_id, so these reads are\n// unmetered (Index metering gates on account_id). Mirrors the Streams internal\n// key (packages/indexer/src/l2/internal-auth.ts). Lives in shared so both the\n// API (seed) and the subgraph processor (consumer) import it without a cycle.\nexport const INDEX_INTERNAL_TENANT_ID = \"tenant_index_internal\";\n\nconst DEFAULT_INDEX_INTERNAL_API_KEY = \"sk-sl_index_internal\";\n\nexport function defaultInternalIndexApiKey(): string {\n\treturn process.env.INDEX_INTERNAL_API_KEY || DEFAULT_INDEX_INTERNAL_API_KEY;\n}\n",
6
+ "import { defaultInternalIndexApiKey } from \"./index-internal-auth.ts\";\n\n/**\n * Low-level transport for the public Index (`/v1/index`) + Streams clock\n * (`/v1/streams`) HTTP APIs: cursor-paginated reads, tip, reorgs. Lives in\n * `shared` (a leaf both the SDK and the subgraph runtime depend on) so the wire\n * format has one home and no package cycle. The SDK's ergonomic client should\n * eventually consume these row types too (see the plan's convergence task).\n *\n * This is intentionally minimal — just the GETs the subgraph runtime's\n * PublicApiBlockSource needs. It is NOT the SDK's full client (walk/consume/\n * retries/auth resolution).\n */\n\nconst PAGE_LIMIT = 1000;\n\ntype Envelope<K extends string, T> = {\n\t[P in K]: T[];\n} & { next_cursor: string | null };\n\n// ── Index API wire shapes (single source of truth) ─────────────────────────\nexport type IndexBlockRow = {\n\tblock_height: number;\n\tblock_hash: string;\n\tparent_hash: string;\n\tburn_block_height: number;\n\tburn_block_hash: string | null;\n\tblock_time: string | null;\n};\n\ntype IndexEventCommon = {\n\tblock_height: number;\n\ttx_id: string;\n\tevent_index: number;\n\tcontract_id: string | null;\n};\n\nexport type IndexEventRow = IndexEventCommon &\n\t(\n\t\t| {\n\t\t\t\tevent_type: \"ft_transfer\" | \"ft_mint\" | \"ft_burn\";\n\t\t\t\tasset_identifier: string;\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tamount: string;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"nft_transfer\" | \"nft_mint\" | \"nft_burn\";\n\t\t\t\tasset_identifier: string;\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tvalue: string;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"stx_transfer\" | \"stx_mint\" | \"stx_burn\";\n\t\t\t\tsender?: string;\n\t\t\t\trecipient?: string;\n\t\t\t\tamount: string;\n\t\t\t\tmemo?: string | null;\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"stx_lock\";\n\t\t\t\tsender: string;\n\t\t\t\tamount: string;\n\t\t\t\tpayload: { unlock_height: string | null };\n\t\t }\n\t\t| {\n\t\t\t\tevent_type: \"print\";\n\t\t\t\tpayload: {\n\t\t\t\t\ttopic: string | null;\n\t\t\t\t\tvalue: unknown;\n\t\t\t\t\traw_value: string | null;\n\t\t\t\t};\n\t\t }\n\t);\n\nexport type IndexTransactionRow = {\n\ttx_id: string;\n\tblock_height: number;\n\ttx_index: number;\n\ttx_type: string;\n\tsender: string;\n\tstatus: string;\n\tcontract_call?: {\n\t\tcontract_id: string;\n\t\tfunction_name: string;\n\t\tfunction_args_hex?: string[] | null;\n\t\tresult_hex?: string | null;\n\t} | null;\n\tsmart_contract?: { contract_id: string | null } | null;\n};\n\nexport type StreamsReorgRow = {\n\tdetected_at: string;\n\tfork_point_height: number;\n\torphaned_range: { from: string; to: string };\n\tnew_canonical_tip: string;\n};\n\nexport type IndexHttpOptions = {\n\t/** Base URL for /v1/index (the decoded data plane). */\n\tindexBaseUrl: string;\n\t/** Bearer for /v1/index. Defaults to the internal enterprise key. */\n\tindexApiKey?: string;\n\t/** Base URL for /v1/streams (the canonical clock). */\n\tstreamsBaseUrl: string;\n\t/** Bearer for /v1/streams (internal enterprise key). */\n\tstreamsApiKey: string;\n};\n\nexport class IndexHttpClient {\n\tprivate readonly indexBaseUrl: string;\n\tprivate readonly indexApiKey: string;\n\tprivate readonly streamsBaseUrl: string;\n\tprivate readonly streamsApiKey: string;\n\n\tconstructor(opts: IndexHttpOptions) {\n\t\tthis.indexBaseUrl = opts.indexBaseUrl.replace(/\\/+$/, \"\");\n\t\tthis.indexApiKey = opts.indexApiKey ?? defaultInternalIndexApiKey();\n\t\tthis.streamsBaseUrl = opts.streamsBaseUrl.replace(/\\/+$/, \"\");\n\t\tthis.streamsApiKey = opts.streamsApiKey;\n\t}\n\n\tprivate async get<T>(url: string, apiKey: string): Promise<T> {\n\t\t// Index reads are anon — omit the header entirely when no key is set, so\n\t\t// an empty key reads anonymously rather than 401-ing as an invalid bearer.\n\t\tconst res = await fetch(url, {\n\t\t\theaders: apiKey ? { authorization: `Bearer ${apiKey}` } : {},\n\t\t});\n\t\tif (!res.ok) {\n\t\t\tthrow new Error(`GET ${url} → ${res.status} ${await res.text()}`);\n\t\t}\n\t\treturn (await res.json()) as T;\n\t}\n\n\t/** Drain a cursor-paginated Index collection over [fromHeight, toHeight]. */\n\tprivate async walk<K extends string, T>(\n\t\tpath: string,\n\t\tkey: K,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t\textraParams: Record<string, string> = {},\n\t): Promise<T[]> {\n\t\tconst out: T[] = [];\n\t\tlet cursor: string | null = null;\n\t\tdo {\n\t\t\tconst params = new URLSearchParams({\n\t\t\t\tto_height: String(toHeight),\n\t\t\t\tlimit: String(PAGE_LIMIT),\n\t\t\t\t...extraParams,\n\t\t\t});\n\t\t\t// from_height and cursor are mutually exclusive — anchor the first page\n\t\t\t// by height, then page forward by cursor only.\n\t\t\tif (cursor) params.set(\"cursor\", cursor);\n\t\t\telse params.set(\"from_height\", String(fromHeight));\n\t\t\tconst env: Envelope<K, T> = await this.get(\n\t\t\t\t`${this.indexBaseUrl}${path}?${params}`,\n\t\t\t\tthis.indexApiKey,\n\t\t\t);\n\t\t\tout.push(...env[key]);\n\t\t\tcursor = env.next_cursor;\n\t\t} while (cursor);\n\t\treturn out;\n\t}\n\n\twalkBlocks(fromHeight: number, toHeight: number): Promise<IndexBlockRow[]> {\n\t\treturn this.walk<\"blocks\", IndexBlockRow>(\n\t\t\t\"/v1/index/blocks\",\n\t\t\t\"blocks\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t);\n\t}\n\n\twalkEvents(\n\t\teventType: string,\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t): Promise<IndexEventRow[]> {\n\t\treturn this.walk<\"events\", IndexEventRow>(\n\t\t\t\"/v1/index/events\",\n\t\t\t\"events\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t\t{ event_type: eventType },\n\t\t);\n\t}\n\n\twalkTransactions(\n\t\tfromHeight: number,\n\t\ttoHeight: number,\n\t): Promise<IndexTransactionRow[]> {\n\t\treturn this.walk<\"transactions\", IndexTransactionRow>(\n\t\t\t\"/v1/index/transactions\",\n\t\t\t\"transactions\",\n\t\t\tfromHeight,\n\t\t\ttoHeight,\n\t\t);\n\t}\n\n\t/** Canonical tip height from the Streams clock. */\n\tasync getStreamsTip(): Promise<number> {\n\t\tconst tip = await this.get<{ block_height: number }>(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/tip`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t\treturn Number(tip.block_height) || 0;\n\t}\n\n\t/**\n\t * Highest block height the Index data plane can serve (tip is inline in every\n\t * envelope). This is the data-availability bound — a consumer must not\n\t * process past it, even if the Streams clock is ahead.\n\t */\n\tasync getIndexTip(): Promise<number> {\n\t\tconst env = await this.get<{ tip: { block_height: number } }>(\n\t\t\t`${this.indexBaseUrl}/v1/index/blocks?limit=1`,\n\t\t\tthis.indexApiKey,\n\t\t);\n\t\treturn Number(env.tip?.block_height) || 0;\n\t}\n\n\t/** Reorgs since a resume token (wall-clock `detected_at`-keyed). */\n\tasync listReorgs(\n\t\tsince: string,\n\t): Promise<{ reorgs: StreamsReorgRow[]; next_since: string | null }> {\n\t\tconst params = new URLSearchParams({ since });\n\t\treturn this.get(\n\t\t\t`${this.streamsBaseUrl}/v1/streams/reorgs?${params}`,\n\t\t\tthis.streamsApiKey,\n\t\t);\n\t}\n}\n"
7
+ ],
8
+ "mappings": ";;;;;;;;;;;;;;;;;AAMO,IAAM,2BAA2B;AAExC,IAAM,iCAAiC;AAEhC,SAAS,0BAA0B,GAAW;AAAA,EACpD,OAAO,QAAQ,IAAI,0BAA0B;AAAA;;;ACG9C,IAAM,aAAa;AAAA;AAgGZ,MAAM,gBAAgB;AAAA,EACX;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,WAAW,CAAC,MAAwB;AAAA,IACnC,KAAK,eAAe,KAAK,aAAa,QAAQ,QAAQ,EAAE;AAAA,IACxD,KAAK,cAAc,KAAK,eAAe,2BAA2B;AAAA,IAClE,KAAK,iBAAiB,KAAK,eAAe,QAAQ,QAAQ,EAAE;AAAA,IAC5D,KAAK,gBAAgB,KAAK;AAAA;AAAA,OAGb,IAAM,CAAC,KAAa,QAA4B;AAAA,IAG7D,MAAM,MAAM,MAAM,MAAM,KAAK;AAAA,MAC5B,SAAS,SAAS,EAAE,eAAe,UAAU,SAAS,IAAI,CAAC;AAAA,IAC5D,CAAC;AAAA,IACD,IAAI,CAAC,IAAI,IAAI;AAAA,MACZ,MAAM,IAAI,MAAM,OAAO,SAAQ,IAAI,UAAU,MAAM,IAAI,KAAK,GAAG;AAAA,IAChE;AAAA,IACA,OAAQ,MAAM,IAAI,KAAK;AAAA;AAAA,OAIV,KAAyB,CACtC,MACA,KACA,YACA,UACA,cAAsC,CAAC,GACxB;AAAA,IACf,MAAM,MAAW,CAAC;AAAA,IAClB,IAAI,SAAwB;AAAA,IAC5B,GAAG;AAAA,MACF,MAAM,SAAS,IAAI,gBAAgB;AAAA,QAClC,WAAW,OAAO,QAAQ;AAAA,QAC1B,OAAO,OAAO,UAAU;AAAA,WACrB;AAAA,MACJ,CAAC;AAAA,MAGD,IAAI;AAAA,QAAQ,OAAO,IAAI,UAAU,MAAM;AAAA,MAClC;AAAA,eAAO,IAAI,eAAe,OAAO,UAAU,CAAC;AAAA,MACjD,MAAM,MAAsB,MAAM,KAAK,IACtC,GAAG,KAAK,eAAe,QAAQ,UAC/B,KAAK,WACN;AAAA,MACA,IAAI,KAAK,GAAG,IAAI,IAAI;AAAA,MACpB,SAAS,IAAI;AAAA,IACd,SAAS;AAAA,IACT,OAAO;AAAA;AAAA,EAGR,UAAU,CAAC,YAAoB,UAA4C;AAAA,IAC1E,OAAO,KAAK,KACX,oBACA,UACA,YACA,QACD;AAAA;AAAA,EAGD,UAAU,CACT,WACA,YACA,UAC2B;AAAA,IAC3B,OAAO,KAAK,KACX,oBACA,UACA,YACA,UACA,EAAE,YAAY,UAAU,CACzB;AAAA;AAAA,EAGD,gBAAgB,CACf,YACA,UACiC;AAAA,IACjC,OAAO,KAAK,KACX,0BACA,gBACA,YACA,QACD;AAAA;AAAA,OAIK,cAAa,GAAoB;AAAA,IACtC,MAAM,MAAM,MAAM,KAAK,IACtB,GAAG,KAAK,iCACR,KAAK,aACN;AAAA,IACA,OAAO,OAAO,IAAI,YAAY,KAAK;AAAA;AAAA,OAQ9B,YAAW,GAAoB;AAAA,IACpC,MAAM,MAAM,MAAM,KAAK,IACtB,GAAG,KAAK,wCACR,KAAK,WACN;AAAA,IACA,OAAO,OAAO,IAAI,KAAK,YAAY,KAAK;AAAA;AAAA,OAInC,WAAU,CACf,OACoE;AAAA,IACpE,MAAM,SAAS,IAAI,gBAAgB,EAAE,MAAM,CAAC;AAAA,IAC5C,OAAO,KAAK,IACX,GAAG,KAAK,oCAAoC,UAC5C,KAAK,aACN;AAAA;AAEF;",
9
+ "debugId": "E1C3D9E21DD5904564756E2164756E21",
10
+ "names": []
11
+ }
@@ -0,0 +1,3 @@
1
+ declare const INDEX_INTERNAL_TENANT_ID = "tenant_index_internal";
2
+ declare function defaultInternalIndexApiKey(): string;
3
+ export { defaultInternalIndexApiKey, INDEX_INTERNAL_TENANT_ID };
@@ -0,0 +1,29 @@
1
+ import { createRequire } from "node:module";
2
+ var __defProp = Object.defineProperty;
3
+ var __returnValue = (v) => v;
4
+ function __exportSetter(name, newValue) {
5
+ this[name] = __returnValue.bind(null, newValue);
6
+ }
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, {
10
+ get: all[name],
11
+ enumerable: true,
12
+ configurable: true,
13
+ set: __exportSetter.bind(all, name)
14
+ });
15
+ };
16
+
17
+ // src/index-internal-auth.ts
18
+ var INDEX_INTERNAL_TENANT_ID = "tenant_index_internal";
19
+ var DEFAULT_INDEX_INTERNAL_API_KEY = "sk-sl_index_internal";
20
+ function defaultInternalIndexApiKey() {
21
+ return process.env.INDEX_INTERNAL_API_KEY || DEFAULT_INDEX_INTERNAL_API_KEY;
22
+ }
23
+ export {
24
+ defaultInternalIndexApiKey,
25
+ INDEX_INTERNAL_TENANT_ID
26
+ };
27
+
28
+ //# debugId=27171696725F807764756E2164756E21
29
+ //# sourceMappingURL=index-internal-auth.js.map
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/index-internal-auth.ts"],
4
+ "sourcesContent": [
5
+ "// Internal service credential for first-party consumers of /v1/index over HTTP\n// (e.g. the subgraph processor's PublicApiBlockSource). Seeded into the Index\n// token store as an enterprise tenant with NO account_id, so these reads are\n// unmetered (Index metering gates on account_id). Mirrors the Streams internal\n// key (packages/indexer/src/l2/internal-auth.ts). Lives in shared so both the\n// API (seed) and the subgraph processor (consumer) import it without a cycle.\nexport const INDEX_INTERNAL_TENANT_ID = \"tenant_index_internal\";\n\nconst DEFAULT_INDEX_INTERNAL_API_KEY = \"sk-sl_index_internal\";\n\nexport function defaultInternalIndexApiKey(): string {\n\treturn process.env.INDEX_INTERNAL_API_KEY || DEFAULT_INDEX_INTERNAL_API_KEY;\n}\n"
6
+ ],
7
+ "mappings": ";;;;;;;;;;;;;;;;;AAMO,IAAM,2BAA2B;AAExC,IAAM,iCAAiC;AAEhC,SAAS,0BAA0B,GAAW;AAAA,EACpD,OAAO,QAAQ,IAAI,0BAA0B;AAAA;",
8
+ "debugId": "27171696725F807764756E2164756E21",
9
+ "names": []
10
+ }
@@ -0,0 +1,26 @@
1
+ import { type Kysely, sql } from "kysely";
2
+
3
+ // Composite index backing /v1/index/events keyset pagination for a bare
4
+ // event-type source (no contract/sender/recipient filter). The reader paginates
5
+ // with `event_type = ? AND (block_height, event_index) > (?, ?)
6
+ // ORDER BY block_height, event_index`. Without a leading-event_type composite,
7
+ // Postgres bitmap-ANDs the single-column event_type index — re-scanning the
8
+ // ENTIRE event-type partition (e.g. ~4.2M `print` rows) on every cursor page,
9
+ // turning a backfill into O(n²) (measured ~6.8s/page vs ~50ms for page one).
10
+ // With this index each page is an index range scan. Mirrors the existing
11
+ // (contract_id|sender|recipient, block_height, event_index) composites that
12
+ // already make filtered queries fast.
13
+ //
14
+ // Already created CONCURRENTLY on prod; IF NOT EXISTS makes this a no-op there
15
+ // and a cheap build on fresh/dev databases (small data).
16
+ export async function up(db: Kysely<unknown>): Promise<void> {
17
+ await sql`CREATE INDEX IF NOT EXISTS decoded_events_type_height_event_idx ON decoded_events (event_type, block_height, event_index) WHERE canonical`.execute(
18
+ db,
19
+ );
20
+ }
21
+
22
+ export async function down(db: Kysely<unknown>): Promise<void> {
23
+ await sql`DROP INDEX IF EXISTS decoded_events_type_height_event_idx`.execute(
24
+ db,
25
+ );
26
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "6.14.0",
3
+ "version": "6.15.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -80,6 +80,14 @@
80
80
  "types": "./dist/src/mode.d.ts",
81
81
  "import": "./dist/src/mode.js"
82
82
  },
83
+ "./index-internal-auth": {
84
+ "types": "./dist/src/index-internal-auth.d.ts",
85
+ "import": "./dist/src/index-internal-auth.js"
86
+ },
87
+ "./index-http": {
88
+ "types": "./dist/src/index-http.d.ts",
89
+ "import": "./dist/src/index-http.js"
90
+ },
83
91
  "./logger": {
84
92
  "types": "./dist/src/logger.d.ts",
85
93
  "import": "./dist/src/logger.js"