web3ql-client 1.2.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 (86) hide show
  1. package/README.md +66 -0
  2. package/contracts/PublicKeyRegistry.sol +87 -0
  3. package/dist/src/access.d.ts +176 -0
  4. package/dist/src/access.d.ts.map +1 -0
  5. package/dist/src/access.js +283 -0
  6. package/dist/src/access.js.map +1 -0
  7. package/dist/src/batch.d.ts +107 -0
  8. package/dist/src/batch.d.ts.map +1 -0
  9. package/dist/src/batch.js +188 -0
  10. package/dist/src/batch.js.map +1 -0
  11. package/dist/src/cli.d.ts +40 -0
  12. package/dist/src/cli.d.ts.map +1 -0
  13. package/dist/src/cli.js +361 -0
  14. package/dist/src/cli.js.map +1 -0
  15. package/dist/src/constraints.d.ts +126 -0
  16. package/dist/src/constraints.d.ts.map +1 -0
  17. package/dist/src/constraints.js +192 -0
  18. package/dist/src/constraints.js.map +1 -0
  19. package/dist/src/crypto.d.ts +118 -0
  20. package/dist/src/crypto.d.ts.map +1 -0
  21. package/dist/src/crypto.js +192 -0
  22. package/dist/src/crypto.js.map +1 -0
  23. package/dist/src/factory-client.d.ts +106 -0
  24. package/dist/src/factory-client.d.ts.map +1 -0
  25. package/dist/src/factory-client.js +202 -0
  26. package/dist/src/factory-client.js.map +1 -0
  27. package/dist/src/index-cache.d.ts +156 -0
  28. package/dist/src/index-cache.d.ts.map +1 -0
  29. package/dist/src/index-cache.js +265 -0
  30. package/dist/src/index-cache.js.map +1 -0
  31. package/dist/src/index.d.ts +60 -0
  32. package/dist/src/index.d.ts.map +1 -0
  33. package/dist/src/index.js +60 -0
  34. package/dist/src/index.js.map +1 -0
  35. package/dist/src/migrations.d.ts +114 -0
  36. package/dist/src/migrations.d.ts.map +1 -0
  37. package/dist/src/migrations.js +173 -0
  38. package/dist/src/migrations.js.map +1 -0
  39. package/dist/src/model.d.ts +198 -0
  40. package/dist/src/model.d.ts.map +1 -0
  41. package/dist/src/model.js +379 -0
  42. package/dist/src/model.js.map +1 -0
  43. package/dist/src/query.d.ts +155 -0
  44. package/dist/src/query.d.ts.map +1 -0
  45. package/dist/src/query.js +386 -0
  46. package/dist/src/query.js.map +1 -0
  47. package/dist/src/registry.d.ts +45 -0
  48. package/dist/src/registry.d.ts.map +1 -0
  49. package/dist/src/registry.js +80 -0
  50. package/dist/src/registry.js.map +1 -0
  51. package/dist/src/schema-manager.d.ts +109 -0
  52. package/dist/src/schema-manager.d.ts.map +1 -0
  53. package/dist/src/schema-manager.js +259 -0
  54. package/dist/src/schema-manager.js.map +1 -0
  55. package/dist/src/table-client.d.ts +156 -0
  56. package/dist/src/table-client.d.ts.map +1 -0
  57. package/dist/src/table-client.js +292 -0
  58. package/dist/src/table-client.js.map +1 -0
  59. package/dist/src/typed-table.d.ts +159 -0
  60. package/dist/src/typed-table.d.ts.map +1 -0
  61. package/dist/src/typed-table.js +246 -0
  62. package/dist/src/typed-table.js.map +1 -0
  63. package/dist/src/types.d.ts +48 -0
  64. package/dist/src/types.d.ts.map +1 -0
  65. package/dist/src/types.js +222 -0
  66. package/dist/src/types.js.map +1 -0
  67. package/keyManager.js +337 -0
  68. package/package.json +38 -0
  69. package/src/access.ts +421 -0
  70. package/src/batch.ts +259 -0
  71. package/src/cli.ts +349 -0
  72. package/src/constraints.ts +283 -0
  73. package/src/crypto.ts +239 -0
  74. package/src/factory-client.ts +237 -0
  75. package/src/index-cache.ts +351 -0
  76. package/src/index.ts +171 -0
  77. package/src/migrations.ts +215 -0
  78. package/src/model.ts +538 -0
  79. package/src/query.ts +508 -0
  80. package/src/registry.ts +100 -0
  81. package/src/schema-manager.ts +301 -0
  82. package/src/table-client.ts +393 -0
  83. package/src/typed-table.ts +340 -0
  84. package/src/types.ts +284 -0
  85. package/tsconfig.json +22 -0
  86. package/walletUtils.js +204 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * @file factory-client.ts
3
+ * @notice High-level client for the Web3QL Factory + Database contracts.
4
+ *
5
+ * Entry point for every Web3QL app:
6
+ * ─────────────────────────────────────────────────────────────
7
+ * const web3ql = new Web3QLClient(factoryAddress, signer, keypair, registryAddress);
8
+ *
9
+ * // Register your public key once (one-time, ~40k gas)
10
+ * await web3ql.register();
11
+ *
12
+ * // Create a personal database (or get existing)
13
+ * const db = await web3ql.getOrCreateDatabase();
14
+ *
15
+ * // Create a table
16
+ * const tableAddr = await db.createTable('users', schemaBytes);
17
+ *
18
+ * // Get an encrypted client for that table
19
+ * const users = db.table(tableAddr);
20
+ *
21
+ * // Write (auto-encrypts)
22
+ * await users.writeRaw(users.deriveKey('users', 1n), '{"name":"Alice"}');
23
+ */
24
+ import { ethers } from 'ethers';
25
+ import { EncryptedTableClient } from './table-client.js';
26
+ import { PublicKeyRegistryClient } from './registry.js';
27
+ // ─────────────────────────────────────────────────────────────
28
+ // ABIs
29
+ // ─────────────────────────────────────────────────────────────
30
+ const FACTORY_ABI = [
31
+ 'function createDatabase(string calldata name) external returns (address)',
32
+ 'function getUserDatabases(address user) external view returns (address[] memory)',
33
+ 'function databaseImplementation() external view returns (address)',
34
+ 'function tableImplementation() external view returns (address)',
35
+ 'function databaseCount() external view returns (uint256)',
36
+ 'function removeDatabase(address db) external',
37
+ 'event DatabaseCreated(address indexed owner, address indexed db, uint256 indexed index)',
38
+ 'event DatabaseRemoved(address indexed owner, address indexed db)',
39
+ ];
40
+ const DATABASE_ABI = [
41
+ 'function createTable(string calldata name, bytes calldata schemaBytes) external returns (address)',
42
+ 'function getTable(string calldata name) external view returns (address)',
43
+ 'function listTables() external view returns (string[] memory)',
44
+ 'event TableCreated(string name, address indexed tableContract, address indexed owner)',
45
+ ];
46
+ // ─────────────────────────────────────────────────────────────
47
+ // DatabaseClient
48
+ // ─────────────────────────────────────────────────────────────
49
+ export class DatabaseClient {
50
+ address;
51
+ contract;
52
+ signer;
53
+ keypair;
54
+ constructor(address, signer, keypair) {
55
+ this.address = address;
56
+ this.signer = signer;
57
+ this.keypair = keypair;
58
+ this.contract = new ethers.Contract(address, DATABASE_ABI, signer);
59
+ }
60
+ /**
61
+ * Create a new encrypted table inside this database.
62
+ * @param name Human-readable table name (matches your schema).
63
+ * @param schemaBytes ABI-encoded schema from @web3ql/compiler's compileSchema().
64
+ * @returns Address of the deployed table proxy.
65
+ */
66
+ async createTable(name, schemaBytes) {
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ const tx = await this.contract.createTable(name, schemaBytes);
69
+ const receipt = await tx.wait();
70
+ const iface = new ethers.Interface(DATABASE_ABI);
71
+ for (const log of receipt.logs) {
72
+ try {
73
+ const parsed = iface.parseLog(log);
74
+ if (parsed?.name === 'TableCreated') {
75
+ return parsed.args['tableContract'];
76
+ }
77
+ }
78
+ catch { /* skip non-matching logs */ }
79
+ }
80
+ throw new Error('DatabaseClient.createTable: TableCreated event not found in logs');
81
+ }
82
+ /**
83
+ * Get the address of an existing table by name.
84
+ * Returns ethers.ZeroAddress if not found.
85
+ */
86
+ async getTable(name) {
87
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
88
+ return this.contract.getTable(name);
89
+ }
90
+ /**
91
+ * List all table names in this database.
92
+ */
93
+ async listTables() {
94
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
95
+ return this.contract.listTables();
96
+ }
97
+ /**
98
+ * Drop (remove from registry) a table by name.
99
+ * Records inside the table are NOT purged by this call — they remain
100
+ * as unreachable ciphertext. Use SchemaManager.dropTable() first to
101
+ * bulk-delete records if you want storage refunds.
102
+ */
103
+ async dropTable(name) {
104
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
+ const tx = await this.contract.dropTable(name);
106
+ return tx.wait();
107
+ }
108
+ /**
109
+ * Get an EncryptedTableClient for a specific table address.
110
+ * Use this after createTable() or getTable() to read/write records.
111
+ */
112
+ table(tableAddress) {
113
+ return new EncryptedTableClient(tableAddress, this.signer, this.keypair);
114
+ }
115
+ }
116
+ // ─────────────────────────────────────────────────────────────
117
+ // Web3QLClient (top-level entry point)
118
+ // ─────────────────────────────────────────────────────────────
119
+ export class Web3QLClient {
120
+ factory;
121
+ signer;
122
+ keypair;
123
+ registry;
124
+ constructor(factoryAddress, signer, keypair, registryAddress) {
125
+ this.signer = signer;
126
+ this.keypair = keypair;
127
+ this.factory = new ethers.Contract(factoryAddress, FACTORY_ABI, signer);
128
+ this.registry = new PublicKeyRegistryClient(registryAddress, signer);
129
+ }
130
+ /**
131
+ * Register the caller's encryption public key on-chain.
132
+ * Must be called once per address before anyone can share records
133
+ * with that address. Safe to call again — will overwrite.
134
+ */
135
+ async register() {
136
+ return this.registry.register(this.keypair);
137
+ }
138
+ /**
139
+ * Check if an address has registered its public key.
140
+ */
141
+ async isRegistered(address) {
142
+ return this.registry.hasKey(address);
143
+ }
144
+ /**
145
+ * Create a new personal database for the calling wallet.
146
+ * Most users only ever need one database — use getOrCreateDatabase().
147
+ * @param name Human-readable label stored immutably on-chain.
148
+ */
149
+ async createDatabase(name = '') {
150
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
151
+ const tx = await this.factory.createDatabase(name);
152
+ const receipt = await tx.wait();
153
+ const iface = new ethers.Interface(FACTORY_ABI);
154
+ for (const log of receipt.logs) {
155
+ try {
156
+ const parsed = iface.parseLog(log);
157
+ if (parsed?.name === 'DatabaseCreated') {
158
+ return new DatabaseClient(parsed.args['db'], this.signer, this.keypair);
159
+ }
160
+ }
161
+ catch { /* skip */ }
162
+ }
163
+ throw new Error('Web3QLClient.createDatabase: DatabaseCreated event not found');
164
+ }
165
+ /**
166
+ * Get all database proxies owned by a user.
167
+ */
168
+ async getDatabases(owner) {
169
+ const addr = owner ?? await this.signer.getAddress();
170
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
171
+ return this.factory.getUserDatabases(addr);
172
+ }
173
+ /**
174
+ * Return the first existing database for the caller, or create one
175
+ * if none exists. Idempotent — safe to call on every app startup.
176
+ * @param name Passed to createDatabase() only when a new one is created.
177
+ */
178
+ async getOrCreateDatabase(name = '') {
179
+ const dbs = await this.getDatabases();
180
+ if (dbs.length > 0) {
181
+ return new DatabaseClient(dbs[0], this.signer, this.keypair);
182
+ }
183
+ return this.createDatabase(name);
184
+ }
185
+ /**
186
+ * Wrap an already-known database address (e.g. loaded from config).
187
+ */
188
+ database(address) {
189
+ return new DatabaseClient(address, this.signer, this.keypair);
190
+ }
191
+ /**
192
+ * Remove a database from the factory’s registry.
193
+ * The proxy contract is NOT destroyed — it remains on-chain.
194
+ * After calling this it will no longer appear in getDatabases().
195
+ */
196
+ async removeDatabase(dbAddress) {
197
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
+ const tx = await this.factory.removeDatabase(dbAddress);
199
+ return tx.wait();
200
+ }
201
+ }
202
+ //# sourceMappingURL=factory-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory-client.js","sourceRoot":"","sources":["../../src/factory-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * @file index-cache.ts
3
+ * @notice Web3QL v1.2 — client-side event-sourced index for fast queries.
4
+ *
5
+ * Problem:
6
+ * The chain stores opaque ciphertext. Without decrypting every record,
7
+ * you cannot answer WHERE queries. For small tables this is fine (the
8
+ * query engine decrypts in batches). For large tables you need an index.
9
+ *
10
+ * Solution — two tiers:
11
+ *
12
+ * Tier 1: Client-side in-memory index (this file)
13
+ * • On first load, decrypt all records and build an in-memory lookup map
14
+ * • Subscribe to chain events to update the cache incrementally
15
+ * • Fast point-lookups and range scans via the query engine
16
+ * • Index survives page refreshes when serialised to localStorage/IndexedDB
17
+ *
18
+ * Tier 2: Relay-maintained SQLite index (v1.2 API)
19
+ * • The relay sees all writes and maintains a server-side SQLite DB
20
+ * • Exposes `/api/connector/query` endpoint for WHERE-based key lookup
21
+ * • SDK calls relay to get matching record keys, then reads+decrypts from chain
22
+ *
23
+ * Usage (client-side index):
24
+ * ─────────────────────────────────────────────────────────────
25
+ * const cache = new TableIndexCache(tableClient, ownerAddress, schema);
26
+ *
27
+ * // Build index from chain (decrypt all records)
28
+ * await cache.build();
29
+ *
30
+ * // Fast in-memory query (no chain reads)
31
+ * const results = cache.query()
32
+ * .where('email', 'eq', 'alice@example.com')
33
+ * .execute();
34
+ *
35
+ * // Subscribe to updates (incrementally maintains index)
36
+ * cache.subscribe(provider);
37
+ *
38
+ * // Persist to localStorage
39
+ * cache.save('users-index');
40
+ * cache.load('users-index');
41
+ * ─────────────────────────────────────────────────────────────
42
+ */
43
+ import { ethers } from 'ethers';
44
+ import type { EncryptedTableClient } from './table-client.js';
45
+ import type { SchemaDefinition } from './types.js';
46
+ import { QueryBuilder } from './query.js';
47
+ import type { Row } from './query.js';
48
+ export interface IndexEntry {
49
+ key: string;
50
+ data: Row;
51
+ version: bigint;
52
+ updatedAt: bigint;
53
+ }
54
+ export interface IndexCacheOptions {
55
+ /** Batch size for initial build (parallel decrypt). Default: 20. */
56
+ buildBatchSize?: number;
57
+ /** Auto-subscribe to write events to keep index fresh. Default: true. */
58
+ autoSubscribe?: boolean;
59
+ /** Max records to index. Default: unlimited. */
60
+ maxRecords?: number;
61
+ }
62
+ export declare class TableIndexCache {
63
+ private tableClient;
64
+ private ownerAddress;
65
+ private schema?;
66
+ private opts;
67
+ /** In-memory index: bytes32 key → IndexEntry */
68
+ private _index;
69
+ private _built;
70
+ private _listener?;
71
+ constructor(tableClient: EncryptedTableClient, ownerAddress: string, schema?: SchemaDefinition, opts?: IndexCacheOptions);
72
+ /**
73
+ * Decrypt all owner records from chain and populate the in-memory index.
74
+ * Returns the number of records indexed.
75
+ */
76
+ build(): Promise<number>;
77
+ /**
78
+ * Subscribe to on-chain write events. Automatically decrypts and updates
79
+ * the index when new records are written by the owner.
80
+ *
81
+ * @param provider An ethers Provider with WebSocket or polling support.
82
+ */
83
+ subscribe(provider: ethers.Provider): void;
84
+ /** Stop listening to chain events. */
85
+ unsubscribe(): void;
86
+ /** Return a QueryBuilder over all indexed records. No chain reads. */
87
+ query(): QueryBuilder<Row>;
88
+ /** Look up a specific record by its bytes32 on-chain key. */
89
+ get(key: string): IndexEntry | undefined;
90
+ /** All indexed entries as an array. */
91
+ all(): IndexEntry[];
92
+ /** Number of records in the index. */
93
+ get size(): number;
94
+ /** True after build() has completed at least once. */
95
+ get built(): boolean;
96
+ /** Manually add or update an entry (e.g. after a local write). */
97
+ upsert(entry: IndexEntry): void;
98
+ /** Remove an entry from the cache (e.g. after a local delete). */
99
+ evict(key: string): void;
100
+ /** Clear the entire index. */
101
+ clear(): void;
102
+ /**
103
+ * Serialise the index to a JSON string.
104
+ * Store in localStorage or IndexedDB to survive page refreshes.
105
+ */
106
+ serialise(): string;
107
+ /** Restore an index from a previously serialised string. */
108
+ deserialise(json: string): void;
109
+ /** Save to localStorage (browser only). */
110
+ save(storageKey: string): void;
111
+ /** Load from localStorage (browser only). */
112
+ load(storageKey: string): boolean;
113
+ }
114
+ export interface RelayQueryRequest {
115
+ tableAddress: string;
116
+ where?: {
117
+ col: string;
118
+ op: string;
119
+ val: unknown;
120
+ }[];
121
+ orderBy?: {
122
+ col: string;
123
+ dir: 'asc' | 'desc';
124
+ }[];
125
+ limit?: number;
126
+ offset?: number;
127
+ }
128
+ export interface RelayQueryResponse {
129
+ /** bytes32 record keys matching the query */
130
+ keys: string[];
131
+ total: number;
132
+ }
133
+ /**
134
+ * QueryRelayClient — sends WHERE queries to the relay's SQLite index endpoint
135
+ * and returns matching bytes32 record keys. The caller then reads+decrypts
136
+ * from chain using the returned keys.
137
+ *
138
+ * This is the v1.2 "fast query" path that avoids decrypting every record.
139
+ */
140
+ export declare class QueryRelayClient {
141
+ private baseUrl;
142
+ constructor(relayBaseUrl: string);
143
+ /**
144
+ * Query the relay's index and return matching bytes32 record keys.
145
+ *
146
+ * @example
147
+ * const { keys } = await relay.query({
148
+ * tableAddress: '0x...',
149
+ * where: [{ col: 'age', op: 'gt', val: 18 }],
150
+ * limit: 50,
151
+ * });
152
+ * const records = await Promise.all(keys.map(k => tableClient.readPlaintext(k)));
153
+ */
154
+ query(req: RelayQueryRequest): Promise<RelayQueryResponse>;
155
+ }
156
+ //# sourceMappingURL=index-cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-cache.d.ts","sourceRoot":"","sources":["../../src/index-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,265 @@
1
+ /**
2
+ * @file index-cache.ts
3
+ * @notice Web3QL v1.2 — client-side event-sourced index for fast queries.
4
+ *
5
+ * Problem:
6
+ * The chain stores opaque ciphertext. Without decrypting every record,
7
+ * you cannot answer WHERE queries. For small tables this is fine (the
8
+ * query engine decrypts in batches). For large tables you need an index.
9
+ *
10
+ * Solution — two tiers:
11
+ *
12
+ * Tier 1: Client-side in-memory index (this file)
13
+ * • On first load, decrypt all records and build an in-memory lookup map
14
+ * • Subscribe to chain events to update the cache incrementally
15
+ * • Fast point-lookups and range scans via the query engine
16
+ * • Index survives page refreshes when serialised to localStorage/IndexedDB
17
+ *
18
+ * Tier 2: Relay-maintained SQLite index (v1.2 API)
19
+ * • The relay sees all writes and maintains a server-side SQLite DB
20
+ * • Exposes `/api/connector/query` endpoint for WHERE-based key lookup
21
+ * • SDK calls relay to get matching record keys, then reads+decrypts from chain
22
+ *
23
+ * Usage (client-side index):
24
+ * ─────────────────────────────────────────────────────────────
25
+ * const cache = new TableIndexCache(tableClient, ownerAddress, schema);
26
+ *
27
+ * // Build index from chain (decrypt all records)
28
+ * await cache.build();
29
+ *
30
+ * // Fast in-memory query (no chain reads)
31
+ * const results = cache.query()
32
+ * .where('email', 'eq', 'alice@example.com')
33
+ * .execute();
34
+ *
35
+ * // Subscribe to updates (incrementally maintains index)
36
+ * cache.subscribe(provider);
37
+ *
38
+ * // Persist to localStorage
39
+ * cache.save('users-index');
40
+ * cache.load('users-index');
41
+ * ─────────────────────────────────────────────────────────────
42
+ */
43
+ import { ethers } from 'ethers';
44
+ import { decodeRow } from './types.js';
45
+ import { query } from './query.js';
46
+ // ─────────────────────────────────────────────────────────────
47
+ // TableIndexCache
48
+ // ─────────────────────────────────────────────────────────────
49
+ export class TableIndexCache {
50
+ tableClient;
51
+ ownerAddress;
52
+ schema;
53
+ opts;
54
+ /** In-memory index: bytes32 key → IndexEntry */
55
+ _index = new Map();
56
+ _built = false;
57
+ _listener;
58
+ constructor(tableClient, ownerAddress, schema, opts = {}) {
59
+ this.tableClient = tableClient;
60
+ this.ownerAddress = ownerAddress;
61
+ this.schema = schema;
62
+ this.opts = {
63
+ buildBatchSize: opts.buildBatchSize ?? 20,
64
+ autoSubscribe: opts.autoSubscribe ?? true,
65
+ maxRecords: opts.maxRecords ?? Infinity,
66
+ };
67
+ }
68
+ // ── Build ───────────────────────────────────────────────────
69
+ /**
70
+ * Decrypt all owner records from chain and populate the in-memory index.
71
+ * Returns the number of records indexed.
72
+ */
73
+ async build() {
74
+ const total = Number(await this.tableClient.ownerRecordCount(this.ownerAddress));
75
+ const clampedTotal = Math.min(total, this.opts.maxRecords);
76
+ const BATCH = this.opts.buildBatchSize;
77
+ for (let i = 0; i < clampedTotal; i += BATCH) {
78
+ const keys = await this.tableClient.listOwnerRecords(this.ownerAddress, BigInt(i), BigInt(Math.min(BATCH, clampedTotal - i)));
79
+ const settled = await Promise.allSettled(keys.map(async (k) => {
80
+ const raw = await this.tableClient.readRaw(k);
81
+ const plain = await this.tableClient.readPlaintext(k);
82
+ const parsed = JSON.parse(plain);
83
+ const data = this.schema ? decodeRow(this.schema, parsed) : parsed;
84
+ return {
85
+ key: k,
86
+ data,
87
+ version: raw.version,
88
+ updatedAt: raw.updatedAt,
89
+ };
90
+ }));
91
+ for (const r of settled) {
92
+ if (r.status === 'fulfilled') {
93
+ this._index.set(r.value.key, r.value);
94
+ }
95
+ }
96
+ }
97
+ this._built = true;
98
+ return this._index.size;
99
+ }
100
+ // ── Live subscription ───────────────────────────────────────
101
+ /**
102
+ * Subscribe to on-chain write events. Automatically decrypts and updates
103
+ * the index when new records are written by the owner.
104
+ *
105
+ * @param provider An ethers Provider with WebSocket or polling support.
106
+ */
107
+ subscribe(provider) {
108
+ // Listen to the raw Transfer-like events on the table contract.
109
+ // Web3QL contracts emit a Write(bytes32 key, address owner) event.
110
+ const iface = new ethers.Interface([
111
+ 'event RecordWritten(bytes32 indexed key, address indexed owner)',
112
+ 'event RecordUpdated(bytes32 indexed key, address indexed owner)',
113
+ 'event RecordDeleted(bytes32 indexed key, address indexed owner)',
114
+ ]);
115
+ const contract = new ethers.Contract(this.tableClient.tableAddress, iface, provider);
116
+ const onWrite = async (key, owner) => {
117
+ if (owner.toLowerCase() !== this.ownerAddress.toLowerCase())
118
+ return;
119
+ try {
120
+ const raw = await this.tableClient.readRaw(key);
121
+ const plain = await this.tableClient.readPlaintext(key);
122
+ const parsed = JSON.parse(plain);
123
+ const data = this.schema ? decodeRow(this.schema, parsed) : parsed;
124
+ this._index.set(key, { key, data, version: raw.version, updatedAt: raw.updatedAt });
125
+ }
126
+ catch { /* record inaccessible — skip */ }
127
+ };
128
+ const onDelete = (key) => {
129
+ this._index.delete(key);
130
+ };
131
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
132
+ contract.on('RecordWritten', onWrite);
133
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
134
+ contract.on('RecordUpdated', onWrite);
135
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
136
+ contract.on('RecordDeleted', onDelete);
137
+ this._listener = () => {
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ contract.off('RecordWritten', onWrite);
140
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
141
+ contract.off('RecordUpdated', onWrite);
142
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
143
+ contract.off('RecordDeleted', onDelete);
144
+ };
145
+ }
146
+ /** Stop listening to chain events. */
147
+ unsubscribe() {
148
+ this._listener?.();
149
+ this._listener = undefined;
150
+ }
151
+ // ── Query ───────────────────────────────────────────────────
152
+ /** Return a QueryBuilder over all indexed records. No chain reads. */
153
+ query() {
154
+ return query(Array.from(this._index.values()).map((e) => e.data));
155
+ }
156
+ /** Look up a specific record by its bytes32 on-chain key. */
157
+ get(key) {
158
+ return this._index.get(key);
159
+ }
160
+ /** All indexed entries as an array. */
161
+ all() {
162
+ return Array.from(this._index.values());
163
+ }
164
+ /** Number of records in the index. */
165
+ get size() { return this._index.size; }
166
+ /** True after build() has completed at least once. */
167
+ get built() { return this._built; }
168
+ // ── Invalidation ────────────────────────────────────────────
169
+ /** Manually add or update an entry (e.g. after a local write). */
170
+ upsert(entry) {
171
+ this._index.set(entry.key, entry);
172
+ }
173
+ /** Remove an entry from the cache (e.g. after a local delete). */
174
+ evict(key) {
175
+ this._index.delete(key);
176
+ }
177
+ /** Clear the entire index. */
178
+ clear() {
179
+ this._index.clear();
180
+ this._built = false;
181
+ }
182
+ // ── Persistence ─────────────────────────────────────────────
183
+ /**
184
+ * Serialise the index to a JSON string.
185
+ * Store in localStorage or IndexedDB to survive page refreshes.
186
+ */
187
+ serialise() {
188
+ const entries = Array.from(this._index.values()).map((e) => ({
189
+ key: e.key,
190
+ data: e.data,
191
+ version: e.version.toString(),
192
+ updatedAt: e.updatedAt.toString(),
193
+ }));
194
+ return JSON.stringify({ version: 1, entries });
195
+ }
196
+ /** Restore an index from a previously serialised string. */
197
+ deserialise(json) {
198
+ const { entries } = JSON.parse(json);
199
+ for (const e of entries) {
200
+ this._index.set(e.key, {
201
+ key: e.key,
202
+ data: e.data,
203
+ version: BigInt(e.version),
204
+ updatedAt: BigInt(e.updatedAt),
205
+ });
206
+ }
207
+ this._built = true;
208
+ }
209
+ /** Save to localStorage (browser only). */
210
+ save(storageKey) {
211
+ const ls = globalThis['localStorage'];
212
+ if (!ls)
213
+ return;
214
+ ls.setItem(storageKey, this.serialise());
215
+ }
216
+ /** Load from localStorage (browser only). */
217
+ load(storageKey) {
218
+ const ls = globalThis['localStorage'];
219
+ if (!ls)
220
+ return false;
221
+ const raw = ls.getItem(storageKey);
222
+ if (!raw)
223
+ return false;
224
+ this.deserialise(raw);
225
+ return true;
226
+ }
227
+ }
228
+ /**
229
+ * QueryRelayClient — sends WHERE queries to the relay's SQLite index endpoint
230
+ * and returns matching bytes32 record keys. The caller then reads+decrypts
231
+ * from chain using the returned keys.
232
+ *
233
+ * This is the v1.2 "fast query" path that avoids decrypting every record.
234
+ */
235
+ export class QueryRelayClient {
236
+ baseUrl;
237
+ constructor(relayBaseUrl) {
238
+ this.baseUrl = relayBaseUrl.replace(/\/$/, '');
239
+ }
240
+ /**
241
+ * Query the relay's index and return matching bytes32 record keys.
242
+ *
243
+ * @example
244
+ * const { keys } = await relay.query({
245
+ * tableAddress: '0x...',
246
+ * where: [{ col: 'age', op: 'gt', val: 18 }],
247
+ * limit: 50,
248
+ * });
249
+ * const records = await Promise.all(keys.map(k => tableClient.readPlaintext(k)));
250
+ */
251
+ async query(req) {
252
+ const url = `${this.baseUrl}/api/connector/query`;
253
+ const resp = await fetch(url, {
254
+ method: 'POST',
255
+ headers: { 'Content-Type': 'application/json' },
256
+ body: JSON.stringify(req),
257
+ });
258
+ if (!resp.ok) {
259
+ const text = await resp.text();
260
+ throw new Error(`QueryRelayClient: relay returned ${resp.status}: ${text}`);
261
+ }
262
+ return resp.json();
263
+ }
264
+ }
265
+ //# sourceMappingURL=index-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index-cache.js","sourceRoot":"","sources":["../../src/index-cache.ts"],"names":[],"mappings":"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"}