@shipfox/client-runners 22.0.3 → 23.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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +10 -0
- package/dist/components/token-settings-sections.stories.js +2 -1
- package/dist/components/token-settings-sections.stories.js.map +1 -1
- package/dist/core/token.d.ts +6 -0
- package/dist/core/token.d.ts.map +1 -1
- package/dist/core/token.js.map +1 -1
- package/dist/hooks/api/provisioner-tokens.d.ts +13 -6
- package/dist/hooks/api/provisioner-tokens.d.ts.map +1 -1
- package/dist/hooks/api/provisioner-tokens.js +39 -7
- package/dist/hooks/api/provisioner-tokens.js.map +1 -1
- package/dist/hooks/api/token-mapper.d.ts +3 -2
- package/dist/hooks/api/token-mapper.d.ts.map +1 -1
- package/dist/hooks/api/token-mapper.js +6 -0
- package/dist/hooks/api/token-mapper.js.map +1 -1
- package/dist/tsconfig.test.tsbuildinfo +1 -1
- package/package.json +4 -4
- package/src/components/provisioner-tokens-settings-section.test.tsx +8 -7
- package/src/components/token-settings-sections.stories.tsx +1 -0
- package/src/core/token.ts +9 -0
- package/src/hooks/api/provisioner-tokens.test.ts +15 -1
- package/src/hooks/api/provisioner-tokens.ts +69 -14
- package/src/hooks/api/token-mapper.test.ts +29 -0
- package/src/hooks/api/token-mapper.ts +11 -1
- package/tsconfig.build.tsbuildinfo +1 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shipfox/client-runners",
|
|
3
3
|
"license": "MIT",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "23.0.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/ShipfoxHQ/shipfox.git",
|
|
@@ -27,11 +27,11 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@swc/helpers": "^0.5.17",
|
|
29
29
|
"@tanstack/react-form": "^1.32.0",
|
|
30
|
-
"@shipfox/api-runners-dto": "13.0.0",
|
|
31
30
|
"@shipfox/client-api": "6.0.1",
|
|
32
|
-
"@shipfox/client-shell": "
|
|
31
|
+
"@shipfox/client-shell": "23.0.0",
|
|
33
32
|
"@shipfox/client-ui": "22.0.3",
|
|
34
|
-
"@shipfox/react-ui": "2.2.0"
|
|
33
|
+
"@shipfox/react-ui": "2.2.0",
|
|
34
|
+
"@shipfox/api-runners-dto": "14.0.0"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
37
|
"@tanstack/react-query": "^5.101.0",
|
|
@@ -86,7 +86,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
86
86
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
87
87
|
const request = input as Request;
|
|
88
88
|
if (request.url.endsWith('/provisioners/active')) {
|
|
89
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
89
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
90
90
|
}
|
|
91
91
|
return Promise.resolve(jsonResponse({tokens: []}));
|
|
92
92
|
});
|
|
@@ -106,7 +106,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
106
106
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
107
107
|
const request = input as Request;
|
|
108
108
|
if (request.url.endsWith('/provisioners/active')) {
|
|
109
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
109
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
110
110
|
}
|
|
111
111
|
if (request.method === 'POST' && request.url.endsWith('/provisioners/tokens')) {
|
|
112
112
|
tokens = [provisionerToken({name: 'Docker provisioner'})];
|
|
@@ -145,7 +145,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
145
145
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
146
146
|
const request = input as Request;
|
|
147
147
|
if (request.url.endsWith('/provisioners/active')) {
|
|
148
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
148
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
149
149
|
}
|
|
150
150
|
return Promise.resolve(
|
|
151
151
|
jsonResponse({
|
|
@@ -181,7 +181,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
181
181
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
182
182
|
const request = input as Request;
|
|
183
183
|
if (request.url.endsWith('/provisioners/active')) {
|
|
184
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
184
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
185
185
|
}
|
|
186
186
|
return Promise.resolve(jsonResponse({tokens: [provisionerToken({name: tokenName})]}));
|
|
187
187
|
});
|
|
@@ -206,7 +206,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
206
206
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
207
207
|
const request = input as Request;
|
|
208
208
|
if (request.url.endsWith('/provisioners/active')) {
|
|
209
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
209
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
210
210
|
}
|
|
211
211
|
if (request.method === 'POST' && request.url.endsWith('/provisioners/tokens')) {
|
|
212
212
|
return Promise.resolve(
|
|
@@ -241,6 +241,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
241
241
|
return Promise.resolve(
|
|
242
242
|
jsonResponse({
|
|
243
243
|
provisioners: [activeProvisioner({id: activeId, name: 'Active provisioner'})],
|
|
244
|
+
installation_runners: 'none',
|
|
244
245
|
}),
|
|
245
246
|
);
|
|
246
247
|
}
|
|
@@ -306,7 +307,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
306
307
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
307
308
|
const request = input as Request;
|
|
308
309
|
if (request.url.endsWith('/provisioners/active')) {
|
|
309
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
310
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
310
311
|
}
|
|
311
312
|
if (request.method === 'POST' && request.url.includes('/revoke')) {
|
|
312
313
|
tokens = [];
|
|
@@ -339,7 +340,7 @@ describe('WorkspaceProvisionerTokensSettingsSection', () => {
|
|
|
339
340
|
const fetchImpl = vi.fn((input: RequestInfo | URL) => {
|
|
340
341
|
const request = input as Request;
|
|
341
342
|
if (request.url.endsWith('/provisioners/active')) {
|
|
342
|
-
return Promise.resolve(jsonResponse({provisioners: []}));
|
|
343
|
+
return Promise.resolve(jsonResponse({provisioners: [], installation_runners: 'none'}));
|
|
343
344
|
}
|
|
344
345
|
if (request.method === 'POST' && request.url.includes('/revoke')) {
|
|
345
346
|
return Promise.resolve(
|
package/src/core/token.ts
CHANGED
|
@@ -22,6 +22,15 @@ export interface ActiveProvisioner {
|
|
|
22
22
|
lastSeenAt: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export type InstallationRunnersStatus = 'managed' | 'none';
|
|
26
|
+
|
|
27
|
+
export type ActiveProvisioners = ActiveProvisioner[];
|
|
28
|
+
|
|
29
|
+
export interface ActiveProvisionersResponse {
|
|
30
|
+
provisioners: ActiveProvisioners;
|
|
31
|
+
installationRunners: InstallationRunnersStatus;
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
export interface CreatedManualRegistrationToken {
|
|
26
35
|
token: string;
|
|
27
36
|
id: string;
|
|
@@ -2,6 +2,7 @@ import {configureApiClient} from '@shipfox/client-api';
|
|
|
2
2
|
import {
|
|
3
3
|
createProvisionerToken,
|
|
4
4
|
listActiveProvisioners,
|
|
5
|
+
listActiveProvisionersResponse,
|
|
5
6
|
listProvisionerTokens,
|
|
6
7
|
revokeProvisionerToken,
|
|
7
8
|
} from './provisioner-tokens.js';
|
|
@@ -105,7 +106,7 @@ describe('provisioner token transports', () => {
|
|
|
105
106
|
expect(request.method).toBe('POST');
|
|
106
107
|
});
|
|
107
108
|
|
|
108
|
-
test('
|
|
109
|
+
test('keeps listActiveProvisioners array-compatible', async () => {
|
|
109
110
|
const fetchImpl = vi.fn().mockResolvedValue(
|
|
110
111
|
jsonResponse({
|
|
111
112
|
provisioners: [
|
|
@@ -116,6 +117,7 @@ describe('provisioner token transports', () => {
|
|
|
116
117
|
last_seen_at: '2026-05-08T01:00:00.000Z',
|
|
117
118
|
},
|
|
118
119
|
],
|
|
120
|
+
installation_runners: 'managed',
|
|
119
121
|
}),
|
|
120
122
|
);
|
|
121
123
|
configureApiClient({fetchImpl});
|
|
@@ -124,9 +126,21 @@ describe('provisioner token transports', () => {
|
|
|
124
126
|
|
|
125
127
|
const request = fetchImpl.mock.calls[0]?.[0] as Request;
|
|
126
128
|
expect(result).toHaveLength(1);
|
|
129
|
+
expect(result[0]?.name).toBe('Docker provisioner');
|
|
127
130
|
expect(request.url).toBe(
|
|
128
131
|
`https://api.example.test/workspaces/${workspaceId}/provisioners/active`,
|
|
129
132
|
);
|
|
130
133
|
expect(request.method).toBe('GET');
|
|
131
134
|
});
|
|
135
|
+
|
|
136
|
+
test('exposes installation runners status through the response adapter', async () => {
|
|
137
|
+
const fetchImpl = vi
|
|
138
|
+
.fn()
|
|
139
|
+
.mockResolvedValue(jsonResponse({provisioners: [], installation_runners: 'managed'}));
|
|
140
|
+
configureApiClient({fetchImpl});
|
|
141
|
+
|
|
142
|
+
const result = await listActiveProvisionersResponse({workspaceId});
|
|
143
|
+
|
|
144
|
+
expect(result).toEqual({provisioners: [], installationRunners: 'managed'});
|
|
145
|
+
});
|
|
132
146
|
});
|
|
@@ -6,20 +6,22 @@ import {
|
|
|
6
6
|
} from '@shipfox/api-runners-dto';
|
|
7
7
|
import {checkedApiRequest} from '@shipfox/client-api';
|
|
8
8
|
import {
|
|
9
|
-
type FetchQueryOptions,
|
|
10
9
|
queryOptions,
|
|
10
|
+
type UseQueryOptions,
|
|
11
11
|
useMutation,
|
|
12
12
|
useQuery,
|
|
13
13
|
useQueryClient,
|
|
14
14
|
} from '@tanstack/react-query';
|
|
15
15
|
import type {
|
|
16
|
-
|
|
16
|
+
ActiveProvisioners,
|
|
17
|
+
ActiveProvisionersResponse,
|
|
17
18
|
CreatedProvisionerToken,
|
|
18
19
|
CreateTokenCommand,
|
|
20
|
+
InstallationRunnersStatus,
|
|
19
21
|
ProvisionerToken,
|
|
20
22
|
} from '#core/token.js';
|
|
21
23
|
import {
|
|
22
|
-
|
|
24
|
+
toActiveProvisionersResponse,
|
|
23
25
|
toCreatedProvisionerToken,
|
|
24
26
|
toCreateTokenBody,
|
|
25
27
|
toProvisionerToken,
|
|
@@ -34,17 +36,24 @@ export const provisionerTokenQueryKeys = {
|
|
|
34
36
|
[...provisionerTokenQueryKeys.all, 'provisioners', workspaceId] as const,
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
type ProvisionerTokensQueryOptions =
|
|
39
|
+
type ProvisionerTokensQueryOptions = UseQueryOptions<
|
|
38
40
|
ProvisionerToken[],
|
|
39
41
|
Error,
|
|
40
42
|
ProvisionerToken[],
|
|
41
43
|
ReturnType<typeof provisionerTokenQueryKeys.list>
|
|
42
44
|
>;
|
|
43
45
|
|
|
44
|
-
type ActiveProvisionersQueryOptions =
|
|
45
|
-
|
|
46
|
+
type ActiveProvisionersQueryOptions = UseQueryOptions<
|
|
47
|
+
ActiveProvisionersResponse,
|
|
46
48
|
Error,
|
|
47
|
-
|
|
49
|
+
ActiveProvisioners,
|
|
50
|
+
ReturnType<typeof provisionerTokenQueryKeys.active>
|
|
51
|
+
>;
|
|
52
|
+
|
|
53
|
+
type InstallationRunnersStatusQueryOptions = UseQueryOptions<
|
|
54
|
+
ActiveProvisionersResponse,
|
|
55
|
+
Error,
|
|
56
|
+
InstallationRunnersStatus,
|
|
48
57
|
ReturnType<typeof provisionerTokenQueryKeys.active>
|
|
49
58
|
>;
|
|
50
59
|
|
|
@@ -93,19 +102,33 @@ export async function revokeProvisionerToken({
|
|
|
93
102
|
return toProvisionerToken(response);
|
|
94
103
|
}
|
|
95
104
|
|
|
96
|
-
export async function
|
|
105
|
+
export async function listActiveProvisionersResponse({
|
|
97
106
|
workspaceId,
|
|
98
107
|
signal,
|
|
99
108
|
}: {
|
|
100
109
|
workspaceId: string;
|
|
101
110
|
signal?: AbortSignal;
|
|
102
|
-
}): Promise<
|
|
111
|
+
}): Promise<ActiveProvisionersResponse> {
|
|
103
112
|
const response = await checkedApiRequest(
|
|
104
113
|
listActiveProvisionersResponseSchema,
|
|
105
114
|
`/workspaces/${workspaceId}/provisioners/active`,
|
|
106
115
|
{signal},
|
|
107
116
|
);
|
|
108
|
-
return response
|
|
117
|
+
return toActiveProvisionersResponse(response);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function listActiveProvisioners({
|
|
121
|
+
workspaceId,
|
|
122
|
+
signal,
|
|
123
|
+
}: {
|
|
124
|
+
workspaceId: string;
|
|
125
|
+
signal?: AbortSignal;
|
|
126
|
+
}): Promise<ActiveProvisioners> {
|
|
127
|
+
const response = await listActiveProvisionersResponse({
|
|
128
|
+
workspaceId,
|
|
129
|
+
...(signal ? {signal} : {}),
|
|
130
|
+
});
|
|
131
|
+
return response.provisioners;
|
|
109
132
|
}
|
|
110
133
|
|
|
111
134
|
export function provisionerTokensQueryOptions(workspaceId: string): ProvisionerTokensQueryOptions {
|
|
@@ -120,12 +143,27 @@ export function provisionerTokensQueryOptions(workspaceId: string): ProvisionerT
|
|
|
120
143
|
export function activeProvisionersQueryOptions(
|
|
121
144
|
workspaceId: string,
|
|
122
145
|
): ActiveProvisionersQueryOptions {
|
|
123
|
-
return
|
|
146
|
+
return {
|
|
124
147
|
queryKey: provisionerTokenQueryKeys.active(workspaceId),
|
|
125
|
-
queryFn: ({signal}) =>
|
|
148
|
+
queryFn: ({signal}) =>
|
|
149
|
+
listActiveProvisionersResponse({workspaceId, ...(signal ? {signal} : {})}),
|
|
150
|
+
select: (response) => response.provisioners,
|
|
126
151
|
refetchInterval: PROVISIONER_TOKEN_REFETCH_INTERVAL_MS,
|
|
127
152
|
refetchIntervalInBackground: false,
|
|
128
|
-
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function installationRunnersStatusQueryOptions(
|
|
157
|
+
workspaceId: string,
|
|
158
|
+
): InstallationRunnersStatusQueryOptions {
|
|
159
|
+
return {
|
|
160
|
+
queryKey: provisionerTokenQueryKeys.active(workspaceId),
|
|
161
|
+
queryFn: ({signal}) =>
|
|
162
|
+
listActiveProvisionersResponse({workspaceId, ...(signal ? {signal} : {})}),
|
|
163
|
+
select: (response) => response.installationRunners,
|
|
164
|
+
refetchInterval: PROVISIONER_TOKEN_REFETCH_INTERVAL_MS,
|
|
165
|
+
refetchIntervalInBackground: false,
|
|
166
|
+
};
|
|
129
167
|
}
|
|
130
168
|
|
|
131
169
|
export function useProvisionerTokensQuery(workspaceId: string | undefined) {
|
|
@@ -136,12 +174,29 @@ export function useProvisionerTokensQuery(workspaceId: string | undefined) {
|
|
|
136
174
|
}
|
|
137
175
|
|
|
138
176
|
export function useActiveProvisionersQuery(workspaceId: string | undefined) {
|
|
139
|
-
return useQuery
|
|
177
|
+
return useQuery<
|
|
178
|
+
ActiveProvisionersResponse,
|
|
179
|
+
Error,
|
|
180
|
+
ActiveProvisioners,
|
|
181
|
+
ReturnType<typeof provisionerTokenQueryKeys.active>
|
|
182
|
+
>({
|
|
140
183
|
...activeProvisionersQueryOptions(workspaceId ?? ''),
|
|
141
184
|
enabled: Boolean(workspaceId),
|
|
142
185
|
});
|
|
143
186
|
}
|
|
144
187
|
|
|
188
|
+
export function useInstallationRunnersStatusQuery(workspaceId: string | undefined) {
|
|
189
|
+
return useQuery<
|
|
190
|
+
ActiveProvisionersResponse,
|
|
191
|
+
Error,
|
|
192
|
+
InstallationRunnersStatus,
|
|
193
|
+
ReturnType<typeof provisionerTokenQueryKeys.active>
|
|
194
|
+
>({
|
|
195
|
+
...installationRunnersStatusQueryOptions(workspaceId ?? ''),
|
|
196
|
+
enabled: Boolean(workspaceId),
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
145
200
|
export function useCreateProvisionerTokenMutation(workspaceId: string) {
|
|
146
201
|
const queryClient = useQueryClient();
|
|
147
202
|
return useMutation({
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
toActiveProvisioner,
|
|
3
|
+
toActiveProvisionersResponse,
|
|
3
4
|
toCreatedManualRegistrationToken,
|
|
4
5
|
toCreatedProvisionerToken,
|
|
5
6
|
toCreateTokenBody,
|
|
@@ -84,6 +85,34 @@ describe('toActiveProvisioner', () => {
|
|
|
84
85
|
});
|
|
85
86
|
});
|
|
86
87
|
|
|
88
|
+
describe('toActiveProvisionersResponse', () => {
|
|
89
|
+
test('maps the transport response to its package-owned model', () => {
|
|
90
|
+
const result = toActiveProvisionersResponse({
|
|
91
|
+
provisioners: [
|
|
92
|
+
{
|
|
93
|
+
id: '33333333-3333-4333-8333-333333333333',
|
|
94
|
+
name: 'Docker provisioner',
|
|
95
|
+
prefix: 'sf_pt_test',
|
|
96
|
+
last_seen_at: '2026-07-22T09:00:00.000Z',
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
installation_runners: 'none',
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
expect(result).toEqual({
|
|
103
|
+
provisioners: [
|
|
104
|
+
{
|
|
105
|
+
id: '33333333-3333-4333-8333-333333333333',
|
|
106
|
+
name: 'Docker provisioner',
|
|
107
|
+
prefix: 'sf_pt_test',
|
|
108
|
+
lastSeenAt: '2026-07-22T09:00:00.000Z',
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
installationRunners: 'none',
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
87
116
|
describe('toCreatedManualRegistrationToken', () => {
|
|
88
117
|
test('maps a raw-token creation response to its package-owned model', () => {
|
|
89
118
|
const result = toCreatedManualRegistrationToken({
|
|
@@ -2,18 +2,19 @@ import type {
|
|
|
2
2
|
ActiveProvisionerDto,
|
|
3
3
|
CreateManualRegistrationTokenResponseDto,
|
|
4
4
|
CreateProvisionerTokenResponseDto,
|
|
5
|
+
ListActiveProvisionersResponseDto,
|
|
5
6
|
ManualRegistrationTokenDto,
|
|
6
7
|
ProvisionerTokenDto,
|
|
7
8
|
} from '@shipfox/api-runners-dto';
|
|
8
9
|
import type {
|
|
9
10
|
ActiveProvisioner,
|
|
11
|
+
ActiveProvisionersResponse,
|
|
10
12
|
CreatedManualRegistrationToken,
|
|
11
13
|
CreatedProvisionerToken,
|
|
12
14
|
CreateTokenCommand,
|
|
13
15
|
ManualRegistrationToken,
|
|
14
16
|
ProvisionerToken,
|
|
15
17
|
} from '#core/token.js';
|
|
16
|
-
|
|
17
18
|
export function toCreateTokenBody(command: CreateTokenCommand) {
|
|
18
19
|
return {
|
|
19
20
|
...(command.name ? {name: command.name} : {}),
|
|
@@ -51,6 +52,15 @@ export function toActiveProvisioner(dto: ActiveProvisionerDto): ActiveProvisione
|
|
|
51
52
|
return {id: dto.id, name: dto.name, prefix: dto.prefix, lastSeenAt: dto.last_seen_at};
|
|
52
53
|
}
|
|
53
54
|
|
|
55
|
+
export function toActiveProvisionersResponse(
|
|
56
|
+
dto: ListActiveProvisionersResponseDto,
|
|
57
|
+
): ActiveProvisionersResponse {
|
|
58
|
+
return {
|
|
59
|
+
provisioners: dto.provisioners.map(toActiveProvisioner),
|
|
60
|
+
installationRunners: dto.installation_runners,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
54
64
|
export function toCreatedManualRegistrationToken(
|
|
55
65
|
dto: CreateManualRegistrationTokenResponseDto,
|
|
56
66
|
): CreatedManualRegistrationToken {
|