@palbase/backend-client 0.7.0 → 0.7.2

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 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 = `/api/${this.path}${qs}`;
55
+ const fullPath = `/${this.path}${qs}`;
54
56
  const reqOpts = {
55
57
  headers: options?.headers,
56
58
  signal: options?.signal
@@ -75,26 +77,41 @@ var BackendClient = class {
75
77
  this.httpClient = httpClient;
76
78
  }
77
79
  /**
78
- * Build a request for one of the user's defineEndpoint handlers.
79
- * Path mirrors the file-based routing: `endpoints/users/get.ts`
80
- * `pb.backend.invoke('users')`. Subdirectories add segments:
81
- * `endpoints/orders/[id]/get.ts` `pb.backend.invoke('orders/123')`.
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 file-based route as `call()` — the
96
+ * builder remains for headers/query/AbortSignal affordances the simple
97
+ * `call()` doesn't expose.
82
98
  */
83
99
  invoke(path) {
84
100
  return new BackendInvocationBuilder(this.httpClient, path);
85
101
  }
86
102
  /**
87
- * One-shot fetch helper. Same semantics as `invoke(path).call(method, options)`.
88
- * Useful for dynamic method+path combinations where the chained
89
- * builder reads worse, e.g. when the method is a runtime variable.
103
+ * One-shot fetch helper for explicit method+path. Kept so existing
104
+ * callers (legacy code, tests) compile; new code should use
105
+ * `call(name, input)` for the typed RPC path.
90
106
  */
91
- call(method, path, options) {
107
+ request(method, path, options) {
92
108
  return this.invoke(path).call(method, options);
93
109
  }
94
110
  };
95
111
  // Annotate the CommonJS export names for ESM import in node:
96
112
  0 && (module.exports = {
97
113
  BackendClient,
98
- BackendInvocationBuilder
114
+ BackendInvocationBuilder,
115
+ LOCALHOST
99
116
  });
100
117
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/backend-client.ts"],"sourcesContent":["export { BackendClient, BackendInvocationBuilder } from './backend-client.js';\nexport type { InvokeOptions } from './backend-client.js';\n","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 = `/api/${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/**\n * BackendClient is the entry point exposed as `pb.backend` on the\n * unified client. It issues calls to per-tenant backend-runtime pods\n * routed through Kong as `<ref>.<host>/api/<path>`. Each defineEndpoint\n * a user ships in their `endpoints/` tree becomes one path here.\n */\nexport class BackendClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Build a request for one of the user's defineEndpoint handlers.\n * Path mirrors the file-based routing: `endpoints/users/get.ts`\n * `pb.backend.invoke('users')`. Subdirectories add segments:\n * `endpoints/orders/[id]/get.ts` `pb.backend.invoke('orders/123')`.\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. Same semantics as `invoke(path).call(method, options)`.\n * Useful for dynamic method+path combinations where the chained\n * builder reads worse, e.g. when the method is a runtime variable.\n */\n call<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;;;ACsBO,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;AAC7D,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;AAQO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAoB,MAA2C;AAC7D,WAAO,IAAI,yBAA4B,KAAK,YAAY,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACE,QACA,MACA,SAC6B;AAC7B,WAAO,KAAK,OAAU,IAAI,EAAE,KAAQ,QAAQ,OAAO;AAAA,EACrD;AACF;","names":[]}
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>/<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 — endpoints are file-based (`pb.backend.call(\"users/list\")`\n // -> `POST /users/list`), so the request path is exactly the\n // endpoint name passed to invoke().\n const fullPath = `/${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>/<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 file-based route as `call()` the\n * builder remains for headers/query/AbortSignal affordances the simple\n * `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,IAAI,KAAK,IAAI,GAAG,EAAE;AACnC,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,EAQA,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,11 +29,30 @@ 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`,
17
54
  * `put`, `patch`, `del`, `call`) issues a request to
18
- * `<host>/api/<path>` with the given HTTP verb. The builder is
55
+ * `<host>/<path>` with the given HTTP verb. The builder is
19
56
  * intentionally tiny — backend endpoints are user-defined, so we
20
57
  * don't bake any DSL on top.
21
58
  */
@@ -31,28 +68,49 @@ 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 backend-runtime pods
37
- * routed through Kong as `<ref>.<host>/api/<path>`. Each defineEndpoint
38
- * a user ships in their `endpoints/` tree becomes one path here.
81
+ * unified client. It issues typed RPC calls to per-tenant
82
+ * backend-runtime pods routed through Kong as `<ref>.<host>/<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
- * Build a request for one of the user's defineEndpoint handlers.
45
- * Path mirrors the file-based routing: `endpoints/users/get.ts`
46
- * `pb.backend.invoke('users')`. Subdirectories add segments:
47
- * `endpoints/orders/[id]/get.ts` `pb.backend.invoke('orders/123')`.
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 file-based route as `call()` — the
104
+ * builder remains for headers/query/AbortSignal affordances the simple
105
+ * `call()` doesn't expose.
48
106
  */
49
107
  invoke<T = unknown>(path: string): BackendInvocationBuilder<T>;
50
108
  /**
51
- * One-shot fetch helper. Same semantics as `invoke(path).call(method, options)`.
52
- * Useful for dynamic method+path combinations where the chained
53
- * builder reads worse, e.g. when the method is a runtime variable.
109
+ * One-shot fetch helper for explicit method+path. Kept so existing
110
+ * callers (legacy code, tests) compile; new code should use
111
+ * `call(name, input)` for the typed RPC path.
54
112
  */
55
- call<T = unknown>(method: string, path: string, options?: InvokeOptions): Promise<PalbaseResponse<T>>;
113
+ request<T = unknown>(method: string, path: string, options?: InvokeOptions): Promise<PalbaseResponse<T>>;
56
114
  }
57
115
 
58
- export { BackendClient, BackendInvocationBuilder, type InvokeOptions };
116
+ 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,11 +29,30 @@ 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`,
17
54
  * `put`, `patch`, `del`, `call`) issues a request to
18
- * `<host>/api/<path>` with the given HTTP verb. The builder is
55
+ * `<host>/<path>` with the given HTTP verb. The builder is
19
56
  * intentionally tiny — backend endpoints are user-defined, so we
20
57
  * don't bake any DSL on top.
21
58
  */
@@ -31,28 +68,49 @@ 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 backend-runtime pods
37
- * routed through Kong as `<ref>.<host>/api/<path>`. Each defineEndpoint
38
- * a user ships in their `endpoints/` tree becomes one path here.
81
+ * unified client. It issues typed RPC calls to per-tenant
82
+ * backend-runtime pods routed through Kong as `<ref>.<host>/<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
- * Build a request for one of the user's defineEndpoint handlers.
45
- * Path mirrors the file-based routing: `endpoints/users/get.ts`
46
- * `pb.backend.invoke('users')`. Subdirectories add segments:
47
- * `endpoints/orders/[id]/get.ts` `pb.backend.invoke('orders/123')`.
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 file-based route as `call()` — the
104
+ * builder remains for headers/query/AbortSignal affordances the simple
105
+ * `call()` doesn't expose.
48
106
  */
49
107
  invoke<T = unknown>(path: string): BackendInvocationBuilder<T>;
50
108
  /**
51
- * One-shot fetch helper. Same semantics as `invoke(path).call(method, options)`.
52
- * Useful for dynamic method+path combinations where the chained
53
- * builder reads worse, e.g. when the method is a runtime variable.
109
+ * One-shot fetch helper for explicit method+path. Kept so existing
110
+ * callers (legacy code, tests) compile; new code should use
111
+ * `call(name, input)` for the typed RPC path.
54
112
  */
55
- call<T = unknown>(method: string, path: string, options?: InvokeOptions): Promise<PalbaseResponse<T>>;
113
+ request<T = unknown>(method: string, path: string, options?: InvokeOptions): Promise<PalbaseResponse<T>>;
56
114
  }
57
115
 
58
- export { BackendClient, BackendInvocationBuilder, type InvokeOptions };
116
+ 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 = `/api/${this.path}${qs}`;
27
+ const fullPath = `/${this.path}${qs}`;
27
28
  const reqOpts = {
28
29
  headers: options?.headers,
29
30
  signal: options?.signal
@@ -48,25 +49,40 @@ var BackendClient = class {
48
49
  this.httpClient = httpClient;
49
50
  }
50
51
  /**
51
- * Build a request for one of the user's defineEndpoint handlers.
52
- * Path mirrors the file-based routing: `endpoints/users/get.ts`
53
- * `pb.backend.invoke('users')`. Subdirectories add segments:
54
- * `endpoints/orders/[id]/get.ts` `pb.backend.invoke('orders/123')`.
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 file-based route as `call()` — the
68
+ * builder remains for headers/query/AbortSignal affordances the simple
69
+ * `call()` doesn't expose.
55
70
  */
56
71
  invoke(path) {
57
72
  return new BackendInvocationBuilder(this.httpClient, path);
58
73
  }
59
74
  /**
60
- * One-shot fetch helper. Same semantics as `invoke(path).call(method, options)`.
61
- * Useful for dynamic method+path combinations where the chained
62
- * builder reads worse, e.g. when the method is a runtime variable.
75
+ * One-shot fetch helper for explicit method+path. Kept so existing
76
+ * callers (legacy code, tests) compile; new code should use
77
+ * `call(name, input)` for the typed RPC path.
63
78
  */
64
- call(method, path, options) {
79
+ request(method, path, options) {
65
80
  return this.invoke(path).call(method, options);
66
81
  }
67
82
  };
68
83
  export {
69
84
  BackendClient,
70
- BackendInvocationBuilder
85
+ BackendInvocationBuilder,
86
+ LOCALHOST
71
87
  };
72
88
  //# 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 = `/api/${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/**\n * BackendClient is the entry point exposed as `pb.backend` on the\n * unified client. It issues calls to per-tenant backend-runtime pods\n * routed through Kong as `<ref>.<host>/api/<path>`. Each defineEndpoint\n * a user ships in their `endpoints/` tree becomes one path here.\n */\nexport class BackendClient {\n private readonly httpClient: HttpClient;\n\n constructor(httpClient: HttpClient) {\n this.httpClient = httpClient;\n }\n\n /**\n * Build a request for one of the user's defineEndpoint handlers.\n * Path mirrors the file-based routing: `endpoints/users/get.ts`\n * `pb.backend.invoke('users')`. Subdirectories add segments:\n * `endpoints/orders/[id]/get.ts` `pb.backend.invoke('orders/123')`.\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. Same semantics as `invoke(path).call(method, options)`.\n * Useful for dynamic method+path combinations where the chained\n * builder reads worse, e.g. when the method is a runtime variable.\n */\n call<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":";AAsBO,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;AAC7D,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;AAQO,IAAM,gBAAN,MAAoB;AAAA,EACR;AAAA,EAEjB,YAAY,YAAwB;AAClC,SAAK,aAAa;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,OAAoB,MAA2C;AAC7D,WAAO,IAAI,yBAA4B,KAAK,YAAY,IAAI;AAAA,EAC9D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,KACE,QACA,MACA,SAC6B;AAC7B,WAAO,KAAK,OAAU,IAAI,EAAE,KAAQ,QAAQ,OAAO;AAAA,EACrD;AACF;","names":[]}
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>/<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 — endpoints are file-based (`pb.backend.call(\"users/list\")`\n // -> `POST /users/list`), so the request path is exactly the\n // endpoint name passed to invoke().\n const fullPath = `/${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>/<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 file-based route as `call()` the\n * builder remains for headers/query/AbortSignal affordances the simple\n * `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,IAAI,KAAK,IAAI,GAAG,EAAE;AACnC,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,EAQA,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.0",
3
+ "version": "0.7.2",
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.7.0"
31
+ "@palbase/core": "^1.0.0"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@biomejs/biome": "^2.0.0",