ankka-console 0.10.0 → 0.11.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/dist/client/control-plane.d.ts +25 -1
- package/dist/client/control-plane.js +60 -1
- package/dist/client/schemas.d.ts +525 -0
- package/dist/client/schemas.js +174 -0
- package/dist/context.d.ts +98 -0
- package/dist/context.js +73 -0
- package/dist/extensions/render.d.ts +2 -0
- package/dist/extensions/render.js +5 -0
- package/dist/extensions/types.d.ts +1 -1
- package/dist/extensions/types.js +7 -0
- package/dist/fonts/inter-latin-ext-wght-normal.woff2 +0 -0
- package/dist/fonts/inter-latin-wght-normal.woff2 +0 -0
- package/dist/index.d.ts +9 -2
- package/dist/index.js +7 -1
- package/dist/routes/auth.sign-out.js +8 -3
- package/dist/routes/front.js +12 -6
- package/dist/routes/logs.d.ts +35 -0
- package/dist/routes/logs.js +15 -14
- package/dist/routes/members.js +11 -8
- package/dist/routes/organization-new.js +7 -4
- package/dist/routes/organization.js +13 -6
- package/dist/routes/project-new.js +8 -4
- package/dist/routes/project.d.ts +73 -2
- package/dist/routes/project.js +81 -9
- package/dist/routes/service-apply.d.ts +5 -2
- package/dist/routes/service-apply.js +17 -14
- package/dist/routes/service-history.d.ts +30 -0
- package/dist/routes/service-history.js +79 -0
- package/dist/routes/service-topology.d.ts +153 -0
- package/dist/routes/service-topology.js +54 -0
- package/dist/routes/service.d.ts +34 -10
- package/dist/routes/service.js +27 -22
- package/dist/routes/stream.service.js +18 -1
- package/dist/routes/tokens.js +8 -6
- package/dist/routes.js +2 -0
- package/dist/styles.css +2 -481
- package/dist/testing/fake-control-plane.d.ts +155 -0
- package/dist/testing/fake-control-plane.js +379 -12
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/scenarios.js +6 -0
- package/dist/theme.css +66 -0
- package/dist/ui/console.d.ts +19 -6
- package/dist/ui/console.js +9 -2
- package/dist/ui/errors.js +7 -5
- package/dist/ui/hosting.d.ts +11 -0
- package/dist/ui/hosting.js +20 -0
- package/dist/ui/primitives/button.d.ts +11 -0
- package/dist/ui/primitives/button.js +21 -0
- package/dist/ui/primitives/select.d.ts +10 -0
- package/dist/ui/primitives/select.js +4 -0
- package/dist/ui/primitives/switch.d.ts +9 -0
- package/dist/ui/primitives/switch.js +4 -0
- package/dist/ui/shape.d.ts +4 -0
- package/dist/ui/shape.js +36 -0
- package/dist/ui/shell.d.ts +76 -0
- package/dist/ui/shell.js +107 -0
- package/dist/ui/tokens.d.ts +82 -0
- package/dist/ui/tokens.js +172 -0
- package/dist/ui/topology/Graph.d.ts +13 -0
- package/dist/ui/topology/Graph.js +56 -0
- package/dist/ui/topology/Table.d.ts +10 -0
- package/dist/ui/topology/Table.js +14 -0
- package/dist/ui/topology/layout.d.ts +113 -0
- package/dist/ui/topology/layout.js +177 -0
- package/dist/ui/use-stream.d.ts +137 -1
- package/dist/ui/use-stream.js +11 -1
- package/package.json +11 -3
package/dist/client/schemas.js
CHANGED
|
@@ -90,6 +90,11 @@ export const projectSummarySchema = z.object({
|
|
|
90
90
|
services: z.number().int(),
|
|
91
91
|
registry: optional(registrySummarySchema),
|
|
92
92
|
});
|
|
93
|
+
export const mountStatusSchema = z.object({
|
|
94
|
+
path: z.string(),
|
|
95
|
+
service: z.string(),
|
|
96
|
+
state: z.string().default(""),
|
|
97
|
+
});
|
|
93
98
|
export const serviceStatusSchema = z.object({
|
|
94
99
|
name: z.string(),
|
|
95
100
|
projectId: z.string(),
|
|
@@ -107,6 +112,19 @@ export const serviceStatusSchema = z.object({
|
|
|
107
112
|
paused: z.boolean().default(false),
|
|
108
113
|
hosting: z.string().default("embedded"),
|
|
109
114
|
protocol: optional(z.string()),
|
|
115
|
+
mounts: z.array(mountStatusSchema).default([]),
|
|
116
|
+
callers: z.array(z.string()).default([]),
|
|
117
|
+
processPort: optional(z.number().int()),
|
|
118
|
+
broker: optional(z.string()),
|
|
119
|
+
/** Topics the service's components use that its project does not declare; absent when not read. */
|
|
120
|
+
undeclaredTopics: optional(z.array(z.string())),
|
|
121
|
+
/** Each side the service takes on a declared topic with a contract, checked against it; absent when not read. */
|
|
122
|
+
topicChecks: optional(z.array(z.lazy(() => topicCheckSchema))),
|
|
123
|
+
/** Each topic source of the service with how far behind it is, from its instances; absent when not read. */
|
|
124
|
+
topicSources: optional(z.array(z.lazy(() => topicSourceReportSchema))),
|
|
125
|
+
objectStorage: optional(z.string()),
|
|
126
|
+
bucket: optional(z.string()),
|
|
127
|
+
bucketAddress: optional(z.string()),
|
|
110
128
|
});
|
|
111
129
|
export const historyActorSchema = z.object({
|
|
112
130
|
subject: z.string(),
|
|
@@ -118,7 +136,14 @@ export const historyEntrySchema = z.object({
|
|
|
118
136
|
generation: z.number().int(),
|
|
119
137
|
actor: optional(historyActorSchema),
|
|
120
138
|
at: optional(z.string()),
|
|
139
|
+
/** On an entry that recorded a descriptor: its image, and a digest two such entries share exactly when the descriptors are the same. */
|
|
140
|
+
image: optional(z.string()),
|
|
141
|
+
digest: optional(z.string()),
|
|
142
|
+
/** On a rollback: the generation whose descriptor was applied again. */
|
|
143
|
+
rolledBackTo: optional(z.number().int()),
|
|
121
144
|
});
|
|
145
|
+
export const rollbackRequestSchema = z.object({ generation: optional(z.number().int()) });
|
|
146
|
+
export const rolledBackSchema = z.object({ rolledBackTo: z.number().int(), status: serviceStatusSchema });
|
|
122
147
|
export const instanceLogsSchema = z.object({
|
|
123
148
|
instance: z.string(),
|
|
124
149
|
output: z.string(),
|
|
@@ -127,6 +152,65 @@ export const instanceLogsSchema = z.object({
|
|
|
127
152
|
export const logsResponseSchema = z.object({
|
|
128
153
|
instances: z.array(instanceLogsSchema),
|
|
129
154
|
});
|
|
155
|
+
// ── Topology ──────────────────────────────────────────────────────────────
|
|
156
|
+
export const topologyHandlerSchema = z.object({
|
|
157
|
+
name: z.string(),
|
|
158
|
+
type: z.string(),
|
|
159
|
+
streaming: optional(z.boolean()),
|
|
160
|
+
});
|
|
161
|
+
export const topologyNodeSchema = z.object({
|
|
162
|
+
id: z.string(),
|
|
163
|
+
kind: z.string(),
|
|
164
|
+
layer: z.number().int(),
|
|
165
|
+
platform: z.boolean(),
|
|
166
|
+
handlers: z.array(topologyHandlerSchema),
|
|
167
|
+
});
|
|
168
|
+
export const declaredEdgeSchema = z.object({ from: z.string(), to: z.string(), kind: z.string() });
|
|
169
|
+
export const callPairSchema = z.object({
|
|
170
|
+
caller: z.string(),
|
|
171
|
+
callee: z.string(),
|
|
172
|
+
handled: z.object({ ok: z.number(), refused: z.number(), failed: z.number() }),
|
|
173
|
+
unanswered: z.object({ timedOut: z.number(), undelivered: z.number() }),
|
|
174
|
+
durationMillis: z.object({ p50: z.number(), p99: z.number(), max: z.number(), bucketed: z.boolean().default(true) }),
|
|
175
|
+
streaming: z.boolean().default(false),
|
|
176
|
+
histogram: z.array(z.number()).default([]),
|
|
177
|
+
});
|
|
178
|
+
export const callEdgeSchema = z.object({ from: z.string(), to: z.string(), pairs: z.array(callPairSchema) });
|
|
179
|
+
export const topologyWindowSchema = z.object({
|
|
180
|
+
seconds: z.number(),
|
|
181
|
+
since: z.string(),
|
|
182
|
+
calls: z.number(),
|
|
183
|
+
unanswered: z.number().default(0),
|
|
184
|
+
});
|
|
185
|
+
export const instanceTopologyDocumentSchema = z.object({
|
|
186
|
+
service: z.object({ name: z.string(), runtime: z.string(), instance: z.string(), startedAt: z.string() }),
|
|
187
|
+
window: topologyWindowSchema,
|
|
188
|
+
nodes: z.array(topologyNodeSchema),
|
|
189
|
+
declared: z.array(declaredEdgeSchema),
|
|
190
|
+
calls: z.array(callEdgeSchema),
|
|
191
|
+
});
|
|
192
|
+
export const instanceStatusSchema = z.enum(["ok", "unreachable", "unsupported", "failed"]);
|
|
193
|
+
export const instanceTopologySchema = z.object({
|
|
194
|
+
pod: z.string(),
|
|
195
|
+
status: instanceStatusSchema,
|
|
196
|
+
problem: optional(z.string()),
|
|
197
|
+
runtime: optional(z.string()),
|
|
198
|
+
readAt: optional(z.string()),
|
|
199
|
+
});
|
|
200
|
+
export const topologyDifferenceSchema = z.object({ node: z.string(), presentOn: z.array(z.string()) });
|
|
201
|
+
/** `GET /services/{projectId}/{name}/topology`: every instance's topology, merged. */
|
|
202
|
+
export const serviceTopologySchema = z.object({
|
|
203
|
+
service: z.string(),
|
|
204
|
+
running: z.number().int(),
|
|
205
|
+
contributing: z.number().int(),
|
|
206
|
+
partial: z.boolean(),
|
|
207
|
+
instances: z.array(instanceTopologySchema),
|
|
208
|
+
window: topologyWindowSchema,
|
|
209
|
+
nodes: z.array(topologyNodeSchema),
|
|
210
|
+
declared: z.array(declaredEdgeSchema),
|
|
211
|
+
calls: z.array(callEdgeSchema),
|
|
212
|
+
differences: z.array(topologyDifferenceSchema),
|
|
213
|
+
});
|
|
130
214
|
export const memberSummarySchema = z.object({
|
|
131
215
|
subject: z.string(),
|
|
132
216
|
role: roleSchema,
|
|
@@ -186,6 +270,83 @@ export const setRegistrySchema = z.object({
|
|
|
186
270
|
username: z.string(),
|
|
187
271
|
password: z.string(),
|
|
188
272
|
});
|
|
273
|
+
/** `PUT /projects/{id}/secrets/{name}`: entries merged into a project secret. */
|
|
274
|
+
export const setProjectSecretSchema = z.object({
|
|
275
|
+
entries: z.record(z.string(), z.string()),
|
|
276
|
+
});
|
|
277
|
+
/** A contract as declared on a topic: its name and the fingerprint of its schema document. */
|
|
278
|
+
export const contractSchema = z.object({
|
|
279
|
+
name: z.string(),
|
|
280
|
+
fingerprint: z.string(),
|
|
281
|
+
});
|
|
282
|
+
/** One side of a declared topic as a running service states it, against the declared contract. */
|
|
283
|
+
export const topicCheckSchema = z.object({
|
|
284
|
+
topic: optional(z.string()),
|
|
285
|
+
service: z.string(),
|
|
286
|
+
component: z.string(),
|
|
287
|
+
direction: z.string(),
|
|
288
|
+
stated: optional(z.string()),
|
|
289
|
+
state: z.string(),
|
|
290
|
+
});
|
|
291
|
+
/**
|
|
292
|
+
* `PUT /projects/{id}/topics/{name}`: the partitions a declared topic has, whether the broker keeps
|
|
293
|
+
* only the last message under each key, and the contract it carries with its schema document.
|
|
294
|
+
*/
|
|
295
|
+
export const topicDeclarationRequestSchema = z.object({
|
|
296
|
+
partitions: z.number().int(),
|
|
297
|
+
compacted: z.boolean().default(false),
|
|
298
|
+
contract: optional(z.object({ name: z.string(), schema: z.unknown() })),
|
|
299
|
+
});
|
|
300
|
+
/** A topic a project declares, and how far the platform has got with it. */
|
|
301
|
+
export const projectTopicSchema = z.object({
|
|
302
|
+
name: z.string(),
|
|
303
|
+
partitions: z.number().int(),
|
|
304
|
+
phase: optional(z.string()),
|
|
305
|
+
detail: optional(z.string()),
|
|
306
|
+
compacted: z.boolean().default(false),
|
|
307
|
+
contract: optional(contractSchema),
|
|
308
|
+
checks: z.array(topicCheckSchema).default([]),
|
|
309
|
+
});
|
|
310
|
+
/**
|
|
311
|
+
* A topic source as a service's instances report it: what reads which topic under which group, the
|
|
312
|
+
* declared broker and contract it states, `lag` (messages past the last one handled, as of the last
|
|
313
|
+
* poll) and `failing` (the reason of the change being delivered again, until one succeeds).
|
|
314
|
+
*/
|
|
315
|
+
export const topicSourceReportSchema = z.object({
|
|
316
|
+
kind: z.string(),
|
|
317
|
+
component: z.string(),
|
|
318
|
+
topic: z.string(),
|
|
319
|
+
group: z.string(),
|
|
320
|
+
start: z.string(),
|
|
321
|
+
version: z.number().int(),
|
|
322
|
+
recordedVersion: optional(z.number().int()),
|
|
323
|
+
behind: z.boolean().default(false),
|
|
324
|
+
broker: optional(z.string()),
|
|
325
|
+
contract: optional(z.string()),
|
|
326
|
+
lag: optional(z.number().int()),
|
|
327
|
+
failing: optional(z.string()),
|
|
328
|
+
});
|
|
329
|
+
/** `PUT /projects/{id}/brokers/{name}`: a broker declared beside the installation's, and how it is reached. */
|
|
330
|
+
export const brokerDeclarationRequestSchema = z.object({
|
|
331
|
+
bootstrap: z.string(),
|
|
332
|
+
shape: z.string(),
|
|
333
|
+
secret: z.string(),
|
|
334
|
+
});
|
|
335
|
+
/** A broker a project declares, as listed: where it is, the shape of its credential, and the project secret holding it. */
|
|
336
|
+
export const projectBrokerSchema = z.object({
|
|
337
|
+
name: z.string(),
|
|
338
|
+
bootstrap: z.string(),
|
|
339
|
+
shape: z.string(),
|
|
340
|
+
secret: z.string(),
|
|
341
|
+
declaredAt: optional(z.string()),
|
|
342
|
+
});
|
|
343
|
+
/** A project secret as the control plane lists it: entries' names, never a value. */
|
|
344
|
+
export const projectSecretSummarySchema = z.object({
|
|
345
|
+
name: z.string(),
|
|
346
|
+
entries: z.array(z.string()),
|
|
347
|
+
setAt: optional(z.string()),
|
|
348
|
+
setBy: optional(z.string()),
|
|
349
|
+
});
|
|
189
350
|
/** Every schema by the Scala type's name, as the fixture files name them. */
|
|
190
351
|
export const schemasByType = {
|
|
191
352
|
AuthDiscovery: authDiscoverySchema,
|
|
@@ -198,11 +359,17 @@ export const schemasByType = {
|
|
|
198
359
|
RegistrySummary: registrySummarySchema,
|
|
199
360
|
ProjectDetail: projectDetailSchema,
|
|
200
361
|
ProjectSummary: projectSummarySchema,
|
|
362
|
+
MountStatus: mountStatusSchema,
|
|
201
363
|
ServiceStatus: serviceStatusSchema,
|
|
202
364
|
HistoryActor: historyActorSchema,
|
|
203
365
|
HistoryEntry: historyEntrySchema,
|
|
366
|
+
RollbackRequest: rollbackRequestSchema,
|
|
367
|
+
RolledBack: rolledBackSchema,
|
|
204
368
|
InstanceLogs: instanceLogsSchema,
|
|
205
369
|
LogsResponse: logsResponseSchema,
|
|
370
|
+
InstanceTopologyDocument: instanceTopologyDocumentSchema,
|
|
371
|
+
InstanceTopology: instanceTopologySchema,
|
|
372
|
+
ServiceTopology: serviceTopologySchema,
|
|
206
373
|
MemberSummary: memberSummarySchema,
|
|
207
374
|
InvitationSummary: invitationSummarySchema,
|
|
208
375
|
MembersResponse: membersResponseSchema,
|
|
@@ -218,4 +385,11 @@ export const schemasByType = {
|
|
|
218
385
|
Repair: repairSchema,
|
|
219
386
|
CreateDeployToken: createDeployTokenSchema,
|
|
220
387
|
SetRegistry: setRegistrySchema,
|
|
388
|
+
SetProjectSecret: setProjectSecretSchema,
|
|
389
|
+
ProjectSecretSummary: projectSecretSummarySchema,
|
|
390
|
+
TopicDeclarationRequest: topicDeclarationRequestSchema,
|
|
391
|
+
ProjectTopic: projectTopicSchema,
|
|
392
|
+
Contract: contractSchema,
|
|
393
|
+
BrokerDeclarationRequest: brokerDeclarationRequestSchema,
|
|
394
|
+
ProjectBroker: projectBrokerSchema,
|
|
221
395
|
};
|
package/dist/context.d.ts
CHANGED
|
@@ -69,15 +69,113 @@ export interface ActionRefusal {
|
|
|
69
69
|
*/
|
|
70
70
|
export declare function act<T>(ctx: ConsoleContext, intent: string, form: FormData, work: () => Promise<T>): Promise<T | ReturnType<typeof data<ActionRefusal>>>;
|
|
71
71
|
export declare const text: (form: FormData, name: string) => string;
|
|
72
|
+
/** The rail's areas, in the order it shows them. */
|
|
73
|
+
export declare const areas: readonly ["organizations", "projects", "services", "members", "tokens"];
|
|
74
|
+
export type Area = (typeof areas)[number];
|
|
75
|
+
export interface Crumb {
|
|
76
|
+
label: string;
|
|
77
|
+
to?: string;
|
|
78
|
+
}
|
|
79
|
+
/** One entry of the shell's listing: a project's service, or an organization's project. */
|
|
80
|
+
export type ListingItem = {
|
|
81
|
+
kind: "service";
|
|
82
|
+
key: string;
|
|
83
|
+
name: string;
|
|
84
|
+
to: string;
|
|
85
|
+
lifecycle: string;
|
|
86
|
+
confirmed: boolean;
|
|
87
|
+
ready: number;
|
|
88
|
+
desired: number;
|
|
89
|
+
} | {
|
|
90
|
+
kind: "project";
|
|
91
|
+
key: string;
|
|
92
|
+
name: string;
|
|
93
|
+
to: string;
|
|
94
|
+
services: number;
|
|
95
|
+
};
|
|
96
|
+
/**
|
|
97
|
+
* What the shell around a page shows: the area the rail marks, where the bar says the member is,
|
|
98
|
+
* the page's primary operation, the organization and project the rail's areas open, and the listing
|
|
99
|
+
* beside the page. A page with no listing (the front page) leaves it out.
|
|
100
|
+
*/
|
|
101
|
+
export interface ShellData {
|
|
102
|
+
area?: Area;
|
|
103
|
+
crumbs: Crumb[];
|
|
104
|
+
primary?: {
|
|
105
|
+
label: string;
|
|
106
|
+
to: string;
|
|
107
|
+
operation?: Operation;
|
|
108
|
+
};
|
|
109
|
+
/** `manages`: the member may manage its members and deploy tokens (an owner, or an administrator). */
|
|
110
|
+
organization?: {
|
|
111
|
+
id: string;
|
|
112
|
+
name: string;
|
|
113
|
+
manages: boolean;
|
|
114
|
+
};
|
|
115
|
+
project?: {
|
|
116
|
+
id: string;
|
|
117
|
+
name: string;
|
|
118
|
+
};
|
|
119
|
+
listing?: {
|
|
120
|
+
title: string;
|
|
121
|
+
label: string;
|
|
122
|
+
items: ListingItem[];
|
|
123
|
+
current?: string;
|
|
124
|
+
};
|
|
125
|
+
}
|
|
72
126
|
/** What every package page's loader data carries for the pages and the host's layout. */
|
|
73
127
|
export interface ConsolePageData {
|
|
74
128
|
mount: string;
|
|
75
129
|
principal: Principal | null;
|
|
76
130
|
hidden: Operation[];
|
|
131
|
+
shell?: ShellData;
|
|
77
132
|
}
|
|
133
|
+
/** The listing of a project's services, in name order, marking the one being read. */
|
|
134
|
+
export declare function servicesListing(project: {
|
|
135
|
+
id: string;
|
|
136
|
+
name: string;
|
|
137
|
+
}, services: {
|
|
138
|
+
name: string;
|
|
139
|
+
lifecycle: string;
|
|
140
|
+
confirmed: boolean;
|
|
141
|
+
readyInstances: number;
|
|
142
|
+
desiredInstances: number;
|
|
143
|
+
}[], current?: string): NonNullable<ShellData["listing"]>;
|
|
144
|
+
/** The listing of an organization's projects, in name order, marking the one being read. */
|
|
145
|
+
export declare function projectsListing(organization: {
|
|
146
|
+
id: string;
|
|
147
|
+
name: string;
|
|
148
|
+
}, projects: {
|
|
149
|
+
id: string;
|
|
150
|
+
name: string;
|
|
151
|
+
organizationId: string;
|
|
152
|
+
services: number;
|
|
153
|
+
}[], current?: string): NonNullable<ShellData["listing"]>;
|
|
78
154
|
/**
|
|
79
155
|
* The page data every package page returns. It is also what makes a page require a session: nobody
|
|
80
156
|
* signed in is `SignInRequired`, which `guard` turns into the trip to sign in, even for a page whose
|
|
81
157
|
* loader asks the control plane nothing.
|
|
82
158
|
*/
|
|
83
159
|
export declare function pageData(ctx: ConsoleContext): Promise<ConsolePageData>;
|
|
160
|
+
/** A page's data with the shell around it; loaders call it once everything the shell names is read. */
|
|
161
|
+
export declare function withShell(page: ConsolePageData, shell: ShellData): ConsolePageData;
|
|
162
|
+
type OrganizationLike = {
|
|
163
|
+
id: string;
|
|
164
|
+
name: string;
|
|
165
|
+
role?: "owner" | "member" | null;
|
|
166
|
+
};
|
|
167
|
+
/** The shell of a page that belongs to an organization: its projects beside the page. */
|
|
168
|
+
export declare function organizationShell(page: ConsolePageData, o: OrganizationLike, projects: {
|
|
169
|
+
id: string;
|
|
170
|
+
name: string;
|
|
171
|
+
organizationId: string;
|
|
172
|
+
services: number;
|
|
173
|
+
}[], area: Area, tail: Crumb[], primary?: ShellData["primary"]): ConsolePageData;
|
|
174
|
+
/** The shell of a page that belongs to a project: its services beside the page. */
|
|
175
|
+
export declare function projectShell(page: ConsolePageData, o: OrganizationLike, p: {
|
|
176
|
+
id: string;
|
|
177
|
+
name: string;
|
|
178
|
+
}, services: Parameters<typeof servicesListing>[1], tail: Crumb[], primary?: ShellData["primary"], current?: string): ConsolePageData;
|
|
179
|
+
/** A service's pages put applying a new descriptor first. */
|
|
180
|
+
export declare function applyPrimary(projectId: string, name: string): NonNullable<ShellData["primary"]>;
|
|
181
|
+
export {};
|
package/dist/context.js
CHANGED
|
@@ -143,6 +143,40 @@ export const text = (form, name) => {
|
|
|
143
143
|
const v = form.get(name);
|
|
144
144
|
return typeof v === "string" ? v.trim() : "";
|
|
145
145
|
};
|
|
146
|
+
/** The rail's areas, in the order it shows them. */
|
|
147
|
+
export const areas = ["organizations", "projects", "services", "members", "tokens"];
|
|
148
|
+
const byName = (a, b) => a.name.localeCompare(b.name);
|
|
149
|
+
/** The listing of a project's services, in name order, marking the one being read. */
|
|
150
|
+
export function servicesListing(project, services, current) {
|
|
151
|
+
const base = `projects/${encodeURIComponent(project.id)}/services/`;
|
|
152
|
+
return {
|
|
153
|
+
title: project.name,
|
|
154
|
+
label: `Services in ${project.name}`,
|
|
155
|
+
current,
|
|
156
|
+
items: [...services].sort(byName).map((s) => ({
|
|
157
|
+
kind: "service",
|
|
158
|
+
key: s.name,
|
|
159
|
+
name: s.name,
|
|
160
|
+
to: base + encodeURIComponent(s.name),
|
|
161
|
+
lifecycle: s.lifecycle,
|
|
162
|
+
confirmed: s.confirmed,
|
|
163
|
+
ready: s.readyInstances,
|
|
164
|
+
desired: s.desiredInstances,
|
|
165
|
+
})),
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/** The listing of an organization's projects, in name order, marking the one being read. */
|
|
169
|
+
export function projectsListing(organization, projects, current) {
|
|
170
|
+
return {
|
|
171
|
+
title: organization.name,
|
|
172
|
+
label: `Projects in ${organization.name}`,
|
|
173
|
+
current,
|
|
174
|
+
items: projects
|
|
175
|
+
.filter((p) => p.organizationId === organization.id)
|
|
176
|
+
.sort(byName)
|
|
177
|
+
.map((p) => ({ kind: "project", key: p.id, name: p.name, to: `projects/${encodeURIComponent(p.id)}`, services: p.services })),
|
|
178
|
+
};
|
|
179
|
+
}
|
|
146
180
|
/**
|
|
147
181
|
* The page data every package page returns. It is also what makes a page require a session: nobody
|
|
148
182
|
* signed in is `SignInRequired`, which `guard` turns into the trip to sign in, even for a page whose
|
|
@@ -154,3 +188,42 @@ export async function pageData(ctx) {
|
|
|
154
188
|
throw new SignInRequired();
|
|
155
189
|
return { mount: ctx.mount, principal, hidden: ctx.extensions.hidden ?? [] };
|
|
156
190
|
}
|
|
191
|
+
/** A page's data with the shell around it; loaders call it once everything the shell names is read. */
|
|
192
|
+
export function withShell(page, shell) {
|
|
193
|
+
return { ...page, shell };
|
|
194
|
+
}
|
|
195
|
+
/** The shell of a page that belongs to an organization: its projects beside the page. */
|
|
196
|
+
export function organizationShell(page, o, projects, area, tail, primary) {
|
|
197
|
+
const orgPath = `organizations/${encodeURIComponent(o.id)}`;
|
|
198
|
+
return withShell(page, {
|
|
199
|
+
area,
|
|
200
|
+
crumbs: [{ label: "Organizations", to: "" }, { label: o.name, to: orgPath }, ...tail],
|
|
201
|
+
primary,
|
|
202
|
+
organization: { id: o.id, name: o.name, manages: o.role === "owner" || (page.principal?.platformAdmin ?? false) },
|
|
203
|
+
listing: projectsListing(o, projects),
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
/** The shell of a page that belongs to a project: its services beside the page. */
|
|
207
|
+
export function projectShell(page, o, p, services, tail, primary, current) {
|
|
208
|
+
return withShell(page, {
|
|
209
|
+
area: current === undefined ? "projects" : "services",
|
|
210
|
+
crumbs: [
|
|
211
|
+
{ label: "Organizations", to: "" },
|
|
212
|
+
{ label: o.name, to: `organizations/${encodeURIComponent(o.id)}` },
|
|
213
|
+
{ label: p.name, to: `projects/${encodeURIComponent(p.id)}` },
|
|
214
|
+
...tail,
|
|
215
|
+
],
|
|
216
|
+
primary,
|
|
217
|
+
organization: { id: o.id, name: o.name, manages: o.role === "owner" || (page.principal?.platformAdmin ?? false) },
|
|
218
|
+
project: { id: p.id, name: p.name },
|
|
219
|
+
listing: servicesListing(p, services, current),
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
/** A service's pages put applying a new descriptor first. */
|
|
223
|
+
export function applyPrimary(projectId, name) {
|
|
224
|
+
return {
|
|
225
|
+
label: "Apply a new descriptor",
|
|
226
|
+
to: `projects/${encodeURIComponent(projectId)}/services/apply?name=${encodeURIComponent(name)}`,
|
|
227
|
+
operation: "service.apply",
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -14,6 +14,8 @@ export declare function Panels<K extends PanelKind>({ kind, entity, loaded }: {
|
|
|
14
14
|
entity: PanelEntities[K];
|
|
15
15
|
loaded?: LoadedPanels;
|
|
16
16
|
}): import("react").JSX.Element | null;
|
|
17
|
+
/** Whether the host added anything beside an operation, so a page knows to make room for it. */
|
|
18
|
+
export declare function useHostActions(operation: Operation): boolean;
|
|
17
19
|
export declare function HostActions({ operation, entity }: {
|
|
18
20
|
operation: Operation;
|
|
19
21
|
entity?: unknown;
|
|
@@ -42,6 +42,11 @@ export function Panels({ kind, entity, loaded }) {
|
|
|
42
42
|
return (_jsxs("section", { className: "ac-panel", "aria-labelledby": `panel-${p.id}`, "data-panel": p.id, children: [_jsx("h2", { id: `panel-${p.id}`, children: p.title }), result && !result.ok ? (_jsxs("p", { className: "ac-panel-failed", children: [p.title, " could not be loaded: ", result.error] })) : (_jsx(PanelBoundary, { title: p.title, children: _jsx(p.Component, { entity: entity, data: result?.ok ? result.data : undefined }) }))] }, p.id));
|
|
43
43
|
}) }));
|
|
44
44
|
}
|
|
45
|
+
/** Whether the host added anything beside an operation, so a page knows to make room for it. */
|
|
46
|
+
export function useHostActions(operation) {
|
|
47
|
+
const { extensions } = useConsole();
|
|
48
|
+
return (extensions.actions?.[operation]?.length ?? 0) > 0;
|
|
49
|
+
}
|
|
45
50
|
export function HostActions({ operation, entity }) {
|
|
46
51
|
const { extensions } = useConsole();
|
|
47
52
|
const actions = extensions.actions?.[operation] ?? [];
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { ComponentType } from "react";
|
|
2
2
|
import type { OrganizationSummary, ProjectDetail, ServiceStatus } from "../client/schemas.ts";
|
|
3
3
|
/** Every operation the console offers, by the name a host uses to add beside it or hide it. */
|
|
4
|
-
export declare const operations: readonly ["organization.create", "organization.rename", "organization.delete", "organization.disable", "organization.enable", "organization.quota.set", "organization.quota.clear", "member.invite", "member.role", "member.remove", "invitation.withdraw", "member.repair", "token.create", "token.revoke", "project.create", "project.rename", "project.delete", "registry.set", "registry.clear", "service.apply", "service.pause", "service.resume", "service.restart", "service.expose", "service.unexpose", "service.delete", "service.logs"];
|
|
4
|
+
export declare const operations: readonly ["organization.create", "organization.rename", "organization.delete", "organization.disable", "organization.enable", "organization.quota.set", "organization.quota.clear", "member.invite", "member.role", "member.remove", "invitation.withdraw", "member.repair", "token.create", "token.revoke", "project.create", "project.rename", "project.delete", "registry.set", "registry.clear", "project-secret.set", "project-secret.unset", "project-topic.set", "project-topic.unset", "project-broker.set", "project-broker.unset", "service.apply", "service.pause", "service.resume", "service.restart", "service.rollback", "service.expose", "service.unexpose", "service.delete", "service.logs"];
|
|
5
5
|
export type Operation = (typeof operations)[number];
|
|
6
6
|
export interface PanelEntities {
|
|
7
7
|
organization: OrganizationSummary;
|
package/dist/extensions/types.js
CHANGED
|
@@ -19,10 +19,17 @@ export const operations = [
|
|
|
19
19
|
"project.delete",
|
|
20
20
|
"registry.set",
|
|
21
21
|
"registry.clear",
|
|
22
|
+
"project-secret.set",
|
|
23
|
+
"project-secret.unset",
|
|
24
|
+
"project-topic.set",
|
|
25
|
+
"project-topic.unset",
|
|
26
|
+
"project-broker.set",
|
|
27
|
+
"project-broker.unset",
|
|
22
28
|
"service.apply",
|
|
23
29
|
"service.pause",
|
|
24
30
|
"service.resume",
|
|
25
31
|
"service.restart",
|
|
32
|
+
"service.rollback",
|
|
26
33
|
"service.expose",
|
|
27
34
|
"service.unexpose",
|
|
28
35
|
"service.delete",
|
|
Binary file
|
|
Binary file
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
-
export { ConsoleProvider, useConsole, ConsoleLink, ConsoleForm } from "./ui/console.tsx";
|
|
1
|
+
export { ConsoleProvider, useConsole, ConsoleLink, ConsoleForm, Breadcrumbs, Field, Submit, OperationForm } from "./ui/console.tsx";
|
|
2
2
|
export type { UseConsole } from "./ui/console.tsx";
|
|
3
|
-
export
|
|
3
|
+
export { Shell, Backdrop, Bar, Rail, Listing, Inspector, Page, SectionTitle, SegmentedLinks, ServiceSections } from "./ui/shell.tsx";
|
|
4
|
+
export type { SegmentLink, ServiceSection } from "./ui/shell.tsx";
|
|
5
|
+
export { Shape } from "./ui/shape.tsx";
|
|
6
|
+
export { Lifecycle } from "./ui/status.tsx";
|
|
7
|
+
export { button } from "./ui/primitives/button.ts";
|
|
8
|
+
export { Switch } from "./ui/primitives/switch.tsx";
|
|
9
|
+
export { Select } from "./ui/primitives/select.tsx";
|
|
10
|
+
export type { Area, Crumb, ListingItem, Principal, ShellData } from "./context.ts";
|
|
4
11
|
export { operations } from "./extensions/types.ts";
|
|
5
12
|
export type { Action, ConsoleExtensions, Operation, Panel, PanelKind, PanelLoadContext } from "./extensions/types.ts";
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
// The browser-safe entry point: what a host's layout and its own components use. Everything that
|
|
2
2
|
// runs only on the server — the routes table, the middleware, sessions, the server itself — is
|
|
3
3
|
// `ankka-console/server`, so a host's browser bundle never resolves a Node module.
|
|
4
|
-
export { ConsoleProvider, useConsole, ConsoleLink, ConsoleForm } from "./ui/console.js";
|
|
4
|
+
export { ConsoleProvider, useConsole, ConsoleLink, ConsoleForm, Breadcrumbs, Field, Submit, OperationForm } from "./ui/console.js";
|
|
5
|
+
export { Shell, Backdrop, Bar, Rail, Listing, Inspector, Page, SectionTitle, SegmentedLinks, ServiceSections } from "./ui/shell.js";
|
|
6
|
+
export { Shape } from "./ui/shape.js";
|
|
7
|
+
export { Lifecycle } from "./ui/status.js";
|
|
8
|
+
export { button } from "./ui/primitives/button.js";
|
|
9
|
+
export { Switch } from "./ui/primitives/switch.js";
|
|
10
|
+
export { Select } from "./ui/primitives/select.js";
|
|
5
11
|
export { operations } from "./extensions/types.js";
|
|
@@ -5,14 +5,19 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
|
|
|
5
5
|
* than passing silently through single sign-on. Only a POST signs anyone out.
|
|
6
6
|
*/
|
|
7
7
|
import { redirect, useLoaderData } from "react-router";
|
|
8
|
-
import { pageData, useConsoleContext } from "../context.js";
|
|
8
|
+
import { pageData, useConsoleContext, withShell } from "../context.js";
|
|
9
9
|
import { ConsoleErrorBoundary } from "../ui/errors.js";
|
|
10
10
|
import { ConsoleForm, ConsoleLink, Submit } from "../ui/console.js";
|
|
11
|
+
import { Page } from "../ui/shell.js";
|
|
12
|
+
import { button } from "../ui/primitives/button.js";
|
|
11
13
|
export const meta = () => [{ title: "Sign out · ankka" }];
|
|
12
14
|
export async function loader({ request, context }) {
|
|
13
15
|
const ctx = useConsoleContext(context);
|
|
14
16
|
const signedIn = (await ctx.runtime.store.read(request)) !== null;
|
|
15
|
-
|
|
17
|
+
const page = await pageData(ctx)
|
|
18
|
+
.then((p) => withShell(p, { area: "organizations", crumbs: [{ label: "Organizations", to: "" }, { label: "Sign out" }] }))
|
|
19
|
+
.catch(() => ({ mount: ctx.mount, principal: null, hidden: [] }));
|
|
20
|
+
return { console: page, signedIn };
|
|
16
21
|
}
|
|
17
22
|
export async function action({ request, context }) {
|
|
18
23
|
const ctx = useConsoleContext(context);
|
|
@@ -32,6 +37,6 @@ export async function action({ request, context }) {
|
|
|
32
37
|
}
|
|
33
38
|
export default function SignOut() {
|
|
34
39
|
const { signedIn } = useLoaderData();
|
|
35
|
-
return (_jsx(
|
|
40
|
+
return (_jsx(Page, { children: signedIn ? (_jsxs(_Fragment, { children: [_jsx("h1", { children: "Sign out" }), _jsxs(ConsoleForm, { to: "auth/sign-out", className: "ac-card", children: [_jsx("p", { children: "Sign out of this console and of the identity provider." }), _jsx("div", { children: _jsx(Submit, { primary: true, children: "Sign out" }) })] })] })) : (_jsxs(_Fragment, { children: [_jsx("h1", { children: "You are signed out" }), _jsx("p", { children: "Your session in this console and in the identity provider has ended." }), _jsx("p", { children: _jsx(ConsoleLink, { to: "auth/sign-in", className: button({ variant: "primary" }), children: "Sign in" }) })] })) }));
|
|
36
41
|
}
|
|
37
42
|
export const ErrorBoundary = ConsoleErrorBoundary;
|
package/dist/routes/front.js
CHANGED
|
@@ -4,22 +4,28 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
* yours with your role in each, or every one of them for a platform administrator.
|
|
5
5
|
*/
|
|
6
6
|
import { useLoaderData } from "react-router";
|
|
7
|
-
import { guard, pageData, useConsoleContext } from "../context.js";
|
|
7
|
+
import { guard, pageData, useConsoleContext, withShell } from "../context.js";
|
|
8
8
|
import { ConsoleErrorBoundary } from "../ui/errors.js";
|
|
9
|
-
import { ConsoleLink
|
|
10
|
-
import {
|
|
9
|
+
import { ConsoleLink } from "../ui/console.js";
|
|
10
|
+
import { Page } from "../ui/shell.js";
|
|
11
|
+
import { HostActions, useHostActions } from "../extensions/render.js";
|
|
11
12
|
export const meta = () => [{ title: "Organizations · ankka" }];
|
|
12
13
|
export async function loader({ context }) {
|
|
13
14
|
const ctx = useConsoleContext(context);
|
|
14
15
|
return guard(ctx, async () => {
|
|
15
16
|
const [whoami, organizations] = await Promise.all([ctx.client.whoami(), ctx.client.listOrganizations()]);
|
|
16
|
-
|
|
17
|
+
const shell = {
|
|
18
|
+
area: "organizations",
|
|
19
|
+
crumbs: [{ label: "Organizations" }],
|
|
20
|
+
primary: { label: "Create an organization", to: "organizations/new", operation: "organization.create" },
|
|
21
|
+
};
|
|
22
|
+
return { console: withShell(await pageData(ctx), shell), whoami, organizations };
|
|
17
23
|
});
|
|
18
24
|
}
|
|
19
25
|
const roleWords = { owner: "Owner", member: "Member" };
|
|
20
26
|
export default function Front() {
|
|
21
27
|
const { whoami, organizations } = useLoaderData();
|
|
22
|
-
const
|
|
23
|
-
return (_jsxs("
|
|
28
|
+
const hostActions = useHostActions("organization.create");
|
|
29
|
+
return (_jsxs(Page, { inspector: hostActions ? (_jsx("div", { className: "ac-ops", children: _jsx(HostActions, { operation: "organization.create" }) })) : null, children: [_jsx("h1", { children: "Organizations" }), _jsxs("p", { className: "ac-lede", children: ["Signed in as ", _jsx("strong", { children: whoami.name ?? whoami.email ?? whoami.subject }), whoami.email && whoami.name ? ` (${whoami.email})` : null, whoami.platformAdmin ? ", a platform administrator: you see every organization in this installation." : "."] }), organizations.length === 0 ? (_jsxs("p", { className: "ac-empty", children: ["You are not a member of any organization yet. Create one, or ask an owner to invite ", whoami.email ?? "you", "."] })) : (_jsx("div", { className: "ac-card ac-table-wrap", children: _jsxs("table", { className: "ac-table", children: [_jsx("caption", { className: "ac-visually-hidden", children: "Organizations you can see" }), _jsx("thead", { children: _jsxs("tr", { children: [_jsx("th", { scope: "col", children: "Name" }), _jsx("th", { scope: "col", children: "Id" }), _jsx("th", { scope: "col", children: "Your role" }), _jsx("th", { scope: "col", className: "ac-num", children: "Projects" }), _jsx("th", { scope: "col", children: "State" })] }) }), _jsx("tbody", { children: organizations.map((o) => (_jsxs("tr", { children: [_jsx("td", { children: _jsx(ConsoleLink, { to: `organizations/${encodeURIComponent(o.id)}`, children: o.name }) }), _jsx("td", { children: o.id }), _jsx("td", { children: o.role ? roleWords[o.role] : "Administrator (not a member)" }), _jsx("td", { className: "ac-num", children: o.projects }), _jsx("td", { children: o.disabled ? "Disabled" : "Active" })] }, o.id))) })] }) }))] }));
|
|
24
30
|
}
|
|
25
31
|
export const ErrorBoundary = ConsoleErrorBoundary;
|
package/dist/routes/logs.d.ts
CHANGED
|
@@ -20,6 +20,40 @@ export declare function loader({ request, params, context }: LoaderFunctionArgs)
|
|
|
20
20
|
paused: boolean;
|
|
21
21
|
hosting: string;
|
|
22
22
|
protocol: string | undefined;
|
|
23
|
+
mounts: {
|
|
24
|
+
path: string;
|
|
25
|
+
service: string;
|
|
26
|
+
state: string;
|
|
27
|
+
}[];
|
|
28
|
+
callers: string[];
|
|
29
|
+
processPort: number | undefined;
|
|
30
|
+
broker: string | undefined;
|
|
31
|
+
undeclaredTopics: string[] | undefined;
|
|
32
|
+
topicChecks: {
|
|
33
|
+
topic: string | undefined;
|
|
34
|
+
service: string;
|
|
35
|
+
component: string;
|
|
36
|
+
direction: string;
|
|
37
|
+
stated: string | undefined;
|
|
38
|
+
state: string;
|
|
39
|
+
}[] | undefined;
|
|
40
|
+
topicSources: {
|
|
41
|
+
kind: string;
|
|
42
|
+
component: string;
|
|
43
|
+
topic: string;
|
|
44
|
+
group: string;
|
|
45
|
+
start: string;
|
|
46
|
+
version: number;
|
|
47
|
+
recordedVersion: number | undefined;
|
|
48
|
+
behind: boolean;
|
|
49
|
+
broker: string | undefined;
|
|
50
|
+
contract: string | undefined;
|
|
51
|
+
lag: number | undefined;
|
|
52
|
+
failing: string | undefined;
|
|
53
|
+
}[] | undefined;
|
|
54
|
+
objectStorage: string | undefined;
|
|
55
|
+
bucket: string | undefined;
|
|
56
|
+
bucketAddress: string | undefined;
|
|
23
57
|
};
|
|
24
58
|
project: {
|
|
25
59
|
id: string;
|
|
@@ -58,6 +92,7 @@ export declare function loader({ request, params, context }: LoaderFunctionArgs)
|
|
|
58
92
|
query: {
|
|
59
93
|
instance: string | undefined;
|
|
60
94
|
previous: boolean;
|
|
95
|
+
platform: boolean;
|
|
61
96
|
tail: number;
|
|
62
97
|
since: number | undefined;
|
|
63
98
|
};
|