@shipfox/api-workspaces 9.3.0 → 10.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.
Files changed (64) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +33 -0
  3. package/README.md +5 -2
  4. package/dist/core/admin-workspaces.d.ts +41 -0
  5. package/dist/core/admin-workspaces.d.ts.map +1 -0
  6. package/dist/core/admin-workspaces.js +86 -0
  7. package/dist/core/admin-workspaces.js.map +1 -0
  8. package/dist/core/index.d.ts +1 -0
  9. package/dist/core/index.d.ts.map +1 -1
  10. package/dist/core/index.js +1 -0
  11. package/dist/core/index.js.map +1 -1
  12. package/dist/db/index.d.ts +2 -2
  13. package/dist/db/index.d.ts.map +1 -1
  14. package/dist/db/index.js +1 -1
  15. package/dist/db/index.js.map +1 -1
  16. package/dist/db/memberships.d.ts +2 -0
  17. package/dist/db/memberships.d.ts.map +1 -1
  18. package/dist/db/memberships.js +4 -2
  19. package/dist/db/memberships.js.map +1 -1
  20. package/dist/db/schema/workspaces.d.ts.map +1 -1
  21. package/dist/db/schema/workspaces.js +4 -2
  22. package/dist/db/schema/workspaces.js.map +1 -1
  23. package/dist/db/workspaces.d.ts +16 -0
  24. package/dist/db/workspaces.d.ts.map +1 -1
  25. package/dist/db/workspaces.js +43 -1
  26. package/dist/db/workspaces.js.map +1 -1
  27. package/dist/index.d.ts +11 -1
  28. package/dist/index.d.ts.map +1 -1
  29. package/dist/index.js +32 -3
  30. package/dist/index.js.map +1 -1
  31. package/dist/presentation/dto/membership.d.ts.map +1 -1
  32. package/dist/presentation/dto/membership.js +1 -0
  33. package/dist/presentation/dto/membership.js.map +1 -1
  34. package/dist/presentation/index.d.ts +1 -1
  35. package/dist/presentation/index.d.ts.map +1 -1
  36. package/dist/presentation/index.js +1 -1
  37. package/dist/presentation/index.js.map +1 -1
  38. package/dist/presentation/routes/admin-workspaces.d.ts +10 -0
  39. package/dist/presentation/routes/admin-workspaces.d.ts.map +1 -0
  40. package/dist/presentation/routes/admin-workspaces.js +84 -0
  41. package/dist/presentation/routes/admin-workspaces.js.map +1 -0
  42. package/dist/presentation/routes/index.d.ts +8 -0
  43. package/dist/presentation/routes/index.d.ts.map +1 -1
  44. package/dist/presentation/routes/index.js +7 -0
  45. package/dist/presentation/routes/index.js.map +1 -1
  46. package/dist/tsconfig.test.tsbuildinfo +1 -1
  47. package/drizzle/0001_complex_beast.sql +1 -0
  48. package/drizzle/meta/0001_snapshot.json +476 -0
  49. package/drizzle/meta/_journal.json +7 -0
  50. package/package.json +8 -5
  51. package/src/core/admin-workspaces.ts +114 -0
  52. package/src/core/index.ts +6 -0
  53. package/src/db/index.ts +4 -0
  54. package/src/db/memberships.ts +8 -1
  55. package/src/db/schema/workspaces.ts +14 -10
  56. package/src/db/workspaces.test.ts +25 -0
  57. package/src/db/workspaces.ts +75 -1
  58. package/src/index.ts +27 -2
  59. package/src/presentation/dto/membership.ts +1 -0
  60. package/src/presentation/index.ts +1 -1
  61. package/src/presentation/routes/admin-workspaces.test.ts +168 -0
  62. package/src/presentation/routes/admin-workspaces.ts +93 -0
  63. package/src/presentation/routes/index.ts +12 -0
  64. package/tsconfig.build.tsbuildinfo +1 -1
@@ -0,0 +1 @@
1
+ CREATE INDEX "workspaces_name_id_idx" ON "workspaces_workspaces" USING btree ("name","id");
@@ -0,0 +1,476 @@
1
+ {
2
+ "id": "8af7d61b-bfa2-426e-81bc-6523c4c0a368",
3
+ "prevId": "074ad011-b8db-44f8-b880-47460ca229d2",
4
+ "version": "7",
5
+ "dialect": "postgresql",
6
+ "tables": {
7
+ "public.workspaces_invitations": {
8
+ "name": "workspaces_invitations",
9
+ "schema": "",
10
+ "columns": {
11
+ "id": {
12
+ "name": "id",
13
+ "type": "uuid",
14
+ "primaryKey": true,
15
+ "notNull": true,
16
+ "default": "uuidv7()"
17
+ },
18
+ "workspace_id": {
19
+ "name": "workspace_id",
20
+ "type": "uuid",
21
+ "primaryKey": false,
22
+ "notNull": true
23
+ },
24
+ "email": {
25
+ "name": "email",
26
+ "type": "text",
27
+ "primaryKey": false,
28
+ "notNull": true
29
+ },
30
+ "hashed_token": {
31
+ "name": "hashed_token",
32
+ "type": "text",
33
+ "primaryKey": false,
34
+ "notNull": true
35
+ },
36
+ "expires_at": {
37
+ "name": "expires_at",
38
+ "type": "timestamp with time zone",
39
+ "primaryKey": false,
40
+ "notNull": true
41
+ },
42
+ "revoked_at": {
43
+ "name": "revoked_at",
44
+ "type": "timestamp with time zone",
45
+ "primaryKey": false,
46
+ "notNull": false
47
+ },
48
+ "accepted_at": {
49
+ "name": "accepted_at",
50
+ "type": "timestamp with time zone",
51
+ "primaryKey": false,
52
+ "notNull": false
53
+ },
54
+ "accepted_by_user_id": {
55
+ "name": "accepted_by_user_id",
56
+ "type": "uuid",
57
+ "primaryKey": false,
58
+ "notNull": false
59
+ },
60
+ "invited_by_user_id": {
61
+ "name": "invited_by_user_id",
62
+ "type": "uuid",
63
+ "primaryKey": false,
64
+ "notNull": true
65
+ },
66
+ "invited_by_display": {
67
+ "name": "invited_by_display",
68
+ "type": "text",
69
+ "primaryKey": false,
70
+ "notNull": false
71
+ },
72
+ "created_at": {
73
+ "name": "created_at",
74
+ "type": "timestamp with time zone",
75
+ "primaryKey": false,
76
+ "notNull": true,
77
+ "default": "now()"
78
+ },
79
+ "updated_at": {
80
+ "name": "updated_at",
81
+ "type": "timestamp with time zone",
82
+ "primaryKey": false,
83
+ "notNull": true,
84
+ "default": "now()"
85
+ }
86
+ },
87
+ "indexes": {
88
+ "workspaces_invitations_hashed_token_unique": {
89
+ "name": "workspaces_invitations_hashed_token_unique",
90
+ "columns": [
91
+ {
92
+ "expression": "hashed_token",
93
+ "isExpression": false,
94
+ "asc": true,
95
+ "nulls": "last"
96
+ }
97
+ ],
98
+ "isUnique": true,
99
+ "concurrently": false,
100
+ "method": "btree",
101
+ "with": {}
102
+ },
103
+ "workspaces_invitations_workspace_email_idx": {
104
+ "name": "workspaces_invitations_workspace_email_idx",
105
+ "columns": [
106
+ {
107
+ "expression": "workspace_id",
108
+ "isExpression": false,
109
+ "asc": true,
110
+ "nulls": "last"
111
+ },
112
+ {
113
+ "expression": "email",
114
+ "isExpression": false,
115
+ "asc": true,
116
+ "nulls": "last"
117
+ }
118
+ ],
119
+ "isUnique": false,
120
+ "concurrently": false,
121
+ "method": "btree",
122
+ "with": {}
123
+ }
124
+ },
125
+ "foreignKeys": {
126
+ "workspaces_invitations_workspace_id_workspaces_workspaces_id_fk": {
127
+ "name": "workspaces_invitations_workspace_id_workspaces_workspaces_id_fk",
128
+ "tableFrom": "workspaces_invitations",
129
+ "tableTo": "workspaces_workspaces",
130
+ "columnsFrom": ["workspace_id"],
131
+ "columnsTo": ["id"],
132
+ "onDelete": "cascade",
133
+ "onUpdate": "no action"
134
+ }
135
+ },
136
+ "compositePrimaryKeys": {},
137
+ "uniqueConstraints": {},
138
+ "policies": {},
139
+ "checkConstraints": {},
140
+ "isRLSEnabled": false
141
+ },
142
+ "public.workspaces_memberships": {
143
+ "name": "workspaces_memberships",
144
+ "schema": "",
145
+ "columns": {
146
+ "id": {
147
+ "name": "id",
148
+ "type": "uuid",
149
+ "primaryKey": true,
150
+ "notNull": true,
151
+ "default": "uuidv7()"
152
+ },
153
+ "user_id": {
154
+ "name": "user_id",
155
+ "type": "uuid",
156
+ "primaryKey": false,
157
+ "notNull": true
158
+ },
159
+ "user_email": {
160
+ "name": "user_email",
161
+ "type": "text",
162
+ "primaryKey": false,
163
+ "notNull": true
164
+ },
165
+ "user_name": {
166
+ "name": "user_name",
167
+ "type": "text",
168
+ "primaryKey": false,
169
+ "notNull": false
170
+ },
171
+ "workspace_id": {
172
+ "name": "workspace_id",
173
+ "type": "uuid",
174
+ "primaryKey": false,
175
+ "notNull": true
176
+ },
177
+ "created_at": {
178
+ "name": "created_at",
179
+ "type": "timestamp with time zone",
180
+ "primaryKey": false,
181
+ "notNull": true,
182
+ "default": "now()"
183
+ },
184
+ "updated_at": {
185
+ "name": "updated_at",
186
+ "type": "timestamp with time zone",
187
+ "primaryKey": false,
188
+ "notNull": true,
189
+ "default": "now()"
190
+ }
191
+ },
192
+ "indexes": {
193
+ "workspaces_memberships_user_workspace_unique": {
194
+ "name": "workspaces_memberships_user_workspace_unique",
195
+ "columns": [
196
+ {
197
+ "expression": "user_id",
198
+ "isExpression": false,
199
+ "asc": true,
200
+ "nulls": "last"
201
+ },
202
+ {
203
+ "expression": "workspace_id",
204
+ "isExpression": false,
205
+ "asc": true,
206
+ "nulls": "last"
207
+ }
208
+ ],
209
+ "isUnique": true,
210
+ "concurrently": false,
211
+ "method": "btree",
212
+ "with": {}
213
+ },
214
+ "workspaces_memberships_workspace_id_idx": {
215
+ "name": "workspaces_memberships_workspace_id_idx",
216
+ "columns": [
217
+ {
218
+ "expression": "workspace_id",
219
+ "isExpression": false,
220
+ "asc": true,
221
+ "nulls": "last"
222
+ }
223
+ ],
224
+ "isUnique": false,
225
+ "concurrently": false,
226
+ "method": "btree",
227
+ "with": {}
228
+ }
229
+ },
230
+ "foreignKeys": {
231
+ "workspaces_memberships_workspace_id_workspaces_workspaces_id_fk": {
232
+ "name": "workspaces_memberships_workspace_id_workspaces_workspaces_id_fk",
233
+ "tableFrom": "workspaces_memberships",
234
+ "tableTo": "workspaces_workspaces",
235
+ "columnsFrom": ["workspace_id"],
236
+ "columnsTo": ["id"],
237
+ "onDelete": "cascade",
238
+ "onUpdate": "no action"
239
+ }
240
+ },
241
+ "compositePrimaryKeys": {},
242
+ "uniqueConstraints": {},
243
+ "policies": {},
244
+ "checkConstraints": {},
245
+ "isRLSEnabled": false
246
+ },
247
+ "public.workspaces_outbox": {
248
+ "name": "workspaces_outbox",
249
+ "schema": "",
250
+ "columns": {
251
+ "id": {
252
+ "name": "id",
253
+ "type": "uuid",
254
+ "primaryKey": true,
255
+ "notNull": true,
256
+ "default": "uuidv7()"
257
+ },
258
+ "event_type": {
259
+ "name": "event_type",
260
+ "type": "text",
261
+ "primaryKey": false,
262
+ "notNull": true
263
+ },
264
+ "ordering_key": {
265
+ "name": "ordering_key",
266
+ "type": "text",
267
+ "primaryKey": false,
268
+ "notNull": false
269
+ },
270
+ "payload": {
271
+ "name": "payload",
272
+ "type": "jsonb",
273
+ "primaryKey": false,
274
+ "notNull": true
275
+ },
276
+ "created_at": {
277
+ "name": "created_at",
278
+ "type": "timestamp with time zone",
279
+ "primaryKey": false,
280
+ "notNull": true,
281
+ "default": "now()"
282
+ },
283
+ "dispatched_at": {
284
+ "name": "dispatched_at",
285
+ "type": "timestamp with time zone",
286
+ "primaryKey": false,
287
+ "notNull": false
288
+ },
289
+ "dispatch_attempts": {
290
+ "name": "dispatch_attempts",
291
+ "type": "integer",
292
+ "primaryKey": false,
293
+ "notNull": true,
294
+ "default": 0
295
+ },
296
+ "next_dispatch_at": {
297
+ "name": "next_dispatch_at",
298
+ "type": "timestamp with time zone",
299
+ "primaryKey": false,
300
+ "notNull": true,
301
+ "default": "now()"
302
+ },
303
+ "last_dispatch_error": {
304
+ "name": "last_dispatch_error",
305
+ "type": "jsonb",
306
+ "primaryKey": false,
307
+ "notNull": false
308
+ },
309
+ "last_dispatch_failed_at": {
310
+ "name": "last_dispatch_failed_at",
311
+ "type": "timestamp with time zone",
312
+ "primaryKey": false,
313
+ "notNull": false
314
+ },
315
+ "dead_lettered_at": {
316
+ "name": "dead_lettered_at",
317
+ "type": "timestamp with time zone",
318
+ "primaryKey": false,
319
+ "notNull": false
320
+ }
321
+ },
322
+ "indexes": {
323
+ "workspaces_outbox_pending_idx": {
324
+ "name": "workspaces_outbox_pending_idx",
325
+ "columns": [
326
+ {
327
+ "expression": "next_dispatch_at",
328
+ "isExpression": false,
329
+ "asc": true,
330
+ "nulls": "last"
331
+ },
332
+ {
333
+ "expression": "created_at",
334
+ "isExpression": false,
335
+ "asc": true,
336
+ "nulls": "last"
337
+ }
338
+ ],
339
+ "isUnique": false,
340
+ "where": "\"dispatched_at\" IS NULL AND \"dead_lettered_at\" IS NULL",
341
+ "concurrently": false,
342
+ "method": "btree",
343
+ "with": {}
344
+ },
345
+ "workspaces_outbox_dispatched_retention_idx": {
346
+ "name": "workspaces_outbox_dispatched_retention_idx",
347
+ "columns": [
348
+ {
349
+ "expression": "dispatched_at",
350
+ "isExpression": false,
351
+ "asc": true,
352
+ "nulls": "last"
353
+ },
354
+ {
355
+ "expression": "id",
356
+ "isExpression": false,
357
+ "asc": true,
358
+ "nulls": "last"
359
+ }
360
+ ],
361
+ "isUnique": false,
362
+ "where": "\"dispatched_at\" IS NOT NULL",
363
+ "concurrently": false,
364
+ "method": "btree",
365
+ "with": {}
366
+ }
367
+ },
368
+ "foreignKeys": {},
369
+ "compositePrimaryKeys": {},
370
+ "uniqueConstraints": {},
371
+ "policies": {},
372
+ "checkConstraints": {},
373
+ "isRLSEnabled": false
374
+ },
375
+ "public.workspaces_workspaces": {
376
+ "name": "workspaces_workspaces",
377
+ "schema": "",
378
+ "columns": {
379
+ "id": {
380
+ "name": "id",
381
+ "type": "uuid",
382
+ "primaryKey": true,
383
+ "notNull": true,
384
+ "default": "uuidv7()"
385
+ },
386
+ "name": {
387
+ "name": "name",
388
+ "type": "text",
389
+ "primaryKey": false,
390
+ "notNull": true
391
+ },
392
+ "status": {
393
+ "name": "status",
394
+ "type": "workspaces_workspace_status",
395
+ "typeSchema": "public",
396
+ "primaryKey": false,
397
+ "notNull": true,
398
+ "default": "'active'"
399
+ },
400
+ "settings": {
401
+ "name": "settings",
402
+ "type": "jsonb",
403
+ "primaryKey": false,
404
+ "notNull": true,
405
+ "default": "'{}'::jsonb"
406
+ },
407
+ "created_by": {
408
+ "name": "created_by",
409
+ "type": "uuid",
410
+ "primaryKey": false,
411
+ "notNull": false
412
+ },
413
+ "created_at": {
414
+ "name": "created_at",
415
+ "type": "timestamp with time zone",
416
+ "primaryKey": false,
417
+ "notNull": true,
418
+ "default": "now()"
419
+ },
420
+ "updated_at": {
421
+ "name": "updated_at",
422
+ "type": "timestamp with time zone",
423
+ "primaryKey": false,
424
+ "notNull": true,
425
+ "default": "now()"
426
+ }
427
+ },
428
+ "indexes": {
429
+ "workspaces_name_id_idx": {
430
+ "name": "workspaces_name_id_idx",
431
+ "columns": [
432
+ {
433
+ "expression": "name",
434
+ "isExpression": false,
435
+ "asc": true,
436
+ "nulls": "last"
437
+ },
438
+ {
439
+ "expression": "id",
440
+ "isExpression": false,
441
+ "asc": true,
442
+ "nulls": "last"
443
+ }
444
+ ],
445
+ "isUnique": false,
446
+ "concurrently": false,
447
+ "method": "btree",
448
+ "with": {}
449
+ }
450
+ },
451
+ "foreignKeys": {},
452
+ "compositePrimaryKeys": {},
453
+ "uniqueConstraints": {},
454
+ "policies": {},
455
+ "checkConstraints": {},
456
+ "isRLSEnabled": false
457
+ }
458
+ },
459
+ "enums": {
460
+ "public.workspaces_workspace_status": {
461
+ "name": "workspaces_workspace_status",
462
+ "schema": "public",
463
+ "values": ["active", "suspended", "deleted"]
464
+ }
465
+ },
466
+ "schemas": {},
467
+ "sequences": {},
468
+ "roles": {},
469
+ "policies": {},
470
+ "views": {},
471
+ "_meta": {
472
+ "columns": {},
473
+ "schemas": {},
474
+ "tables": {}
475
+ }
476
+ }
@@ -8,6 +8,13 @@
8
8
  "when": 1777229181112,
9
9
  "tag": "0000_initial",
10
10
  "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "7",
15
+ "when": 1785217735221,
16
+ "tag": "0001_complex_beast",
17
+ "breakpoints": true
11
18
  }
12
19
  ]
13
20
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shipfox/api-workspaces",
3
3
  "license": "MIT",
4
- "version": "9.3.0",
4
+ "version": "10.0.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/ShipfoxHQ/shipfox.git",
@@ -25,15 +25,18 @@
25
25
  "drizzle-orm": "^0.45.2",
26
26
  "zod": "^4.4.3",
27
27
  "@shipfox/api-common-dto": "9.2.0",
28
- "@shipfox/api-auth-context": "9.3.0",
29
- "@shipfox/api-workspaces-dto": "9.3.0",
28
+ "@shipfox/api-auth-context": "10.0.0",
29
+ "@shipfox/api-auth-dto": "10.0.0",
30
+ "@shipfox/api-projects-dto": "10.0.0",
31
+ "@shipfox/api-runners-dto": "10.0.0",
32
+ "@shipfox/api-workspaces-dto": "10.0.0",
30
33
  "@shipfox/config": "1.2.4",
31
34
  "@shipfox/inter-module": "0.2.2",
32
35
  "@shipfox/node-drizzle": "0.3.4",
33
- "@shipfox/node-fastify": "0.3.4",
36
+ "@shipfox/node-fastify": "0.4.0",
34
37
  "@shipfox/node-email": "0.3.4",
35
38
  "@shipfox/node-mailer": "0.2.4",
36
- "@shipfox/node-module": "1.0.3",
39
+ "@shipfox/node-module": "1.0.4",
37
40
  "@shipfox/node-opentelemetry": "0.6.3",
38
41
  "@shipfox/node-outbox": "0.2.6",
39
42
  "@shipfox/node-postgres": "0.4.4",
@@ -0,0 +1,114 @@
1
+ import type {ProjectsModuleClient} from '@shipfox/api-projects-dto/inter-module';
2
+ import type {RunnersInterModuleClient} from '@shipfox/api-runners-dto/inter-module';
3
+ import type {StringIdCursor} from '@shipfox/node-drizzle';
4
+ import {logger} from '@shipfox/node-opentelemetry';
5
+ import {listAdminWorkspaces as listAdminWorkspaceRows} from '#db/workspaces.js';
6
+
7
+ export interface WorkspaceAdministratorSummary {
8
+ id: string;
9
+ name: string;
10
+ status: 'active' | 'suspended' | 'deleted';
11
+ memberSummary: {count: number};
12
+ projectSummary: {state: 'available'; count: number} | {state: 'unknown'};
13
+ jobCounts: {state: 'available'; queued: number; running: number} | {state: 'unknown'};
14
+ createdAt: Date;
15
+ updatedAt: Date;
16
+ }
17
+
18
+ export interface ListWorkspaceAdministratorSummariesParams {
19
+ workspaceId?: string | undefined;
20
+ search?: string | undefined;
21
+ status?: 'active' | 'suspended' | 'deleted' | undefined;
22
+ limit: number;
23
+ cursor?: StringIdCursor | undefined;
24
+ projects: ProjectsModuleClient;
25
+ runners: RunnersInterModuleClient;
26
+ }
27
+
28
+ export interface ListWorkspaceAdministratorSummariesResult {
29
+ workspaces: WorkspaceAdministratorSummary[];
30
+ nextCursor: StringIdCursor | null;
31
+ }
32
+
33
+ export async function listWorkspaceAdministratorSummaries(
34
+ params: ListWorkspaceAdministratorSummariesParams,
35
+ ): Promise<ListWorkspaceAdministratorSummariesResult> {
36
+ const result = await listAdminWorkspaceRows(params);
37
+ const workspaceIds = result.workspaces.map(({id}) => id);
38
+ const [projectCounts, jobCounts] = await Promise.all([
39
+ getProjectCounts(params.projects, workspaceIds),
40
+ getJobCounts(params.runners, workspaceIds),
41
+ ]);
42
+
43
+ return {
44
+ workspaces: result.workspaces.map((workspace) => ({
45
+ id: workspace.id,
46
+ name: workspace.name,
47
+ status: workspace.status,
48
+ memberSummary: {count: workspace.memberCount},
49
+ projectSummary: projectCounts?.get(workspace.id) ?? {state: 'unknown'},
50
+ jobCounts: jobCounts?.get(workspace.id) ?? {state: 'unknown'},
51
+ createdAt: workspace.createdAt,
52
+ updatedAt: workspace.updatedAt,
53
+ })),
54
+ nextCursor: result.nextCursor,
55
+ };
56
+ }
57
+
58
+ async function getProjectCounts(
59
+ projects: ProjectsModuleClient,
60
+ workspaceIds: string[],
61
+ ): Promise<Map<string, {state: 'available'; count: number} | {state: 'unknown'}> | null> {
62
+ if (workspaceIds.length === 0) return new Map();
63
+
64
+ try {
65
+ const result = await projects.getWorkspaceProjectCounts({workspaceIds});
66
+ const counts = new Map(
67
+ result.counts.map(({workspaceId, count}) => [
68
+ workspaceId,
69
+ {state: 'available', count} as const,
70
+ ]),
71
+ );
72
+ return new Map(
73
+ workspaceIds.map((workspaceId) => [
74
+ workspaceId,
75
+ counts.get(workspaceId) ?? {state: 'unknown'},
76
+ ]),
77
+ );
78
+ } catch (error) {
79
+ logger().warn(
80
+ {operation: 'admin-workspace-project-summary', err: error},
81
+ 'Project summary unavailable',
82
+ );
83
+ return null;
84
+ }
85
+ }
86
+
87
+ async function getJobCounts(
88
+ runners: RunnersInterModuleClient,
89
+ workspaceIds: string[],
90
+ ): Promise<Map<
91
+ string,
92
+ {state: 'available'; queued: number; running: number} | {state: 'unknown'}
93
+ > | null> {
94
+ if (workspaceIds.length === 0) return new Map();
95
+
96
+ try {
97
+ const result = await runners.getWorkspaceJobCounts({workspaceIds});
98
+ const counts = new Map(
99
+ result.counts.map(({workspaceId, queued, running}) => [
100
+ workspaceId,
101
+ {state: 'available', queued, running} as const,
102
+ ]),
103
+ );
104
+ return new Map(
105
+ workspaceIds.map((workspaceId) => [
106
+ workspaceId,
107
+ counts.get(workspaceId) ?? {state: 'unknown'},
108
+ ]),
109
+ );
110
+ } catch (error) {
111
+ logger().warn({operation: 'admin-workspace-job-counts', err: error}, 'Job counts unavailable');
112
+ return null;
113
+ }
114
+ }
package/src/core/index.ts CHANGED
@@ -1,3 +1,9 @@
1
+ export {
2
+ type ListWorkspaceAdministratorSummariesParams,
3
+ type ListWorkspaceAdministratorSummariesResult,
4
+ listWorkspaceAdministratorSummaries,
5
+ type WorkspaceAdministratorSummary,
6
+ } from './admin-workspaces.js';
1
7
  export type {Invitation} from './entities/invitation.js';
2
8
  export type {Membership} from './entities/membership.js';
3
9
  export type {Workspace, WorkspaceStatus} from './entities/workspace.js';
package/src/db/index.ts CHANGED
@@ -24,7 +24,10 @@ export {
24
24
  } from './memberships.js';
25
25
  export {workspacesOutbox} from './schema/outbox.js';
26
26
  export type {
27
+ AdminWorkspaceRow,
27
28
  CreateWorkspaceParams,
29
+ ListAdminWorkspaceParams,
30
+ ListAdminWorkspaceResult,
28
31
  UpdateWorkspaceParams,
29
32
  WorkspaceServiceMetrics,
30
33
  } from './workspaces.js';
@@ -32,6 +35,7 @@ export {
32
35
  createWorkspace,
33
36
  getWorkspaceById,
34
37
  getWorkspaceServiceMetrics,
38
+ listAdminWorkspaces,
35
39
  updateWorkspace,
36
40
  } from './workspaces.js';
37
41