@enterprisestandard/esp 0.0.8-beta.20260205.5

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 ADDED
@@ -0,0 +1,38 @@
1
+ ## @enterprisestandard/esp
2
+
3
+ Helpers for ESP onboarding: when a new tenant is created on an ESA, an ESI calls the ESP to register the new tenant app so it appears in the ESP and can establish workload/SCIM connectivity. This package defines the registration contract and includes a small client and parsing helper. It is separate from the tenant API.
4
+
5
+ ### ESI (caller) example
6
+
7
+ ```ts
8
+ import { registerTenantApp } from '@enterprisestandard/esp';
9
+
10
+ await registerTenantApp('https://iam.example.com/api/es/tenant-apps', {
11
+ tenantId: 'tenant-123',
12
+ companyId: 'acme',
13
+ companyName: 'Acme Corp',
14
+ environmentType: 'PROD',
15
+ tenantUrl: 'https://tenant-123.example.com',
16
+ scimBaseUrl: 'https://tenant-123.example.com/api/es/iam',
17
+ workload: {
18
+ idpTokenUrl: 'https://sso.example.com/realms/acme/protocol/openid-connect/token',
19
+ clientId: 'es-iam',
20
+ clientSecret: 'secret',
21
+ issuer: 'https://sso.example.com/realms/acme',
22
+ },
23
+ });
24
+ ```
25
+
26
+ ### ESP (receiver) example
27
+
28
+ ```ts
29
+ import { parseRegisterTenantAppRequest } from '@enterprisestandard/esp';
30
+
31
+ export async function handleRegisterTenantApp(request: Request) {
32
+ const payload = await parseRegisterTenantAppRequest(request);
33
+ // Persist payload as a new application/tenant entry in the ESP.
34
+ return new Response(JSON.stringify({ registered: true }), { status: 201 });
35
+ }
36
+ ```
37
+
38
+ See the main repository README for tenant management and workload identity concepts.
@@ -0,0 +1,79 @@
1
+ import { EnvironmentType, WorkloadConfig } from "@enterprisestandard/core";
2
+ type RegisterTenantAppPayload = {
3
+ /**
4
+ * App/tenant identifier (same tenantId used by the ESA).
5
+ */
6
+ tenantId: string;
7
+ /**
8
+ * Company identifier (reporting only).
9
+ */
10
+ companyId: string;
11
+ /**
12
+ * Company name.
13
+ */
14
+ companyName: string;
15
+ /**
16
+ * Environment type (POC, DEV, QA, PROD).
17
+ */
18
+ environmentType: EnvironmentType;
19
+ /**
20
+ * Base URL of the tenant (if known).
21
+ */
22
+ tenantUrl?: string;
23
+ /**
24
+ * Display name for ESP UIs.
25
+ */
26
+ displayName?: string;
27
+ /**
28
+ * Product identifier (optional categorization for ESPs/ESIs).
29
+ */
30
+ productId?: string;
31
+ /**
32
+ * Application identifier (optional categorization for ESPs/ESIs).
33
+ */
34
+ applicationId?: string;
35
+ /**
36
+ * Base URL for the ESA's SCIM endpoints (e.g. https://tenant/app/api/es/iam).
37
+ */
38
+ scimBaseUrl?: string;
39
+ /**
40
+ * Workload identity configuration the ESP should use to call the ESA.
41
+ */
42
+ workload?: WorkloadConfig;
43
+ };
44
+ type RegisterTenantAppResult = {
45
+ registered: true;
46
+ appId?: string;
47
+ message?: string;
48
+ };
49
+ type RegisterTenantAppError = {
50
+ status: number;
51
+ code?: string;
52
+ message?: string;
53
+ details?: unknown;
54
+ };
55
+ type TenantAppRegistry = {
56
+ register: (payload: RegisterTenantAppPayload) => Promise<RegisterTenantAppResult>;
57
+ };
58
+ /**
59
+ * Parse and lightly validate a register-tenant-app request body.
60
+ * Throws if required fields are missing or invalid.
61
+ */
62
+ declare function parseRegisterTenantAppRequest(request: Request): Promise<RegisterTenantAppPayload>;
63
+ type HeadersInit = Headers | Array<[string, string]> | Record<string, string>;
64
+ type RegisterTenantAppOptions = {
65
+ headers?: HeadersInit;
66
+ fetch?: typeof fetch;
67
+ };
68
+ declare class RegisterTenantAppClientError extends Error {
69
+ readonly status: number;
70
+ readonly code?: string;
71
+ readonly details?: unknown;
72
+ constructor(error: RegisterTenantAppError);
73
+ }
74
+ /**
75
+ * Register a tenant app with an ESP endpoint.
76
+ * The ESP is responsible for authenticating the caller.
77
+ */
78
+ declare function registerTenantApp(espRegistrationUrl: string, payload: RegisterTenantAppPayload, options?: RegisterTenantAppOptions): Promise<RegisterTenantAppResult>;
79
+ export { registerTenantApp, parseRegisterTenantAppRequest, TenantAppRegistry, RegisterTenantAppResult, RegisterTenantAppPayload, RegisterTenantAppError, RegisterTenantAppClientError };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ class Q extends Error{status;code;details;constructor(A){super(A.message??"Register tenant app failed.");this.name="RegisterTenantAppClientError",this.status=A.status,this.code=A.code,this.details=A.details,Object.setPrototypeOf(this,Q.prototype)}}async function H(A){let z={status:A.status,message:`Register tenant app failed with status ${A.status}.`},D=await A.text();if(!D)return z;try{let J=JSON.parse(D);return{status:A.status,code:J.code,message:J.message??z.message,details:J.details}}catch{return{...z,details:D}}}function R(A){if(!A)return{"Content-Type":"application/json"};if(Array.isArray(A))return[["Content-Type","application/json"],...A];if(A instanceof Headers){let z=new Headers(A);if(!z.has("Content-Type"))z.set("Content-Type","application/json");return z}return{"Content-Type":"application/json",...A}}async function w(A,z,D={}){let G=await(D.fetch??fetch)(A,{method:"POST",headers:R(D.headers),body:JSON.stringify(z)});if(!G.ok){let K=await H(G);throw new Q(K)}if(G.status===204)return{registered:!0};let L=await G.text();if(!L)return{registered:!0};try{return JSON.parse(L)}catch{return{registered:!0,message:"Registration succeeded; response was not JSON."}}}var Z=["POC","DEV","QA","PROD"];function $(A){return A!==null&&typeof A==="object"&&!Array.isArray(A)&&Object.getPrototypeOf(A)===Object.prototype}function W(A,z,D,J){let G=A[z];if(G===void 0||G===null){if(J)D.push(`${z} is required`);return}if(typeof G!=="string"){D.push(`${z} must be a string`);return}if(J&&G.trim().length===0){D.push(`${z} must not be empty`);return}return G}async function M(A){let z;try{z=await A.json()}catch{throw Error("Register tenant app payload must be valid JSON.")}if(!$(z))throw Error("Register tenant app payload must be an object.");let D=[],J=W(z,"tenantId",D,!0),G=W(z,"companyId",D,!0),L=W(z,"companyName",D,!0),K=W(z,"environmentType",D,!0);if(K&&!Z.includes(K))D.push(`environmentType must be one of ${Z.join(", ")}`);let B=["tenantUrl","displayName","productId","applicationId","scimBaseUrl"];for(let X of B)if(z[X]!==void 0&&typeof z[X]!=="string")D.push(`${X} must be a string`);if(z.workload!==void 0&&!$(z.workload))D.push("workload must be an object");if(D.length>0)throw Error(`Invalid register tenant app payload: ${D.join("; ")}`);return{...z,tenantId:J??"",companyId:G??"",companyName:L??"",environmentType:K}}export{w as registerTenantApp,M as parseRegisterTenantAppRequest,Q as RegisterTenantAppClientError};
package/package.json ADDED
@@ -0,0 +1,26 @@
1
+ {
2
+ "name": "@enterprisestandard/esp",
3
+ "version": "0.0.8-beta.20260205.5",
4
+ "description": "Enterprise Standard ESP helpers",
5
+ "private": false,
6
+ "author": "enterprisestandard",
7
+ "license": "proprietary",
8
+ "type": "module",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "module": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "import": {
17
+ "types": "./dist/index.d.ts",
18
+ "default": "./dist/index.js"
19
+ }
20
+ },
21
+ "./package.json": "./package.json"
22
+ },
23
+ "dependencies": {
24
+ "@enterprisestandard/core": "0.0.8-beta.20260205.5"
25
+ }
26
+ }