20 Commits
1.0.5 ... 1.1.7

Author SHA1 Message Date
c6ad01b710 Fix textChannelService.ts websocket
All checks were successful
Setup testing environment and test the code / build (push) Successful in 1m1s
Publish to NPM / build-and-publish (release) Successful in 30s
2026-04-11 17:14:39 +02:00
113cff5512 Fix textChannelService.ts websocket
Some checks failed
Setup testing environment and test the code / build (push) Failing after 1s
Publish to NPM / build-and-publish (release) Successful in 40s
2026-04-11 17:03:19 +02:00
2c91b73a60 Fix textChannelService.ts websocket
Some checks failed
Setup testing environment and test the code / build (push) Has been cancelled
2026-04-11 17:02:53 +02:00
866c8a1838 Removed dotenv
All checks were successful
Setup testing environment and test the code / build (push) Successful in 1m22s
Publish to NPM / build-and-publish (release) Successful in 29s
2026-04-10 21:17:16 +02:00
926a28b7f9 SessionManager hotfix
All checks were successful
Setup testing environment and test the code / build (push) Successful in 2m37s
Publish to NPM / build-and-publish (release) Successful in 1m44s
2026-04-10 19:18:13 +02:00
9d6a18dda4 Updated Attachment type
All checks were successful
Setup testing environment and test the code / build (push) Successful in 2m42s
Publish to NPM / build-and-publish (release) Successful in 2m47s
2026-04-10 09:18:16 +02:00
76f573023f WebSocket onNewConnId scope hotfix
All checks were successful
Setup testing environment and test the code / build (push) Successful in 1m10s
Publish to NPM / build-and-publish (release) Successful in 32s
2026-04-10 08:14:47 +02:00
b217123b99 Fix tests
All checks were successful
Setup testing environment and test the code / build (push) Successful in 1m9s
Publish to NPM / build-and-publish (release) Successful in 32s
2026-04-10 08:01:11 +02:00
59f7e10dd7 Race-condition fix
Some checks failed
Setup testing environment and test the code / build (push) Failing after 1m9s
2026-04-10 07:59:45 +02:00
56a0167120 WebSocket update
Some checks failed
Setup testing environment and test the code / build (push) Failing after 1m26s
Publish to NPM / build-and-publish (release) Successful in 29s
2026-04-10 07:38:52 +02:00
dc782003b0 Improve DX for message sending
Some checks failed
Setup testing environment and test the code / build (push) Failing after 0s
Publish to NPM / build-and-publish (release) Successful in 35s
2026-04-09 16:56:11 +02:00
2af9142d6c Improve DX for message sending
Some checks failed
Setup testing environment and test the code / build (push) Failing after 1m39s
Publish to NPM / build-and-publish (release) Failing after 33s
2026-04-09 16:53:08 +02:00
40905b225c Improve DX for message sending
Some checks failed
Setup testing environment and test the code / build (push) Has been cancelled
2026-04-09 16:52:55 +02:00
77e032fdb2 HotFix
All checks were successful
Setup testing environment and test the code / build (push) Successful in 59s
Publish to NPM / build-and-publish (release) Successful in 31s
2026-04-09 12:04:42 +02:00
96a5e5896b HotFix
All checks were successful
Setup testing environment and test the code / build (push) Successful in 1m13s
Publish to NPM / build-and-publish (release) Successful in 33s
2026-04-09 11:56:40 +02:00
14fe7ef41d HotFix 2026-04-09 11:56:29 +02:00
d04bd6a437 HotFix
All checks were successful
Setup testing environment and test the code / build (push) Successful in 57s
Publish to NPM / build-and-publish (release) Successful in 33s
2026-04-09 11:18:47 +02:00
0e553767b6 HotFix
Some checks failed
Publish to NPM / build-and-publish (release) Has been cancelled
Setup testing environment and test the code / build (push) Failing after 1m1s
2026-04-09 11:16:45 +02:00
b7af5497a4 Fix DM WebSocket
Some checks failed
Setup testing environment and test the code / build (push) Failing after 1m22s
Publish to NPM / build-and-publish (release) Successful in 30s
2026-04-09 11:12:41 +02:00
1cd629e3c1 loadSessions hotfix
All checks were successful
Setup testing environment and test the code / build (push) Successful in 1m20s
Publish to NPM / build-and-publish (release) Successful in 27s
2026-04-08 19:18:06 +02:00
24 changed files with 74 additions and 48 deletions

View File

@@ -45,13 +45,11 @@ jobs:
with:
node-version: '24'
- name: Create .env file
run: |
echo "API_URL=http://api:3000" >> .env
echo "CDN_URL=http://cdn:4000" >> .env
echo "WS_URL=ws://api:3000" >> .env
- name: Run Vitest
env:
API_URL: http://api:3000
CDN_URL: http://cdn:4000
WS_URL: ws://api:3000
run: |
npm install
npm test --experimental-websocket

8
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "chatenium-sdk",
"version": "1.0.0",
"name": "@chatenium/chatenium-sdk",
"version": "1.0.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "chatenium-sdk",
"version": "1.0.0",
"name": "@chatenium/chatenium-sdk",
"version": "1.0.8",
"dependencies": {
"@faker-js/faker": "^10.4.0",
"axios": "^1.14.0",

View File

@@ -1,6 +1,6 @@
{
"name": "@chatenium/chatenium-sdk",
"version": "1.0.5",
"version": "1.1.7",
"description": "A library for interacting with the Chatenium API",
"type": "module",
"main": "dist/index.js",
@@ -39,7 +39,6 @@
"dependencies": {
"@faker-js/faker": "^10.4.0",
"axios": "^1.14.0",
"dotenv": "^17.4.0",
"msw": "^2.12.14",
"uuid": "^13.0.0"
}

View File

@@ -5,19 +5,12 @@ export interface SDKConfig {
}
declare const process: any;
declare const require: any;
const isNode =
typeof process !== 'undefined' &&
typeof process.versions !== 'undefined' &&
typeof process.versions.node !== 'undefined';
if (isNode) {
try {
require('dotenv').config();
} catch { }
}
const getEnv = (key: string): string | undefined => {
if (!isNode) return undefined;
return process.env?.[key];

View File

@@ -64,9 +64,10 @@ export class WebSocketHandler {
if (payl.action == "connectionId") {
console.log("ConnectionID received")
const data: WSConnIdPayload = JSON.parse(payl.data);
this.connectionId = data.connId;
this.listeners.forEach(listener => {
console.log(data.connId, listener)
listener.onNewConnId(data.connId)
this.connectionId = data.connId;
})
} else {
this.listeners.forEach(listener => {
@@ -78,6 +79,10 @@ export class WebSocketHandler {
}
public registerService(service: WSListenerPipe) {
console.log("Registering service", service)
if (this.connId) {
service.onNewConnId(this.connId)
}
this.listeners.add(service);
}

View File

@@ -17,6 +17,7 @@ export interface JoinWebsocketRoomReq {
channelId: string
networkId: string
categoryId: string
disableAutoRemove: boolean
}
export interface StreamRegistry {

View File

@@ -24,4 +24,5 @@ export interface Attachment {
path: string
height: number
width: number
extraMetaData: Record<string, string> // Used by clients
}

View File

@@ -58,6 +58,7 @@ export interface JoinWsRoomReq {
connId: string
chatid: string
userid: string
disableAutoRemove: boolean
}
// Response schemas

View File

@@ -41,5 +41,5 @@ export interface FileData {
}
export interface FileUploadProgressListener {
fileProgressUpdate: (fileId: string, allChunks: number, chunksDone: number) => void
fileProgressUpdate: (tempMsgId: string, fileId: string, allChunks: number, chunksDone: number) => void
}

View File

@@ -255,6 +255,7 @@ export interface JoinWebSocketRoomReq {
userid: string
connId: string
networkId: string
disableAutoRemove: boolean
}
export interface GetFromInviteReq {

View File

@@ -78,6 +78,7 @@ export interface JoinWsRoomReq {
networkId: string
categoryId: string
userid: string
disableAutoRemove: boolean
}
// Response schemas

View File

@@ -28,4 +28,8 @@ export const dmHandlers = [
message: body.message,
})
}),
http.post('*/v2/chat/dm/joinWebSocketRoom', async () => {
return HttpResponse.json()
}),
]

View File

@@ -31,7 +31,7 @@ export class BroadcastChannelService {
})
WebSocketHandler.getInstance().registerService({
identifier: channelId,
onNewConnId: this.onNewConnId,
onNewConnId: newConnId => this.onNewConnId(newConnId),
onNewMessage: wsMessageListener,
})
}
@@ -81,6 +81,7 @@ export class BroadcastChannelService {
networkId: this.networkId,
connId: WebSocketHandler.getInstance().connId,
categoryId: this.categoryId,
disableAutoRemove: true
});
return
} catch (e) {

View File

@@ -32,7 +32,7 @@ export class ChatService {
})
WebSocketHandler.getInstance().registerService({
identifier: userid,
onNewConnId: this.onNewConnId,
onNewConnId: newConnId => this.onNewConnId(newConnId),
onNewMessage: wsMessageListener,
})
}

View File

@@ -23,7 +23,7 @@ describe("DmService", () => {
it('should send a new message', async () => {
const message = faker.internet.displayName()
const newMessage = await service.sendMessage(message)
const newMessage = await service.sendMessage("", message)
expect(newMessage.message).toBe(message)
});
})

View File

@@ -36,14 +36,15 @@ export class DMService {
})
WebSocketHandler.getInstance().registerService({
identifier: chatid,
onNewConnId: this.onNewConnId,
onNewConnId: newConnId => this.onNewConnId(newConnId),
onNewMessage: wsMessageListener,
})
}
private onNewConnId(newConnId: string) {
console.log("NetworkService: New connection id")
console.log("DmService: New connection id")
this.client.defaults.headers["X-WS-ID"] = newConnId;
this.joinWebSocketRoom().then()
}
/**
@@ -129,17 +130,18 @@ export class DMService {
/**
* Sends a new message to the chat
* @param tempMsgId
* @param message
* @param replyTo
* @param replyToMessage
* @param attachments
* @param progressListener
*/
async sendMessage(message: string, replyTo: string | null = null, replyToMessage: string | null = null, attachments: FileData[] | null = null, progressListener: FileUploadProgressListener | null = null): Promise<Message> {
async sendMessage(tempMsgId: string, message: string, replyTo: string | null = null, replyToMessage: string | null = null, attachments: FileData[] | null = null, progressListener: FileUploadProgressListener | null = null): Promise<Message> {
let uploadId = ""
if (attachments) {
const uploader = new FileUploadService(this.token)
uploadId = await uploader.uploadFiles(this.chatid, this.userid, attachments, progressListener!)
uploadId = await uploader.uploadFiles(tempMsgId, this.chatid, this.userid, attachments, progressListener!)
}
try {
const resp = await this.client.post<Message>("chat/dm/finishMessage", <FinishMessageReq>{
@@ -244,10 +246,11 @@ export class DMService {
*/
async joinWebSocketRoom(): Promise<void> {
try {
const resp = await this.client.patch("chat/dm/joinWebSocketRoom", <JoinWsRoomReq>{
await this.client.post("v2/chat/dm/joinWebSocketRoom", <JoinWsRoomReq>{
chatid: this.chatid,
userid: this.userid,
connId: WebSocketHandler.getInstance().connId,
disableAutoRemove: true
});
return
} catch (e) {

View File

@@ -4,7 +4,7 @@ import {FileUploadService} from './fileUploadService.js';
describe("fileUploadService", () => {
it('should upload files', async () => {
const service = new FileUploadService("");
const uploadId = await service.uploadFiles("", "", [], {
const uploadId = await service.uploadFiles("","", "", [], {
fileProgressUpdate: () => {}
})
expect(uploadId).toBe("MockUploadId")

View File

@@ -39,12 +39,13 @@ export class FileUploadService {
/**
* Automatically registers the upload, calculates chunksize and uploads each chunk and returns with an uploadId that must be provided when sending the message
* @param tempMsgId
* @param roomId chatid or channelId
* @param userid
* @param files
* @param listener
*/
async uploadFiles(roomId: string, userid: string, files: FileData[], listener: FileUploadProgressListener): Promise<string> {
async uploadFiles(tempMsgId: string, roomId: string, userid: string, files: FileData[], listener: FileUploadProgressListener): Promise<string> {
let registrations: FileUploadRegistration[] = [];
files.forEach(file => {
@@ -63,7 +64,7 @@ export class FileUploadService {
files: registrations,
});
for (let filesUploaded = 0; filesUploaded < files.length; filesUploaded++) {
await this.uploadFile(resp.data.uploadId, roomId, userid, files[filesUploaded], registrations[filesUploaded], listener)
await this.uploadFile(tempMsgId, resp.data.uploadId, roomId, userid, files[filesUploaded], registrations[filesUploaded], listener)
}
await this.finishUpload(roomId, userid, resp.data.uploadId)
return resp.data.uploadId
@@ -93,7 +94,7 @@ export class FileUploadService {
}
}
private async uploadFile(uploadId: string, roomId: string, userid: string, file: FileData, registration: FileUploadRegistration, listener: FileUploadProgressListener): Promise<void> {
private async uploadFile(tempMsgId: string, uploadId: string, roomId: string, userid: string, file: FileData, registration: FileUploadRegistration, listener: FileUploadProgressListener): Promise<void> {
const chunkSize = this.calculateChunkSize(file.data.size);
const totalChunks = Math.ceil(file.data.size / chunkSize);
@@ -105,7 +106,7 @@ export class FileUploadService {
const chunk = new Uint8Array(arrayBuffer.slice(start, end));
const base64 = this.uint8ToBase64(chunk);
await this.uploadChunk(uploadId, roomId, userid, registration.fileId, base64);
listener.fileProgressUpdate(file.fileId, totalChunks, i)
listener.fileProgressUpdate(tempMsgId, file.fileId, totalChunks, i)
}
}

View File

@@ -39,7 +39,7 @@ export class NetworkService {
})
WebSocketHandler.getInstance().registerService({
identifier: networkId,
onNewConnId: this.onNewConnId,
onNewConnId: newConnId => this.onNewConnId(newConnId),
onNewMessage: wsMessageListener,
})
}
@@ -863,6 +863,7 @@ export class NetworkService {
userid: this.userid,
networkId: this.networkId,
connId: connId,
disableAutoRemove: true
});
return
} catch (e) {

View File

@@ -36,13 +36,21 @@ export class SessionManager {
async loadSessions(): Promise<Session[]> {
const tokens = await this.keyring.getAll()
const sessions: Session[] = []
for (const token of tokens) {
const userData = await this.database.get("sessions", token.split(".")[0])
if (userData) {
sessions.push({
token: token,
userData: JSON.parse(userData)
})
for (const tokenKey of tokens) {
try {
const token = await this.keyring.get(tokenKey)
const userData = await this.database.get("sessions", tokenKey)
if (userData && userData.trim().length > 0) {
sessions.push({
token: token,
userData: JSON.parse(userData)
})
}
} catch (e) {
console.error(`Failed to parse session for ${tokenKey}:`, e)
continue
}
}
@@ -69,6 +77,11 @@ export class SessionManager {
*/
async loadPreferredSession() {
const sessions = await this.loadSessions()
if (sessions.length == 0) {
throw new Error("No sessions found")
}
let preferredUser = await this.getPreferredUser()
if (preferredUser == "") {
preferredUser = sessions[0].userData.userid

View File

@@ -40,7 +40,7 @@ export class TextChannelServiceService {
})
WebSocketHandler.getInstance().registerService({
identifier: channelId,
onNewConnId: this.onNewConnId,
onNewConnId: newConnId => this.onNewConnId(newConnId),
onNewMessage: wsMessageListener,
})
}
@@ -137,17 +137,18 @@ export class TextChannelServiceService {
/**
* Sends a new message to the chat
* @param tempMsgId
* @param message
* @param replyTo
* @param replyToMessage
* @param attachments
* @param progressListener
*/
async sendMessage(message: string, replyTo: string | null = null, replyToMessage: string | null = null, attachments: FileData[] | null = null, progressListener: FileUploadProgressListener | null = null): Promise<Message> {
async sendMessage(tempMsgId: string, message: string, replyTo: string | null = null, replyToMessage: string | null = null, attachments: FileData[] | null = null, progressListener: FileUploadProgressListener | null = null): Promise<Message> {
let uploadId = ""
if (attachments) {
const uploader = new FileUploadService(this.token)
uploadId = await uploader.uploadFiles(this.channelId, this.userid, attachments, progressListener!)
uploadId = await uploader.uploadFiles(tempMsgId, this.channelId, this.userid, attachments, progressListener!)
}
try {
const resp = await this.client.post<Message>("network/channel/finishMessage", <FinishMessageReq>{
@@ -262,12 +263,13 @@ export class TextChannelServiceService {
*/
async joinWebSocketRoom(): Promise<void> {
try {
const resp = await this.client.patch("network/channel/joinWebSocketRoom", <JoinWsRoomReq>{
const resp = await this.client.post("v2/network/channel/joinWebSocketRoom", <JoinWsRoomReq>{
networkId: this.networkId,
channelId: this.channelId,
categoryId: this.categoryId,
userid: this.userid,
connId: WebSocketHandler.getInstance().connId,
disableAutoRemove: true
});
return
} catch (e) {

View File

@@ -38,7 +38,7 @@ describe("DmService Integration Testing", () => {
it('should send a message', async () => {
const message = faker.lorem.paragraph()
const newMessage = await service.sendMessage(message)
const newMessage = await service.sendMessage("", message)
expect(newMessage.message).toBe(message)
});

View File

@@ -18,6 +18,7 @@ describe("FileUploadService Integration Testing", () => {
const service = new FileUploadService(FILE_UPL_SERVICE_TESTING_TOKEN);
await service.uploadFiles(
"",
FILE_UPL_SERVICE_TESTING_CHAT_ID,
FILE_UPL_SERVICE_TESTING_USER_ID,
[

View File

@@ -43,7 +43,7 @@ describe("DmService Integration Testing", () => {
it('should send a message', async () => {
const message = faker.lorem.paragraph()
const newMessage = await service.sendMessage(message)
const newMessage = await service.sendMessage("", message)
expect(newMessage.message).toBe(message)
});