@secondlayer/shared 6.28.1 → 6.30.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.
Files changed (48) hide show
  1. package/dist/src/db/index.d.ts +59 -2
  2. package/dist/src/db/index.js +4 -1
  3. package/dist/src/db/index.js.map +3 -3
  4. package/dist/src/db/queries/chain-reorgs.d.ts +53 -1
  5. package/dist/src/db/queries/chain-reorgs.js +4 -1
  6. package/dist/src/db/queries/chain-reorgs.js.map +3 -3
  7. package/dist/src/db/queries/contracts.d.ts +53 -1
  8. package/dist/src/db/queries/integrity.d.ts +53 -1
  9. package/dist/src/db/queries/subgraph-gaps.d.ts +53 -1
  10. package/dist/src/db/queries/subgraph-operations.d.ts +53 -1
  11. package/dist/src/db/queries/subgraphs.d.ts +63 -2
  12. package/dist/src/db/queries/subgraphs.js +16 -1
  13. package/dist/src/db/queries/subgraphs.js.map +4 -4
  14. package/dist/src/db/queries/subscriptions.d.ts +53 -1
  15. package/dist/src/db/schema.d.ts +56 -2
  16. package/dist/src/errors.d.ts +11 -2
  17. package/dist/src/errors.js +14 -2
  18. package/dist/src/errors.js.map +3 -3
  19. package/dist/src/index-http.d.ts +3 -0
  20. package/dist/src/index-http.js +12 -1
  21. package/dist/src/index-http.js.map +3 -3
  22. package/dist/src/index.d.ts +91 -11
  23. package/dist/src/index.js +101 -38
  24. package/dist/src/index.js.map +7 -7
  25. package/dist/src/node/hiro-client.d.ts +15 -1
  26. package/dist/src/node/hiro-client.js +10 -1
  27. package/dist/src/node/hiro-client.js.map +3 -3
  28. package/dist/src/node/local-client.d.ts +53 -1
  29. package/dist/src/schemas/index.d.ts +14 -0
  30. package/dist/src/schemas/index.js +3 -2
  31. package/dist/src/schemas/index.js.map +3 -3
  32. package/dist/src/schemas/subgraphs.d.ts +14 -0
  33. package/dist/src/schemas/subgraphs.js +3 -2
  34. package/dist/src/schemas/subgraphs.js.map +3 -3
  35. package/dist/src/subgraphs/spec.d.ts +3 -0
  36. package/dist/src/subgraphs/spec.js +83 -36
  37. package/dist/src/subgraphs/spec.js.map +3 -3
  38. package/dist/src/x402.d.ts +38 -0
  39. package/dist/src/x402.js +74 -0
  40. package/dist/src/x402.js.map +10 -0
  41. package/migrations/0090_events_streams_filter_idx.ts +8 -0
  42. package/migrations/0091_x402_payments.ts +41 -0
  43. package/migrations/0092_subgraph_visibility.ts +33 -0
  44. package/migrations/0093_ghost_accounts.ts +47 -0
  45. package/migrations/0094_paid_subgraph_deploys.ts +39 -0
  46. package/migrations/0095_x402_balances.ts +37 -0
  47. package/migrations/0096_x402_continuity.ts +48 -0
  48. package/package.json +6 -2
@@ -0,0 +1,33 @@
1
+ import { type Kysely, sql } from "kysely";
2
+ import { onControlPlane } from "../src/db/migration-role.ts";
3
+
4
+ // Subgraph read visibility. 'public' = anon-readable on /v1/subgraphs/:name
5
+ // (wildcard CORS, anon rate limits); 'private' = reads require the owning
6
+ // account's bearer key, anon resolution 404s. Existing rows default 'private'
7
+ // so nothing already deployed becomes world-readable; the public default for
8
+ // new managed deploys is applied at the API layer, not here. Public names are
9
+ // a single global namespace (claim-on-publish), enforced by the partial
10
+ // unique index. Control-plane (TARGET).
11
+ export async function up(db: Kysely<unknown>): Promise<void> {
12
+ await sql`SET lock_timeout = '30s'`.execute(db);
13
+ await onControlPlane(async () => {
14
+ await sql`
15
+ ALTER TABLE subgraphs
16
+ ADD COLUMN visibility TEXT NOT NULL DEFAULT 'private'
17
+ CHECK (visibility IN ('public', 'private'))
18
+ `.execute(db);
19
+ await sql`
20
+ CREATE UNIQUE INDEX subgraphs_public_name_uidx
21
+ ON subgraphs (name) WHERE visibility = 'public'
22
+ `.execute(db);
23
+ });
24
+ }
25
+
26
+ export async function down(db: Kysely<unknown>): Promise<void> {
27
+ await onControlPlane(async () => {
28
+ await sql`DROP INDEX IF EXISTS subgraphs_public_name_uidx`.execute(db);
29
+ await sql`ALTER TABLE subgraphs DROP COLUMN IF EXISTS visibility`.execute(
30
+ db,
31
+ );
32
+ });
33
+ }
@@ -0,0 +1,47 @@
1
+ import { type Kysely, sql } from "kysely";
2
+ import { onControlPlane } from "../src/db/migration-role.ts";
3
+
4
+ // Ghost accounts: anonymous self-serve API keys. `POST /v1/keys` with no auth
5
+ // mints an account with ghost=true and email NULL; a claim token (hash stored,
6
+ // raw returned once in the claim URL) later attaches an email via the magic-link
7
+ // flow. Email's NOT NULL is dropped; the existing plain UNIQUE constraint stays —
8
+ // Postgres unique constraints ignore NULLs (multiple NULL emails coexist), so
9
+ // `ON CONFLICT (email)` upserts keep working unchanged and no partial index is
10
+ // needed. Control-plane (TARGET).
11
+ export async function up(db: Kysely<unknown>): Promise<void> {
12
+ await sql`SET lock_timeout = '30s'`.execute(db);
13
+ await onControlPlane(async () => {
14
+ await sql`
15
+ ALTER TABLE accounts
16
+ ADD COLUMN ghost BOOLEAN NOT NULL DEFAULT false
17
+ `.execute(db);
18
+ await sql`ALTER TABLE accounts ALTER COLUMN email DROP NOT NULL`.execute(
19
+ db,
20
+ );
21
+ await sql`
22
+ CREATE TABLE claim_tokens (
23
+ id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
24
+ account_id UUID NOT NULL REFERENCES accounts(id) ON DELETE CASCADE,
25
+ token_hash TEXT NOT NULL UNIQUE,
26
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
27
+ expires_at TIMESTAMPTZ NOT NULL,
28
+ used_at TIMESTAMPTZ
29
+ )
30
+ `.execute(db);
31
+ // Sweeper + claim flow look tokens up by account.
32
+ await sql`CREATE INDEX claim_tokens_account_id_idx ON claim_tokens (account_id)`.execute(
33
+ db,
34
+ );
35
+ });
36
+ }
37
+
38
+ export async function down(db: Kysely<unknown>): Promise<void> {
39
+ await onControlPlane(async () => {
40
+ await sql`DROP TABLE IF EXISTS claim_tokens`.execute(db);
41
+ // Unclaimed ghosts have NULL emails — they cannot survive a NOT NULL
42
+ // restore, so drop them before reinstating the constraint.
43
+ await sql`DELETE FROM accounts WHERE email IS NULL`.execute(db);
44
+ await sql`ALTER TABLE accounts ALTER COLUMN email SET NOT NULL`.execute(db);
45
+ await sql`ALTER TABLE accounts DROP COLUMN IF EXISTS ghost`.execute(db);
46
+ });
47
+ }
@@ -0,0 +1,39 @@
1
+ import { sql } from "kysely";
2
+ import type { Kysely } from "kysely";
3
+ import { onControlPlane } from "../src/db/migration-role.ts";
4
+
5
+ /**
6
+ * x402-paid subgraph deploys (agent-owned indexers).
7
+ *
8
+ * - `accounts.wallet_principal`: a Stacks principal that owns a wallet-ghost
9
+ * account — the identity behind accountless paid deploys. One account per
10
+ * principal (partial unique; NULL for every other account).
11
+ * - `subgraphs.expires_at`: paid deploys live for a bounded window unless
12
+ * renewed (paid) or the owning account is claimed; NULL = no expiry
13
+ * (every non-paid subgraph).
14
+ */
15
+ export async function up(db: Kysely<unknown>): Promise<void> {
16
+ await onControlPlane(async () => {
17
+ await sql`ALTER TABLE accounts ADD COLUMN wallet_principal TEXT`.execute(
18
+ db,
19
+ );
20
+ await sql`CREATE UNIQUE INDEX accounts_wallet_principal_uidx ON accounts (wallet_principal) WHERE wallet_principal IS NOT NULL`.execute(
21
+ db,
22
+ );
23
+ await sql`ALTER TABLE subgraphs ADD COLUMN expires_at TIMESTAMPTZ`.execute(
24
+ db,
25
+ );
26
+ });
27
+ }
28
+
29
+ export async function down(db: Kysely<unknown>): Promise<void> {
30
+ await onControlPlane(async () => {
31
+ await sql`ALTER TABLE subgraphs DROP COLUMN IF EXISTS expires_at`.execute(
32
+ db,
33
+ );
34
+ await sql`DROP INDEX IF EXISTS accounts_wallet_principal_uidx`.execute(db);
35
+ await sql`ALTER TABLE accounts DROP COLUMN IF EXISTS wallet_principal`.execute(
36
+ db,
37
+ );
38
+ });
39
+ }
@@ -0,0 +1,37 @@
1
+ import { sql } from "kysely";
2
+ import type { Kysely } from "kysely";
3
+ import { onControlPlane } from "../src/db/migration-role.ts";
4
+
5
+ /**
6
+ * Prepaid x402 credit.
7
+ *
8
+ * - `x402_balances`: one running USD-micros balance per payer principal.
9
+ * Deposits (confirmed-tier on-chain payments) credit it; per-call
10
+ * drawdowns debit it atomically (`balance >= price` guard in the UPDATE,
11
+ * CHECK as the backstop). No per-drawdown ledger rows — deposits are the
12
+ * on-chain accounting record.
13
+ * - `x402_payments.kind`: distinguishes per-call payments from balance
14
+ * deposits in the existing ledger.
15
+ */
16
+ export async function up(db: Kysely<unknown>): Promise<void> {
17
+ await onControlPlane(async () => {
18
+ await sql`
19
+ CREATE TABLE x402_balances (
20
+ principal TEXT PRIMARY KEY,
21
+ balance_usd_micros BIGINT NOT NULL DEFAULT 0 CHECK (balance_usd_micros >= 0),
22
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
23
+ )
24
+ `.execute(db);
25
+ await sql`
26
+ ALTER TABLE x402_payments
27
+ ADD COLUMN kind TEXT NOT NULL DEFAULT 'payment'
28
+ `.execute(db);
29
+ });
30
+ }
31
+
32
+ export async function down(db: Kysely<unknown>): Promise<void> {
33
+ await onControlPlane(async () => {
34
+ await sql`ALTER TABLE x402_payments DROP COLUMN IF EXISTS kind`.execute(db);
35
+ await sql`DROP TABLE IF EXISTS x402_balances`.execute(db);
36
+ });
37
+ }
@@ -0,0 +1,48 @@
1
+ import { sql } from "kysely";
2
+ import type { Kysely } from "kysely";
3
+ import { onControlPlane } from "../src/db/migration-role.ts";
4
+
5
+ /**
6
+ * Wallet→account continuity (the x402 funnel).
7
+ *
8
+ * - `x402_payments.account_id`: once a wallet links to a claimed account, its
9
+ * historical on-chain payments attach here (nullable — accountless rows
10
+ * stay unlinked; SET NULL survives account deletion/sweeps).
11
+ * - `x402_balances.spent_month` / `spent_month_usd_micros`: rolling
12
+ * month-bucketed consumption per principal — powers the "spent $X this
13
+ * month, Pro removes the meter" nudge without re-pricing ledger rows.
14
+ */
15
+ export async function up(db: Kysely<unknown>): Promise<void> {
16
+ await onControlPlane(async () => {
17
+ await sql`
18
+ ALTER TABLE x402_payments
19
+ ADD COLUMN account_id UUID REFERENCES accounts(id) ON DELETE SET NULL
20
+ `.execute(db);
21
+ await sql`
22
+ CREATE INDEX x402_payments_account_idx ON x402_payments (account_id)
23
+ WHERE account_id IS NOT NULL
24
+ `.execute(db);
25
+ await sql`ALTER TABLE x402_balances ADD COLUMN spent_month TEXT`.execute(
26
+ db,
27
+ );
28
+ await sql`
29
+ ALTER TABLE x402_balances
30
+ ADD COLUMN spent_month_usd_micros BIGINT NOT NULL DEFAULT 0
31
+ `.execute(db);
32
+ });
33
+ }
34
+
35
+ export async function down(db: Kysely<unknown>): Promise<void> {
36
+ await onControlPlane(async () => {
37
+ await sql`ALTER TABLE x402_balances DROP COLUMN IF EXISTS spent_month_usd_micros`.execute(
38
+ db,
39
+ );
40
+ await sql`ALTER TABLE x402_balances DROP COLUMN IF EXISTS spent_month`.execute(
41
+ db,
42
+ );
43
+ await sql`DROP INDEX IF EXISTS x402_payments_account_idx`.execute(db);
44
+ await sql`ALTER TABLE x402_payments DROP COLUMN IF EXISTS account_id`.execute(
45
+ db,
46
+ );
47
+ });
48
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@secondlayer/shared",
3
- "version": "6.28.1",
3
+ "version": "6.30.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "type": "git",
@@ -112,6 +112,10 @@
112
112
  "types": "./dist/src/errors.d.ts",
113
113
  "import": "./dist/src/errors.js"
114
114
  },
115
+ "./x402": {
116
+ "types": "./dist/src/x402.d.ts",
117
+ "import": "./dist/src/x402.js"
118
+ },
115
119
  "./crypto/secrets": {
116
120
  "types": "./dist/src/crypto/secrets.d.ts",
117
121
  "import": "./dist/src/crypto/secrets.js"
@@ -163,7 +167,7 @@
163
167
  "prepublishOnly": "bun run build"
164
168
  },
165
169
  "dependencies": {
166
- "@secondlayer/stacks": "^2.4.0",
170
+ "@secondlayer/stacks": "^2.5.0",
167
171
  "kysely": "0.28.15",
168
172
  "kysely-postgres-js": "3.0.0",
169
173
  "postgres": "^3.4.6",