@palbase/backend-client 0.7.0 → 0.7.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/dist/index.cjs +29 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +71 -12
- package/dist/index.d.ts +71 -12
- package/dist/index.js +27 -10
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -21,11 +21,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
23
|
BackendClient: () => BackendClient,
|
|
24
|
-
BackendInvocationBuilder: () => BackendInvocationBuilder
|
|
24
|
+
BackendInvocationBuilder: () => BackendInvocationBuilder,
|
|
25
|
+
LOCALHOST: () => LOCALHOST
|
|
25
26
|
});
|
|
26
27
|
module.exports = __toCommonJS(index_exports);
|
|
27
28
|
|
|
28
29
|
// src/backend-client.ts
|
|
30
|
+
var LOCALHOST = { custom: "http://localhost:4003" };
|
|
29
31
|
var BackendInvocationBuilder = class {
|
|
30
32
|
httpClient;
|
|
31
33
|
path;
|
|
@@ -50,7 +52,7 @@ var BackendInvocationBuilder = class {
|
|
|
50
52
|
}
|
|
51
53
|
call(method, options) {
|
|
52
54
|
const qs = options?.query ? this.buildQuery(options.query) : "";
|
|
53
|
-
const fullPath = `/
|
|
55
|
+
const fullPath = `/rpc/${this.path}${qs}`;
|
|
54
56
|
const reqOpts = {
|
|
55
57
|
headers: options?.headers,
|
|
56
58
|
signal: options?.signal
|
|
@@ -75,26 +77,42 @@ var BackendClient = class {
|
|
|
75
77
|
this.httpClient = httpClient;
|
|
76
78
|
}
|
|
77
79
|
/**
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
80
|
+
* Typed RPC dispatch — the primary API. When the user augments
|
|
81
|
+
* `BackendEndpoints` (typically via `palbase backend types`), the
|
|
82
|
+
* `name` argument autocompletes and `input` / return type are
|
|
83
|
+
* inferred. Without augmentation both default to `unknown`.
|
|
84
|
+
*
|
|
85
|
+
* ```ts
|
|
86
|
+
* const r = await pb.backend.call('checkout', { items: ['a'] });
|
|
87
|
+
* // r.data is typed when BackendEndpoints['checkout'] is declared.
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
call(name, input, options) {
|
|
91
|
+
return this.invoke(name).post(input, options);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Lower-level builder for callers that need GET/PUT/DELETE etc. The
|
|
95
|
+
* builder talks to the same `/rpc/{path}` route as `call()` — the
|
|
96
|
+
* runtime treats every verb identically and the wire convention is
|
|
97
|
+
* RPC-shaped, but the builder remains for headers/query/AbortSignal
|
|
98
|
+
* affordances the simple `call()` doesn't expose.
|
|
82
99
|
*/
|
|
83
100
|
invoke(path) {
|
|
84
101
|
return new BackendInvocationBuilder(this.httpClient, path);
|
|
85
102
|
}
|
|
86
103
|
/**
|
|
87
|
-
* One-shot fetch helper
|
|
88
|
-
*
|
|
89
|
-
*
|
|
104
|
+
* One-shot fetch helper for explicit method+path. Kept so existing
|
|
105
|
+
* callers (legacy code, tests) compile; new code should use
|
|
106
|
+
* `call(name, input)` for the typed RPC path.
|
|
90
107
|
*/
|
|
91
|
-
|
|
108
|
+
request(method, path, options) {
|
|
92
109
|
return this.invoke(path).call(method, options);
|
|
93
110
|
}
|
|
94
111
|
};
|
|
95
112
|
// Annotate the CommonJS export names for ESM import in node:
|
|
96
113
|
0 && (module.exports = {
|
|
97
114
|
BackendClient,
|
|
98
|
-
BackendInvocationBuilder
|
|
115
|
+
BackendInvocationBuilder,
|
|
116
|
+
LOCALHOST
|
|
99
117
|
});
|
|
100
118
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/backend-client.ts"],"sourcesContent":["export {
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/backend-client.ts"],"sourcesContent":["export {\n BackendClient,\n BackendInvocationBuilder,\n LOCALHOST,\n} from './backend-client.js';\nexport type {\n InvokeOptions,\n RpcCallOptions,\n BackendEndpoints,\n BackendEnvironment,\n} from './backend-client.js';\n","import type { HttpClient, PalbaseResponse, RequestOptions } from '@palbase/core';\n\n/**\n * Module augmentation hook — users export their endpoint map from\n * `.palbase/types.d.ts` and add the type via:\n *\n * ```ts\n * declare module '@palbase/backend-client' {\n * interface BackendEndpoints {\n * checkout: { input: { items: string[] }; output: { orderId: string } };\n * }\n * }\n * ```\n *\n * `pb.backend.call('checkout', { items: ['a'] })` then resolves the\n * input + output types automatically. Without augmentation the call\n * falls back to `unknown` and the developer has to cast manually.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target on purpose\nexport interface BackendEndpoints {}\n\n/**\n * Options accepted by every backend invocation. `body` is JSON-encoded\n * for POST/PUT/PATCH; `query` is appended as a query string. `signal`\n * lets callers wire AbortController for cancellation.\n */\nexport interface InvokeOptions {\n body?: unknown;\n query?: Record<string, string | number | boolean | null | undefined>;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * Per-call options for the typed RPC `call(name, input)` API.\n * `headers` and `signal` cover the common needs; the body is the\n * input argument itself, so there is no `body` here.\n */\nexport interface RpcCallOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * Backend connection environment. Drives the base path the client\n * issues requests against. Default is `'remote'` — Kong gateway via\n * the configured Palbase host.\n */\nexport type BackendEnvironment =\n | 'remote'\n | { custom: string };\n\n/** Convenience alias for the most common dev override. */\nexport const LOCALHOST: BackendEnvironment = { custom: 'http://localhost:4003' };\n\n/**\n * BackendInvocationBuilder is the chainable handle returned by\n * `pb.backend.invoke('<path>')`. Each terminal method (`get`, `post`,\n * `put`, `patch`, `del`, `call`) issues a request to\n * `<host>/api/<path>` with the given HTTP verb. The builder is\n * intentionally tiny — backend endpoints are user-defined, so we\n * don't bake any DSL on top.\n */\nexport class BackendInvocationBuilder<T = unknown> {\n private readonly httpClient: HttpClient;\n private readonly path: string;\n\n constructor(httpClient: HttpClient, path: string) {\n this.httpClient = httpClient;\n // Strip exactly one leading slash so callers can type either\n // `pb.backend.invoke('hello')` or `pb.backend.invoke('/hello')`.\n this.path = path.replace(/^\\/+/, '');\n }\n\n get(options?: InvokeOptions): Promise<PalbaseResponse<T>> {\n return this.call<T>('GET', options);\n }\n post(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('POST', { ...options, body });\n }\n put(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('PUT', { ...options, body });\n }\n patch(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('PATCH', { ...options, body });\n }\n del(options?: InvokeOptions): Promise<PalbaseResponse<T>> {\n return this.call<T>('DELETE', options);\n }\n\n call<R = T>(method: string, options?: InvokeOptions): Promise<PalbaseResponse<R>> {\n const qs = options?.query ? this.buildQuery(options.query) : '';\n // Adım B14 — RPC convention: every endpoint is reachable as\n // `POST /rpc/{operationId}`. Kong's palbase-gateway plugin rewrites\n // the path to `/env/{ref}/rpc/{operationId}` server-side.\n const fullPath = `/rpc/${this.path}${qs}`;\n const reqOpts: RequestOptions = {\n headers: options?.headers,\n signal: options?.signal,\n };\n if (options?.body !== undefined) {\n // HttpClient JSON-encodes body when content-type is unset.\n // We pass through raw — caller can override Content-Type via\n // headers if they need form-urlencoded etc.\n (reqOpts as RequestOptions & { body?: unknown }).body = options.body;\n }\n return this.httpClient.request<R>(method, fullPath, reqOpts);\n }\n\n private buildQuery(q: NonNullable<InvokeOptions['query']>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(q)) {\n if (v === undefined || v === null) continue;\n parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n }\n return parts.length ? `?${parts.join('&')}` : '';\n }\n}\n\n/** Resolve typed input/output for an operation when the user has\n * augmented `BackendEndpoints`; otherwise fall back to `unknown`. */\ntype EndpointInput<K extends string> = K extends keyof BackendEndpoints\n ? BackendEndpoints[K] extends { input: infer I }\n ? I\n : unknown\n : unknown;\ntype EndpointOutput<K extends string> = K extends keyof BackendEndpoints\n ? BackendEndpoints[K] extends { output: infer O }\n ? O\n : unknown\n : unknown;\n\n/**\n * BackendClient is the entry point exposed as `pb.backend` on the\n * unified client. It issues typed RPC calls to per-tenant\n * backend-runtime pods routed through Kong as `<ref>.<host>/rpc/<name>`.\n * Each defineEndpoint a user ships in their `endpoints/` tree becomes\n * one operation here.\n */\nexport class BackendClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Typed RPC dispatch — the primary API. When the user augments\n * `BackendEndpoints` (typically via `palbase backend types`), the\n * `name` argument autocompletes and `input` / return type are\n * inferred. Without augmentation both default to `unknown`.\n *\n * ```ts\n * const r = await pb.backend.call('checkout', { items: ['a'] });\n * // r.data is typed when BackendEndpoints['checkout'] is declared.\n * ```\n */\n call<K extends string>(\n name: K & (K extends keyof BackendEndpoints ? K : string),\n input: EndpointInput<K>,\n options?: RpcCallOptions,\n ): Promise<PalbaseResponse<EndpointOutput<K>>> {\n return this.invoke<EndpointOutput<K>>(name).post(input, options) as Promise<\n PalbaseResponse<EndpointOutput<K>>\n >;\n }\n\n /**\n * Lower-level builder for callers that need GET/PUT/DELETE etc. The\n * builder talks to the same `/rpc/{path}` route as `call()` — the\n * runtime treats every verb identically and the wire convention is\n * RPC-shaped, but the builder remains for headers/query/AbortSignal\n * affordances the simple `call()` doesn't expose.\n */\n invoke<T = unknown>(path: string): BackendInvocationBuilder<T> {\n return new BackendInvocationBuilder<T>(this.httpClient, path);\n }\n\n /**\n * One-shot fetch helper for explicit method+path. Kept so existing\n * callers (legacy code, tests) compile; new code should use\n * `call(name, input)` for the typed RPC path.\n */\n request<T = unknown>(\n method: string,\n path: string,\n options?: InvokeOptions,\n ): Promise<PalbaseResponse<T>> {\n return this.invoke<T>(path).call<T>(method, options);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACqDO,IAAM,YAAgC,EAAE,QAAQ,wBAAwB;AAUxE,IAAM,2BAAN,MAA4C;AAAA,EAChC;AAAA,EACA;AAAA,EAEjB,YAAY,YAAwB,MAAc;AAChD,SAAK,aAAa;AAGlB,SAAK,OAAO,KAAK,QAAQ,QAAQ,EAAE;AAAA,EACrC;AAAA,EAEA,IAAI,SAAsD;AACxD,WAAO,KAAK,KAAQ,OAAO,OAAO;AAAA,EACpC;AAAA,EACA,KAAK,MAAgB,SAAoE;AACvF,WAAO,KAAK,KAAQ,QAAQ,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EAClD;AAAA,EACA,IAAI,MAAgB,SAAoE;AACtF,WAAO,KAAK,KAAQ,OAAO,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EACjD;AAAA,EACA,MAAM,MAAgB,SAAoE;AACxF,WAAO,KAAK,KAAQ,SAAS,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EACnD;AAAA,EACA,IAAI,SAAsD;AACxD,WAAO,KAAK,KAAQ,UAAU,OAAO;AAAA,EACvC;AAAA,EAEA,KAAY,QAAgB,SAAsD;AAChF,UAAM,KAAK,SAAS,QAAQ,KAAK,WAAW,QAAQ,KAAK,IAAI;AAI7D,UAAM,WAAW,QAAQ,KAAK,IAAI,GAAG,EAAE;AACvC,UAAM,UAA0B;AAAA,MAC9B,SAAS,SAAS;AAAA,MAClB,QAAQ,SAAS;AAAA,IACnB;AACA,QAAI,SAAS,SAAS,QAAW;AAI/B,MAAC,QAAgD,OAAO,QAAQ;AAAA,IAClE;AACA,WAAO,KAAK,WAAW,QAAW,QAAQ,UAAU,OAAO;AAAA,EAC7D;AAAA,EAEQ,WAAW,GAAgD;AACjE,UAAM,QAAkB,CAAC;AACzB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAAC,GAAG;AACtC,UAAI,MAAM,UAAa,MAAM,KAAM;AACnC,YAAM,KAAK,GAAG,mBAAmB,CAAC,CAAC,IAAI,mBAAmB,OAAO,CAAC,CAAC,CAAC,EAAE;AAAA,IACxE;AACA,WAAO,MAAM,SAAS,IAAI,MAAM,KAAK,GAAG,CAAC,KAAK;AAAA,EAChD;AACF;AAsBO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,KACE,MACA,OACA,SAC6C;AAC7C,WAAO,KAAK,OAA0B,IAAI,EAAE,KAAK,OAAO,OAAO;AAAA,EAGjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAoB,MAA2C;AAC7D,WAAO,IAAI,yBAA4B,KAAK,YAAY,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QACE,QACA,MACA,SAC6B;AAC7B,WAAO,KAAK,OAAU,IAAI,EAAE,KAAQ,QAAQ,OAAO;AAAA,EACrD;AACF;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { HttpClient, PalbaseResponse } from '@palbase/core';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Module augmentation hook — users export their endpoint map from
|
|
5
|
+
* `.palbase/types.d.ts` and add the type via:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* declare module '@palbase/backend-client' {
|
|
9
|
+
* interface BackendEndpoints {
|
|
10
|
+
* checkout: { input: { items: string[] }; output: { orderId: string } };
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `pb.backend.call('checkout', { items: ['a'] })` then resolves the
|
|
16
|
+
* input + output types automatically. Without augmentation the call
|
|
17
|
+
* falls back to `unknown` and the developer has to cast manually.
|
|
18
|
+
*/
|
|
19
|
+
interface BackendEndpoints {
|
|
20
|
+
}
|
|
3
21
|
/**
|
|
4
22
|
* Options accepted by every backend invocation. `body` is JSON-encoded
|
|
5
23
|
* for POST/PUT/PATCH; `query` is appended as a query string. `signal`
|
|
@@ -11,6 +29,25 @@ interface InvokeOptions {
|
|
|
11
29
|
headers?: Record<string, string>;
|
|
12
30
|
signal?: AbortSignal;
|
|
13
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Per-call options for the typed RPC `call(name, input)` API.
|
|
34
|
+
* `headers` and `signal` cover the common needs; the body is the
|
|
35
|
+
* input argument itself, so there is no `body` here.
|
|
36
|
+
*/
|
|
37
|
+
interface RpcCallOptions {
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Backend connection environment. Drives the base path the client
|
|
43
|
+
* issues requests against. Default is `'remote'` — Kong gateway via
|
|
44
|
+
* the configured Palbase host.
|
|
45
|
+
*/
|
|
46
|
+
type BackendEnvironment = 'remote' | {
|
|
47
|
+
custom: string;
|
|
48
|
+
};
|
|
49
|
+
/** Convenience alias for the most common dev override. */
|
|
50
|
+
declare const LOCALHOST: BackendEnvironment;
|
|
14
51
|
/**
|
|
15
52
|
* BackendInvocationBuilder is the chainable handle returned by
|
|
16
53
|
* `pb.backend.invoke('<path>')`. Each terminal method (`get`, `post`,
|
|
@@ -31,28 +68,50 @@ declare class BackendInvocationBuilder<T = unknown> {
|
|
|
31
68
|
call<R = T>(method: string, options?: InvokeOptions): Promise<PalbaseResponse<R>>;
|
|
32
69
|
private buildQuery;
|
|
33
70
|
}
|
|
71
|
+
/** Resolve typed input/output for an operation when the user has
|
|
72
|
+
* augmented `BackendEndpoints`; otherwise fall back to `unknown`. */
|
|
73
|
+
type EndpointInput<K extends string> = K extends keyof BackendEndpoints ? BackendEndpoints[K] extends {
|
|
74
|
+
input: infer I;
|
|
75
|
+
} ? I : unknown : unknown;
|
|
76
|
+
type EndpointOutput<K extends string> = K extends keyof BackendEndpoints ? BackendEndpoints[K] extends {
|
|
77
|
+
output: infer O;
|
|
78
|
+
} ? O : unknown : unknown;
|
|
34
79
|
/**
|
|
35
80
|
* BackendClient is the entry point exposed as `pb.backend` on the
|
|
36
|
-
* unified client. It issues calls to per-tenant
|
|
37
|
-
* routed through Kong as `<ref>.<host>/
|
|
38
|
-
* a user ships in their `endpoints/` tree becomes
|
|
81
|
+
* unified client. It issues typed RPC calls to per-tenant
|
|
82
|
+
* backend-runtime pods routed through Kong as `<ref>.<host>/rpc/<name>`.
|
|
83
|
+
* Each defineEndpoint a user ships in their `endpoints/` tree becomes
|
|
84
|
+
* one operation here.
|
|
39
85
|
*/
|
|
40
86
|
declare class BackendClient {
|
|
41
87
|
private readonly httpClient;
|
|
42
88
|
constructor(httpClient: HttpClient);
|
|
43
89
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
90
|
+
* Typed RPC dispatch — the primary API. When the user augments
|
|
91
|
+
* `BackendEndpoints` (typically via `palbase backend types`), the
|
|
92
|
+
* `name` argument autocompletes and `input` / return type are
|
|
93
|
+
* inferred. Without augmentation both default to `unknown`.
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* const r = await pb.backend.call('checkout', { items: ['a'] });
|
|
97
|
+
* // r.data is typed when BackendEndpoints['checkout'] is declared.
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
call<K extends string>(name: K & (K extends keyof BackendEndpoints ? K : string), input: EndpointInput<K>, options?: RpcCallOptions): Promise<PalbaseResponse<EndpointOutput<K>>>;
|
|
101
|
+
/**
|
|
102
|
+
* Lower-level builder for callers that need GET/PUT/DELETE etc. The
|
|
103
|
+
* builder talks to the same `/rpc/{path}` route as `call()` — the
|
|
104
|
+
* runtime treats every verb identically and the wire convention is
|
|
105
|
+
* RPC-shaped, but the builder remains for headers/query/AbortSignal
|
|
106
|
+
* affordances the simple `call()` doesn't expose.
|
|
48
107
|
*/
|
|
49
108
|
invoke<T = unknown>(path: string): BackendInvocationBuilder<T>;
|
|
50
109
|
/**
|
|
51
|
-
* One-shot fetch helper
|
|
52
|
-
*
|
|
53
|
-
*
|
|
110
|
+
* One-shot fetch helper for explicit method+path. Kept so existing
|
|
111
|
+
* callers (legacy code, tests) compile; new code should use
|
|
112
|
+
* `call(name, input)` for the typed RPC path.
|
|
54
113
|
*/
|
|
55
|
-
|
|
114
|
+
request<T = unknown>(method: string, path: string, options?: InvokeOptions): Promise<PalbaseResponse<T>>;
|
|
56
115
|
}
|
|
57
116
|
|
|
58
|
-
export { BackendClient, BackendInvocationBuilder, type InvokeOptions };
|
|
117
|
+
export { BackendClient, type BackendEndpoints, type BackendEnvironment, BackendInvocationBuilder, type InvokeOptions, LOCALHOST, type RpcCallOptions };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
import { HttpClient, PalbaseResponse } from '@palbase/core';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Module augmentation hook — users export their endpoint map from
|
|
5
|
+
* `.palbase/types.d.ts` and add the type via:
|
|
6
|
+
*
|
|
7
|
+
* ```ts
|
|
8
|
+
* declare module '@palbase/backend-client' {
|
|
9
|
+
* interface BackendEndpoints {
|
|
10
|
+
* checkout: { input: { items: string[] }; output: { orderId: string } };
|
|
11
|
+
* }
|
|
12
|
+
* }
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* `pb.backend.call('checkout', { items: ['a'] })` then resolves the
|
|
16
|
+
* input + output types automatically. Without augmentation the call
|
|
17
|
+
* falls back to `unknown` and the developer has to cast manually.
|
|
18
|
+
*/
|
|
19
|
+
interface BackendEndpoints {
|
|
20
|
+
}
|
|
3
21
|
/**
|
|
4
22
|
* Options accepted by every backend invocation. `body` is JSON-encoded
|
|
5
23
|
* for POST/PUT/PATCH; `query` is appended as a query string. `signal`
|
|
@@ -11,6 +29,25 @@ interface InvokeOptions {
|
|
|
11
29
|
headers?: Record<string, string>;
|
|
12
30
|
signal?: AbortSignal;
|
|
13
31
|
}
|
|
32
|
+
/**
|
|
33
|
+
* Per-call options for the typed RPC `call(name, input)` API.
|
|
34
|
+
* `headers` and `signal` cover the common needs; the body is the
|
|
35
|
+
* input argument itself, so there is no `body` here.
|
|
36
|
+
*/
|
|
37
|
+
interface RpcCallOptions {
|
|
38
|
+
headers?: Record<string, string>;
|
|
39
|
+
signal?: AbortSignal;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Backend connection environment. Drives the base path the client
|
|
43
|
+
* issues requests against. Default is `'remote'` — Kong gateway via
|
|
44
|
+
* the configured Palbase host.
|
|
45
|
+
*/
|
|
46
|
+
type BackendEnvironment = 'remote' | {
|
|
47
|
+
custom: string;
|
|
48
|
+
};
|
|
49
|
+
/** Convenience alias for the most common dev override. */
|
|
50
|
+
declare const LOCALHOST: BackendEnvironment;
|
|
14
51
|
/**
|
|
15
52
|
* BackendInvocationBuilder is the chainable handle returned by
|
|
16
53
|
* `pb.backend.invoke('<path>')`. Each terminal method (`get`, `post`,
|
|
@@ -31,28 +68,50 @@ declare class BackendInvocationBuilder<T = unknown> {
|
|
|
31
68
|
call<R = T>(method: string, options?: InvokeOptions): Promise<PalbaseResponse<R>>;
|
|
32
69
|
private buildQuery;
|
|
33
70
|
}
|
|
71
|
+
/** Resolve typed input/output for an operation when the user has
|
|
72
|
+
* augmented `BackendEndpoints`; otherwise fall back to `unknown`. */
|
|
73
|
+
type EndpointInput<K extends string> = K extends keyof BackendEndpoints ? BackendEndpoints[K] extends {
|
|
74
|
+
input: infer I;
|
|
75
|
+
} ? I : unknown : unknown;
|
|
76
|
+
type EndpointOutput<K extends string> = K extends keyof BackendEndpoints ? BackendEndpoints[K] extends {
|
|
77
|
+
output: infer O;
|
|
78
|
+
} ? O : unknown : unknown;
|
|
34
79
|
/**
|
|
35
80
|
* BackendClient is the entry point exposed as `pb.backend` on the
|
|
36
|
-
* unified client. It issues calls to per-tenant
|
|
37
|
-
* routed through Kong as `<ref>.<host>/
|
|
38
|
-
* a user ships in their `endpoints/` tree becomes
|
|
81
|
+
* unified client. It issues typed RPC calls to per-tenant
|
|
82
|
+
* backend-runtime pods routed through Kong as `<ref>.<host>/rpc/<name>`.
|
|
83
|
+
* Each defineEndpoint a user ships in their `endpoints/` tree becomes
|
|
84
|
+
* one operation here.
|
|
39
85
|
*/
|
|
40
86
|
declare class BackendClient {
|
|
41
87
|
private readonly httpClient;
|
|
42
88
|
constructor(httpClient: HttpClient);
|
|
43
89
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
90
|
+
* Typed RPC dispatch — the primary API. When the user augments
|
|
91
|
+
* `BackendEndpoints` (typically via `palbase backend types`), the
|
|
92
|
+
* `name` argument autocompletes and `input` / return type are
|
|
93
|
+
* inferred. Without augmentation both default to `unknown`.
|
|
94
|
+
*
|
|
95
|
+
* ```ts
|
|
96
|
+
* const r = await pb.backend.call('checkout', { items: ['a'] });
|
|
97
|
+
* // r.data is typed when BackendEndpoints['checkout'] is declared.
|
|
98
|
+
* ```
|
|
99
|
+
*/
|
|
100
|
+
call<K extends string>(name: K & (K extends keyof BackendEndpoints ? K : string), input: EndpointInput<K>, options?: RpcCallOptions): Promise<PalbaseResponse<EndpointOutput<K>>>;
|
|
101
|
+
/**
|
|
102
|
+
* Lower-level builder for callers that need GET/PUT/DELETE etc. The
|
|
103
|
+
* builder talks to the same `/rpc/{path}` route as `call()` — the
|
|
104
|
+
* runtime treats every verb identically and the wire convention is
|
|
105
|
+
* RPC-shaped, but the builder remains for headers/query/AbortSignal
|
|
106
|
+
* affordances the simple `call()` doesn't expose.
|
|
48
107
|
*/
|
|
49
108
|
invoke<T = unknown>(path: string): BackendInvocationBuilder<T>;
|
|
50
109
|
/**
|
|
51
|
-
* One-shot fetch helper
|
|
52
|
-
*
|
|
53
|
-
*
|
|
110
|
+
* One-shot fetch helper for explicit method+path. Kept so existing
|
|
111
|
+
* callers (legacy code, tests) compile; new code should use
|
|
112
|
+
* `call(name, input)` for the typed RPC path.
|
|
54
113
|
*/
|
|
55
|
-
|
|
114
|
+
request<T = unknown>(method: string, path: string, options?: InvokeOptions): Promise<PalbaseResponse<T>>;
|
|
56
115
|
}
|
|
57
116
|
|
|
58
|
-
export { BackendClient, BackendInvocationBuilder, type InvokeOptions };
|
|
117
|
+
export { BackendClient, type BackendEndpoints, type BackendEnvironment, BackendInvocationBuilder, type InvokeOptions, LOCALHOST, type RpcCallOptions };
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/backend-client.ts
|
|
2
|
+
var LOCALHOST = { custom: "http://localhost:4003" };
|
|
2
3
|
var BackendInvocationBuilder = class {
|
|
3
4
|
httpClient;
|
|
4
5
|
path;
|
|
@@ -23,7 +24,7 @@ var BackendInvocationBuilder = class {
|
|
|
23
24
|
}
|
|
24
25
|
call(method, options) {
|
|
25
26
|
const qs = options?.query ? this.buildQuery(options.query) : "";
|
|
26
|
-
const fullPath = `/
|
|
27
|
+
const fullPath = `/rpc/${this.path}${qs}`;
|
|
27
28
|
const reqOpts = {
|
|
28
29
|
headers: options?.headers,
|
|
29
30
|
signal: options?.signal
|
|
@@ -48,25 +49,41 @@ var BackendClient = class {
|
|
|
48
49
|
this.httpClient = httpClient;
|
|
49
50
|
}
|
|
50
51
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
52
|
+
* Typed RPC dispatch — the primary API. When the user augments
|
|
53
|
+
* `BackendEndpoints` (typically via `palbase backend types`), the
|
|
54
|
+
* `name` argument autocompletes and `input` / return type are
|
|
55
|
+
* inferred. Without augmentation both default to `unknown`.
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* const r = await pb.backend.call('checkout', { items: ['a'] });
|
|
59
|
+
* // r.data is typed when BackendEndpoints['checkout'] is declared.
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
call(name, input, options) {
|
|
63
|
+
return this.invoke(name).post(input, options);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Lower-level builder for callers that need GET/PUT/DELETE etc. The
|
|
67
|
+
* builder talks to the same `/rpc/{path}` route as `call()` — the
|
|
68
|
+
* runtime treats every verb identically and the wire convention is
|
|
69
|
+
* RPC-shaped, but the builder remains for headers/query/AbortSignal
|
|
70
|
+
* affordances the simple `call()` doesn't expose.
|
|
55
71
|
*/
|
|
56
72
|
invoke(path) {
|
|
57
73
|
return new BackendInvocationBuilder(this.httpClient, path);
|
|
58
74
|
}
|
|
59
75
|
/**
|
|
60
|
-
* One-shot fetch helper
|
|
61
|
-
*
|
|
62
|
-
*
|
|
76
|
+
* One-shot fetch helper for explicit method+path. Kept so existing
|
|
77
|
+
* callers (legacy code, tests) compile; new code should use
|
|
78
|
+
* `call(name, input)` for the typed RPC path.
|
|
63
79
|
*/
|
|
64
|
-
|
|
80
|
+
request(method, path, options) {
|
|
65
81
|
return this.invoke(path).call(method, options);
|
|
66
82
|
}
|
|
67
83
|
};
|
|
68
84
|
export {
|
|
69
85
|
BackendClient,
|
|
70
|
-
BackendInvocationBuilder
|
|
86
|
+
BackendInvocationBuilder,
|
|
87
|
+
LOCALHOST
|
|
71
88
|
};
|
|
72
89
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/backend-client.ts"],"sourcesContent":["import type { HttpClient, PalbaseResponse, RequestOptions } from '@palbase/core';\n\n/**\n * Options accepted by every backend invocation. `body` is JSON-encoded\n * for POST/PUT/PATCH; `query` is appended as a query string. `signal`\n * lets callers wire AbortController for cancellation.\n */\nexport interface InvokeOptions {\n body?: unknown;\n query?: Record<string, string | number | boolean | null | undefined>;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * BackendInvocationBuilder is the chainable handle returned by\n * `pb.backend.invoke('<path>')`. Each terminal method (`get`, `post`,\n * `put`, `patch`, `del`, `call`) issues a request to\n * `<host>/api/<path>` with the given HTTP verb. The builder is\n * intentionally tiny — backend endpoints are user-defined, so we\n * don't bake any DSL on top.\n */\nexport class BackendInvocationBuilder<T = unknown> {\n private readonly httpClient: HttpClient;\n private readonly path: string;\n\n constructor(httpClient: HttpClient, path: string) {\n this.httpClient = httpClient;\n // Strip exactly one leading slash so callers can type either\n // `pb.backend.invoke('hello')` or `pb.backend.invoke('/hello')`.\n this.path = path.replace(/^\\/+/, '');\n }\n\n get(options?: InvokeOptions): Promise<PalbaseResponse<T>> {\n return this.call<T>('GET', options);\n }\n post(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('POST', { ...options, body });\n }\n put(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('PUT', { ...options, body });\n }\n patch(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('PATCH', { ...options, body });\n }\n del(options?: InvokeOptions): Promise<PalbaseResponse<T>> {\n return this.call<T>('DELETE', options);\n }\n\n call<R = T>(method: string, options?: InvokeOptions): Promise<PalbaseResponse<R>> {\n const qs = options?.query ? this.buildQuery(options.query) : '';\n const fullPath = `/
|
|
1
|
+
{"version":3,"sources":["../src/backend-client.ts"],"sourcesContent":["import type { HttpClient, PalbaseResponse, RequestOptions } from '@palbase/core';\n\n/**\n * Module augmentation hook — users export their endpoint map from\n * `.palbase/types.d.ts` and add the type via:\n *\n * ```ts\n * declare module '@palbase/backend-client' {\n * interface BackendEndpoints {\n * checkout: { input: { items: string[] }; output: { orderId: string } };\n * }\n * }\n * ```\n *\n * `pb.backend.call('checkout', { items: ['a'] })` then resolves the\n * input + output types automatically. Without augmentation the call\n * falls back to `unknown` and the developer has to cast manually.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target on purpose\nexport interface BackendEndpoints {}\n\n/**\n * Options accepted by every backend invocation. `body` is JSON-encoded\n * for POST/PUT/PATCH; `query` is appended as a query string. `signal`\n * lets callers wire AbortController for cancellation.\n */\nexport interface InvokeOptions {\n body?: unknown;\n query?: Record<string, string | number | boolean | null | undefined>;\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * Per-call options for the typed RPC `call(name, input)` API.\n * `headers` and `signal` cover the common needs; the body is the\n * input argument itself, so there is no `body` here.\n */\nexport interface RpcCallOptions {\n headers?: Record<string, string>;\n signal?: AbortSignal;\n}\n\n/**\n * Backend connection environment. Drives the base path the client\n * issues requests against. Default is `'remote'` — Kong gateway via\n * the configured Palbase host.\n */\nexport type BackendEnvironment =\n | 'remote'\n | { custom: string };\n\n/** Convenience alias for the most common dev override. */\nexport const LOCALHOST: BackendEnvironment = { custom: 'http://localhost:4003' };\n\n/**\n * BackendInvocationBuilder is the chainable handle returned by\n * `pb.backend.invoke('<path>')`. Each terminal method (`get`, `post`,\n * `put`, `patch`, `del`, `call`) issues a request to\n * `<host>/api/<path>` with the given HTTP verb. The builder is\n * intentionally tiny — backend endpoints are user-defined, so we\n * don't bake any DSL on top.\n */\nexport class BackendInvocationBuilder<T = unknown> {\n private readonly httpClient: HttpClient;\n private readonly path: string;\n\n constructor(httpClient: HttpClient, path: string) {\n this.httpClient = httpClient;\n // Strip exactly one leading slash so callers can type either\n // `pb.backend.invoke('hello')` or `pb.backend.invoke('/hello')`.\n this.path = path.replace(/^\\/+/, '');\n }\n\n get(options?: InvokeOptions): Promise<PalbaseResponse<T>> {\n return this.call<T>('GET', options);\n }\n post(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('POST', { ...options, body });\n }\n put(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('PUT', { ...options, body });\n }\n patch(body?: unknown, options?: Omit<InvokeOptions, 'body'>): Promise<PalbaseResponse<T>> {\n return this.call<T>('PATCH', { ...options, body });\n }\n del(options?: InvokeOptions): Promise<PalbaseResponse<T>> {\n return this.call<T>('DELETE', options);\n }\n\n call<R = T>(method: string, options?: InvokeOptions): Promise<PalbaseResponse<R>> {\n const qs = options?.query ? this.buildQuery(options.query) : '';\n // Adım B14 — RPC convention: every endpoint is reachable as\n // `POST /rpc/{operationId}`. Kong's palbase-gateway plugin rewrites\n // the path to `/env/{ref}/rpc/{operationId}` server-side.\n const fullPath = `/rpc/${this.path}${qs}`;\n const reqOpts: RequestOptions = {\n headers: options?.headers,\n signal: options?.signal,\n };\n if (options?.body !== undefined) {\n // HttpClient JSON-encodes body when content-type is unset.\n // We pass through raw — caller can override Content-Type via\n // headers if they need form-urlencoded etc.\n (reqOpts as RequestOptions & { body?: unknown }).body = options.body;\n }\n return this.httpClient.request<R>(method, fullPath, reqOpts);\n }\n\n private buildQuery(q: NonNullable<InvokeOptions['query']>): string {\n const parts: string[] = [];\n for (const [k, v] of Object.entries(q)) {\n if (v === undefined || v === null) continue;\n parts.push(`${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);\n }\n return parts.length ? `?${parts.join('&')}` : '';\n }\n}\n\n/** Resolve typed input/output for an operation when the user has\n * augmented `BackendEndpoints`; otherwise fall back to `unknown`. */\ntype EndpointInput<K extends string> = K extends keyof BackendEndpoints\n ? BackendEndpoints[K] extends { input: infer I }\n ? I\n : unknown\n : unknown;\ntype EndpointOutput<K extends string> = K extends keyof BackendEndpoints\n ? BackendEndpoints[K] extends { output: infer O }\n ? O\n : unknown\n : unknown;\n\n/**\n * BackendClient is the entry point exposed as `pb.backend` on the\n * unified client. It issues typed RPC calls to per-tenant\n * backend-runtime pods routed through Kong as `<ref>.<host>/rpc/<name>`.\n * Each defineEndpoint a user ships in their `endpoints/` tree becomes\n * one operation here.\n */\nexport class BackendClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Typed RPC dispatch — the primary API. When the user augments\n * `BackendEndpoints` (typically via `palbase backend types`), the\n * `name` argument autocompletes and `input` / return type are\n * inferred. Without augmentation both default to `unknown`.\n *\n * ```ts\n * const r = await pb.backend.call('checkout', { items: ['a'] });\n * // r.data is typed when BackendEndpoints['checkout'] is declared.\n * ```\n */\n call<K extends string>(\n name: K & (K extends keyof BackendEndpoints ? K : string),\n input: EndpointInput<K>,\n options?: RpcCallOptions,\n ): Promise<PalbaseResponse<EndpointOutput<K>>> {\n return this.invoke<EndpointOutput<K>>(name).post(input, options) as Promise<\n PalbaseResponse<EndpointOutput<K>>\n >;\n }\n\n /**\n * Lower-level builder for callers that need GET/PUT/DELETE etc. The\n * builder talks to the same `/rpc/{path}` route as `call()` — the\n * runtime treats every verb identically and the wire convention is\n * RPC-shaped, but the builder remains for headers/query/AbortSignal\n * affordances the simple `call()` doesn't expose.\n */\n invoke<T = unknown>(path: string): BackendInvocationBuilder<T> {\n return new BackendInvocationBuilder<T>(this.httpClient, path);\n }\n\n /**\n * One-shot fetch helper for explicit method+path. Kept so existing\n * callers (legacy code, tests) compile; new code should use\n * `call(name, input)` for the typed RPC path.\n */\n request<T = unknown>(\n method: string,\n path: string,\n options?: InvokeOptions,\n ): Promise<PalbaseResponse<T>> {\n return this.invoke<T>(path).call<T>(method, options);\n }\n}\n"],"mappings":";AAqDO,IAAM,YAAgC,EAAE,QAAQ,wBAAwB;AAUxE,IAAM,2BAAN,MAA4C;AAAA,EAChC;AAAA,EACA;AAAA,EAEjB,YAAY,YAAwB,MAAc;AAChD,SAAK,aAAa;AAGlB,SAAK,OAAO,KAAK,QAAQ,QAAQ,EAAE;AAAA,EACrC;AAAA,EAEA,IAAI,SAAsD;AACxD,WAAO,KAAK,KAAQ,OAAO,OAAO;AAAA,EACpC;AAAA,EACA,KAAK,MAAgB,SAAoE;AACvF,WAAO,KAAK,KAAQ,QAAQ,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EAClD;AAAA,EACA,IAAI,MAAgB,SAAoE;AACtF,WAAO,KAAK,KAAQ,OAAO,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EACjD;AAAA,EACA,MAAM,MAAgB,SAAoE;AACxF,WAAO,KAAK,KAAQ,SAAS,EAAE,GAAG,SAAS,KAAK,CAAC;AAAA,EACnD;AAAA,EACA,IAAI,SAAsD;AACxD,WAAO,KAAK,KAAQ,UAAU,OAAO;AAAA,EACvC;AAAA,EAEA,KAAY,QAAgB,SAAsD;AAChF,UAAM,KAAK,SAAS,QAAQ,KAAK,WAAW,QAAQ,KAAK,IAAI;AAI7D,UAAM,WAAW,QAAQ,KAAK,IAAI,GAAG,EAAE;AACvC,UAAM,UAA0B;AAAA,MAC9B,SAAS,SAAS;AAAA,MAClB,QAAQ,SAAS;AAAA,IACnB;AACA,QAAI,SAAS,SAAS,QAAW;AAI/B,MAAC,QAAgD,OAAO,QAAQ;AAAA,IAClE;AACA,WAAO,KAAK,WAAW,QAAW,QAAQ,UAAU,OAAO;AAAA,EAC7D;AAAA,EAEQ,WAAW,GAAgD;AACjE,UAAM,QAAkB,CAAC;AACzB,eAAW,CAAC,GAAG,CAAC,KAAK,OAAO,QAAQ,CAAC,GAAG;AACtC,UAAI,MAAM,UAAa,MAAM,KAAM;AACnC,YAAM,KAAK,GAAG,mBAAmB,CAAC,CAAC,IAAI,mBAAmB,OAAO,CAAC,CAAC,CAAC,EAAE;AAAA,IACxE;AACA,WAAO,MAAM,SAAS,IAAI,MAAM,KAAK,GAAG,CAAC,KAAK;AAAA,EAChD;AACF;AAsBO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,KACE,MACA,OACA,SAC6C;AAC7C,WAAO,KAAK,OAA0B,IAAI,EAAE,KAAK,OAAO,OAAO;AAAA,EAGjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,OAAoB,MAA2C;AAC7D,WAAO,IAAI,yBAA4B,KAAK,YAAY,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,QACE,QACA,MACA,SAC6B;AAC7B,WAAO,KAAK,OAAU,IAAI,EAAE,KAAQ,QAAQ,OAAO;AAAA,EACrD;AACF;","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@palbase/backend-client",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
4
4
|
"description": "Palbase backend client — call your project's defineEndpoint handlers from the browser/server",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@palbase/core": "^0.
|
|
31
|
+
"@palbase/core": "^0.9.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@biomejs/biome": "^2.0.0",
|