getricky 0.0.0-stage → 0.1.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.
Files changed (88) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +136 -2
  3. package/dist/cjs/client.d.ts +51 -0
  4. package/dist/cjs/client.js +124 -0
  5. package/dist/cjs/errors.d.ts +35 -0
  6. package/dist/cjs/errors.js +55 -0
  7. package/dist/cjs/generated/client/client.gen.d.ts +2 -0
  8. package/dist/cjs/generated/client/client.gen.js +220 -0
  9. package/dist/cjs/generated/client/index.d.ts +10 -0
  10. package/dist/cjs/generated/client/index.js +17 -0
  11. package/dist/cjs/generated/client/types.gen.d.ts +120 -0
  12. package/dist/cjs/generated/client/types.gen.js +3 -0
  13. package/dist/cjs/generated/client/utils.gen.d.ts +37 -0
  14. package/dist/cjs/generated/client/utils.gen.js +239 -0
  15. package/dist/cjs/generated/client.gen.d.ts +12 -0
  16. package/dist/cjs/generated/client.gen.js +6 -0
  17. package/dist/cjs/generated/core/auth.gen.d.ts +25 -0
  18. package/dist/cjs/generated/core/auth.gen.js +18 -0
  19. package/dist/cjs/generated/core/bodySerializer.gen.d.ts +25 -0
  20. package/dist/cjs/generated/core/bodySerializer.gen.js +60 -0
  21. package/dist/cjs/generated/core/params.gen.d.ts +43 -0
  22. package/dist/cjs/generated/core/params.gen.js +112 -0
  23. package/dist/cjs/generated/core/pathSerializer.gen.d.ts +33 -0
  24. package/dist/cjs/generated/core/pathSerializer.gen.js +115 -0
  25. package/dist/cjs/generated/core/queryKeySerializer.gen.d.ts +18 -0
  26. package/dist/cjs/generated/core/queryKeySerializer.gen.js +98 -0
  27. package/dist/cjs/generated/core/serverSentEvents.gen.d.ts +71 -0
  28. package/dist/cjs/generated/core/serverSentEvents.gen.js +135 -0
  29. package/dist/cjs/generated/core/types.gen.d.ts +83 -0
  30. package/dist/cjs/generated/core/types.gen.js +3 -0
  31. package/dist/cjs/generated/core/utils.gen.d.ts +19 -0
  32. package/dist/cjs/generated/core/utils.gen.js +93 -0
  33. package/dist/cjs/generated/idempotency.gen.d.ts +2 -0
  34. package/dist/cjs/generated/idempotency.gen.js +16 -0
  35. package/dist/cjs/generated/index.d.ts +2 -0
  36. package/dist/cjs/generated/index.js +6 -0
  37. package/dist/cjs/generated/sdk.gen.d.ts +259 -0
  38. package/dist/cjs/generated/sdk.gen.js +493 -0
  39. package/dist/cjs/generated/types.gen.d.ts +2557 -0
  40. package/dist/cjs/generated/types.gen.js +3 -0
  41. package/dist/cjs/index.d.ts +7 -0
  42. package/dist/cjs/index.js +12 -0
  43. package/dist/cjs/package.json +1 -0
  44. package/dist/cjs/webhooks.d.ts +25 -0
  45. package/dist/cjs/webhooks.js +59 -0
  46. package/dist/esm/client.d.ts +51 -0
  47. package/dist/esm/client.js +120 -0
  48. package/dist/esm/errors.d.ts +35 -0
  49. package/dist/esm/errors.js +49 -0
  50. package/dist/esm/generated/client/client.gen.d.ts +2 -0
  51. package/dist/esm/generated/client/client.gen.js +216 -0
  52. package/dist/esm/generated/client/index.d.ts +10 -0
  53. package/dist/esm/generated/client/index.js +6 -0
  54. package/dist/esm/generated/client/types.gen.d.ts +120 -0
  55. package/dist/esm/generated/client/types.gen.js +2 -0
  56. package/dist/esm/generated/client/utils.gen.d.ts +37 -0
  57. package/dist/esm/generated/client/utils.gen.js +228 -0
  58. package/dist/esm/generated/client.gen.d.ts +12 -0
  59. package/dist/esm/generated/client.gen.js +3 -0
  60. package/dist/esm/generated/core/auth.gen.d.ts +25 -0
  61. package/dist/esm/generated/core/auth.gen.js +14 -0
  62. package/dist/esm/generated/core/bodySerializer.gen.d.ts +25 -0
  63. package/dist/esm/generated/core/bodySerializer.gen.js +57 -0
  64. package/dist/esm/generated/core/params.gen.d.ts +43 -0
  65. package/dist/esm/generated/core/params.gen.js +109 -0
  66. package/dist/esm/generated/core/pathSerializer.gen.d.ts +33 -0
  67. package/dist/esm/generated/core/pathSerializer.gen.js +106 -0
  68. package/dist/esm/generated/core/queryKeySerializer.gen.d.ts +18 -0
  69. package/dist/esm/generated/core/queryKeySerializer.gen.js +92 -0
  70. package/dist/esm/generated/core/serverSentEvents.gen.d.ts +71 -0
  71. package/dist/esm/generated/core/serverSentEvents.gen.js +132 -0
  72. package/dist/esm/generated/core/types.gen.d.ts +83 -0
  73. package/dist/esm/generated/core/types.gen.js +2 -0
  74. package/dist/esm/generated/core/utils.gen.d.ts +19 -0
  75. package/dist/esm/generated/core/utils.gen.js +87 -0
  76. package/dist/esm/generated/idempotency.gen.d.ts +2 -0
  77. package/dist/esm/generated/idempotency.gen.js +13 -0
  78. package/dist/esm/generated/index.d.ts +2 -0
  79. package/dist/esm/generated/index.js +2 -0
  80. package/dist/esm/generated/sdk.gen.d.ts +259 -0
  81. package/dist/esm/generated/sdk.gen.js +489 -0
  82. package/dist/esm/generated/types.gen.d.ts +2557 -0
  83. package/dist/esm/generated/types.gen.js +2 -0
  84. package/dist/esm/index.d.ts +7 -0
  85. package/dist/esm/index.js +3 -0
  86. package/dist/esm/webhooks.d.ts +25 -0
  87. package/dist/esm/webhooks.js +56 -0
  88. package/package.json +64 -4
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ricky
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,137 @@
1
- # Temporary Holding Version
1
+ # Ricky TypeScript SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ The official TypeScript and JavaScript client for the [Ricky](https://getricky.ai) REST API: contacts, conversations, messages, templates and webhooks of one organization.
4
+
5
+ - Guides and reference: <https://getricky.ai/docs/api>, also in [the API repository](https://github.com/ricardostmalo/ricky-api/tree/main/docs) as Markdown
6
+ - Source and OpenAPI description: <https://github.com/ricardostmalo/ricky-api>
7
+
8
+ Node.js 18 or later, ES modules or CommonJS, with types included. No runtime dependencies: it uses the built-in `fetch`.
9
+
10
+ ```bash
11
+ npm install getricky
12
+ ```
13
+
14
+ ## Quick start
15
+
16
+ Create a key in Ricky under **Settings → API** and keep it on your server. `new Ricky()` reads it from `RICKY_API_KEY` when you pass none.
17
+
18
+ ```ts
19
+ import { Ricky } from 'getricky';
20
+
21
+ const ricky = new Ricky(); // or new Ricky('rky_live_…')
22
+
23
+ // The channels the organization can send from.
24
+ const { data: channels } = await ricky.listChannels();
25
+ const whatsapp = channels.find((channel) => channel.type === 'whatsapp');
26
+
27
+ // Send an approved WhatsApp template to a phone number. Ricky finds or creates
28
+ // the contact and its conversation, and answers with the send it queued.
29
+ let { data: send } = await ricky.startConversation({
30
+ body: {
31
+ channel_id: whatsapp!.id,
32
+ phone: '+50760001234',
33
+ template: {
34
+ id: '1234567890',
35
+ components: [{ type: 'body', parameters: [{ type: 'text', text: 'Ana' }] }],
36
+ },
37
+ },
38
+ headers: { 'Idempotency-Key': 'order-10482-ready' },
39
+ });
40
+
41
+ // Follow it until the provider accepts it.
42
+ while (send.status === 'queued') {
43
+ await new Promise((resolve) => setTimeout(resolve, 2000));
44
+ ({ data: send } = await ricky.getSend({ path: { send_id: send.id } }));
45
+ }
46
+ console.log(send.status, send.failure_reason);
47
+ ```
48
+
49
+ Every operation of the API is a method named after its `operationId` (`listContacts`, `sendMessage`, …). Each takes one object with the parts the operation uses, `{ path, query, body, headers }`, and resolves to the response body as the API sends it: `{ data }` for one resource and `{ data, next_cursor }` for a list. Every request and response is typed, and every schema is exported as a type (`Contact`, `Conversation`, `SendMessage`, …).
50
+
51
+ ## Pagination
52
+
53
+ `paginate` walks every page of a list operation and yields its items; `.pages()` yields whole pages:
54
+
55
+ ```ts
56
+ for await (const contact of ricky.paginate(ricky.listContacts, { query: { tag_id } })) {
57
+ console.log(contact.id, contact.first_name);
58
+ }
59
+
60
+ for await (const page of ricky
61
+ .paginate(ricky.listMessages, { path: { conversation_id } })
62
+ .pages()) {
63
+ console.log(page.data.length);
64
+ }
65
+ ```
66
+
67
+ ## Errors
68
+
69
+ A failed request rejects with `RickyError`, carrying the API's `code`, the HTTP `status`, the `param` that caused it when known, and the `requestId` to quote to support. A request that never got an answer rejects with `RickyConnectionError`.
70
+
71
+ ```ts
72
+ import { RickyError } from 'getricky';
73
+
74
+ try {
75
+ await ricky.sendMessage({
76
+ path: { conversation_id },
77
+ body: { type: 'text', text: 'Hola' },
78
+ });
79
+ } catch (error) {
80
+ if (error instanceof RickyError && error.code === 'outside_messaging_window') {
81
+ // Send an approved template instead.
82
+ } else {
83
+ throw error;
84
+ }
85
+ }
86
+ ```
87
+
88
+ ## Retries and idempotency
89
+
90
+ Writes that accept an `Idempotency-Key` get a random one unless you pass `headers: { 'Idempotency-Key': … }`. Pass your own, derived from your data, to make a retry from your side safe too: the same key and body replay the first response instead of repeating the write.
91
+
92
+ Reads, and writes that carry a key, are retried twice on connection errors, `429` and `5xx`, waiting as `Retry-After` says or backing off exponentially. Change it with `new Ricky(key, { maxRetries })`. A write the API may have applied before failing answers `request_processing` on retry: read the resource before trying again.
93
+
94
+ ## Webhooks
95
+
96
+ `verifyWebhook` checks the `X-Ricky-Signature` and `X-Ricky-Timestamp` headers against the raw body, rejects deliveries more than five minutes old, and returns the event. With Express:
97
+
98
+ ```ts
99
+ import express from 'express';
100
+ import { verifyWebhook, WebhookVerificationError } from 'getricky';
101
+
102
+ const app = express();
103
+
104
+ app.post('/ricky/webhooks', express.raw({ type: 'application/json' }), (req, res) => {
105
+ let event;
106
+ try {
107
+ event = verifyWebhook(req.body, req.headers, process.env.RICKY_WEBHOOK_SECRET!);
108
+ } catch (error) {
109
+ if (error instanceof WebhookVerificationError) return res.sendStatus(401);
110
+ throw error;
111
+ }
112
+ if (event.type === 'message.received') {
113
+ // Queue the work; deduplicate by event.id.
114
+ }
115
+ res.sendStatus(204);
116
+ });
117
+ ```
118
+
119
+ In a framework built on the Fetch API (Next.js route handlers, Hono), pass `await request.text()` and `request.headers`.
120
+
121
+ Answer within 10 seconds. Deliveries can repeat, so deduplicate by `event.id`. New event types ship without a new API version: handle the ones you know and ignore the rest.
122
+
123
+ ## Configuration
124
+
125
+ ```ts
126
+ new Ricky('rky_live_…', {
127
+ baseUrl: 'https://getricky.ai/api/v1',
128
+ maxRetries: 2,
129
+ fetch: globalThis.fetch, // any fetch implementation
130
+ });
131
+ ```
132
+
133
+ Cancel a request by passing `signal` from an `AbortController` with its options.
134
+
135
+ ## License
136
+
137
+ [MIT](LICENSE)
@@ -0,0 +1,51 @@
1
+ import { RickyApi } from './generated/sdk.gen.js';
2
+ export declare const DEFAULT_BASE_URL = "https://getricky.ai/api/v1";
3
+ export type RickyOptions = {
4
+ /** Defaults to `https://getricky.ai/api/v1`. */
5
+ baseUrl?: string;
6
+ /** A fetch implementation; defaults to the global `fetch`. */
7
+ fetch?: typeof fetch;
8
+ /** Retries after the first attempt for retryable failures. Defaults to 2. */
9
+ maxRetries?: number;
10
+ };
11
+ /** One page of a list: `next_cursor` is null on the last page. */
12
+ export type Page<TItem> = {
13
+ data: TItem[];
14
+ next_cursor: string | null;
15
+ };
16
+ /** Any list operation of the client: it resolves to `{ data: [...], next_cursor }`. */
17
+ type ListOperation = (options: any) => Promise<unknown>;
18
+ /** The item type of a list operation. */
19
+ type ItemOf<TList extends ListOperation> = Awaited<ReturnType<TList>> extends infer TPage ? TPage extends {
20
+ data: (infer TItem)[];
21
+ } ? TItem : never : never;
22
+ /** Items of every page, in order; `pages()` yields whole pages instead. */
23
+ export type Paginator<TItem> = AsyncIterable<TItem> & {
24
+ pages(): AsyncIterable<Page<TItem>>;
25
+ };
26
+ /**
27
+ * A client for one organization's API key. Every operation in the API is a
28
+ * method named after its `operationId`, taking `{ path, query, body, headers }`
29
+ * and resolving to the response body (`{ data }` or `{ data, next_cursor }`).
30
+ * Failures reject with `RickyError` or `RickyConnectionError`.
31
+ *
32
+ * Writes that accept an `Idempotency-Key` get a random one unless the caller
33
+ * passes `headers: { 'Idempotency-Key': … }`. Reads and keyed writes are
34
+ * retried on connection errors, 429 and 5xx, honoring `Retry-After`.
35
+ */
36
+ export declare class Ricky extends RickyApi {
37
+ /**
38
+ * @param apiKey An organization API key (`rky_live_…`); defaults to the
39
+ * `RICKY_API_KEY` environment variable. Use it only from a server.
40
+ */
41
+ constructor(apiKey?: string, options?: RickyOptions);
42
+ /**
43
+ * Walks a list operation page by page, passing each `next_cursor` back as
44
+ * `cursor`.
45
+ *
46
+ * @example
47
+ * for await (const contact of ricky.paginate(ricky.listContacts, { query: { tag_id } })) { … }
48
+ */
49
+ paginate<TList extends ListOperation>(list: TList, options?: Parameters<TList>[0]): Paginator<ItemOf<TList>>;
50
+ }
51
+ export {};
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Ricky = exports.DEFAULT_BASE_URL = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const errors_js_1 = require("./errors.js");
6
+ const index_js_1 = require("./generated/client/index.js");
7
+ const idempotency_gen_js_1 = require("./generated/idempotency.gen.js");
8
+ const sdk_gen_js_1 = require("./generated/sdk.gen.js");
9
+ exports.DEFAULT_BASE_URL = 'https://getricky.ai/api/v1';
10
+ const RETRYABLE_STATUSES = new Set([429, 500, 502, 503, 504]);
11
+ const MAX_RETRY_AFTER_SECONDS = 60;
12
+ /**
13
+ * A client for one organization's API key. Every operation in the API is a
14
+ * method named after its `operationId`, taking `{ path, query, body, headers }`
15
+ * and resolving to the response body (`{ data }` or `{ data, next_cursor }`).
16
+ * Failures reject with `RickyError` or `RickyConnectionError`.
17
+ *
18
+ * Writes that accept an `Idempotency-Key` get a random one unless the caller
19
+ * passes `headers: { 'Idempotency-Key': … }`. Reads and keyed writes are
20
+ * retried on connection errors, 429 and 5xx, honoring `Retry-After`.
21
+ */
22
+ class Ricky extends sdk_gen_js_1.RickyApi {
23
+ /**
24
+ * @param apiKey An organization API key (`rky_live_…`); defaults to the
25
+ * `RICKY_API_KEY` environment variable. Use it only from a server.
26
+ */
27
+ constructor(apiKey, options = {}) {
28
+ const key = apiKey ?? globalThis.process?.env?.RICKY_API_KEY;
29
+ if (!key)
30
+ throw new Error('Pass an API key or set RICKY_API_KEY.');
31
+ const client = (0, index_js_1.createClient)((0, index_js_1.createConfig)({
32
+ auth: key,
33
+ baseUrl: options.baseUrl ?? exports.DEFAULT_BASE_URL,
34
+ fetch: retryingFetch(options.fetch ?? globalThis.fetch, options.maxRetries ?? 2),
35
+ throwOnError: true,
36
+ }));
37
+ client.interceptors.request.use((request, { url }) => {
38
+ if (!request.headers.has('Idempotency-Key') &&
39
+ idempotency_gen_js_1.idempotentOperations.has(`${request.method} ${url}`)) {
40
+ request.headers.set('Idempotency-Key', (0, node_crypto_1.randomUUID)());
41
+ }
42
+ return request;
43
+ });
44
+ client.interceptors.error.use((error, response) => response ? errors_js_1.RickyError.fromResponse(response, error) : asConnectionError(error));
45
+ super({ client });
46
+ }
47
+ /**
48
+ * Walks a list operation page by page, passing each `next_cursor` back as
49
+ * `cursor`.
50
+ *
51
+ * @example
52
+ * for await (const contact of ricky.paginate(ricky.listContacts, { query: { tag_id } })) { … }
53
+ */
54
+ paginate(list, options) {
55
+ const initial = options;
56
+ const fetchPage = (cursor) => list.call(this, {
57
+ ...initial,
58
+ query: { ...initial?.query, ...(cursor === undefined ? {} : { cursor }) },
59
+ });
60
+ async function* pages() {
61
+ let cursor = initial?.query?.cursor;
62
+ do {
63
+ const page = await fetchPage(cursor);
64
+ yield page;
65
+ cursor = page.next_cursor ?? undefined;
66
+ } while (cursor !== undefined);
67
+ }
68
+ return {
69
+ async *[Symbol.asyncIterator]() {
70
+ for await (const page of pages())
71
+ yield* page.data;
72
+ },
73
+ pages,
74
+ };
75
+ }
76
+ }
77
+ exports.Ricky = Ricky;
78
+ function retryingFetch(baseFetch, maxRetries) {
79
+ return async (input, init) => {
80
+ const request = new Request(input, init);
81
+ const retryable = request.method === 'GET' ||
82
+ request.method === 'HEAD' ||
83
+ request.headers.has('Idempotency-Key');
84
+ for (let attempt = 0;; attempt += 1) {
85
+ const last = !retryable || attempt >= maxRetries;
86
+ let response;
87
+ try {
88
+ response = await baseFetch(last ? request : request.clone());
89
+ }
90
+ catch (error) {
91
+ if (last || request.signal.aborted)
92
+ throw asConnectionError(error);
93
+ await sleep(backoffSeconds(attempt));
94
+ continue;
95
+ }
96
+ if (last || !RETRYABLE_STATUSES.has(response.status))
97
+ return response;
98
+ const delay = retryAfterSeconds(response.headers.get('retry-after')) ?? backoffSeconds(attempt);
99
+ await response.body?.cancel();
100
+ await sleep(delay);
101
+ }
102
+ };
103
+ }
104
+ /** `Retry-After` as seconds (delta or HTTP date), capped; null when absent or invalid. */
105
+ function retryAfterSeconds(header) {
106
+ if (header === null || header.trim() === '')
107
+ return null;
108
+ const seconds = /^\d+$/.test(header.trim())
109
+ ? Number(header.trim())
110
+ : (Date.parse(header) - Date.now()) / 1000;
111
+ return Number.isFinite(seconds) ? Math.min(Math.max(seconds, 0), MAX_RETRY_AFTER_SECONDS) : null;
112
+ }
113
+ /** 0.5 s, 1 s, 2 s … up to 8 s, with up to 25 % jitter below. */
114
+ function backoffSeconds(attempt) {
115
+ return Math.min(0.5 * 2 ** attempt, 8) * (1 - Math.random() * 0.25);
116
+ }
117
+ function sleep(seconds) {
118
+ return new Promise((resolve) => setTimeout(resolve, seconds * 1000));
119
+ }
120
+ function asConnectionError(error) {
121
+ return error instanceof errors_js_1.RickyError || error instanceof errors_js_1.RickyConnectionError
122
+ ? error
123
+ : new errors_js_1.RickyConnectionError(error);
124
+ }
@@ -0,0 +1,35 @@
1
+ import type { Error as ErrorBody } from './generated/types.gen.js';
2
+ /** A stable error code from the API's catalog. New codes may appear at any time. */
3
+ export type RickyErrorCode = ErrorBody['error']['code'] | (string & {});
4
+ /**
5
+ * The API answered with an error. `code` is stable and safe to branch on;
6
+ * `message` is for people. `code` and `requestId` are null only when the
7
+ * response did not come from the API itself (a proxy page, for example).
8
+ */
9
+ export declare class RickyError extends Error {
10
+ readonly name = "RickyError";
11
+ readonly status: number;
12
+ readonly code: RickyErrorCode | null;
13
+ readonly param: string | null;
14
+ readonly requestId: string | null;
15
+ readonly headers: Headers;
16
+ constructor(options: {
17
+ code: RickyErrorCode | null;
18
+ headers: Headers;
19
+ message: string;
20
+ param: string | null;
21
+ requestId: string | null;
22
+ status: number;
23
+ });
24
+ /** Builds the error from a non-2xx response and its body, parsed when it was JSON. */
25
+ static fromResponse(response: Response, body: unknown): RickyError;
26
+ }
27
+ /** The request never got an answer: DNS, TLS, a dropped connection or an abort. */
28
+ export declare class RickyConnectionError extends Error {
29
+ readonly name = "RickyConnectionError";
30
+ constructor(cause: unknown);
31
+ }
32
+ /** A webhook delivery failed verification and must be rejected. */
33
+ export declare class WebhookVerificationError extends Error {
34
+ readonly name = "WebhookVerificationError";
35
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.WebhookVerificationError = exports.RickyConnectionError = exports.RickyError = void 0;
4
+ /**
5
+ * The API answered with an error. `code` is stable and safe to branch on;
6
+ * `message` is for people. `code` and `requestId` are null only when the
7
+ * response did not come from the API itself (a proxy page, for example).
8
+ */
9
+ class RickyError extends Error {
10
+ name = 'RickyError';
11
+ status;
12
+ code;
13
+ param;
14
+ requestId;
15
+ headers;
16
+ constructor(options) {
17
+ super(options.message);
18
+ this.status = options.status;
19
+ this.code = options.code;
20
+ this.param = options.param;
21
+ this.requestId = options.requestId;
22
+ this.headers = options.headers;
23
+ }
24
+ /** Builds the error from a non-2xx response and its body, parsed when it was JSON. */
25
+ static fromResponse(response, body) {
26
+ const error = isRecord(body) && isRecord(body.error) ? body.error : null;
27
+ const text = (value) => (typeof value === 'string' ? value : null);
28
+ return new RickyError({
29
+ code: text(error?.code),
30
+ headers: response.headers,
31
+ message: text(error?.message) ??
32
+ `HTTP ${response.status}${typeof body === 'string' && body ? `: ${body.slice(0, 200)}` : ''}`,
33
+ param: text(error?.param),
34
+ requestId: text(error?.request_id) ?? response.headers.get('x-request-id'),
35
+ status: response.status,
36
+ });
37
+ }
38
+ }
39
+ exports.RickyError = RickyError;
40
+ /** The request never got an answer: DNS, TLS, a dropped connection or an abort. */
41
+ class RickyConnectionError extends Error {
42
+ name = 'RickyConnectionError';
43
+ constructor(cause) {
44
+ super(cause instanceof Error ? cause.message : 'The request to Ricky failed', { cause });
45
+ }
46
+ }
47
+ exports.RickyConnectionError = RickyConnectionError;
48
+ /** A webhook delivery failed verification and must be rejected. */
49
+ class WebhookVerificationError extends Error {
50
+ name = 'WebhookVerificationError';
51
+ }
52
+ exports.WebhookVerificationError = WebhookVerificationError;
53
+ function isRecord(value) {
54
+ return typeof value === 'object' && value !== null;
55
+ }
@@ -0,0 +1,2 @@
1
+ import type { Client, Config } from './types.gen.js';
2
+ export declare const createClient: (config?: Config) => Client;
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ // This file is auto-generated by @hey-api/openapi-ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.createClient = void 0;
5
+ const serverSentEvents_gen_js_1 = require("../core/serverSentEvents.gen.js");
6
+ const utils_gen_js_1 = require("../core/utils.gen.js");
7
+ const utils_gen_js_2 = require("./utils.gen.js");
8
+ const createClient = (config = {}) => {
9
+ let _config = (0, utils_gen_js_2.mergeConfigs)((0, utils_gen_js_2.createConfig)(), config);
10
+ const getConfig = () => ({ ..._config });
11
+ const setConfig = (config) => {
12
+ _config = (0, utils_gen_js_2.mergeConfigs)(_config, config);
13
+ return getConfig();
14
+ };
15
+ const interceptors = (0, utils_gen_js_2.createInterceptors)();
16
+ const beforeRequest = async (options) => {
17
+ const opts = {
18
+ ..._config,
19
+ ...options,
20
+ fetch: options.fetch ?? _config.fetch ?? globalThis.fetch,
21
+ headers: (0, utils_gen_js_2.mergeHeaders)(_config.headers, options.headers),
22
+ serializedBody: undefined,
23
+ };
24
+ if (opts.security) {
25
+ await (0, utils_gen_js_2.setAuthParams)(opts);
26
+ }
27
+ if (opts.requestValidator) {
28
+ await opts.requestValidator(opts);
29
+ }
30
+ if (opts.body !== undefined && opts.bodySerializer) {
31
+ opts.serializedBody = opts.bodySerializer(opts.body);
32
+ }
33
+ // remove Content-Type header if body is empty to avoid sending invalid requests
34
+ if (opts.body === undefined || opts.serializedBody === '') {
35
+ opts.headers.delete('Content-Type');
36
+ }
37
+ const resolvedOpts = opts;
38
+ const url = (0, utils_gen_js_2.buildUrl)(resolvedOpts);
39
+ return { opts: resolvedOpts, url };
40
+ };
41
+ const request = async (options) => {
42
+ const throwOnError = options.throwOnError ?? _config.throwOnError;
43
+ const responseStyle = options.responseStyle ?? _config.responseStyle;
44
+ let request;
45
+ let response;
46
+ try {
47
+ const { opts, url } = await beforeRequest(options);
48
+ const requestInit = {
49
+ redirect: 'follow',
50
+ ...opts,
51
+ body: (0, utils_gen_js_1.getValidRequestBody)(opts),
52
+ };
53
+ request = new Request(url, requestInit);
54
+ for (const fn of interceptors.request.fns) {
55
+ if (fn) {
56
+ request = await fn(request, opts);
57
+ }
58
+ }
59
+ // fetch must be assigned here, otherwise it would throw the error:
60
+ // TypeError: Failed to execute 'fetch' on 'Window': Illegal invocation
61
+ const _fetch = opts.fetch;
62
+ response = await _fetch(request);
63
+ for (const fn of interceptors.response.fns) {
64
+ if (fn) {
65
+ response = await fn(response, request, opts);
66
+ }
67
+ }
68
+ const result = {
69
+ request,
70
+ response,
71
+ };
72
+ if (response.ok) {
73
+ const parseAs = (opts.parseAs === 'auto'
74
+ ? (0, utils_gen_js_2.getParseAs)(response.headers.get('Content-Type'))
75
+ : opts.parseAs) ?? 'json';
76
+ if (response.status === 204 || response.headers.get('Content-Length') === '0') {
77
+ let emptyData;
78
+ switch (parseAs) {
79
+ case 'arrayBuffer':
80
+ case 'blob':
81
+ case 'text':
82
+ emptyData = await response[parseAs]();
83
+ break;
84
+ case 'formData':
85
+ emptyData = new FormData();
86
+ break;
87
+ case 'stream':
88
+ emptyData = response.body;
89
+ break;
90
+ case 'json':
91
+ default:
92
+ emptyData = {};
93
+ break;
94
+ }
95
+ return opts.responseStyle === 'data'
96
+ ? emptyData
97
+ : {
98
+ data: emptyData,
99
+ ...result,
100
+ };
101
+ }
102
+ let data;
103
+ switch (parseAs) {
104
+ case 'arrayBuffer':
105
+ case 'blob':
106
+ case 'formData':
107
+ case 'text':
108
+ data = await response[parseAs]();
109
+ break;
110
+ case 'json': {
111
+ // Some servers return 200 with no Content-Length and empty body.
112
+ // response.json() would throw; read as text and parse if non-empty.
113
+ const text = await response.text();
114
+ data = text ? JSON.parse(text) : {};
115
+ break;
116
+ }
117
+ case 'stream':
118
+ return opts.responseStyle === 'data'
119
+ ? response.body
120
+ : {
121
+ data: response.body,
122
+ ...result,
123
+ };
124
+ }
125
+ if (parseAs === 'json') {
126
+ if (opts.responseValidator) {
127
+ await opts.responseValidator(data);
128
+ }
129
+ if (opts.responseTransformer) {
130
+ data = await opts.responseTransformer(data);
131
+ }
132
+ }
133
+ return opts.responseStyle === 'data'
134
+ ? data
135
+ : {
136
+ data,
137
+ ...result,
138
+ };
139
+ }
140
+ const textError = await response.text();
141
+ let jsonError;
142
+ try {
143
+ jsonError = JSON.parse(textError);
144
+ }
145
+ catch {
146
+ // noop
147
+ }
148
+ throw jsonError ?? textError;
149
+ }
150
+ catch (error) {
151
+ let finalError = error;
152
+ for (const fn of interceptors.error.fns) {
153
+ if (fn) {
154
+ finalError = await fn(finalError, response, request, options);
155
+ }
156
+ }
157
+ finalError = finalError || {};
158
+ if (throwOnError) {
159
+ throw finalError;
160
+ }
161
+ // TODO: we probably want to return error and improve types
162
+ return responseStyle === 'data'
163
+ ? undefined
164
+ : {
165
+ error: finalError,
166
+ request,
167
+ response,
168
+ };
169
+ }
170
+ };
171
+ const makeMethodFn = (method) => (options) => request({ ...options, method });
172
+ const makeSseFn = (method) => async (options) => {
173
+ const { opts, url } = await beforeRequest(options);
174
+ return (0, serverSentEvents_gen_js_1.createSseClient)({
175
+ ...opts,
176
+ body: opts.body,
177
+ method,
178
+ onRequest: async (url, init) => {
179
+ let request = new Request(url, init);
180
+ for (const fn of interceptors.request.fns) {
181
+ if (fn) {
182
+ request = await fn(request, opts);
183
+ }
184
+ }
185
+ return request;
186
+ },
187
+ serializedBody: (0, utils_gen_js_1.getValidRequestBody)(opts),
188
+ url,
189
+ });
190
+ };
191
+ const _buildUrl = (options) => (0, utils_gen_js_2.buildUrl)({ ..._config, ...options });
192
+ return {
193
+ buildUrl: _buildUrl,
194
+ connect: makeMethodFn('CONNECT'),
195
+ delete: makeMethodFn('DELETE'),
196
+ get: makeMethodFn('GET'),
197
+ getConfig,
198
+ head: makeMethodFn('HEAD'),
199
+ interceptors,
200
+ options: makeMethodFn('OPTIONS'),
201
+ patch: makeMethodFn('PATCH'),
202
+ post: makeMethodFn('POST'),
203
+ put: makeMethodFn('PUT'),
204
+ request,
205
+ setConfig,
206
+ sse: {
207
+ connect: makeSseFn('CONNECT'),
208
+ delete: makeSseFn('DELETE'),
209
+ get: makeSseFn('GET'),
210
+ head: makeSseFn('HEAD'),
211
+ options: makeSseFn('OPTIONS'),
212
+ patch: makeSseFn('PATCH'),
213
+ post: makeSseFn('POST'),
214
+ put: makeSseFn('PUT'),
215
+ trace: makeSseFn('TRACE'),
216
+ },
217
+ trace: makeMethodFn('TRACE'),
218
+ };
219
+ };
220
+ exports.createClient = createClient;
@@ -0,0 +1,10 @@
1
+ export type { Auth } from '../core/auth.gen.js';
2
+ export type { QuerySerializerOptions } from '../core/bodySerializer.gen.js';
3
+ export { formDataBodySerializer, jsonBodySerializer, urlSearchParamsBodySerializer, } from '../core/bodySerializer.gen.js';
4
+ export { buildClientParams } from '../core/params.gen.js';
5
+ export { serializeQueryKeyValue } from '../core/queryKeySerializer.gen.js';
6
+ export type { ServerSentEventsResult } from '../core/serverSentEvents.gen.js';
7
+ export type { ClientMeta } from '../core/types.gen.js';
8
+ export { createClient } from './client.gen.js';
9
+ export type { Client, ClientOptions, Config, CreateClientConfig, Options, RequestOptions, RequestResult, ResolvedRequestOptions, ResponseStyle, TDataShape, } from './types.gen.js';
10
+ export { createConfig, mergeHeaders } from './utils.gen.js';