Compare commits
No commits in common. "main" and "v1.0.2-dev" have entirely different histories.
main
...
v1.0.2-dev
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,4 @@
|
||||
package-lock.json
|
||||
pnpm-lock.yaml
|
||||
node_modules
|
||||
dist
|
||||
test
|
@ -2,8 +2,8 @@ const fs = require("fs");
|
||||
|
||||
const files = []
|
||||
|
||||
fs.readdirSync("dist").filter( f => f.endsWith(".js") ).forEach( f => {
|
||||
files.push(`export * from "./${f.split(".")[0]}"`)
|
||||
fs.readdirSync("dist").filter( f => f.endsWith(".d.ts") ).forEach( f => {
|
||||
files.push(`export * from "./${f}"`)
|
||||
});
|
||||
|
||||
fs.writeFileSync("dist/index.d.ts", files.join("\n"))
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "marcsync",
|
||||
"version": "1.2.1",
|
||||
"version": "1.0.2-dev",
|
||||
"description": "A NodeJS MarcSync Client to communicate with MarcSync's API",
|
||||
"main": "dist/marcsync.js",
|
||||
"types": "dist/index.d.ts",
|
||||
@ -23,11 +23,6 @@
|
||||
},
|
||||
"homepage": "https://github.com/MarciiTheDev/marcsync-nodejs-client#readme",
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.3.3",
|
||||
"dotenv": "^16.3.1",
|
||||
"typescript": "^5.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/signalr": "^7.0.7"
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Entry, EntryData, EntryNotFound } from "./Entry";
|
||||
import { Unauthorized } from "./marcsync";
|
||||
|
||||
export class Collection<T extends EntryData> {
|
||||
export class Collection {
|
||||
|
||||
private _accessToken: string;
|
||||
private _collectionName: string;
|
||||
@ -149,7 +149,7 @@ export class Collection<T extends EntryData> {
|
||||
* });
|
||||
*
|
||||
*/
|
||||
async createEntry(data: T): Promise<Entry<T>> {
|
||||
async createEntry(data: EntryData): Promise<Entry> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v0/entries/${this._collectionName}`, {
|
||||
method: "POST",
|
||||
@ -185,7 +185,7 @@ export class Collection<T extends EntryData> {
|
||||
*
|
||||
* const entry = await collection.getEntryById("my-entry-id");
|
||||
*/
|
||||
async getEntryById(id: string): Promise<Entry<T>> {
|
||||
async getEntryById(id: string): Promise<Entry> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}?methodOverwrite=GET`, {
|
||||
method: "PATCH",
|
||||
@ -203,7 +203,7 @@ export class Collection<T extends EntryData> {
|
||||
const json = await result.json();
|
||||
if (!json.success) throw new Error();
|
||||
if(json.entries.length === 0) throw new EntryNotFound();
|
||||
return new Entry<T>(this._accessToken, this._collectionName, json.entries[0]);
|
||||
return new Entry(this._accessToken, this._collectionName, json.entries[0]);
|
||||
} catch (e) {
|
||||
if(e instanceof Unauthorized) throw new Unauthorized();
|
||||
if(e instanceof EntryNotFound) throw new EntryNotFound();
|
||||
@ -236,7 +236,7 @@ export class Collection<T extends EntryData> {
|
||||
* @see {@link EntryData} for more information about entry data.
|
||||
*
|
||||
*/
|
||||
async getEntries(filter?: Partial<{ [K in keyof T]: T[K] }>): Promise<Entry<T>[]> {
|
||||
async getEntries(filter: EntryData): Promise<Entry[]> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}?methodOverwrite=GET`, {
|
||||
method: "PATCH",
|
||||
@ -245,7 +245,7 @@ export class Collection<T extends EntryData> {
|
||||
"content-type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
filters: filter || {}
|
||||
filters: filter
|
||||
})
|
||||
})
|
||||
if (result.status === 401) throw new Unauthorized();
|
||||
@ -297,7 +297,7 @@ export class Collection<T extends EntryData> {
|
||||
* **__warning: Will delete the entries from the collection. This action cannot be undone.__**
|
||||
*
|
||||
*/
|
||||
async deleteEntries(filter?: Partial<{ [K in keyof T]: T[K] }>): Promise<number> {
|
||||
async deleteEntries(filter: EntryData): Promise<number> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
|
||||
method: "DELETE",
|
||||
@ -324,7 +324,7 @@ export class Collection<T extends EntryData> {
|
||||
* @returns The Id of the updated entry
|
||||
*
|
||||
*/
|
||||
async updateEntryById(id: string, data: Partial<{ [K in keyof T]: T[K] }>): Promise<string> {
|
||||
async updateEntryById(id: string, data: EntryData): Promise<string> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
|
||||
method: "PUT",
|
||||
@ -355,7 +355,7 @@ export class Collection<T extends EntryData> {
|
||||
* @returns The amount of updated entries
|
||||
*
|
||||
*/
|
||||
async updateEntries(filter: Partial<{ [K in keyof T]: T[K] }>, data: Partial<{ [K in keyof T]: T[K] }>): Promise<number> {
|
||||
async updateEntries(filter: EntryData, data: EntryData): Promise<number> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
|
||||
method: "PUT",
|
||||
|
100
src/Entry.ts
100
src/Entry.ts
@ -1,15 +1,15 @@
|
||||
type WithDefaultId<T extends EntryData> = T['_id'] extends string
|
||||
? T
|
||||
: T & { _id: string };
|
||||
export class Entry {
|
||||
|
||||
export class BaseEntry<T extends EntryData> {
|
||||
|
||||
private _data: T;
|
||||
private _accessToken: string;
|
||||
private _collectionName: string;
|
||||
private _entryId: string;
|
||||
private _data: EntryData;
|
||||
|
||||
constructor(private data: T, private collectionName: string) {
|
||||
this._data = data;
|
||||
constructor(accessToken: string, collectionName: string, data: EntryData) {
|
||||
this._accessToken = accessToken;
|
||||
this._collectionName = collectionName;
|
||||
this._entryId = data._id;
|
||||
this._data = data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -35,8 +35,36 @@ export class BaseEntry<T extends EntryData> {
|
||||
* @see {@link EntryData} for more information about entry data.
|
||||
*
|
||||
*/
|
||||
getValues(): WithDefaultId<T> {
|
||||
return this._data as WithDefaultId<T>;
|
||||
getValues(): EntryData {
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param key - The key of the value to get
|
||||
* @returns The value of the specified key
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* import { Client } from "marcsync";
|
||||
*
|
||||
* const client = new Client("<my access token>");
|
||||
* const collection = client.getCollection("my-collection");
|
||||
*
|
||||
* const entry = await collection.getEntryById("my-entry-id");
|
||||
*
|
||||
* const name = entry.getValueAs<string>("name");
|
||||
*
|
||||
* console.log(name);
|
||||
*
|
||||
* @remarks
|
||||
* This method is useful if you want to get the value of a specific key as a specific type.
|
||||
*
|
||||
* @see {@link EntryData} for more information about entry data.
|
||||
*
|
||||
*/
|
||||
getValueAs<T>(key: string): T {
|
||||
return this._data[key];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -63,33 +91,8 @@ export class BaseEntry<T extends EntryData> {
|
||||
* @see {@link EntryData} for more information about entry data.
|
||||
*
|
||||
*/
|
||||
getValue<K extends keyof WithDefaultId<T>>(key: K): WithDefaultId<T>[K] {
|
||||
return (this._data as WithDefaultId<T>)[key];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns The name of the collection of the entry
|
||||
*
|
||||
*/
|
||||
getCollectionName(): string {
|
||||
return this._collectionName;
|
||||
}
|
||||
|
||||
protected _setData(data: T) {
|
||||
this._data = data;
|
||||
}
|
||||
}
|
||||
|
||||
export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
|
||||
private _accessToken: string;
|
||||
private _entryId: string;
|
||||
|
||||
constructor(accessToken: string, collectionName: string, data: T) {
|
||||
super(data, collectionName);
|
||||
this._accessToken = accessToken;
|
||||
this._entryId = data._id!;
|
||||
getValue(key: string): any {
|
||||
return this._data[key];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,9 +118,9 @@ export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
* This method is useful if you want to update the value of a specific key.
|
||||
*
|
||||
*/
|
||||
async updateValue<K extends keyof WithDefaultId<T>>(key: K, value: WithDefaultId<T>[K]): Promise<WithDefaultId<T>> {
|
||||
async updateValue(key: string, value: any): Promise<EntryData> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this.getCollectionName()}`, {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
authorization: this._accessToken,
|
||||
@ -137,10 +140,8 @@ export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
} catch (err) {
|
||||
throw new EntryUpdateFailed(err);
|
||||
}
|
||||
const data = this.getValues();
|
||||
data[key] = value;
|
||||
this._setData(data);
|
||||
return data;
|
||||
this._data[key] = value;
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -169,9 +170,9 @@ export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
* @see {@link updateValue} for more information about updating a single value.
|
||||
*
|
||||
*/
|
||||
async updateValues(values: Partial<{ [K in keyof WithDefaultId<T>]: WithDefaultId<T>[K] }>): Promise<WithDefaultId<T>> {
|
||||
async updateValues(values: EntryData): Promise<EntryData> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this.getCollectionName()}`, {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
authorization: this._accessToken,
|
||||
@ -189,12 +190,10 @@ export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
} catch (err) {
|
||||
throw new EntryUpdateFailed(err);
|
||||
}
|
||||
const data = this.getValues();
|
||||
for (const key in values) {
|
||||
data[key] = values[key]!;
|
||||
this._data[key] = values[key];
|
||||
}
|
||||
this._setData(data);
|
||||
return data;
|
||||
return this._data;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -204,7 +203,7 @@ export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
*/
|
||||
async delete(): Promise<void> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this.getCollectionName()}`, {
|
||||
const result = await fetch(`https://api.marcsync.dev/v1/entries/${this._collectionName}`, {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
authorization: this._accessToken,
|
||||
@ -225,7 +224,6 @@ export class Entry<T extends EntryData> extends BaseEntry<T> {
|
||||
}
|
||||
|
||||
export interface EntryData {
|
||||
_id?: string
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
|
@ -1,83 +0,0 @@
|
||||
import { BaseEntry, Entry, EntryData } from "./Entry";
|
||||
import { ClientEvents } from "./marcsync";
|
||||
import * as signalR from "@microsoft/signalr";
|
||||
|
||||
export class SubscriptionManager {
|
||||
|
||||
private _subscriptions: Record<keyof ClientEvents, ((...args: any) => void)[]>;
|
||||
private _hubConnection: signalR.HubConnection;
|
||||
private _accessToken: string;
|
||||
|
||||
|
||||
constructor(accessToken: string) {
|
||||
this._accessToken = accessToken;
|
||||
this._subscriptions = {
|
||||
entryCreated: [],
|
||||
entryDeleted: [],
|
||||
entryUpdated: []
|
||||
} as Record<keyof ClientEvents, ((...args: any) => void)[]>;
|
||||
this._hubConnection = new signalR.HubConnectionBuilder()
|
||||
.withUrl("https://ws.marcsync.dev/websocket?access_token=Bearer " + accessToken, {
|
||||
skipNegotiation: true,
|
||||
transport: signalR.HttpTransportType.WebSockets
|
||||
})
|
||||
.withAutomaticReconnect([0, 2000, 10000, 30000, 60000])
|
||||
.configureLogging(signalR.LogLevel.None)
|
||||
.build();
|
||||
this._hubConnection.start()
|
||||
.catch(err => {
|
||||
console.error(err.toString());
|
||||
process.exit(1);
|
||||
});
|
||||
this.handleSubscriptions();
|
||||
}
|
||||
|
||||
subscribe(subscription: keyof ClientEvents, callback: () => void) {
|
||||
if (!this._subscriptions[subscription]) this._subscriptions[subscription] = [];
|
||||
this._subscriptions[subscription].push(callback);
|
||||
}
|
||||
|
||||
private async handleSubscriptions() {
|
||||
this._hubConnection.on("entryCreated", (e: string) => {
|
||||
let d = JSON.parse(e) as EntryCreatedEvent;
|
||||
this._subscriptions.entryCreated.forEach(callback => { try { callback(new Entry(this._accessToken, d.data.collectionName, d.data.values), d.databaseId, d.timestamp) } catch(e) {console.error(e)} });
|
||||
});
|
||||
this._hubConnection.on("entryDeleted", (e: string) => {
|
||||
let d = JSON.parse(e) as EntryDeletedEvent;
|
||||
this._subscriptions.entryDeleted.forEach(callback => { try { callback(new BaseEntry(d.data.values, d.data.collectionName), d.databaseId, d.timestamp) } catch(e) {console.error(e)} });
|
||||
})
|
||||
this._hubConnection.on("entryUpdated", (e: string) => {
|
||||
let d = JSON.parse(e) as EntryUpdatedEvent;
|
||||
this._subscriptions.entryUpdated.forEach(callback => { try { callback(new BaseEntry(d.data.oldValues, d.data.collectionName), new Entry(this._accessToken, d.data.collectionName, d.data.newValues), d.databaseId, d.timestamp) } catch(e) {console.error(e)} });
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export interface BaseEvent {
|
||||
databaseId: string;
|
||||
timestamp: number;
|
||||
type: number;
|
||||
}
|
||||
|
||||
export interface EntryCreatedEvent extends BaseEvent {
|
||||
data: {
|
||||
collectionName: string;
|
||||
values: EntryData;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EntryDeletedEvent extends BaseEvent {
|
||||
data: {
|
||||
collectionName: string;
|
||||
values: EntryData;
|
||||
};
|
||||
}
|
||||
|
||||
export interface EntryUpdatedEvent extends BaseEvent {
|
||||
data: {
|
||||
collectionName: string;
|
||||
oldValues: EntryData;
|
||||
newValues: EntryData;
|
||||
};
|
||||
}
|
@ -1,11 +1,9 @@
|
||||
import { Collection, CollectionAlreadyExists, CollectionNotFound } from "./Collection";
|
||||
import { BaseEntry, Entry, EntryData } from "./Entry";
|
||||
import { SubscriptionManager } from "./SubscriptionManager";
|
||||
import { Entry, EntryData } from "./Entry";
|
||||
|
||||
export class Client {
|
||||
|
||||
private _accessToken: string;
|
||||
private _subscriptions: SubscriptionManager;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -14,7 +12,6 @@ export class Client {
|
||||
*
|
||||
*/
|
||||
constructor(accessToken: string) {
|
||||
this._subscriptions = new SubscriptionManager(accessToken);
|
||||
this._accessToken = accessToken;
|
||||
}
|
||||
|
||||
@ -31,8 +28,8 @@ export class Client {
|
||||
* const collection = client.getCollection("my-collection");
|
||||
*
|
||||
*/
|
||||
getCollection<T extends EntryData = EntryData>(collectionName: string): Collection<T> {
|
||||
return new Collection<T>(this._accessToken, collectionName);
|
||||
getCollection(collectionName: string) {
|
||||
return new Collection(this._accessToken, collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -51,7 +48,7 @@ export class Client {
|
||||
* This method is useful if you want to fetch the collection from the server to check if it exists before using it.
|
||||
*
|
||||
*/
|
||||
async fetchCollection<T extends EntryData = EntryData>(collectionName: string): Promise<Collection<T>> {
|
||||
async fetchCollection(collectionName: string): Promise<Collection> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v0/collection/${collectionName}`, {
|
||||
method: "GET",
|
||||
@ -66,7 +63,7 @@ export class Client {
|
||||
if (e instanceof Unauthorized) throw new Unauthorized();
|
||||
throw new CollectionNotFound();
|
||||
}
|
||||
return new Collection<T>(this._accessToken, collectionName);
|
||||
return new Collection(this._accessToken, collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +80,7 @@ export class Client {
|
||||
*
|
||||
* @remarks
|
||||
*/
|
||||
async createCollection<T extends EntryData = EntryData>(collectionName: string): Promise<Collection<T>> {
|
||||
async createCollection(collectionName: string): Promise<Collection> {
|
||||
try {
|
||||
const result = await fetch(`https://api.marcsync.dev/v0/collection/${collectionName}`, {
|
||||
method: "POST",
|
||||
@ -100,18 +97,6 @@ export class Client {
|
||||
}
|
||||
return new Collection(this._accessToken, collectionName);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param event - The event to listen to
|
||||
* @param listener - The listener to call when the event is emitted
|
||||
* @returns The client instance
|
||||
*
|
||||
*/
|
||||
public on<K extends keyof ClientEvents>(event: K, listener: (...args: ClientEvents[K]) => void): this {
|
||||
this._subscriptions.subscribe(event, listener);
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
export class Unauthorized extends Error {
|
||||
@ -119,9 +104,3 @@ export class Unauthorized extends Error {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export interface ClientEvents {
|
||||
entryCreated: [entry: Entry<EntryData>, databaseId: string, timestamp: number];
|
||||
entryUpdated: [oldEntry: BaseEntry<EntryData>, newEntry: Entry<EntryData>, databaseId: string, timestamp: number];
|
||||
entryDeleted: [entry: BaseEntry<EntryData>, databaseId: string, timestamp: number];
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user