@plantops/iam-client 0.1.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 (66) hide show
  1. package/README.md +11 -0
  2. package/dist/auth.d.ts +135 -0
  3. package/dist/auth.d.ts.map +1 -0
  4. package/dist/auth.js +168 -0
  5. package/dist/client.d.ts +110 -0
  6. package/dist/client.d.ts.map +1 -0
  7. package/dist/client.js +135 -0
  8. package/dist/endpoints/applications.d.ts +48 -0
  9. package/dist/endpoints/applications.d.ts.map +1 -0
  10. package/dist/endpoints/applications.js +30 -0
  11. package/dist/endpoints/audit.d.ts +43 -0
  12. package/dist/endpoints/audit.d.ts.map +1 -0
  13. package/dist/endpoints/audit.js +41 -0
  14. package/dist/endpoints/auth.d.ts +56 -0
  15. package/dist/endpoints/auth.d.ts.map +1 -0
  16. package/dist/endpoints/auth.js +88 -0
  17. package/dist/endpoints/authz.d.ts +31 -0
  18. package/dist/endpoints/authz.d.ts.map +1 -0
  19. package/dist/endpoints/authz.js +39 -0
  20. package/dist/endpoints/bindings.d.ts +17 -0
  21. package/dist/endpoints/bindings.d.ts.map +1 -0
  22. package/dist/endpoints/bindings.js +16 -0
  23. package/dist/endpoints/clients.d.ts +35 -0
  24. package/dist/endpoints/clients.d.ts.map +1 -0
  25. package/dist/endpoints/clients.js +31 -0
  26. package/dist/endpoints/entitlements.d.ts +40 -0
  27. package/dist/endpoints/entitlements.d.ts.map +1 -0
  28. package/dist/endpoints/entitlements.js +43 -0
  29. package/dist/endpoints/index.d.ts +22 -0
  30. package/dist/endpoints/index.d.ts.map +1 -0
  31. package/dist/endpoints/index.js +21 -0
  32. package/dist/endpoints/navigation.d.ts +25 -0
  33. package/dist/endpoints/navigation.d.ts.map +1 -0
  34. package/dist/endpoints/navigation.js +24 -0
  35. package/dist/endpoints/roles.d.ts +25 -0
  36. package/dist/endpoints/roles.d.ts.map +1 -0
  37. package/dist/endpoints/roles.js +21 -0
  38. package/dist/endpoints/scopes.d.ts +19 -0
  39. package/dist/endpoints/scopes.d.ts.map +1 -0
  40. package/dist/endpoints/scopes.js +18 -0
  41. package/dist/endpoints/service-accounts.d.ts +19 -0
  42. package/dist/endpoints/service-accounts.d.ts.map +1 -0
  43. package/dist/endpoints/service-accounts.js +19 -0
  44. package/dist/endpoints/users.d.ts +39 -0
  45. package/dist/endpoints/users.d.ts.map +1 -0
  46. package/dist/endpoints/users.js +24 -0
  47. package/dist/errors.d.ts +70 -0
  48. package/dist/errors.d.ts.map +1 -0
  49. package/dist/errors.js +113 -0
  50. package/dist/http.d.ts +113 -0
  51. package/dist/http.d.ts.map +1 -0
  52. package/dist/http.js +164 -0
  53. package/dist/index.d.ts +36 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +35 -0
  56. package/dist/lib/iam-client.d.ts +2 -0
  57. package/dist/lib/iam-client.d.ts.map +1 -0
  58. package/dist/lib/iam-client.js +3 -0
  59. package/dist/resolve-cache.d.ts +65 -0
  60. package/dist/resolve-cache.d.ts.map +1 -0
  61. package/dist/resolve-cache.js +98 -0
  62. package/dist/testing/mock-server.d.ts +65 -0
  63. package/dist/testing/mock-server.d.ts.map +1 -0
  64. package/dist/testing/mock-server.js +107 -0
  65. package/dist/tsconfig.lib.tsbuildinfo +1 -0
  66. package/package.json +40 -0
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `/iam/audit` — the queryable side of governance, Doc 06 §12.
3
+ *
4
+ * The last gap in "every Doc 06 endpoint has a typed method". `client.ts` used
5
+ * to explain why it was open: Session 25 had not built the endpoint and
6
+ * `@plantops/contracts` typed no audit record, so a method here would have had
7
+ * to invent both. Both exist now, so this closes it.
8
+ *
9
+ * ## One route, two tiers, and no tier parameter
10
+ *
11
+ * `iam.platform.audit.read` and `iam.client.audit.read` both admit, and which of
12
+ * them the caller holds decides nothing about the request. What a reader sees is
13
+ * decided by the `audit_trail_read` policy alone (Doc 07 §6, Doc 10 §7): a client
14
+ * admin their own tenant's rows, a platform admin everything including the
15
+ * `client_id IS NULL` rows that record platform-level acts. So there is no
16
+ * `?tier=`, and `client_id` only ever *narrows* what RLS already allows — a
17
+ * tenant the caller may not see is an empty page, never a 403.
18
+ *
19
+ * ## The export answers CSV, and says so
20
+ *
21
+ * It takes the same filters and no page: it is the whole of the filter or it is
22
+ * refused, because a truncated compliance export is indistinguishable from a
23
+ * complete one. `accept: 'text'` is what lets it come back through the same
24
+ * transport as everything else — same token, same error mapping — rather than
25
+ * being fetched around the outside of this library.
26
+ *
27
+ * There is no mutating route here and there is not meant to be (Doc 10 §7).
28
+ */
29
+ import type { AuditExportQuery, AuditQuery, AuditRecordDTO, Paginated } from '@plantops/contracts';
30
+ import type { Requester } from '../http.js';
31
+ export interface AuditApi {
32
+ list(query?: AuditQuery): Promise<Paginated<AuditRecordDTO>>;
33
+ /**
34
+ * The same filter as a CSV document, which the export itself audits
35
+ * (`audit.exported`, Doc 10 §7).
36
+ *
37
+ * Returns the CSV text. Handing it to the browser as a file is the caller's
38
+ * job — this library runs in Node too, where there is nothing to download to.
39
+ */
40
+ export(query?: AuditExportQuery): Promise<string>;
41
+ }
42
+ export declare function auditEndpoints(request: Requester): AuditApi;
43
+ //# sourceMappingURL=audit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.d.ts","sourceRoot":"","sources":["../../src/endpoints/audit.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACd,SAAS,EACV,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,QAAQ;IACvB,IAAI,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC;IAC7D;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,CAAC,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnD;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,SAAS,GAAG,QAAQ,CAa3D"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * `/iam/audit` — the queryable side of governance, Doc 06 §12.
3
+ *
4
+ * The last gap in "every Doc 06 endpoint has a typed method". `client.ts` used
5
+ * to explain why it was open: Session 25 had not built the endpoint and
6
+ * `@plantops/contracts` typed no audit record, so a method here would have had
7
+ * to invent both. Both exist now, so this closes it.
8
+ *
9
+ * ## One route, two tiers, and no tier parameter
10
+ *
11
+ * `iam.platform.audit.read` and `iam.client.audit.read` both admit, and which of
12
+ * them the caller holds decides nothing about the request. What a reader sees is
13
+ * decided by the `audit_trail_read` policy alone (Doc 07 §6, Doc 10 §7): a client
14
+ * admin their own tenant's rows, a platform admin everything including the
15
+ * `client_id IS NULL` rows that record platform-level acts. So there is no
16
+ * `?tier=`, and `client_id` only ever *narrows* what RLS already allows — a
17
+ * tenant the caller may not see is an empty page, never a 403.
18
+ *
19
+ * ## The export answers CSV, and says so
20
+ *
21
+ * It takes the same filters and no page: it is the whole of the filter or it is
22
+ * refused, because a truncated compliance export is indistinguishable from a
23
+ * complete one. `accept: 'text'` is what lets it come back through the same
24
+ * transport as everything else — same token, same error mapping — rather than
25
+ * being fetched around the outside of this library.
26
+ *
27
+ * There is no mutating route here and there is not meant to be (Doc 10 §7).
28
+ */
29
+ import { IAM_ROUTE_PREFIX } from '@plantops/contracts';
30
+ export function auditEndpoints(request) {
31
+ const base = `${IAM_ROUTE_PREFIX}/audit`;
32
+ return {
33
+ list: (query) => request({ method: 'GET', path: base, query: { ...query } }),
34
+ export: (query) => request({
35
+ method: 'GET',
36
+ path: `${base}/export`,
37
+ query: { ...query },
38
+ accept: 'text',
39
+ }),
40
+ };
41
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * `/auth/*` — Doc 06 §3.
3
+ *
4
+ * The only endpoint module that touches state: a login, a refresh and a service
5
+ * token all produce credentials, and dropping them into {@link TokenSession} is
6
+ * the whole reason a consumer never has to think about the `Authorization`
7
+ * header. Logout does the reverse on both sides of the wire — the server
8
+ * revokes the session, the client forgets the tokens — and both happen even if
9
+ * the call fails, because a caller who asked to be logged out is logged out.
10
+ */
11
+ import type { AccessTokenResponse, LoginRequest, ServiceTokenRequest, SessionDTO, TokenPairResponse } from '@plantops/contracts';
12
+ import type { TokenSession } from '../auth.js';
13
+ import type { Requester } from '../http.js';
14
+ /**
15
+ * `POST /auth/login`, plus the device label.
16
+ *
17
+ * The label is what makes the session list of Doc 06 §3 actionable — "Gate-3
18
+ * Terminal" rather than a uuid — and the endpoint has always accepted it. It is
19
+ * absent from `LoginRequest` in `@plantops/contracts`, so it is added here
20
+ * rather than silently dropped.
21
+ */
22
+ export interface LoginInput extends LoginRequest {
23
+ device_label?: string;
24
+ }
25
+ /** `POST /auth/password/reset-request` (Doc 06 §3). */
26
+ export interface PasswordResetRequestInput {
27
+ email: string;
28
+ client_slug: string;
29
+ }
30
+ /** `POST /auth/password/reset` (Doc 06 §3). */
31
+ export interface PasswordResetInput {
32
+ token: string;
33
+ new_password: string;
34
+ }
35
+ export interface AuthApi {
36
+ /** Human login. Stores the pair; the client is authenticated when it returns. */
37
+ login(input: LoginInput): Promise<TokenPairResponse>;
38
+ /**
39
+ * Renews explicitly. Shares the single in-flight exchange with the automatic
40
+ * refresh, so calling it during a burst costs nothing extra.
41
+ */
42
+ refresh(): Promise<TokenPairResponse>;
43
+ /** Service-account client-credentials exchange. No refresh token by design. */
44
+ serviceToken(input: ServiceTokenRequest): Promise<AccessTokenResponse>;
45
+ /** Revokes the current session server-side and forgets the tokens locally. */
46
+ logout(): Promise<void>;
47
+ /** The caller's own sessions — never anybody else's (Doc 03 §6). */
48
+ sessions(): Promise<SessionDTO[]>;
49
+ revokeSession(sessionId: string): Promise<void>;
50
+ requestPasswordReset(input: PasswordResetRequestInput): Promise<void>;
51
+ resetPassword(input: PasswordResetInput): Promise<void>;
52
+ }
53
+ export declare function authEndpoints(request: Requester, session: TokenSession,
54
+ /** Fired whenever the subject behind the tokens may have changed. */
55
+ onIdentityChange: () => void): AuthApi;
56
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/endpoints/auth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACZ,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;;GAOG;AACH,MAAM,WAAW,UAAW,SAAQ,YAAY;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,uDAAuD;AACvD,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,+CAA+C;AAC/C,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,OAAO;IACtB,iFAAiF;IACjF,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACrD;;;OAGG;IACH,OAAO,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACtC,+EAA+E;IAC/E,YAAY,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACvE,8EAA8E;IAC9E,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,oEAAoE;IACpE,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC,CAAC;IAClC,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,oBAAoB,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtE,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,SAAS,EAClB,OAAO,EAAE,YAAY;AACrB,qEAAqE;AACrE,gBAAgB,EAAE,MAAM,IAAI,GAC3B,OAAO,CAgFT"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * `/auth/*` — Doc 06 §3.
3
+ *
4
+ * The only endpoint module that touches state: a login, a refresh and a service
5
+ * token all produce credentials, and dropping them into {@link TokenSession} is
6
+ * the whole reason a consumer never has to think about the `Authorization`
7
+ * header. Logout does the reverse on both sides of the wire — the server
8
+ * revokes the session, the client forgets the tokens — and both happen even if
9
+ * the call fails, because a caller who asked to be logged out is logged out.
10
+ */
11
+ import { AUTH_ROUTE_PREFIX } from '@plantops/contracts';
12
+ export function authEndpoints(request, session,
13
+ /** Fired whenever the subject behind the tokens may have changed. */
14
+ onIdentityChange) {
15
+ return {
16
+ async login(input) {
17
+ const pair = await request({
18
+ method: 'POST',
19
+ path: `${AUTH_ROUTE_PREFIX}/login`,
20
+ body: input,
21
+ auth: 'none',
22
+ });
23
+ await session.adopt(pair);
24
+ onIdentityChange();
25
+ return pair;
26
+ },
27
+ async refresh() {
28
+ const pair = await session.refresh();
29
+ onIdentityChange();
30
+ return pair;
31
+ },
32
+ async serviceToken(input) {
33
+ const token = await request({
34
+ method: 'POST',
35
+ path: `${AUTH_ROUTE_PREFIX}/token`,
36
+ body: input,
37
+ auth: 'none',
38
+ });
39
+ await session.adopt(token);
40
+ onIdentityChange();
41
+ return token;
42
+ },
43
+ async logout() {
44
+ try {
45
+ await request({
46
+ method: 'POST',
47
+ path: `${AUTH_ROUTE_PREFIX}/logout`,
48
+ body: {},
49
+ });
50
+ }
51
+ finally {
52
+ // Local state is cleared even when the revocation call failed. The
53
+ // alternative — staying "logged in" because the network was down —
54
+ // leaves a token in a store the user believes they have emptied.
55
+ await session.clear();
56
+ onIdentityChange();
57
+ }
58
+ },
59
+ sessions() {
60
+ return request({
61
+ method: 'GET',
62
+ path: `${AUTH_ROUTE_PREFIX}/sessions`,
63
+ });
64
+ },
65
+ revokeSession(sessionId) {
66
+ return request({
67
+ method: 'POST',
68
+ path: `${AUTH_ROUTE_PREFIX}/sessions/${encodeURIComponent(sessionId)}/revoke`,
69
+ });
70
+ },
71
+ requestPasswordReset(input) {
72
+ return request({
73
+ method: 'POST',
74
+ path: `${AUTH_ROUTE_PREFIX}/password/reset-request`,
75
+ body: input,
76
+ auth: 'none',
77
+ });
78
+ },
79
+ resetPassword(input) {
80
+ return request({
81
+ method: 'POST',
82
+ path: `${AUTH_ROUTE_PREFIX}/password/reset`,
83
+ body: input,
84
+ auth: 'none',
85
+ });
86
+ },
87
+ };
88
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `/iam/permissions/*`, `/iam/introspect`, `/iam/.well-known/jwks.json` — the
3
+ * resolution endpoints of Doc 06 §11.
4
+ *
5
+ * These are the contract every future PlantOps module depends on, and the only
6
+ * part of this library that is on a hot path. `resolve` here is the *uncached*
7
+ * call; the cached one a module should actually use is `IamClient.grants()`,
8
+ * which puts {@link ResolveCache} in front of it.
9
+ *
10
+ * `jwks` is deliberately unauthenticated: it is the public half of the signing
11
+ * key, fetched by anyone verifying a token locally — which Doc 06 §11 says
12
+ * modules should prefer over `introspect`, so that the IAM stays off the
13
+ * per-request critical path.
14
+ */
15
+ import type { IntrospectResponse, JwksResponse, PermissionCheckRequest, PermissionCheckResponse, ResolvedGrants, ResolveQuery } from '@plantops/contracts';
16
+ import type { Requester } from '../http.js';
17
+ export interface PermissionsApi {
18
+ /**
19
+ * The bearer's complete grant set, minimized per Doc 04 §4.1. Not paginated —
20
+ * it is one cacheable unit. `applicationId` narrows it to one app's slice.
21
+ */
22
+ resolve(query?: ResolveQuery): Promise<ResolvedGrants>;
23
+ /** The point check: does this subject hold this permission over this node? */
24
+ check(body: PermissionCheckRequest): Promise<PermissionCheckResponse>;
25
+ /** For modules verifying an opaque or edge-case token. */
26
+ introspect(token: string): Promise<IntrospectResponse>;
27
+ /** The public keys, for local JWT verification. Needs no bearer token. */
28
+ jwks(): Promise<JwksResponse>;
29
+ }
30
+ export declare function permissionsEndpoints(request: Requester): PermissionsApi;
31
+ //# sourceMappingURL=authz.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"authz.d.ts","sourceRoot":"","sources":["../../src/endpoints/authz.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,YAAY,EACZ,sBAAsB,EACtB,uBAAuB,EACvB,cAAc,EACd,YAAY,EACb,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,cAAc;IAC7B;;;OAGG;IACH,OAAO,CAAC,KAAK,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACvD,8EAA8E;IAC9E,KAAK,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACtE,0DAA0D;IAC1D,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;IACvD,0EAA0E;IAC1E,IAAI,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;CAC/B;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,SAAS,GAAG,cAAc,CA2BvE"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * `/iam/permissions/*`, `/iam/introspect`, `/iam/.well-known/jwks.json` — the
3
+ * resolution endpoints of Doc 06 §11.
4
+ *
5
+ * These are the contract every future PlantOps module depends on, and the only
6
+ * part of this library that is on a hot path. `resolve` here is the *uncached*
7
+ * call; the cached one a module should actually use is `IamClient.grants()`,
8
+ * which puts {@link ResolveCache} in front of it.
9
+ *
10
+ * `jwks` is deliberately unauthenticated: it is the public half of the signing
11
+ * key, fetched by anyone verifying a token locally — which Doc 06 §11 says
12
+ * modules should prefer over `introspect`, so that the IAM stays off the
13
+ * per-request critical path.
14
+ */
15
+ import { IAM_ROUTE_PREFIX } from '@plantops/contracts';
16
+ export function permissionsEndpoints(request) {
17
+ return {
18
+ resolve: (query) => request({
19
+ method: 'GET',
20
+ path: `${IAM_ROUTE_PREFIX}/permissions/resolve`,
21
+ query: { ...query },
22
+ }),
23
+ check: (body) => request({
24
+ method: 'POST',
25
+ path: `${IAM_ROUTE_PREFIX}/permissions/check`,
26
+ body,
27
+ }),
28
+ introspect: (token) => request({
29
+ method: 'POST',
30
+ path: `${IAM_ROUTE_PREFIX}/introspect`,
31
+ body: { token },
32
+ }),
33
+ jwks: () => request({
34
+ method: 'GET',
35
+ path: `${IAM_ROUTE_PREFIX}/.well-known/jwks.json`,
36
+ auth: 'none',
37
+ }),
38
+ };
39
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * `/iam/role-bindings/*` — where WHO, WHAT and WHERE meet, Doc 06 §9.
3
+ *
4
+ * A binding is the only object in the system that grants anything: subject ×
5
+ * role × scope node, optionally until a date. Everything the resolution engine
6
+ * answers is a fold over these rows (Doc 04 §4).
7
+ */
8
+ import type { CreateRoleBindingRequest, Paginated, RoleBindingDTO, RoleBindingsQuery } from '@plantops/contracts';
9
+ import type { Requester } from '../http.js';
10
+ export interface RoleBindingsApi {
11
+ /** Subject is user XOR service account; the scope node must be the tenant's. */
12
+ create(body: CreateRoleBindingRequest): Promise<RoleBindingDTO>;
13
+ list(query?: RoleBindingsQuery): Promise<Paginated<RoleBindingDTO>>;
14
+ remove(id: string): Promise<void>;
15
+ }
16
+ export declare function roleBindingsEndpoints(request: Requester): RoleBindingsApi;
17
+ //# sourceMappingURL=bindings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindings.d.ts","sourceRoot":"","sources":["../../src/endpoints/bindings.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,SAAS,EACT,cAAc,EACd,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B,gFAAgF;IAChF,MAAM,CAAC,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK,CAAC,EAAE,iBAAiB,GAAG,OAAO,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,CAAC;IACpE,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,GAAG,eAAe,CASzE"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * `/iam/role-bindings/*` — where WHO, WHAT and WHERE meet, Doc 06 §9.
3
+ *
4
+ * A binding is the only object in the system that grants anything: subject ×
5
+ * role × scope node, optionally until a date. Everything the resolution engine
6
+ * answers is a fold over these rows (Doc 04 §4).
7
+ */
8
+ import { IAM_ROUTE_PREFIX } from '@plantops/contracts';
9
+ export function roleBindingsEndpoints(request) {
10
+ const base = `${IAM_ROUTE_PREFIX}/role-bindings`;
11
+ return {
12
+ create: (body) => request({ method: 'POST', path: base, body }),
13
+ list: (query) => request({ method: 'GET', path: base, query: { ...query } }),
14
+ remove: (id) => request({ method: 'DELETE', path: `${base}/${encodeURIComponent(id)}` }),
15
+ };
16
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * `/iam/clients/*` — tenant provisioning, Doc 06 §5.
3
+ *
4
+ * Platform-admin surface: creating a tenant, enabling applications for it, and
5
+ * minting the first client-admin who can then do everything else themselves
6
+ * (Doc 02 §5).
7
+ */
8
+ import type { BillableSiteRequest, BillableSitesRequest, ClientAdminDTO, ClientApplicationDTO, ClientDTO, CreateClientAdminRequest, CreateClientRequest, EnableApplicationsRequest, Paginated, PaginationQuery, ScopeNodeDTO, UpdateClientApplicationRequest, UpdateClientRequest } from '@plantops/contracts';
9
+ import type { Requester } from '../http.js';
10
+ export interface ClientsApi {
11
+ create(body: CreateClientRequest): Promise<ClientDTO>;
12
+ list(query?: PaginationQuery): Promise<Paginated<ClientDTO>>;
13
+ /** Update, or suspend. */
14
+ update(id: string, body: UpdateClientRequest): Promise<ClientDTO>;
15
+ enableApplications(id: string, body: EnableApplicationsRequest): Promise<ClientApplicationDTO[]>;
16
+ listApplications(id: string): Promise<ClientApplicationDTO[]>;
17
+ /** The per-tenant on/off switch; disabling drops that app's grants (Doc 02 §7). */
18
+ updateApplication(id: string, applicationId: string, body: UpdateClientApplicationRequest): Promise<ClientApplicationDTO>;
19
+ /**
20
+ * Marks or clears one node as a billable site — the meter of Doc 11 §10.1.
21
+ * Platform-tier, and never derived from `scope_node.kind`.
22
+ */
23
+ setBillableSite(id: string, scopeNodeId: string, body: BillableSiteRequest): Promise<ScopeNodeDTO>;
24
+ /**
25
+ * The same meter over many nodes — one call when provisioning a tenant.
26
+ *
27
+ * All-or-nothing: a batch breaching `max_sites` is refused entire. Ids
28
+ * outside this tenant are absent from the result rather than an error.
29
+ */
30
+ setBillableSites(id: string, body: BillableSitesRequest): Promise<ScopeNodeDTO[]>;
31
+ /** The initial client-admin user and its binding, in one call. */
32
+ createAdmin(id: string, body: CreateClientAdminRequest): Promise<ClientAdminDTO>;
33
+ }
34
+ export declare function clientsEndpoints(request: Requester): ClientsApi;
35
+ //# sourceMappingURL=clients.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clients.d.ts","sourceRoot":"","sources":["../../src/endpoints/clients.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,mBAAmB,EACnB,oBAAoB,EACpB,cAAc,EACd,oBAAoB,EACpB,SAAS,EACT,wBAAwB,EACxB,mBAAmB,EACnB,yBAAyB,EACzB,SAAS,EACT,eAAe,EACf,YAAY,EACZ,8BAA8B,EAC9B,mBAAmB,EACpB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACtD,IAAI,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;IAC7D,0BAA0B;IAC1B,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAElE,kBAAkB,CAChB,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IACnC,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC,CAAC;IAC9D,mFAAmF;IACnF,iBAAiB,CACf,EAAE,EAAE,MAAM,EACV,aAAa,EAAE,MAAM,EACrB,IAAI,EAAE,8BAA8B,GACnC,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAEjC;;;OAGG;IACH,eAAe,CACb,EAAE,EAAE,MAAM,EACV,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,YAAY,CAAC,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAElF,kEAAkE;IAClE,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;CAClF;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,SAAS,GAAG,UAAU,CAgC/D"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * `/iam/clients/*` — tenant provisioning, Doc 06 §5.
3
+ *
4
+ * Platform-admin surface: creating a tenant, enabling applications for it, and
5
+ * minting the first client-admin who can then do everything else themselves
6
+ * (Doc 02 §5).
7
+ */
8
+ import { IAM_ROUTE_PREFIX } from '@plantops/contracts';
9
+ export function clientsEndpoints(request) {
10
+ const base = `${IAM_ROUTE_PREFIX}/clients`;
11
+ const at = (id, suffix = '') => `${base}/${encodeURIComponent(id)}${suffix}`;
12
+ return {
13
+ create: (body) => request({ method: 'POST', path: base, body }),
14
+ list: (query) => request({ method: 'GET', path: base, query: { ...query } }),
15
+ update: (id, body) => request({ method: 'PATCH', path: at(id), body }),
16
+ setBillableSite: (id, scopeNodeId, body) => request({
17
+ method: 'PATCH',
18
+ path: at(id, `/scopes/${encodeURIComponent(scopeNodeId)}/billable-site`),
19
+ body,
20
+ }),
21
+ setBillableSites: (id, body) => request({ method: 'PATCH', path: at(id, '/billable-sites'), body }),
22
+ enableApplications: (id, body) => request({ method: 'POST', path: at(id, '/applications'), body }),
23
+ listApplications: (id) => request({ method: 'GET', path: at(id, '/applications') }),
24
+ updateApplication: (id, applicationId, body) => request({
25
+ method: 'PATCH',
26
+ path: at(id, `/applications/${encodeURIComponent(applicationId)}`),
27
+ body,
28
+ }),
29
+ createAdmin: (id, body) => request({ method: 'POST', path: at(id, '/admins'), body }),
30
+ };
31
+ }
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `/iam/entitlements` and `/iam/licence` — what this tenant is entitled to.
3
+ *
4
+ * Both are readable by any authenticated subject of the tenant, so neither
5
+ * needs a platform credential. They answer different questions and a consumer
6
+ * usually wants the first:
7
+ *
8
+ * - {@link EntitlementsApi.view} is the whole picture — the tenant's term and
9
+ * ceilings, live usage counts, and one row per application with its own term.
10
+ * - {@link EntitlementsApi.licence} is the date alone, which is what a console
11
+ * header renders. It predates the other and is kept because it has consumers.
12
+ *
13
+ * ## Cache this on a clock, never on the grants counter
14
+ *
15
+ * `IamClient.grants()` is cached against a `(client, subject)` version counter
16
+ * that moves whenever access changes. Entitlement state has no such counter and
17
+ * cannot have one: **nothing writes at the moment a term expires**. Cache it
18
+ * against {@link EntitlementsView.as_of} with a bound of your choosing — five
19
+ * minutes is the documented suggestion — and never against `ResolvedGrants`.
20
+ *
21
+ * Getting this wrong does not produce a briefly-stale value. It produces
22
+ * `expired: false` forever.
23
+ *
24
+ * ## Nothing here is enforced by the IAM
25
+ *
26
+ * An application past its term still resolves grants and still appears in
27
+ * navigation. Refusing is the consuming module's job, and Doc 12 states that
28
+ * obligation normatively. Reading `expired` and doing nothing with it is the
29
+ * same as not reading it.
30
+ */
31
+ import type { EntitlementsView, LicenceView } from '@plantops/contracts';
32
+ import type { Requester } from '../http.js';
33
+ export interface EntitlementsApi {
34
+ /** Term, ceilings, live usage, and one row per application. */
35
+ view(): Promise<EntitlementsView>;
36
+ /** The effective term alone — licence file and subscription, whichever is sooner. */
37
+ licence(): Promise<LicenceView>;
38
+ }
39
+ export declare function entitlementsEndpoints(request: Requester): EntitlementsApi;
40
+ //# sourceMappingURL=entitlements.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entitlements.d.ts","sourceRoot":"","sources":["../../src/endpoints/entitlements.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGzE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,MAAM,WAAW,eAAe;IAC9B,+DAA+D;IAC/D,IAAI,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAClC,qFAAqF;IACrF,OAAO,IAAI,OAAO,CAAC,WAAW,CAAC,CAAC;CACjC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,SAAS,GAAG,eAAe,CAazE"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * `/iam/entitlements` and `/iam/licence` — what this tenant is entitled to.
3
+ *
4
+ * Both are readable by any authenticated subject of the tenant, so neither
5
+ * needs a platform credential. They answer different questions and a consumer
6
+ * usually wants the first:
7
+ *
8
+ * - {@link EntitlementsApi.view} is the whole picture — the tenant's term and
9
+ * ceilings, live usage counts, and one row per application with its own term.
10
+ * - {@link EntitlementsApi.licence} is the date alone, which is what a console
11
+ * header renders. It predates the other and is kept because it has consumers.
12
+ *
13
+ * ## Cache this on a clock, never on the grants counter
14
+ *
15
+ * `IamClient.grants()` is cached against a `(client, subject)` version counter
16
+ * that moves whenever access changes. Entitlement state has no such counter and
17
+ * cannot have one: **nothing writes at the moment a term expires**. Cache it
18
+ * against {@link EntitlementsView.as_of} with a bound of your choosing — five
19
+ * minutes is the documented suggestion — and never against `ResolvedGrants`.
20
+ *
21
+ * Getting this wrong does not produce a briefly-stale value. It produces
22
+ * `expired: false` forever.
23
+ *
24
+ * ## Nothing here is enforced by the IAM
25
+ *
26
+ * An application past its term still resolves grants and still appears in
27
+ * navigation. Refusing is the consuming module's job, and Doc 12 states that
28
+ * obligation normatively. Reading `expired` and doing nothing with it is the
29
+ * same as not reading it.
30
+ */
31
+ import { IAM_ROUTE_PREFIX } from '@plantops/contracts';
32
+ export function entitlementsEndpoints(request) {
33
+ return {
34
+ view: () => request({
35
+ method: 'GET',
36
+ path: `${IAM_ROUTE_PREFIX}/entitlements`,
37
+ }),
38
+ licence: () => request({
39
+ method: 'GET',
40
+ path: `${IAM_ROUTE_PREFIX}/licence`,
41
+ }),
42
+ };
43
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * The endpoint modules, one per section of Doc 06.
3
+ *
4
+ * Each exports an interface and a factory that takes a {@link Requester} and
5
+ * nothing else. That shape is what keeps `client.ts` from becoming a
6
+ * thousand-line class, and it is what lets a consumer that wants only one
7
+ * surface — a migration script that talks to the registry, say — build it
8
+ * without a token store or a cache.
9
+ */
10
+ export * from './applications.js';
11
+ export * from './audit.js';
12
+ export * from './auth.js';
13
+ export * from './authz.js';
14
+ export * from './bindings.js';
15
+ export * from './clients.js';
16
+ export * from './entitlements.js';
17
+ export * from './navigation.js';
18
+ export * from './roles.js';
19
+ export * from './scopes.js';
20
+ export * from './service-accounts.js';
21
+ export * from './users.js';
22
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/endpoints/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,YAAY,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * The endpoint modules, one per section of Doc 06.
3
+ *
4
+ * Each exports an interface and a factory that takes a {@link Requester} and
5
+ * nothing else. That shape is what keeps `client.ts` from becoming a
6
+ * thousand-line class, and it is what lets a consumer that wants only one
7
+ * surface — a migration script that talks to the registry, say — build it
8
+ * without a token store or a cache.
9
+ */
10
+ export * from './applications.js';
11
+ export * from './audit.js';
12
+ export * from './auth.js';
13
+ export * from './authz.js';
14
+ export * from './bindings.js';
15
+ export * from './clients.js';
16
+ export * from './entitlements.js';
17
+ export * from './navigation.js';
18
+ export * from './roles.js';
19
+ export * from './scopes.js';
20
+ export * from './service-accounts.js';
21
+ export * from './users.js';
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `GET /iam/navigation` — the menu, Doc 06 §11 and Doc 05.
3
+ *
4
+ * A pure function of the bearer's grants and the nav catalog: containers with
5
+ * no visible descendant are pruned, unmapped nodes are hidden unless they opt
6
+ * in with `is_public`, and disabled applications never appear. Called with no
7
+ * `applicationId` it returns the cross-application shell — one top-level node
8
+ * per enabled app.
9
+ *
10
+ * Not cached here. The menu changes when the catalog changes as well as when
11
+ * grants change, and it is fetched once per shell load rather than once per
12
+ * request, so there is no burst for a cache to absorb — only staleness to
13
+ * introduce.
14
+ */
15
+ import type { NavigationResponse } from '@plantops/contracts';
16
+ import type { Requester } from '../http.js';
17
+ /** Doc 05 §5's optional narrowing to one application's menu. */
18
+ export interface NavigationQuery {
19
+ applicationId?: string;
20
+ }
21
+ export interface NavigationApi {
22
+ tree(query?: NavigationQuery): Promise<NavigationResponse>;
23
+ }
24
+ export declare function navigationEndpoints(request: Requester): NavigationApi;
25
+ //# sourceMappingURL=navigation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"navigation.d.ts","sourceRoot":"","sources":["../../src/endpoints/navigation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAG9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,gEAAgE;AAChE,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,KAAK,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAC;CAC5D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,GAAG,aAAa,CASrE"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * `GET /iam/navigation` — the menu, Doc 06 §11 and Doc 05.
3
+ *
4
+ * A pure function of the bearer's grants and the nav catalog: containers with
5
+ * no visible descendant are pruned, unmapped nodes are hidden unless they opt
6
+ * in with `is_public`, and disabled applications never appear. Called with no
7
+ * `applicationId` it returns the cross-application shell — one top-level node
8
+ * per enabled app.
9
+ *
10
+ * Not cached here. The menu changes when the catalog changes as well as when
11
+ * grants change, and it is fetched once per shell load rather than once per
12
+ * request, so there is no burst for a cache to absorb — only staleness to
13
+ * introduce.
14
+ */
15
+ import { IAM_ROUTE_PREFIX } from '@plantops/contracts';
16
+ export function navigationEndpoints(request) {
17
+ return {
18
+ tree: (query) => request({
19
+ method: 'GET',
20
+ path: `${IAM_ROUTE_PREFIX}/navigation`,
21
+ query: { ...query },
22
+ }),
23
+ };
24
+ }
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `/iam/roles/*` — the WHAT dimension, Doc 06 §7.
3
+ *
4
+ * A role is a bag of permissions drawn from the tenant's *enabled* applications;
5
+ * `setPermissions` is a PUT because the screen behind it is a checklist, and a
6
+ * checklist submits a set, not a diff.
7
+ */
8
+ import type { CreateRoleRequest, Paginated, PaginationQuery, PermissionCatalogResponse, RoleDTO, RolePermissionsResponse, SetRolePermissionsRequest, UpdateRoleRequest } from '@plantops/contracts';
9
+ import type { Requester } from '../http.js';
10
+ export interface RolesApi {
11
+ create(body: CreateRoleRequest): Promise<RoleDTO>;
12
+ list(query?: PaginationQuery): Promise<Paginated<RoleDTO>>;
13
+ update(id: string, body: UpdateRoleRequest): Promise<RoleDTO>;
14
+ /** Cascades the role's bindings, with an audit record for each (Doc 06 §7). */
15
+ remove(id: string): Promise<void>;
16
+ /**
17
+ * Everything a role of the caller's tenant may be given — the source of
18
+ * Doc 09 §3.2's picker. Every entry is one `setPermissions` will accept.
19
+ */
20
+ permissionCatalog(): Promise<PermissionCatalogResponse>;
21
+ permissions(id: string): Promise<RolePermissionsResponse>;
22
+ setPermissions(id: string, body: SetRolePermissionsRequest): Promise<RolePermissionsResponse>;
23
+ }
24
+ export declare function rolesEndpoints(request: Requester): RolesApi;
25
+ //# sourceMappingURL=roles.d.ts.map