alsabase 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +272 -0
- package/dist/Client.d.ts +60 -0
- package/dist/Client.js +220 -0
- package/dist/ClientResponseError.d.ts +25 -0
- package/dist/ClientResponseError.js +46 -0
- package/dist/index.cjs +1428 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +1405 -0
- package/dist/services/BaseService.d.ts +7 -0
- package/dist/services/BaseService.js +9 -0
- package/dist/services/CollectionService.d.ts +42 -0
- package/dist/services/CollectionService.js +94 -0
- package/dist/services/FileService.d.ts +71 -0
- package/dist/services/FileService.js +119 -0
- package/dist/services/HooksService.d.ts +76 -0
- package/dist/services/HooksService.js +135 -0
- package/dist/services/LogService.d.ts +32 -0
- package/dist/services/LogService.js +63 -0
- package/dist/services/RealtimeService.d.ts +28 -0
- package/dist/services/RealtimeService.js +186 -0
- package/dist/services/RecordService.d.ts +105 -0
- package/dist/services/RecordService.js +287 -0
- package/dist/services/SuperuserService.d.ts +35 -0
- package/dist/services/SuperuserService.js +80 -0
- package/dist/stores/AsyncAuthStore.d.ts +13 -0
- package/dist/stores/AsyncAuthStore.js +32 -0
- package/dist/stores/BaseAuthStore.d.ts +18 -0
- package/dist/stores/BaseAuthStore.js +81 -0
- package/dist/stores/LocalAuthStore.d.ts +8 -0
- package/dist/stores/LocalAuthStore.js +43 -0
- package/dist/types.d.ts +241 -0
- package/dist/types.js +1 -0
- package/package.json +49 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseAuthStore, AuthModelType } from "./BaseAuthStore";
|
|
2
|
+
export interface AsyncAuthStoreOptions {
|
|
3
|
+
save?: (serialized: string) => Promise<void>;
|
|
4
|
+
clear?: () => Promise<void>;
|
|
5
|
+
initial?: string;
|
|
6
|
+
}
|
|
7
|
+
export declare class AsyncAuthStore extends BaseAuthStore {
|
|
8
|
+
private _saveHandler?;
|
|
9
|
+
private _clearHandler?;
|
|
10
|
+
constructor(options?: AsyncAuthStoreOptions);
|
|
11
|
+
save(token: string, model: AuthModelType): void;
|
|
12
|
+
clear(): void;
|
|
13
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { BaseAuthStore } from "./BaseAuthStore";
|
|
2
|
+
export class AsyncAuthStore extends BaseAuthStore {
|
|
3
|
+
_saveHandler;
|
|
4
|
+
_clearHandler;
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super();
|
|
7
|
+
this._saveHandler = options.save;
|
|
8
|
+
this._clearHandler = options.clear;
|
|
9
|
+
if (options.initial) {
|
|
10
|
+
try {
|
|
11
|
+
const parsed = JSON.parse(options.initial);
|
|
12
|
+
if (parsed && typeof parsed === "object") {
|
|
13
|
+
this._token = parsed.token || "";
|
|
14
|
+
this._model = parsed.model || null;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
catch { }
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
save(token, model) {
|
|
21
|
+
super.save(token, model);
|
|
22
|
+
if (this._saveHandler) {
|
|
23
|
+
this._saveHandler(JSON.stringify({ token: this._token, model: this._model })).catch((err) => console.error("AsyncAuthStore save failed:", err));
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
clear() {
|
|
27
|
+
super.clear();
|
|
28
|
+
if (this._clearHandler) {
|
|
29
|
+
this._clearHandler().catch((err) => console.error("AsyncAuthStore clear failed:", err));
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { RecordModel, SuperuserModel } from "../types";
|
|
2
|
+
export type AuthModelType = RecordModel | SuperuserModel | null;
|
|
3
|
+
export type OnStoreChangeFunc = (token: string, model: AuthModelType) => void;
|
|
4
|
+
export declare class BaseAuthStore {
|
|
5
|
+
protected _token: string;
|
|
6
|
+
protected _model: AuthModelType;
|
|
7
|
+
private _listeners;
|
|
8
|
+
get token(): string;
|
|
9
|
+
get model(): AuthModelType;
|
|
10
|
+
get isValid(): boolean;
|
|
11
|
+
get isSuperuser(): boolean;
|
|
12
|
+
get isAdmin(): boolean;
|
|
13
|
+
save(token: string, model: AuthModelType): void;
|
|
14
|
+
clear(): void;
|
|
15
|
+
onChange(callback: OnStoreChangeFunc): () => void;
|
|
16
|
+
protected triggerChange(): void;
|
|
17
|
+
parseJwt(token: string): any;
|
|
18
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
export class BaseAuthStore {
|
|
2
|
+
_token = "";
|
|
3
|
+
_model = null;
|
|
4
|
+
_listeners = new Set();
|
|
5
|
+
get token() {
|
|
6
|
+
return this._token;
|
|
7
|
+
}
|
|
8
|
+
get model() {
|
|
9
|
+
return this._model;
|
|
10
|
+
}
|
|
11
|
+
get isValid() {
|
|
12
|
+
if (!this.token)
|
|
13
|
+
return false;
|
|
14
|
+
const jwt = this.parseJwt(this.token);
|
|
15
|
+
if (!jwt || !jwt.exp)
|
|
16
|
+
return true; // without exp header, treat as valid if present
|
|
17
|
+
const now = Math.floor(Date.now() / 1000);
|
|
18
|
+
return jwt.exp > now;
|
|
19
|
+
}
|
|
20
|
+
get isSuperuser() {
|
|
21
|
+
if (!this.model)
|
|
22
|
+
return false;
|
|
23
|
+
return !!this.model.email && this.model.collectionName === undefined;
|
|
24
|
+
}
|
|
25
|
+
get isAdmin() {
|
|
26
|
+
return this.isSuperuser;
|
|
27
|
+
}
|
|
28
|
+
save(token, model) {
|
|
29
|
+
this._token = token || "";
|
|
30
|
+
this._model = model || null;
|
|
31
|
+
this.triggerChange();
|
|
32
|
+
}
|
|
33
|
+
clear() {
|
|
34
|
+
this._token = "";
|
|
35
|
+
this._model = null;
|
|
36
|
+
this.triggerChange();
|
|
37
|
+
}
|
|
38
|
+
onChange(callback) {
|
|
39
|
+
this._listeners.add(callback);
|
|
40
|
+
return () => {
|
|
41
|
+
this._listeners.delete(callback);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
triggerChange() {
|
|
45
|
+
for (const listener of this._listeners) {
|
|
46
|
+
try {
|
|
47
|
+
listener(this._token, this._model);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error("AuthStore change listener error:", err);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
parseJwt(token) {
|
|
55
|
+
if (!token)
|
|
56
|
+
return null;
|
|
57
|
+
try {
|
|
58
|
+
const base64Url = token.split(".")[1];
|
|
59
|
+
if (!base64Url)
|
|
60
|
+
return null;
|
|
61
|
+
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
|
|
62
|
+
let jsonPayload;
|
|
63
|
+
if (typeof atob === "function") {
|
|
64
|
+
jsonPayload = decodeURIComponent(atob(base64)
|
|
65
|
+
.split("")
|
|
66
|
+
.map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
|
|
67
|
+
.join(""));
|
|
68
|
+
}
|
|
69
|
+
else if (typeof globalThis.Buffer !== "undefined") {
|
|
70
|
+
jsonPayload = globalThis.Buffer.from(base64, "base64").toString("utf8");
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
return JSON.parse(jsonPayload);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { BaseAuthStore, AuthModelType } from "./BaseAuthStore";
|
|
2
|
+
export declare class LocalAuthStore extends BaseAuthStore {
|
|
3
|
+
storageKey: string;
|
|
4
|
+
constructor(storageKey?: string);
|
|
5
|
+
private loadInitial;
|
|
6
|
+
save(token: string, model: AuthModelType): void;
|
|
7
|
+
clear(): void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BaseAuthStore } from "./BaseAuthStore";
|
|
2
|
+
const DEFAULT_STORAGE_KEY = "alsabase_auth";
|
|
3
|
+
export class LocalAuthStore extends BaseAuthStore {
|
|
4
|
+
storageKey;
|
|
5
|
+
constructor(storageKey = DEFAULT_STORAGE_KEY) {
|
|
6
|
+
super();
|
|
7
|
+
this.storageKey = storageKey;
|
|
8
|
+
this.loadInitial();
|
|
9
|
+
}
|
|
10
|
+
loadInitial() {
|
|
11
|
+
if (typeof window === "undefined" || !window.localStorage)
|
|
12
|
+
return;
|
|
13
|
+
try {
|
|
14
|
+
const raw = window.localStorage.getItem(this.storageKey);
|
|
15
|
+
if (!raw)
|
|
16
|
+
return;
|
|
17
|
+
const parsed = JSON.parse(raw);
|
|
18
|
+
if (parsed && typeof parsed === "object") {
|
|
19
|
+
this._token = parsed.token || "";
|
|
20
|
+
this._model = parsed.model || null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
catch { }
|
|
24
|
+
}
|
|
25
|
+
save(token, model) {
|
|
26
|
+
super.save(token, model);
|
|
27
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
28
|
+
try {
|
|
29
|
+
window.localStorage.setItem(this.storageKey, JSON.stringify({ token: this._token, model: this._model }));
|
|
30
|
+
}
|
|
31
|
+
catch { }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
clear() {
|
|
35
|
+
super.clear();
|
|
36
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
37
|
+
try {
|
|
38
|
+
window.localStorage.removeItem(this.storageKey);
|
|
39
|
+
}
|
|
40
|
+
catch { }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
export interface BaseModel {
|
|
2
|
+
id: string;
|
|
3
|
+
created?: string;
|
|
4
|
+
updated?: string;
|
|
5
|
+
[key: string]: any;
|
|
6
|
+
}
|
|
7
|
+
export interface RecordModel extends BaseModel {
|
|
8
|
+
id: string;
|
|
9
|
+
collectionId?: string;
|
|
10
|
+
collectionName?: string;
|
|
11
|
+
created?: string;
|
|
12
|
+
updated?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AuthModel extends RecordModel {
|
|
15
|
+
email?: string;
|
|
16
|
+
username?: string;
|
|
17
|
+
verified?: boolean;
|
|
18
|
+
emailVisibility?: boolean;
|
|
19
|
+
}
|
|
20
|
+
export interface SuperuserModel extends BaseModel {
|
|
21
|
+
id: string;
|
|
22
|
+
email: string;
|
|
23
|
+
role?: string;
|
|
24
|
+
created?: string;
|
|
25
|
+
updated?: string;
|
|
26
|
+
}
|
|
27
|
+
export type AdminModel = SuperuserModel;
|
|
28
|
+
export interface AuthResponse<T = RecordModel> {
|
|
29
|
+
token: string;
|
|
30
|
+
record: T;
|
|
31
|
+
meta?: any;
|
|
32
|
+
}
|
|
33
|
+
export interface SuperuserAuthResponse {
|
|
34
|
+
token: string;
|
|
35
|
+
user: SuperuserModel;
|
|
36
|
+
}
|
|
37
|
+
export type AdminAuthResponse = SuperuserAuthResponse;
|
|
38
|
+
export interface ListResult<T> {
|
|
39
|
+
page: number;
|
|
40
|
+
perPage: number;
|
|
41
|
+
totalItems: number;
|
|
42
|
+
totalPages: number;
|
|
43
|
+
items: T[];
|
|
44
|
+
}
|
|
45
|
+
export interface SendOptions extends RequestInit {
|
|
46
|
+
query?: Record<string, any>;
|
|
47
|
+
params?: Record<string, any>;
|
|
48
|
+
headers?: Record<string, string>;
|
|
49
|
+
body?: any;
|
|
50
|
+
requestKey?: string | null;
|
|
51
|
+
autoCancel?: boolean;
|
|
52
|
+
}
|
|
53
|
+
export interface CommonOptions {
|
|
54
|
+
fields?: string;
|
|
55
|
+
expand?: string;
|
|
56
|
+
filter?: string;
|
|
57
|
+
sort?: string;
|
|
58
|
+
requestKey?: string | null;
|
|
59
|
+
[key: string]: any;
|
|
60
|
+
}
|
|
61
|
+
export interface ListOptions extends CommonOptions {
|
|
62
|
+
page?: number;
|
|
63
|
+
perPage?: number;
|
|
64
|
+
skipTotal?: boolean;
|
|
65
|
+
}
|
|
66
|
+
export interface RecordOptions extends CommonOptions {
|
|
67
|
+
}
|
|
68
|
+
export interface RecordListOptions extends ListOptions {
|
|
69
|
+
}
|
|
70
|
+
export interface FullListOptions extends CommonOptions {
|
|
71
|
+
batch?: number;
|
|
72
|
+
}
|
|
73
|
+
export interface FieldDef {
|
|
74
|
+
name: string;
|
|
75
|
+
type: "text" | "number" | "bool" | "email" | "url" | "date" | "autodate" | "select" | "json" | "file" | "relation";
|
|
76
|
+
required?: boolean;
|
|
77
|
+
unique?: boolean;
|
|
78
|
+
presentable?: boolean;
|
|
79
|
+
hidden?: boolean;
|
|
80
|
+
helpText?: string;
|
|
81
|
+
defaultValue?: any;
|
|
82
|
+
min?: number;
|
|
83
|
+
max?: number;
|
|
84
|
+
noDecimal?: boolean;
|
|
85
|
+
pattern?: string;
|
|
86
|
+
autogeneratePattern?: string;
|
|
87
|
+
maxSize?: number;
|
|
88
|
+
maxSelect?: number;
|
|
89
|
+
mimeTypes?: string[];
|
|
90
|
+
thumbs?: string[];
|
|
91
|
+
protected?: boolean;
|
|
92
|
+
values?: string[];
|
|
93
|
+
relationCollection?: string;
|
|
94
|
+
onCreate?: boolean;
|
|
95
|
+
onUpdate?: boolean;
|
|
96
|
+
options?: Record<string, any>;
|
|
97
|
+
}
|
|
98
|
+
export interface CollectionRule {
|
|
99
|
+
list?: string | null;
|
|
100
|
+
view?: string | null;
|
|
101
|
+
create?: string | null;
|
|
102
|
+
update?: string | null;
|
|
103
|
+
delete?: string | null;
|
|
104
|
+
listRule?: string | null;
|
|
105
|
+
viewRule?: string | null;
|
|
106
|
+
createRule?: string | null;
|
|
107
|
+
updateRule?: string | null;
|
|
108
|
+
deleteRule?: string | null;
|
|
109
|
+
authWithPassword?: string;
|
|
110
|
+
authRule?: string;
|
|
111
|
+
manageRule?: string;
|
|
112
|
+
}
|
|
113
|
+
export interface CollectionModel {
|
|
114
|
+
id: string;
|
|
115
|
+
name: string;
|
|
116
|
+
type: "base" | "auth" | "view";
|
|
117
|
+
fields?: FieldDef[];
|
|
118
|
+
schema?: FieldDef[];
|
|
119
|
+
rules?: CollectionRule;
|
|
120
|
+
indexes?: string[];
|
|
121
|
+
options?: Record<string, any>;
|
|
122
|
+
created?: string;
|
|
123
|
+
updated?: string;
|
|
124
|
+
created_at?: string;
|
|
125
|
+
updated_at?: string;
|
|
126
|
+
}
|
|
127
|
+
export interface LogModel {
|
|
128
|
+
id: string;
|
|
129
|
+
timestamp: string;
|
|
130
|
+
level: "INFO" | "WARN" | "ERROR";
|
|
131
|
+
method?: string;
|
|
132
|
+
path?: string;
|
|
133
|
+
status?: number;
|
|
134
|
+
duration_ms?: number;
|
|
135
|
+
error_message?: string;
|
|
136
|
+
stack_trace?: string;
|
|
137
|
+
metadata_json?: string;
|
|
138
|
+
}
|
|
139
|
+
export interface LogListOptions {
|
|
140
|
+
page?: number;
|
|
141
|
+
perPage?: number;
|
|
142
|
+
level?: string;
|
|
143
|
+
search?: string;
|
|
144
|
+
includeSuperusers?: boolean;
|
|
145
|
+
requestKey?: string | null;
|
|
146
|
+
}
|
|
147
|
+
export interface LogTimelineItem {
|
|
148
|
+
time_bucket: string;
|
|
149
|
+
total: number;
|
|
150
|
+
errors: number;
|
|
151
|
+
}
|
|
152
|
+
export interface LogStats {
|
|
153
|
+
total: number;
|
|
154
|
+
error: number;
|
|
155
|
+
warn: number;
|
|
156
|
+
info: number;
|
|
157
|
+
avgDurationMs: number;
|
|
158
|
+
}
|
|
159
|
+
export interface RealtimeRecordEvent<T = any> {
|
|
160
|
+
action: "create" | "update" | "delete";
|
|
161
|
+
collection: string;
|
|
162
|
+
record: T;
|
|
163
|
+
timestamp: string;
|
|
164
|
+
}
|
|
165
|
+
export interface RealtimeCustomEvent<T = any> {
|
|
166
|
+
topic: string;
|
|
167
|
+
data: T;
|
|
168
|
+
event?: string;
|
|
169
|
+
timestamp: string;
|
|
170
|
+
}
|
|
171
|
+
export type RealtimeListener<T = any> = (event: RealtimeRecordEvent<T> | RealtimeCustomEvent<T> | any) => void;
|
|
172
|
+
export type UnsubscribeFunc = () => void;
|
|
173
|
+
export interface HookRouteDef {
|
|
174
|
+
method: string;
|
|
175
|
+
path: string;
|
|
176
|
+
authLevel: string;
|
|
177
|
+
sourceFile: string;
|
|
178
|
+
}
|
|
179
|
+
export interface HookCronDef {
|
|
180
|
+
name: string;
|
|
181
|
+
schedule: string;
|
|
182
|
+
sourceFile: string;
|
|
183
|
+
active: boolean;
|
|
184
|
+
last_run_at?: string | null;
|
|
185
|
+
last_status?: string | null;
|
|
186
|
+
last_duration_ms?: number | null;
|
|
187
|
+
}
|
|
188
|
+
export interface HookCommandDef {
|
|
189
|
+
name: string;
|
|
190
|
+
description?: string;
|
|
191
|
+
usage?: string;
|
|
192
|
+
sourceFile?: string;
|
|
193
|
+
type?: string;
|
|
194
|
+
}
|
|
195
|
+
export interface HookFileItem {
|
|
196
|
+
name: string;
|
|
197
|
+
filename?: string;
|
|
198
|
+
sizeBytes: number;
|
|
199
|
+
updatedAt: string;
|
|
200
|
+
error?: string | null;
|
|
201
|
+
isFolder?: boolean;
|
|
202
|
+
}
|
|
203
|
+
export interface HooksOverview {
|
|
204
|
+
hooksDir: string;
|
|
205
|
+
files: HookFileItem[];
|
|
206
|
+
routes: HookRouteDef[];
|
|
207
|
+
crons: HookCronDef[];
|
|
208
|
+
commands: HookCommandDef[];
|
|
209
|
+
totalFiles: number;
|
|
210
|
+
totalRoutes: number;
|
|
211
|
+
totalCrons: number;
|
|
212
|
+
totalCommands: number;
|
|
213
|
+
}
|
|
214
|
+
export interface FileDetailResponse {
|
|
215
|
+
name: string;
|
|
216
|
+
content: string;
|
|
217
|
+
sizeBytes: number;
|
|
218
|
+
updatedAt: string;
|
|
219
|
+
}
|
|
220
|
+
export interface TreeNodeItem {
|
|
221
|
+
name: string;
|
|
222
|
+
fullPath: string;
|
|
223
|
+
isFolder: boolean;
|
|
224
|
+
sizeBytes?: number;
|
|
225
|
+
updatedAt?: string;
|
|
226
|
+
}
|
|
227
|
+
export interface TreeResponse {
|
|
228
|
+
items: TreeNodeItem[];
|
|
229
|
+
dir: string;
|
|
230
|
+
total: number;
|
|
231
|
+
}
|
|
232
|
+
export interface BatchUploadFileItem {
|
|
233
|
+
path: string;
|
|
234
|
+
content: string;
|
|
235
|
+
isBase64?: boolean;
|
|
236
|
+
}
|
|
237
|
+
export interface BatchUploadResponse {
|
|
238
|
+
success: boolean;
|
|
239
|
+
count: number;
|
|
240
|
+
saved: string[];
|
|
241
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "alsabase",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Official JavaScript/TypeScript SDK for AlsaBase - lightweight, realtime, batteries-included backend with SQLite",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"import": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"require": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.cjs"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"README.md",
|
|
23
|
+
"LICENSE"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsc && node ./build.mjs",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "tsx ./test/sdk.test.ts"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"alsabase",
|
|
32
|
+
"sdk",
|
|
33
|
+
"client",
|
|
34
|
+
"database",
|
|
35
|
+
"sqlite",
|
|
36
|
+
"realtime",
|
|
37
|
+
"auth",
|
|
38
|
+
"typescript"
|
|
39
|
+
],
|
|
40
|
+
"author": "AlsaBase",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"socket.io-client": "^4.8.3"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@types/node": "^20.11.0",
|
|
47
|
+
"typescript": "^5.3.3"
|
|
48
|
+
}
|
|
49
|
+
}
|