Store fixes
This commit is contained in:
@@ -1,6 +1,11 @@
|
|||||||
import { BaseStore } from './base-store.js';
|
import { BaseStore } from './base-store.js';
|
||||||
import { Encrypter, SSEClient } from '../utils';
|
import { Encrypter, SSEClient } from '../utils';
|
||||||
import { Bytes, PrivateKey, PublicKey } from '@xocash/primitives';
|
import {
|
||||||
|
Bytes,
|
||||||
|
PrivateKey,
|
||||||
|
PublicKey,
|
||||||
|
decodeExtendedJson,
|
||||||
|
} from '@xocash/primitives';
|
||||||
import { pack, unpack } from 'msgpackr';
|
import { pack, unpack } from 'msgpackr';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
@@ -68,6 +73,7 @@ export class StoreRemoteHTTP extends BaseStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async subscribeEvents() {
|
async subscribeEvents() {
|
||||||
|
// Compute our signature (to authenticate with the SSE endpoint).
|
||||||
const timestamp = Math.floor(Date.now() / 1000);
|
const timestamp = Math.floor(Date.now() / 1000);
|
||||||
const signature = await StoreRemoteHTTP.signRequest(
|
const signature = await StoreRemoteHTTP.signRequest(
|
||||||
this.privateKey.toHex(),
|
this.privateKey.toHex(),
|
||||||
@@ -76,6 +82,7 @@ export class StoreRemoteHTTP extends BaseStore {
|
|||||||
{},
|
{},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Define our headers.
|
||||||
const headers = {
|
const headers = {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
@@ -84,13 +91,19 @@ export class StoreRemoteHTTP extends BaseStore {
|
|||||||
'X-Timestamp': timestamp.toString(),
|
'X-Timestamp': timestamp.toString(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const sseClient = new SSEClient(`${this.baseURL}events`, {
|
// Create our SSE Client.
|
||||||
|
// NOTE: SSE only supports text-encoding, so we use Extended JSON instead of msgpackr (which is binary).
|
||||||
|
// TODO: It would be really nice to somehow be able to support msgpackr for consistency though.
|
||||||
|
// But, from a bandwidth point of view, this is not crucial: These payloads are "live" payloads (thus, there's not much data).
|
||||||
|
await SSEClient.from(`${this.baseURL}events`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
onMessage: (e) => {
|
onMessage: (e) => {
|
||||||
// Validate the payload using Zod.
|
// Validate the payload using Zod.
|
||||||
// TODO: Use msgpackr
|
// TODO: Use msgpackr
|
||||||
const payload = StoreRemoteHTTPEventSchema.parse(JSON.parse(e.data));
|
const payload = StoreRemoteHTTPEventSchema.parse(
|
||||||
|
decodeExtendedJson(e.data),
|
||||||
|
);
|
||||||
|
|
||||||
// Emit the event (update) to any listeners.
|
// Emit the event (update) to any listeners.
|
||||||
this.emit(payload.action, {
|
this.emit(payload.action, {
|
||||||
@@ -100,8 +113,6 @@ export class StoreRemoteHTTP extends BaseStore {
|
|||||||
},
|
},
|
||||||
body: JSON.stringify({}),
|
body: JSON.stringify({}),
|
||||||
});
|
});
|
||||||
|
|
||||||
await sseClient.connect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async makeRequest(endpoint: string, payload: object = {}) {
|
private async makeRequest(endpoint: string, payload: object = {}) {
|
||||||
|
|||||||
@@ -77,8 +77,10 @@ export class SSEClient {
|
|||||||
cache: 'no-store',
|
cache: 'no-store',
|
||||||
};
|
};
|
||||||
|
|
||||||
// Using an async IIFE to handle the connection without blocking
|
// Using a regular Promise executor (not async)
|
||||||
return new Promise(async (resolve) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
// Move the async logic inside a separate function
|
||||||
|
const establishConnection = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(this.url, fetchOptions);
|
const response = await fetch(this.url, fetchOptions);
|
||||||
|
|
||||||
@@ -99,11 +101,11 @@ export class SSEClient {
|
|||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
let buffer = '';
|
let buffer = '';
|
||||||
|
|
||||||
// Start the read here so we can resolve and then start handlng the stream
|
// Start the read here so we can resolve and then start handling the stream
|
||||||
// We will await this at the start of the loop, and redeclare it at the end of each loop iteration
|
// We will await this at the start of the loop, and redeclare it at the end of each loop iteration
|
||||||
let readPromise = reader.read();
|
let readPromise = reader.read();
|
||||||
|
|
||||||
// Resolve before starting the loop so that we can start peforming other actions
|
// Resolve before starting the loop so that we can start performing other actions
|
||||||
// Keep this out of the loop to avoid flags or `already resolved` errors.
|
// Keep this out of the loop to avoid flags or `already resolved` errors.
|
||||||
resolve();
|
resolve();
|
||||||
|
|
||||||
@@ -147,9 +149,13 @@ export class SSEClient {
|
|||||||
this.options.onClose();
|
this.options.onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Doesnt seem like you want to propagate the error here.
|
// Now we'll reject the promise instead of resolving
|
||||||
resolve();
|
reject(error);
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Execute the async function
|
||||||
|
establishConnection();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user