@lagapp/sdk 0.1.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.
Files changed (124) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +21 -0
  3. package/README.md +207 -0
  4. package/dist/client.d.ts +36 -0
  5. package/dist/client.d.ts.map +1 -0
  6. package/dist/client.js +45 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/errors.d.ts +67 -0
  9. package/dist/errors.d.ts.map +1 -0
  10. package/dist/errors.js +103 -0
  11. package/dist/errors.js.map +1 -0
  12. package/dist/http.d.ts +61 -0
  13. package/dist/http.d.ts.map +1 -0
  14. package/dist/http.js +187 -0
  15. package/dist/http.js.map +1 -0
  16. package/dist/index.d.ts +23 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +24 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/pagination.d.ts +39 -0
  21. package/dist/pagination.d.ts.map +1 -0
  22. package/dist/pagination.js +26 -0
  23. package/dist/pagination.js.map +1 -0
  24. package/dist/resources/dms.d.ts +39 -0
  25. package/dist/resources/dms.d.ts.map +1 -0
  26. package/dist/resources/dms.js +76 -0
  27. package/dist/resources/dms.js.map +1 -0
  28. package/dist/resources/event-guests.d.ts +30 -0
  29. package/dist/resources/event-guests.d.ts.map +1 -0
  30. package/dist/resources/event-guests.js +39 -0
  31. package/dist/resources/event-guests.js.map +1 -0
  32. package/dist/resources/event-templates.d.ts +24 -0
  33. package/dist/resources/event-templates.d.ts.map +1 -0
  34. package/dist/resources/event-templates.js +42 -0
  35. package/dist/resources/event-templates.js.map +1 -0
  36. package/dist/resources/events.d.ts +38 -0
  37. package/dist/resources/events.d.ts.map +1 -0
  38. package/dist/resources/events.js +73 -0
  39. package/dist/resources/events.js.map +1 -0
  40. package/dist/resources/friends.d.ts +32 -0
  41. package/dist/resources/friends.d.ts.map +1 -0
  42. package/dist/resources/friends.js +65 -0
  43. package/dist/resources/friends.js.map +1 -0
  44. package/dist/resources/images.d.ts +41 -0
  45. package/dist/resources/images.d.ts.map +1 -0
  46. package/dist/resources/images.js +106 -0
  47. package/dist/resources/images.js.map +1 -0
  48. package/dist/resources/room-messages.d.ts +24 -0
  49. package/dist/resources/room-messages.d.ts.map +1 -0
  50. package/dist/resources/room-messages.js +49 -0
  51. package/dist/resources/room-messages.js.map +1 -0
  52. package/dist/resources/rooms.d.ts +24 -0
  53. package/dist/resources/rooms.d.ts.map +1 -0
  54. package/dist/resources/rooms.js +42 -0
  55. package/dist/resources/rooms.js.map +1 -0
  56. package/dist/resources/server-invites.d.ts +29 -0
  57. package/dist/resources/server-invites.d.ts.map +1 -0
  58. package/dist/resources/server-invites.js +59 -0
  59. package/dist/resources/server-invites.js.map +1 -0
  60. package/dist/resources/server-members.d.ts +27 -0
  61. package/dist/resources/server-members.d.ts.map +1 -0
  62. package/dist/resources/server-members.js +64 -0
  63. package/dist/resources/server-members.js.map +1 -0
  64. package/dist/resources/server-roles.d.ts +23 -0
  65. package/dist/resources/server-roles.d.ts.map +1 -0
  66. package/dist/resources/server-roles.js +51 -0
  67. package/dist/resources/server-roles.js.map +1 -0
  68. package/dist/resources/servers.d.ts +37 -0
  69. package/dist/resources/servers.d.ts.map +1 -0
  70. package/dist/resources/servers.js +72 -0
  71. package/dist/resources/servers.js.map +1 -0
  72. package/dist/resources/system.d.ts +20 -0
  73. package/dist/resources/system.d.ts.map +1 -0
  74. package/dist/resources/system.js +27 -0
  75. package/dist/resources/system.js.map +1 -0
  76. package/dist/resources/users.d.ts +48 -0
  77. package/dist/resources/users.d.ts.map +1 -0
  78. package/dist/resources/users.js +103 -0
  79. package/dist/resources/users.js.map +1 -0
  80. package/dist/types/common.d.ts +27 -0
  81. package/dist/types/common.d.ts.map +1 -0
  82. package/dist/types/common.js +23 -0
  83. package/dist/types/common.js.map +1 -0
  84. package/dist/types/dm.d.ts +33 -0
  85. package/dist/types/dm.d.ts.map +1 -0
  86. package/dist/types/dm.js +2 -0
  87. package/dist/types/dm.js.map +1 -0
  88. package/dist/types/event.d.ts +101 -0
  89. package/dist/types/event.d.ts.map +1 -0
  90. package/dist/types/event.js +2 -0
  91. package/dist/types/event.js.map +1 -0
  92. package/dist/types/friend.d.ts +51 -0
  93. package/dist/types/friend.d.ts.map +1 -0
  94. package/dist/types/friend.js +2 -0
  95. package/dist/types/friend.js.map +1 -0
  96. package/dist/types/image.d.ts +33 -0
  97. package/dist/types/image.d.ts.map +1 -0
  98. package/dist/types/image.js +3 -0
  99. package/dist/types/image.js.map +1 -0
  100. package/dist/types/index.d.ts +10 -0
  101. package/dist/types/index.d.ts.map +1 -0
  102. package/dist/types/index.js +10 -0
  103. package/dist/types/index.js.map +1 -0
  104. package/dist/types/room.d.ts +98 -0
  105. package/dist/types/room.d.ts.map +1 -0
  106. package/dist/types/room.js +2 -0
  107. package/dist/types/room.js.map +1 -0
  108. package/dist/types/server.d.ts +134 -0
  109. package/dist/types/server.d.ts.map +1 -0
  110. package/dist/types/server.js +2 -0
  111. package/dist/types/server.js.map +1 -0
  112. package/dist/types/system.d.ts +14 -0
  113. package/dist/types/system.d.ts.map +1 -0
  114. package/dist/types/system.js +2 -0
  115. package/dist/types/system.js.map +1 -0
  116. package/dist/types/user.d.ts +61 -0
  117. package/dist/types/user.d.ts.map +1 -0
  118. package/dist/types/user.js +2 -0
  119. package/dist/types/user.js.map +1 -0
  120. package/dist/version.d.ts +5 -0
  121. package/dist/version.d.ts.map +1 -0
  122. package/dist/version.js +5 -0
  123. package/dist/version.js.map +1 -0
  124. package/package.json +54 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,17 @@
1
+ # Changelog
2
+
3
+ All notable changes to `@lagapp/sdk` are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2026-04-10
9
+
10
+ ### Added
11
+ - Initial release covering the public REST surface of the Lag API.
12
+ - `LagClient` with resources: `system`, `users`, `friends`, `dms`, `servers`, `events`, `images`.
13
+ - Sub-resources: server `invites`, `members`, `roles`, `rooms`, room `messages`, event `guests`, event `templates`.
14
+ - Cursor-based pagination helpers with `AsyncIterable` page walking.
15
+ - Typed error hierarchy: `LagApiError`, `LagAuthError`, `LagPermissionError`, `LagNotFoundError`, `LagConflictError`, `LagRateLimitError`, `LagServerError`, `LagConnectionError`.
16
+ - Automatic retries with exponential backoff and `Retry-After` support.
17
+ - Multipart image upload from path, `Buffer`, `Uint8Array`, `Blob`, or `ReadableStream`.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lag
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 ADDED
@@ -0,0 +1,207 @@
1
+ # @lagapp/sdk
2
+
3
+ The official TypeScript / Node SDK for the [Lag](https://trylag.com) API.
4
+
5
+ `@lagapp/sdk` is a small, hand-written REST client for the public Lag API. It
6
+ covers users, friends, DMs, servers, rooms, room messages, events, and image
7
+ uploads. It does **not** include the WebSocket protocol or the voice client -
8
+ those are out of scope for this package.
9
+
10
+ - Zero runtime dependencies (uses the global `fetch` from Node 18+).
11
+ - ESM, ships `.d.ts` declarations.
12
+ - Typed error hierarchy with automatic retries on transient failures.
13
+ - Cursor pagination with an async-iterable helper for walking pages.
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install @lagapp/sdk
19
+ # or
20
+ pnpm add @lagapp/sdk
21
+ # or
22
+ yarn add @lagapp/sdk
23
+ ```
24
+
25
+ ## Quickstart
26
+
27
+ ```ts
28
+ import { LagClient } from '@lagapp/sdk';
29
+
30
+ const client = new LagClient({
31
+ token: process.env.LAG_TOKEN!, // a Personal Access Token (lag_pat_*) or Supabase JWT
32
+ });
33
+
34
+ const me = await client.users.me();
35
+ console.log(`Hello, ${me.displayName ?? me.username}`);
36
+
37
+ const servers = await client.servers.list();
38
+ for (const server of servers) {
39
+ console.log(`- ${server.name} (${server.memberCount} members)`);
40
+ }
41
+ ```
42
+
43
+ ## Authentication
44
+
45
+ The SDK accepts any Bearer token the API would accept:
46
+
47
+ - A **Personal Access Token** (`lag_pat_*`) - the recommended option for
48
+ scripts, bots, and CI. Create one in the Lag web app under settings, or via
49
+ the `lag` CLI with `lag auth login`.
50
+ - A **Supabase JWT** - useful when you already have an authenticated session,
51
+ e.g. inside a SvelteKit server load function.
52
+
53
+ The token is sent as `Authorization: Bearer <token>` on every request. The SDK
54
+ does not implement OAuth flows, refresh tokens, or browser-based login - if
55
+ you need any of those, obtain a token elsewhere and pass it in.
56
+
57
+ ## Configuration
58
+
59
+ ```ts
60
+ const client = new LagClient({
61
+ token: 'lag_pat_...',
62
+ baseUrl: 'https://api.trylag.com', // default
63
+ timeoutMs: 30_000, // default
64
+ maxRetries: 2, // default
65
+ userAgent: 'my-app/1.0', // optional override
66
+ fetch: customFetch, // optional fetch implementation
67
+ });
68
+ ```
69
+
70
+ `maxRetries` controls how many times the client will retry on a transient
71
+ failure (5xx, 429, network error). Backoff is exponential with jitter, capped
72
+ at ~8s. The server's `Retry-After` header is honored on 429.
73
+
74
+ ## Resources
75
+
76
+ Every resource hangs off the `LagClient` instance:
77
+
78
+ | Property | What it covers |
79
+ |---|---|
80
+ | `client.system` | `/health`, `/version`, `/system-status`, `/config` |
81
+ | `client.users` | `/users/me`, `/users/me/avatar`, `/users/:id`, `/users/search`, `/users/check-username`, Steam helpers |
82
+ | `client.friends` | List, requests, send/accept/decline, remove, block |
83
+ | `client.dms` | Conversations + messages with cursor pagination |
84
+ | `client.servers` | Servers CRUD, icon upload, leave |
85
+ | `client.servers.invites` | Create / list / revoke / preview / join |
86
+ | `client.servers.members` | Kick, ban, mute (and the corresponding lists) |
87
+ | `client.servers.roles` | Role CRUD and assignment |
88
+ | `client.servers.rooms` | Voice rooms |
89
+ | `client.servers.rooms.messages` | Room chat: list/send/edit/delete with cursor pagination |
90
+ | `client.events` | Server events: list/create/get/update/cancel/RSVP |
91
+ | `client.events.guests` | Host-side guest moderation |
92
+ | `client.events.templates` | Recurring event templates |
93
+ | `client.images` | Multipart upload, metadata, status, delete |
94
+
95
+ ### Pagination
96
+
97
+ DM and room message endpoints return:
98
+
99
+ ```ts
100
+ { messages: [...], hasMore: boolean, nextCursor: string | null }
101
+ ```
102
+
103
+ You can call them by hand:
104
+
105
+ ```ts
106
+ let cursor: string | undefined;
107
+ do {
108
+ const page = await client.dms.listMessages(convId, { limit: 50, cursor });
109
+ for (const msg of page.messages) handle(msg);
110
+ cursor = page.nextCursor ?? undefined;
111
+ } while (cursor);
112
+ ```
113
+
114
+ Or async-iterate them with the built-in walker:
115
+
116
+ ```ts
117
+ for await (const page of client.dms.iterMessages(convId, { limit: 50 })) {
118
+ for (const msg of page.items) handle(msg);
119
+ }
120
+ ```
121
+
122
+ The same pattern works for room messages via
123
+ `client.servers.rooms.messages.iter(serverId, roomId)`.
124
+
125
+ ### Image upload
126
+
127
+ ```ts
128
+ import { readFileSync } from 'node:fs';
129
+ import { LagClient } from '@lagapp/sdk';
130
+
131
+ const client = new LagClient({ token: process.env.LAG_TOKEN! });
132
+
133
+ // From a file path:
134
+ await client.images.upload('./avatar.png', { purpose: 'avatar' });
135
+
136
+ // From raw bytes:
137
+ const bytes = readFileSync('./avatar.png');
138
+ await client.images.upload(bytes, {
139
+ purpose: 'avatar',
140
+ filename: 'avatar.png',
141
+ contentType: 'image/png',
142
+ });
143
+
144
+ // From a Blob (works in browsers and Node 18+):
145
+ const blob = new Blob([bytes], { type: 'image/png' });
146
+ await client.images.upload(blob, { purpose: 'general' });
147
+ ```
148
+
149
+ The SDK builds the multipart request itself; you do not need to construct
150
+ `FormData`. Maximum upload size is 25 MiB.
151
+
152
+ ## Error handling
153
+
154
+ Every non-2xx response becomes a typed error you can match with `instanceof`:
155
+
156
+ ```ts
157
+ import {
158
+ LagApiError,
159
+ LagAuthError,
160
+ LagPermissionError,
161
+ LagNotFoundError,
162
+ LagConflictError,
163
+ LagRateLimitError,
164
+ LagServerError,
165
+ LagConnectionError,
166
+ } from '@lagapp/sdk';
167
+
168
+ try {
169
+ await client.servers.get('does-not-exist');
170
+ } catch (err) {
171
+ if (err instanceof LagNotFoundError) {
172
+ // 404
173
+ } else if (err instanceof LagRateLimitError) {
174
+ // 429 - err.retryAfterSeconds may contain the server's hint
175
+ } else if (err instanceof LagApiError) {
176
+ // any other API error
177
+ console.error(err.status, err.message, err.body);
178
+ } else {
179
+ throw err;
180
+ }
181
+ }
182
+ ```
183
+
184
+ Network failures (DNS, refused connection, timeouts before any response)
185
+ throw `LagConnectionError`. Everything else is a subclass of `LagApiError`.
186
+
187
+ ## Local development
188
+
189
+ ```bash
190
+ npm install
191
+ npm run build # tsc -> dist/
192
+ npm run typecheck
193
+ npm test # node --test via tsx
194
+ ```
195
+
196
+ Tests run against an in-process HTTP server (`test/helpers/mock-server.ts`)
197
+ so no real Lag API is required. Each resource has its own test file.
198
+
199
+ ## Related
200
+
201
+ - The Lag API itself - source under `product/apps/api/` in the Lag monorepo.
202
+ - `lag-sdk` for Python (sibling package in `sdks/python/`).
203
+ - The `lag` CLI - `cli/` in the Lag monorepo, MIT licensed.
204
+
205
+ ## License
206
+
207
+ MIT. See [`LICENSE`](./LICENSE).
@@ -0,0 +1,36 @@
1
+ import { type LagClientOptions } from './http.js';
2
+ import { SystemResource } from './resources/system.js';
3
+ import { UsersResource } from './resources/users.js';
4
+ import { FriendsResource } from './resources/friends.js';
5
+ import { DmsResource } from './resources/dms.js';
6
+ import { ServersResource } from './resources/servers.js';
7
+ import { EventsResource } from './resources/events.js';
8
+ import { ImagesResource } from './resources/images.js';
9
+ /**
10
+ * The Lag SDK entry point.
11
+ *
12
+ * Construct it with a bearer token (a Personal Access Token, format
13
+ * `lag_pat_*`, or a Supabase JWT) and access resources off the instance:
14
+ *
15
+ * ```ts
16
+ * import { LagClient } from '@lagapp/sdk';
17
+ *
18
+ * const client = new LagClient({ token: process.env.LAG_TOKEN! });
19
+ * const me = await client.users.me();
20
+ * const servers = await client.servers.list();
21
+ * ```
22
+ *
23
+ * The client is stateless beyond the underlying fetch implementation, so a
24
+ * single instance is safe to share across requests / workers.
25
+ */
26
+ export declare class LagClient {
27
+ readonly system: SystemResource;
28
+ readonly users: UsersResource;
29
+ readonly friends: FriendsResource;
30
+ readonly dms: DmsResource;
31
+ readonly servers: ServersResource;
32
+ readonly events: EventsResource;
33
+ readonly images: ImagesResource;
34
+ constructor(options: LagClientOptions);
35
+ }
36
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,qBAAa,SAAS;IACpB,SAAgB,MAAM,EAAE,cAAc,CAAC;IACvC,SAAgB,KAAK,EAAE,aAAa,CAAC;IACrC,SAAgB,OAAO,EAAE,eAAe,CAAC;IACzC,SAAgB,GAAG,EAAE,WAAW,CAAC;IACjC,SAAgB,OAAO,EAAE,eAAe,CAAC;IACzC,SAAgB,MAAM,EAAE,cAAc,CAAC;IACvC,SAAgB,MAAM,EAAE,cAAc,CAAC;gBAE3B,OAAO,EAAE,gBAAgB;CAUtC"}
package/dist/client.js ADDED
@@ -0,0 +1,45 @@
1
+ import { HttpClient } from './http.js';
2
+ import { SystemResource } from './resources/system.js';
3
+ import { UsersResource } from './resources/users.js';
4
+ import { FriendsResource } from './resources/friends.js';
5
+ import { DmsResource } from './resources/dms.js';
6
+ import { ServersResource } from './resources/servers.js';
7
+ import { EventsResource } from './resources/events.js';
8
+ import { ImagesResource } from './resources/images.js';
9
+ /**
10
+ * The Lag SDK entry point.
11
+ *
12
+ * Construct it with a bearer token (a Personal Access Token, format
13
+ * `lag_pat_*`, or a Supabase JWT) and access resources off the instance:
14
+ *
15
+ * ```ts
16
+ * import { LagClient } from '@lagapp/sdk';
17
+ *
18
+ * const client = new LagClient({ token: process.env.LAG_TOKEN! });
19
+ * const me = await client.users.me();
20
+ * const servers = await client.servers.list();
21
+ * ```
22
+ *
23
+ * The client is stateless beyond the underlying fetch implementation, so a
24
+ * single instance is safe to share across requests / workers.
25
+ */
26
+ export class LagClient {
27
+ system;
28
+ users;
29
+ friends;
30
+ dms;
31
+ servers;
32
+ events;
33
+ images;
34
+ constructor(options) {
35
+ const http = new HttpClient(options);
36
+ this.system = new SystemResource(http);
37
+ this.users = new UsersResource(http);
38
+ this.friends = new FriendsResource(http);
39
+ this.dms = new DmsResource(http);
40
+ this.servers = new ServersResource(http);
41
+ this.events = new EventsResource(http);
42
+ this.images = new ImagesResource(http);
43
+ }
44
+ }
45
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAyB,MAAM,WAAW,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,OAAO,SAAS;IACJ,MAAM,CAAiB;IACvB,KAAK,CAAgB;IACrB,OAAO,CAAkB;IACzB,GAAG,CAAc;IACjB,OAAO,CAAkB;IACzB,MAAM,CAAiB;IACvB,MAAM,CAAiB;IAEvC,YAAY,OAAyB;QACnC,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,KAAK,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACrC,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,GAAG,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,OAAO,GAAG,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC;QACzC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC;IACzC,CAAC;CACF"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Error envelope returned by the Lag API.
3
+ *
4
+ * Every API error response has the shape `{ error, message, statusCode }`.
5
+ * The SDK parses these into a typed exception hierarchy so consumers can
6
+ * `instanceof`-match the failure cases they care about.
7
+ */
8
+ export interface LagApiErrorBody {
9
+ error: string;
10
+ message: string;
11
+ statusCode: number;
12
+ }
13
+ /**
14
+ * Base class for every error thrown by the Lag SDK.
15
+ *
16
+ * Subclasses correspond to specific HTTP status codes:
17
+ * - `LagAuthError` - 401
18
+ * - `LagPermissionError` - 403
19
+ * - `LagNotFoundError` - 404
20
+ * - `LagConflictError` - 409
21
+ * - `LagRateLimitError` - 429
22
+ * - `LagServerError` - 5xx
23
+ *
24
+ * Network failures (DNS, connection refused, timeouts before any status was
25
+ * received) are wrapped in `LagConnectionError`.
26
+ */
27
+ export declare class LagApiError extends Error {
28
+ readonly status: number;
29
+ readonly code: string;
30
+ readonly body: LagApiErrorBody | null;
31
+ readonly requestId: string | null;
32
+ constructor(message: string, status: number, code: string, body: LagApiErrorBody | null, requestId: string | null);
33
+ }
34
+ export declare class LagAuthError extends LagApiError {
35
+ constructor(message: string, body: LagApiErrorBody | null, requestId: string | null);
36
+ }
37
+ export declare class LagPermissionError extends LagApiError {
38
+ constructor(message: string, body: LagApiErrorBody | null, requestId: string | null);
39
+ }
40
+ export declare class LagNotFoundError extends LagApiError {
41
+ constructor(message: string, body: LagApiErrorBody | null, requestId: string | null);
42
+ }
43
+ export declare class LagConflictError extends LagApiError {
44
+ constructor(message: string, body: LagApiErrorBody | null, requestId: string | null);
45
+ }
46
+ export declare class LagRateLimitError extends LagApiError {
47
+ /** Seconds the server asked the client to wait, parsed from `Retry-After`. */
48
+ readonly retryAfterSeconds: number | null;
49
+ constructor(message: string, body: LagApiErrorBody | null, requestId: string | null, retryAfterSeconds: number | null);
50
+ }
51
+ export declare class LagServerError extends LagApiError {
52
+ constructor(message: string, status: number, body: LagApiErrorBody | null, requestId: string | null);
53
+ }
54
+ /** Thrown when the request never reached the server (DNS, refused, timed out). */
55
+ export declare class LagConnectionError extends LagApiError {
56
+ readonly cause?: unknown;
57
+ constructor(message: string, cause?: unknown);
58
+ }
59
+ /**
60
+ * Build the appropriate `LagApiError` subclass for an HTTP response.
61
+ *
62
+ * The Lag API always returns a JSON error envelope, but we still defend
63
+ * against truncated or non-JSON bodies (e.g. an upstream proxy returning
64
+ * plain text on 502).
65
+ */
66
+ export declare function errorFromResponse(status: number, body: LagApiErrorBody | null, requestId: string | null, retryAfterSeconds: number | null): LagApiError;
67
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;;;;GAaG;AACH,qBAAa,WAAY,SAAQ,KAAK;IACpC,SAAgB,MAAM,EAAE,MAAM,CAAC;IAC/B,SAAgB,IAAI,EAAE,MAAM,CAAC;IAC7B,SAAgB,IAAI,EAAE,eAAe,GAAG,IAAI,CAAC;IAC7C,SAAgB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;gBAGvC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,eAAe,GAAG,IAAI,EAC5B,SAAS,EAAE,MAAM,GAAG,IAAI;CAS3B;AAED,qBAAa,YAAa,SAAQ,WAAW;gBAC/B,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAIpF;AAED,qBAAa,kBAAmB,SAAQ,WAAW;gBACrC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAIpF;AAED,qBAAa,gBAAiB,SAAQ,WAAW;gBACnC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAIpF;AAED,qBAAa,gBAAiB,SAAQ,WAAW;gBACnC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAIpF;AAED,qBAAa,iBAAkB,SAAQ,WAAW;IAChD,8EAA8E;IAC9E,SAAgB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;gBAG/C,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,eAAe,GAAG,IAAI,EAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI;CAMnC;AAED,qBAAa,cAAe,SAAQ,WAAW;gBACjC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,IAAI,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;CAIpG;AAED,kFAAkF;AAClF,qBAAa,kBAAmB,SAAQ,WAAW;IACjD,SAAyB,KAAK,CAAC,EAAE,OAAO,CAAC;gBAC7B,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO;CAK7C;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,IAAI,EAAE,eAAe,GAAG,IAAI,EAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,EACxB,iBAAiB,EAAE,MAAM,GAAG,IAAI,GAC/B,WAAW,CAiBb"}
package/dist/errors.js ADDED
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Base class for every error thrown by the Lag SDK.
3
+ *
4
+ * Subclasses correspond to specific HTTP status codes:
5
+ * - `LagAuthError` - 401
6
+ * - `LagPermissionError` - 403
7
+ * - `LagNotFoundError` - 404
8
+ * - `LagConflictError` - 409
9
+ * - `LagRateLimitError` - 429
10
+ * - `LagServerError` - 5xx
11
+ *
12
+ * Network failures (DNS, connection refused, timeouts before any status was
13
+ * received) are wrapped in `LagConnectionError`.
14
+ */
15
+ export class LagApiError extends Error {
16
+ status;
17
+ code;
18
+ body;
19
+ requestId;
20
+ constructor(message, status, code, body, requestId) {
21
+ super(message);
22
+ this.name = 'LagApiError';
23
+ this.status = status;
24
+ this.code = code;
25
+ this.body = body;
26
+ this.requestId = requestId;
27
+ }
28
+ }
29
+ export class LagAuthError extends LagApiError {
30
+ constructor(message, body, requestId) {
31
+ super(message, 401, 'unauthorized', body, requestId);
32
+ this.name = 'LagAuthError';
33
+ }
34
+ }
35
+ export class LagPermissionError extends LagApiError {
36
+ constructor(message, body, requestId) {
37
+ super(message, 403, 'forbidden', body, requestId);
38
+ this.name = 'LagPermissionError';
39
+ }
40
+ }
41
+ export class LagNotFoundError extends LagApiError {
42
+ constructor(message, body, requestId) {
43
+ super(message, 404, 'not_found', body, requestId);
44
+ this.name = 'LagNotFoundError';
45
+ }
46
+ }
47
+ export class LagConflictError extends LagApiError {
48
+ constructor(message, body, requestId) {
49
+ super(message, 409, 'conflict', body, requestId);
50
+ this.name = 'LagConflictError';
51
+ }
52
+ }
53
+ export class LagRateLimitError extends LagApiError {
54
+ /** Seconds the server asked the client to wait, parsed from `Retry-After`. */
55
+ retryAfterSeconds;
56
+ constructor(message, body, requestId, retryAfterSeconds) {
57
+ super(message, 429, 'rate_limited', body, requestId);
58
+ this.name = 'LagRateLimitError';
59
+ this.retryAfterSeconds = retryAfterSeconds;
60
+ }
61
+ }
62
+ export class LagServerError extends LagApiError {
63
+ constructor(message, status, body, requestId) {
64
+ super(message, status, 'server_error', body, requestId);
65
+ this.name = 'LagServerError';
66
+ }
67
+ }
68
+ /** Thrown when the request never reached the server (DNS, refused, timed out). */
69
+ export class LagConnectionError extends LagApiError {
70
+ cause;
71
+ constructor(message, cause) {
72
+ super(message, 0, 'connection_error', null, null);
73
+ this.name = 'LagConnectionError';
74
+ this.cause = cause;
75
+ }
76
+ }
77
+ /**
78
+ * Build the appropriate `LagApiError` subclass for an HTTP response.
79
+ *
80
+ * The Lag API always returns a JSON error envelope, but we still defend
81
+ * against truncated or non-JSON bodies (e.g. an upstream proxy returning
82
+ * plain text on 502).
83
+ */
84
+ export function errorFromResponse(status, body, requestId, retryAfterSeconds) {
85
+ const message = body?.message || `HTTP ${status}`;
86
+ switch (status) {
87
+ case 401:
88
+ return new LagAuthError(message, body, requestId);
89
+ case 403:
90
+ return new LagPermissionError(message, body, requestId);
91
+ case 404:
92
+ return new LagNotFoundError(message, body, requestId);
93
+ case 409:
94
+ return new LagConflictError(message, body, requestId);
95
+ case 429:
96
+ return new LagRateLimitError(message, body, requestId, retryAfterSeconds);
97
+ default:
98
+ if (status >= 500)
99
+ return new LagServerError(message, status, body, requestId);
100
+ return new LagApiError(message, status, body?.error ?? `http_${status}`, body, requestId);
101
+ }
102
+ }
103
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import { LagApiError } from './errors.js';
2
+ export interface LagClientOptions {
3
+ /** Bearer token: a Personal Access Token (lag_pat_*) or Supabase JWT. */
4
+ token: string;
5
+ /** API base URL. Defaults to `https://api.trylag.com`. */
6
+ baseUrl?: string;
7
+ /** Per-request timeout in milliseconds. Defaults to 30000 (30s). */
8
+ timeoutMs?: number;
9
+ /** Maximum retry attempts for transient failures. Defaults to 2. */
10
+ maxRetries?: number;
11
+ /** Override the User-Agent string. */
12
+ userAgent?: string;
13
+ /** Custom fetch implementation (defaults to global fetch). */
14
+ fetch?: typeof fetch;
15
+ }
16
+ export interface RequestOptions {
17
+ method: 'GET' | 'POST' | 'PATCH' | 'PUT' | 'DELETE';
18
+ path: string;
19
+ query?: Record<string, string | number | boolean | null | undefined>;
20
+ body?: unknown;
21
+ /** When true, body is sent raw (multipart) instead of JSON-encoded. */
22
+ rawBody?: BodyInit;
23
+ /** Extra headers merged onto the default set. */
24
+ headers?: Record<string, string>;
25
+ /** Override the client default timeout. */
26
+ timeoutMs?: number;
27
+ /** Override the client default retry count. */
28
+ maxRetries?: number;
29
+ }
30
+ /**
31
+ * Low-level HTTP transport shared by every resource.
32
+ *
33
+ * Responsibilities:
34
+ * - Inject Authorization, User-Agent, Accept headers.
35
+ * - Build query strings, JSON-encode bodies, parse JSON responses.
36
+ * - Map non-2xx responses to typed errors.
37
+ * - Retry transient failures (5xx, 429, network) with exponential backoff.
38
+ * - Honor the server's `Retry-After` header on 429.
39
+ * - Enforce per-request timeouts via AbortController.
40
+ */
41
+ export declare class HttpClient {
42
+ private readonly token;
43
+ private readonly baseUrl;
44
+ private readonly timeoutMs;
45
+ private readonly maxRetries;
46
+ private readonly userAgent;
47
+ private readonly fetchImpl;
48
+ constructor(opts: LagClientOptions);
49
+ request<T>(opts: RequestOptions): Promise<T>;
50
+ private buildUrl;
51
+ private buildHeaders;
52
+ private buildBody;
53
+ private decodeBody;
54
+ private parseErrorBody;
55
+ }
56
+ /**
57
+ * Re-export so consumers can `instanceof`-check transport-layer errors
58
+ * without importing from a deeper path.
59
+ */
60
+ export { LagApiError };
61
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,WAAW,EAIZ,MAAM,aAAa,CAAC;AAGrB,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,oEAAoE;IACpE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC,CAAC;IACrE,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,uEAAuE;IACvE,OAAO,CAAC,EAAE,QAAQ,CAAC;IACnB,iDAAiD;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAMD;;;;;;;;;;GAUG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAS;IAC/B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAe;gBAE7B,IAAI,EAAE,gBAAgB;IAe5B,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IA2DlD,OAAO,CAAC,QAAQ;IAehB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,SAAS;YAMH,UAAU;YAgBV,cAAc;CAkB7B;AAyBD;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,CAAC"}