feat: add subscribeToEntry

This commit is contained in:
Derrick Hammer 2023-12-10 21:06:43 -05:00
parent 41274582b9
commit 672b462bbe
Signed by: pcfreak30
GPG Key ID: C997C339BE476FF2
4 changed files with 104 additions and 38 deletions

31
npm-shrinkwrap.json generated
View File

@ -8,14 +8,16 @@
"name": "@lumeweb/s5-js",
"version": "0.1.0",
"dependencies": {
"@lumeweb/libs5": "^0.1.0-develop.77",
"@lumeweb/libs5": "^0.1.0-develop.78",
"@noble/hashes": "^1.3.2",
"axios": "^1.6.2",
"isomorphic-ws": "^5.0.0",
"tus-js-client": "^4.0.0",
"url-join": "^5.0.0"
},
"devDependencies": {
"@lumeweb/node-library-preset": "^0.2.7",
"@types/ws": "^8.5.10",
"presetter": "*"
}
},
@ -1786,9 +1788,9 @@
}
},
"node_modules/@lumeweb/libs5": {
"version": "0.1.0-develop.77",
"resolved": "https://registry.npmjs.org/@lumeweb/libs5/-/libs5-0.1.0-develop.77.tgz",
"integrity": "sha512-CDq5rhFFpLWouHIQCrc+VXO73ZXnIOOq3DoUzHJonqsUpQ2dBOZFJwfkumWVwlHIaEXHbfaRUrq6CCpE86L9vQ==",
"version": "0.1.0-develop.78",
"resolved": "https://registry.npmjs.org/@lumeweb/libs5/-/libs5-0.1.0-develop.78.tgz",
"integrity": "sha512-7ippBR7x9KbhkEjoMWPYZHJmmVxqeO2LvtAN+gJdj74NiAVujsKC1S3u5sXAeXyHdQ4Grsl8ClUfDumSirkdXg==",
"dependencies": {
"@noble/curves": "^1.1.0",
"@noble/hashes": "^1.3.1",
@ -3513,7 +3515,6 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz",
"integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==",
"dev": true,
"peer": true,
"dependencies": {
"undici-types": "~5.26.4"
}
@ -3552,6 +3553,15 @@
"dev": true,
"peer": true
},
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
"integrity": "sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==",
"dev": true,
"dependencies": {
"@types/node": "*"
}
},
"node_modules/@types/yargs": {
"version": "17.0.32",
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz",
@ -9234,6 +9244,14 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"dev": true
},
"node_modules/isomorphic-ws": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz",
"integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==",
"peerDependencies": {
"ws": "*"
}
},
"node_modules/issue-parser": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/issue-parser/-/issue-parser-6.0.0.tgz",
@ -19658,8 +19676,7 @@
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
"dev": true,
"peer": true
"dev": true
},
"node_modules/unicorn-magic": {
"version": "0.1.0",

View File

@ -5,6 +5,7 @@
"module": "lib/index.js",
"devDependencies": {
"@lumeweb/node-library-preset": "^0.2.7",
"@types/ws": "^8.5.10",
"presetter": "*"
},
"readme": "ERROR: No README data found!",
@ -15,9 +16,10 @@
"semantic-release": "semantic-release"
},
"dependencies": {
"@lumeweb/libs5": "^0.1.0-develop.77",
"@lumeweb/libs5": "^0.1.0-develop.78",
"@noble/hashes": "^1.3.2",
"axios": "^1.6.2",
"isomorphic-ws": "^5.0.0",
"tus-js-client": "^4.0.0",
"url-join": "^5.0.0"
},

View File

@ -24,6 +24,7 @@ import {
ExecuteRequestError,
Headers,
} from "./request.js";
import { subscribeToEntry } from "./methods/registry.js";
/**
* Custom client options.
@ -125,6 +126,9 @@ export class S5Client {
getCidUrl = getCidUrl;
getMetadata = getMetadata;
// Registry
subscribeToEntry = subscribeToEntry;
/**
* The S5 Client which can be used to access S5-net.
*
@ -268,36 +272,6 @@ export class S5Client {
}
}
// ===============
// Private Methods
// ===============
/**
* Gets the current server URL for the portal. You should generally use
* `portalUrl` instead - this method can be used for detecting whether the
* current URL is a server URL.
*
* @returns - The portal server URL.
*/
protected async resolvePortalServerUrl(): Promise<string> {
const response = await this.executeRequest({
...this.customOptions,
method: "head",
url: this.initialPortalUrl,
});
if (!response.headers) {
throw new Error(
"Did not get 'headers' in response despite a successful request. Please try again and report this issue to the devs if it persists.",
);
}
const portalUrl = response.headers["s5-server-api"];
if (!portalUrl) {
throw new Error("Could not get server portal URL for the given portal");
}
return portalUrl;
}
/**
* Make a request to resolve the provided `initialPortalUrl`.
*

73
src/methods/registry.ts Normal file
View File

@ -0,0 +1,73 @@
import { DEFAULT_BASE_OPTIONS } from "../utils/options.js";
import { CustomClientOptions, S5Client } from "../client.js";
import { ensureBytes } from "@noble/curves/abstract/utils";
import WS from "isomorphic-ws";
import { buildRequestUrl } from "#request.js";
import { Packer, SignedRegistryEntry } from "@lumeweb/libs5";
import { deserializeRegistryEntry } from "@lumeweb/libs5/lib/service/registry.js";
import { Buffer } from "buffer";
export const DEFAULT_GET_ENTRY_OPTIONS = {
...DEFAULT_BASE_OPTIONS,
endpointGetEntry: "/s5/registry",
};
export const DEFAULT_SET_ENTRY_OPTIONS = {
...DEFAULT_BASE_OPTIONS,
endpointSetEntry: "/s5/registry",
deleteForever: false,
};
export const DEFAULT_SUBSCRIBE_ENTRY_OPTIONS = {
...DEFAULT_BASE_OPTIONS,
endpointSubscribeEntry: "/s5/registry/subscription",
};
export type BaseCustomOptions = CustomClientOptions;
export type CustomRegistryOptions = BaseCustomOptions & {
endpointSubscribeEntry?: string;
};
export async function subscribeToEntry(
this: S5Client,
publicKey: Uint8Array,
customOptions?: CustomRegistryOptions,
) {
const opts = {
...DEFAULT_SUBSCRIBE_ENTRY_OPTIONS,
...this.customOptions,
...customOptions,
};
publicKey = ensureBytes("public key", publicKey, 32);
const url = await buildRequestUrl(this, {
baseUrl: await this.portalUrl(),
endpointPath: opts.endpointSubscribeEntry,
});
const socket = new WS(url);
socket.once("open", () => {
const packer = new Packer();
packer.pack(2);
packer.pack(publicKey);
socket.send(packer.takeBytes());
});
return {
listen(cb: (entry: SignedRegistryEntry) => void) {
socket.on("message", (data) => {
cb(deserializeRegistryEntry(new Uint8Array(data as Buffer)));
});
},
end() {
if ([socket.CLOSING, socket.CLOSED].includes(socket.readyState as any)) {
return;
}
socket.close();
},
};
}