@startsimpli/api 0.5.20 → 0.5.21
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/package.json +10 -10
- package/src/constants/endpoints.ts +33 -0
- package/src/index.ts +16 -1
- package/src/lib/__tests__/companies-api.test.ts +53 -0
- package/src/lib/__tests__/domain-claims-api.test.ts +68 -0
- package/src/lib/__tests__/team-invitations-api.test.ts +50 -0
- package/src/lib/__tests__/teams-api.test.ts +74 -0
- package/src/lib/companies-api.ts +48 -0
- package/src/lib/domain-claims-api.ts +63 -0
- package/src/lib/team-invitations-api.ts +49 -0
- package/src/lib/teams-api.ts +73 -0
- package/src/lib/users-api.ts +13 -0
- package/src/types/index.ts +22 -0
- package/src/types/team.ts +175 -0
- package/src/types/user.ts +20 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@startsimpli/api",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.21",
|
|
4
4
|
"description": "Type-safe Django REST API client for StartSimpli apps",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -23,14 +23,6 @@
|
|
|
23
23
|
"publishConfig": {
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"build": "tsup",
|
|
28
|
-
"dev": "tsup --watch",
|
|
29
|
-
"type-check": "tsc --noEmit",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"test:watch": "vitest",
|
|
32
|
-
"clean": "rm -rf dist"
|
|
33
|
-
},
|
|
34
26
|
"dependencies": {
|
|
35
27
|
"isomorphic-dompurify": "^3.10.0",
|
|
36
28
|
"zod": "^4.3.6"
|
|
@@ -60,5 +52,13 @@
|
|
|
60
52
|
"tsup": "^8.5.1",
|
|
61
53
|
"typescript": "^6.0.3",
|
|
62
54
|
"vitest": "^4.1.5"
|
|
55
|
+
},
|
|
56
|
+
"scripts": {
|
|
57
|
+
"build": "tsup",
|
|
58
|
+
"dev": "tsup --watch",
|
|
59
|
+
"type-check": "tsc --noEmit",
|
|
60
|
+
"test": "vitest run",
|
|
61
|
+
"test:watch": "vitest",
|
|
62
|
+
"clean": "rm -rf dist"
|
|
63
63
|
}
|
|
64
|
-
}
|
|
64
|
+
}
|
|
@@ -59,6 +59,9 @@ export const ENDPOINTS = {
|
|
|
59
59
|
CONTACT_ENRICH: (id: string) => `api/v1/contacts/${id}/enrich`,
|
|
60
60
|
CONTACTS_ENRICH_APOLLO: 'api/v1/contacts/enrich-apollo',
|
|
61
61
|
|
|
62
|
+
// Auth
|
|
63
|
+
AUTH_EARLY_REGISTER: 'api/v1/auth/early-register',
|
|
64
|
+
|
|
62
65
|
// Users
|
|
63
66
|
USER_ME: 'api/v1/users/me',
|
|
64
67
|
USER_CHANGE_PASSWORD: 'api/v1/users/me/change-password',
|
|
@@ -66,6 +69,36 @@ export const ENDPOINTS = {
|
|
|
66
69
|
// Companies / Feature flags
|
|
67
70
|
FEATURE_FLAGS: 'api/v1/companies/feature-flags',
|
|
68
71
|
|
|
72
|
+
// Companies (startsim-o7s) — accepts numeric id OR slug
|
|
73
|
+
COMPANIES: 'api/v1/companies',
|
|
74
|
+
COMPANY: (idOrSlug: string) => `api/v1/companies/${idOrSlug}`,
|
|
75
|
+
COMPANY_FEATURE_FLAGS: (idOrSlug: string) => `api/v1/companies/${idOrSlug}/feature-flags`,
|
|
76
|
+
|
|
77
|
+
// Teams (startsim-o7s, startsim-tsm)
|
|
78
|
+
TEAMS: 'api/v1/teams',
|
|
79
|
+
TEAM: (idOrSlug: string) => `api/v1/teams/${idOrSlug}`,
|
|
80
|
+
TEAM_MEMBERS: (idOrSlug: string) => `api/v1/teams/${idOrSlug}/members`,
|
|
81
|
+
TEAM_BULK_INVITE: (idOrSlug: string) => `api/v1/teams/${idOrSlug}/bulk-invite`,
|
|
82
|
+
TEAM_REMOVE_MEMBER: (idOrSlug: string) => `api/v1/teams/${idOrSlug}/remove-member`,
|
|
83
|
+
TEAM_UPDATE_ROLE: (idOrSlug: string) => `api/v1/teams/${idOrSlug}/update-role`,
|
|
84
|
+
TEAM_MEMBERS_MY_TEAMS: 'api/v1/team-members/my-teams',
|
|
85
|
+
|
|
86
|
+
// Team invitations (startsim-tsm)
|
|
87
|
+
TEAM_INVITATIONS: 'api/v1/team-invitations',
|
|
88
|
+
TEAM_INVITATION: (id: string) => `api/v1/team-invitations/${id}`,
|
|
89
|
+
TEAM_INVITATION_ACCEPT: (id: string) => `api/v1/team-invitations/${id}/accept`,
|
|
90
|
+
TEAM_INVITATION_REVOKE: (id: string) => `api/v1/team-invitations/${id}/revoke`,
|
|
91
|
+
|
|
92
|
+
// Email domain claims (startsim-gpu)
|
|
93
|
+
TEAM_DOMAIN_CLAIMS: 'api/v1/team-domain-claims',
|
|
94
|
+
TEAM_DOMAIN_CLAIM: (id: string) => `api/v1/team-domain-claims/${id}`,
|
|
95
|
+
TEAM_DOMAIN_CLAIM_VERIFY_DNS: (id: string) => `api/v1/team-domain-claims/${id}/verify-dns`,
|
|
96
|
+
TEAM_DOMAIN_CLAIM_VERIFY_EMAIL_INITIATE: (id: string) =>
|
|
97
|
+
`api/v1/team-domain-claims/${id}/verify-email-initiate`,
|
|
98
|
+
TEAM_DOMAIN_CLAIM_VERIFY_EMAIL_CODE: (id: string) =>
|
|
99
|
+
`api/v1/team-domain-claims/${id}/verify-email-code`,
|
|
100
|
+
TEAM_DOMAIN_CLAIM_REVOKE: (id: string) => `api/v1/team-domain-claims/${id}/revoke`,
|
|
101
|
+
|
|
69
102
|
// Markets (instruments, prices, analytics, news, health)
|
|
70
103
|
INSTRUMENTS: 'api/v1/markets/instruments',
|
|
71
104
|
INSTRUMENT: (symbol: string) => `api/v1/markets/instruments/${symbol}`,
|
package/src/index.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type { MessageStats } from './lib/message-stats';
|
|
|
25
25
|
export { MessageTemplatesApi } from './lib/message-templates-api';
|
|
26
26
|
export type { MessageTemplate, MessageTemplateFilters, CreateMessageTemplateInput } from './lib/message-templates-api';
|
|
27
27
|
export { UsersApi } from './lib/users-api';
|
|
28
|
-
export type { UserProfile, UpdateProfileRequest, ChangePasswordRequest, ChangePasswordResponse } from './types/user';
|
|
28
|
+
export type { UserProfile, UpdateProfileRequest, ChangePasswordRequest, ChangePasswordResponse, EarlyRegisterRequest, EarlyRegisterResponse } from './types/user';
|
|
29
29
|
export { FunnelsApi, isFunnelRunConflict, isFunnelValidationError } from './lib/funnels-api';
|
|
30
30
|
export type {
|
|
31
31
|
FunnelPreviewResult,
|
|
@@ -164,6 +164,10 @@ import { TargetListsApi } from './lib/target-lists-api';
|
|
|
164
164
|
import { MessageTemplatesApi } from './lib/message-templates-api';
|
|
165
165
|
import { MarketsApi } from './lib/markets-api';
|
|
166
166
|
import { VaultApi } from './lib/vault-api';
|
|
167
|
+
import { CompaniesApi } from './lib/companies-api';
|
|
168
|
+
import { TeamsApi } from './lib/teams-api';
|
|
169
|
+
import { TeamInvitationsApi } from './lib/team-invitations-api';
|
|
170
|
+
import { DomainClaimsApi } from './lib/domain-claims-api';
|
|
167
171
|
|
|
168
172
|
import type { ApiClientConfig } from './lib/api-client';
|
|
169
173
|
|
|
@@ -189,9 +193,20 @@ export function createStartSimpliApi(config: ApiClientConfig = {}) {
|
|
|
189
193
|
messageTemplates: new MessageTemplatesApi(client),
|
|
190
194
|
markets: new MarketsApi(client),
|
|
191
195
|
vault: new VaultApi(client),
|
|
196
|
+
companies: new CompaniesApi(client),
|
|
197
|
+
teams: new TeamsApi(client),
|
|
198
|
+
teamInvitations: new TeamInvitationsApi(client),
|
|
199
|
+
domainClaims: new DomainClaimsApi(client),
|
|
192
200
|
};
|
|
193
201
|
}
|
|
194
202
|
|
|
203
|
+
// Team management (startsim-o7s)
|
|
204
|
+
export { CompaniesApi } from './lib/companies-api';
|
|
205
|
+
export { TeamsApi } from './lib/teams-api';
|
|
206
|
+
export type { MyTeamMembership } from './lib/teams-api';
|
|
207
|
+
export { TeamInvitationsApi } from './lib/team-invitations-api';
|
|
208
|
+
export { DomainClaimsApi } from './lib/domain-claims-api';
|
|
209
|
+
|
|
195
210
|
// Vault API
|
|
196
211
|
export { VaultApi } from './lib/vault-api';
|
|
197
212
|
export type {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { CompaniesApi } from '../companies-api';
|
|
3
|
+
|
|
4
|
+
function makeApi() {
|
|
5
|
+
const fetch = { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn() };
|
|
6
|
+
const api = new CompaniesApi({ fetch } as never);
|
|
7
|
+
return { api, fetch };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('CompaniesApi', () => {
|
|
11
|
+
let api: CompaniesApi;
|
|
12
|
+
let fetch: { get: ReturnType<typeof vi.fn>; post: ReturnType<typeof vi.fn>; patch: ReturnType<typeof vi.fn>; delete: ReturnType<typeof vi.fn> };
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
({ api, fetch } = makeApi());
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('list passes params through to the companies endpoint', async () => {
|
|
19
|
+
fetch.get.mockResolvedValue({ results: [], count: 0 });
|
|
20
|
+
await api.list({ search: 'acme', pageSize: 50 });
|
|
21
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/companies', {
|
|
22
|
+
params: { search: 'acme', pageSize: 50 },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('retrieve accepts either id or slug in the path', async () => {
|
|
27
|
+
fetch.get.mockResolvedValue({ id: '1', slug: 'acme' });
|
|
28
|
+
await api.retrieve('acme');
|
|
29
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/companies/acme');
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('update PATCHes the company with the camelCase payload', async () => {
|
|
33
|
+
fetch.patch.mockResolvedValue({ id: '1', slug: 'acme', name: 'Acme Inc.' });
|
|
34
|
+
await api.update('acme', { name: 'Acme Inc.' });
|
|
35
|
+
expect(fetch.patch).toHaveBeenCalledWith('api/v1/companies/acme', { name: 'Acme Inc.' });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('featureFlags.get unwraps the flags envelope', async () => {
|
|
39
|
+
fetch.get.mockResolvedValue({ flags: { section_inbox: true } });
|
|
40
|
+
const flags = await api.featureFlags.get('acme');
|
|
41
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/companies/acme/feature-flags');
|
|
42
|
+
expect(flags).toEqual({ section_inbox: true });
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('featureFlags.update PATCHes a wrapped flags payload', async () => {
|
|
46
|
+
fetch.patch.mockResolvedValue({ flags: { section_inbox: false } });
|
|
47
|
+
const flags = await api.featureFlags.update('acme', { section_inbox: false });
|
|
48
|
+
expect(fetch.patch).toHaveBeenCalledWith('api/v1/companies/acme/feature-flags', {
|
|
49
|
+
flags: { section_inbox: false },
|
|
50
|
+
});
|
|
51
|
+
expect(flags).toEqual({ section_inbox: false });
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { DomainClaimsApi } from '../domain-claims-api';
|
|
3
|
+
|
|
4
|
+
function makeApi() {
|
|
5
|
+
const fetch = { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn() };
|
|
6
|
+
const api = new DomainClaimsApi({ fetch } as never);
|
|
7
|
+
return { api, fetch };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('DomainClaimsApi', () => {
|
|
11
|
+
let api: DomainClaimsApi;
|
|
12
|
+
let fetch: { get: ReturnType<typeof vi.fn>; post: ReturnType<typeof vi.fn>; patch: ReturnType<typeof vi.fn>; delete: ReturnType<typeof vi.fn> };
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
({ api, fetch } = makeApi());
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('list hits the team-domain-claims endpoint', async () => {
|
|
19
|
+
fetch.get.mockResolvedValue({ results: [], count: 0 });
|
|
20
|
+
await api.list({ companyId: 'c1', verified: true });
|
|
21
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/team-domain-claims', {
|
|
22
|
+
params: { companyId: 'c1', verified: true },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('create POSTs the claim payload + returns the visible token', async () => {
|
|
27
|
+
fetch.post.mockResolvedValue({
|
|
28
|
+
id: 'd1',
|
|
29
|
+
domain: 'acme.com',
|
|
30
|
+
verificationToken: 'startsim-verify=xyz',
|
|
31
|
+
});
|
|
32
|
+
const r = await api.create({ companyId: 'c1', domain: 'acme.com' });
|
|
33
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/team-domain-claims', {
|
|
34
|
+
companyId: 'c1',
|
|
35
|
+
domain: 'acme.com',
|
|
36
|
+
});
|
|
37
|
+
expect(r.verificationToken).toBe('startsim-verify=xyz');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('verifyDns posts the verify-dns action', async () => {
|
|
41
|
+
fetch.post.mockResolvedValue({ id: 'd1', verified: true });
|
|
42
|
+
await api.verifyDns('d1');
|
|
43
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/team-domain-claims/d1/verify-dns');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('verifyEmailInitiate posts the initiate action', async () => {
|
|
47
|
+
fetch.post.mockResolvedValue({ detail: 'sent' });
|
|
48
|
+
await api.verifyEmailInitiate('d1');
|
|
49
|
+
expect(fetch.post).toHaveBeenCalledWith(
|
|
50
|
+
'api/v1/team-domain-claims/d1/verify-email-initiate',
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('verifyEmailCode posts the code', async () => {
|
|
55
|
+
fetch.post.mockResolvedValue({ id: 'd1', verified: true });
|
|
56
|
+
await api.verifyEmailCode('d1', '123456');
|
|
57
|
+
expect(fetch.post).toHaveBeenCalledWith(
|
|
58
|
+
'api/v1/team-domain-claims/d1/verify-email-code',
|
|
59
|
+
{ code: '123456' },
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('revoke posts the revoke action', async () => {
|
|
64
|
+
fetch.post.mockResolvedValue(undefined);
|
|
65
|
+
await api.revoke('d1');
|
|
66
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/team-domain-claims/d1/revoke');
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { TeamInvitationsApi } from '../team-invitations-api';
|
|
3
|
+
|
|
4
|
+
function makeApi() {
|
|
5
|
+
const fetch = { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn() };
|
|
6
|
+
const api = new TeamInvitationsApi({ fetch } as never);
|
|
7
|
+
return { api, fetch };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('TeamInvitationsApi', () => {
|
|
11
|
+
let api: TeamInvitationsApi;
|
|
12
|
+
let fetch: { get: ReturnType<typeof vi.fn>; post: ReturnType<typeof vi.fn>; patch: ReturnType<typeof vi.fn>; delete: ReturnType<typeof vi.fn> };
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
({ api, fetch } = makeApi());
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('list reaches the invitations endpoint', async () => {
|
|
19
|
+
fetch.get.mockResolvedValue({ results: [], count: 0 });
|
|
20
|
+
await api.list({ teamId: 't1' });
|
|
21
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/team-invitations', {
|
|
22
|
+
params: { teamId: 't1' },
|
|
23
|
+
});
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
it('create POSTs the invitation payload', async () => {
|
|
27
|
+
fetch.post.mockResolvedValue({ id: 'i1', token: 'raw-token' });
|
|
28
|
+
const r = await api.create({ email: 'a@x.com', teamId: 't1', role: 'member' });
|
|
29
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/team-invitations', {
|
|
30
|
+
email: 'a@x.com',
|
|
31
|
+
teamId: 't1',
|
|
32
|
+
role: 'member',
|
|
33
|
+
});
|
|
34
|
+
expect(r.token).toBe('raw-token');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('revoke POSTs the revoke action', async () => {
|
|
38
|
+
fetch.post.mockResolvedValue(undefined);
|
|
39
|
+
await api.revoke('i1');
|
|
40
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/team-invitations/i1/revoke');
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('accept POSTs the token in the body', async () => {
|
|
44
|
+
fetch.post.mockResolvedValue({ id: 'i1', acceptedAt: '2025-01-01' });
|
|
45
|
+
await api.accept('i1', 'raw-token');
|
|
46
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/team-invitations/i1/accept', {
|
|
47
|
+
token: 'raw-token',
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
});
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { TeamsApi } from '../teams-api';
|
|
3
|
+
|
|
4
|
+
function makeApi() {
|
|
5
|
+
const fetch = { get: vi.fn(), post: vi.fn(), patch: vi.fn(), delete: vi.fn() };
|
|
6
|
+
const api = new TeamsApi({ fetch } as never);
|
|
7
|
+
return { api, fetch };
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('TeamsApi', () => {
|
|
11
|
+
let api: TeamsApi;
|
|
12
|
+
let fetch: { get: ReturnType<typeof vi.fn>; post: ReturnType<typeof vi.fn>; patch: ReturnType<typeof vi.fn>; delete: ReturnType<typeof vi.fn> };
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
({ api, fetch } = makeApi());
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('list hits the teams endpoint with params', async () => {
|
|
19
|
+
fetch.get.mockResolvedValue({ results: [], count: 0 });
|
|
20
|
+
await api.list({ companyId: 'c1' });
|
|
21
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/teams', { params: { companyId: 'c1' } });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('retrieve accepts slug', async () => {
|
|
25
|
+
fetch.get.mockResolvedValue({ id: '1', slug: 'eng' });
|
|
26
|
+
await api.retrieve('eng');
|
|
27
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/teams/eng');
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('members returns the bare array (no pagination wrapper)', async () => {
|
|
31
|
+
fetch.get.mockResolvedValue([{ id: 'm1', userId: 'u1', role: 'owner' }]);
|
|
32
|
+
const members = await api.members('eng');
|
|
33
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/teams/eng/members');
|
|
34
|
+
expect(members).toHaveLength(1);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('bulkInvite POSTs the invitations array wrapped in an envelope', async () => {
|
|
38
|
+
fetch.post.mockResolvedValue({ invited: [], skipped: [] });
|
|
39
|
+
await api.bulkInvite('eng', [
|
|
40
|
+
{ email: 'a@x.com', role: 'member' },
|
|
41
|
+
{ email: 'b@x.com', role: 'admin' },
|
|
42
|
+
]);
|
|
43
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/teams/eng/bulk-invite', {
|
|
44
|
+
invitations: [
|
|
45
|
+
{ email: 'a@x.com', role: 'member' },
|
|
46
|
+
{ email: 'b@x.com', role: 'admin' },
|
|
47
|
+
],
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('removeMember sends userId in the body, not the URL', async () => {
|
|
52
|
+
fetch.post.mockResolvedValue(undefined);
|
|
53
|
+
await api.removeMember('eng', 'u42');
|
|
54
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/teams/eng/remove-member', {
|
|
55
|
+
userId: 'u42',
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('updateRole sends userId + role in the body', async () => {
|
|
60
|
+
fetch.post.mockResolvedValue({ id: 'm1', userId: 'u42', role: 'admin' });
|
|
61
|
+
await api.updateRole('eng', 'u42', 'admin');
|
|
62
|
+
expect(fetch.post).toHaveBeenCalledWith('api/v1/teams/eng/update-role', {
|
|
63
|
+
userId: 'u42',
|
|
64
|
+
role: 'admin',
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('myTeams returns the unpaginated membership array', async () => {
|
|
69
|
+
fetch.get.mockResolvedValue([{ id: 'm1', teamId: 't1', role: 'owner' }]);
|
|
70
|
+
const rows = await api.myTeams();
|
|
71
|
+
expect(fetch.get).toHaveBeenCalledWith('api/v1/team-members/my-teams');
|
|
72
|
+
expect(rows[0].role).toBe('owner');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Companies API wrapper for /api/v1/companies/*.
|
|
3
|
+
*
|
|
4
|
+
* The Django CompanyViewSet accepts numeric id OR slug as the URL identifier.
|
|
5
|
+
* Feature-flags get their own nested endpoint so app shells can fetch flags
|
|
6
|
+
* without pulling the full company object. startsim-o7s.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ApiClient } from './api-client';
|
|
10
|
+
import { ENDPOINTS } from '../constants/endpoints';
|
|
11
|
+
import type {
|
|
12
|
+
Company,
|
|
13
|
+
CompanyListParams,
|
|
14
|
+
UpdateCompanyInput,
|
|
15
|
+
} from '../types/team';
|
|
16
|
+
import type { FeatureFlags, FeatureFlagsResponse } from './feature-flags';
|
|
17
|
+
import type { PaginatedResponse } from '../types';
|
|
18
|
+
|
|
19
|
+
export class CompaniesApi {
|
|
20
|
+
constructor(private client: ApiClient) {}
|
|
21
|
+
|
|
22
|
+
/** List companies the current user belongs to. */
|
|
23
|
+
list(params?: CompanyListParams): Promise<PaginatedResponse<Company>> {
|
|
24
|
+
return this.client.fetch.get<PaginatedResponse<Company>>(ENDPOINTS.COMPANIES, { params });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Retrieve a single company by id or slug. */
|
|
28
|
+
retrieve(idOrSlug: string): Promise<Company> {
|
|
29
|
+
return this.client.fetch.get<Company>(ENDPOINTS.COMPANY(idOrSlug));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** PATCH update — name, slug, settings. */
|
|
33
|
+
update(idOrSlug: string, patch: UpdateCompanyInput): Promise<Company> {
|
|
34
|
+
return this.client.fetch.patch<Company>(ENDPOINTS.COMPANY(idOrSlug), patch);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Per-company feature-flag accessors (admin-gated on PATCH). */
|
|
38
|
+
readonly featureFlags = {
|
|
39
|
+
get: (idOrSlug: string): Promise<FeatureFlags> =>
|
|
40
|
+
this.client.fetch
|
|
41
|
+
.get<FeatureFlagsResponse>(ENDPOINTS.COMPANY_FEATURE_FLAGS(idOrSlug))
|
|
42
|
+
.then((r) => r.flags),
|
|
43
|
+
update: (idOrSlug: string, patch: Partial<FeatureFlags>): Promise<FeatureFlags> =>
|
|
44
|
+
this.client.fetch
|
|
45
|
+
.patch<FeatureFlagsResponse>(ENDPOINTS.COMPANY_FEATURE_FLAGS(idOrSlug), { flags: patch })
|
|
46
|
+
.then((r) => r.flags),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* EmailDomainClaim API wrapper for /api/v1/team-domain-claims/*.
|
|
3
|
+
*
|
|
4
|
+
* Two verification methods are supported by the backend (startsim-gpu):
|
|
5
|
+
* - DNS TXT: caller adds a TXT record, then calls verify-dns.
|
|
6
|
+
* - Email attestation: backend sends a 6-digit code to a postmaster@<domain>
|
|
7
|
+
* mailbox; caller submits the code via verify-email-code.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ApiClient } from './api-client';
|
|
11
|
+
import { ENDPOINTS } from '../constants/endpoints';
|
|
12
|
+
import type {
|
|
13
|
+
EmailDomainClaim,
|
|
14
|
+
DomainClaimListParams,
|
|
15
|
+
CreateDomainClaimInput,
|
|
16
|
+
DomainVerifyEmailInitiateResponse,
|
|
17
|
+
} from '../types/team';
|
|
18
|
+
import type { PaginatedResponse } from '../types';
|
|
19
|
+
|
|
20
|
+
export class DomainClaimsApi {
|
|
21
|
+
constructor(private client: ApiClient) {}
|
|
22
|
+
|
|
23
|
+
list(params?: DomainClaimListParams): Promise<PaginatedResponse<EmailDomainClaim>> {
|
|
24
|
+
return this.client.fetch.get<PaginatedResponse<EmailDomainClaim>>(
|
|
25
|
+
ENDPOINTS.TEAM_DOMAIN_CLAIMS,
|
|
26
|
+
{ params },
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create a new claim. The response includes the verification_token (visible
|
|
32
|
+
* only to the creator); subsequent reads return null.
|
|
33
|
+
*/
|
|
34
|
+
create(input: CreateDomainClaimInput): Promise<EmailDomainClaim> {
|
|
35
|
+
return this.client.fetch.post<EmailDomainClaim>(ENDPOINTS.TEAM_DOMAIN_CLAIMS, input);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Trigger DNS TXT lookup. Backend marks verified=true on a clean match. */
|
|
39
|
+
verifyDns(id: string): Promise<EmailDomainClaim> {
|
|
40
|
+
return this.client.fetch.post<EmailDomainClaim>(
|
|
41
|
+
ENDPOINTS.TEAM_DOMAIN_CLAIM_VERIFY_DNS(id),
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Kick off email-attestation flow — backend emails postmaster@<domain>. */
|
|
46
|
+
verifyEmailInitiate(id: string): Promise<DomainVerifyEmailInitiateResponse> {
|
|
47
|
+
return this.client.fetch.post<DomainVerifyEmailInitiateResponse>(
|
|
48
|
+
ENDPOINTS.TEAM_DOMAIN_CLAIM_VERIFY_EMAIL_INITIATE(id),
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Submit the postmaster mailbox's 6-digit code to complete attestation. */
|
|
53
|
+
verifyEmailCode(id: string, code: string): Promise<EmailDomainClaim> {
|
|
54
|
+
return this.client.fetch.post<EmailDomainClaim>(
|
|
55
|
+
ENDPOINTS.TEAM_DOMAIN_CLAIM_VERIFY_EMAIL_CODE(id),
|
|
56
|
+
{ code },
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
revoke(id: string): Promise<void> {
|
|
61
|
+
return this.client.fetch.post<void>(ENDPOINTS.TEAM_DOMAIN_CLAIM_REVOKE(id));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TeamInvitations API wrapper for /api/v1/team-invitations/*.
|
|
3
|
+
*
|
|
4
|
+
* The accept endpoint is intentionally public-ish — it accepts an unauth
|
|
5
|
+
* caller carrying the one-time invite token in the body. The TeamMember
|
|
6
|
+
* row is created idempotently by the backend manager (startsim-tsm).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ApiClient } from './api-client';
|
|
10
|
+
import { ENDPOINTS } from '../constants/endpoints';
|
|
11
|
+
import type {
|
|
12
|
+
TeamInvitation,
|
|
13
|
+
TeamInvitationListParams,
|
|
14
|
+
CreateTeamInvitationInput,
|
|
15
|
+
} from '../types/team';
|
|
16
|
+
import type { PaginatedResponse } from '../types';
|
|
17
|
+
|
|
18
|
+
export class TeamInvitationsApi {
|
|
19
|
+
constructor(private client: ApiClient) {}
|
|
20
|
+
|
|
21
|
+
list(params?: TeamInvitationListParams): Promise<PaginatedResponse<TeamInvitation>> {
|
|
22
|
+
return this.client.fetch.get<PaginatedResponse<TeamInvitation>>(
|
|
23
|
+
ENDPOINTS.TEAM_INVITATIONS,
|
|
24
|
+
{ params },
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Send a single invitation. The create response includes the raw token;
|
|
30
|
+
* subsequent reads will redact it.
|
|
31
|
+
*/
|
|
32
|
+
create(input: CreateTeamInvitationInput): Promise<TeamInvitation> {
|
|
33
|
+
return this.client.fetch.post<TeamInvitation>(ENDPOINTS.TEAM_INVITATIONS, input);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
revoke(id: string): Promise<void> {
|
|
37
|
+
return this.client.fetch.post<void>(ENDPOINTS.TEAM_INVITATION_REVOKE(id));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Accept an invitation. The caller posts the one-time token; the backend
|
|
42
|
+
* wires the TeamMember idempotently (startsim-tsm).
|
|
43
|
+
*/
|
|
44
|
+
accept(id: string, token: string): Promise<TeamInvitation> {
|
|
45
|
+
return this.client.fetch.post<TeamInvitation>(ENDPOINTS.TEAM_INVITATION_ACCEPT(id), {
|
|
46
|
+
token,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Teams API wrapper for /api/v1/teams/* plus the bulk-mgmt actions shipped
|
|
3
|
+
* by startsim-tsm (bulk-invite, remove-member, update-role).
|
|
4
|
+
*
|
|
5
|
+
* The Django TeamViewSet accepts numeric id OR slug. Member-mutation
|
|
6
|
+
* endpoints take user_id in the body, not the URL. startsim-o7s.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ApiClient } from './api-client';
|
|
10
|
+
import { ENDPOINTS } from '../constants/endpoints';
|
|
11
|
+
import type {
|
|
12
|
+
Team,
|
|
13
|
+
TeamMember,
|
|
14
|
+
TeamListParams,
|
|
15
|
+
BulkInviteEntry,
|
|
16
|
+
BulkInviteResult,
|
|
17
|
+
TeamRole,
|
|
18
|
+
} from '../types/team';
|
|
19
|
+
import type { PaginatedResponse } from '../types';
|
|
20
|
+
|
|
21
|
+
/** Shape returned by /team-members/my-teams/ — a flat array of memberships. */
|
|
22
|
+
export interface MyTeamMembership extends TeamMember {
|
|
23
|
+
team?: Team;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export class TeamsApi {
|
|
27
|
+
constructor(private client: ApiClient) {}
|
|
28
|
+
|
|
29
|
+
/** List teams visible to the current user. */
|
|
30
|
+
list(params?: TeamListParams): Promise<PaginatedResponse<Team>> {
|
|
31
|
+
return this.client.fetch.get<PaginatedResponse<Team>>(ENDPOINTS.TEAMS, { params });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Retrieve a single team by id or slug. */
|
|
35
|
+
retrieve(idOrSlug: string): Promise<Team> {
|
|
36
|
+
return this.client.fetch.get<Team>(ENDPOINTS.TEAM(idOrSlug));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** List members of a team. Backend returns the full set (no pagination). */
|
|
40
|
+
members(idOrSlug: string): Promise<TeamMember[]> {
|
|
41
|
+
return this.client.fetch.get<TeamMember[]>(ENDPOINTS.TEAM_MEMBERS(idOrSlug));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Bulk-invite endpoint shipped by startsim-tsm. Sends one POST with the
|
|
46
|
+
* full list of {email, role} pairs and returns invited + skipped.
|
|
47
|
+
*/
|
|
48
|
+
bulkInvite(idOrSlug: string, invitations: BulkInviteEntry[]): Promise<BulkInviteResult> {
|
|
49
|
+
return this.client.fetch.post<BulkInviteResult>(ENDPOINTS.TEAM_BULK_INVITE(idOrSlug), {
|
|
50
|
+
invitations,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Remove a member from the team. user_id goes in the body (not the URL). */
|
|
55
|
+
removeMember(idOrSlug: string, userId: string): Promise<void> {
|
|
56
|
+
return this.client.fetch.post<void>(ENDPOINTS.TEAM_REMOVE_MEMBER(idOrSlug), {
|
|
57
|
+
userId,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Promote/demote a member — fails if it would leave zero OWNERs. */
|
|
62
|
+
updateRole(idOrSlug: string, userId: string, role: TeamRole): Promise<TeamMember> {
|
|
63
|
+
return this.client.fetch.post<TeamMember>(ENDPOINTS.TEAM_UPDATE_ROLE(idOrSlug), {
|
|
64
|
+
userId,
|
|
65
|
+
role,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Return the current user's membership rows across every team they touch. */
|
|
70
|
+
myTeams(): Promise<MyTeamMembership[]> {
|
|
71
|
+
return this.client.fetch.get<MyTeamMembership[]>(ENDPOINTS.TEAM_MEMBERS_MY_TEAMS);
|
|
72
|
+
}
|
|
73
|
+
}
|
package/src/lib/users-api.ts
CHANGED
|
@@ -7,6 +7,8 @@ import type {
|
|
|
7
7
|
UpdateProfileRequest,
|
|
8
8
|
ChangePasswordRequest,
|
|
9
9
|
ChangePasswordResponse,
|
|
10
|
+
EarlyRegisterRequest,
|
|
11
|
+
EarlyRegisterResponse,
|
|
10
12
|
} from '../types/user';
|
|
11
13
|
import { ENDPOINTS } from '../constants/endpoints';
|
|
12
14
|
import type { ApiClient } from './api-client';
|
|
@@ -37,4 +39,15 @@ export class UsersApi {
|
|
|
37
39
|
data
|
|
38
40
|
);
|
|
39
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Passwordless early-access registration (name + email only).
|
|
45
|
+
* Public endpoint — no auth token required.
|
|
46
|
+
*/
|
|
47
|
+
async earlyRegister(data: EarlyRegisterRequest): Promise<EarlyRegisterResponse> {
|
|
48
|
+
return this.client.fetch.post<EarlyRegisterResponse>(
|
|
49
|
+
ENDPOINTS.AUTH_EARLY_REGISTER,
|
|
50
|
+
data
|
|
51
|
+
);
|
|
52
|
+
}
|
|
40
53
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -98,6 +98,28 @@ export type {
|
|
|
98
98
|
ApolloEnrichmentSummary,
|
|
99
99
|
} from './enrichment';
|
|
100
100
|
|
|
101
|
+
// Team / Company / Invitation / Domain-claim types (startsim-o7s)
|
|
102
|
+
export type {
|
|
103
|
+
Company,
|
|
104
|
+
AuthUserLike,
|
|
105
|
+
TeamRole,
|
|
106
|
+
Team,
|
|
107
|
+
TeamMember,
|
|
108
|
+
TeamInvitation,
|
|
109
|
+
DomainVerificationMethod,
|
|
110
|
+
EmailDomainClaim,
|
|
111
|
+
CompanyListParams,
|
|
112
|
+
UpdateCompanyInput,
|
|
113
|
+
TeamListParams,
|
|
114
|
+
BulkInviteEntry,
|
|
115
|
+
BulkInviteResult,
|
|
116
|
+
TeamInvitationListParams,
|
|
117
|
+
CreateTeamInvitationInput,
|
|
118
|
+
DomainClaimListParams,
|
|
119
|
+
CreateDomainClaimInput,
|
|
120
|
+
DomainVerifyEmailInitiateResponse,
|
|
121
|
+
} from './team';
|
|
122
|
+
|
|
101
123
|
// Error types
|
|
102
124
|
export type {
|
|
103
125
|
FieldError,
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Team / Company / Invitation / Domain-claim types.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the Django serializers in apps.companies / apps.teams /
|
|
5
|
+
* apps.email_domain_claims. The FetchWrapper transforms snake_case ↔ camelCase
|
|
6
|
+
* at the boundary, so types here are camelCase. startsim-o7s.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/** A Company is the top-level org tenant in StartSimpli. */
|
|
10
|
+
export interface Company {
|
|
11
|
+
id: string;
|
|
12
|
+
slug: string;
|
|
13
|
+
name: string;
|
|
14
|
+
/** Free-form per-company settings blob (feature flags + product prefs). */
|
|
15
|
+
settings: Record<string, unknown>;
|
|
16
|
+
/** Hydrated by `?expand=owner`; otherwise just present as a foreign-key id. */
|
|
17
|
+
owner?: AuthUserLike | null;
|
|
18
|
+
ownerId?: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Trimmed AuthUser used by the team-mgmt serializers.
|
|
25
|
+
*
|
|
26
|
+
* Imported from @startsimpli/auth in practice, but we keep an internal shape
|
|
27
|
+
* here so @startsimpli/api stays decoupled from the auth package's exports.
|
|
28
|
+
* The real AuthUser is structurally compatible.
|
|
29
|
+
*/
|
|
30
|
+
export interface AuthUserLike {
|
|
31
|
+
id: string;
|
|
32
|
+
email: string;
|
|
33
|
+
firstName?: string;
|
|
34
|
+
lastName?: string;
|
|
35
|
+
fullName?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Role hierarchy on a TeamMember (mirrors backend `Role` choices). */
|
|
39
|
+
export type TeamRole = 'owner' | 'admin' | 'member' | 'viewer';
|
|
40
|
+
|
|
41
|
+
/** A Team scopes role assignments inside a Company. */
|
|
42
|
+
export interface Team {
|
|
43
|
+
id: string;
|
|
44
|
+
slug: string;
|
|
45
|
+
name: string;
|
|
46
|
+
companyId: string;
|
|
47
|
+
settings: Record<string, unknown>;
|
|
48
|
+
createdAt: string;
|
|
49
|
+
updatedAt: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** A TeamMember pins a user into a Team at a specific role. */
|
|
53
|
+
export interface TeamMember {
|
|
54
|
+
id: string;
|
|
55
|
+
userId: string;
|
|
56
|
+
teamId: string;
|
|
57
|
+
role: TeamRole;
|
|
58
|
+
invitedById?: string | null;
|
|
59
|
+
joinedAt: string;
|
|
60
|
+
/** Optional hydrated user (when serializer expands it). */
|
|
61
|
+
user?: AuthUserLike;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Pending or accepted invitation to join a Team.
|
|
66
|
+
* `token` is write-only after create — surfaced only in the create response
|
|
67
|
+
* so apps can build accept-links.
|
|
68
|
+
*/
|
|
69
|
+
export interface TeamInvitation {
|
|
70
|
+
id: string;
|
|
71
|
+
email: string;
|
|
72
|
+
teamId: string;
|
|
73
|
+
role: TeamRole;
|
|
74
|
+
/** Only present in the create-response. Backend redacts on subsequent reads. */
|
|
75
|
+
token?: string;
|
|
76
|
+
invitedById: string;
|
|
77
|
+
expiresAt: string;
|
|
78
|
+
acceptedAt?: string | null;
|
|
79
|
+
revokedAt?: string | null;
|
|
80
|
+
isExpired: boolean;
|
|
81
|
+
isAccepted: boolean;
|
|
82
|
+
createdAt: string;
|
|
83
|
+
updatedAt: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Verification method used for an EmailDomainClaim. */
|
|
87
|
+
export type DomainVerificationMethod = 'dns_txt' | 'email_attestation';
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A Company's claim on an email domain — gates "anyone @acme.com auto-joins"
|
|
91
|
+
* behavior once verified. See startsim-gpu.
|
|
92
|
+
*/
|
|
93
|
+
export interface EmailDomainClaim {
|
|
94
|
+
id: string;
|
|
95
|
+
companyId: string;
|
|
96
|
+
domain: string;
|
|
97
|
+
verified: boolean;
|
|
98
|
+
verificationMethod?: DomainVerificationMethod | null;
|
|
99
|
+
/** Only visible to the creator on POST — otherwise null/absent. */
|
|
100
|
+
verificationToken?: string | null;
|
|
101
|
+
verifiedAt?: string | null;
|
|
102
|
+
verifiedById?: string | null;
|
|
103
|
+
lastVerifiedCheckAt?: string | null;
|
|
104
|
+
createdAt: string;
|
|
105
|
+
updatedAt: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ---- API request/payload shapes -------------------------------------------
|
|
109
|
+
|
|
110
|
+
export interface CompanyListParams {
|
|
111
|
+
page?: number;
|
|
112
|
+
pageSize?: number;
|
|
113
|
+
search?: string;
|
|
114
|
+
ordering?: string;
|
|
115
|
+
[key: string]: unknown;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export interface UpdateCompanyInput {
|
|
119
|
+
name?: string;
|
|
120
|
+
slug?: string;
|
|
121
|
+
settings?: Record<string, unknown>;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export interface TeamListParams {
|
|
125
|
+
page?: number;
|
|
126
|
+
pageSize?: number;
|
|
127
|
+
search?: string;
|
|
128
|
+
companyId?: string;
|
|
129
|
+
ordering?: string;
|
|
130
|
+
[key: string]: unknown;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface BulkInviteEntry {
|
|
134
|
+
email: string;
|
|
135
|
+
role: TeamRole;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export interface BulkInviteResult {
|
|
139
|
+
invited: TeamInvitation[];
|
|
140
|
+
/** Backend may return duplicates / failures per the bulk endpoint. */
|
|
141
|
+
skipped?: Array<{ email: string; reason: string }>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export interface TeamInvitationListParams {
|
|
145
|
+
page?: number;
|
|
146
|
+
pageSize?: number;
|
|
147
|
+
teamId?: string;
|
|
148
|
+
ordering?: string;
|
|
149
|
+
[key: string]: unknown;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface CreateTeamInvitationInput {
|
|
153
|
+
email: string;
|
|
154
|
+
teamId: string;
|
|
155
|
+
role: TeamRole;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export interface DomainClaimListParams {
|
|
159
|
+
page?: number;
|
|
160
|
+
pageSize?: number;
|
|
161
|
+
companyId?: string;
|
|
162
|
+
verified?: boolean;
|
|
163
|
+
ordering?: string;
|
|
164
|
+
[key: string]: unknown;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export interface CreateDomainClaimInput {
|
|
168
|
+
companyId: string;
|
|
169
|
+
domain: string;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface DomainVerifyEmailInitiateResponse {
|
|
173
|
+
/** Backend confirms an attestation email has been queued. */
|
|
174
|
+
detail: string;
|
|
175
|
+
}
|
package/src/types/user.ts
CHANGED
|
@@ -24,9 +24,28 @@ export interface UpdateProfileRequest {
|
|
|
24
24
|
export interface ChangePasswordRequest {
|
|
25
25
|
oldPassword: string;
|
|
26
26
|
newPassword: string;
|
|
27
|
-
newPasswordConfirm: string;
|
|
28
27
|
}
|
|
29
28
|
|
|
30
29
|
export interface ChangePasswordResponse {
|
|
31
30
|
detail: string;
|
|
32
31
|
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Passwordless early-access registration — name + email only.
|
|
35
|
+
* Hits POST /api/v1/auth/early-register/ (public, no auth).
|
|
36
|
+
*/
|
|
37
|
+
export interface EarlyRegisterRequest {
|
|
38
|
+
email: string;
|
|
39
|
+
firstName?: string;
|
|
40
|
+
lastName?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface EarlyRegisterResponse {
|
|
44
|
+
user: {
|
|
45
|
+
id: string;
|
|
46
|
+
email: string;
|
|
47
|
+
firstName: string;
|
|
48
|
+
lastName: string;
|
|
49
|
+
};
|
|
50
|
+
message: string;
|
|
51
|
+
}
|