@ocap/statedb-sqlite 1.29.5
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/README.md +66 -0
- package/esm/_virtual/rolldown_runtime.mjs +21 -0
- package/esm/db.d.mts +92 -0
- package/esm/db.mjs +257 -0
- package/esm/index.d.mts +4 -0
- package/esm/index.mjs +7 -0
- package/esm/interfaces.d.mts +201 -0
- package/esm/interfaces.mjs +1 -0
- package/esm/kysely.d.mts +43 -0
- package/esm/kysely.mjs +62 -0
- package/esm/migrations/001-genesis.d.mts +14 -0
- package/esm/migrations/001-genesis.mjs +52 -0
- package/esm/migrations/index.d.mts +24 -0
- package/esm/migrations/index.mjs +60 -0
- package/esm/package.mjs +70 -0
- package/esm/table/account.d.mts +40 -0
- package/esm/table/account.mjs +99 -0
- package/esm/table/balance.d.mts +39 -0
- package/esm/table/balance.mjs +69 -0
- package/esm/table/base.d.mts +84 -0
- package/esm/table/base.mjs +217 -0
- package/esm/table/rollup.d.mts +22 -0
- package/esm/table/rollup.mjs +44 -0
- package/esm/table/token.d.mts +23 -0
- package/esm/table/token.mjs +42 -0
- package/lib/_virtual/rolldown_runtime.cjs +43 -0
- package/lib/db.cjs +259 -0
- package/lib/db.d.cts +92 -0
- package/lib/index.cjs +9 -0
- package/lib/index.d.cts +4 -0
- package/lib/interfaces.cjs +0 -0
- package/lib/interfaces.d.cts +201 -0
- package/lib/kysely.cjs +63 -0
- package/lib/kysely.d.cts +43 -0
- package/lib/migrations/001-genesis.cjs +59 -0
- package/lib/migrations/001-genesis.d.cts +14 -0
- package/lib/migrations/index.cjs +62 -0
- package/lib/migrations/index.d.cts +24 -0
- package/lib/package.cjs +76 -0
- package/lib/table/account.cjs +101 -0
- package/lib/table/account.d.cts +40 -0
- package/lib/table/balance.cjs +71 -0
- package/lib/table/balance.d.cts +39 -0
- package/lib/table/base.cjs +221 -0
- package/lib/table/base.d.cts +84 -0
- package/lib/table/rollup.cjs +45 -0
- package/lib/table/rollup.d.cts +22 -0
- package/lib/table/token.cjs +43 -0
- package/lib/table/token.d.cts +23 -0
- package/package.json +75 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { StateDBTable } from "@ocap/statedb";
|
|
2
|
+
import crypto from "node:crypto";
|
|
3
|
+
import omit from "lodash/omit.js";
|
|
4
|
+
|
|
5
|
+
//#region src/table/base.ts
|
|
6
|
+
/**
|
|
7
|
+
* SQLite State Table base class
|
|
8
|
+
* Extends StateDBTable to provide SQLite-specific implementation
|
|
9
|
+
*/
|
|
10
|
+
var SqliteTable = class SqliteTable extends StateDBTable {
|
|
11
|
+
constructor(config) {
|
|
12
|
+
super(config.uniqIndex);
|
|
13
|
+
this.name = config.name;
|
|
14
|
+
this.db = config.db;
|
|
15
|
+
this.schema = config.schema || {};
|
|
16
|
+
this.balanceTable = config.balanceTable;
|
|
17
|
+
this.syncBalance = config.syncBalance ?? false;
|
|
18
|
+
if (this.syncBalance && !this.balanceTable) throw new Error("balanceTable is required when syncBalance is true");
|
|
19
|
+
this.markReady();
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the Kysely executor (use transaction if available)
|
|
23
|
+
*/
|
|
24
|
+
getExecutor(context) {
|
|
25
|
+
return context?.txn || this.db;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Generate primary key from document
|
|
29
|
+
* Handles both simple string keys and composite keys
|
|
30
|
+
*/
|
|
31
|
+
generatePrimaryKey(doc) {
|
|
32
|
+
if (typeof doc === "string") return doc;
|
|
33
|
+
if (Array.isArray(this.uniqIndex)) {
|
|
34
|
+
const key = this.uniqIndex.map((id) => doc[id]).map((item) => typeof item === "object" ? JSON.stringify(item) : item).join("-");
|
|
35
|
+
return crypto.createHash("md5").update(key).digest("hex");
|
|
36
|
+
}
|
|
37
|
+
return doc[this.uniqIndex];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Convert a key to a query object
|
|
41
|
+
*/
|
|
42
|
+
formatKeyToQuery(key) {
|
|
43
|
+
if (Array.isArray(this.uniqIndex) && typeof key === "object") {
|
|
44
|
+
const keyObj = key;
|
|
45
|
+
return Object.fromEntries(this.uniqIndex.map((k) => [this.mapColumnName(k), keyObj[k]]));
|
|
46
|
+
}
|
|
47
|
+
if (typeof key === "object") {
|
|
48
|
+
const keyObj = key;
|
|
49
|
+
return { [this.mapColumnName(this.primaryKey)]: keyObj[this.primaryKey] };
|
|
50
|
+
}
|
|
51
|
+
return { [this.mapColumnName(this.primaryKey)]: key };
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Map column name for reserved keywords
|
|
55
|
+
*/
|
|
56
|
+
mapColumnName(name) {
|
|
57
|
+
return this.schema.columnMapping?.[name] ?? name;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Reverse map column name
|
|
61
|
+
*/
|
|
62
|
+
unmapColumnName(name) {
|
|
63
|
+
const mapping = this.schema.columnMapping;
|
|
64
|
+
if (!mapping) return name;
|
|
65
|
+
for (const [original, mapped] of Object.entries(mapping)) if (mapped === name) return original;
|
|
66
|
+
return name;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Serialize data for database insert
|
|
70
|
+
* - Converts objects/arrays to JSON strings
|
|
71
|
+
* - Converts booleans to integers (0/1) for SQLite
|
|
72
|
+
* - Maps column names for reserved keywords
|
|
73
|
+
*/
|
|
74
|
+
serializeForDb(data) {
|
|
75
|
+
const result = {};
|
|
76
|
+
const jsonFields = new Set(this.schema.jsonFields || []);
|
|
77
|
+
const booleanFields = new Set(this.schema.booleanFields || []);
|
|
78
|
+
for (const [key, value] of Object.entries(data)) {
|
|
79
|
+
const mappedKey = this.mapColumnName(key);
|
|
80
|
+
if (value === void 0) result[mappedKey] = null;
|
|
81
|
+
else if (booleanFields.has(key) && typeof value === "boolean") result[mappedKey] = value ? 1 : 0;
|
|
82
|
+
else if (jsonFields.has(key) || value !== null && typeof value === "object") result[mappedKey] = JSON.stringify(value);
|
|
83
|
+
else result[mappedKey] = value;
|
|
84
|
+
}
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Deserialize data from database read
|
|
89
|
+
* - Parses JSON strings back to objects
|
|
90
|
+
* - Converts integers (0/1) back to booleans
|
|
91
|
+
* - Unmaps column names
|
|
92
|
+
*/
|
|
93
|
+
deserializeFromDb(data) {
|
|
94
|
+
if (!data) return null;
|
|
95
|
+
const result = {};
|
|
96
|
+
const jsonFields = new Set(this.schema.jsonFields || []);
|
|
97
|
+
const booleanFields = new Set(this.schema.booleanFields || []);
|
|
98
|
+
for (const [key, value] of Object.entries(data)) {
|
|
99
|
+
const unmappedKey = this.unmapColumnName(key);
|
|
100
|
+
if (booleanFields.has(unmappedKey) && typeof value === "number") result[unmappedKey] = value === 1;
|
|
101
|
+
else if (jsonFields.has(unmappedKey) && typeof value === "string") try {
|
|
102
|
+
result[unmappedKey] = JSON.parse(value);
|
|
103
|
+
} catch {
|
|
104
|
+
result[unmappedKey] = value;
|
|
105
|
+
}
|
|
106
|
+
else result[unmappedKey] = value;
|
|
107
|
+
}
|
|
108
|
+
delete result.$loki;
|
|
109
|
+
delete result.meta;
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
async _create(key, attrs = {}, context) {
|
|
113
|
+
const sqlContext = context;
|
|
114
|
+
const executor = this.getExecutor(sqlContext);
|
|
115
|
+
const insertAttrs = { ...attrs };
|
|
116
|
+
if (Array.isArray(this.uniqIndex)) {
|
|
117
|
+
if (typeof key === "object") Object.assign(insertAttrs, key);
|
|
118
|
+
} else {
|
|
119
|
+
const id = this.generatePrimaryKey(key);
|
|
120
|
+
insertAttrs[this.primaryKey] = id;
|
|
121
|
+
}
|
|
122
|
+
const lookupKey = Array.isArray(this.uniqIndex) ? Object.fromEntries(this.uniqIndex.map((k) => [k, insertAttrs[k]])) : insertAttrs[this.primaryKey];
|
|
123
|
+
if (await SqliteTable.prototype._get.call(this, lookupKey, context)) throw new Error(`${this.name} already exists: ${JSON.stringify(key)}`);
|
|
124
|
+
if (this.syncBalance) delete insertAttrs.tokens;
|
|
125
|
+
const serialized = this.serializeForDb(insertAttrs);
|
|
126
|
+
await executor.insertInto(this.name).values(serialized).execute();
|
|
127
|
+
if (this.syncBalance && attrs.tokens) insertAttrs.tokens = await this.balanceTable.updateBalance({
|
|
128
|
+
address: insertAttrs.address,
|
|
129
|
+
tokens: attrs.tokens,
|
|
130
|
+
context: attrs.context
|
|
131
|
+
}, context);
|
|
132
|
+
return insertAttrs;
|
|
133
|
+
}
|
|
134
|
+
async _get(key, context) {
|
|
135
|
+
if (!key) return null;
|
|
136
|
+
const sqlContext = context;
|
|
137
|
+
const executor = this.getExecutor(sqlContext);
|
|
138
|
+
let query;
|
|
139
|
+
if (Array.isArray(this.uniqIndex)) if (typeof key === "object") {
|
|
140
|
+
const keyObj = key;
|
|
141
|
+
query = Object.fromEntries(this.uniqIndex.map((k) => [this.mapColumnName(k), keyObj[k]]));
|
|
142
|
+
} else return null;
|
|
143
|
+
else query = this.formatKeyToQuery(key);
|
|
144
|
+
let qb = executor.selectFrom(this.name).selectAll();
|
|
145
|
+
for (const [k, v] of Object.entries(query)) {
|
|
146
|
+
if (v === void 0) continue;
|
|
147
|
+
qb = qb.where(k, "=", v);
|
|
148
|
+
}
|
|
149
|
+
const result = await qb.executeTakeFirst();
|
|
150
|
+
const deserialized = this.deserializeFromDb(result);
|
|
151
|
+
if (deserialized && this.syncBalance) {
|
|
152
|
+
const balance = await this.balanceTable.getBalance(deserialized.address, context);
|
|
153
|
+
deserialized.tokens = {
|
|
154
|
+
...deserialized.tokens || {},
|
|
155
|
+
...balance
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
return deserialized;
|
|
159
|
+
}
|
|
160
|
+
_history(_key, _context) {
|
|
161
|
+
return [];
|
|
162
|
+
}
|
|
163
|
+
async _update(key, updates, context) {
|
|
164
|
+
const sqlContext = context;
|
|
165
|
+
const executor = this.getExecutor(sqlContext);
|
|
166
|
+
let lookupKey = key;
|
|
167
|
+
if (Array.isArray(this.uniqIndex) && typeof key !== "object") {
|
|
168
|
+
const updatesObj = updates;
|
|
169
|
+
lookupKey = Object.fromEntries(this.uniqIndex.map((k) => [k, updatesObj[k]]));
|
|
170
|
+
}
|
|
171
|
+
const existing = await SqliteTable.prototype._get.call(this, lookupKey, context);
|
|
172
|
+
if (!existing) throw new Error(`${this.name} does not exist: ${JSON.stringify(key)}`);
|
|
173
|
+
let query;
|
|
174
|
+
if (Array.isArray(this.uniqIndex)) {
|
|
175
|
+
const keyObj = typeof lookupKey === "object" ? lookupKey : existing;
|
|
176
|
+
query = Object.fromEntries(this.uniqIndex.map((k) => [this.mapColumnName(k), keyObj[k]]));
|
|
177
|
+
} else query = this.formatKeyToQuery(key);
|
|
178
|
+
const updateAttrs = { ...updates };
|
|
179
|
+
if (Array.isArray(this.uniqIndex)) for (const k of this.uniqIndex) delete updateAttrs[k];
|
|
180
|
+
else delete updateAttrs[this.primaryKey];
|
|
181
|
+
if (this.syncBalance) delete updateAttrs.tokens;
|
|
182
|
+
if (Object.keys(updateAttrs).length > 0) {
|
|
183
|
+
const serialized = this.serializeForDb(updateAttrs);
|
|
184
|
+
let qb = executor.updateTable(this.name).set(serialized);
|
|
185
|
+
for (const [k, v] of Object.entries(query)) {
|
|
186
|
+
if (v === void 0) continue;
|
|
187
|
+
qb = qb.where(k, "=", v);
|
|
188
|
+
}
|
|
189
|
+
await qb.execute();
|
|
190
|
+
}
|
|
191
|
+
const merged = {
|
|
192
|
+
...existing,
|
|
193
|
+
...updateAttrs
|
|
194
|
+
};
|
|
195
|
+
if (this.syncBalance && updates.tokens) merged.tokens = await this.balanceTable.updateBalance({
|
|
196
|
+
address: merged.address,
|
|
197
|
+
tokens: updates.tokens,
|
|
198
|
+
context: updates.context
|
|
199
|
+
}, context);
|
|
200
|
+
return merged;
|
|
201
|
+
}
|
|
202
|
+
async _reset(_context) {
|
|
203
|
+
const sqlContext = _context;
|
|
204
|
+
await this.getExecutor(sqlContext).deleteFrom(this.name).execute();
|
|
205
|
+
}
|
|
206
|
+
async updateOrCreate(exist, state, ctx) {
|
|
207
|
+
const id = this.generatePrimaryKey(state);
|
|
208
|
+
const attrs = omit(state, Array.isArray(this.uniqIndex) ? this.uniqIndex : [this.uniqIndex]);
|
|
209
|
+
if (!id) throw new Error("Cannot update or create without uniq index");
|
|
210
|
+
if (exist) return this.update(id, attrs, ctx);
|
|
211
|
+
return this.create(id, attrs, ctx);
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
var base_default = SqliteTable;
|
|
215
|
+
|
|
216
|
+
//#endregion
|
|
217
|
+
export { base_default as default };
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import SqliteTable, { SqliteTableConfig } from "./base.mjs";
|
|
2
|
+
import { IOperationContext, IRollupState } from "@ocap/types";
|
|
3
|
+
|
|
4
|
+
//#region src/table/rollup.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Rollup Table
|
|
8
|
+
* Extends base table with tokenAddress-based existence check
|
|
9
|
+
*/
|
|
10
|
+
declare class RollupTable extends SqliteTable<IRollupState> {
|
|
11
|
+
constructor(config: Omit<SqliteTableConfig, 'name' | 'uniqIndex' | 'schema'>);
|
|
12
|
+
/**
|
|
13
|
+
* Check if an active (non-paused) rollup with the given tokenAddress exists
|
|
14
|
+
*
|
|
15
|
+
* @param tokenAddress - Token address to check
|
|
16
|
+
* @param context - Operation context with optional transaction
|
|
17
|
+
* @returns true if active rollup with tokenAddress exists, false if tokenAddress is empty/undefined or rollup is paused
|
|
18
|
+
*/
|
|
19
|
+
existByToken(tokenAddress: string, context?: IOperationContext): Promise<boolean>;
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
export { RollupTable as default };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import base_default from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/table/rollup.ts
|
|
4
|
+
/**
|
|
5
|
+
* Rollup Table
|
|
6
|
+
* Extends base table with tokenAddress-based existence check
|
|
7
|
+
*/
|
|
8
|
+
var RollupTable = class extends base_default {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super({
|
|
11
|
+
...config,
|
|
12
|
+
name: "rollup",
|
|
13
|
+
uniqIndex: "address",
|
|
14
|
+
schema: {
|
|
15
|
+
jsonFields: [
|
|
16
|
+
"seedValidators",
|
|
17
|
+
"validators",
|
|
18
|
+
"migrateHistory",
|
|
19
|
+
"vaultHistory",
|
|
20
|
+
"data",
|
|
21
|
+
"context"
|
|
22
|
+
],
|
|
23
|
+
booleanFields: ["paused", "closed"]
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Check if an active (non-paused) rollup with the given tokenAddress exists
|
|
29
|
+
*
|
|
30
|
+
* @param tokenAddress - Token address to check
|
|
31
|
+
* @param context - Operation context with optional transaction
|
|
32
|
+
* @returns true if active rollup with tokenAddress exists, false if tokenAddress is empty/undefined or rollup is paused
|
|
33
|
+
*/
|
|
34
|
+
async existByToken(tokenAddress, context) {
|
|
35
|
+
if (!tokenAddress) return false;
|
|
36
|
+
const sqlContext = context;
|
|
37
|
+
const executor = this.getExecutor(sqlContext);
|
|
38
|
+
return ((await executor.selectFrom("rollup").select(executor.fn.count("address").as("count")).where("tokenAddress", "=", tokenAddress).where("paused", "=", 0).executeTakeFirst())?.count ?? 0) > 0;
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
var rollup_default = RollupTable;
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
export { rollup_default as default };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import SqliteTable, { SqliteTableConfig } from "./base.mjs";
|
|
2
|
+
import { IOperationContext, ITokenState } from "@ocap/types";
|
|
3
|
+
|
|
4
|
+
//#region src/table/token.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Token Table
|
|
8
|
+
* Extends base table with symbol-based existence check
|
|
9
|
+
*/
|
|
10
|
+
declare class TokenTable extends SqliteTable<ITokenState> {
|
|
11
|
+
constructor(config: Omit<SqliteTableConfig, 'name' | 'uniqIndex' | 'schema'>);
|
|
12
|
+
/**
|
|
13
|
+
* Check if a token with the given symbol exists
|
|
14
|
+
*
|
|
15
|
+
* @param symbol - Token symbol to check
|
|
16
|
+
* @param context - Operation context with optional transaction
|
|
17
|
+
* @returns true if token with symbol exists
|
|
18
|
+
* @throws Error if symbol is empty
|
|
19
|
+
*/
|
|
20
|
+
existBySymbol(symbol: string, context?: IOperationContext): Promise<boolean>;
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
export { TokenTable as default };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import base_default from "./base.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/table/token.ts
|
|
4
|
+
/**
|
|
5
|
+
* Token Table
|
|
6
|
+
* Extends base table with symbol-based existence check
|
|
7
|
+
*/
|
|
8
|
+
var TokenTable = class extends base_default {
|
|
9
|
+
constructor(config) {
|
|
10
|
+
super({
|
|
11
|
+
...config,
|
|
12
|
+
name: "token",
|
|
13
|
+
uniqIndex: "address",
|
|
14
|
+
schema: { jsonFields: [
|
|
15
|
+
"foreignToken",
|
|
16
|
+
"metadata",
|
|
17
|
+
"spenders",
|
|
18
|
+
"minters",
|
|
19
|
+
"data",
|
|
20
|
+
"context"
|
|
21
|
+
] }
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a token with the given symbol exists
|
|
26
|
+
*
|
|
27
|
+
* @param symbol - Token symbol to check
|
|
28
|
+
* @param context - Operation context with optional transaction
|
|
29
|
+
* @returns true if token with symbol exists
|
|
30
|
+
* @throws Error if symbol is empty
|
|
31
|
+
*/
|
|
32
|
+
async existBySymbol(symbol, context) {
|
|
33
|
+
if (!symbol) throw new Error("param symbol is required");
|
|
34
|
+
const sqlContext = context;
|
|
35
|
+
const executor = this.getExecutor(sqlContext);
|
|
36
|
+
return ((await executor.selectFrom("token").select(executor.fn.count("address").as("count")).where("symbol", "=", symbol).executeTakeFirst())?.count ?? 0) > 0;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var token_default = TokenTable;
|
|
40
|
+
|
|
41
|
+
//#endregion
|
|
42
|
+
export { token_default as default };
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
//#region rolldown:runtime
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __exportAll = (all, symbols) => {
|
|
9
|
+
let target = {};
|
|
10
|
+
for (var name in all) {
|
|
11
|
+
__defProp(target, name, {
|
|
12
|
+
get: all[name],
|
|
13
|
+
enumerable: true
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
if (symbols) {
|
|
17
|
+
__defProp(target, Symbol.toStringTag, { value: "Module" });
|
|
18
|
+
}
|
|
19
|
+
return target;
|
|
20
|
+
};
|
|
21
|
+
var __copyProps = (to, from, except, desc) => {
|
|
22
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
23
|
+
for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
24
|
+
key = keys[i];
|
|
25
|
+
if (!__hasOwnProp.call(to, key) && key !== except) {
|
|
26
|
+
__defProp(to, key, {
|
|
27
|
+
get: ((k) => from[k]).bind(null, key),
|
|
28
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return to;
|
|
34
|
+
};
|
|
35
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
36
|
+
value: mod,
|
|
37
|
+
enumerable: true
|
|
38
|
+
}) : target, mod));
|
|
39
|
+
|
|
40
|
+
//#endregion
|
|
41
|
+
|
|
42
|
+
exports.__exportAll = __exportAll;
|
|
43
|
+
exports.__toESM = __toESM;
|
package/lib/db.cjs
ADDED
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
const require_rolldown_runtime = require('./_virtual/rolldown_runtime.cjs');
|
|
3
|
+
const require_kysely = require('./kysely.cjs');
|
|
4
|
+
const require_migrations_index = require('./migrations/index.cjs');
|
|
5
|
+
const require_table_base = require('./table/base.cjs');
|
|
6
|
+
const require_table_account = require('./table/account.cjs');
|
|
7
|
+
const require_table_balance = require('./table/balance.cjs');
|
|
8
|
+
const require_table_rollup = require('./table/rollup.cjs');
|
|
9
|
+
const require_table_token = require('./table/token.cjs');
|
|
10
|
+
const require_package = require('./package.cjs');
|
|
11
|
+
let _ocap_statedb = require("@ocap/statedb");
|
|
12
|
+
|
|
13
|
+
//#region src/db.ts
|
|
14
|
+
const { name, version } = require_package.default;
|
|
15
|
+
const SQLITE_BUSY_CODES = ["SQLITE_BUSY", "SQLITE_LOCKED"];
|
|
16
|
+
/**
|
|
17
|
+
* SQLite StateDB Implementation
|
|
18
|
+
* Uses SQLite via Kysely as the storage backend
|
|
19
|
+
*/
|
|
20
|
+
var SqliteStateDB = class extends _ocap_statedb.StateDB {
|
|
21
|
+
/**
|
|
22
|
+
* Creates an instance of SqliteStateDB
|
|
23
|
+
*
|
|
24
|
+
* @param config - SQLite configuration (defaults to in-memory)
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* // In-memory database for testing
|
|
29
|
+
* const db = new SqliteStateDB();
|
|
30
|
+
* await db.initialize();
|
|
31
|
+
*
|
|
32
|
+
* // File-based database for production
|
|
33
|
+
* const db = new SqliteStateDB({ filename: './data/state.sqlite' });
|
|
34
|
+
* await db.initialize();
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
constructor(config = { filename: ":memory:" }) {
|
|
38
|
+
super();
|
|
39
|
+
this.initialized = false;
|
|
40
|
+
this.name = name;
|
|
41
|
+
this.version = version;
|
|
42
|
+
this.config = config;
|
|
43
|
+
this.initSync();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Synchronous initialization
|
|
47
|
+
* Called from constructor for immediate table availability
|
|
48
|
+
*/
|
|
49
|
+
initSync() {
|
|
50
|
+
this.db = require_kysely.createKysely(this.config);
|
|
51
|
+
this.balance = new require_table_balance.default({ db: this.db });
|
|
52
|
+
this.account = new require_table_account.default({
|
|
53
|
+
db: this.db,
|
|
54
|
+
syncBalance: true,
|
|
55
|
+
balanceTable: this.balance
|
|
56
|
+
});
|
|
57
|
+
this.factory = new require_table_base.default({
|
|
58
|
+
name: "factory",
|
|
59
|
+
uniqIndex: "address",
|
|
60
|
+
db: this.db,
|
|
61
|
+
syncBalance: true,
|
|
62
|
+
balanceTable: this.balance,
|
|
63
|
+
schema: { jsonFields: [
|
|
64
|
+
"trustedIssuers",
|
|
65
|
+
"tokens",
|
|
66
|
+
"input",
|
|
67
|
+
"output",
|
|
68
|
+
"display",
|
|
69
|
+
"hooks",
|
|
70
|
+
"data",
|
|
71
|
+
"context",
|
|
72
|
+
"stake"
|
|
73
|
+
] }
|
|
74
|
+
});
|
|
75
|
+
this.stake = new require_table_base.default({
|
|
76
|
+
name: "stake",
|
|
77
|
+
uniqIndex: "address",
|
|
78
|
+
db: this.db,
|
|
79
|
+
syncBalance: true,
|
|
80
|
+
balanceTable: this.balance,
|
|
81
|
+
schema: {
|
|
82
|
+
jsonFields: [
|
|
83
|
+
"tokens",
|
|
84
|
+
"assets",
|
|
85
|
+
"slashers",
|
|
86
|
+
"revokedTokens",
|
|
87
|
+
"revokedAssets",
|
|
88
|
+
"data",
|
|
89
|
+
"context"
|
|
90
|
+
],
|
|
91
|
+
booleanFields: ["revocable"]
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
this.asset = new require_table_base.default({
|
|
95
|
+
name: "asset",
|
|
96
|
+
uniqIndex: "address",
|
|
97
|
+
db: this.db,
|
|
98
|
+
schema: {
|
|
99
|
+
jsonFields: [
|
|
100
|
+
"endpoint",
|
|
101
|
+
"display",
|
|
102
|
+
"tags",
|
|
103
|
+
"stake",
|
|
104
|
+
"data",
|
|
105
|
+
"context"
|
|
106
|
+
],
|
|
107
|
+
booleanFields: ["readonly", "transferrable"]
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
this.delegation = new require_table_base.default({
|
|
111
|
+
name: "delegation",
|
|
112
|
+
uniqIndex: "address",
|
|
113
|
+
db: this.db,
|
|
114
|
+
schema: {
|
|
115
|
+
jsonFields: [
|
|
116
|
+
"ops",
|
|
117
|
+
"data",
|
|
118
|
+
"context"
|
|
119
|
+
],
|
|
120
|
+
columnMapping: {
|
|
121
|
+
from: "from_",
|
|
122
|
+
to: "to_"
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
this.tx = new require_table_base.default({
|
|
127
|
+
name: "tx",
|
|
128
|
+
uniqIndex: "hash",
|
|
129
|
+
db: this.db,
|
|
130
|
+
schema: {
|
|
131
|
+
jsonFields: [
|
|
132
|
+
"receipts",
|
|
133
|
+
"tx",
|
|
134
|
+
"context"
|
|
135
|
+
],
|
|
136
|
+
columnMapping: { index: "index_" },
|
|
137
|
+
booleanFields: [
|
|
138
|
+
"receiptsVerified",
|
|
139
|
+
"finalized",
|
|
140
|
+
"valid"
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
this.token = new require_table_token.default({ db: this.db });
|
|
145
|
+
this.chain = new require_table_base.default({
|
|
146
|
+
name: "chain",
|
|
147
|
+
uniqIndex: "address",
|
|
148
|
+
db: this.db,
|
|
149
|
+
schema: { jsonFields: [
|
|
150
|
+
"accounts",
|
|
151
|
+
"moderator",
|
|
152
|
+
"token",
|
|
153
|
+
"transaction",
|
|
154
|
+
"vaults",
|
|
155
|
+
"context"
|
|
156
|
+
] }
|
|
157
|
+
});
|
|
158
|
+
this.rollup = new require_table_rollup.default({ db: this.db });
|
|
159
|
+
this.rollupBlock = new require_table_base.default({
|
|
160
|
+
name: "rollupBlock",
|
|
161
|
+
uniqIndex: "hash",
|
|
162
|
+
db: this.db,
|
|
163
|
+
schema: {
|
|
164
|
+
jsonFields: [
|
|
165
|
+
"txs",
|
|
166
|
+
"signatures",
|
|
167
|
+
"data",
|
|
168
|
+
"context"
|
|
169
|
+
],
|
|
170
|
+
booleanFields: ["governance"]
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
this.evidence = new require_table_base.default({
|
|
174
|
+
name: "evidence",
|
|
175
|
+
uniqIndex: "hash",
|
|
176
|
+
db: this.db,
|
|
177
|
+
schema: { jsonFields: ["data", "context"] }
|
|
178
|
+
});
|
|
179
|
+
this.tokenFactory = new require_table_base.default({
|
|
180
|
+
name: "tokenFactory",
|
|
181
|
+
uniqIndex: "address",
|
|
182
|
+
db: this.db,
|
|
183
|
+
schema: { jsonFields: [
|
|
184
|
+
"curve",
|
|
185
|
+
"input",
|
|
186
|
+
"output",
|
|
187
|
+
"data",
|
|
188
|
+
"context"
|
|
189
|
+
] }
|
|
190
|
+
});
|
|
191
|
+
this.attachReadyListeners();
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Async initialization - runs migrations
|
|
195
|
+
* Call this after constructor to ensure database schema is ready
|
|
196
|
+
*/
|
|
197
|
+
async initialize() {
|
|
198
|
+
if (this.initialized) return;
|
|
199
|
+
await require_migrations_index.runMigrations(this.db);
|
|
200
|
+
this.initialized = true;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Execute multiple operations within a database transaction
|
|
204
|
+
* Provides retry logic for SQLite BUSY errors (lock conflicts)
|
|
205
|
+
*
|
|
206
|
+
* @param fn - Function receiving Kysely transaction object
|
|
207
|
+
* @param options - Optional retry configuration
|
|
208
|
+
* @returns Result of the transaction function
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* await db.runAsLambda(async (txn) => {
|
|
213
|
+
* await txn.insertInto('account').values({ ... }).execute();
|
|
214
|
+
* await txn.updateTable('balance').set({ ... }).execute();
|
|
215
|
+
* });
|
|
216
|
+
* ```
|
|
217
|
+
*/
|
|
218
|
+
async runAsLambda(fn, options = {}) {
|
|
219
|
+
const { retryLimit = 2 } = options;
|
|
220
|
+
let lastError;
|
|
221
|
+
for (let attempt = 0; attempt <= retryLimit; attempt++) try {
|
|
222
|
+
return await this.db.transaction().execute(async (txn) => {
|
|
223
|
+
return await fn(txn);
|
|
224
|
+
});
|
|
225
|
+
} catch (err) {
|
|
226
|
+
lastError = err;
|
|
227
|
+
const code = err?.code;
|
|
228
|
+
if (!SQLITE_BUSY_CODES.includes(code)) throw err;
|
|
229
|
+
if (attempt < retryLimit) await new Promise((r) => setTimeout(r, 10 * 2 ** attempt));
|
|
230
|
+
}
|
|
231
|
+
throw lastError;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Reset all tables (for testing)
|
|
235
|
+
*/
|
|
236
|
+
async reset() {
|
|
237
|
+
const tables = Object.keys(this.readyMarks);
|
|
238
|
+
for (const table of tables) {
|
|
239
|
+
const tableInstance = this[table];
|
|
240
|
+
if (tableInstance?.reset) await tableInstance.reset();
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Close database connection
|
|
245
|
+
*/
|
|
246
|
+
async close() {
|
|
247
|
+
await this.db.destroy();
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Get the underlying Kysely instance (for advanced operations)
|
|
251
|
+
*/
|
|
252
|
+
getKysely() {
|
|
253
|
+
return this.db;
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
var db_default = SqliteStateDB;
|
|
257
|
+
|
|
258
|
+
//#endregion
|
|
259
|
+
exports.default = db_default;
|