@substrat-run/control-plane-client 0.1.0 → 0.1.1
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/README.md +1 -1
- package/dist/builder-client.d.ts +76 -0
- package/dist/builder-client.d.ts.map +1 -0
- package/dist/builder-client.js +150 -0
- package/dist/builder-client.js.map +1 -0
- package/dist/builder-types.d.ts +191 -0
- package/dist/builder-types.d.ts.map +1 -0
- package/dist/builder-types.js +2 -0
- package/dist/builder-types.js.map +1 -0
- package/dist/client.d.ts +111 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +122 -0
- package/dist/client.js.map +1 -0
- package/dist/headers.d.ts +20 -0
- package/dist/headers.d.ts.map +1 -0
- package/dist/headers.js +20 -0
- package/dist/headers.js.map +1 -0
- package/dist/identity-tenants.d.ts +30 -0
- package/dist/identity-tenants.d.ts.map +1 -0
- package/dist/identity-tenants.js +23 -0
- package/dist/identity-tenants.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/transport.d.ts +162 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +222 -0
- package/dist/transport.js.map +1 -0
- package/package.json +11 -11
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @substrat-run/control-plane-client
|
|
2
2
|
|
|
3
3
|
The typed HTTP client for the Substrat control-plane API — one transport for the CLI, the
|
|
4
|
-
console and a vertical's connect seam: how a request is authenticated, how a refusal is
|
|
4
|
+
console, the dashboard and a vertical's connect seam: how a request is authenticated, how a refusal is
|
|
5
5
|
read (RFC 9457 problem documents), and what an empty answer is.
|
|
6
6
|
|
|
7
7
|
```sh
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { Page } from '@substrat-run/contracts';
|
|
2
|
+
import type { AdoptServingResult, AdoptVerticalResult, BindHostnameBody, BindScopeVersionBody, BoundScopeVersion, ChannelRow, ExportBreakAck, HostnameRow, ListingRecord, PromoteAcknowledge, PromoteResult, ProvisionScopeResult, PulledDump, RebindScopeBody, RebindVerticalResult, RestoreBody, ScopeHealthRecord, ScopeRecord, ScopeRow, VersionRow, Whoami } from './builder-types.js';
|
|
3
|
+
import { ControlPlaneTransport } from './transport.js';
|
|
4
|
+
/** One page of a list route: how many, and where after. */
|
|
5
|
+
export interface PageRequest {
|
|
6
|
+
limit?: number;
|
|
7
|
+
cursor?: string | null;
|
|
8
|
+
}
|
|
9
|
+
/** The page size a walk to the END asks for — the routes' ceiling, to keep the walk short. */
|
|
10
|
+
export declare const WALK_PAGE_LIMIT = 200;
|
|
11
|
+
/**
|
|
12
|
+
* Walk a paged list to the end. Every GET list route answers `{ entries, nextCursor }`, and
|
|
13
|
+
* a builder tool's reads are complete-list semantics (the max semver, installs joined to
|
|
14
|
+
* hostnames), so it follows the cursor at the ceiling page size until it runs out.
|
|
15
|
+
*/
|
|
16
|
+
export declare function walkPages<T>(fetchPage: (page: {
|
|
17
|
+
limit: number;
|
|
18
|
+
cursor: string | null;
|
|
19
|
+
}) => Promise<Page<T>>): Promise<T[]>;
|
|
20
|
+
/**
|
|
21
|
+
* The control-plane routes a builder's tooling calls — the CLI's whole surface (#971): who
|
|
22
|
+
* am I, a vertical's versions and channels, promote, hostnames, and the scope tools.
|
|
23
|
+
*
|
|
24
|
+
* It extends the transport and adds nothing to how a request is made. A write names its
|
|
25
|
+
* `content-type` itself, so the same method sends the same request whether the client was
|
|
26
|
+
* built to send the JSON type on everything (as the hostname and preview commands always
|
|
27
|
+
* have) or on nothing (as a bare read always has). A refusal is a `ControlPlaneError`
|
|
28
|
+
* carrying the raw body, which is how a caller that renders its own messages — the CLI —
|
|
29
|
+
* keeps them; a 2xx that is not JSON is a `malformed` one.
|
|
30
|
+
*
|
|
31
|
+
* Responses are typed, not parsed (`builder-types.ts`).
|
|
32
|
+
*/
|
|
33
|
+
export declare class ControlPlaneBuilderClient extends ControlPlaneTransport {
|
|
34
|
+
private write;
|
|
35
|
+
whoami(): Promise<Whoami>;
|
|
36
|
+
/** The `substrat login` code→token exchange. Unauthenticated: the code is the credential. */
|
|
37
|
+
exchangeLoginCode(code: string, verifier: string): Promise<{
|
|
38
|
+
token?: string;
|
|
39
|
+
}>;
|
|
40
|
+
listVerticals(page?: PageRequest): Promise<Page<{
|
|
41
|
+
slug: string;
|
|
42
|
+
}>>;
|
|
43
|
+
listVersions(slug: string, page?: PageRequest): Promise<Page<VersionRow>>;
|
|
44
|
+
listChannels(slug: string, page?: PageRequest): Promise<Page<ChannelRow>>;
|
|
45
|
+
/** The permission registry a version carries (`null`: it kept none). */
|
|
46
|
+
getVersionRegistry<R>(slug: string, versionId: string): Promise<{
|
|
47
|
+
registry: R | null;
|
|
48
|
+
}>;
|
|
49
|
+
/** The migrations a version adds over `base`, as the control plane diffs them. */
|
|
50
|
+
getVersionMigrations<M>(slug: string, versionId: string, base: string): Promise<{
|
|
51
|
+
migrations: M | null;
|
|
52
|
+
}>;
|
|
53
|
+
promoteChannel(slug: string, channel: string, versionId: string, acknowledge?: PromoteAcknowledge): Promise<PromoteResult>;
|
|
54
|
+
setListing(slug: string, listed: boolean): Promise<ListingRecord>;
|
|
55
|
+
/** A builder asks for listing; a staff operator reviews it. */
|
|
56
|
+
requestPublish(slug: string): Promise<void>;
|
|
57
|
+
adoptVerticalServing(slug: string, body: ExportBreakAck): Promise<AdoptVerticalResult | null>;
|
|
58
|
+
listScopes(tenantId: string, page?: PageRequest): Promise<Page<ScopeRow>>;
|
|
59
|
+
listHostnames(tenantId: string, page?: PageRequest): Promise<Page<HostnameRow>>;
|
|
60
|
+
bindHostname(body: BindHostnameBody): Promise<HostnameRow>;
|
|
61
|
+
verifyHostname(hostname: string): Promise<HostnameRow>;
|
|
62
|
+
/** Unbind one hostname. Reads the answer like every other call here: an empty body is not JSON. */
|
|
63
|
+
unbindHostname(hostname: string): Promise<unknown>;
|
|
64
|
+
private scopePath;
|
|
65
|
+
getScope(tenantId: string, scopeId: string): Promise<ScopeRecord>;
|
|
66
|
+
/** Reaches into the vertical's own deployment, so it can fail while the record answers. */
|
|
67
|
+
getScopeHealth(tenantId: string, scopeId: string): Promise<ScopeHealthRecord>;
|
|
68
|
+
exportScope(tenantId: string, scopeId: string, full: boolean): Promise<PulledDump>;
|
|
69
|
+
/** The restore's answer carries nothing the caller reads — only that it landed. */
|
|
70
|
+
restoreScope(tenantId: string, scopeId: string, body: RestoreBody): Promise<void>;
|
|
71
|
+
adoptServing(tenantId: string, scopeId: string, body: ExportBreakAck): Promise<AdoptServingResult>;
|
|
72
|
+
provisionScope(tenantId: string, scopeId: string): Promise<ProvisionScopeResult>;
|
|
73
|
+
bindScopeVersion(tenantId: string, scopeId: string, body: BindScopeVersionBody): Promise<BoundScopeVersion>;
|
|
74
|
+
rebindScopeVertical(tenantId: string, scopeId: string, body: RebindScopeBody): Promise<RebindVerticalResult>;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=builder-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder-client.d.ts","sourceRoot":"","sources":["../src/builder-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { ControlPlaneTransport } from './transport.js';
|
|
2
|
+
/** The page size a walk to the END asks for — the routes' ceiling, to keep the walk short. */
|
|
3
|
+
export const WALK_PAGE_LIMIT = 200;
|
|
4
|
+
/**
|
|
5
|
+
* Walk a paged list to the end. Every GET list route answers `{ entries, nextCursor }`, and
|
|
6
|
+
* a builder tool's reads are complete-list semantics (the max semver, installs joined to
|
|
7
|
+
* hostnames), so it follows the cursor at the ceiling page size until it runs out.
|
|
8
|
+
*/
|
|
9
|
+
export async function walkPages(fetchPage) {
|
|
10
|
+
const out = [];
|
|
11
|
+
let cursor = null;
|
|
12
|
+
do {
|
|
13
|
+
const page = await fetchPage({ limit: WALK_PAGE_LIMIT, cursor });
|
|
14
|
+
out.push(...page.entries);
|
|
15
|
+
cursor = page.nextCursor;
|
|
16
|
+
} while (cursor !== null);
|
|
17
|
+
return out;
|
|
18
|
+
}
|
|
19
|
+
const seg = encodeURIComponent;
|
|
20
|
+
/** `?a=b&limit=…&cursor=…` — the leading params first, then the page, in the order the CLI always sent them. */
|
|
21
|
+
function queryOf(lead, page) {
|
|
22
|
+
const parts = Object.entries(lead).map(([k, v]) => `${k}=${seg(v)}`);
|
|
23
|
+
if (page?.limit !== undefined)
|
|
24
|
+
parts.push(`limit=${page.limit}`);
|
|
25
|
+
if (page?.cursor)
|
|
26
|
+
parts.push(`cursor=${seg(page.cursor)}`);
|
|
27
|
+
return parts.length > 0 ? `?${parts.join('&')}` : '';
|
|
28
|
+
}
|
|
29
|
+
const JSON_TYPE = { 'content-type': 'application/json' };
|
|
30
|
+
/**
|
|
31
|
+
* The control-plane routes a builder's tooling calls — the CLI's whole surface (#971): who
|
|
32
|
+
* am I, a vertical's versions and channels, promote, hostnames, and the scope tools.
|
|
33
|
+
*
|
|
34
|
+
* It extends the transport and adds nothing to how a request is made. A write names its
|
|
35
|
+
* `content-type` itself, so the same method sends the same request whether the client was
|
|
36
|
+
* built to send the JSON type on everything (as the hostname and preview commands always
|
|
37
|
+
* have) or on nothing (as a bare read always has). A refusal is a `ControlPlaneError`
|
|
38
|
+
* carrying the raw body, which is how a caller that renders its own messages — the CLI —
|
|
39
|
+
* keeps them; a 2xx that is not JSON is a `malformed` one.
|
|
40
|
+
*
|
|
41
|
+
* Responses are typed, not parsed (`builder-types.ts`).
|
|
42
|
+
*/
|
|
43
|
+
export class ControlPlaneBuilderClient extends ControlPlaneTransport {
|
|
44
|
+
write(method, body) {
|
|
45
|
+
return {
|
|
46
|
+
method,
|
|
47
|
+
headers: { ...JSON_TYPE },
|
|
48
|
+
...(body === undefined ? {} : { body: JSON.stringify(body) }),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
// -- identity ---------------------------------------------------------------
|
|
52
|
+
whoami() {
|
|
53
|
+
return this.read('/auth/whoami');
|
|
54
|
+
}
|
|
55
|
+
/** The `substrat login` code→token exchange. Unauthenticated: the code is the credential. */
|
|
56
|
+
exchangeLoginCode(code, verifier) {
|
|
57
|
+
return this.read('/auth/cli/token', this.write('POST', { code, verifier }));
|
|
58
|
+
}
|
|
59
|
+
// -- the registry -----------------------------------------------------------
|
|
60
|
+
listVerticals(page) {
|
|
61
|
+
return this.read(`/verticals${queryOf({}, page)}`);
|
|
62
|
+
}
|
|
63
|
+
listVersions(slug, page) {
|
|
64
|
+
return this.read(`/verticals/${seg(slug)}/versions${queryOf({}, page)}`);
|
|
65
|
+
}
|
|
66
|
+
listChannels(slug, page) {
|
|
67
|
+
return this.read(`/verticals/${seg(slug)}/channels${queryOf({}, page)}`);
|
|
68
|
+
}
|
|
69
|
+
/** The permission registry a version carries (`null`: it kept none). */
|
|
70
|
+
getVersionRegistry(slug, versionId) {
|
|
71
|
+
return this.read(`/verticals/${seg(slug)}/versions/${seg(versionId)}/registry`);
|
|
72
|
+
}
|
|
73
|
+
/** The migrations a version adds over `base`, as the control plane diffs them. */
|
|
74
|
+
getVersionMigrations(slug, versionId, base) {
|
|
75
|
+
return this.read(`/verticals/${seg(slug)}/versions/${seg(versionId)}/migrations?base=${seg(base)}`);
|
|
76
|
+
}
|
|
77
|
+
promoteChannel(slug, channel, versionId, acknowledge) {
|
|
78
|
+
const path = `/verticals/${seg(slug)}/channels/${seg(channel)}/promote`;
|
|
79
|
+
return this.read(path, this.write('POST', { versionId, ...(acknowledge ? { acknowledge } : {}) }));
|
|
80
|
+
}
|
|
81
|
+
setListing(slug, listed) {
|
|
82
|
+
const path = `/verticals/${seg(slug)}/listing`;
|
|
83
|
+
return this.read(path, this.write('POST', { listed }));
|
|
84
|
+
}
|
|
85
|
+
/** A builder asks for listing; a staff operator reviews it. */
|
|
86
|
+
requestPublish(slug) {
|
|
87
|
+
const path = `/verticals/${seg(slug)}/publish-request`;
|
|
88
|
+
return this.send(path, { method: 'POST', headers: { ...JSON_TYPE }, body: '{}' }).then(() => undefined);
|
|
89
|
+
}
|
|
90
|
+
adoptVerticalServing(slug, body) {
|
|
91
|
+
const path = `/verticals/${seg(slug)}/adopt-serving`;
|
|
92
|
+
// Lenient on purpose: a 2xx whose body is not JSON reads as "nothing reported", which is
|
|
93
|
+
// what the command prints for it.
|
|
94
|
+
return this.send(path, this.write('POST', body)).then((res) => res.json().catch(() => null));
|
|
95
|
+
}
|
|
96
|
+
// -- the directory ----------------------------------------------------------
|
|
97
|
+
listScopes(tenantId, page) {
|
|
98
|
+
return this.read(`/scopes${queryOf({ tenantId }, page)}`);
|
|
99
|
+
}
|
|
100
|
+
listHostnames(tenantId, page) {
|
|
101
|
+
return this.read(`/hostnames${queryOf({ tenantId }, page)}`);
|
|
102
|
+
}
|
|
103
|
+
bindHostname(body) {
|
|
104
|
+
return this.read('/hostnames', this.write('POST', body));
|
|
105
|
+
}
|
|
106
|
+
verifyHostname(hostname) {
|
|
107
|
+
const path = `/hostnames/${seg(hostname)}/verify`;
|
|
108
|
+
return this.read(path, this.write('POST'));
|
|
109
|
+
}
|
|
110
|
+
/** Unbind one hostname. Reads the answer like every other call here: an empty body is not JSON. */
|
|
111
|
+
unbindHostname(hostname) {
|
|
112
|
+
return this.read(`/hostnames/${seg(hostname)}`, this.write('DELETE'));
|
|
113
|
+
}
|
|
114
|
+
// -- one scope --------------------------------------------------------------
|
|
115
|
+
scopePath(tenantId, scopeId) {
|
|
116
|
+
return `/tenants/${seg(tenantId)}/scopes/${seg(scopeId)}`;
|
|
117
|
+
}
|
|
118
|
+
getScope(tenantId, scopeId) {
|
|
119
|
+
return this.read(this.scopePath(tenantId, scopeId));
|
|
120
|
+
}
|
|
121
|
+
/** Reaches into the vertical's own deployment, so it can fail while the record answers. */
|
|
122
|
+
getScopeHealth(tenantId, scopeId) {
|
|
123
|
+
return this.read(`${this.scopePath(tenantId, scopeId)}/health`);
|
|
124
|
+
}
|
|
125
|
+
exportScope(tenantId, scopeId, full) {
|
|
126
|
+
return this.read(`${this.scopePath(tenantId, scopeId)}/export${full ? '?full=true' : ''}`);
|
|
127
|
+
}
|
|
128
|
+
/** The restore's answer carries nothing the caller reads — only that it landed. */
|
|
129
|
+
restoreScope(tenantId, scopeId, body) {
|
|
130
|
+
const path = `${this.scopePath(tenantId, scopeId)}/restore`;
|
|
131
|
+
return this.send(path, this.write('POST', body)).then(() => undefined);
|
|
132
|
+
}
|
|
133
|
+
adoptServing(tenantId, scopeId, body) {
|
|
134
|
+
const path = `${this.scopePath(tenantId, scopeId)}/adopt-serving`;
|
|
135
|
+
return this.read(path, this.write('POST', body));
|
|
136
|
+
}
|
|
137
|
+
provisionScope(tenantId, scopeId) {
|
|
138
|
+
const path = `${this.scopePath(tenantId, scopeId)}/provision`;
|
|
139
|
+
return this.read(path, this.write('POST'));
|
|
140
|
+
}
|
|
141
|
+
bindScopeVersion(tenantId, scopeId, body) {
|
|
142
|
+
const path = `${this.scopePath(tenantId, scopeId)}/version`;
|
|
143
|
+
return this.read(path, this.write('POST', body));
|
|
144
|
+
}
|
|
145
|
+
rebindScopeVertical(tenantId, scopeId, body) {
|
|
146
|
+
const path = `${this.scopePath(tenantId, scopeId)}/rebind-vertical`;
|
|
147
|
+
return this.read(path, this.write('POST', body));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=builder-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder-client.js","sourceRoot":"","sources":["../src/builder-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import type { ExportBreak } from '@substrat-run/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* The answers a builder's tooling reads off the control plane — the fields the CLI actually
|
|
4
|
+
* uses, typed and NOT parsed (#971): a field the deployed plane omits arrives as `undefined`
|
|
5
|
+
* rather than as a thrown error, which is the behaviour the CLI has always had. The server's
|
|
6
|
+
* own schemas live in `@substrat-run/contracts`; these are deliberately the narrower reading.
|
|
7
|
+
*/
|
|
8
|
+
/** `GET /auth/whoami` — the signed-in user and the tenants they can build for. */
|
|
9
|
+
export interface Whoami {
|
|
10
|
+
user: {
|
|
11
|
+
id: string;
|
|
12
|
+
email?: string;
|
|
13
|
+
} | null;
|
|
14
|
+
tenants: {
|
|
15
|
+
id: string;
|
|
16
|
+
slug: string;
|
|
17
|
+
name: string;
|
|
18
|
+
}[];
|
|
19
|
+
}
|
|
20
|
+
export interface VersionRow {
|
|
21
|
+
id: string;
|
|
22
|
+
version: string;
|
|
23
|
+
admission: string;
|
|
24
|
+
deploymentRef?: string;
|
|
25
|
+
}
|
|
26
|
+
export interface ChannelRow {
|
|
27
|
+
channel: string;
|
|
28
|
+
versionId: string;
|
|
29
|
+
/**
|
|
30
|
+
* What prod's stable serving script actually runs (#286/#321). Differs from `versionId`
|
|
31
|
+
* when an in-place serve failed: the channel was promoted but the scopes run old code.
|
|
32
|
+
*/
|
|
33
|
+
servingVersionId?: string | null;
|
|
34
|
+
}
|
|
35
|
+
/** One scope of a tenant as the directory lists it (`GET /scopes?tenantId=`). */
|
|
36
|
+
export interface ScopeRow {
|
|
37
|
+
id: string;
|
|
38
|
+
slug: string;
|
|
39
|
+
name: string;
|
|
40
|
+
status: string;
|
|
41
|
+
vertical: string | null;
|
|
42
|
+
verticalVersionId: string | null;
|
|
43
|
+
forkedFrom: string | null;
|
|
44
|
+
servingRef?: string | null;
|
|
45
|
+
createdAt: string;
|
|
46
|
+
}
|
|
47
|
+
/** A DNS record the tenant must publish (contracts' dnsRecord). */
|
|
48
|
+
export interface DnsRecordRow {
|
|
49
|
+
type: 'hostname' | 'txt';
|
|
50
|
+
name: string;
|
|
51
|
+
value: string;
|
|
52
|
+
status: string | null;
|
|
53
|
+
}
|
|
54
|
+
/** One hostname binding as the control plane returns it (contracts' HostnameBinding). */
|
|
55
|
+
export interface HostnameRow {
|
|
56
|
+
hostname: string;
|
|
57
|
+
tenantId: string;
|
|
58
|
+
scopeId: string;
|
|
59
|
+
verticalSlug: string | null;
|
|
60
|
+
surface: string;
|
|
61
|
+
status: string;
|
|
62
|
+
statusNote: string | null;
|
|
63
|
+
canonical: boolean;
|
|
64
|
+
createdAt: string;
|
|
65
|
+
customHostnameId: string | null;
|
|
66
|
+
validationRecords: DnsRecordRow[];
|
|
67
|
+
}
|
|
68
|
+
export interface BindHostnameBody {
|
|
69
|
+
hostname: string;
|
|
70
|
+
tenantId: string;
|
|
71
|
+
scopeId: string;
|
|
72
|
+
surface: string;
|
|
73
|
+
canonical: boolean;
|
|
74
|
+
}
|
|
75
|
+
/** The installed apps a promote breaks (#1705 PR 3), as the control plane lists them to this caller. */
|
|
76
|
+
export interface ExportBreaks {
|
|
77
|
+
affected: ExportBreak[];
|
|
78
|
+
otherTenants?: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* One store the promote minted for an already-installed tenant (#825) — a store declared
|
|
82
|
+
* by THIS version that the tenant, having been created before the declaration existed,
|
|
83
|
+
* did not have.
|
|
84
|
+
*/
|
|
85
|
+
export interface MintedStore {
|
|
86
|
+
tenantId: string;
|
|
87
|
+
binding: string;
|
|
88
|
+
kind: 'relational' | 'blob';
|
|
89
|
+
}
|
|
90
|
+
export interface PromoteAcknowledge {
|
|
91
|
+
permissionChange?: boolean;
|
|
92
|
+
migrationChange?: boolean;
|
|
93
|
+
exportBreak?: boolean;
|
|
94
|
+
}
|
|
95
|
+
export interface PromoteResult {
|
|
96
|
+
channel: string;
|
|
97
|
+
versionId: string;
|
|
98
|
+
/** Present only when this promote minted stores, or tried and could not. `minted` names
|
|
99
|
+
* only tenants the caller may see; `otherTenants` counts the rest of the fleet the sweep
|
|
100
|
+
* also covered. */
|
|
101
|
+
storeBackfill?: {
|
|
102
|
+
minted: MintedStore[];
|
|
103
|
+
otherTenants?: number;
|
|
104
|
+
error?: string;
|
|
105
|
+
};
|
|
106
|
+
/** Present when an acknowledged export break reached installed apps (#1705 PR 3). */
|
|
107
|
+
exportBreaks?: ExportBreaks;
|
|
108
|
+
}
|
|
109
|
+
export interface DumpTable {
|
|
110
|
+
name: string;
|
|
111
|
+
ddl: string;
|
|
112
|
+
columns: string[];
|
|
113
|
+
rows: unknown[][];
|
|
114
|
+
}
|
|
115
|
+
/** `GET …/scopes/:id/export` — a scope's data as the control plane released it. */
|
|
116
|
+
export interface PulledDump {
|
|
117
|
+
tenantId: string;
|
|
118
|
+
scopeId: string;
|
|
119
|
+
capturedAt: string;
|
|
120
|
+
masked: boolean;
|
|
121
|
+
tables: DumpTable[];
|
|
122
|
+
}
|
|
123
|
+
export interface RestoreBody {
|
|
124
|
+
tenantId: string;
|
|
125
|
+
scopeId: string;
|
|
126
|
+
capturedAt: string;
|
|
127
|
+
tables: DumpTable[];
|
|
128
|
+
}
|
|
129
|
+
export interface ScopeRecord {
|
|
130
|
+
slug: string;
|
|
131
|
+
name: string;
|
|
132
|
+
status: string;
|
|
133
|
+
vertical: string | null;
|
|
134
|
+
verticalVersionId: string | null;
|
|
135
|
+
servingRef?: string | null;
|
|
136
|
+
schemaVersion: string;
|
|
137
|
+
createdAt: string;
|
|
138
|
+
}
|
|
139
|
+
export interface ScopeHealthRecord {
|
|
140
|
+
roleCount: number | null;
|
|
141
|
+
roleProjectionEmpty: boolean;
|
|
142
|
+
missingStores?: {
|
|
143
|
+
binding: string;
|
|
144
|
+
kind: string;
|
|
145
|
+
}[];
|
|
146
|
+
}
|
|
147
|
+
/** What a move's refusal may ask the caller to acknowledge (#1756). */
|
|
148
|
+
export interface ExportBreakAck {
|
|
149
|
+
acknowledge?: {
|
|
150
|
+
exportBreak: true;
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
export interface BindScopeVersionBody extends ExportBreakAck {
|
|
154
|
+
versionId: string;
|
|
155
|
+
snapshot?: true | undefined;
|
|
156
|
+
}
|
|
157
|
+
export interface RebindScopeBody extends ExportBreakAck {
|
|
158
|
+
vertical: string;
|
|
159
|
+
ackMigrations?: true | undefined;
|
|
160
|
+
abandonData?: true | undefined;
|
|
161
|
+
}
|
|
162
|
+
export interface AdoptServingResult {
|
|
163
|
+
servingRef?: string;
|
|
164
|
+
alreadyAdopted?: boolean;
|
|
165
|
+
tables?: number;
|
|
166
|
+
}
|
|
167
|
+
export interface AdoptVerticalResult {
|
|
168
|
+
adopted?: string[];
|
|
169
|
+
alreadyAdopted?: string[];
|
|
170
|
+
}
|
|
171
|
+
export interface RebindVerticalResult {
|
|
172
|
+
servingRef?: string;
|
|
173
|
+
versionId?: string;
|
|
174
|
+
alreadyBound?: boolean;
|
|
175
|
+
tables?: number;
|
|
176
|
+
dataAbandoned?: boolean;
|
|
177
|
+
}
|
|
178
|
+
export interface ProvisionScopeResult {
|
|
179
|
+
owner?: string;
|
|
180
|
+
storeError?: string;
|
|
181
|
+
}
|
|
182
|
+
export interface BoundScopeVersion {
|
|
183
|
+
verticalVersionId: string | null;
|
|
184
|
+
vertical: string | null;
|
|
185
|
+
servingRef?: string | null;
|
|
186
|
+
}
|
|
187
|
+
export interface ListingRecord {
|
|
188
|
+
slug: string;
|
|
189
|
+
listed: boolean;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=builder-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder-types.d.ts","sourceRoot":"","sources":["../src/builder-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAE3D;;;;;GAKG;AAEH,kFAAkF;AAClF,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC5C,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD;AAED,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,iFAAiF;AACjF,MAAM,WAAW,QAAQ;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,mEAAmE;AACnE,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,UAAU,GAAG,KAAK,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,yFAAyF;AACzF,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,iBAAiB,EAAE,YAAY,EAAE,CAAC;CACnC;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,wGAAwG;AACxG,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,YAAY,GAAG,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB;;wBAEoB;IACpB,aAAa,CAAC,EAAE;QAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAAC,YAAY,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACjF,qFAAqF;IACrF,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,OAAO,EAAE,EAAE,CAAC;CACnB;AAED,mFAAmF;AACnF,MAAM,WAAW,UAAU;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,SAAS,EAAE,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,aAAa,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACrD;AAED,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE;QAAE,WAAW,EAAE,IAAI,CAAA;KAAE,CAAC;CACrC;AAED,MAAM,WAAW,oBAAqB,SAAQ,cAAc;IAC1D,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,eAAgB,SAAQ,cAAc;IACrD,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;IACjC,WAAW,CAAC,EAAE,IAAI,GAAG,SAAS,CAAC;CAChC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,iBAAiB;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"builder-types.js","sourceRoot":"","sources":["../src/builder-types.ts"],"names":[],"mappings":""}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import type { EntitlementGrant, EntitlementGrantInput, QueryScopeInput, ReadScopeTableInput, Scope, ScopeId, ScopeQueryResult, DenialFilter, DenialSummary, PermissionDenial, ScopeTable, ScopeTablePage, Tenant, TenantId } from '@substrat-run/contracts';
|
|
2
|
+
import { ControlPlaneError, ControlPlaneTransport } from './transport.js';
|
|
3
|
+
import { type IdentityTenant } from './identity-tenants.js';
|
|
4
|
+
/**
|
|
5
|
+
* A typed HTTP client for the control-plane API — the vertical side of the
|
|
6
|
+
* connect seam (first-flow.md slice 4).
|
|
7
|
+
*
|
|
8
|
+
* A vertical that runs against a *separately deployed* shared control plane uses
|
|
9
|
+
* this to (a) register its tenant, entitlements, and scope on boot, and (b) gate
|
|
10
|
+
* each request on the directory's authoritative lifecycle — `assertScopeActive`
|
|
11
|
+
* fails closed exactly as the kernel's own `getScope` does, so a suspend in the
|
|
12
|
+
* console bites the vertical's next operation even across process and deployment
|
|
13
|
+
* boundaries.
|
|
14
|
+
*
|
|
15
|
+
* What it deliberately does NOT do: write roles or grants. Those are not on the
|
|
16
|
+
* control-plane HTTP surface (api.ts §4.5 — permission writes are the human
|
|
17
|
+
* checkpoint, D-22/D-29), so a connected vertical keeps its permission model
|
|
18
|
+
* local and treats the shared plane as the authority for tenant/scope lifecycle
|
|
19
|
+
* and entitlements only.
|
|
20
|
+
*
|
|
21
|
+
* `fetch` is injectable: pass a Worker service-binding's fetch, or the router's
|
|
22
|
+
* own `app.fetch` for an in-process test, instead of the global.
|
|
23
|
+
*/
|
|
24
|
+
export interface ControlPlaneClientOptions {
|
|
25
|
+
/** Base URL of the control-plane API, e.g. `https://cp.example.com` or `http://127.0.0.1:8788`. */
|
|
26
|
+
baseUrl: string;
|
|
27
|
+
/**
|
|
28
|
+
* The platform actor id stamped as the audit subject on every write — sent as the
|
|
29
|
+
* dev-only `x-platform-actor` header, and ONLY when no `serviceToken` is set. With a
|
|
30
|
+
* token the control plane resolves the subject from the token (its fixed service
|
|
31
|
+
* actor), so the header is never consulted there and is not sent (#980).
|
|
32
|
+
*/
|
|
33
|
+
actor: string;
|
|
34
|
+
/**
|
|
35
|
+
* A service credential (`x-service-token`) proving the caller is an authorized
|
|
36
|
+
* vertical, not just anyone with an actor id. Required when the control plane
|
|
37
|
+
* has real auth — the dev-actor header alone does not authenticate there. When
|
|
38
|
+
* set, it is the request's only credential: `actor` is not sent.
|
|
39
|
+
*/
|
|
40
|
+
serviceToken?: string;
|
|
41
|
+
/** Defaults to the global `fetch`. */
|
|
42
|
+
fetch?: typeof globalThis.fetch;
|
|
43
|
+
}
|
|
44
|
+
export { ControlPlaneError };
|
|
45
|
+
export interface ClientProvisionScopeInput {
|
|
46
|
+
tenantId: TenantId;
|
|
47
|
+
scopeId: ScopeId;
|
|
48
|
+
slug?: string;
|
|
49
|
+
kind?: string;
|
|
50
|
+
name?: string;
|
|
51
|
+
vertical?: string | null;
|
|
52
|
+
jurisdiction?: 'eu' | 'us' | 'global';
|
|
53
|
+
}
|
|
54
|
+
export declare class ControlPlaneClient extends ControlPlaneTransport {
|
|
55
|
+
constructor(options: ControlPlaneClientOptions);
|
|
56
|
+
createTenant(input: {
|
|
57
|
+
id: TenantId;
|
|
58
|
+
slug: string;
|
|
59
|
+
name: string;
|
|
60
|
+
}): Promise<Tenant>;
|
|
61
|
+
grantEntitlement(tenantId: TenantId, key: string, plan?: EntitlementGrantInput): Promise<EntitlementGrant[]>;
|
|
62
|
+
provisionScope(input: ClientProvisionScopeInput): Promise<Scope>;
|
|
63
|
+
/**
|
|
64
|
+
* Confirm the scope exists here, moving the directory row provisioning → active.
|
|
65
|
+
*
|
|
66
|
+
* In this (push) direction the vertical has already built the scope locally, so
|
|
67
|
+
* registering and confirming are the same moment — but they stay two calls so
|
|
68
|
+
* `provisionScope` means one thing everywhere, and so the directory is never the
|
|
69
|
+
* one deciding a scope is ready (K-31).
|
|
70
|
+
*/
|
|
71
|
+
activateScope(tenantId: TenantId, scopeId: ScopeId): Promise<Scope>;
|
|
72
|
+
getTenant(tenantId: TenantId): Promise<Tenant | undefined>;
|
|
73
|
+
getScopeRecord(tenantId: TenantId, scopeId: ScopeId): Promise<Scope | undefined>;
|
|
74
|
+
listEntitlements(tenantId: TenantId): Promise<EntitlementGrant[]>;
|
|
75
|
+
/**
|
|
76
|
+
* The tenants a login builds for, each flagged with whether it holds the `builder`
|
|
77
|
+
* entitlement — the builder studio's membership read (builder-plane.md §4). `externalId`
|
|
78
|
+
* is the login's OIDC subject. Service-token gated: an unset token is refused by the
|
|
79
|
+
* plane, never bypassed.
|
|
80
|
+
*
|
|
81
|
+
* The answer is PARSED, not asserted: a plane that renamed `entitled`, or answered an
|
|
82
|
+
* `{ error }` body with a 200, would hand a cast `entitled: undefined` — falsy, so the
|
|
83
|
+
* studio would lock the tenant out with the ordinary "not enabled" page and nothing
|
|
84
|
+
* anywhere would say the directory had changed shape. A refused parse throws instead.
|
|
85
|
+
* The message names the shape that was wrong, never the body — the body is directory
|
|
86
|
+
* facts about a person's tenants.
|
|
87
|
+
*/
|
|
88
|
+
identityTenants(externalId: string): Promise<IdentityTenant[]>;
|
|
89
|
+
/** Every table in the scope's own database, with row counts (Data view). */
|
|
90
|
+
listScopeTables(tenantId: TenantId, scopeId: ScopeId): Promise<ScopeTable[]>;
|
|
91
|
+
/** A bounded page of one table of the scope's database. */
|
|
92
|
+
readScopeTable(tenantId: TenantId, scopeId: ScopeId, input: ReadScopeTableInput): Promise<ScopeTablePage>;
|
|
93
|
+
/** One read-only SQL statement against the scope's database — the console (#219). */
|
|
94
|
+
queryScope(tenantId: TenantId, scopeId: ScopeId, input: QueryScopeInput): Promise<ScopeQueryResult>;
|
|
95
|
+
/**
|
|
96
|
+
* The scope's recorded permission refusals, bucketed per (actor, permission) with the
|
|
97
|
+
* window's own facts beside them — the view to open first, because the raw log's
|
|
98
|
+
* volume is attacker-influenceable and a prober can flood a newest-first page.
|
|
99
|
+
*/
|
|
100
|
+
summarizeDenials(tenantId: TenantId, scopeId: ScopeId, filter?: DenialFilter): Promise<DenialSummary>;
|
|
101
|
+
/** The raw rows behind a bucket, newest first. */
|
|
102
|
+
listDenials(tenantId: TenantId, scopeId: ScopeId, filter?: DenialFilter): Promise<PermissionDenial[]>;
|
|
103
|
+
/**
|
|
104
|
+
* The gate. Throws unless the tenant is active AND the scope exists and is
|
|
105
|
+
* active — the same fail-closed logic the kernel's `validateScopeAccess`
|
|
106
|
+
* applies locally, so a tenant-level cascade suspend bites too, not just a
|
|
107
|
+
* per-scope one. Call it before handing a request to the local scope host.
|
|
108
|
+
*/
|
|
109
|
+
assertScopeActive(tenantId: TenantId, scopeId: ScopeId): Promise<void>;
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
|