@shadowob/sdk 0.3.4 → 0.4.1
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/dist/index.cjs +1195 -0
- package/dist/index.d.cts +1085 -0
- package/dist/index.d.ts +1085 -0
- package/dist/index.js +1152 -0
- package/package.json +25 -5
- package/__tests__/client.test.ts +0 -256
- package/__tests__/constants.test.ts +0 -38
- package/src/client.ts +0 -1570
- package/src/constants.ts +0 -15
- package/src/index.ts +0 -64
- package/src/socket.ts +0 -202
- package/src/types.ts +0 -438
- package/tsconfig.json +0 -8
package/package.json
CHANGED
|
@@ -1,18 +1,38 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shadowob/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Shadow SDK — typed REST client and real-time Socket.IO event listener for Shadow servers",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./
|
|
7
|
-
"
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"require": "./dist/index.cjs",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
8
10
|
"exports": {
|
|
9
|
-
".":
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"development": "./src/index.ts",
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"require": "./dist/index.cjs",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
}
|
|
10
18
|
},
|
|
19
|
+
"files": [
|
|
20
|
+
"dist"
|
|
21
|
+
],
|
|
11
22
|
"dependencies": {
|
|
12
23
|
"socket.io-client": "^4.8.1",
|
|
13
|
-
"@shadowob/shared": "0.
|
|
24
|
+
"@shadowob/shared": "0.4.1"
|
|
25
|
+
},
|
|
26
|
+
"devDependencies": {
|
|
27
|
+
"tsup": "^8.5.0",
|
|
28
|
+
"typescript": "^5.9.3"
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
14
32
|
},
|
|
15
33
|
"scripts": {
|
|
34
|
+
"build": "tsup",
|
|
35
|
+
"dev": "tsup --watch",
|
|
16
36
|
"test": "vitest run",
|
|
17
37
|
"test:watch": "vitest"
|
|
18
38
|
}
|
package/__tests__/client.test.ts
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
2
|
-
import { ShadowClient } from '../src/client'
|
|
3
|
-
|
|
4
|
-
describe('ShadowClient', () => {
|
|
5
|
-
let client: ShadowClient
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
client = new ShadowClient('https://api.example.com', 'test-token-123')
|
|
9
|
-
})
|
|
10
|
-
|
|
11
|
-
describe('constructor', () => {
|
|
12
|
-
it('should strip trailing /api from baseUrl', () => {
|
|
13
|
-
const c = new ShadowClient('https://api.example.com/api', 'token')
|
|
14
|
-
// Access internal state via a request that would expose the URL
|
|
15
|
-
expect(c).toBeDefined()
|
|
16
|
-
})
|
|
17
|
-
|
|
18
|
-
it('should strip trailing /api/ from baseUrl', () => {
|
|
19
|
-
const c = new ShadowClient('https://api.example.com/api/', 'token')
|
|
20
|
-
expect(c).toBeDefined()
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
it('should leave baseUrl without /api suffix unchanged', () => {
|
|
24
|
-
const c = new ShadowClient('https://api.example.com', 'token')
|
|
25
|
-
expect(c).toBeDefined()
|
|
26
|
-
})
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
describe('request error handling', () => {
|
|
30
|
-
beforeEach(() => {
|
|
31
|
-
vi.stubGlobal('fetch', vi.fn())
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
afterEach(() => {
|
|
35
|
-
vi.unstubAllGlobals()
|
|
36
|
-
})
|
|
37
|
-
|
|
38
|
-
it('should throw on non-ok response with status and body', async () => {
|
|
39
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
40
|
-
ok: false,
|
|
41
|
-
status: 401,
|
|
42
|
-
text: () => Promise.resolve('Unauthorized'),
|
|
43
|
-
})
|
|
44
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
45
|
-
|
|
46
|
-
await expect(client.getMe()).rejects.toThrow(
|
|
47
|
-
'Shadow API GET /api/auth/me failed (401): Unauthorized',
|
|
48
|
-
)
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
it('should include authorization header in requests', async () => {
|
|
52
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
53
|
-
ok: true,
|
|
54
|
-
json: () => Promise.resolve({ id: '1', username: 'test' }),
|
|
55
|
-
})
|
|
56
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
57
|
-
|
|
58
|
-
await client.getMe()
|
|
59
|
-
|
|
60
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
61
|
-
expect.any(String),
|
|
62
|
-
expect.objectContaining({
|
|
63
|
-
headers: expect.objectContaining({
|
|
64
|
-
Authorization: 'Bearer test-token-123',
|
|
65
|
-
}),
|
|
66
|
-
}),
|
|
67
|
-
)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('should set Content-Type to application/json', async () => {
|
|
71
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
72
|
-
ok: true,
|
|
73
|
-
json: () => Promise.resolve({ token: 'new', user: {} }),
|
|
74
|
-
})
|
|
75
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
76
|
-
|
|
77
|
-
await client.login({ email: 'a@b.com', password: '12345678' })
|
|
78
|
-
|
|
79
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
80
|
-
expect.any(String),
|
|
81
|
-
expect.objectContaining({
|
|
82
|
-
headers: expect.objectContaining({
|
|
83
|
-
'Content-Type': 'application/json',
|
|
84
|
-
}),
|
|
85
|
-
}),
|
|
86
|
-
)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('should handle fetch text() error gracefully', async () => {
|
|
90
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
91
|
-
ok: false,
|
|
92
|
-
status: 500,
|
|
93
|
-
text: () => Promise.reject(new Error('parse error')),
|
|
94
|
-
})
|
|
95
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
96
|
-
|
|
97
|
-
await expect(client.getMe()).rejects.toThrow('failed (500)')
|
|
98
|
-
})
|
|
99
|
-
})
|
|
100
|
-
|
|
101
|
-
describe('auth methods', () => {
|
|
102
|
-
beforeEach(() => {
|
|
103
|
-
vi.stubGlobal('fetch', vi.fn())
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
afterEach(() => {
|
|
107
|
-
vi.unstubAllGlobals()
|
|
108
|
-
})
|
|
109
|
-
|
|
110
|
-
it('should call register with correct path and body', async () => {
|
|
111
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
112
|
-
ok: true,
|
|
113
|
-
json: () => Promise.resolve({ token: 'jwt', user: { id: '1' } }),
|
|
114
|
-
})
|
|
115
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
116
|
-
|
|
117
|
-
const data = {
|
|
118
|
-
email: 'test@example.com',
|
|
119
|
-
password: 'password123',
|
|
120
|
-
username: 'testuser',
|
|
121
|
-
inviteCode: 'ABCD1234',
|
|
122
|
-
}
|
|
123
|
-
await client.register(data)
|
|
124
|
-
|
|
125
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
126
|
-
'https://api.example.com/api/auth/register',
|
|
127
|
-
expect.objectContaining({
|
|
128
|
-
method: 'POST',
|
|
129
|
-
body: JSON.stringify(data),
|
|
130
|
-
}),
|
|
131
|
-
)
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
it('should call login with correct path and body', async () => {
|
|
135
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
136
|
-
ok: true,
|
|
137
|
-
json: () => Promise.resolve({ token: 'jwt', user: { id: '1' } }),
|
|
138
|
-
})
|
|
139
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
140
|
-
|
|
141
|
-
await client.login({ email: 'test@example.com', password: 'password123' })
|
|
142
|
-
|
|
143
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
144
|
-
'https://api.example.com/api/auth/login',
|
|
145
|
-
expect.objectContaining({
|
|
146
|
-
method: 'POST',
|
|
147
|
-
}),
|
|
148
|
-
)
|
|
149
|
-
})
|
|
150
|
-
|
|
151
|
-
it('should call getMe with correct path', async () => {
|
|
152
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
153
|
-
ok: true,
|
|
154
|
-
json: () => Promise.resolve({ id: '1', username: 'test' }),
|
|
155
|
-
})
|
|
156
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
157
|
-
|
|
158
|
-
await client.getMe()
|
|
159
|
-
|
|
160
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
161
|
-
'https://api.example.com/api/auth/me',
|
|
162
|
-
expect.objectContaining({
|
|
163
|
-
headers: expect.objectContaining({
|
|
164
|
-
Authorization: 'Bearer test-token-123',
|
|
165
|
-
}),
|
|
166
|
-
}),
|
|
167
|
-
)
|
|
168
|
-
})
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
describe('server methods', () => {
|
|
172
|
-
beforeEach(() => {
|
|
173
|
-
vi.stubGlobal('fetch', vi.fn())
|
|
174
|
-
})
|
|
175
|
-
|
|
176
|
-
afterEach(() => {
|
|
177
|
-
vi.unstubAllGlobals()
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('should call listServers with GET', async () => {
|
|
181
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
182
|
-
ok: true,
|
|
183
|
-
json: () => Promise.resolve([]),
|
|
184
|
-
})
|
|
185
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
186
|
-
|
|
187
|
-
await client.listServers()
|
|
188
|
-
|
|
189
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
190
|
-
'https://api.example.com/api/servers',
|
|
191
|
-
expect.any(Object),
|
|
192
|
-
)
|
|
193
|
-
})
|
|
194
|
-
|
|
195
|
-
it('should call createServer with POST and body', async () => {
|
|
196
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
197
|
-
ok: true,
|
|
198
|
-
json: () => Promise.resolve({ id: 's1', name: 'Test' }),
|
|
199
|
-
})
|
|
200
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
201
|
-
|
|
202
|
-
await client.createServer({ name: 'Test' })
|
|
203
|
-
|
|
204
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
205
|
-
'https://api.example.com/api/servers',
|
|
206
|
-
expect.objectContaining({
|
|
207
|
-
method: 'POST',
|
|
208
|
-
body: JSON.stringify({ name: 'Test' }),
|
|
209
|
-
}),
|
|
210
|
-
)
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
describe('message methods', () => {
|
|
215
|
-
beforeEach(() => {
|
|
216
|
-
vi.stubGlobal('fetch', vi.fn())
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
afterEach(() => {
|
|
220
|
-
vi.unstubAllGlobals()
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
it('should call sendMessage with correct path', async () => {
|
|
224
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
225
|
-
ok: true,
|
|
226
|
-
json: () => Promise.resolve({ id: 'm1' }),
|
|
227
|
-
})
|
|
228
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
229
|
-
|
|
230
|
-
await client.sendMessage('ch1', 'Hello')
|
|
231
|
-
|
|
232
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
233
|
-
'https://api.example.com/api/channels/ch1/messages',
|
|
234
|
-
expect.objectContaining({
|
|
235
|
-
method: 'POST',
|
|
236
|
-
body: JSON.stringify({ content: 'Hello' }),
|
|
237
|
-
}),
|
|
238
|
-
)
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
it('should call getMessages with channel ID', async () => {
|
|
242
|
-
const mockFetch = vi.fn().mockResolvedValue({
|
|
243
|
-
ok: true,
|
|
244
|
-
json: () => Promise.resolve([]),
|
|
245
|
-
})
|
|
246
|
-
vi.stubGlobal('fetch', mockFetch)
|
|
247
|
-
|
|
248
|
-
await client.getMessages('ch1')
|
|
249
|
-
|
|
250
|
-
expect(mockFetch).toHaveBeenCalledWith(
|
|
251
|
-
expect.stringContaining('/api/channels/ch1/messages'),
|
|
252
|
-
expect.any(Object),
|
|
253
|
-
)
|
|
254
|
-
})
|
|
255
|
-
})
|
|
256
|
-
})
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { CLIENT_EVENTS, channelRoom, SERVER_EVENTS, threadRoom, userRoom } from '../src/constants'
|
|
3
|
-
|
|
4
|
-
describe('room helpers', () => {
|
|
5
|
-
describe('channelRoom', () => {
|
|
6
|
-
it('should build channel room name', () => {
|
|
7
|
-
expect(channelRoom('abc123')).toBe('channel:abc123')
|
|
8
|
-
})
|
|
9
|
-
|
|
10
|
-
it('should handle empty string', () => {
|
|
11
|
-
expect(channelRoom('')).toBe('channel:')
|
|
12
|
-
})
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
describe('threadRoom', () => {
|
|
16
|
-
it('should build thread room name', () => {
|
|
17
|
-
expect(threadRoom('t456')).toBe('thread:t456')
|
|
18
|
-
})
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
describe('userRoom', () => {
|
|
22
|
-
it('should build user room name', () => {
|
|
23
|
-
expect(userRoom('u789')).toBe('user:u789')
|
|
24
|
-
})
|
|
25
|
-
})
|
|
26
|
-
})
|
|
27
|
-
|
|
28
|
-
describe('re-exported constants', () => {
|
|
29
|
-
it('should re-export CLIENT_EVENTS from shared', () => {
|
|
30
|
-
expect(CLIENT_EVENTS).toBeDefined()
|
|
31
|
-
expect(CLIENT_EVENTS.CHANNEL_JOIN).toBe('channel:join')
|
|
32
|
-
})
|
|
33
|
-
|
|
34
|
-
it('should re-export SERVER_EVENTS from shared', () => {
|
|
35
|
-
expect(SERVER_EVENTS).toBeDefined()
|
|
36
|
-
expect(SERVER_EVENTS.MESSAGE_NEW).toBe('message:new')
|
|
37
|
-
})
|
|
38
|
-
})
|