@tapstack/db 1.0.6 → 3.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 +655 -0
- package/dist/adapters/index.d.ts +6 -0
- package/dist/adapters/index.js +22 -0
- package/dist/adapters/nodejs.adapter.d.ts +63 -0
- package/dist/adapters/nodejs.adapter.js +204 -0
- package/dist/adapters/types.d.ts +77 -0
- package/dist/adapters/types.js +19 -0
- package/dist/index.d.ts +101 -21
- package/dist/index.js +114 -41
- package/dist/modules/automations.d.ts +109 -0
- package/dist/modules/automations.js +59 -0
- package/dist/modules/conversations.d.ts +82 -0
- package/dist/modules/conversations.js +54 -0
- package/dist/modules/fields.d.ts +30 -9
- package/dist/modules/fields.js +31 -13
- package/dist/modules/files.d.ts +68 -0
- package/dist/modules/files.js +115 -0
- package/dist/modules/index.d.ts +12 -0
- package/dist/modules/index.js +28 -0
- package/dist/modules/objects.d.ts +30 -9
- package/dist/modules/objects.js +35 -13
- package/dist/modules/organizations.d.ts +69 -0
- package/dist/modules/organizations.js +83 -0
- package/dist/modules/records.d.ts +47 -5
- package/dist/modules/records.js +70 -5
- package/dist/modules/workspaces.d.ts +44 -0
- package/dist/modules/workspaces.js +57 -0
- package/dist/types.d.ts +159 -10
- package/dist/types.js +19 -0
- package/package.json +16 -7
- package/src/__tests__/client.test.ts +305 -49
- package/src/adapters/index.ts +13 -0
- package/src/adapters/nodejs.adapter.ts +298 -0
- package/src/adapters/types.ts +108 -0
- package/src/index.ts +132 -44
- package/src/modules/automations.ts +157 -0
- package/src/modules/conversations.ts +134 -0
- package/src/modules/fields.ts +64 -14
- package/src/modules/files.ts +144 -0
- package/src/modules/index.ts +19 -0
- package/src/modules/objects.ts +46 -14
- package/src/modules/organizations.ts +137 -0
- package/src/modules/records.ts +119 -6
- package/src/modules/workspaces.ts +95 -0
- package/src/types.ts +229 -9
- package/dist/request.d.ts +0 -2
- package/dist/request.js +0 -20
- package/src/request.ts +0 -14
|
@@ -1,84 +1,340 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
TapstackDBClient,
|
|
3
|
+
IInstanceAdapter,
|
|
4
|
+
createClient,
|
|
5
|
+
createCustomClient,
|
|
6
|
+
} from '../index';
|
|
3
7
|
|
|
4
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Mock adapter for testing
|
|
10
|
+
*/
|
|
11
|
+
class MockAdapter implements IInstanceAdapter {
|
|
12
|
+
public requests: Array<{
|
|
13
|
+
method: string;
|
|
14
|
+
path: string;
|
|
15
|
+
options?: { body?: unknown; params?: Record<string, string> };
|
|
16
|
+
}> = [];
|
|
17
|
+
public uploads: Array<{ path: string; file: File | Blob; metadata?: Record<string, string> }> = [];
|
|
18
|
+
private workspaceId: string | null = null;
|
|
19
|
+
public mockResponse: unknown = {};
|
|
5
20
|
|
|
6
|
-
|
|
21
|
+
async request<T>(
|
|
22
|
+
method: string,
|
|
23
|
+
path: string,
|
|
24
|
+
options?: { body?: unknown; params?: Record<string, string> }
|
|
25
|
+
): Promise<T> {
|
|
26
|
+
this.requests.push({ method, path, options });
|
|
27
|
+
return this.mockResponse as T;
|
|
28
|
+
}
|
|
7
29
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
30
|
+
async upload<T>(
|
|
31
|
+
path: string,
|
|
32
|
+
file: File | Blob,
|
|
33
|
+
metadata?: Record<string, string>
|
|
34
|
+
): Promise<T> {
|
|
35
|
+
this.uploads.push({ path, file, metadata });
|
|
36
|
+
return this.mockResponse as T;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setWorkspaceId(id: string | null): void {
|
|
40
|
+
this.workspaceId = id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getWorkspaceId(): string | null {
|
|
44
|
+
return this.workspaceId;
|
|
45
|
+
}
|
|
15
46
|
|
|
47
|
+
clearRequests(): void {
|
|
48
|
+
this.requests = [];
|
|
49
|
+
this.uploads = [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
describe('TapstackDBClient', () => {
|
|
54
|
+
let adapter: MockAdapter;
|
|
16
55
|
let client: TapstackDBClient;
|
|
17
56
|
|
|
18
57
|
beforeEach(() => {
|
|
19
|
-
|
|
20
|
-
client = new TapstackDBClient(
|
|
58
|
+
adapter = new MockAdapter();
|
|
59
|
+
client = new TapstackDBClient(adapter);
|
|
21
60
|
});
|
|
22
61
|
|
|
23
62
|
afterEach(() => {
|
|
24
|
-
|
|
63
|
+
adapter.clearRequests();
|
|
25
64
|
});
|
|
26
65
|
|
|
66
|
+
describe('Objects Module', () => {
|
|
67
|
+
it('list() calls GET schema/objects', async () => {
|
|
68
|
+
adapter.mockResponse = { objects: [] };
|
|
69
|
+
await client.objects.list();
|
|
70
|
+
expect(adapter.requests).toHaveLength(1);
|
|
71
|
+
expect(adapter.requests[0]).toEqual({
|
|
72
|
+
method: 'GET',
|
|
73
|
+
path: 'schema/objects',
|
|
74
|
+
options: undefined,
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('get() calls GET schema/objects/:slug', async () => {
|
|
79
|
+
adapter.mockResponse = { id: '1', slug: 'contacts' };
|
|
80
|
+
await client.objects.get('contacts');
|
|
81
|
+
expect(adapter.requests[0]).toEqual({
|
|
82
|
+
method: 'GET',
|
|
83
|
+
path: 'schema/objects/contacts',
|
|
84
|
+
options: undefined,
|
|
85
|
+
});
|
|
86
|
+
});
|
|
27
87
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
88
|
+
it('create() calls POST schema/objects', async () => {
|
|
89
|
+
const payload = { singleName: 'Contact', pluralName: 'Contacts' };
|
|
90
|
+
adapter.mockResponse = { id: '1', ...payload };
|
|
91
|
+
await client.objects.create(payload);
|
|
92
|
+
expect(adapter.requests[0]).toEqual({
|
|
93
|
+
method: 'POST',
|
|
94
|
+
path: 'schema/objects',
|
|
95
|
+
options: { body: payload },
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('update() calls PATCH schema/objects/:slug', async () => {
|
|
100
|
+
const payload = { singleName: 'Updated Contact' };
|
|
101
|
+
await client.objects.update('contacts', payload);
|
|
102
|
+
expect(adapter.requests[0]).toEqual({
|
|
103
|
+
method: 'PATCH',
|
|
104
|
+
path: 'schema/objects/contacts',
|
|
105
|
+
options: { body: payload },
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('delete() calls DELETE schema/objects/:slug', async () => {
|
|
110
|
+
await client.objects.delete('contacts');
|
|
111
|
+
expect(adapter.requests[0]).toEqual({
|
|
112
|
+
method: 'DELETE',
|
|
113
|
+
path: 'schema/objects/contacts',
|
|
114
|
+
options: { params: { soft: 'true' } },
|
|
115
|
+
});
|
|
116
|
+
});
|
|
31
117
|
});
|
|
32
118
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
119
|
+
describe('Fields Module', () => {
|
|
120
|
+
it('list() calls GET schema/objects/:slug/fields', async () => {
|
|
121
|
+
adapter.mockResponse = { fields: [] };
|
|
122
|
+
await client.fields.list('contacts');
|
|
123
|
+
expect(adapter.requests[0]).toEqual({
|
|
124
|
+
method: 'GET',
|
|
125
|
+
path: 'schema/objects/contacts/fields',
|
|
126
|
+
options: undefined,
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it('create() calls POST schema/objects/:slug/fields', async () => {
|
|
131
|
+
const payload = { label: 'Email', value: 'email', type: 'email' as const };
|
|
132
|
+
await client.fields.create('contacts', payload);
|
|
133
|
+
expect(adapter.requests[0]).toEqual({
|
|
134
|
+
method: 'POST',
|
|
135
|
+
path: 'schema/objects/contacts/fields',
|
|
136
|
+
options: { body: payload },
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('update() calls PATCH schema/objects/:slug/fields/:fieldId', async () => {
|
|
141
|
+
const payload = { label: 'Updated Email' };
|
|
142
|
+
await client.fields.update('contacts', 'field-1', payload);
|
|
143
|
+
expect(adapter.requests[0]).toEqual({
|
|
144
|
+
method: 'PATCH',
|
|
145
|
+
path: 'schema/objects/contacts/fields/field-1',
|
|
146
|
+
options: { body: payload },
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('delete() calls DELETE schema/objects/:slug/fields/:fieldId', async () => {
|
|
151
|
+
await client.fields.delete('contacts', 'field-1');
|
|
152
|
+
expect(adapter.requests[0]).toEqual({
|
|
153
|
+
method: 'DELETE',
|
|
154
|
+
path: 'schema/objects/contacts/fields/field-1',
|
|
155
|
+
options: { params: { soft: 'true' } },
|
|
156
|
+
});
|
|
157
|
+
});
|
|
36
158
|
});
|
|
37
159
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
160
|
+
describe('Records Module', () => {
|
|
161
|
+
it('list() calls GET records/:slug', async () => {
|
|
162
|
+
adapter.mockResponse = { items: [], total: 0 };
|
|
163
|
+
await client.records.list('contacts');
|
|
164
|
+
expect(adapter.requests[0]).toEqual({
|
|
165
|
+
method: 'GET',
|
|
166
|
+
path: 'records/contacts',
|
|
167
|
+
options: { params: {} },
|
|
168
|
+
});
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('list() with options adds query params', async () => {
|
|
172
|
+
adapter.mockResponse = { items: [], total: 0 };
|
|
173
|
+
await client.records.list('contacts', {
|
|
174
|
+
page: 2,
|
|
175
|
+
pageSize: 10,
|
|
176
|
+
sortBy: 'createdAt',
|
|
177
|
+
sortOrder: 'desc',
|
|
178
|
+
});
|
|
179
|
+
expect(adapter.requests[0].options?.params).toEqual({
|
|
180
|
+
page: '2',
|
|
181
|
+
pageSize: '10',
|
|
182
|
+
sortBy: 'createdAt',
|
|
183
|
+
sortOrder: 'desc',
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('get() calls GET records/:slug/:id', async () => {
|
|
188
|
+
await client.records.get('contacts', 'rec-1');
|
|
189
|
+
expect(adapter.requests[0]).toEqual({
|
|
190
|
+
method: 'GET',
|
|
191
|
+
path: 'records/contacts/rec-1',
|
|
192
|
+
options: undefined,
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
it('create() calls POST records/:slug', async () => {
|
|
197
|
+
const payload = { data: { name: 'John' } };
|
|
198
|
+
await client.records.create('contacts', payload);
|
|
199
|
+
expect(adapter.requests[0]).toEqual({
|
|
200
|
+
method: 'POST',
|
|
201
|
+
path: 'records/contacts',
|
|
202
|
+
options: { body: payload },
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
it('update() calls PATCH records/:slug/:id', async () => {
|
|
207
|
+
const payload = { data: { name: 'Jane' } };
|
|
208
|
+
await client.records.update('contacts', 'rec-1', payload);
|
|
209
|
+
expect(adapter.requests[0]).toEqual({
|
|
210
|
+
method: 'PATCH',
|
|
211
|
+
path: 'records/contacts/rec-1',
|
|
212
|
+
options: { body: payload },
|
|
213
|
+
});
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('delete() calls DELETE records/:slug/:id', async () => {
|
|
217
|
+
await client.records.delete('contacts', 'rec-1');
|
|
218
|
+
expect(adapter.requests[0]).toEqual({
|
|
219
|
+
method: 'DELETE',
|
|
220
|
+
path: 'records/contacts/rec-1',
|
|
221
|
+
options: { params: { soft: 'true' } },
|
|
222
|
+
});
|
|
223
|
+
});
|
|
42
224
|
});
|
|
43
225
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
226
|
+
describe('Organizations Module', () => {
|
|
227
|
+
it('list() calls GET organizations', async () => {
|
|
228
|
+
adapter.mockResponse = { organizations: [] };
|
|
229
|
+
await client.organizations.list();
|
|
230
|
+
expect(adapter.requests[0]).toEqual({
|
|
231
|
+
method: 'GET',
|
|
232
|
+
path: 'organizations',
|
|
233
|
+
options: undefined,
|
|
234
|
+
});
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
it('create() calls POST organizations', async () => {
|
|
238
|
+
const payload = { name: 'Acme Corp' };
|
|
239
|
+
await client.organizations.create(payload);
|
|
240
|
+
expect(adapter.requests[0]).toEqual({
|
|
241
|
+
method: 'POST',
|
|
242
|
+
path: 'organizations',
|
|
243
|
+
options: { body: payload },
|
|
244
|
+
});
|
|
245
|
+
});
|
|
47
246
|
});
|
|
48
247
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
248
|
+
describe('Workspaces Module', () => {
|
|
249
|
+
it('list() calls GET workspaces', async () => {
|
|
250
|
+
adapter.mockResponse = { workspaces: [] };
|
|
251
|
+
await client.workspaces.list();
|
|
252
|
+
expect(adapter.requests[0]).toEqual({
|
|
253
|
+
method: 'GET',
|
|
254
|
+
path: 'workspaces',
|
|
255
|
+
options: undefined,
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
it('create() calls POST workspaces/org/:orgId', async () => {
|
|
260
|
+
const payload = { name: 'Production' };
|
|
261
|
+
await client.workspaces.create('org-1', payload);
|
|
262
|
+
expect(adapter.requests[0]).toEqual({
|
|
263
|
+
method: 'POST',
|
|
264
|
+
path: 'workspaces/org/org-1',
|
|
265
|
+
options: { body: payload },
|
|
266
|
+
});
|
|
267
|
+
});
|
|
52
268
|
});
|
|
53
269
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
270
|
+
describe('Files Module', () => {
|
|
271
|
+
it('list() calls GET files/list', async () => {
|
|
272
|
+
adapter.mockResponse = { files: [], total: 0 };
|
|
273
|
+
await client.files.list();
|
|
274
|
+
expect(adapter.requests[0]).toEqual({
|
|
275
|
+
method: 'GET',
|
|
276
|
+
path: 'files/list',
|
|
277
|
+
options: { params: {} },
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it('upload() uses adapter.upload', async () => {
|
|
282
|
+
const file = new Blob(['test'], { type: 'text/plain' });
|
|
283
|
+
await client.files.upload(file, { folderId: 'folder-1' });
|
|
284
|
+
expect(adapter.uploads).toHaveLength(1);
|
|
285
|
+
expect(adapter.uploads[0]).toEqual({
|
|
286
|
+
path: 'files/upload',
|
|
287
|
+
file,
|
|
288
|
+
metadata: { folderId: 'folder-1' },
|
|
289
|
+
});
|
|
290
|
+
});
|
|
57
291
|
});
|
|
58
292
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
293
|
+
describe('Workspace Context', () => {
|
|
294
|
+
it('setWorkspaceId updates adapter workspace', () => {
|
|
295
|
+
client.setWorkspaceId('ws-123');
|
|
296
|
+
expect(adapter.getWorkspaceId()).toBe('ws-123');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('getWorkspaceId returns current workspace', () => {
|
|
300
|
+
adapter.setWorkspaceId('ws-456');
|
|
301
|
+
expect(client.getWorkspaceId()).toBe('ws-456');
|
|
302
|
+
});
|
|
62
303
|
});
|
|
304
|
+
});
|
|
63
305
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
306
|
+
describe('Factory Functions', () => {
|
|
307
|
+
// Mock fetch for adapter tests
|
|
308
|
+
const mockFetch = jest.fn();
|
|
309
|
+
global.fetch = mockFetch;
|
|
310
|
+
|
|
311
|
+
beforeEach(() => {
|
|
312
|
+
mockFetch.mockReset();
|
|
313
|
+
mockFetch.mockResolvedValue({
|
|
314
|
+
status: 200,
|
|
315
|
+
json: () => Promise.resolve({ success: true, data: {} }),
|
|
316
|
+
});
|
|
67
317
|
});
|
|
68
318
|
|
|
69
|
-
it('
|
|
70
|
-
|
|
71
|
-
|
|
319
|
+
it('createClient creates a client with default API URL', () => {
|
|
320
|
+
const client = createClient({
|
|
321
|
+
apiKey: 'api-key',
|
|
322
|
+
});
|
|
323
|
+
expect(client).toBeInstanceOf(TapstackDBClient);
|
|
72
324
|
});
|
|
73
325
|
|
|
74
|
-
it('
|
|
75
|
-
|
|
76
|
-
|
|
326
|
+
it('createClient creates a client with custom base URL', () => {
|
|
327
|
+
const client = createClient({
|
|
328
|
+
baseUrl: 'https://api.example.com',
|
|
329
|
+
apiKey: 'api-key',
|
|
330
|
+
});
|
|
331
|
+
expect(client).toBeInstanceOf(TapstackDBClient);
|
|
77
332
|
});
|
|
78
333
|
|
|
79
|
-
it('
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
expect(
|
|
334
|
+
it('createCustomClient creates a client with custom adapter', () => {
|
|
335
|
+
const adapter = new MockAdapter();
|
|
336
|
+
const client = createCustomClient(adapter);
|
|
337
|
+
expect(client).toBeInstanceOf(TapstackDBClient);
|
|
338
|
+
expect(client.getAdapter()).toBe(adapter);
|
|
83
339
|
});
|
|
84
340
|
});
|