s5-js/src/methods/registry.ts

144 lines
3.4 KiB
TypeScript
Raw Normal View History

2023-12-11 02:06:43 +00:00
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";
2023-12-12 03:44:43 +00:00
import {
CID,
createKeyPair,
KeyPairEd25519,
Packer,
SignedRegistryEntry,
} from "@lumeweb/libs5";
2023-12-12 03:26:42 +00:00
import {
deserializeRegistryEntry,
2023-12-12 03:44:43 +00:00
signRegistryEntry,
2023-12-12 03:26:42 +00:00
verifyRegistryEntry,
} from "@lumeweb/libs5/lib/service/registry.js";
2023-12-11 02:06:43 +00:00
import { Buffer } from "buffer";
2023-12-12 03:26:42 +00:00
import { throwValidationError } from "../utils/validation.js";
import { base64url } from "multiformats/bases/base64";
2023-12-11 02:06:43 +00:00
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",
};
2023-12-12 03:26:42 +00:00
export const DEFAULT_PUBLISH_ENTRY_OPTIONS = {
...DEFAULT_BASE_OPTIONS,
endpointPublishEntry: "/s5/registry",
};
2023-12-11 02:06:43 +00:00
export type BaseCustomOptions = CustomClientOptions;
export type CustomRegistryOptions = BaseCustomOptions & {
endpointSubscribeEntry?: string;
2023-12-12 03:26:42 +00:00
endpointPublishEntry?: string;
2023-12-11 02:06:43 +00:00
};
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();
},
};
}
2023-12-12 03:26:42 +00:00
export async function publishEntry(
this: S5Client,
signedEntry: SignedRegistryEntry,
customOptions?: CustomRegistryOptions,
) {
const opts = {
...DEFAULT_PUBLISH_ENTRY_OPTIONS,
...this.customOptions,
...customOptions,
};
if (!verifyRegistryEntry(signedEntry)) {
throwValidationError(
"signedEntry", // name of the variable
signedEntry, // actual value
"parameter", // valueKind (assuming it's a function parameter)
"a valid signed registry entry", // expected description
);
}
return await this.executeRequest({
...opts,
endpointPath: opts.endpointPublishEntry,
method: "post",
data: {
pk: base64url.encode(signedEntry.pk),
revision: signedEntry.revision,
data: base64url.encode(signedEntry.data),
signature: base64url.encode(signedEntry.signature),
},
});
}
2023-12-12 03:44:43 +00:00
export async function createEntry(
this: S5Client,
sk: Uint8Array | KeyPairEd25519,
cid: CID,
revision = 0,
) {
if (sk instanceof Uint8Array) {
sk = createKeyPair(sk);
}
const entry = {
kp: sk,
data: cid.toBytes(),
revision,
};
return this.publishEntry(signRegistryEntry(entry));
}