@optimystic/db-p2p-storage-rn 0.1.1
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/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/logger.d.ts +3 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +6 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/mmkv-storage.d.ts +47 -0
- package/dist/src/mmkv-storage.d.ts.map +1 -0
- package/dist/src/mmkv-storage.js +129 -0
- package/dist/src/mmkv-storage.js.map +1 -0
- package/package.json +68 -0
- package/src/index.ts +2 -0
- package/src/logger.ts +8 -0
- package/src/mmkv-storage.ts +172 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,KAAK,CAAC,QAAQ,CAEjE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,cAAc,GAAG,8BAA8B,CAAC;AAEtD,MAAM,UAAU,YAAY,CAAC,YAAoB;IAChD,OAAO,KAAK,CAAC,GAAG,cAAc,IAAI,YAAY,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { BlockId, IBlock, Transform, ActionId, ActionRev } from '@optimystic/db-core';
|
|
2
|
+
import type { BlockMetadata, IRawStorage } from '@optimystic/db-p2p';
|
|
3
|
+
/** MMKV storage interface matching react-native-mmkv */
|
|
4
|
+
export interface MMKV {
|
|
5
|
+
getString(key: string): string | undefined;
|
|
6
|
+
set(key: string, value: string): void;
|
|
7
|
+
delete(key: string): void;
|
|
8
|
+
getAllKeys(): string[];
|
|
9
|
+
contains(key: string): boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface MMKVStorageOptions {
|
|
12
|
+
/** The MMKV instance to use */
|
|
13
|
+
mmkv: MMKV;
|
|
14
|
+
/** Optional prefix for all keys to namespace this storage */
|
|
15
|
+
prefix?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare class MMKVRawStorage implements IRawStorage {
|
|
18
|
+
private readonly mmkv;
|
|
19
|
+
private readonly prefix;
|
|
20
|
+
constructor(options: MMKVStorageOptions);
|
|
21
|
+
getMetadata(blockId: BlockId): Promise<BlockMetadata | undefined>;
|
|
22
|
+
saveMetadata(blockId: BlockId, metadata: BlockMetadata): Promise<void>;
|
|
23
|
+
getRevision(blockId: BlockId, rev: number): Promise<ActionId | undefined>;
|
|
24
|
+
saveRevision(blockId: BlockId, rev: number, actionId: ActionId): Promise<void>;
|
|
25
|
+
getPendingTransaction(blockId: BlockId, actionId: ActionId): Promise<Transform | undefined>;
|
|
26
|
+
savePendingTransaction(blockId: BlockId, actionId: ActionId, transform: Transform): Promise<void>;
|
|
27
|
+
deletePendingTransaction(blockId: BlockId, actionId: ActionId): Promise<void>;
|
|
28
|
+
listPendingTransactions(blockId: BlockId): AsyncIterable<ActionId>;
|
|
29
|
+
getTransaction(blockId: BlockId, actionId: ActionId): Promise<Transform | undefined>;
|
|
30
|
+
saveTransaction(blockId: BlockId, actionId: ActionId, transform: Transform): Promise<void>;
|
|
31
|
+
listRevisions(blockId: BlockId, startRev: number, endRev: number): AsyncIterable<ActionRev>;
|
|
32
|
+
getMaterializedBlock(blockId: BlockId, actionId: ActionId): Promise<IBlock | undefined>;
|
|
33
|
+
saveMaterializedBlock(blockId: BlockId, actionId: ActionId, block?: IBlock): Promise<void>;
|
|
34
|
+
promotePendingTransaction(blockId: BlockId, actionId: ActionId): Promise<void>;
|
|
35
|
+
private metadataKey;
|
|
36
|
+
private revisionKey;
|
|
37
|
+
private pendingKey;
|
|
38
|
+
private transactionKey;
|
|
39
|
+
private materializedKey;
|
|
40
|
+
private pendingIndexKey;
|
|
41
|
+
private getJson;
|
|
42
|
+
private setJson;
|
|
43
|
+
private getPendingIndex;
|
|
44
|
+
private addToPendingIndex;
|
|
45
|
+
private removeFromPendingIndex;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=mmkv-storage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mmkv-storage.d.ts","sourceRoot":"","sources":["../../src/mmkv-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { createLogger } from './logger.js';
|
|
2
|
+
const log = createLogger('storage:mmkv');
|
|
3
|
+
export class MMKVRawStorage {
|
|
4
|
+
mmkv;
|
|
5
|
+
prefix;
|
|
6
|
+
constructor(options) {
|
|
7
|
+
this.mmkv = options.mmkv;
|
|
8
|
+
this.prefix = options.prefix ?? 'optimystic:';
|
|
9
|
+
}
|
|
10
|
+
async getMetadata(blockId) {
|
|
11
|
+
return this.getJson(this.metadataKey(blockId));
|
|
12
|
+
}
|
|
13
|
+
async saveMetadata(blockId, metadata) {
|
|
14
|
+
this.setJson(this.metadataKey(blockId), metadata);
|
|
15
|
+
}
|
|
16
|
+
async getRevision(blockId, rev) {
|
|
17
|
+
return this.mmkv.getString(this.revisionKey(blockId, rev));
|
|
18
|
+
}
|
|
19
|
+
async saveRevision(blockId, rev, actionId) {
|
|
20
|
+
this.mmkv.set(this.revisionKey(blockId, rev), actionId);
|
|
21
|
+
}
|
|
22
|
+
async getPendingTransaction(blockId, actionId) {
|
|
23
|
+
return this.getJson(this.pendingKey(blockId, actionId));
|
|
24
|
+
}
|
|
25
|
+
async savePendingTransaction(blockId, actionId, transform) {
|
|
26
|
+
this.setJson(this.pendingKey(blockId, actionId), transform);
|
|
27
|
+
this.addToPendingIndex(blockId, actionId);
|
|
28
|
+
}
|
|
29
|
+
async deletePendingTransaction(blockId, actionId) {
|
|
30
|
+
this.mmkv.delete(this.pendingKey(blockId, actionId));
|
|
31
|
+
this.removeFromPendingIndex(blockId, actionId);
|
|
32
|
+
}
|
|
33
|
+
async *listPendingTransactions(blockId) {
|
|
34
|
+
const index = this.getPendingIndex(blockId);
|
|
35
|
+
for (const actionId of index) {
|
|
36
|
+
if (this.mmkv.contains(this.pendingKey(blockId, actionId))) {
|
|
37
|
+
yield actionId;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async getTransaction(blockId, actionId) {
|
|
42
|
+
return this.getJson(this.transactionKey(blockId, actionId));
|
|
43
|
+
}
|
|
44
|
+
async saveTransaction(blockId, actionId, transform) {
|
|
45
|
+
this.setJson(this.transactionKey(blockId, actionId), transform);
|
|
46
|
+
}
|
|
47
|
+
async *listRevisions(blockId, startRev, endRev) {
|
|
48
|
+
const ascending = startRev <= endRev;
|
|
49
|
+
for (let rev = startRev; ascending ? rev <= endRev : rev >= endRev; ascending ? ++rev : --rev) {
|
|
50
|
+
const actionId = await this.getRevision(blockId, rev);
|
|
51
|
+
if (actionId) {
|
|
52
|
+
yield { actionId, rev };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async getMaterializedBlock(blockId, actionId) {
|
|
57
|
+
return this.getJson(this.materializedKey(blockId, actionId));
|
|
58
|
+
}
|
|
59
|
+
async saveMaterializedBlock(blockId, actionId, block) {
|
|
60
|
+
const key = this.materializedKey(blockId, actionId);
|
|
61
|
+
if (block) {
|
|
62
|
+
this.setJson(key, block);
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
this.mmkv.delete(key);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async promotePendingTransaction(blockId, actionId) {
|
|
69
|
+
const pendingKey = this.pendingKey(blockId, actionId);
|
|
70
|
+
const content = this.mmkv.getString(pendingKey);
|
|
71
|
+
if (!content) {
|
|
72
|
+
throw new Error(`Pending action ${actionId} not found for block ${blockId}`);
|
|
73
|
+
}
|
|
74
|
+
this.mmkv.set(this.transactionKey(blockId, actionId), content);
|
|
75
|
+
this.mmkv.delete(pendingKey);
|
|
76
|
+
this.removeFromPendingIndex(blockId, actionId);
|
|
77
|
+
}
|
|
78
|
+
metadataKey(blockId) {
|
|
79
|
+
return `${this.prefix}${blockId}:meta`;
|
|
80
|
+
}
|
|
81
|
+
revisionKey(blockId, rev) {
|
|
82
|
+
return `${this.prefix}${blockId}:rev:${rev}`;
|
|
83
|
+
}
|
|
84
|
+
pendingKey(blockId, actionId) {
|
|
85
|
+
return `${this.prefix}${blockId}:pend:${actionId}`;
|
|
86
|
+
}
|
|
87
|
+
transactionKey(blockId, actionId) {
|
|
88
|
+
return `${this.prefix}${blockId}:trx:${actionId}`;
|
|
89
|
+
}
|
|
90
|
+
materializedKey(blockId, actionId) {
|
|
91
|
+
return `${this.prefix}${blockId}:block:${actionId}`;
|
|
92
|
+
}
|
|
93
|
+
pendingIndexKey(blockId) {
|
|
94
|
+
return `${this.prefix}${blockId}:pend-idx`;
|
|
95
|
+
}
|
|
96
|
+
getJson(key) {
|
|
97
|
+
const value = this.mmkv.getString(key);
|
|
98
|
+
if (!value)
|
|
99
|
+
return undefined;
|
|
100
|
+
try {
|
|
101
|
+
return JSON.parse(value);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
log('Failed to parse JSON for key %s: %o', key, err);
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
setJson(key, value) {
|
|
109
|
+
this.mmkv.set(key, JSON.stringify(value));
|
|
110
|
+
}
|
|
111
|
+
getPendingIndex(blockId) {
|
|
112
|
+
return this.getJson(this.pendingIndexKey(blockId)) ?? [];
|
|
113
|
+
}
|
|
114
|
+
addToPendingIndex(blockId, actionId) {
|
|
115
|
+
const index = this.getPendingIndex(blockId);
|
|
116
|
+
if (!index.includes(actionId)) {
|
|
117
|
+
index.push(actionId);
|
|
118
|
+
this.setJson(this.pendingIndexKey(blockId), index);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
removeFromPendingIndex(blockId, actionId) {
|
|
122
|
+
const index = this.getPendingIndex(blockId);
|
|
123
|
+
const filtered = index.filter(id => id !== actionId);
|
|
124
|
+
if (filtered.length !== index.length) {
|
|
125
|
+
this.setJson(this.pendingIndexKey(blockId), filtered);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=mmkv-storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mmkv-storage.js","sourceRoot":"","sources":["../../src/mmkv-storage.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@optimystic/db-p2p-storage-rn",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "React Native storage backend for @optimystic/db-p2p using MMKV",
|
|
6
|
+
"main": "dist/src/index.js",
|
|
7
|
+
"types": "./dist/src/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"src",
|
|
10
|
+
"dist",
|
|
11
|
+
"!dist/test",
|
|
12
|
+
"!**/*.tsbuildinfo"
|
|
13
|
+
],
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/src/index.d.ts",
|
|
17
|
+
"import": "./dist/src/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/gotchoices/optimystic.git",
|
|
23
|
+
"directory": "packages/db-p2p-storage-rn"
|
|
24
|
+
},
|
|
25
|
+
"author": "Got Choices Foundation",
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"keywords": [
|
|
28
|
+
"optimystic",
|
|
29
|
+
"database",
|
|
30
|
+
"p2p",
|
|
31
|
+
"storage",
|
|
32
|
+
"react-native",
|
|
33
|
+
"mmkv"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"clean": "aegir clean",
|
|
37
|
+
"build": "aegir build --env node",
|
|
38
|
+
"lint": "aegir lint",
|
|
39
|
+
"test": "aegir test",
|
|
40
|
+
"test:node": "aegir test -t node",
|
|
41
|
+
"dep-check": "aegir dep-check"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@types/debug": "^4.1.12",
|
|
45
|
+
"@types/mocha": "^10.0.10",
|
|
46
|
+
"@types/node": "^25.1.0",
|
|
47
|
+
"aegir": "^47.0.26",
|
|
48
|
+
"typescript": "^5.9.3"
|
|
49
|
+
},
|
|
50
|
+
"dependencies": {
|
|
51
|
+
"@optimystic/db-core": "^0.4.1",
|
|
52
|
+
"@optimystic/db-p2p": "^0.1.2",
|
|
53
|
+
"debug": "^4.4.3"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"react-native-mmkv": "^3.0.0 || ^2.0.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"react-native-mmkv": {
|
|
60
|
+
"optional": false
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"aegir": {
|
|
64
|
+
"build": {
|
|
65
|
+
"bundle": false
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/index.ts
ADDED
package/src/logger.ts
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import type { BlockId, IBlock, Transform, ActionId, ActionRev } from '@optimystic/db-core';
|
|
2
|
+
import type { BlockMetadata, IRawStorage } from '@optimystic/db-p2p';
|
|
3
|
+
import { createLogger } from './logger.js';
|
|
4
|
+
|
|
5
|
+
const log = createLogger('storage:mmkv');
|
|
6
|
+
|
|
7
|
+
/** MMKV storage interface matching react-native-mmkv */
|
|
8
|
+
export interface MMKV {
|
|
9
|
+
getString(key: string): string | undefined;
|
|
10
|
+
set(key: string, value: string): void;
|
|
11
|
+
delete(key: string): void;
|
|
12
|
+
getAllKeys(): string[];
|
|
13
|
+
contains(key: string): boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface MMKVStorageOptions {
|
|
17
|
+
/** The MMKV instance to use */
|
|
18
|
+
mmkv: MMKV;
|
|
19
|
+
/** Optional prefix for all keys to namespace this storage */
|
|
20
|
+
prefix?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class MMKVRawStorage implements IRawStorage {
|
|
24
|
+
private readonly mmkv: MMKV;
|
|
25
|
+
private readonly prefix: string;
|
|
26
|
+
|
|
27
|
+
constructor(options: MMKVStorageOptions) {
|
|
28
|
+
this.mmkv = options.mmkv;
|
|
29
|
+
this.prefix = options.prefix ?? 'optimystic:';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async getMetadata(blockId: BlockId): Promise<BlockMetadata | undefined> {
|
|
33
|
+
return this.getJson<BlockMetadata>(this.metadataKey(blockId));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async saveMetadata(blockId: BlockId, metadata: BlockMetadata): Promise<void> {
|
|
37
|
+
this.setJson(this.metadataKey(blockId), metadata);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async getRevision(blockId: BlockId, rev: number): Promise<ActionId | undefined> {
|
|
41
|
+
return this.mmkv.getString(this.revisionKey(blockId, rev)) as ActionId | undefined;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async saveRevision(blockId: BlockId, rev: number, actionId: ActionId): Promise<void> {
|
|
45
|
+
this.mmkv.set(this.revisionKey(blockId, rev), actionId);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async getPendingTransaction(blockId: BlockId, actionId: ActionId): Promise<Transform | undefined> {
|
|
49
|
+
return this.getJson<Transform>(this.pendingKey(blockId, actionId));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async savePendingTransaction(blockId: BlockId, actionId: ActionId, transform: Transform): Promise<void> {
|
|
53
|
+
this.setJson(this.pendingKey(blockId, actionId), transform);
|
|
54
|
+
this.addToPendingIndex(blockId, actionId);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async deletePendingTransaction(blockId: BlockId, actionId: ActionId): Promise<void> {
|
|
58
|
+
this.mmkv.delete(this.pendingKey(blockId, actionId));
|
|
59
|
+
this.removeFromPendingIndex(blockId, actionId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async *listPendingTransactions(blockId: BlockId): AsyncIterable<ActionId> {
|
|
63
|
+
const index = this.getPendingIndex(blockId);
|
|
64
|
+
for (const actionId of index) {
|
|
65
|
+
if (this.mmkv.contains(this.pendingKey(blockId, actionId as ActionId))) {
|
|
66
|
+
yield actionId as ActionId;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async getTransaction(blockId: BlockId, actionId: ActionId): Promise<Transform | undefined> {
|
|
72
|
+
return this.getJson<Transform>(this.transactionKey(blockId, actionId));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async saveTransaction(blockId: BlockId, actionId: ActionId, transform: Transform): Promise<void> {
|
|
76
|
+
this.setJson(this.transactionKey(blockId, actionId), transform);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async *listRevisions(blockId: BlockId, startRev: number, endRev: number): AsyncIterable<ActionRev> {
|
|
80
|
+
const ascending = startRev <= endRev;
|
|
81
|
+
for (let rev = startRev; ascending ? rev <= endRev : rev >= endRev; ascending ? ++rev : --rev) {
|
|
82
|
+
const actionId = await this.getRevision(blockId, rev);
|
|
83
|
+
if (actionId) {
|
|
84
|
+
yield { actionId, rev };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async getMaterializedBlock(blockId: BlockId, actionId: ActionId): Promise<IBlock | undefined> {
|
|
90
|
+
return this.getJson<IBlock>(this.materializedKey(blockId, actionId));
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async saveMaterializedBlock(blockId: BlockId, actionId: ActionId, block?: IBlock): Promise<void> {
|
|
94
|
+
const key = this.materializedKey(blockId, actionId);
|
|
95
|
+
if (block) {
|
|
96
|
+
this.setJson(key, block);
|
|
97
|
+
} else {
|
|
98
|
+
this.mmkv.delete(key);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async promotePendingTransaction(blockId: BlockId, actionId: ActionId): Promise<void> {
|
|
103
|
+
const pendingKey = this.pendingKey(blockId, actionId);
|
|
104
|
+
const content = this.mmkv.getString(pendingKey);
|
|
105
|
+
if (!content) {
|
|
106
|
+
throw new Error(`Pending action ${actionId} not found for block ${blockId}`);
|
|
107
|
+
}
|
|
108
|
+
this.mmkv.set(this.transactionKey(blockId, actionId), content);
|
|
109
|
+
this.mmkv.delete(pendingKey);
|
|
110
|
+
this.removeFromPendingIndex(blockId, actionId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private metadataKey(blockId: BlockId): string {
|
|
114
|
+
return `${this.prefix}${blockId}:meta`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private revisionKey(blockId: BlockId, rev: number): string {
|
|
118
|
+
return `${this.prefix}${blockId}:rev:${rev}`;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
private pendingKey(blockId: BlockId, actionId: ActionId): string {
|
|
122
|
+
return `${this.prefix}${blockId}:pend:${actionId}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private transactionKey(blockId: BlockId, actionId: ActionId): string {
|
|
126
|
+
return `${this.prefix}${blockId}:trx:${actionId}`;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
private materializedKey(blockId: BlockId, actionId: ActionId): string {
|
|
130
|
+
return `${this.prefix}${blockId}:block:${actionId}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
private pendingIndexKey(blockId: BlockId): string {
|
|
134
|
+
return `${this.prefix}${blockId}:pend-idx`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private getJson<T>(key: string): T | undefined {
|
|
138
|
+
const value = this.mmkv.getString(key);
|
|
139
|
+
if (!value) return undefined;
|
|
140
|
+
try {
|
|
141
|
+
return JSON.parse(value) as T;
|
|
142
|
+
} catch (err) {
|
|
143
|
+
log('Failed to parse JSON for key %s: %o', key, err);
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private setJson(key: string, value: unknown): void {
|
|
149
|
+
this.mmkv.set(key, JSON.stringify(value));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
private getPendingIndex(blockId: BlockId): string[] {
|
|
153
|
+
return this.getJson<string[]>(this.pendingIndexKey(blockId)) ?? [];
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
private addToPendingIndex(blockId: BlockId, actionId: ActionId): void {
|
|
157
|
+
const index = this.getPendingIndex(blockId);
|
|
158
|
+
if (!index.includes(actionId)) {
|
|
159
|
+
index.push(actionId);
|
|
160
|
+
this.setJson(this.pendingIndexKey(blockId), index);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
private removeFromPendingIndex(blockId: BlockId, actionId: ActionId): void {
|
|
165
|
+
const index = this.getPendingIndex(blockId);
|
|
166
|
+
const filtered = index.filter(id => id !== actionId);
|
|
167
|
+
if (filtered.length !== index.length) {
|
|
168
|
+
this.setJson(this.pendingIndexKey(blockId), filtered);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|