@microboxlabs/miot-calendar-client 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.
package/README.md ADDED
@@ -0,0 +1,92 @@
1
+ # @microboxlabs/miot-calendar-client
2
+
3
+ TypeScript client for the ModularIoT Calendar API. Zero dependencies — uses the native `fetch` API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @microboxlabs/miot-calendar-client
9
+ ```
10
+
11
+ ## Quick Start
12
+
13
+ ```ts
14
+ import { createMiotCalendarClient } from "@microboxlabs/miot-calendar-client";
15
+
16
+ const client = createMiotCalendarClient({
17
+ baseUrl: "https://your-api-host.com",
18
+ });
19
+
20
+ // List calendars
21
+ const calendars = await client.calendars.list();
22
+
23
+ // Create a booking
24
+ const booking = await client.bookings.create({
25
+ calendarId: "cal-123",
26
+ resource: { id: "truck-42", type: "vehicle", label: "Truck 42" },
27
+ slot: { date: "2026-03-01", hour: 10, minutes: 0 },
28
+ });
29
+ ```
30
+
31
+ ## Configuration
32
+
33
+ ```ts
34
+ createMiotCalendarClient({
35
+ baseUrl: "https://your-api-host.com", // Required
36
+ headers: { Authorization: "Bearer ..." }, // Optional default headers
37
+ fetch: customFetch, // Optional fetch implementation
38
+ });
39
+ ```
40
+
41
+ ## API Reference
42
+
43
+ ### `client.calendars`
44
+
45
+ | Method | Description |
46
+ |---|---|
47
+ | `list(params?)` | List calendars. Filter by `active`. |
48
+ | `get(id)` | Get a calendar by ID. |
49
+ | `create(body)` | Create a calendar. |
50
+ | `update(id, body)` | Update a calendar. |
51
+ | `deactivate(id)` | Deactivate a calendar. |
52
+ | `listTimeWindows(calendarId)` | List time windows for a calendar. |
53
+ | `createTimeWindow(calendarId, body)` | Create a time window. |
54
+ | `updateTimeWindow(calendarId, timeWindowId, body)` | Update a time window. |
55
+
56
+ ### `client.bookings`
57
+
58
+ | Method | Description |
59
+ |---|---|
60
+ | `list(params?)` | List bookings. Filter by `calendarId`, `startDate`, `endDate`. |
61
+ | `get(id)` | Get a booking by ID. |
62
+ | `create(body, options?)` | Create a booking. Pass `{ userId }` to set `X-User-Id` header. |
63
+ | `cancel(id)` | Cancel a booking. |
64
+ | `listByResource(resourceId)` | List bookings for a specific resource. |
65
+
66
+ ### `client.slots`
67
+
68
+ | Method | Description |
69
+ |---|---|
70
+ | `list(params)` | List slots. Filter by `calendarId`, `available`, `startDate`, `endDate`. |
71
+ | `get(id)` | Get a slot by ID. |
72
+ | `generate(body)` | Generate slots for a date range. |
73
+ | `updateStatus(id, body)` | Update slot status (`OPEN`, `FULL`, `CLOSED`). |
74
+
75
+ ## Error Handling
76
+
77
+ ```ts
78
+ import { MiotCalendarApiError } from "@microboxlabs/miot-calendar-client";
79
+
80
+ try {
81
+ await client.bookings.get("non-existent");
82
+ } catch (error) {
83
+ if (error instanceof MiotCalendarApiError) {
84
+ console.log(error.status); // HTTP status code
85
+ console.log(error.body); // ErrorResponse object or raw string
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## License
91
+
92
+ Private — Microbox Labs.
@@ -0,0 +1,162 @@
1
+ type SlotStatus = "OPEN" | "FULL" | "CLOSED";
2
+ interface ResourceData {
3
+ id: string;
4
+ type?: string;
5
+ label?: string;
6
+ data?: Record<string, unknown>;
7
+ }
8
+ interface SlotData {
9
+ date: string;
10
+ hour: number;
11
+ minutes: number;
12
+ }
13
+ interface BookingRequest {
14
+ calendarId: string;
15
+ resource: ResourceData;
16
+ slot: SlotData;
17
+ }
18
+ interface BookingResponse {
19
+ id: string;
20
+ calendarId: string;
21
+ resource: ResourceData;
22
+ slot: SlotData;
23
+ createdAt: string;
24
+ createdBy?: string;
25
+ }
26
+ interface BookingListResponse {
27
+ data: BookingResponse[];
28
+ total: number;
29
+ }
30
+ interface CalendarRequest {
31
+ code: string;
32
+ name: string;
33
+ description?: string;
34
+ timezone?: string;
35
+ active?: boolean;
36
+ }
37
+ interface CalendarResponse {
38
+ id: string;
39
+ code: string;
40
+ name: string;
41
+ description?: string;
42
+ timezone: string;
43
+ active: boolean;
44
+ createdAt: string;
45
+ updatedAt: string;
46
+ }
47
+ interface TimeWindowRequest {
48
+ name: string;
49
+ startHour: number;
50
+ endHour: number;
51
+ validFrom: string;
52
+ slotDurationMinutes?: number;
53
+ capacityPerSlot?: number;
54
+ daysOfWeek?: string;
55
+ validTo?: string;
56
+ active?: boolean;
57
+ }
58
+ interface TimeWindowResponse {
59
+ id: string;
60
+ calendarId: string;
61
+ name: string;
62
+ startHour: number;
63
+ endHour: number;
64
+ slotDurationMinutes: number;
65
+ capacityPerSlot: number;
66
+ daysOfWeek: string;
67
+ validFrom: string;
68
+ validTo?: string;
69
+ active: boolean;
70
+ createdAt: string;
71
+ updatedAt: string;
72
+ }
73
+ interface GenerateSlotsRequest {
74
+ calendarId: string;
75
+ startDate: string;
76
+ endDate: string;
77
+ }
78
+ interface GenerateSlotsResponse {
79
+ slotsCreated: number;
80
+ slotsSkipped: number;
81
+ message: string;
82
+ }
83
+ interface SlotResponse {
84
+ id: string;
85
+ calendarId: string;
86
+ timeWindowId?: string;
87
+ slotDate: string;
88
+ slotHour: number;
89
+ slotMinutes: number;
90
+ capacity: number;
91
+ currentOccupancy: number;
92
+ availableCapacity: number;
93
+ status: SlotStatus;
94
+ createdAt: string;
95
+ updatedAt: string;
96
+ }
97
+ interface SlotListResponse {
98
+ data: SlotResponse[];
99
+ total: number;
100
+ }
101
+ interface UpdateSlotStatusRequest {
102
+ status: SlotStatus;
103
+ }
104
+ interface ErrorResponse {
105
+ error: string;
106
+ message: string;
107
+ status: number;
108
+ timestamp: string;
109
+ }
110
+ interface ClientConfig {
111
+ baseUrl: string;
112
+ headers?: Record<string, string>;
113
+ fetch?: typeof fetch;
114
+ }
115
+
116
+ declare function createMiotCalendarClient(config: ClientConfig): {
117
+ bookings: {
118
+ list(params?: {
119
+ calendarId?: string;
120
+ startDate?: string;
121
+ endDate?: string;
122
+ }): Promise<BookingListResponse>;
123
+ get(id: string): Promise<BookingResponse>;
124
+ create(body: BookingRequest, options?: {
125
+ userId?: string;
126
+ }): Promise<BookingResponse>;
127
+ cancel(id: string): Promise<void>;
128
+ listByResource(resourceId: string): Promise<BookingListResponse>;
129
+ };
130
+ calendars: {
131
+ list(params?: {
132
+ active?: boolean;
133
+ }): Promise<CalendarResponse[]>;
134
+ get(id: string): Promise<CalendarResponse>;
135
+ create(body: CalendarRequest): Promise<CalendarResponse>;
136
+ update(id: string, body: CalendarRequest): Promise<CalendarResponse>;
137
+ deactivate(id: string): Promise<void>;
138
+ listTimeWindows(calendarId: string): Promise<TimeWindowResponse[]>;
139
+ createTimeWindow(calendarId: string, body: TimeWindowRequest): Promise<TimeWindowResponse>;
140
+ updateTimeWindow(calendarId: string, timeWindowId: string, body: TimeWindowRequest): Promise<TimeWindowResponse>;
141
+ };
142
+ slots: {
143
+ list(params: {
144
+ calendarId: string;
145
+ available?: boolean;
146
+ startDate?: string;
147
+ endDate?: string;
148
+ }): Promise<SlotListResponse>;
149
+ get(id: string): Promise<SlotResponse>;
150
+ generate(body: GenerateSlotsRequest): Promise<GenerateSlotsResponse>;
151
+ updateStatus(id: string, body: UpdateSlotStatusRequest): Promise<SlotResponse>;
152
+ };
153
+ };
154
+
155
+ declare class MiotCalendarApiError extends Error {
156
+ readonly status: number;
157
+ readonly body: ErrorResponse | string;
158
+ name: "MiotCalendarApiError";
159
+ constructor(status: number, body: ErrorResponse | string);
160
+ }
161
+
162
+ export { type BookingListResponse, type BookingRequest, type BookingResponse, type CalendarRequest, type CalendarResponse, type ClientConfig, type ErrorResponse, type GenerateSlotsRequest, type GenerateSlotsResponse, MiotCalendarApiError, type ResourceData, type SlotData, type SlotListResponse, type SlotResponse, type SlotStatus, type TimeWindowRequest, type TimeWindowResponse, type UpdateSlotStatusRequest, createMiotCalendarClient };
package/dist/index.js ADDED
@@ -0,0 +1,139 @@
1
+ // src/errors.ts
2
+ var MiotCalendarApiError = class extends Error {
3
+ constructor(status, body) {
4
+ super(typeof body === "string" ? body : body.message);
5
+ this.status = status;
6
+ this.body = body;
7
+ }
8
+ name = "MiotCalendarApiError";
9
+ };
10
+
11
+ // src/resources/bookings.ts
12
+ var BASE = "/api/v1/miot-calendar/bookings";
13
+ function createBookingsApi(fetcher) {
14
+ return {
15
+ list(params) {
16
+ return fetcher("GET", BASE, { query: params });
17
+ },
18
+ get(id) {
19
+ return fetcher("GET", `${BASE}/${id}`);
20
+ },
21
+ create(body, options) {
22
+ return fetcher("POST", BASE, {
23
+ body,
24
+ headers: options?.userId ? { "X-User-Id": options.userId } : void 0
25
+ });
26
+ },
27
+ cancel(id) {
28
+ return fetcher("DELETE", `${BASE}/${id}`);
29
+ },
30
+ listByResource(resourceId) {
31
+ return fetcher("GET", `${BASE}/resource/${resourceId}`);
32
+ }
33
+ };
34
+ }
35
+
36
+ // src/resources/calendars.ts
37
+ var BASE2 = "/api/v1/miot-calendar/calendars";
38
+ function createCalendarsApi(fetcher) {
39
+ return {
40
+ list(params) {
41
+ return fetcher("GET", BASE2, { query: params });
42
+ },
43
+ get(id) {
44
+ return fetcher("GET", `${BASE2}/${id}`);
45
+ },
46
+ create(body) {
47
+ return fetcher("POST", BASE2, { body });
48
+ },
49
+ update(id, body) {
50
+ return fetcher("PUT", `${BASE2}/${id}`, { body });
51
+ },
52
+ deactivate(id) {
53
+ return fetcher("DELETE", `${BASE2}/${id}`);
54
+ },
55
+ listTimeWindows(calendarId) {
56
+ return fetcher("GET", `${BASE2}/${calendarId}/time-windows`);
57
+ },
58
+ createTimeWindow(calendarId, body) {
59
+ return fetcher("POST", `${BASE2}/${calendarId}/time-windows`, { body });
60
+ },
61
+ updateTimeWindow(calendarId, timeWindowId, body) {
62
+ return fetcher("PUT", `${BASE2}/${calendarId}/time-windows/${timeWindowId}`, {
63
+ body
64
+ });
65
+ }
66
+ };
67
+ }
68
+
69
+ // src/resources/slots.ts
70
+ var BASE3 = "/api/v1/miot-calendar/slots";
71
+ function createSlotsApi(fetcher) {
72
+ return {
73
+ list(params) {
74
+ return fetcher("GET", BASE3, { query: params });
75
+ },
76
+ get(id) {
77
+ return fetcher("GET", `${BASE3}/${id}`);
78
+ },
79
+ generate(body) {
80
+ return fetcher("POST", `${BASE3}/generate`, { body });
81
+ },
82
+ updateStatus(id, body) {
83
+ return fetcher("PATCH", `${BASE3}/${id}/status`, { body });
84
+ }
85
+ };
86
+ }
87
+
88
+ // src/client.ts
89
+ function buildUrl(baseUrl, path, query) {
90
+ const url = new URL(path, baseUrl);
91
+ if (query) {
92
+ for (const [key, value] of Object.entries(query)) {
93
+ if (value !== void 0) {
94
+ url.searchParams.set(key, String(value));
95
+ }
96
+ }
97
+ }
98
+ return url.toString();
99
+ }
100
+ function createMiotCalendarClient(config) {
101
+ const fetchFn = config.fetch ?? globalThis.fetch;
102
+ const fetcher = async (method, path, options) => {
103
+ const url = buildUrl(config.baseUrl, path, options?.query);
104
+ const headers = {
105
+ ...config.headers,
106
+ ...options?.headers
107
+ };
108
+ if (options?.body !== void 0) {
109
+ headers["Content-Type"] = "application/json";
110
+ }
111
+ const response = await fetchFn(url, {
112
+ method,
113
+ headers,
114
+ body: options?.body === void 0 ? void 0 : JSON.stringify(options.body)
115
+ });
116
+ if (!response.ok) {
117
+ let body;
118
+ try {
119
+ body = await response.json();
120
+ } catch {
121
+ body = await response.text();
122
+ }
123
+ throw new MiotCalendarApiError(response.status, body);
124
+ }
125
+ if (response.status === 204) {
126
+ return void 0;
127
+ }
128
+ return response.json();
129
+ };
130
+ return {
131
+ bookings: createBookingsApi(fetcher),
132
+ calendars: createCalendarsApi(fetcher),
133
+ slots: createSlotsApi(fetcher)
134
+ };
135
+ }
136
+ export {
137
+ MiotCalendarApiError,
138
+ createMiotCalendarClient
139
+ };
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@microboxlabs/miot-calendar-client",
3
+ "version": "0.1.1",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "types": "./dist/index.d.ts",
8
+ "import": "./dist/index.js"
9
+ }
10
+ },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsup",
16
+ "test": "vitest run",
17
+ "lint": "eslint .",
18
+ "check-types": "tsc --noEmit"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "devDependencies": {
24
+ "@repo/eslint-config": "*",
25
+ "@repo/typescript-config": "*",
26
+ "tsup": "^8.0.0",
27
+ "typescript": "5.8.2",
28
+ "vitest": "^4.0.18"
29
+ }
30
+ }