@nilejs/client 0.0.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 +110 -0
- package/dist/index.cjs +144 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +66 -0
- package/dist/index.d.ts +66 -0
- package/dist/index.js +117 -0
- package/dist/index.js.map +1 -0
- package/package.json +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @nilejs/client
|
|
2
|
+
|
|
3
|
+
A standalone, type-safe client for the [Nile](https://github.com/nile-js/nile) backend framework.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **Type-safe:** Generates and consumes types from your Nile backend for full compile-time payload validation.
|
|
8
|
+
- **Graceful Error Handling:** Uses the result pattern `{ error, data }` instead of throwing exceptions.
|
|
9
|
+
- **Zero Runtime Dependencies:** Built on raw `fetch` for maximum compatibility (uses `slang-ts` for safety).
|
|
10
|
+
- **Flexible:** Supports custom headers, credentials, and timeouts per request.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# Using npm
|
|
16
|
+
npm install @nilejs/client
|
|
17
|
+
|
|
18
|
+
# Using pnpm
|
|
19
|
+
pnpm add @nilejs/client
|
|
20
|
+
|
|
21
|
+
# Using bun
|
|
22
|
+
bun add @nilejs/client
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
### 1. Create a Client
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
import { createNileClient } from "@nilejs/client";
|
|
31
|
+
|
|
32
|
+
const nile = createNileClient({
|
|
33
|
+
baseUrl: "http://localhost:8000/api",
|
|
34
|
+
credentials: "include",
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 2. Invoke Actions
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
const { error, data } = await nile.invoke({
|
|
42
|
+
service: "tasks",
|
|
43
|
+
action: "create",
|
|
44
|
+
payload: { title: "Buy milk" },
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
if (error) {
|
|
48
|
+
console.error("Action failed:", error);
|
|
49
|
+
} else {
|
|
50
|
+
console.log("Success:", data);
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 3. Full Type Safety (Recommended)
|
|
55
|
+
|
|
56
|
+
Generate your types using the Nile CLI:
|
|
57
|
+
`nile generate schema --output ./src/generated`
|
|
58
|
+
|
|
59
|
+
Then use the generated `ServicePayloads` map:
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { createNileClient } from "@nilejs/client";
|
|
63
|
+
import type { ServicePayloads } from "./generated/types";
|
|
64
|
+
|
|
65
|
+
const nile = createNileClient<ServicePayloads>({
|
|
66
|
+
baseUrl: "/api"
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
// Full autocomplete and type-checking for service, action, and payload
|
|
70
|
+
await nile.invoke({
|
|
71
|
+
service: "tasks",
|
|
72
|
+
action: "create",
|
|
73
|
+
payload: { title: "Buy milk" }
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 4. Discovery & Reflection
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// Explore services and actions
|
|
81
|
+
const { data: services } = await nile.explore({ service: "*", action: "*" });
|
|
82
|
+
|
|
83
|
+
// Fetch action schemas
|
|
84
|
+
const { data: schemas } = await nile.schema({ service: "tasks", action: "*" });
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## API
|
|
88
|
+
|
|
89
|
+
### `createNileClient<T>(config)`
|
|
90
|
+
|
|
91
|
+
Returns a Nile client instance.
|
|
92
|
+
|
|
93
|
+
- `config.baseUrl`: The base URL of your Nile API (e.g., `http://localhost:8000/api`).
|
|
94
|
+
- `config.credentials`: Request credentials (`include`, `omit`, `same-origin`).
|
|
95
|
+
- `config.headers`: Global headers to include in every request.
|
|
96
|
+
- `config.timeout`: Default request timeout in milliseconds (default: 30000).
|
|
97
|
+
|
|
98
|
+
### Methods
|
|
99
|
+
|
|
100
|
+
All methods return a `Promise<ClientResult<T>>` where `ClientResult<T>` is `{ error: string | null, data: T | null }`.
|
|
101
|
+
|
|
102
|
+
- `invoke(params)`: Execute a specific service action.
|
|
103
|
+
- `explore(params)`: Discover services/actions using wildcards.
|
|
104
|
+
- `schema(params)`: Fetch action schemas as JSON Schema.
|
|
105
|
+
|
|
106
|
+
Each method accepts an optional `timeout` and any standard fetch `headers` in its parameters.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
createNileClient: () => createNileClient
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(index_exports);
|
|
26
|
+
|
|
27
|
+
// src/safe-try.ts
|
|
28
|
+
function safeTry(fn) {
|
|
29
|
+
try {
|
|
30
|
+
const result = fn();
|
|
31
|
+
if (result instanceof Promise) {
|
|
32
|
+
return result.then(
|
|
33
|
+
(value) => ({
|
|
34
|
+
isOk: true,
|
|
35
|
+
value,
|
|
36
|
+
isErr: false,
|
|
37
|
+
error: null
|
|
38
|
+
})
|
|
39
|
+
).catch(
|
|
40
|
+
(error) => ({
|
|
41
|
+
isOk: false,
|
|
42
|
+
value: null,
|
|
43
|
+
isErr: true,
|
|
44
|
+
error
|
|
45
|
+
})
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
return { isOk: true, value: result, isErr: false, error: null };
|
|
49
|
+
} catch (error) {
|
|
50
|
+
return { isOk: false, value: null, isErr: true, error };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/request.ts
|
|
55
|
+
async function sendNileRequest(config, intent, service, action, payload, options = {}) {
|
|
56
|
+
const { timeout = config.timeout ?? 3e4, ...fetchOptions } = options;
|
|
57
|
+
const controller = new AbortController();
|
|
58
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
59
|
+
const requestResult = await safeTry(async () => {
|
|
60
|
+
const response = await fetch(`${config.baseUrl}/services`, {
|
|
61
|
+
method: "POST",
|
|
62
|
+
headers: {
|
|
63
|
+
"Content-Type": "application/json",
|
|
64
|
+
...config.headers,
|
|
65
|
+
...fetchOptions.headers
|
|
66
|
+
},
|
|
67
|
+
credentials: config.credentials,
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
intent,
|
|
70
|
+
service,
|
|
71
|
+
action,
|
|
72
|
+
payload: payload ?? {}
|
|
73
|
+
}),
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
...fetchOptions
|
|
76
|
+
});
|
|
77
|
+
clearTimeout(timeoutId);
|
|
78
|
+
const json = await response.json();
|
|
79
|
+
return json;
|
|
80
|
+
});
|
|
81
|
+
if (requestResult.isErr) {
|
|
82
|
+
const error = requestResult.error;
|
|
83
|
+
let errorMsg = "An unknown error occurred";
|
|
84
|
+
if (error instanceof Error) {
|
|
85
|
+
errorMsg = error.name === "AbortError" ? "Request timed out" : error.message;
|
|
86
|
+
} else {
|
|
87
|
+
errorMsg = String(error);
|
|
88
|
+
}
|
|
89
|
+
return { error: errorMsg, data: null };
|
|
90
|
+
}
|
|
91
|
+
const result = requestResult.value;
|
|
92
|
+
if (!result.status) {
|
|
93
|
+
return { error: result.message, data: result.data };
|
|
94
|
+
}
|
|
95
|
+
return { error: null, data: result.data };
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// src/create-client.ts
|
|
99
|
+
function createNileClient(config) {
|
|
100
|
+
return {
|
|
101
|
+
/** Invoke a service action through the backend engine pipeline */
|
|
102
|
+
invoke: async (params) => {
|
|
103
|
+
const { service, action, payload, ...options } = params;
|
|
104
|
+
const result = await sendNileRequest(
|
|
105
|
+
config,
|
|
106
|
+
"execute",
|
|
107
|
+
service,
|
|
108
|
+
action,
|
|
109
|
+
payload,
|
|
110
|
+
options
|
|
111
|
+
);
|
|
112
|
+
return result;
|
|
113
|
+
},
|
|
114
|
+
/** Discover available services and actions (supports "*" wildcard) */
|
|
115
|
+
explore: async (params) => {
|
|
116
|
+
const { service, action, ...options } = params;
|
|
117
|
+
return await sendNileRequest(
|
|
118
|
+
config,
|
|
119
|
+
"explore",
|
|
120
|
+
service,
|
|
121
|
+
action,
|
|
122
|
+
{},
|
|
123
|
+
options
|
|
124
|
+
);
|
|
125
|
+
},
|
|
126
|
+
/** Get action schemas as JSON Schema (supports "*" wildcard) */
|
|
127
|
+
schema: async (params) => {
|
|
128
|
+
const { service, action, ...options } = params;
|
|
129
|
+
return await sendNileRequest(
|
|
130
|
+
config,
|
|
131
|
+
"schema",
|
|
132
|
+
service,
|
|
133
|
+
action,
|
|
134
|
+
{},
|
|
135
|
+
options
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
141
|
+
0 && (module.exports = {
|
|
142
|
+
createNileClient
|
|
143
|
+
});
|
|
144
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/safe-try.ts","../src/request.ts","../src/create-client.ts"],"sourcesContent":["export { createNileClient } from \"./create-client.js\";\nexport type * from \"./types.js\";\n","/**\n * Internal result type for safeTry\n */\nexport type SafeResult<T, E = unknown> =\n | { isOk: true; value: T; isErr: false; error: null }\n | { isOk: false; value: null; isErr: true; error: E };\n\n/**\n * Gracefully handles both synchronous and asynchronous operations by returning a result object.\n * This replaces the need for external slang-ts dependency and avoids try/catch boilerplate.\n *\n * @param fn - The function to execute (sync or async)\n * @returns A result object containing either the value or the error\n *\n * @example\n * const { isOk, value, error } = await safeTry(() => fetch(url));\n */\nexport function safeTry<T>(\n fn: () => T | Promise<T>\n): Promise<SafeResult<T>> | SafeResult<T> {\n try {\n const result = fn();\n\n if (result instanceof Promise) {\n return result\n .then(\n (value): SafeResult<T> => ({\n isOk: true,\n value,\n isErr: false,\n error: null,\n })\n )\n .catch(\n (error): SafeResult<T> => ({\n isOk: false,\n value: null,\n isErr: true,\n error,\n })\n );\n }\n\n return { isOk: true, value: result, isErr: false, error: null };\n } catch (error) {\n return { isOk: false, value: null, isErr: true, error };\n }\n}\n","import { safeTry } from \"./safe-try.js\";\nimport type {\n ClientResult,\n ExternalResponse,\n FetchOptions,\n NileClientConfig,\n} from \"./types.js\";\n\n/**\n * Handles communication with the Nile /services endpoint.\n * Uses fetch + safeTry for crash safety and consistent { error, data } results.\n */\nexport async function sendNileRequest<T = Record<string, unknown>>(\n config: NileClientConfig,\n intent: \"explore\" | \"execute\" | \"schema\",\n service: string,\n action: string,\n payload: unknown,\n options: FetchOptions = {}\n): Promise<ClientResult<T>> {\n const { timeout = config.timeout ?? 30_000, ...fetchOptions } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const requestResult = await safeTry(async () => {\n const response = await fetch(`${config.baseUrl}/services`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n ...(fetchOptions.headers as Record<string, string>),\n },\n credentials: config.credentials,\n body: JSON.stringify({\n intent,\n service,\n action,\n payload: payload ?? {},\n }),\n signal: controller.signal,\n ...fetchOptions,\n });\n\n clearTimeout(timeoutId);\n\n const json = (await response.json()) as ExternalResponse<T>;\n return json;\n });\n\n if (requestResult.isErr) {\n const error: unknown = requestResult.error;\n let errorMsg = \"An unknown error occurred\";\n\n if (error instanceof Error) {\n errorMsg =\n error.name === \"AbortError\" ? \"Request timed out\" : error.message;\n } else {\n errorMsg = String(error);\n }\n\n return { error: errorMsg, data: null };\n }\n\n const result = requestResult.value;\n\n if (!result.status) {\n return { error: result.message, data: result.data };\n }\n\n return { error: null, data: result.data };\n}\n","import { sendNileRequest } from \"./request.js\";\nimport type {\n ClientResult,\n FetchOptions,\n NileClient,\n NileClientConfig,\n} from \"./types.js\";\n\n/**\n * Creates a type-safe Nile client for interacting with backend services.\n *\n * @param config - The client configuration including baseUrl\n * @returns A factory object with execute, explore, and schema methods\n *\n * @example\n * const nile = createNileClient<ServicePayloads>({ baseUrl: \"/api\" });\n * const { error, data } = await nile.invoke({\n * service: \"tasks\",\n * action: \"create\",\n * payload: { title: \"Buy milk\" }\n * });\n */\nexport function createNileClient<\n TPayloads = Record<string, Record<string, unknown>>,\n>(config: NileClientConfig): NileClient<TPayloads> {\n return {\n /** Invoke a service action through the backend engine pipeline */\n invoke: async <\n S extends keyof TPayloads & string,\n A extends keyof TPayloads[S] & string,\n >(\n params: { service: S; action: A; payload: TPayloads[S][A] } & FetchOptions\n ) => {\n const { service, action, payload, ...options } = params;\n const result = await sendNileRequest<Record<string, unknown>>(\n config,\n \"execute\",\n service,\n action,\n payload,\n options\n );\n return result as ClientResult;\n },\n\n /** Discover available services and actions (supports \"*\" wildcard) */\n explore: async (\n params: { service: string; action: string } & FetchOptions\n ) => {\n const { service, action, ...options } = params;\n return await sendNileRequest(\n config,\n \"explore\",\n service,\n action,\n {},\n options\n );\n },\n\n /** Get action schemas as JSON Schema (supports \"*\" wildcard) */\n schema: async (\n params: { service: string; action: string } & FetchOptions\n ) => {\n const { service, action, ...options } = params;\n return await sendNileRequest(\n config,\n \"schema\",\n service,\n action,\n {},\n options\n );\n },\n };\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Standard Nile API response shape */
|
|
2
|
+
interface ExternalResponse<T = Record<string, unknown>> {
|
|
3
|
+
status: boolean;
|
|
4
|
+
message: string;
|
|
5
|
+
data: T;
|
|
6
|
+
}
|
|
7
|
+
/** Result pattern: { error, data } (graceful failure over throwing) */
|
|
8
|
+
interface ClientResult<T = Record<string, unknown>> {
|
|
9
|
+
error: string | null;
|
|
10
|
+
data: T | null;
|
|
11
|
+
}
|
|
12
|
+
/** Nile intent payload structure */
|
|
13
|
+
interface NileIntentParams<T = Record<string, unknown>> {
|
|
14
|
+
service: string;
|
|
15
|
+
action: string;
|
|
16
|
+
payload?: T;
|
|
17
|
+
}
|
|
18
|
+
/** Client configuration */
|
|
19
|
+
interface NileClientConfig {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
credentials?: "include" | "omit" | "same-origin";
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
/** Optional timeout in ms (default: 30000) */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Options for individual fetch calls */
|
|
27
|
+
interface FetchOptions extends Omit<RequestInit, "body" | "method"> {
|
|
28
|
+
timeout?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Interface for the typed client */
|
|
31
|
+
interface NileClient<TPayloads = Record<string, Record<string, unknown>>> {
|
|
32
|
+
/** Invoke an action with full type-safety for payload */
|
|
33
|
+
invoke: <S extends keyof TPayloads & string, A extends keyof TPayloads[S] & string>(params: {
|
|
34
|
+
service: S;
|
|
35
|
+
action: A;
|
|
36
|
+
payload: TPayloads[S][A];
|
|
37
|
+
} & FetchOptions) => Promise<ClientResult>;
|
|
38
|
+
/** Discover services/actions (supports "*" wildcard) */
|
|
39
|
+
explore: (params: {
|
|
40
|
+
service: string;
|
|
41
|
+
action: string;
|
|
42
|
+
} & FetchOptions) => Promise<ClientResult>;
|
|
43
|
+
/** Get action schemas as JSON Schema (supports "*" wildcard) */
|
|
44
|
+
schema: (params: {
|
|
45
|
+
service: string;
|
|
46
|
+
action: string;
|
|
47
|
+
} & FetchOptions) => Promise<ClientResult>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a type-safe Nile client for interacting with backend services.
|
|
52
|
+
*
|
|
53
|
+
* @param config - The client configuration including baseUrl
|
|
54
|
+
* @returns A factory object with execute, explore, and schema methods
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const nile = createNileClient<ServicePayloads>({ baseUrl: "/api" });
|
|
58
|
+
* const { error, data } = await nile.invoke({
|
|
59
|
+
* service: "tasks",
|
|
60
|
+
* action: "create",
|
|
61
|
+
* payload: { title: "Buy milk" }
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
declare function createNileClient<TPayloads = Record<string, Record<string, unknown>>>(config: NileClientConfig): NileClient<TPayloads>;
|
|
65
|
+
|
|
66
|
+
export { type ClientResult, type ExternalResponse, type FetchOptions, type NileClient, type NileClientConfig, type NileIntentParams, createNileClient };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/** Standard Nile API response shape */
|
|
2
|
+
interface ExternalResponse<T = Record<string, unknown>> {
|
|
3
|
+
status: boolean;
|
|
4
|
+
message: string;
|
|
5
|
+
data: T;
|
|
6
|
+
}
|
|
7
|
+
/** Result pattern: { error, data } (graceful failure over throwing) */
|
|
8
|
+
interface ClientResult<T = Record<string, unknown>> {
|
|
9
|
+
error: string | null;
|
|
10
|
+
data: T | null;
|
|
11
|
+
}
|
|
12
|
+
/** Nile intent payload structure */
|
|
13
|
+
interface NileIntentParams<T = Record<string, unknown>> {
|
|
14
|
+
service: string;
|
|
15
|
+
action: string;
|
|
16
|
+
payload?: T;
|
|
17
|
+
}
|
|
18
|
+
/** Client configuration */
|
|
19
|
+
interface NileClientConfig {
|
|
20
|
+
baseUrl: string;
|
|
21
|
+
credentials?: "include" | "omit" | "same-origin";
|
|
22
|
+
headers?: Record<string, string>;
|
|
23
|
+
/** Optional timeout in ms (default: 30000) */
|
|
24
|
+
timeout?: number;
|
|
25
|
+
}
|
|
26
|
+
/** Options for individual fetch calls */
|
|
27
|
+
interface FetchOptions extends Omit<RequestInit, "body" | "method"> {
|
|
28
|
+
timeout?: number;
|
|
29
|
+
}
|
|
30
|
+
/** Interface for the typed client */
|
|
31
|
+
interface NileClient<TPayloads = Record<string, Record<string, unknown>>> {
|
|
32
|
+
/** Invoke an action with full type-safety for payload */
|
|
33
|
+
invoke: <S extends keyof TPayloads & string, A extends keyof TPayloads[S] & string>(params: {
|
|
34
|
+
service: S;
|
|
35
|
+
action: A;
|
|
36
|
+
payload: TPayloads[S][A];
|
|
37
|
+
} & FetchOptions) => Promise<ClientResult>;
|
|
38
|
+
/** Discover services/actions (supports "*" wildcard) */
|
|
39
|
+
explore: (params: {
|
|
40
|
+
service: string;
|
|
41
|
+
action: string;
|
|
42
|
+
} & FetchOptions) => Promise<ClientResult>;
|
|
43
|
+
/** Get action schemas as JSON Schema (supports "*" wildcard) */
|
|
44
|
+
schema: (params: {
|
|
45
|
+
service: string;
|
|
46
|
+
action: string;
|
|
47
|
+
} & FetchOptions) => Promise<ClientResult>;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Creates a type-safe Nile client for interacting with backend services.
|
|
52
|
+
*
|
|
53
|
+
* @param config - The client configuration including baseUrl
|
|
54
|
+
* @returns A factory object with execute, explore, and schema methods
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* const nile = createNileClient<ServicePayloads>({ baseUrl: "/api" });
|
|
58
|
+
* const { error, data } = await nile.invoke({
|
|
59
|
+
* service: "tasks",
|
|
60
|
+
* action: "create",
|
|
61
|
+
* payload: { title: "Buy milk" }
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
declare function createNileClient<TPayloads = Record<string, Record<string, unknown>>>(config: NileClientConfig): NileClient<TPayloads>;
|
|
65
|
+
|
|
66
|
+
export { type ClientResult, type ExternalResponse, type FetchOptions, type NileClient, type NileClientConfig, type NileIntentParams, createNileClient };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// src/safe-try.ts
|
|
2
|
+
function safeTry(fn) {
|
|
3
|
+
try {
|
|
4
|
+
const result = fn();
|
|
5
|
+
if (result instanceof Promise) {
|
|
6
|
+
return result.then(
|
|
7
|
+
(value) => ({
|
|
8
|
+
isOk: true,
|
|
9
|
+
value,
|
|
10
|
+
isErr: false,
|
|
11
|
+
error: null
|
|
12
|
+
})
|
|
13
|
+
).catch(
|
|
14
|
+
(error) => ({
|
|
15
|
+
isOk: false,
|
|
16
|
+
value: null,
|
|
17
|
+
isErr: true,
|
|
18
|
+
error
|
|
19
|
+
})
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
return { isOk: true, value: result, isErr: false, error: null };
|
|
23
|
+
} catch (error) {
|
|
24
|
+
return { isOk: false, value: null, isErr: true, error };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/request.ts
|
|
29
|
+
async function sendNileRequest(config, intent, service, action, payload, options = {}) {
|
|
30
|
+
const { timeout = config.timeout ?? 3e4, ...fetchOptions } = options;
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
33
|
+
const requestResult = await safeTry(async () => {
|
|
34
|
+
const response = await fetch(`${config.baseUrl}/services`, {
|
|
35
|
+
method: "POST",
|
|
36
|
+
headers: {
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
...config.headers,
|
|
39
|
+
...fetchOptions.headers
|
|
40
|
+
},
|
|
41
|
+
credentials: config.credentials,
|
|
42
|
+
body: JSON.stringify({
|
|
43
|
+
intent,
|
|
44
|
+
service,
|
|
45
|
+
action,
|
|
46
|
+
payload: payload ?? {}
|
|
47
|
+
}),
|
|
48
|
+
signal: controller.signal,
|
|
49
|
+
...fetchOptions
|
|
50
|
+
});
|
|
51
|
+
clearTimeout(timeoutId);
|
|
52
|
+
const json = await response.json();
|
|
53
|
+
return json;
|
|
54
|
+
});
|
|
55
|
+
if (requestResult.isErr) {
|
|
56
|
+
const error = requestResult.error;
|
|
57
|
+
let errorMsg = "An unknown error occurred";
|
|
58
|
+
if (error instanceof Error) {
|
|
59
|
+
errorMsg = error.name === "AbortError" ? "Request timed out" : error.message;
|
|
60
|
+
} else {
|
|
61
|
+
errorMsg = String(error);
|
|
62
|
+
}
|
|
63
|
+
return { error: errorMsg, data: null };
|
|
64
|
+
}
|
|
65
|
+
const result = requestResult.value;
|
|
66
|
+
if (!result.status) {
|
|
67
|
+
return { error: result.message, data: result.data };
|
|
68
|
+
}
|
|
69
|
+
return { error: null, data: result.data };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/create-client.ts
|
|
73
|
+
function createNileClient(config) {
|
|
74
|
+
return {
|
|
75
|
+
/** Invoke a service action through the backend engine pipeline */
|
|
76
|
+
invoke: async (params) => {
|
|
77
|
+
const { service, action, payload, ...options } = params;
|
|
78
|
+
const result = await sendNileRequest(
|
|
79
|
+
config,
|
|
80
|
+
"execute",
|
|
81
|
+
service,
|
|
82
|
+
action,
|
|
83
|
+
payload,
|
|
84
|
+
options
|
|
85
|
+
);
|
|
86
|
+
return result;
|
|
87
|
+
},
|
|
88
|
+
/** Discover available services and actions (supports "*" wildcard) */
|
|
89
|
+
explore: async (params) => {
|
|
90
|
+
const { service, action, ...options } = params;
|
|
91
|
+
return await sendNileRequest(
|
|
92
|
+
config,
|
|
93
|
+
"explore",
|
|
94
|
+
service,
|
|
95
|
+
action,
|
|
96
|
+
{},
|
|
97
|
+
options
|
|
98
|
+
);
|
|
99
|
+
},
|
|
100
|
+
/** Get action schemas as JSON Schema (supports "*" wildcard) */
|
|
101
|
+
schema: async (params) => {
|
|
102
|
+
const { service, action, ...options } = params;
|
|
103
|
+
return await sendNileRequest(
|
|
104
|
+
config,
|
|
105
|
+
"schema",
|
|
106
|
+
service,
|
|
107
|
+
action,
|
|
108
|
+
{},
|
|
109
|
+
options
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
export {
|
|
115
|
+
createNileClient
|
|
116
|
+
};
|
|
117
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/safe-try.ts","../src/request.ts","../src/create-client.ts"],"sourcesContent":["/**\n * Internal result type for safeTry\n */\nexport type SafeResult<T, E = unknown> =\n | { isOk: true; value: T; isErr: false; error: null }\n | { isOk: false; value: null; isErr: true; error: E };\n\n/**\n * Gracefully handles both synchronous and asynchronous operations by returning a result object.\n * This replaces the need for external slang-ts dependency and avoids try/catch boilerplate.\n *\n * @param fn - The function to execute (sync or async)\n * @returns A result object containing either the value or the error\n *\n * @example\n * const { isOk, value, error } = await safeTry(() => fetch(url));\n */\nexport function safeTry<T>(\n fn: () => T | Promise<T>\n): Promise<SafeResult<T>> | SafeResult<T> {\n try {\n const result = fn();\n\n if (result instanceof Promise) {\n return result\n .then(\n (value): SafeResult<T> => ({\n isOk: true,\n value,\n isErr: false,\n error: null,\n })\n )\n .catch(\n (error): SafeResult<T> => ({\n isOk: false,\n value: null,\n isErr: true,\n error,\n })\n );\n }\n\n return { isOk: true, value: result, isErr: false, error: null };\n } catch (error) {\n return { isOk: false, value: null, isErr: true, error };\n }\n}\n","import { safeTry } from \"./safe-try.js\";\nimport type {\n ClientResult,\n ExternalResponse,\n FetchOptions,\n NileClientConfig,\n} from \"./types.js\";\n\n/**\n * Handles communication with the Nile /services endpoint.\n * Uses fetch + safeTry for crash safety and consistent { error, data } results.\n */\nexport async function sendNileRequest<T = Record<string, unknown>>(\n config: NileClientConfig,\n intent: \"explore\" | \"execute\" | \"schema\",\n service: string,\n action: string,\n payload: unknown,\n options: FetchOptions = {}\n): Promise<ClientResult<T>> {\n const { timeout = config.timeout ?? 30_000, ...fetchOptions } = options;\n\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), timeout);\n\n const requestResult = await safeTry(async () => {\n const response = await fetch(`${config.baseUrl}/services`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n ...config.headers,\n ...(fetchOptions.headers as Record<string, string>),\n },\n credentials: config.credentials,\n body: JSON.stringify({\n intent,\n service,\n action,\n payload: payload ?? {},\n }),\n signal: controller.signal,\n ...fetchOptions,\n });\n\n clearTimeout(timeoutId);\n\n const json = (await response.json()) as ExternalResponse<T>;\n return json;\n });\n\n if (requestResult.isErr) {\n const error: unknown = requestResult.error;\n let errorMsg = \"An unknown error occurred\";\n\n if (error instanceof Error) {\n errorMsg =\n error.name === \"AbortError\" ? \"Request timed out\" : error.message;\n } else {\n errorMsg = String(error);\n }\n\n return { error: errorMsg, data: null };\n }\n\n const result = requestResult.value;\n\n if (!result.status) {\n return { error: result.message, data: result.data };\n }\n\n return { error: null, data: result.data };\n}\n","import { sendNileRequest } from \"./request.js\";\nimport type {\n ClientResult,\n FetchOptions,\n NileClient,\n NileClientConfig,\n} from \"./types.js\";\n\n/**\n * Creates a type-safe Nile client for interacting with backend services.\n *\n * @param config - The client configuration including baseUrl\n * @returns A factory object with execute, explore, and schema methods\n *\n * @example\n * const nile = createNileClient<ServicePayloads>({ baseUrl: \"/api\" });\n * const { error, data } = await nile.invoke({\n * service: \"tasks\",\n * action: \"create\",\n * payload: { title: \"Buy milk\" }\n * });\n */\nexport function createNileClient<\n TPayloads = Record<string, Record<string, unknown>>,\n>(config: NileClientConfig): NileClient<TPayloads> {\n return {\n /** Invoke a service action through the backend engine pipeline */\n invoke: async <\n S extends keyof TPayloads & string,\n A extends keyof TPayloads[S] & string,\n >(\n params: { service: S; action: A; payload: TPayloads[S][A] } & FetchOptions\n ) => {\n const { service, action, payload, ...options } = params;\n const result = await sendNileRequest<Record<string, unknown>>(\n config,\n \"execute\",\n service,\n action,\n payload,\n options\n );\n return result as ClientResult;\n },\n\n /** Discover available services and actions (supports \"*\" wildcard) */\n explore: async (\n params: { service: string; action: string } & FetchOptions\n ) => {\n const { service, action, ...options } = params;\n return await sendNileRequest(\n config,\n \"explore\",\n service,\n action,\n {},\n options\n );\n },\n\n /** Get action schemas as JSON Schema (supports \"*\" wildcard) */\n schema: async (\n params: { service: string; action: string } & FetchOptions\n ) => {\n const { service, action, ...options } = params;\n return await sendNileRequest(\n config,\n \"schema\",\n service,\n action,\n {},\n options\n );\n },\n };\n}\n"],"mappings":";AAiBO,SAAS,QACd,IACwC;AACxC,MAAI;AACF,UAAM,SAAS,GAAG;AAElB,QAAI,kBAAkB,SAAS;AAC7B,aAAO,OACJ;AAAA,QACC,CAAC,WAA0B;AAAA,UACzB,MAAM;AAAA,UACN;AAAA,UACA,OAAO;AAAA,UACP,OAAO;AAAA,QACT;AAAA,MACF,EACC;AAAA,QACC,CAAC,WAA0B;AAAA,UACzB,MAAM;AAAA,UACN,OAAO;AAAA,UACP,OAAO;AAAA,UACP;AAAA,QACF;AAAA,MACF;AAAA,IACJ;AAEA,WAAO,EAAE,MAAM,MAAM,OAAO,QAAQ,OAAO,OAAO,OAAO,KAAK;AAAA,EAChE,SAAS,OAAO;AACd,WAAO,EAAE,MAAM,OAAO,OAAO,MAAM,OAAO,MAAM,MAAM;AAAA,EACxD;AACF;;;ACnCA,eAAsB,gBACpB,QACA,QACA,SACA,QACA,SACA,UAAwB,CAAC,GACC;AAC1B,QAAM,EAAE,UAAU,OAAO,WAAW,KAAQ,GAAG,aAAa,IAAI;AAEhE,QAAM,aAAa,IAAI,gBAAgB;AACvC,QAAM,YAAY,WAAW,MAAM,WAAW,MAAM,GAAG,OAAO;AAE9D,QAAM,gBAAgB,MAAM,QAAQ,YAAY;AAC9C,UAAM,WAAW,MAAM,MAAM,GAAG,OAAO,OAAO,aAAa;AAAA,MACzD,QAAQ;AAAA,MACR,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAG,OAAO;AAAA,QACV,GAAI,aAAa;AAAA,MACnB;AAAA,MACA,aAAa,OAAO;AAAA,MACpB,MAAM,KAAK,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,WAAW,CAAC;AAAA,MACvB,CAAC;AAAA,MACD,QAAQ,WAAW;AAAA,MACnB,GAAG;AAAA,IACL,CAAC;AAED,iBAAa,SAAS;AAEtB,UAAM,OAAQ,MAAM,SAAS,KAAK;AAClC,WAAO;AAAA,EACT,CAAC;AAED,MAAI,cAAc,OAAO;AACvB,UAAM,QAAiB,cAAc;AACrC,QAAI,WAAW;AAEf,QAAI,iBAAiB,OAAO;AAC1B,iBACE,MAAM,SAAS,eAAe,sBAAsB,MAAM;AAAA,IAC9D,OAAO;AACL,iBAAW,OAAO,KAAK;AAAA,IACzB;AAEA,WAAO,EAAE,OAAO,UAAU,MAAM,KAAK;AAAA,EACvC;AAEA,QAAM,SAAS,cAAc;AAE7B,MAAI,CAAC,OAAO,QAAQ;AAClB,WAAO,EAAE,OAAO,OAAO,SAAS,MAAM,OAAO,KAAK;AAAA,EACpD;AAEA,SAAO,EAAE,OAAO,MAAM,MAAM,OAAO,KAAK;AAC1C;;;ACjDO,SAAS,iBAEd,QAAiD;AACjD,SAAO;AAAA;AAAA,IAEL,QAAQ,OAIN,WACG;AACH,YAAM,EAAE,SAAS,QAAQ,SAAS,GAAG,QAAQ,IAAI;AACjD,YAAM,SAAS,MAAM;AAAA,QACnB;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA;AAAA,IAGA,SAAS,OACP,WACG;AACH,YAAM,EAAE,SAAS,QAAQ,GAAG,QAAQ,IAAI;AACxC,aAAO,MAAM;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,CAAC;AAAA,QACD;AAAA,MACF;AAAA,IACF;AAAA;AAAA,IAGA,QAAQ,OACN,WACG;AACH,YAAM,EAAE,SAAS,QAAQ,GAAG,QAAQ,IAAI;AACxC,aAAO,MAAM;AAAA,QACX;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,CAAC;AAAA,QACD;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nilejs/client",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Standalone, type-safe client for Nile backend framework",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "tsup",
|
|
21
|
+
"dev": "tsup --watch"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5",
|
|
25
|
+
"tsup": "^8.5.1"
|
|
26
|
+
}
|
|
27
|
+
}
|