@qcobro/sdk 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # @qcobro/sdk
2
+
3
+ A developer-friendly TypeScript SDK for the [QCobro](https://qcobro.com) API. It wraps the
4
+ server's tRPC interface behind an ergonomic, fully-typed `Client` so you can manage QCobro
5
+ resources without touching transport details.
6
+
7
+ This release covers **portfolios**, including account synchronization. More resources land in
8
+ follow-up releases.
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @qcobro/sdk
14
+ ```
15
+
16
+ Requires a runtime with a global `fetch` (Node ≥18 or a modern browser). For older runtimes,
17
+ pass a `fetch` polyfill via the `fetch` option.
18
+
19
+ ## Quick start
20
+
21
+ ```ts
22
+ import { Client } from "@qcobro/sdk";
23
+
24
+ const client = new Client({ endpoint: "https://api.qcobro.com" });
25
+
26
+ // Authenticate and pick the workspace to act in.
27
+ await client.login({ email: "me@acme.com", password: process.env.QCOBRO_PASSWORD! });
28
+ client.useWorkspace("WO6ueex0qan9ojhf820wgiae3qi5luy08y");
29
+
30
+ // ...or, for unattended/server-to-server integrations, use a workspace API key:
31
+ await client.loginWithApiKey({
32
+ accessKeyId: "WO6ueex0qan9ojhf820wgiae3qi5luy08y",
33
+ accessKeySecret: process.env.QCOBRO_API_SECRET!
34
+ });
35
+
36
+ // Manage portfolios.
37
+ const portfolio = await client.portfolios.create({
38
+ name: "Q3 delinquencies",
39
+ clientId: "acme",
40
+ currency: "USD"
41
+ });
42
+
43
+ const { items, total } = await client.portfolios.listAccounts({ portfolioId: portfolio.id });
44
+
45
+ await client.portfolios.syncAccounts({
46
+ portfolioId: portfolio.id,
47
+ mode: "APPEND_ONLY",
48
+ rows: [{ externalId: "A-1", fullName: "Jane Doe", outstandingBalance: 1200.5 }]
49
+ });
50
+ ```
51
+
52
+ ## Authentication & tokens
53
+
54
+ Authenticate one of two ways:
55
+
56
+ - `login({ email, password })` — interactive credentials login (returns id/access/refresh tokens).
57
+ - `loginWithApiKey({ accessKeyId, accessKeySecret })` — a workspace API key, for unattended
58
+ server-to-server use where there's no human to type a password.
59
+
60
+ The `Client` holds tokens **in memory**. Persisting them (so a session survives a restart) is
61
+ your responsibility:
62
+
63
+ ```ts
64
+ // Save after login...
65
+ const tokens = client.getTokens();
66
+ // ...and restore later.
67
+ const client = new Client({ endpoint }).setTokens(tokens);
68
+
69
+ // Refresh an expired access token.
70
+ await client.refresh();
71
+ ```
72
+
73
+ **Auto-refresh.** By default, if a call returns `UNAUTHORIZED` and a refresh token is held, the
74
+ client refreshes the access token **once** and replays the request transparently — concurrent
75
+ failures share a single refresh. If the refresh itself fails, the original `UNAUTHORIZED` is
76
+ surfaced. Disable it with `new Client({ endpoint, autoRefresh: false })`.
77
+
78
+ ## Validation & errors
79
+
80
+ Inputs are validated client-side against the shared QCobro schemas **before** a request is
81
+ sent. Invalid input throws a `ValidationError` with field-level details, and no network call is
82
+ made. Server-side authorization failures (e.g. an unauthenticated or wrong-workspace call)
83
+ surface as the server's error.
84
+
85
+ ```ts
86
+ import { ValidationError } from "@qcobro/sdk";
87
+
88
+ try {
89
+ await client.portfolios.create({ name: "", clientId: "acme", currency: "USD" });
90
+ } catch (err) {
91
+ if (err instanceof ValidationError) {
92
+ console.error(err.fieldErrors); // [{ field: "name", message: "...", code: "..." }]
93
+ }
94
+ }
95
+ ```
96
+
97
+ ## API reference
98
+
99
+ Generate the markdown API reference with:
100
+
101
+ ```bash
102
+ npm run docs
103
+ ```
104
+
105
+ Output lands in `docs/`.
@@ -0,0 +1,132 @@
1
+ import { type CreateTRPCClient } from "@trpc/client";
2
+ import type { AppRouter } from "@qcobro/apiserver";
3
+ import type { LoginInput, ApiKeyLoginInput } from "@qcobro/common";
4
+ import { PortfoliosResource } from "./resources/portfolios.js";
5
+ /** Tokens issued by the QCobro Identity service. */
6
+ export interface Tokens {
7
+ /** Short-lived bearer token attached to every authenticated request. */
8
+ accessToken?: string;
9
+ /** Long-lived token used to obtain a fresh access token via {@link Client.refresh}. */
10
+ refreshToken?: string;
11
+ /** JWT describing the authenticated user (claims), when issued. */
12
+ idToken?: string;
13
+ }
14
+ /** Options for constructing a {@link Client}. */
15
+ export interface ClientOptions {
16
+ /**
17
+ * Base URL of the QCobro API. Defaults to `https://api.qcobro.com`; override
18
+ * only to target another environment (e.g. `http://localhost:3000`). The SDK
19
+ * appends the `/trpc` path itself.
20
+ */
21
+ endpoint?: string;
22
+ /**
23
+ * `fetch` implementation to use. Defaults to the global `fetch` (Node ≥18 and
24
+ * modern browsers). Provide this to supply a polyfill in older runtimes.
25
+ */
26
+ fetch?: typeof globalThis.fetch;
27
+ /** An access token to start authenticated, instead of calling {@link Client.login}. */
28
+ accessToken?: string;
29
+ /** A refresh token, enabling {@link Client.refresh} without re-login. */
30
+ refreshToken?: string;
31
+ /** The accessKeyId of the workspace to act in. Also settable via {@link Client.useWorkspace}. */
32
+ workspace?: string;
33
+ /**
34
+ * When `true` (the default), an `UNAUTHORIZED` response triggers a single
35
+ * token refresh and one replay of the failed request, provided a refresh
36
+ * token is held. Set to `false` to disable and surface `UNAUTHORIZED` directly.
37
+ */
38
+ autoRefresh?: boolean;
39
+ }
40
+ /**
41
+ * The QCobro API client.
42
+ *
43
+ * A single `Client` owns the connection and the authentication lifecycle, and
44
+ * exposes resources as namespaces with friendly methods (e.g.
45
+ * {@link Client.portfolios}). It transparently attaches the bearer token and the
46
+ * active-workspace header to every request.
47
+ *
48
+ * Tokens are held **in memory only** — persisting them (if you want sessions to
49
+ * survive a restart) is the caller's responsibility via {@link Client.getTokens}
50
+ * and {@link Client.setTokens}.
51
+ *
52
+ * @example
53
+ * ```ts
54
+ * const client = new Client();
55
+ * await client.login({ email: "me@acme.com", password: process.env.QCOBRO_PASSWORD! });
56
+ * client.useWorkspace("WO6ueex0qan9ojhf820wgiae3qi5luy08y");
57
+ *
58
+ * await client.portfolios.create({ name: "Q3 delinquencies", clientId: "acme" });
59
+ * const portfolios = await client.portfolios.list();
60
+ * ```
61
+ */
62
+ export declare class Client {
63
+ #private;
64
+ /**
65
+ * The underlying typed tRPC proxy. Exposed as an escape hatch for procedures
66
+ * the SDK does not yet wrap; prefer the resource namespaces where available.
67
+ */
68
+ readonly trpc: CreateTRPCClient<AppRouter>;
69
+ /** Portfolio operations (list, get, create, update, delete, accounts, sync). */
70
+ readonly portfolios: PortfoliosResource;
71
+ constructor(options?: ClientOptions);
72
+ /**
73
+ * Run a request, transparently refreshing the access token once on an
74
+ * `UNAUTHORIZED` error and replaying the request. Used internally by the
75
+ * resource namespaces.
76
+ *
77
+ * Refresh happens at most once per failed request: if the replay also fails,
78
+ * that error is surfaced (no retry loop). Concurrent failures share a single
79
+ * in-flight refresh. If auto-refresh is disabled, no refresh token is held, or
80
+ * the refresh itself fails, the original error is surfaced unchanged.
81
+ *
82
+ * @internal
83
+ */
84
+ request<T>(fn: () => Promise<T>): Promise<T>;
85
+ /**
86
+ * Authenticate with email and password. On success the issued access (and
87
+ * refresh) token is stored on the client and used for subsequent calls.
88
+ *
89
+ * @returns the issued tokens.
90
+ */
91
+ login(input: LoginInput): Promise<Tokens>;
92
+ /**
93
+ * Authenticate with a workspace API key (accessKeyId + accessKeySecret),
94
+ * intended for unattended, server-to-server integrations. On success the
95
+ * issued access (and refresh) token is stored on the client and used for
96
+ * subsequent calls.
97
+ *
98
+ * @returns the issued tokens.
99
+ *
100
+ * @example
101
+ * ```ts
102
+ * await client.loginWithApiKey({
103
+ * accessKeyId: "WO6ueex0qan9ojhf820wgiae3qi5luy08y",
104
+ * accessKeySecret: process.env.QCOBRO_API_SECRET!
105
+ * });
106
+ * ```
107
+ */
108
+ loginWithApiKey(input: ApiKeyLoginInput): Promise<Tokens>;
109
+ /**
110
+ * Exchange a refresh token for a fresh access token. Uses the refresh token
111
+ * held by the client unless one is passed explicitly. The new access token
112
+ * replaces the current one.
113
+ *
114
+ * @returns the issued tokens.
115
+ */
116
+ refresh(refreshToken?: string): Promise<Tokens>;
117
+ /**
118
+ * Select the active workspace by its accessKeyId. Subsequent workspace-scoped
119
+ * calls act within this workspace.
120
+ */
121
+ useWorkspace(accessKeyId: string): this;
122
+ /** The accessKeyId of the active workspace, or `undefined` if none is selected. */
123
+ get workspace(): string | undefined;
124
+ /** Returns the tokens currently held by the client (in memory). */
125
+ getTokens(): Tokens;
126
+ /**
127
+ * Replace the tokens held by the client. Useful to resume a session from
128
+ * tokens you persisted yourself.
129
+ */
130
+ setTokens(tokens: Tokens): this;
131
+ }
132
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmC,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACtF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAQ/D,oDAAoD;AACpD,MAAM,WAAW,MAAM;IACrB,wEAAwE;IACxE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uFAAuF;IACvF,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,iDAAiD;AACjD,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,KAAK,CAAC,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAChC,uFAAuF;IACvF,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,MAAM;;IACjB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,SAAS,CAAC,CAAC;IAE3C,gFAAgF;IAChF,QAAQ,CAAC,UAAU,EAAE,kBAAkB,CAAC;gBAS5B,OAAO,GAAE,aAAkB;IA2BvC;;;;;;;;;;;OAWG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IAgClD;;;;;OAKG;IACG,KAAK,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IAO/C;;;;;;;;;;;;;;;OAeG;IACG,eAAe,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC;IAO/D;;;;;;OAMG;IACG,OAAO,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAWrD;;;OAGG;IACH,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAKvC,mFAAmF;IACnF,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAElC;IAED,mEAAmE;IACnE,SAAS,IAAI,MAAM;IAInB;;;OAGG;IACH,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI;CAKhC"}
package/dist/client.js ADDED
@@ -0,0 +1,192 @@
1
+ import { createTRPCClient, httpBatchLink } from "@trpc/client";
2
+ import { PortfoliosResource } from "./resources/portfolios.js";
3
+ /** Header the apiserver reads to scope a request to a workspace. */
4
+ const WORKSPACE_HEADER = "x-workspace";
5
+ /** Default QCobro API base URL, used when no `endpoint` is provided. */
6
+ const DEFAULT_ENDPOINT = "https://api.qcobro.com";
7
+ /**
8
+ * The QCobro API client.
9
+ *
10
+ * A single `Client` owns the connection and the authentication lifecycle, and
11
+ * exposes resources as namespaces with friendly methods (e.g.
12
+ * {@link Client.portfolios}). It transparently attaches the bearer token and the
13
+ * active-workspace header to every request.
14
+ *
15
+ * Tokens are held **in memory only** — persisting them (if you want sessions to
16
+ * survive a restart) is the caller's responsibility via {@link Client.getTokens}
17
+ * and {@link Client.setTokens}.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const client = new Client();
22
+ * await client.login({ email: "me@acme.com", password: process.env.QCOBRO_PASSWORD! });
23
+ * client.useWorkspace("WO6ueex0qan9ojhf820wgiae3qi5luy08y");
24
+ *
25
+ * await client.portfolios.create({ name: "Q3 delinquencies", clientId: "acme" });
26
+ * const portfolios = await client.portfolios.list();
27
+ * ```
28
+ */
29
+ export class Client {
30
+ /**
31
+ * The underlying typed tRPC proxy. Exposed as an escape hatch for procedures
32
+ * the SDK does not yet wrap; prefer the resource namespaces where available.
33
+ */
34
+ trpc;
35
+ /** Portfolio operations (list, get, create, update, delete, accounts, sync). */
36
+ portfolios;
37
+ #accessToken;
38
+ #refreshToken;
39
+ #workspace;
40
+ #autoRefresh;
41
+ // Shared in-flight refresh, so concurrent UNAUTHORIZED calls refresh once.
42
+ #refreshInFlight = null;
43
+ constructor(options = {}) {
44
+ this.#accessToken = options.accessToken;
45
+ this.#refreshToken = options.refreshToken;
46
+ this.#workspace = options.workspace;
47
+ this.#autoRefresh = options.autoRefresh ?? true;
48
+ const url = `${(options.endpoint ?? DEFAULT_ENDPOINT).replace(/\/+$/, "")}/trpc`;
49
+ this.trpc = createTRPCClient({
50
+ links: [
51
+ httpBatchLink({
52
+ url,
53
+ fetch: options.fetch,
54
+ // Read current auth state on every request so a `login()` or
55
+ // `useWorkspace()` after construction applies without rebuilding.
56
+ headers: () => {
57
+ const headers = {};
58
+ if (this.#accessToken)
59
+ headers.Authorization = `Bearer ${this.#accessToken}`;
60
+ if (this.#workspace)
61
+ headers[WORKSPACE_HEADER] = this.#workspace;
62
+ return headers;
63
+ }
64
+ })
65
+ ]
66
+ });
67
+ this.portfolios = new PortfoliosResource(this.trpc, (fn) => this.request(fn));
68
+ }
69
+ /**
70
+ * Run a request, transparently refreshing the access token once on an
71
+ * `UNAUTHORIZED` error and replaying the request. Used internally by the
72
+ * resource namespaces.
73
+ *
74
+ * Refresh happens at most once per failed request: if the replay also fails,
75
+ * that error is surfaced (no retry loop). Concurrent failures share a single
76
+ * in-flight refresh. If auto-refresh is disabled, no refresh token is held, or
77
+ * the refresh itself fails, the original error is surfaced unchanged.
78
+ *
79
+ * @internal
80
+ */
81
+ async request(fn) {
82
+ try {
83
+ return await fn();
84
+ }
85
+ catch (err) {
86
+ if (!this.#shouldRefresh(err))
87
+ throw err;
88
+ try {
89
+ await this.#refreshOnce();
90
+ }
91
+ catch {
92
+ // Refresh failed (e.g. expired refresh token) — surface the original
93
+ // auth error rather than the refresh failure.
94
+ throw err;
95
+ }
96
+ return fn();
97
+ }
98
+ }
99
+ #shouldRefresh(err) {
100
+ if (!this.#autoRefresh || !this.#refreshToken)
101
+ return false;
102
+ return err?.data?.code === "UNAUTHORIZED";
103
+ }
104
+ #refreshOnce() {
105
+ if (!this.#refreshInFlight) {
106
+ this.#refreshInFlight = this.refresh()
107
+ .then(() => undefined)
108
+ .finally(() => {
109
+ this.#refreshInFlight = null;
110
+ });
111
+ }
112
+ return this.#refreshInFlight;
113
+ }
114
+ /**
115
+ * Authenticate with email and password. On success the issued access (and
116
+ * refresh) token is stored on the client and used for subsequent calls.
117
+ *
118
+ * @returns the issued tokens.
119
+ */
120
+ async login(input) {
121
+ const tokens = await this.trpc.auth.login.mutate(input);
122
+ this.#accessToken = tokens.accessToken;
123
+ this.#refreshToken = tokens.refreshToken;
124
+ return tokens;
125
+ }
126
+ /**
127
+ * Authenticate with a workspace API key (accessKeyId + accessKeySecret),
128
+ * intended for unattended, server-to-server integrations. On success the
129
+ * issued access (and refresh) token is stored on the client and used for
130
+ * subsequent calls.
131
+ *
132
+ * @returns the issued tokens.
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * await client.loginWithApiKey({
137
+ * accessKeyId: "WO6ueex0qan9ojhf820wgiae3qi5luy08y",
138
+ * accessKeySecret: process.env.QCOBRO_API_SECRET!
139
+ * });
140
+ * ```
141
+ */
142
+ async loginWithApiKey(input) {
143
+ const tokens = await this.trpc.auth.exchangeApiKey.mutate(input);
144
+ this.#accessToken = tokens.accessToken;
145
+ this.#refreshToken = tokens.refreshToken;
146
+ return tokens;
147
+ }
148
+ /**
149
+ * Exchange a refresh token for a fresh access token. Uses the refresh token
150
+ * held by the client unless one is passed explicitly. The new access token
151
+ * replaces the current one.
152
+ *
153
+ * @returns the issued tokens.
154
+ */
155
+ async refresh(refreshToken) {
156
+ const token = refreshToken ?? this.#refreshToken;
157
+ if (!token) {
158
+ throw new Error("No refresh token available; pass one or call login() first.");
159
+ }
160
+ const tokens = await this.trpc.auth.refresh.mutate({ refreshToken: token });
161
+ this.#accessToken = tokens.accessToken;
162
+ if (tokens.refreshToken)
163
+ this.#refreshToken = tokens.refreshToken;
164
+ return tokens;
165
+ }
166
+ /**
167
+ * Select the active workspace by its accessKeyId. Subsequent workspace-scoped
168
+ * calls act within this workspace.
169
+ */
170
+ useWorkspace(accessKeyId) {
171
+ this.#workspace = accessKeyId;
172
+ return this;
173
+ }
174
+ /** The accessKeyId of the active workspace, or `undefined` if none is selected. */
175
+ get workspace() {
176
+ return this.#workspace;
177
+ }
178
+ /** Returns the tokens currently held by the client (in memory). */
179
+ getTokens() {
180
+ return { accessToken: this.#accessToken, refreshToken: this.#refreshToken };
181
+ }
182
+ /**
183
+ * Replace the tokens held by the client. Useful to resume a session from
184
+ * tokens you persisted yourself.
185
+ */
186
+ setTokens(tokens) {
187
+ this.#accessToken = tokens.accessToken;
188
+ this.#refreshToken = tokens.refreshToken;
189
+ return this;
190
+ }
191
+ }
192
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * `@qcobro/sdk` — a developer-friendly TypeScript SDK for the QCobro API.
3
+ *
4
+ * Construct a {@link Client}, authenticate, select a workspace, and call typed
5
+ * resource methods (e.g. {@link Client.portfolios}).
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { Client } from "./client.js";
10
+ export type { ClientOptions, Tokens } from "./client.js";
11
+ export { PortfoliosResource } from "./resources/portfolios.js";
12
+ export { ValidationError, type FieldError } from "@qcobro/common";
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACrC,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAI/D,OAAO,EAAE,eAAe,EAAE,KAAK,UAAU,EAAE,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `@qcobro/sdk` — a developer-friendly TypeScript SDK for the QCobro API.
3
+ *
4
+ * Construct a {@link Client}, authenticate, select a workspace, and call typed
5
+ * resource methods (e.g. {@link Client.portfolios}).
6
+ *
7
+ * @packageDocumentation
8
+ */
9
+ export { Client } from "./client.js";
10
+ export { PortfoliosResource } from "./resources/portfolios.js";
11
+ // Re-export the shared structured error so callers can detect client-side
12
+ // validation failures without depending on `@qcobro/common` directly.
13
+ export { ValidationError } from "@qcobro/common";
14
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAErC,OAAO,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAE/D,0EAA0E;AAC1E,sEAAsE;AACtE,OAAO,EAAE,eAAe,EAAmB,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,140 @@
1
+ import type { CreateTRPCClient } from "@trpc/client";
2
+ import type { AppRouter } from "@qcobro/apiserver";
3
+ type RouterClient = CreateTRPCClient<AppRouter>;
4
+ type Portfolios = RouterClient["portfolios"];
5
+ /** Input accepted by each portfolios method, derived from the server router. */
6
+ type ListInput = Parameters<Portfolios["list"]["query"]>[0];
7
+ type GetInput = Parameters<Portfolios["get"]["query"]>[0];
8
+ type CreateInput = Parameters<Portfolios["create"]["mutate"]>[0];
9
+ type UpdateInput = Parameters<Portfolios["update"]["mutate"]>[0];
10
+ type DeleteInput = Parameters<Portfolios["delete"]["mutate"]>[0];
11
+ type ListAccountsInput = Parameters<Portfolios["listAccounts"]["query"]>[0];
12
+ type SyncAccountsInput = Parameters<Portfolios["syncAccounts"]["mutate"]>[0];
13
+ /**
14
+ * Portfolio operations.
15
+ *
16
+ * All methods are workspace-scoped: the {@link Client} must be authenticated and
17
+ * have an active workspace selected. Inputs are validated client-side against the
18
+ * shared `@qcobro/common` schemas before any request is sent — invalid input
19
+ * throws a {@link ValidationError} and never reaches the network.
20
+ *
21
+ * Obtain an instance via `client.portfolios`; do not construct it directly.
22
+ */
23
+ /** Runs a request, transparently refreshing + replaying once on `UNAUTHORIZED`. */
24
+ type RequestRunner = <T>(fn: () => Promise<T>) => Promise<T>;
25
+ export declare class PortfoliosResource {
26
+ #private;
27
+ /** @internal */
28
+ constructor(trpc: RouterClient, request: RequestRunner);
29
+ /** List the active workspace's portfolios. Pass `includeArchived` to include archived ones. */
30
+ list(input?: ListInput): Promise<{
31
+ id: string;
32
+ name: string;
33
+ workspaceRef: string;
34
+ archivedAt: string | null;
35
+ createdAt: string;
36
+ updatedAt: string;
37
+ accountCount: number;
38
+ recoveredAmount: number;
39
+ clientId: string;
40
+ totalOutstandingBalance: number;
41
+ }[]>;
42
+ /** Get a single portfolio by id within the active workspace. */
43
+ get(input: GetInput): Promise<{
44
+ id: string;
45
+ name: string;
46
+ workspaceRef: string;
47
+ archivedAt: string | null;
48
+ createdAt: string;
49
+ updatedAt: string;
50
+ accountCount: number;
51
+ recoveredAmount: number;
52
+ clientId: string;
53
+ totalOutstandingBalance: number;
54
+ }>;
55
+ /** Create a portfolio in the active workspace. */
56
+ create(input: CreateInput): Promise<{
57
+ id: string;
58
+ name: string;
59
+ workspaceRef: string;
60
+ archivedAt: string | null;
61
+ createdAt: string;
62
+ updatedAt: string;
63
+ accountCount: number;
64
+ recoveredAmount: number;
65
+ clientId: string;
66
+ totalOutstandingBalance: number;
67
+ }>;
68
+ /** Update a portfolio. Set `archived: true` to archive it, `false` to restore it. */
69
+ update(input: UpdateInput): Promise<{
70
+ id: string;
71
+ name: string;
72
+ workspaceRef: string;
73
+ archivedAt: string | null;
74
+ createdAt: string;
75
+ updatedAt: string;
76
+ accountCount: number;
77
+ recoveredAmount: number;
78
+ clientId: string;
79
+ totalOutstandingBalance: number;
80
+ }>;
81
+ /** Delete a portfolio in the active workspace. */
82
+ delete(input: DeleteInput): Promise<{
83
+ id: string;
84
+ name: string;
85
+ workspaceRef: string;
86
+ archivedAt: string | null;
87
+ createdAt: string;
88
+ updatedAt: string;
89
+ accountCount: number;
90
+ recoveredAmount: number;
91
+ clientId: string;
92
+ totalOutstandingBalance: number;
93
+ }>;
94
+ /** List a page of a portfolio's accounts, with the total count. */
95
+ listAccounts(input: ListAccountsInput): Promise<{
96
+ items: {
97
+ id: string;
98
+ portfolioId: string;
99
+ archivedAt: string | null;
100
+ createdAt: string;
101
+ updatedAt: string;
102
+ outstandingBalance: number;
103
+ email: string | null;
104
+ phone: string | null;
105
+ externalId: string;
106
+ fullName: string;
107
+ preferredLanguage: string | null;
108
+ bestTimeToCall: string | null;
109
+ customerSegment: string | null;
110
+ principalAmount: number;
111
+ termsAmount: number;
112
+ termsFrequency: string | null;
113
+ termsLength: number;
114
+ daysPastDue: number;
115
+ missedInstallments: number;
116
+ lastPaymentDate: string | null;
117
+ lastPaymentAmount: number | null;
118
+ negotiationOptions: string | null;
119
+ lastContactedAt: string | null;
120
+ suppressUntil: string | null;
121
+ intentStatus: import("@prisma/client").$Enums.IntentStatus | null;
122
+ totalAttempts: number;
123
+ }[];
124
+ total: number;
125
+ }>;
126
+ /**
127
+ * Synchronize a batch of account rows into a portfolio.
128
+ *
129
+ * `mode` controls the merge strategy: `APPEND_ONLY` adds new rows,
130
+ * `UPDATE_EXISTING` updates rows that already exist, `REPLACE` replaces the set.
131
+ */
132
+ syncAccounts(input: SyncAccountsInput): Promise<{
133
+ created: number;
134
+ updated: number;
135
+ archived: number;
136
+ total: number;
137
+ }>;
138
+ }
139
+ export {};
140
+ //# sourceMappingURL=portfolios.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolios.d.ts","sourceRoot":"","sources":["../../src/resources/portfolios.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAWnD,KAAK,YAAY,GAAG,gBAAgB,CAAC,SAAS,CAAC,CAAC;AAChD,KAAK,UAAU,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;AAE7C,gFAAgF;AAChF,KAAK,SAAS,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,KAAK,QAAQ,GAAG,UAAU,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE,KAAK,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE,KAAK,WAAW,GAAG,UAAU,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACjE,KAAK,iBAAiB,GAAG,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5E,KAAK,iBAAiB,GAAG,UAAU,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAS7E;;;;;;;;;GASG;AACH,mFAAmF;AACnF,KAAK,aAAa,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,CAAC;AAE7D,qBAAa,kBAAkB;;IAI7B,gBAAgB;gBACJ,IAAI,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa;IAKtD,+FAA+F;IACzF,IAAI,CAAC,KAAK,CAAC,EAAE,SAAS;;;;;;;;;;;;IAK5B,gEAAgE;IAC1D,GAAG,CAAC,KAAK,EAAE,QAAQ;;;;;;;;;;;;IAKzB,kDAAkD;IAC5C,MAAM,CAAC,KAAK,EAAE,WAAW;;;;;;;;;;;;IAK/B,qFAAqF;IAC/E,MAAM,CAAC,KAAK,EAAE,WAAW;;;;;;;;;;;;IAK/B,kDAAkD;IAC5C,MAAM,CAAC,KAAK,EAAE,WAAW;;;;;;;;;;;;IAK/B,mEAAmE;IAC7D,YAAY,CAAC,KAAK,EAAE,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAK3C;;;;;OAKG;IACG,YAAY,CAAC,KAAK,EAAE,iBAAiB;;;;;;CAI5C"}
@@ -0,0 +1,59 @@
1
+ import { createPortfolioSchema, updatePortfolioSchema, deletePortfolioSchema, syncAccountsInputSchema, ValidationError } from "@qcobro/common";
2
+ import { listPortfoliosSchema, getPortfolioSchema, listAccountsSchema } from "../schemas.js";
3
+ /** Validate `input` against `schema`, throwing a structured {@link ValidationError} on failure. */
4
+ function parse(schema, input) {
5
+ const result = schema.safeParse(input);
6
+ if (!result.success)
7
+ throw new ValidationError(result.error);
8
+ return result.data;
9
+ }
10
+ export class PortfoliosResource {
11
+ #trpc;
12
+ #request;
13
+ /** @internal */
14
+ constructor(trpc, request) {
15
+ this.#trpc = trpc;
16
+ this.#request = request;
17
+ }
18
+ /** List the active workspace's portfolios. Pass `includeArchived` to include archived ones. */
19
+ async list(input) {
20
+ const parsed = parse(listPortfoliosSchema, input);
21
+ return this.#request(() => this.#trpc.portfolios.list.query(parsed));
22
+ }
23
+ /** Get a single portfolio by id within the active workspace. */
24
+ async get(input) {
25
+ const parsed = parse(getPortfolioSchema, input);
26
+ return this.#request(() => this.#trpc.portfolios.get.query(parsed));
27
+ }
28
+ /** Create a portfolio in the active workspace. */
29
+ async create(input) {
30
+ const parsed = parse(createPortfolioSchema, input);
31
+ return this.#request(() => this.#trpc.portfolios.create.mutate(parsed));
32
+ }
33
+ /** Update a portfolio. Set `archived: true` to archive it, `false` to restore it. */
34
+ async update(input) {
35
+ const parsed = parse(updatePortfolioSchema, input);
36
+ return this.#request(() => this.#trpc.portfolios.update.mutate(parsed));
37
+ }
38
+ /** Delete a portfolio in the active workspace. */
39
+ async delete(input) {
40
+ const parsed = parse(deletePortfolioSchema, input);
41
+ return this.#request(() => this.#trpc.portfolios.delete.mutate(parsed));
42
+ }
43
+ /** List a page of a portfolio's accounts, with the total count. */
44
+ async listAccounts(input) {
45
+ const parsed = parse(listAccountsSchema, input);
46
+ return this.#request(() => this.#trpc.portfolios.listAccounts.query(parsed));
47
+ }
48
+ /**
49
+ * Synchronize a batch of account rows into a portfolio.
50
+ *
51
+ * `mode` controls the merge strategy: `APPEND_ONLY` adds new rows,
52
+ * `UPDATE_EXISTING` updates rows that already exist, `REPLACE` replaces the set.
53
+ */
54
+ async syncAccounts(input) {
55
+ const parsed = parse(syncAccountsInputSchema, input);
56
+ return this.#request(() => this.#trpc.portfolios.syncAccounts.mutate(parsed));
57
+ }
58
+ }
59
+ //# sourceMappingURL=portfolios.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"portfolios.js","sourceRoot":"","sources":["../../src/resources/portfolios.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Local schemas for portfolio operations whose inputs the apiserver defines
4
+ * inline (rather than in `@qcobro/common`). Kept minimal and matched to the
5
+ * server's inline `z.object(...)` shapes so the SDK can validate client-side
6
+ * before a request is sent. Contract-bearing inputs (create/update/delete/
7
+ * syncAccounts) reuse the shared `@qcobro/common` schemas directly.
8
+ */
9
+ /** Input for `portfolios.list`. */
10
+ export declare const listPortfoliosSchema: z.ZodOptional<z.ZodObject<{
11
+ includeArchived: z.ZodOptional<z.ZodBoolean>;
12
+ }, z.core.$strip>>;
13
+ export type ListPortfoliosInput = z.infer<typeof listPortfoliosSchema>;
14
+ /** Input for `portfolios.get`. */
15
+ export declare const getPortfolioSchema: z.ZodObject<{
16
+ id: z.ZodString;
17
+ }, z.core.$strip>;
18
+ export type GetPortfolioInput = z.infer<typeof getPortfolioSchema>;
19
+ /** Input for `portfolios.listAccounts`. */
20
+ export declare const listAccountsSchema: z.ZodObject<{
21
+ portfolioId: z.ZodString;
22
+ limit: z.ZodOptional<z.ZodNumber>;
23
+ offset: z.ZodOptional<z.ZodNumber>;
24
+ }, z.core.$strip>;
25
+ export type ListAccountsInput = z.infer<typeof listAccountsSchema>;
26
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AAEH,mCAAmC;AACnC,eAAO,MAAM,oBAAoB;;kBAIpB,CAAC;AACd,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAEvE,kCAAkC;AAClC,eAAO,MAAM,kBAAkB;;iBAE7B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAEnE,2CAA2C;AAC3C,eAAO,MAAM,kBAAkB;;;;iBAI7B,CAAC;AACH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Local schemas for portfolio operations whose inputs the apiserver defines
4
+ * inline (rather than in `@qcobro/common`). Kept minimal and matched to the
5
+ * server's inline `z.object(...)` shapes so the SDK can validate client-side
6
+ * before a request is sent. Contract-bearing inputs (create/update/delete/
7
+ * syncAccounts) reuse the shared `@qcobro/common` schemas directly.
8
+ */
9
+ /** Input for `portfolios.list`. */
10
+ export const listPortfoliosSchema = z
11
+ .object({
12
+ includeArchived: z.boolean().optional()
13
+ })
14
+ .optional();
15
+ /** Input for `portfolios.get`. */
16
+ export const getPortfolioSchema = z.object({
17
+ id: z.string().min(1)
18
+ });
19
+ /** Input for `portfolios.listAccounts`. */
20
+ export const listAccountsSchema = z.object({
21
+ portfolioId: z.string().min(1),
22
+ limit: z.number().int().min(1).max(200).optional(),
23
+ offset: z.number().int().min(0).optional()
24
+ });
25
+ //# sourceMappingURL=schemas.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AAEH,mCAAmC;AACnC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC;KAClC,MAAM,CAAC;IACN,eAAe,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACxC,CAAC;KACD,QAAQ,EAAE,CAAC;AAGd,kCAAkC;AAClC,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,EAAE,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;CACtB,CAAC,CAAC;AAGH,2CAA2C;AAC3C,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IACzC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClD,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC3C,CAAC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,37 @@
1
+ {
2
+ "name": "@qcobro/sdk",
3
+ "version": "1.11.0",
4
+ "description": "Developer-friendly TypeScript SDK for the QCobro API.",
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsc -b --force",
17
+ "clean": "rm -rf dist docs *.tsbuildinfo",
18
+ "typecheck": "tsc --noEmit",
19
+ "test": "node --import tsx --test \"src/**/*.test.ts\"",
20
+ "docs": "typedoc"
21
+ },
22
+ "dependencies": {
23
+ "@qcobro/common": "^1.11.0",
24
+ "@trpc/client": "^11.0.0",
25
+ "zod": "^4.0.0"
26
+ },
27
+ "devDependencies": {
28
+ "@qcobro/apiserver": "^1.11.0",
29
+ "@trpc/server": "^11.0.0",
30
+ "@types/express": "^5.0.0",
31
+ "express": "^5.0.0",
32
+ "tsx": "^4.0.0",
33
+ "typedoc": "^0.28.0",
34
+ "typedoc-plugin-markdown": "^4.0.0",
35
+ "typescript": "^5.9.0"
36
+ }
37
+ }