@secondlayer/shared 11.19.3 → 11.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/archive/range-digest.d.ts +61 -1
- package/dist/src/archive/semantic-digest-builder.d.ts +61 -1
- package/dist/src/classify/index.d.ts +83 -0
- package/dist/src/classify/index.js +464 -0
- package/dist/src/classify/index.js.map +18 -0
- package/dist/src/coverage/index.d.ts +61 -1
- package/dist/src/db/index.d.ts +72 -2
- package/dist/src/db/index.js +5 -1
- package/dist/src/db/index.js.map +3 -3
- package/dist/src/db/queries/chain-reorgs.d.ts +61 -1
- package/dist/src/db/queries/chain-reorgs.js +5 -1
- package/dist/src/db/queries/chain-reorgs.js.map +3 -3
- package/dist/src/db/queries/contracts.d.ts +61 -1
- package/dist/src/db/queries/instance.d.ts +61 -1
- package/dist/src/db/queries/integrity.d.ts +61 -1
- package/dist/src/db/queries/subgraph-gaps.d.ts +61 -1
- package/dist/src/db/queries/subgraph-operations.d.ts +61 -1
- package/dist/src/db/queries/subgraphs.d.ts +61 -1
- package/dist/src/db/queries/sync-scope.d.ts +61 -1
- package/dist/src/db/queries/webhooks.d.ts +61 -1
- package/dist/src/db/schema.d.ts +68 -2
- package/dist/src/error-envelope.d.ts +48 -0
- package/dist/src/error-envelope.js +76 -0
- package/dist/src/error-envelope.js.map +10 -0
- package/dist/src/index.d.ts +72 -2
- package/dist/src/index.js +5 -1
- package/dist/src/index.js.map +3 -3
- package/dist/src/leader.d.ts +4 -1
- package/dist/src/leader.js +3 -1
- package/dist/src/leader.js.map +3 -3
- package/dist/src/node/local-client.d.ts +71 -2
- package/dist/src/node/local-client.js +4 -2
- package/dist/src/node/local-client.js.map +3 -3
- package/dist/src/queue/listener.js +5 -1
- package/dist/src/queue/listener.js.map +3 -3
- package/dist/src/types.d.ts +0 -1
- package/migrations/0153_vm_events_height_ordinal_pk.ts +80 -0
- package/migrations/0154_state_writes.ts +62 -0
- package/migrations/0155_api_failed_requests.ts +37 -0
- package/migrations/0156_feedback_tickets.ts +47 -0
- package/package.json +17 -1
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
import { onChainPlane } from "../src/db/migration-role.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* `vm_events` primary key = `(block_height, ordinal)`, the row's logical id.
|
|
6
|
+
*
|
|
7
|
+
* The random `id UUID` key put every insert on a random page of a 5 GB index,
|
|
8
|
+
* and that random read was most of the indexer's write time on a full sync.
|
|
9
|
+
* The logical key is append-ordered, so inserts touch the right edge only.
|
|
10
|
+
*
|
|
11
|
+
* Catalog-only on any size table: `vm_events_logical_id_uniq` already holds
|
|
12
|
+
* the key, so it is promoted (`ADD PRIMARY KEY USING INDEX`, no rebuild, no
|
|
13
|
+
* NOT NULL scan: both columns are already NOT NULL) and `DROP COLUMN` only
|
|
14
|
+
* marks the column dropped. The ACCESS EXCLUSIVE lock is held for the catalog
|
|
15
|
+
* swap, not a table pass. Only a DB missing the unique index builds it inline.
|
|
16
|
+
*
|
|
17
|
+
* Indexes dropped:
|
|
18
|
+
* - `vm_events_block_height_idx`: a prefix of the new key.
|
|
19
|
+
* - `vm_events_type_height_idx`: a prefix of
|
|
20
|
+
* `vm_events_type_height_ordinal_idx`, which the Index and Streams vm
|
|
21
|
+
* readers seek (`type`, height range, `ORDER BY block_height, ordinal`).
|
|
22
|
+
* Kept: `vm_events_tx_id_idx`. No reader filters vm_events by tx_id, but
|
|
23
|
+
* `vm_events_tx_id_fkey` cascades from `transactions`; without it every
|
|
24
|
+
* transaction delete (reorg replace, repair) seq-scans vm_events.
|
|
25
|
+
*
|
|
26
|
+
* `vm_events_archive.id` stays TEXT; archives now write
|
|
27
|
+
* `block_height:ordinal` there (see `archiveOrphanedHeight`).
|
|
28
|
+
*/
|
|
29
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
30
|
+
await onChainPlane(async () => {
|
|
31
|
+
await sql`SET LOCAL lock_timeout = '30s'`.execute(db);
|
|
32
|
+
await sql`SET LOCAL statement_timeout = 0`.execute(db);
|
|
33
|
+
|
|
34
|
+
const hasIdColumn = await sql<{ one: number }>`
|
|
35
|
+
SELECT 1 AS one FROM information_schema.columns
|
|
36
|
+
WHERE table_schema = current_schema()
|
|
37
|
+
AND table_name = 'vm_events' AND column_name = 'id'
|
|
38
|
+
`.execute(db);
|
|
39
|
+
if (hasIdColumn.rows.length === 0) return;
|
|
40
|
+
|
|
41
|
+
await sql`
|
|
42
|
+
CREATE UNIQUE INDEX IF NOT EXISTS vm_events_logical_id_uniq
|
|
43
|
+
ON vm_events (block_height, ordinal)
|
|
44
|
+
`.execute(db);
|
|
45
|
+
await sql`ALTER TABLE vm_events DROP CONSTRAINT vm_events_pkey`.execute(db);
|
|
46
|
+
await sql`
|
|
47
|
+
ALTER TABLE vm_events
|
|
48
|
+
ADD CONSTRAINT vm_events_pkey PRIMARY KEY
|
|
49
|
+
USING INDEX vm_events_logical_id_uniq
|
|
50
|
+
`.execute(db);
|
|
51
|
+
await sql`ALTER TABLE vm_events DROP COLUMN id`.execute(db);
|
|
52
|
+
await sql`DROP INDEX IF EXISTS vm_events_block_height_idx`.execute(db);
|
|
53
|
+
await sql`DROP INDEX IF EXISTS vm_events_type_height_idx`.execute(db);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
58
|
+
await onChainPlane(async () => {
|
|
59
|
+
await sql`SET LOCAL lock_timeout = '30s'`.execute(db);
|
|
60
|
+
await sql`SET LOCAL statement_timeout = 0`.execute(db);
|
|
61
|
+
await sql`
|
|
62
|
+
CREATE INDEX IF NOT EXISTS vm_events_type_height_idx
|
|
63
|
+
ON vm_events (type, block_height)
|
|
64
|
+
`.execute(db);
|
|
65
|
+
await sql`
|
|
66
|
+
CREATE INDEX IF NOT EXISTS vm_events_block_height_idx
|
|
67
|
+
ON vm_events (block_height)
|
|
68
|
+
`.execute(db);
|
|
69
|
+
await sql`
|
|
70
|
+
ALTER TABLE vm_events
|
|
71
|
+
ADD COLUMN IF NOT EXISTS id UUID NOT NULL DEFAULT gen_random_uuid()
|
|
72
|
+
`.execute(db);
|
|
73
|
+
await sql`ALTER TABLE vm_events DROP CONSTRAINT vm_events_pkey`.execute(db);
|
|
74
|
+
await sql`ALTER TABLE vm_events ADD PRIMARY KEY (id)`.execute(db);
|
|
75
|
+
await sql`
|
|
76
|
+
CREATE UNIQUE INDEX IF NOT EXISTS vm_events_logical_id_uniq
|
|
77
|
+
ON vm_events (block_height, ordinal)
|
|
78
|
+
`.execute(db);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
import { onChainPlane } from "../src/db/migration-role.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Opt-in node `state_writes` (`events_keys` `"state_writes"`): the exact MARF
|
|
6
|
+
* writes a block committed, captured at the storage layer. Replaces the
|
|
7
|
+
* evaluator-hooked `vm_events` var_set/map_* rows for verification: every
|
|
8
|
+
* changed MARF leaf in a block must be named here.
|
|
9
|
+
*
|
|
10
|
+
* Key = `(block_height, ordinal)`, the node's per-block write order, so inserts
|
|
11
|
+
* append at the right edge (same shape as `vm_events` after 0153). No other
|
|
12
|
+
* index: nothing reads this table yet.
|
|
13
|
+
*
|
|
14
|
+
* `tx_index` is stored as delivered, not resolved to a `tx_id` FK. It is null
|
|
15
|
+
* for block-level writes, and a `transactions` FK would cascade this table off
|
|
16
|
+
* every transaction sweep (child-range repair, re-mine moves) the way
|
|
17
|
+
* `vm_events.tx_id` does. Rows are height-scoped only: they follow `blocks` via
|
|
18
|
+
* ON DELETE CASCADE, like every vm row sweep relies on (0133).
|
|
19
|
+
*/
|
|
20
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
21
|
+
await onChainPlane(async () => {
|
|
22
|
+
await sql`SET LOCAL lock_timeout = '30s'`.execute(db);
|
|
23
|
+
|
|
24
|
+
await sql`
|
|
25
|
+
CREATE TABLE IF NOT EXISTS state_writes (
|
|
26
|
+
block_height BIGINT NOT NULL
|
|
27
|
+
REFERENCES blocks (height) ON DELETE CASCADE,
|
|
28
|
+
ordinal INTEGER NOT NULL,
|
|
29
|
+
tx_index INTEGER NULL,
|
|
30
|
+
key TEXT NOT NULL,
|
|
31
|
+
value_hex TEXT NOT NULL,
|
|
32
|
+
CONSTRAINT state_writes_pkey PRIMARY KEY (block_height, ordinal)
|
|
33
|
+
)
|
|
34
|
+
`.execute(db);
|
|
35
|
+
|
|
36
|
+
await sql`
|
|
37
|
+
CREATE TABLE IF NOT EXISTS state_writes_archive (
|
|
38
|
+
archive_id BIGSERIAL PRIMARY KEY,
|
|
39
|
+
id TEXT NOT NULL,
|
|
40
|
+
block_height BIGINT NOT NULL,
|
|
41
|
+
ordinal INTEGER NOT NULL,
|
|
42
|
+
tx_index INTEGER NULL,
|
|
43
|
+
key TEXT NOT NULL,
|
|
44
|
+
value_hex TEXT NOT NULL,
|
|
45
|
+
orphaned_block_hash TEXT,
|
|
46
|
+
archived_at TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
47
|
+
)
|
|
48
|
+
`.execute(db);
|
|
49
|
+
|
|
50
|
+
await sql`
|
|
51
|
+
CREATE INDEX IF NOT EXISTS state_writes_archive_height_idx
|
|
52
|
+
ON state_writes_archive (block_height)
|
|
53
|
+
`.execute(db);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
58
|
+
await onChainPlane(async () => {
|
|
59
|
+
await sql`DROP TABLE IF EXISTS state_writes_archive`.execute(db);
|
|
60
|
+
await sql`DROP TABLE IF EXISTS state_writes`.execute(db);
|
|
61
|
+
});
|
|
62
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
import { onControlPlane } from "../src/db/migration-role.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* 24h, account-scoped record of failed hosted API requests (method, path,
|
|
6
|
+
* status, code, short message, redacted query). Written fire-and-forget by
|
|
7
|
+
* the error-envelope middleware and read back as server-side evidence when
|
|
8
|
+
* a caller reports a problem by `request_id`. Purged hourly by the worker.
|
|
9
|
+
*/
|
|
10
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
11
|
+
await onControlPlane(async () => {
|
|
12
|
+
await sql`
|
|
13
|
+
CREATE TABLE api_failed_requests (
|
|
14
|
+
request_id text PRIMARY KEY,
|
|
15
|
+
account_id uuid NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
16
|
+
method text NOT NULL,
|
|
17
|
+
path text NOT NULL,
|
|
18
|
+
status integer NOT NULL,
|
|
19
|
+
code text NOT NULL,
|
|
20
|
+
message text NOT NULL DEFAULT '',
|
|
21
|
+
query jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
22
|
+
origin text,
|
|
23
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
24
|
+
)
|
|
25
|
+
`.execute(db);
|
|
26
|
+
await sql`
|
|
27
|
+
CREATE INDEX api_failed_requests_created_at_idx
|
|
28
|
+
ON api_failed_requests (created_at)
|
|
29
|
+
`.execute(db);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
34
|
+
await onControlPlane(async () => {
|
|
35
|
+
await sql`DROP TABLE IF EXISTS api_failed_requests`.execute(db);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { type Kysely, sql } from "kysely";
|
|
2
|
+
import { onControlPlane } from "../src/db/migration-role.ts";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Hosted problem reports filed by agents at `POST /v1/feedback`. `attempted`
|
|
6
|
+
* is copied from `api_failed_requests` by the server, never taken from the
|
|
7
|
+
* caller. `status` starts `new`; the classify worker moves it to
|
|
8
|
+
* `classified` and fills `route` / `classification`. One idempotency key per
|
|
9
|
+
* account de-duplicates retries (NULL keys are always distinct).
|
|
10
|
+
*/
|
|
11
|
+
export async function up(db: Kysely<unknown>): Promise<void> {
|
|
12
|
+
await onControlPlane(async () => {
|
|
13
|
+
await sql`SET lock_timeout = '30s'`.execute(db);
|
|
14
|
+
|
|
15
|
+
await sql`
|
|
16
|
+
CREATE TABLE feedback_tickets (
|
|
17
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
18
|
+
account_id uuid NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
|
|
19
|
+
idempotency_key text,
|
|
20
|
+
request_id text,
|
|
21
|
+
intent text NOT NULL,
|
|
22
|
+
expected jsonb,
|
|
23
|
+
kind_hint text,
|
|
24
|
+
evidence jsonb,
|
|
25
|
+
attempted jsonb,
|
|
26
|
+
origin text NOT NULL,
|
|
27
|
+
status text NOT NULL DEFAULT 'new' CHECK (status IN ('new', 'classified')),
|
|
28
|
+
route text,
|
|
29
|
+
classification jsonb,
|
|
30
|
+
classified_at timestamptz,
|
|
31
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
32
|
+
UNIQUE (account_id, idempotency_key)
|
|
33
|
+
)
|
|
34
|
+
`.execute(db);
|
|
35
|
+
|
|
36
|
+
await sql`
|
|
37
|
+
CREATE INDEX feedback_tickets_new_idx
|
|
38
|
+
ON feedback_tickets (created_at) WHERE status = 'new'
|
|
39
|
+
`.execute(db);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function down(db: Kysely<unknown>): Promise<void> {
|
|
44
|
+
await onControlPlane(async () => {
|
|
45
|
+
await sql`DROP TABLE IF EXISTS feedback_tickets`.execute(db);
|
|
46
|
+
});
|
|
47
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@secondlayer/shared",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.21.0",
|
|
4
4
|
"description": "Internal building blocks shared across Secondlayer packages.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -97,6 +97,10 @@
|
|
|
97
97
|
"types": "./dist/src/mode.d.ts",
|
|
98
98
|
"import": "./dist/src/mode.js"
|
|
99
99
|
},
|
|
100
|
+
"./error-envelope": {
|
|
101
|
+
"types": "./dist/src/error-envelope.d.ts",
|
|
102
|
+
"import": "./dist/src/error-envelope.js"
|
|
103
|
+
},
|
|
100
104
|
"./index-internal-auth": {
|
|
101
105
|
"types": "./dist/src/index-internal-auth.d.ts",
|
|
102
106
|
"import": "./dist/src/index-internal-auth.js"
|
|
@@ -224,6 +228,10 @@
|
|
|
224
228
|
"./email": {
|
|
225
229
|
"types": "./dist/src/email/index.d.ts",
|
|
226
230
|
"import": "./dist/src/email/index.js"
|
|
231
|
+
},
|
|
232
|
+
"./classify": {
|
|
233
|
+
"types": "./dist/src/classify/index.d.ts",
|
|
234
|
+
"import": "./dist/src/classify/index.js"
|
|
227
235
|
}
|
|
228
236
|
},
|
|
229
237
|
"files": ["dist", "migrations"],
|
|
@@ -243,6 +251,14 @@
|
|
|
243
251
|
"postgres": "^3.4.6",
|
|
244
252
|
"zod": "^4.3.6"
|
|
245
253
|
},
|
|
254
|
+
"peerDependencies": {
|
|
255
|
+
"ai": "7.0.133"
|
|
256
|
+
},
|
|
257
|
+
"peerDependenciesMeta": {
|
|
258
|
+
"ai": {
|
|
259
|
+
"optional": true
|
|
260
|
+
}
|
|
261
|
+
},
|
|
246
262
|
"devDependencies": {
|
|
247
263
|
"@types/bun": "latest",
|
|
248
264
|
"typescript": "^5"
|