orita-sdk 1.0.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/dist/index.js ADDED
@@ -0,0 +1,268 @@
1
+ // src/errors.ts
2
+ var OritaError = class extends Error {
3
+ constructor(message, statusCode, response) {
4
+ super(message);
5
+ this.name = "OritaError";
6
+ this.statusCode = statusCode;
7
+ this.response = response;
8
+ const ErrCtor = Error;
9
+ if (ErrCtor.captureStackTrace) {
10
+ ErrCtor.captureStackTrace(this, this.constructor);
11
+ }
12
+ }
13
+ };
14
+ var OritaAuthError = class extends OritaError {
15
+ constructor(message = "Invalid or missing API key", response) {
16
+ super(message, 401, response);
17
+ this.name = "OritaAuthError";
18
+ }
19
+ };
20
+ var OritaNotFoundError = class extends OritaError {
21
+ constructor(message = "Resource not found", response) {
22
+ super(message, 404, response);
23
+ this.name = "OritaNotFoundError";
24
+ }
25
+ };
26
+ var OritaSlotUnavailableError = class extends OritaError {
27
+ constructor(message = "Slot is no longer available", response) {
28
+ super(message, 409, response);
29
+ this.name = "OritaSlotUnavailableError";
30
+ }
31
+ };
32
+
33
+ // src/client.ts
34
+ var DEFAULT_BASE_URL = "https://orita.online/api/v1";
35
+ var OritaClient = class {
36
+ constructor(options) {
37
+ const { apiKey, baseUrl = DEFAULT_BASE_URL } = options;
38
+ if (!apiKey.startsWith("orita_")) {
39
+ throw new OritaAuthError("API key must start with 'orita_'");
40
+ }
41
+ this.apiKey = apiKey;
42
+ this.baseUrl = baseUrl.replace(/\/$/, "");
43
+ }
44
+ // ── Private helpers ────────────────────────────────────────────────────────
45
+ buildUrl(path, params) {
46
+ const url = new URL(`${this.baseUrl}${path}`);
47
+ if (params) {
48
+ for (const [key, value] of Object.entries(params)) {
49
+ url.searchParams.set(key, value);
50
+ }
51
+ }
52
+ return url.toString();
53
+ }
54
+ async request(method, path, options = {}) {
55
+ const url = this.buildUrl(path, options.params);
56
+ const init = {
57
+ method,
58
+ headers: {
59
+ Authorization: `Bearer ${this.apiKey}`,
60
+ "Content-Type": "application/json",
61
+ Accept: "application/json"
62
+ }
63
+ };
64
+ if (options.body !== void 0) {
65
+ init.body = JSON.stringify(options.body);
66
+ }
67
+ let res;
68
+ try {
69
+ res = await fetch(url, init);
70
+ } catch (err) {
71
+ throw new OritaError(
72
+ `Network error: ${err instanceof Error ? err.message : String(err)}`
73
+ );
74
+ }
75
+ let json;
76
+ try {
77
+ json = await res.json();
78
+ } catch {
79
+ throw new OritaError(`Invalid JSON response (HTTP ${res.status})`);
80
+ }
81
+ if (res.status === 401) {
82
+ const msg = json?.error ?? "Invalid or missing API key";
83
+ throw new OritaAuthError(msg, json);
84
+ }
85
+ if (res.status === 404) {
86
+ const msg = json?.error ?? "Resource not found";
87
+ throw new OritaNotFoundError(msg, json);
88
+ }
89
+ if (res.status === 409) {
90
+ const msg = json?.error ?? "Slot is no longer available";
91
+ throw new OritaSlotUnavailableError(msg, json);
92
+ }
93
+ if (!res.ok) {
94
+ const msg = json?.error ?? `HTTP ${res.status}`;
95
+ throw new OritaError(msg, res.status, json);
96
+ }
97
+ return json;
98
+ }
99
+ // ── Public API ─────────────────────────────────────────────────────────────
100
+ /**
101
+ * List all active event types for your account.
102
+ *
103
+ * @example
104
+ * const eventTypes = await orita.getEventTypes();
105
+ * // [{ id: "evt_abc123", title: "Initial Consultation", duration: 30, ... }]
106
+ */
107
+ async getEventTypes() {
108
+ const res = await this.request("GET", "/event-types");
109
+ return res.data;
110
+ }
111
+ /**
112
+ * Get available time slots for an event type on a given date.
113
+ *
114
+ * @param eventTypeId - Event type ID from `getEventTypes()`
115
+ * @param date - Date in `YYYY-MM-DD` format
116
+ *
117
+ * @example
118
+ * const { slots } = await orita.getSlots('evt_abc123', '2026-08-01');
119
+ * // [{ label: "09:00 AM", value: "09:00" }, ...]
120
+ */
121
+ async getSlots(eventTypeId, date) {
122
+ const res = await this.request(
123
+ "GET",
124
+ "/slots",
125
+ { params: { eventTypeId, date } }
126
+ );
127
+ return res;
128
+ }
129
+ /**
130
+ * Book an appointment. Returns the created booking object.
131
+ *
132
+ * @example
133
+ * const booking = await orita.book({
134
+ * eventTypeId: 'evt_abc123',
135
+ * date: '2026-08-01',
136
+ * time: '10:00',
137
+ * clientName: 'Ana',
138
+ * clientLastname: 'López',
139
+ * clientEmail: 'ana@example.com',
140
+ * });
141
+ * console.log(booking.id); // "book_xyz789"
142
+ */
143
+ async book(params) {
144
+ const body = {
145
+ eventTypeId: params.eventTypeId,
146
+ date: params.date,
147
+ time: params.time,
148
+ clientName: params.clientName,
149
+ clientLastname: params.clientLastname,
150
+ clientEmail: params.clientEmail
151
+ };
152
+ if (params.clientPhone) body.clientPhone = params.clientPhone;
153
+ if (params.clientTimezone) body.clientTimezone = params.clientTimezone;
154
+ if (params.notes) body.notes = params.notes;
155
+ const res = await this.request("POST", "/bookings", { body });
156
+ return res.data;
157
+ }
158
+ /**
159
+ * List your bookings with optional filtering.
160
+ *
161
+ * @example
162
+ * const bookings = await orita.getBookings({ status: 'confirmed' });
163
+ */
164
+ async getBookings(params = {}) {
165
+ const query = {};
166
+ if (params.page !== void 0) query.page = String(params.page);
167
+ if (params.limit !== void 0) query.limit = String(params.limit);
168
+ if (params.status) query.status = params.status;
169
+ const res = await this.request("GET", "/bookings", { params: query });
170
+ return res.data;
171
+ }
172
+ /**
173
+ * Retrieve a single booking by ID.
174
+ *
175
+ * @example
176
+ * const booking = await orita.getBooking('book_xyz789');
177
+ * console.log(booking.status); // "confirmed"
178
+ */
179
+ async getBooking(bookingId) {
180
+ const res = await this.request("GET", `/bookings/${bookingId}`);
181
+ return res.data;
182
+ }
183
+ /**
184
+ * Cancel a booking by ID.
185
+ *
186
+ * @param bookingId - The booking ID to cancel
187
+ * @param reason - Optional cancellation reason
188
+ *
189
+ * @example
190
+ * const result = await orita.cancelBooking('book_xyz789', 'Client requested reschedule');
191
+ */
192
+ async cancelBooking(bookingId, reason) {
193
+ const body = {};
194
+ if (reason) body.reason = reason;
195
+ const res = await this.request(
196
+ "POST",
197
+ `/bookings/${bookingId}/cancel`,
198
+ { body: Object.keys(body).length ? body : void 0 }
199
+ );
200
+ return res.data;
201
+ }
202
+ /**
203
+ * Get your own Capability Manifest (your profile).
204
+ * Requires authentication.
205
+ *
206
+ * @example
207
+ * const profile = await orita.getMyProfile();
208
+ * console.log(profile.username);
209
+ */
210
+ async getMyProfile() {
211
+ const res = await this.request("GET", "/profile");
212
+ return res.data;
213
+ }
214
+ /**
215
+ * Update your profile fields.
216
+ *
217
+ * @example
218
+ * const updated = await orita.updateProfile({ bio: 'AI-native therapist' });
219
+ */
220
+ async updateProfile(fields) {
221
+ const res = await this.request("PUT", "/profile", { body: fields });
222
+ return res.data;
223
+ }
224
+ /**
225
+ * Fetch the **public** Capability Manifest for any professional by username.
226
+ * No API key is needed for this request.
227
+ *
228
+ * @param username - The professional's username (e.g. "dra-martinez")
229
+ *
230
+ * @example
231
+ * const manifest = await orita.getProfile('dra-martinez');
232
+ * console.log(manifest.eventTypes);
233
+ */
234
+ async getProfile(username) {
235
+ const url = this.buildUrl("/profile", { username });
236
+ let res;
237
+ try {
238
+ res = await fetch(url, {
239
+ headers: { Accept: "application/json" }
240
+ });
241
+ } catch (err) {
242
+ throw new OritaError(
243
+ `Network error: ${err instanceof Error ? err.message : String(err)}`
244
+ );
245
+ }
246
+ let json;
247
+ try {
248
+ json = await res.json();
249
+ } catch {
250
+ throw new OritaError(`Invalid JSON response (HTTP ${res.status})`);
251
+ }
252
+ if (res.status === 404) {
253
+ throw new OritaNotFoundError(`Professional '${username}' not found`, json);
254
+ }
255
+ if (!res.ok) {
256
+ const msg = json?.error ?? `HTTP ${res.status}`;
257
+ throw new OritaError(msg, res.status, json);
258
+ }
259
+ return json.data;
260
+ }
261
+ };
262
+ export {
263
+ OritaAuthError,
264
+ OritaClient,
265
+ OritaError,
266
+ OritaNotFoundError,
267
+ OritaSlotUnavailableError
268
+ };
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "orita-sdk",
3
+ "version": "1.0.0",
4
+ "description": "The scheduling infrastructure for AI agents — official Node.js / TypeScript SDK",
5
+ "keywords": [
6
+ "orita",
7
+ "scheduling",
8
+ "ai",
9
+ "agents",
10
+ "booking",
11
+ "appointments",
12
+ "sdk"
13
+ ],
14
+ "homepage": "https://orita.online",
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/Alkilo-do/orita-node.git"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/Alkilo-do/orita-node/issues"
21
+ },
22
+ "license": "MIT",
23
+ "author": "Alkilo-do",
24
+ "type": "module",
25
+ "main": "./dist/index.cjs",
26
+ "module": "./dist/index.js",
27
+ "types": "./dist/index.d.ts",
28
+ "exports": {
29
+ ".": {
30
+ "import": {
31
+ "types": "./dist/index.d.ts",
32
+ "default": "./dist/index.js"
33
+ },
34
+ "require": {
35
+ "types": "./dist/index.d.cts",
36
+ "default": "./dist/index.cjs"
37
+ }
38
+ }
39
+ },
40
+ "files": [
41
+ "dist",
42
+ "README.md",
43
+ "LICENSE"
44
+ ],
45
+ "scripts": {
46
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
47
+ "typecheck": "tsc --noEmit",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "devDependencies": {
51
+ "tsup": "^8.5.1",
52
+ "typescript": "^5.9.3"
53
+ },
54
+ "engines": {
55
+ "node": ">=18.0.0"
56
+ }
57
+ }