@novasamatech/statement-store 0.8.7-0 → 0.8.7-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/adapter/inMemory.d.ts +32 -0
- package/dist/adapter/inMemory.js +56 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +2 -1
- package/dist/session/session.d.ts +7 -0
- package/dist/session/session.js +357 -132
- package/dist/session/session.spec.js +802 -335
- package/dist/session/statementProver.d.ts +3 -0
- package/dist/session/statementProver.js +20 -4
- package/dist/session/statementProver.spec.d.ts +1 -0
- package/dist/session/statementProver.spec.js +71 -0
- package/dist/session/types.d.ts +12 -0
- package/package.json +3 -3
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Statement } from '@novasamatech/sdk-statement';
|
|
2
|
+
import type { StatementStoreAdapter } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* An in-memory {@link StatementStoreAdapter} that replicates the real statement
|
|
5
|
+
* store's observable contract, for tests and local development.
|
|
6
|
+
*
|
|
7
|
+
* Fidelity rules (mirroring the on-chain store):
|
|
8
|
+
* - One statement per channel. A same-channel write is accepted only with a
|
|
9
|
+
* STRICTLY HIGHER expiry; an equal-or-lower expiry is rejected with
|
|
10
|
+
* {@link ExpiryTooLowError} (the store's `channelPriorityTooLow`). A
|
|
11
|
+
* byte-identical resubmission is "known" → ok, with no duplicate.
|
|
12
|
+
* - `queryStatements` returns the current per-channel statements matching the
|
|
13
|
+
* topic filter (superseded statements are evicted).
|
|
14
|
+
* - `subscribeStatements` streams statements submitted AFTER subscription that
|
|
15
|
+
* match the filter — the initial snapshot is obtained via `queryStatements`,
|
|
16
|
+
* exactly as a consumer does. Delivery is synchronous on submit.
|
|
17
|
+
*
|
|
18
|
+
* Two mirrored sessions can share ONE store to exercise full bidirectional
|
|
19
|
+
* host ↔ peer flows: a submit by one is observed by the other.
|
|
20
|
+
*
|
|
21
|
+
* It IS a {@link StatementStoreAdapter} (pass it straight to `createSession`)
|
|
22
|
+
* with extra inspection helpers attached.
|
|
23
|
+
*/
|
|
24
|
+
export type InMemoryStatementStore = StatementStoreAdapter & {
|
|
25
|
+
/** Statements currently retained — one per channel (highest expiry wins). */
|
|
26
|
+
currentStatements(): Statement[];
|
|
27
|
+
/** Every accepted submission, in order (excludes rejected writes and known no-ops). */
|
|
28
|
+
acceptedStatements(): Statement[];
|
|
29
|
+
/** Number of live subscriptions (increases on subscribe, decreases on unsubscribe). */
|
|
30
|
+
activeSubscriptions(): number;
|
|
31
|
+
};
|
|
32
|
+
export declare function createInMemoryStatementStore(): InMemoryStatementStore;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { errAsync, okAsync } from 'neverthrow';
|
|
2
|
+
import { toHex } from 'polkadot-api/utils';
|
|
3
|
+
import { ExpiryTooLowError } from './types.js';
|
|
4
|
+
export function createInMemoryStatementStore() {
|
|
5
|
+
const channels = new Map();
|
|
6
|
+
const accepted = [];
|
|
7
|
+
const subscribers = new Set();
|
|
8
|
+
const topicsOf = (s) => s.topics ?? [];
|
|
9
|
+
const matches = (filter, s) => {
|
|
10
|
+
const topics = topicsOf(s);
|
|
11
|
+
return 'matchAll' in filter
|
|
12
|
+
? filter.matchAll.every(t => topics.includes(toHex(t)))
|
|
13
|
+
: filter.matchAny.some(t => topics.includes(toHex(t)));
|
|
14
|
+
};
|
|
15
|
+
// Statement identity for the "known" (dedup) check.
|
|
16
|
+
const keyOf = (s) => `${s.channel ?? ''}|${(s.expiry ?? 0n).toString()}|${topicsOf(s).join(',')}|${s.data ? toHex(s.data) : ''}`;
|
|
17
|
+
const adapter = {
|
|
18
|
+
queryStatements(filter) {
|
|
19
|
+
return okAsync([...channels.values()].filter(s => matches(filter, s)));
|
|
20
|
+
},
|
|
21
|
+
subscribeStatements(filter, callback) {
|
|
22
|
+
const sub = { filter, callback };
|
|
23
|
+
subscribers.add(sub);
|
|
24
|
+
return () => {
|
|
25
|
+
subscribers.delete(sub);
|
|
26
|
+
};
|
|
27
|
+
},
|
|
28
|
+
submitStatement(statement) {
|
|
29
|
+
const channel = statement.channel ?? '';
|
|
30
|
+
const existing = channels.get(channel);
|
|
31
|
+
const submittedExpiry = statement.expiry ?? 0n;
|
|
32
|
+
if (existing) {
|
|
33
|
+
if (keyOf(existing) === keyOf(statement))
|
|
34
|
+
return okAsync(undefined); // known
|
|
35
|
+
const existingExpiry = existing.expiry ?? 0n;
|
|
36
|
+
if (submittedExpiry <= existingExpiry) {
|
|
37
|
+
return errAsync(new ExpiryTooLowError(submittedExpiry, existingExpiry));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
channels.set(channel, statement);
|
|
41
|
+
accepted.push(statement);
|
|
42
|
+
for (const sub of subscribers) {
|
|
43
|
+
if (matches(sub.filter, statement)) {
|
|
44
|
+
sub.callback({ statements: [statement], isComplete: true });
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return okAsync(undefined);
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
return {
|
|
51
|
+
...adapter,
|
|
52
|
+
currentStatements: () => [...channels.values()],
|
|
53
|
+
acceptedStatements: () => [...accepted],
|
|
54
|
+
activeSubscriptions: () => subscribers.size,
|
|
55
|
+
};
|
|
56
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -5,14 +5,17 @@ export type { AccountId, LocalSessionAccount, RemoteSessionAccount, SessionAccou
|
|
|
5
5
|
export { AccountIdCodec, LocalSessionAccountCodec, RemoteSessionAccountCodec, createAccountId, createLocalSessionAccount, createRemoteSessionAccount, } from './model/sessionAccount.js';
|
|
6
6
|
export type { Session } from './session/types.js';
|
|
7
7
|
export { createSession } from './session/session.js';
|
|
8
|
+
export type { ResponseStatus } from './session/scale/statementData.js';
|
|
8
9
|
export { Request, Response, ResponseCode, StatementData } from './session/scale/statementData.js';
|
|
9
10
|
export type { StatementProver } from './session/statementProver.js';
|
|
10
|
-
export { createSr25519Prover } from './session/statementProver.js';
|
|
11
|
+
export { createSlotAccountProver, createSr25519Prover } from './session/statementProver.js';
|
|
11
12
|
export type { Encryption } from './session/encyption.js';
|
|
12
13
|
export { createEncryption } from './session/encyption.js';
|
|
13
14
|
export { DecodingError, DecryptionError, UnknownError } from './session/error.js';
|
|
14
15
|
export type { LazyClient } from './adapter/lazyClient.js';
|
|
15
16
|
export { createLazyClient } from './adapter/lazyClient.js';
|
|
17
|
+
export type { InMemoryStatementStore } from './adapter/inMemory.js';
|
|
18
|
+
export { createInMemoryStatementStore } from './adapter/inMemory.js';
|
|
16
19
|
export type { StatementStoreAdapter } from './adapter/types.js';
|
|
17
20
|
export { AccountFullError, AlreadyExpiredError, BadProofError, DataTooLargeError, EncodingTooLargeError, ExpiryTooLowError, InternalStoreError, KnownExpiredError, NoAllowanceError, NoProofError, StorageFullError, } from './adapter/types.js';
|
|
18
21
|
export { createPapiStatementStoreAdapter } from './adapter/rpc.js';
|
package/dist/index.js
CHANGED
|
@@ -2,10 +2,11 @@ export { SessionIdCodec, createSessionId } from './model/session.js';
|
|
|
2
2
|
export { AccountIdCodec, LocalSessionAccountCodec, RemoteSessionAccountCodec, createAccountId, createLocalSessionAccount, createRemoteSessionAccount, } from './model/sessionAccount.js';
|
|
3
3
|
export { createSession } from './session/session.js';
|
|
4
4
|
export { Request, Response, ResponseCode, StatementData } from './session/scale/statementData.js';
|
|
5
|
-
export { createSr25519Prover } from './session/statementProver.js';
|
|
5
|
+
export { createSlotAccountProver, createSr25519Prover } from './session/statementProver.js';
|
|
6
6
|
export { createEncryption } from './session/encyption.js';
|
|
7
7
|
export { DecodingError, DecryptionError, UnknownError } from './session/error.js';
|
|
8
8
|
export { createLazyClient } from './adapter/lazyClient.js';
|
|
9
|
+
export { createInMemoryStatementStore } from './adapter/inMemory.js';
|
|
9
10
|
export { AccountFullError, AlreadyExpiredError, BadProofError, DataTooLargeError, EncodingTooLargeError, ExpiryTooLowError, InternalStoreError, KnownExpiredError, NoAllowanceError, NoProofError, StorageFullError, } from './adapter/types.js';
|
|
10
11
|
export { createPapiStatementStoreAdapter } from './adapter/rpc.js';
|
|
11
12
|
export { createSr25519Derivation, createSr25519Secret, deriveSlotAccountPublicKey, deriveSr25519PublicKey, ensureSubstrateSlotSr25519Ready, ensureSubstrateSr25519Ready, khash, signSlotAccountSecret, signWithSr25519Secret, verifySlotAccountSignature, verifySr25519Signature, } from './crypto.js';
|
|
@@ -25,5 +25,12 @@ export type SessionParams = {
|
|
|
25
25
|
sessionKey: Uint8Array;
|
|
26
26
|
maxRequestSize?: number;
|
|
27
27
|
};
|
|
28
|
+
/**
|
|
29
|
+
* Fixed per-statement wire overhead reserved before sizing the request payload:
|
|
30
|
+
* topic (32) + channel (32) + expiry (8) + proof signature (64) + signer (32).
|
|
31
|
+
* Mirrors the Android/iOS sessions, which size message batches against
|
|
32
|
+
* `maxStatementSize - overhead` rather than the raw statement limit.
|
|
33
|
+
*/
|
|
34
|
+
export declare const STATEMENT_OVERHEAD: number;
|
|
28
35
|
export declare function nextExpiry(current: bigint): bigint;
|
|
29
36
|
export declare function createSession({ localAccount, remoteAccount, statementStore, encryption, prover, sessionKey, maxRequestSize, }: SessionParams): Session;
|