@kortexya/reasoninglayer 0.23.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -113,19 +113,55 @@ console.log(result.solutions); // Solutions with bindings
113
113
 
114
114
  ```typescript
115
115
  const client = new ReasoningLayerClient({
116
- baseUrl: 'http://localhost:8083', // Required
117
- tenantId: 'your-tenant-uuid', // Required, sent as X-Tenant-Id
118
- bearerToken: 'eyJhbGciOi...', // Optional, sent as Authorization: Bearer
119
- userId: 'user-uuid', // Optional, sent as X-User-Id
120
- namespaceId: 'ns-uuid', // Optional, sent as X-Namespace-Id
121
- maxRetries: 3, // Default: 3
122
- timeoutMs: 30000, // Default: 30000ms
123
- retryOn503: true, // Default: true
124
- fetch: customFetch, // Optional: custom fetch implementation
125
- interceptors: [loggingMiddleware], // Optional: request/response middleware
116
+ baseUrl: 'http://localhost:8083', // Required
117
+ tenantId: 'your-tenant-uuid', // Required, sent as X-Tenant-Id
118
+ auth: { mode: 'bearer', token: 'eyJ…' }, // Required see Authentication below
119
+ userId: 'user-uuid', // Optional, sent as X-User-Id
120
+ namespaceId: 'ns-uuid', // Optional, sent as X-Namespace-Id
121
+ maxRetries: 3, // Default: 3
122
+ timeoutMs: 30000, // Default: 30000ms
123
+ retryOn503: true, // Default: false
124
+ fetch: customFetch, // Optional: custom fetch implementation
125
+ interceptors: [loggingMiddleware], // Optional: request/response middleware
126
126
  });
127
127
  ```
128
128
 
129
+ ### Authentication
130
+
131
+ The `auth` field is **required** and must be one of two modes. There is no
132
+ unauthenticated mode — the SDK will not silently ship requests that the
133
+ gateway would reject.
134
+
135
+ **`bearer` — server-side / programmatic usage.** Send a long-lived API token
136
+ (typically a service-account token issued by the auth gateway) on every
137
+ request as `Authorization: Bearer <token>`.
138
+
139
+ ```typescript
140
+ const client = new ReasoningLayerClient({
141
+ baseUrl: 'https://platform.example.com',
142
+ tenantId: '550e8400-e29b-41d4-a716-446655440000',
143
+ auth: { mode: 'bearer', token: process.env.RL_API_TOKEN! },
144
+ });
145
+ ```
146
+
147
+ **`cookie` — in-browser SPA usage.** Rely on the session cookie set by the
148
+ auth gateway after an interactive login. The SDK issues HTTP requests with
149
+ `credentials: 'include'` so the cookie is attached even on cross-origin
150
+ requests; WebSocket connections rely on the browser attaching the cookie
151
+ automatically (same-origin).
152
+
153
+ ```typescript
154
+ const client = new ReasoningLayerClient({
155
+ baseUrl: 'https://platform.example.com',
156
+ tenantId: '550e8400-e29b-41d4-a716-446655440000',
157
+ auth: { mode: 'cookie' },
158
+ });
159
+ ```
160
+
161
+ The choice between modes belongs to the **deployment context** of the
162
+ caller, not the SDK — pick `bearer` from servers and CI, `cookie` from
163
+ browser code that has already gone through the platform's login flow.
164
+
129
165
  ## Response Metadata
130
166
 
131
167
  ```typescript
package/dist/index.cjs CHANGED
@@ -7,7 +7,7 @@ var __export = (target, all) => {
7
7
  };
8
8
 
9
9
  // src/config.ts
10
- var SDK_VERSION = "0.23.0";
10
+ var SDK_VERSION = "1.0.0";
11
11
  function resolveConfig(config) {
12
12
  if (!config.baseUrl) {
13
13
  throw new Error("ClientConfig.baseUrl is required");
@@ -15,13 +15,21 @@ function resolveConfig(config) {
15
15
  if (!config.tenantId) {
16
16
  throw new Error("ClientConfig.tenantId is required");
17
17
  }
18
+ if (!config.auth) {
19
+ throw new Error(
20
+ "ClientConfig.auth is required \u2014 pass { mode: 'bearer', token } or { mode: 'cookie' }"
21
+ );
22
+ }
23
+ if (config.auth.mode === "bearer" && !config.auth.token) {
24
+ throw new Error("ClientConfig.auth.token is required when mode is 'bearer'");
25
+ }
18
26
  return {
19
27
  baseUrl: config.baseUrl.replace(/\/+$/, ""),
20
28
  tenantId: config.tenantId,
21
29
  userId: config.userId,
22
30
  namespaceId: config.namespaceId,
23
31
  authenticatedUser: config.authenticatedUser,
24
- bearerToken: config.bearerToken,
32
+ auth: config.auth,
25
33
  timeoutMs: config.timeoutMs ?? 3e4,
26
34
  maxRetries: config.maxRetries ?? 3,
27
35
  retryOn503: config.retryOn503 ?? false,
@@ -377,8 +385,8 @@ var WebSocketClient = class {
377
385
  buildUrl(path, params) {
378
386
  const baseUrl = this.config.baseUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:");
379
387
  const queryParams = new URLSearchParams({ tenant_id: this.config.tenantId });
380
- if (this.config.bearerToken) {
381
- queryParams.set("token", this.config.bearerToken);
388
+ if (this.config.auth.mode === "bearer") {
389
+ queryParams.set("token", this.config.auth.token);
382
390
  }
383
391
  if (params) {
384
392
  for (const [key, value] of Object.entries(params)) {
@@ -617,7 +625,7 @@ function buildAuthHeaders(config) {
617
625
  if (config.userId) headers["X-User-Id"] = config.userId;
618
626
  if (config.namespaceId) headers["X-Namespace-Id"] = config.namespaceId;
619
627
  if (config.authenticatedUser) headers["X-Authenticated-User"] = config.authenticatedUser;
620
- if (config.bearerToken) headers["Authorization"] = `Bearer ${config.bearerToken}`;
628
+ if (config.auth.mode === "bearer") headers["Authorization"] = `Bearer ${config.auth.token}`;
621
629
  return headers;
622
630
  }
623
631
  function transformRequestInit(init) {
@@ -644,10 +652,14 @@ function createCustomFetch(config) {
644
652
  const response = await executeFetch(input, transformedInit, timeoutMs, config);
645
653
  if (response.ok) return response;
646
654
  let body;
655
+ const rawText = await response.clone().text().catch(() => "");
647
656
  try {
648
- body = await response.clone().json();
657
+ body = rawText ? JSON.parse(rawText) : { error: "unknown", message: `HTTP ${response.status}` };
649
658
  } catch {
650
- body = { error: "unknown", message: `HTTP ${response.status}` };
659
+ body = {
660
+ error: "unknown",
661
+ message: rawText.trim() || `HTTP ${response.status}`
662
+ };
651
663
  }
652
664
  const apiError = createApiError(response.status, body, response.headers);
653
665
  if (response.status === 429 && attempt < maxRetries) {
@@ -685,7 +697,11 @@ async function executeFetch(input, init, timeoutMs, config) {
685
697
  const existingSignal = init?.signal;
686
698
  const combinedSignal = existingSignal ? AbortSignal.any([timeoutController.signal, existingSignal]) : timeoutController.signal;
687
699
  const url = typeof input === "string" ? input : input instanceof URL ? input.toString() : input.url;
688
- const request = new Request(url, { ...init, signal: combinedSignal });
700
+ const request = new Request(url, {
701
+ ...init,
702
+ signal: combinedSignal,
703
+ credentials: config.auth.mode === "cookie" ? "include" : "same-origin"
704
+ });
689
705
  try {
690
706
  const fetchFn = config.fetch;
691
707
  const baseFetch = (req) => fetchFn(req);
@@ -19393,10 +19409,14 @@ function ComponentHealthDtoFromApiToFront(dto) {
19393
19409
  };
19394
19410
  }
19395
19411
  function EnrichedHealthResponseFromApiToFront(dto) {
19412
+ if (dto == null) {
19413
+ return { buildInfo: void 0, components: [], status: "unknown" };
19414
+ }
19415
+ const rawBuildInfo = dto.build_info;
19396
19416
  return {
19397
- buildInfo: BuildInfoDtoFromApiToFront(dto.build_info),
19398
- components: dto.components.map(ComponentHealthDtoFromApiToFront),
19399
- status: dto.status
19417
+ buildInfo: rawBuildInfo == null ? void 0 : BuildInfoDtoFromApiToFront(rawBuildInfo),
19418
+ components: (dto.components ?? []).map(ComponentHealthDtoFromApiToFront),
19419
+ status: dto.status ?? "unknown"
19400
19420
  };
19401
19421
  }
19402
19422