@lumnsh/node 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.
package/dist/index.mjs ADDED
@@ -0,0 +1,400 @@
1
+ // src/version.ts
2
+ var SDK_VERSION = "0.1.0";
3
+
4
+ // src/errors.ts
5
+ var LumnError = class extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = "LumnError";
9
+ }
10
+ };
11
+ var LumnApiError = class extends LumnError {
12
+ status;
13
+ code;
14
+ suggestion;
15
+ details;
16
+ constructor(status, body) {
17
+ super(body.message);
18
+ this.name = "LumnApiError";
19
+ this.status = status;
20
+ this.code = body.code;
21
+ this.suggestion = body.suggestion;
22
+ this.details = body.details;
23
+ }
24
+ };
25
+ var LumnAuthenticationError = class extends LumnApiError {
26
+ constructor(body) {
27
+ super(401, body);
28
+ this.name = "LumnAuthenticationError";
29
+ }
30
+ };
31
+ var LumnValidationError = class extends LumnApiError {
32
+ constructor(body) {
33
+ super(400, body);
34
+ this.name = "LumnValidationError";
35
+ }
36
+ };
37
+ var LumnNotFoundError = class extends LumnApiError {
38
+ constructor(body) {
39
+ super(404, body);
40
+ this.name = "LumnNotFoundError";
41
+ }
42
+ };
43
+ var LumnRateLimitError = class extends LumnApiError {
44
+ retryAfter;
45
+ constructor(body, retryAfter) {
46
+ super(429, body);
47
+ this.name = "LumnRateLimitError";
48
+ this.retryAfter = retryAfter;
49
+ }
50
+ };
51
+ var LumnConnectionError = class extends LumnError {
52
+ constructor(message, cause) {
53
+ super(message);
54
+ this.cause = cause;
55
+ this.name = "LumnConnectionError";
56
+ }
57
+ };
58
+ var LumnTimeoutError = class extends LumnError {
59
+ constructor(timeoutMs) {
60
+ super(`Request timed out after ${timeoutMs}ms`);
61
+ this.name = "LumnTimeoutError";
62
+ }
63
+ };
64
+
65
+ // src/resource.ts
66
+ var ApiResource = class {
67
+ constructor(_client) {
68
+ this._client = _client;
69
+ }
70
+ async get(path, query) {
71
+ return this._client._request("GET", path, void 0, query);
72
+ }
73
+ async post(path, body) {
74
+ return this._client._request("POST", path, body);
75
+ }
76
+ async del(path, query) {
77
+ return this._client._request("DELETE", path, void 0, query);
78
+ }
79
+ async _list(path, params) {
80
+ const query = {};
81
+ if (params) {
82
+ for (const [key, value] of Object.entries(params)) {
83
+ if (value !== void 0) {
84
+ query[key] = String(value);
85
+ }
86
+ }
87
+ }
88
+ const raw = await this._client._request("GET", path, void 0, query);
89
+ const result = {
90
+ data: raw.data,
91
+ hasMore: raw.has_more,
92
+ cursor: raw.cursor,
93
+ [Symbol.asyncIterator]: () => {
94
+ let currentData = raw.data;
95
+ let currentCursor = raw.cursor;
96
+ let currentHasMore = raw.has_more;
97
+ let index = 0;
98
+ return {
99
+ next: async () => {
100
+ if (index < currentData.length) {
101
+ return { value: currentData[index++], done: false };
102
+ }
103
+ if (!currentHasMore || !currentCursor) {
104
+ return { value: void 0, done: true };
105
+ }
106
+ const nextParams = { ...params, cursor: currentCursor };
107
+ const nextQuery = {};
108
+ for (const [key, value] of Object.entries(nextParams)) {
109
+ if (value !== void 0) {
110
+ nextQuery[key] = String(value);
111
+ }
112
+ }
113
+ const nextRaw = await this._client._request("GET", path, void 0, nextQuery);
114
+ currentData = nextRaw.data;
115
+ currentCursor = nextRaw.cursor;
116
+ currentHasMore = nextRaw.has_more;
117
+ index = 0;
118
+ if (currentData.length === 0) {
119
+ return { value: void 0, done: true };
120
+ }
121
+ return { value: currentData[index++], done: false };
122
+ }
123
+ };
124
+ }
125
+ };
126
+ return result;
127
+ }
128
+ };
129
+
130
+ // src/resources/customers.ts
131
+ var Customers = class extends ApiResource {
132
+ async create(params) {
133
+ return this.post("/api/v1/customers", params);
134
+ }
135
+ async list(params) {
136
+ return this._list("/api/v1/customers", params);
137
+ }
138
+ };
139
+
140
+ // src/resources/products.ts
141
+ var Products = class extends ApiResource {
142
+ async create(params) {
143
+ return this.post("/api/v1/products", params);
144
+ }
145
+ async list(params) {
146
+ return this._list("/api/v1/products", params);
147
+ }
148
+ };
149
+
150
+ // src/resources/features.ts
151
+ var Features = class extends ApiResource {
152
+ async create(params) {
153
+ return this.post("/api/v1/features", params);
154
+ }
155
+ async list(params) {
156
+ return this._list("/api/v1/features", params);
157
+ }
158
+ };
159
+
160
+ // src/resources/entitlements.ts
161
+ var Entitlements = class extends ApiResource {
162
+ async check(params) {
163
+ return this.get("/api/v1/entitlements", {
164
+ customer_id: params.customer_id,
165
+ feature_key: params.feature_key
166
+ });
167
+ }
168
+ };
169
+
170
+ // src/resources/payments.ts
171
+ var Payments = class extends ApiResource {
172
+ async list(params) {
173
+ return this._list("/api/v1/payments", params);
174
+ }
175
+ };
176
+
177
+ // src/resources/subscriptions.ts
178
+ var Subscriptions = class extends ApiResource {
179
+ async list(params) {
180
+ return this._list("/api/v1/subscriptions", params);
181
+ }
182
+ };
183
+
184
+ // src/resources/events.ts
185
+ var Events = class extends ApiResource {
186
+ async list(params) {
187
+ return this._list("/api/v1/events", params);
188
+ }
189
+ };
190
+
191
+ // src/resources/meter.ts
192
+ var Meter = class extends ApiResource {
193
+ async record(params) {
194
+ return this.post("/api/v1/meter", params);
195
+ }
196
+ };
197
+
198
+ // src/resources/sync.ts
199
+ var Sync = class extends ApiResource {
200
+ async trigger(params) {
201
+ return this.post("/api/v1/sync", params);
202
+ }
203
+ };
204
+
205
+ // src/resources/connections.ts
206
+ var Connections = class extends ApiResource {
207
+ async create(params) {
208
+ return this.post("/api/v1/connections", params);
209
+ }
210
+ async list() {
211
+ return this.get("/api/v1/connections");
212
+ }
213
+ };
214
+
215
+ // src/resources/api-keys.ts
216
+ var ApiKeys = class extends ApiResource {
217
+ async create(params) {
218
+ return this.post("/api/v1/api-keys", params);
219
+ }
220
+ async list() {
221
+ return this.get("/api/v1/api-keys");
222
+ }
223
+ async revoke(id) {
224
+ return this.del("/api/v1/api-keys", { id });
225
+ }
226
+ };
227
+
228
+ // src/resources/copy-config.ts
229
+ var CopyConfig = class extends ApiResource {
230
+ async copy(params) {
231
+ return this.post("/api/v1/copy-config", params);
232
+ }
233
+ };
234
+
235
+ // src/resources/checkout.ts
236
+ var Checkout = class extends ApiResource {
237
+ async create(params) {
238
+ return this.post("/api/v1/checkout", params);
239
+ }
240
+ };
241
+
242
+ // src/client.ts
243
+ var DEFAULT_BASE_URL = "https://app.lumn.dev";
244
+ var DEFAULT_TIMEOUT = 3e4;
245
+ var DEFAULT_MAX_RETRIES = 2;
246
+ var Lumn = class {
247
+ _apiKey;
248
+ _baseUrl;
249
+ _timeout;
250
+ _maxRetries;
251
+ _fetch;
252
+ customers;
253
+ products;
254
+ features;
255
+ entitlements;
256
+ payments;
257
+ subscriptions;
258
+ events;
259
+ meter;
260
+ sync;
261
+ connections;
262
+ apiKeys;
263
+ copyConfig;
264
+ checkout;
265
+ constructor(config) {
266
+ if (config === void 0 || typeof config === "string") {
267
+ this._apiKey = config ?? "";
268
+ this._baseUrl = DEFAULT_BASE_URL;
269
+ this._timeout = DEFAULT_TIMEOUT;
270
+ this._maxRetries = DEFAULT_MAX_RETRIES;
271
+ this._fetch = globalThis.fetch;
272
+ } else {
273
+ this._apiKey = config.apiKey;
274
+ this._baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
275
+ this._timeout = config.timeout ?? DEFAULT_TIMEOUT;
276
+ this._maxRetries = config.maxRetries ?? DEFAULT_MAX_RETRIES;
277
+ this._fetch = config.fetch ?? globalThis.fetch;
278
+ }
279
+ this.customers = new Customers(this);
280
+ this.products = new Products(this);
281
+ this.features = new Features(this);
282
+ this.entitlements = new Entitlements(this);
283
+ this.payments = new Payments(this);
284
+ this.subscriptions = new Subscriptions(this);
285
+ this.events = new Events(this);
286
+ this.meter = new Meter(this);
287
+ this.sync = new Sync(this);
288
+ this.connections = new Connections(this);
289
+ this.apiKeys = new ApiKeys(this);
290
+ this.copyConfig = new CopyConfig(this);
291
+ this.checkout = new Checkout(this);
292
+ }
293
+ /** @internal */
294
+ async _request(method, path, body, query) {
295
+ let lastError;
296
+ for (let attempt = 0; attempt <= this._maxRetries; attempt++) {
297
+ try {
298
+ const url = this._buildUrl(path, query);
299
+ const headers = {
300
+ Authorization: `Bearer ${this._apiKey}`,
301
+ "User-Agent": `@lumnsh/node/${SDK_VERSION}`
302
+ };
303
+ if (body !== void 0) {
304
+ headers["Content-Type"] = "application/json";
305
+ }
306
+ const response = await this._fetch(url, {
307
+ method,
308
+ headers,
309
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
310
+ signal: AbortSignal.timeout(this._timeout)
311
+ });
312
+ if (!response.ok) {
313
+ const errorBody = await response.json().catch(() => ({
314
+ code: "unknown_error",
315
+ message: response.statusText
316
+ }));
317
+ const errorData = errorBody.error ?? errorBody;
318
+ if (response.status === 429) {
319
+ const retryAfter = response.headers.get("Retry-After");
320
+ const retryMs = retryAfter ? Number(retryAfter) * 1e3 : 1e3;
321
+ if (attempt < this._maxRetries) {
322
+ await this._sleep(retryMs);
323
+ continue;
324
+ }
325
+ throw new LumnRateLimitError(
326
+ errorData,
327
+ retryAfter ? Number(retryAfter) : null
328
+ );
329
+ }
330
+ if (response.status >= 500 && attempt < this._maxRetries) {
331
+ await this._sleep(1e3 * Math.pow(2, attempt));
332
+ continue;
333
+ }
334
+ this._throwTypedError(response.status, errorData);
335
+ }
336
+ const json = await response.json();
337
+ return json.data !== void 0 ? json.data : json;
338
+ } catch (error) {
339
+ if (error instanceof LumnApiError || error instanceof LumnConnectionError || error instanceof LumnTimeoutError) {
340
+ throw error;
341
+ }
342
+ if (error instanceof DOMException && error.name === "TimeoutError") {
343
+ throw new LumnTimeoutError(this._timeout);
344
+ }
345
+ if (error instanceof TypeError && error.message.includes("fetch")) {
346
+ lastError = error;
347
+ if (attempt < this._maxRetries) {
348
+ await this._sleep(1e3 * Math.pow(2, attempt));
349
+ continue;
350
+ }
351
+ throw new LumnConnectionError(
352
+ `Failed to connect to ${this._baseUrl}`,
353
+ error
354
+ );
355
+ }
356
+ throw error;
357
+ }
358
+ }
359
+ throw lastError ?? new LumnConnectionError(`Failed after ${this._maxRetries + 1} attempts`);
360
+ }
361
+ _buildUrl(path, query) {
362
+ const url = new URL(path, this._baseUrl);
363
+ if (query) {
364
+ for (const [key, value] of Object.entries(query)) {
365
+ if (value !== void 0) {
366
+ url.searchParams.set(key, String(value));
367
+ }
368
+ }
369
+ }
370
+ return url.toString();
371
+ }
372
+ _throwTypedError(status, body) {
373
+ switch (status) {
374
+ case 401:
375
+ throw new LumnAuthenticationError(body);
376
+ case 400:
377
+ throw new LumnValidationError(body);
378
+ case 404:
379
+ throw new LumnNotFoundError(body);
380
+ default:
381
+ throw new LumnApiError(status, body);
382
+ }
383
+ }
384
+ _sleep(ms) {
385
+ return new Promise((resolve) => setTimeout(resolve, ms));
386
+ }
387
+ };
388
+ export {
389
+ Lumn,
390
+ LumnApiError,
391
+ LumnAuthenticationError,
392
+ LumnConnectionError,
393
+ LumnError,
394
+ LumnNotFoundError,
395
+ LumnRateLimitError,
396
+ LumnTimeoutError,
397
+ LumnValidationError,
398
+ SDK_VERSION,
399
+ Lumn as default
400
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@lumnsh/node",
3
+ "version": "0.1.0",
4
+ "description": "Official Lumn Node.js SDK — provider-agnostic payment orchestration",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "devDependencies": {
24
+ "@types/node": "^22.12.0",
25
+ "tsup": "^8.0.0",
26
+ "typescript": "^5.7.3",
27
+ "@lumn/typescript-config": "0.0.1"
28
+ },
29
+ "engines": {
30
+ "node": ">=18"
31
+ },
32
+ "keywords": [
33
+ "lumn",
34
+ "payments",
35
+ "payment-orchestration",
36
+ "billing",
37
+ "subscriptions"
38
+ ],
39
+ "license": "MIT",
40
+ "scripts": {
41
+ "type-check": "tsc --noEmit",
42
+ "build": "tsup src/index.ts --format cjs,esm --dts",
43
+ "clean": "rm -rf dist"
44
+ }
45
+ }