ankka-console 0.10.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 (99) hide show
  1. package/README.md +32 -0
  2. package/dist/auth/oidc.d.ts +64 -0
  3. package/dist/auth/oidc.js +199 -0
  4. package/dist/client/control-plane.d.ts +67 -0
  5. package/dist/client/control-plane.js +208 -0
  6. package/dist/client/errors.d.ts +26 -0
  7. package/dist/client/errors.js +59 -0
  8. package/dist/client/index.d.ts +4 -0
  9. package/dist/client/index.js +3 -0
  10. package/dist/client/schemas.d.ts +365 -0
  11. package/dist/client/schemas.js +221 -0
  12. package/dist/context.d.ts +83 -0
  13. package/dist/context.js +156 -0
  14. package/dist/extensions/render.d.ts +20 -0
  15. package/dist/extensions/render.js +49 -0
  16. package/dist/extensions/types.d.ts +44 -0
  17. package/dist/extensions/types.js +30 -0
  18. package/dist/index.d.ts +5 -0
  19. package/dist/index.js +5 -0
  20. package/dist/middleware.d.ts +14 -0
  21. package/dist/middleware.js +48 -0
  22. package/dist/options.d.ts +45 -0
  23. package/dist/options.js +39 -0
  24. package/dist/routes/auth.callback.d.ts +11 -0
  25. package/dist/routes/auth.callback.js +43 -0
  26. package/dist/routes/auth.sign-in.d.ts +11 -0
  27. package/dist/routes/auth.sign-in.js +29 -0
  28. package/dist/routes/auth.sign-out.d.ts +19 -0
  29. package/dist/routes/auth.sign-out.js +37 -0
  30. package/dist/routes/front.d.ts +41 -0
  31. package/dist/routes/front.js +25 -0
  32. package/dist/routes/logs.d.ts +67 -0
  33. package/dist/routes/logs.js +72 -0
  34. package/dist/routes/members.d.ts +45 -0
  35. package/dist/routes/members.js +59 -0
  36. package/dist/routes/organization-new.d.ts +14 -0
  37. package/dist/routes/organization-new.js +30 -0
  38. package/dist/routes/organization.d.ts +43 -0
  39. package/dist/routes/organization.js +82 -0
  40. package/dist/routes/project-new.d.ts +27 -0
  41. package/dist/routes/project-new.js +27 -0
  42. package/dist/routes/project.d.ts +59 -0
  43. package/dist/routes/project.js +61 -0
  44. package/dist/routes/service-apply.d.ts +44 -0
  45. package/dist/routes/service-apply.js +65 -0
  46. package/dist/routes/service.d.ts +71 -0
  47. package/dist/routes/service.js +73 -0
  48. package/dist/routes/stream.project.d.ts +3 -0
  49. package/dist/routes/stream.project.js +25 -0
  50. package/dist/routes/stream.service.d.ts +9 -0
  51. package/dist/routes/stream.service.js +68 -0
  52. package/dist/routes/tokens.d.ts +47 -0
  53. package/dist/routes/tokens.js +50 -0
  54. package/dist/routes.d.ts +28 -0
  55. package/dist/routes.js +48 -0
  56. package/dist/runtime.d.ts +28 -0
  57. package/dist/runtime.js +77 -0
  58. package/dist/server/create-console-server.d.ts +33 -0
  59. package/dist/server/create-console-server.js +203 -0
  60. package/dist/server/index.d.ts +15 -0
  61. package/dist/server/index.js +13 -0
  62. package/dist/server/transport.d.ts +25 -0
  63. package/dist/server/transport.js +97 -0
  64. package/dist/session/cookie-store.d.ts +51 -0
  65. package/dist/session/cookie-store.js +66 -0
  66. package/dist/session/cookies.d.ts +15 -0
  67. package/dist/session/cookies.js +33 -0
  68. package/dist/session/seal.d.ts +12 -0
  69. package/dist/session/seal.js +38 -0
  70. package/dist/session/token-cache.d.ts +21 -0
  71. package/dist/session/token-cache.js +47 -0
  72. package/dist/session/token-source.d.ts +41 -0
  73. package/dist/session/token-source.js +63 -0
  74. package/dist/stream/log-follow.d.ts +18 -0
  75. package/dist/stream/log-follow.js +40 -0
  76. package/dist/stream/registry.d.ts +6 -0
  77. package/dist/stream/registry.js +34 -0
  78. package/dist/stream/sse.d.ts +14 -0
  79. package/dist/stream/sse.js +87 -0
  80. package/dist/styles.css +481 -0
  81. package/dist/testing/fake-control-plane.d.ts +161 -0
  82. package/dist/testing/fake-control-plane.js +715 -0
  83. package/dist/testing/fake-issuer.d.ts +66 -0
  84. package/dist/testing/fake-issuer.js +303 -0
  85. package/dist/testing/index.d.ts +5 -0
  86. package/dist/testing/index.js +3 -0
  87. package/dist/testing/scenarios.d.ts +6 -0
  88. package/dist/testing/scenarios.js +44 -0
  89. package/dist/ui/console.d.ts +60 -0
  90. package/dist/ui/console.js +73 -0
  91. package/dist/ui/errors.d.ts +6 -0
  92. package/dist/ui/errors.js +30 -0
  93. package/dist/ui/refused.d.ts +6 -0
  94. package/dist/ui/refused.js +12 -0
  95. package/dist/ui/status.d.ts +5 -0
  96. package/dist/ui/status.js +23 -0
  97. package/dist/ui/use-stream.d.ts +59 -0
  98. package/dist/ui/use-stream.js +67 -0
  99. package/package.json +79 -0
@@ -0,0 +1,481 @@
1
+ /* The console's pages. Every class is prefixed `ac-`; every colour, face and space is a custom
2
+ property on .ac-root, so a host restyles the pages by overriding properties, not rules.
3
+
4
+ Birch paper and lake ink by day, the lake at night after dark. One accent, duckbill amber, is kept
5
+ for focus and for what is live; red is kept for failure. No web fonts: a console inside a cluster
6
+ must not fetch anything from outside it. */
7
+
8
+ .ac-root {
9
+ --ac-paper: #f1f4f1;
10
+ --ac-surface: #fbfcfb;
11
+ --ac-ink: #13343b;
12
+ --ac-reed: #4f6b56;
13
+ --ac-water: #d5e2de;
14
+ --ac-amber: #e0a526;
15
+ --ac-amber-ink: #6b4a00;
16
+ --ac-red: #a8231b;
17
+ --ac-red-wash: #fbe9e7;
18
+ --ac-green: #1f6b3a;
19
+ --ac-serif: Charter, "Bitstream Charter", "Sitka Text", Cambria, Georgia, serif;
20
+ --ac-sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
21
+ --ac-step: 1rem;
22
+ --ac-measure: 72rem;
23
+ --ac-radius-control: 6px;
24
+
25
+ color: var(--ac-ink);
26
+ background: var(--ac-paper);
27
+ font-family: var(--ac-sans);
28
+ font-size: 1rem;
29
+ line-height: 1.5;
30
+ font-variant-numeric: tabular-nums;
31
+ min-height: 100vh;
32
+ }
33
+
34
+ @media (prefers-color-scheme: dark) {
35
+ .ac-root {
36
+ --ac-paper: #0f2226;
37
+ --ac-surface: #142b30;
38
+ --ac-ink: #e3ecea;
39
+ --ac-reed: #9fbcaa;
40
+ --ac-water: #26434a;
41
+ --ac-amber: #f0b83c;
42
+ --ac-amber-ink: #f0b83c;
43
+ --ac-red: #ff8a80;
44
+ --ac-red-wash: #3a1b19;
45
+ --ac-green: #7fd4a0;
46
+ }
47
+ }
48
+
49
+ .ac-root *,
50
+ .ac-root *::before,
51
+ .ac-root *::after {
52
+ box-sizing: border-box;
53
+ }
54
+
55
+ /* ── Frame ───────────────────────────────────────────────────────────────── */
56
+
57
+ .ac-bar {
58
+ display: flex;
59
+ align-items: baseline;
60
+ gap: var(--ac-step);
61
+ flex-wrap: wrap;
62
+ padding: calc(var(--ac-step) * 0.75) var(--ac-step);
63
+ border-bottom: 1px solid var(--ac-water);
64
+ }
65
+
66
+ .ac-wordmark {
67
+ font-family: var(--ac-serif);
68
+ font-size: 1.5rem;
69
+ font-weight: 700;
70
+ letter-spacing: -0.01em;
71
+ color: var(--ac-ink);
72
+ text-decoration: none;
73
+ }
74
+
75
+ .ac-wordmark-sub {
76
+ font-family: var(--ac-serif);
77
+ font-style: italic;
78
+ color: var(--ac-reed);
79
+ }
80
+
81
+ .ac-bar-person {
82
+ margin-left: auto;
83
+ display: flex;
84
+ align-items: baseline;
85
+ gap: calc(var(--ac-step) * 0.75);
86
+ color: var(--ac-reed);
87
+ }
88
+
89
+ .ac-bar-person form {
90
+ display: inline;
91
+ }
92
+
93
+ .ac-main {
94
+ max-width: var(--ac-measure);
95
+ padding: var(--ac-step);
96
+ margin: 0 auto;
97
+ }
98
+
99
+ .ac-page > * + * {
100
+ margin-top: calc(var(--ac-step) * 1.5);
101
+ }
102
+
103
+ .ac-root h1,
104
+ .ac-root h2 {
105
+ font-family: var(--ac-serif);
106
+ line-height: 1.2;
107
+ margin: 0;
108
+ font-weight: 700;
109
+ }
110
+
111
+ .ac-root h1 {
112
+ font-size: clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem);
113
+ letter-spacing: -0.015em;
114
+ }
115
+
116
+ .ac-root h2 {
117
+ font-size: 1.35rem;
118
+ margin-top: calc(var(--ac-step) * 2.5);
119
+ }
120
+
121
+ .ac-root p {
122
+ margin: 0;
123
+ max-width: 68ch;
124
+ }
125
+
126
+ .ac-lede {
127
+ color: var(--ac-reed);
128
+ }
129
+
130
+ .ac-root a {
131
+ color: inherit;
132
+ text-decoration-color: var(--ac-reed);
133
+ text-underline-offset: 0.2em;
134
+ }
135
+
136
+ .ac-root a:hover {
137
+ text-decoration-color: var(--ac-amber);
138
+ text-decoration-thickness: 2px;
139
+ }
140
+
141
+ .ac-root :focus-visible {
142
+ outline: 3px solid var(--ac-amber);
143
+ outline-offset: 2px;
144
+ border-radius: 2px;
145
+ }
146
+
147
+ /* ── Breadcrumbs ─────────────────────────────────────────────────────────── */
148
+
149
+ .ac-crumbs ol {
150
+ display: flex;
151
+ flex-wrap: wrap;
152
+ gap: 0.25rem;
153
+ list-style: none;
154
+ padding: 0;
155
+ margin: 0;
156
+ color: var(--ac-reed);
157
+ }
158
+
159
+ .ac-crumbs li + li::before {
160
+ content: "›";
161
+ margin-right: 0.25rem;
162
+ }
163
+
164
+ /* ── Tables: every listing is one ───────────────────────────────────────── */
165
+
166
+ .ac-table-wrap {
167
+ overflow-x: auto;
168
+ }
169
+
170
+ .ac-table {
171
+ width: 100%;
172
+ border-collapse: collapse;
173
+ }
174
+
175
+ .ac-table th,
176
+ .ac-table td {
177
+ text-align: left;
178
+ padding: 0.6rem 0.75rem 0.6rem 0;
179
+ border-bottom: 1px solid var(--ac-water);
180
+ vertical-align: top;
181
+ }
182
+
183
+ .ac-table th {
184
+ font-weight: 600;
185
+ color: var(--ac-reed);
186
+ font-size: 0.9rem;
187
+ }
188
+
189
+ .ac-table td.ac-num,
190
+ .ac-table th.ac-num {
191
+ text-align: right;
192
+ }
193
+
194
+ .ac-empty {
195
+ padding: var(--ac-step) 0;
196
+ color: var(--ac-reed);
197
+ }
198
+
199
+ /* ── Facts: a detail page's fields ──────────────────────────────────────── */
200
+
201
+ .ac-facts {
202
+ display: grid;
203
+ grid-template-columns: minmax(9rem, max-content) 1fr;
204
+ gap: 0.4rem var(--ac-step);
205
+ margin: 0;
206
+ }
207
+
208
+ .ac-facts dt {
209
+ color: var(--ac-reed);
210
+ }
211
+
212
+ .ac-facts dd {
213
+ margin: 0;
214
+ overflow-wrap: anywhere;
215
+ }
216
+
217
+ /* ── Status ─────────────────────────────────────────────────────────────── */
218
+
219
+ .ac-status {
220
+ display: inline-flex;
221
+ align-items: baseline;
222
+ gap: 0.4em;
223
+ font-weight: 600;
224
+ white-space: nowrap;
225
+ }
226
+
227
+ .ac-status-mark {
228
+ font-size: 0.85em;
229
+ letter-spacing: -0.15em;
230
+ }
231
+
232
+ .ac-status-ready .ac-status-mark {
233
+ color: var(--ac-green);
234
+ }
235
+
236
+ .ac-status-moving .ac-status-mark,
237
+ .ac-status-partial .ac-status-mark {
238
+ color: var(--ac-amber-ink);
239
+ }
240
+
241
+ .ac-status-trouble {
242
+ color: var(--ac-red);
243
+ }
244
+
245
+ .ac-status-stopped .ac-status-mark,
246
+ .ac-status-idle .ac-status-mark {
247
+ color: var(--ac-reed);
248
+ }
249
+
250
+ .ac-status-note {
251
+ font-weight: 400;
252
+ color: var(--ac-reed);
253
+ }
254
+
255
+ .ac-live {
256
+ display: inline-flex;
257
+ align-items: center;
258
+ gap: 0.4em;
259
+ color: var(--ac-reed);
260
+ font-size: 0.9rem;
261
+ }
262
+
263
+ .ac-live::before {
264
+ content: "";
265
+ width: 0.5rem;
266
+ height: 0.5rem;
267
+ border-radius: 50%;
268
+ background: var(--ac-amber);
269
+ }
270
+
271
+ @media (prefers-reduced-motion: no-preference) {
272
+ .ac-live::before {
273
+ animation: ac-ripple 2.4s ease-out infinite;
274
+ }
275
+ }
276
+
277
+ @keyframes ac-ripple {
278
+ 0% {
279
+ box-shadow: 0 0 0 0 color-mix(in srgb, var(--ac-amber) 60%, transparent);
280
+ }
281
+ 100% {
282
+ box-shadow: 0 0 0 0.6rem transparent;
283
+ }
284
+ }
285
+
286
+ /* ── Forms ──────────────────────────────────────────────────────────────── */
287
+
288
+ .ac-field {
289
+ display: grid;
290
+ gap: 0.25rem;
291
+ max-width: 32rem;
292
+ }
293
+
294
+ .ac-field label {
295
+ font-weight: 600;
296
+ }
297
+
298
+ .ac-root input,
299
+ .ac-root select,
300
+ .ac-root textarea {
301
+ font: inherit;
302
+ color: var(--ac-ink);
303
+ background: var(--ac-surface);
304
+ border: 1px solid var(--ac-reed);
305
+ border-radius: var(--ac-radius-control);
306
+ padding: 0.5rem 0.6rem;
307
+ width: 100%;
308
+ }
309
+
310
+ .ac-root textarea {
311
+ font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
312
+ font-size: 0.9rem;
313
+ min-height: 16rem;
314
+ }
315
+
316
+ .ac-root input[type="checkbox"] {
317
+ width: auto;
318
+ }
319
+
320
+ .ac-root [aria-invalid="true"] {
321
+ border-color: var(--ac-red);
322
+ }
323
+
324
+ .ac-hint {
325
+ color: var(--ac-reed);
326
+ font-size: 0.9rem;
327
+ }
328
+
329
+ .ac-field-error {
330
+ color: var(--ac-red);
331
+ }
332
+
333
+ .ac-form {
334
+ display: grid;
335
+ gap: var(--ac-step);
336
+ }
337
+
338
+ .ac-inline {
339
+ display: flex;
340
+ flex-wrap: wrap;
341
+ gap: 0.5rem;
342
+ align-items: end;
343
+ }
344
+
345
+ .ac-inline .ac-field {
346
+ flex: 1 1 12rem;
347
+ }
348
+
349
+ .ac-button {
350
+ font: inherit;
351
+ font-weight: 600;
352
+ display: inline-block;
353
+ padding: 0.45rem 0.9rem;
354
+ border-radius: var(--ac-radius-control);
355
+ border: 1px solid var(--ac-ink);
356
+ background: var(--ac-ink);
357
+ color: var(--ac-paper);
358
+ cursor: pointer;
359
+ text-decoration: none;
360
+ white-space: nowrap;
361
+ }
362
+
363
+ .ac-button-quiet {
364
+ background: transparent;
365
+ color: var(--ac-ink);
366
+ }
367
+
368
+ .ac-button-danger {
369
+ background: var(--ac-red);
370
+ border-color: var(--ac-red);
371
+ color: #fff;
372
+ }
373
+
374
+ .ac-button[disabled] {
375
+ opacity: 0.55;
376
+ cursor: progress;
377
+ }
378
+
379
+ .ac-actions {
380
+ display: flex;
381
+ flex-wrap: wrap;
382
+ gap: 0.5rem;
383
+ align-items: center;
384
+ }
385
+
386
+ .ac-actions form {
387
+ display: inline;
388
+ }
389
+
390
+ /* Secondary and destructive operations wait behind a disclosure, so a page shows its state first. */
391
+ .ac-more {
392
+ border-top: 1px solid var(--ac-water);
393
+ padding-top: var(--ac-step);
394
+ }
395
+
396
+ .ac-more summary {
397
+ cursor: pointer;
398
+ font-weight: 600;
399
+ }
400
+
401
+ .ac-more[open] > * + * {
402
+ margin-top: var(--ac-step);
403
+ }
404
+
405
+ .ac-danger-zone {
406
+ border-left: 3px solid var(--ac-red);
407
+ padding-left: var(--ac-step);
408
+ }
409
+
410
+ /* ── Messages ───────────────────────────────────────────────────────────── */
411
+
412
+ .ac-refusal {
413
+ border-left: 3px solid var(--ac-red);
414
+ background: var(--ac-red-wash);
415
+ padding: 0.6rem 0.8rem;
416
+ }
417
+
418
+ .ac-refusal ul {
419
+ margin: 0.4rem 0 0;
420
+ padding-left: 1.2rem;
421
+ }
422
+
423
+ .ac-notice {
424
+ border-left: 3px solid var(--ac-amber);
425
+ padding: 0.6rem 0.8rem;
426
+ background: var(--ac-surface);
427
+ }
428
+
429
+ .ac-secret {
430
+ display: block;
431
+ font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
432
+ background: var(--ac-surface);
433
+ border: 1px dashed var(--ac-reed);
434
+ padding: 0.6rem;
435
+ overflow-wrap: anywhere;
436
+ user-select: all;
437
+ }
438
+
439
+ /* ── Logs ───────────────────────────────────────────────────────────────── */
440
+
441
+ .ac-log {
442
+ font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
443
+ font-size: 0.85rem;
444
+ background: var(--ac-surface);
445
+ border: 1px solid var(--ac-water);
446
+ padding: 0.75rem;
447
+ max-height: 32rem;
448
+ overflow: auto;
449
+ white-space: pre-wrap;
450
+ overflow-wrap: anywhere;
451
+ margin: 0;
452
+ }
453
+
454
+ /* ── Panels a host adds ─────────────────────────────────────────────────── */
455
+
456
+ .ac-panel {
457
+ border-top: 1px solid var(--ac-water);
458
+ padding-top: var(--ac-step);
459
+ }
460
+
461
+ .ac-panel-failed {
462
+ color: var(--ac-red);
463
+ }
464
+
465
+ .ac-visually-hidden {
466
+ position: absolute;
467
+ width: 1px;
468
+ height: 1px;
469
+ overflow: hidden;
470
+ clip-path: inset(50%);
471
+ white-space: nowrap;
472
+ }
473
+
474
+ @media (max-width: 40rem) {
475
+ .ac-facts {
476
+ grid-template-columns: 1fr;
477
+ }
478
+ .ac-facts dd {
479
+ margin-bottom: 0.5rem;
480
+ }
481
+ }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * A control plane in one process: every route the console uses, over in-memory state, with the
3
+ * control plane's rules as the console can observe them — who sees what, `404` rather than `403`
4
+ * for an outsider, owner-only operations, tombstoned ids, `409` for a non-empty delete, a disabled
5
+ * organization refusing changes, descriptor validation, a deploy token shown once.
6
+ *
7
+ * It answers from state rather than from a script, so it cannot run out quietly. What a test needs
8
+ * to steer is explicit: `script()` makes the next answer on one route a given status, `tick()` moves
9
+ * services through their lifecycle, `appendLog()` writes a line, and `hideNewFromListings()` makes
10
+ * listings lag writes the way the real control plane's projections do. `visited` records every route
11
+ * template exercised, which the parity test reads.
12
+ */
13
+ import { type Server } from "node:http";
14
+ export interface FakeClaims {
15
+ sub: string;
16
+ name?: string;
17
+ email?: string;
18
+ email_verified?: boolean;
19
+ realm_access?: {
20
+ roles?: string[];
21
+ };
22
+ exp?: number;
23
+ }
24
+ export interface FakeControlPlaneOptions {
25
+ /** Turns a bearer into claims, or `null` when it is not a valid token. Defaults to decoding a JWT and checking `exp`. */
26
+ verify?: (token: string) => Promise<FakeClaims | null> | FakeClaims | null;
27
+ issuer?: string;
28
+ clientId?: string;
29
+ audience?: string;
30
+ /** Who may create organizations: anyone signed in, or platform administrators only. */
31
+ organizationCreation?: "open" | "platform-admin";
32
+ signupUrl?: string;
33
+ baseDomain?: string;
34
+ port?: number;
35
+ }
36
+ type Role = "owner" | "member";
37
+ interface Member {
38
+ role: Role;
39
+ email?: string;
40
+ display?: string;
41
+ since: string;
42
+ addedBy?: string;
43
+ }
44
+ interface Invitation {
45
+ role: Role;
46
+ invitedAt: string;
47
+ invitedBy?: string;
48
+ }
49
+ interface Org {
50
+ id: string;
51
+ name: string;
52
+ disabled: boolean;
53
+ quota?: {
54
+ projects?: number;
55
+ services?: number;
56
+ instances?: number;
57
+ };
58
+ members: Map<string, Member>;
59
+ invitations: Map<string, Invitation>;
60
+ hidden: boolean;
61
+ }
62
+ interface Registry {
63
+ server: string;
64
+ username: string;
65
+ password: string;
66
+ setAt: string;
67
+ setBy?: string;
68
+ }
69
+ interface Project {
70
+ id: string;
71
+ name: string;
72
+ organizationId: string;
73
+ registry?: Registry;
74
+ hidden: boolean;
75
+ }
76
+ interface Service {
77
+ name: string;
78
+ projectId: string;
79
+ lifecycle: string;
80
+ generation: number;
81
+ image: string;
82
+ readyInstances: number;
83
+ desiredInstances: number;
84
+ detail?: string;
85
+ exposed: boolean;
86
+ paused: boolean;
87
+ history: {
88
+ kind: string;
89
+ generation: number;
90
+ actor?: {
91
+ subject: string;
92
+ display?: string;
93
+ administrative: boolean;
94
+ };
95
+ at: string;
96
+ }[];
97
+ logs: Map<string, string[]>;
98
+ previousLogs: Map<string, string[]>;
99
+ }
100
+ interface Token {
101
+ id: string;
102
+ organizationId: string;
103
+ label: string;
104
+ subject: string;
105
+ secret: string;
106
+ createdBy?: string;
107
+ createdAt: string;
108
+ expiresAt?: string;
109
+ revoked: boolean;
110
+ }
111
+ export interface FakeControlPlane {
112
+ url: string;
113
+ server: Server;
114
+ /** `METHOD /template` for every route answered, e.g. `GET /organizations/{organizationId}`. */
115
+ visited: Set<string>;
116
+ /** The number of requests answered per `METHOD /template`. */
117
+ counts: Map<string, number>;
118
+ /** Make the next request matching `METHOD /template` answer `status` with `error` as the reason. */
119
+ script(route: string, status: number, error?: string): void;
120
+ /** Advance every service in a transitional state by one step. */
121
+ tick(): void;
122
+ appendLog(projectId: string, name: string, line: string, instance?: string): void;
123
+ /** Who may create organizations from now on, and where the refusal sends people. */
124
+ policy(creation: "open" | "platform-admin", signupUrl?: string): void;
125
+ /** From now on, created organizations and projects are missing from listings until `settle()`. */
126
+ hideNewFromListings(): void;
127
+ settle(): void;
128
+ /** Direct state access for assertions and seeding. */
129
+ state: {
130
+ organizations: Map<string, Org>;
131
+ projects: Map<string, Project>;
132
+ services: Map<string, Service>;
133
+ tokens: Map<string, Token>;
134
+ tombstones: Set<string>;
135
+ };
136
+ seed(seed: FakeSeed): void;
137
+ close(): Promise<void>;
138
+ }
139
+ export interface FakeSeed {
140
+ organizations?: {
141
+ id: string;
142
+ name: string;
143
+ owners?: string[];
144
+ members?: string[];
145
+ disabled?: boolean;
146
+ }[];
147
+ projects?: {
148
+ id: string;
149
+ name: string;
150
+ organizationId: string;
151
+ }[];
152
+ services?: {
153
+ projectId: string;
154
+ name: string;
155
+ image?: string;
156
+ lifecycle?: string;
157
+ instances?: number;
158
+ }[];
159
+ }
160
+ export declare function fakeControlPlane(options?: FakeControlPlaneOptions): Promise<FakeControlPlane>;
161
+ export {};