* fix: redesign and compact Signed-off-by: Leonid Kaganov <lleo@lleo.me> * fix: comments Signed-off-by: Leonid Kaganov <lleo@lleo.me> * fix: correctly dropping connections in timeout Signed-off-by: Leonid Kaganov <lleo@lleo.me> * feature: loglevel in config Signed-off-by: Leonid Kaganov <lleo@lleo.me> * fix: loglevel in config Signed-off-by: Leonid Kaganov <lleo@lleo.me> * features lopt: direct personal messages between websockets by username Signed-off-by: Leonid Kaganov <lleo@lleo.me> * fix collaborator security query Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com> * Fix svelte warnings Signed-off-by: Artem Savchenko <armisav@gmail.com> * Change log Signed-off-by: Artem Savchenko <armisav@gmail.com> * Add workspace permissions enum Signed-off-by: Artem Savchenko <armisav@gmail.com> * Add changelog Signed-off-by: Artem Savchenko <armisav@gmail.com> * Permission methods Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update core and account versions Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix predicate Signed-off-by: Artem Savchenko <armisav@gmail.com> * Add changelog Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix object clone Signed-off-by: Artem Savchenko <armisav@gmail.com> * Bump and add changelog Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix exception in getTypeOf Signed-off-by: Artem Savchenko <armisav@gmail.com> * Disable changelog check Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix failed to fetch errors in account client Signed-off-by: Artem Savchenko <armisav@gmail.com> * Clean up Signed-off-by: Artem Savchenko <armisav@gmail.com> * Support in-memory mode for hulypulse Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update platform-rig Signed-off-by: Artem Savchenko <armisav@gmail.com> * Comment change log verification Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update pnpm lock Signed-off-by: Artem Savchenko <armisav@gmail.com> * Sync with foundation Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update platform-rig Signed-off-by: Artem Savchenko <armisav@gmail.com> * Sync with foundation and bump versions Signed-off-by: Artem Savchenko <armisav@gmail.com> * Sync with foundations Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update and increment versions Signed-off-by: Artem Savchenko <armisav@gmail.com> * Skip change log verification Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix hulypulse connections Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix formatting and ci Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix validation warnings Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix formatting Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix validation Signed-off-by: Artem Savchenko <armisav@gmail.com> * Fix pnpm lock Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update versions Signed-off-by: Artem Savchenko <armisav@gmail.com> * Update hulypulse version Signed-off-by: Artem Savchenko <armisav@gmail.com> --------- Signed-off-by: Leonid Kaganov <lleo@lleo.me> Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com> Signed-off-by: Artem Savchenko <armisav@gmail.com> Co-authored-by: Leonid Kaganov <lleo@lleo.me> Co-authored-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
HulypulseClient
A TypeScript/Node.js client for the Hulypulse WebSocket server.
Supports automatic reconnection, request–response correlation, get / put / delete, and subscriptions.
Main Methods
put(key: string, data: string, TTL?: number): Promise
Stores a value under a key.
TTL (optional) — time-to-live in seconds.
Resolves with true if the operation succeeded.
await client.put("workspace/users/123", "Alice", 60) → true
get(key: string): Promise<any | false>
Retrieves the value for a key.
Resolves with the value if found.
Resolves with false if the key does not exist.
const value = await client.get("workspace/users/123") if (value) { console.log("User data:", value) } else { console.log("User not found") }
get_full(key: string): Promise<{data, etag, expires_at} | false>
Retrieves the full record:
data — stored value,
etag — data identifier,
expires_at — expiration in seconds.
const full = await client.get_full("workspace/users/123") if (full) { console.log(full.data, full.etag, full.expires_at) }
delete(key: string): Promise
Deletes a key.
Resolves with true if the key was deleted.
Resolves with false if the key was not found.
const deleted = await client.delete("workspace/users/123") console.log(deleted ? "Deleted" : "Not found")
subscribe(key: string, callback: (msg, key, index) => void): Promise
Subscribes to updates for a key (or prefix).
The callback is invoked on every event: Set, Del, Expired
Resolves with true if a new subscription was created.
Resolves with false if the callback was already subscribed.
const cb = (msg, key, index) => {
if( msg.message === 'Expired' ) console.log(${msg.key} was expired)
}
await client.subscribe("workspace/users/", cb) // Now cb will be called when any key starting with "workspace/users/" changes
unsubscribe(key: string, callback: Callback): Promise
Unsubscribes a specific callback.
Resolves with true if the callback was removed (and if it was the last one, the server gets an unsub message).
Resolves with false if the callback was not found.
await client.unsubscribe("workspace/users/", cb)
send(message: any): Promise
Low-level method to send a raw message.
Automatically attaches a correlation id.
Resolves when a response with the same correlation is received.
const reply = await client.send({ type: "get", key: "workspace/users/123" }) console.log("Raw reply:", reply)
Reconnection
If the connection drops, the client automatically reconnects.
All active subscriptions are re-sent to the server after reconnect.
Closing
The client supports both manual closing and the new using syntax (TypeScript 5.2+).
clientSymbol.dispose // closes the connection
or, if needed internally:
(client as any).close()
Usage Example
import { HulypulseClient } from "./hulypulse_client.js"
async function main() {
// connect
const client = await HulypulseClient.connect("wss://hulypulse_mem.lleo.me/ws")
// subscribe to updates
const cb = (msg, key, index) => {
console.log("Update for", key, ":", msg)
}
await client.subscribe("workspace/users/", cb)
// put value
await client.put("workspace/users/123", JSON.stringify({ name: "Alice" }), 5)
// get value
const value = await client.get("workspace/users/123")
console.log("Fetched:", value)
// get full record
const full = await client.get_full("workspace/users/123")
if (full) {
console.log(full.data, full.etag, full.expires_at)
}
// delete key
const deleted = await client.delete("workspace/users/123")
console.log(deleted ? "Deleted" : "Not found")
// unsubscribe
await client.unsubscribe("workspace/users/", cb)
// low-level send
const reply = await client.send({ type: "sublist" })
console.log("My sublists:", reply)
// dispose
client[Symbol.dispose]()
}
main()