syntro-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,305 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ Syntro: () => Syntro
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+ var Syntro = class {
27
+ constructor(apiKey, config = {}) {
28
+ if (!apiKey) throw new Error("[Syntro] apiKey is required");
29
+ this.apiKey = apiKey;
30
+ this.baseUrl = (config.baseUrl ?? "https://api.syntro.run").replace(/\/$/, "");
31
+ }
32
+ // ─── Helpers ────────────────────────────────────────────────────────────────
33
+ get headers() {
34
+ return { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` };
35
+ }
36
+ async post(path, body) {
37
+ const res = await fetch(`${this.baseUrl}${path}`, {
38
+ method: "POST",
39
+ headers: this.headers,
40
+ body: JSON.stringify(body)
41
+ });
42
+ return res.json();
43
+ }
44
+ async put(path, body) {
45
+ const res = await fetch(`${this.baseUrl}${path}`, {
46
+ method: "PUT",
47
+ headers: this.headers,
48
+ body: JSON.stringify(body)
49
+ });
50
+ return res.json();
51
+ }
52
+ async patch(path, body) {
53
+ const res = await fetch(`${this.baseUrl}${path}`, {
54
+ method: "PATCH",
55
+ headers: this.headers,
56
+ body: JSON.stringify(body)
57
+ });
58
+ return res.json();
59
+ }
60
+ async del(path) {
61
+ const res = await fetch(`${this.baseUrl}${path}`, {
62
+ method: "DELETE",
63
+ headers: this.headers
64
+ });
65
+ return res.json();
66
+ }
67
+ async get(path, params) {
68
+ const url = new URL(`${this.baseUrl}${path}`);
69
+ if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, String(v)));
70
+ const res = await fetch(url.toString(), { method: "GET", headers: this.headers });
71
+ return res.json();
72
+ }
73
+ // ─── Events ─────────────────────────────────────────────────────────────────
74
+ /**
75
+ * Track an event.
76
+ * @param type - 'CUSTOM' | 'ERROR' | 'AUTH'
77
+ * @param name - Short event key, e.g. 'cart_add'
78
+ * @param message - Optional description
79
+ * @param metadata - Optional arbitrary JSON
80
+ * @param userId - Optional ProjectUser ID
81
+ */
82
+ async event(type, name, message, metadata, userId) {
83
+ try {
84
+ return await this.post("/v1/events", { type, name, message, metadata, userId });
85
+ } catch (err) {
86
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
87
+ }
88
+ }
89
+ /**
90
+ * Track an ERROR event.
91
+ * @example
92
+ * await syntro.sendError('checkout_failed', 'Error loading page', { code: 500 });
93
+ */
94
+ async sendError(name, message, metadata) {
95
+ return this.event("ERROR", name, message, metadata);
96
+ }
97
+ /** Get event statistics grouped by name (for analytics dashboard). */
98
+ async getStats(type) {
99
+ return this.get("/v1/events/stats", type ? { type } : {});
100
+ }
101
+ /** List raw events with pagination. */
102
+ async listEvents(options) {
103
+ return this.get("/v1/events", {
104
+ ...options?.type ? { type: options.type } : {},
105
+ ...options?.limit !== void 0 ? { limit: options.limit } : {},
106
+ ...options?.skip !== void 0 ? { skip: options.skip } : {}
107
+ });
108
+ }
109
+ // ─── Auth ────────────────────────────────────────────────────────────────────
110
+ /**
111
+ * Register a new end-user.
112
+ * @example
113
+ * const { user, token, error } = await syntro.register('john', 'john@email.com', 'pass123');
114
+ */
115
+ async register(username, email, password) {
116
+ try {
117
+ const res = await this.post(
118
+ "/v1/auth/register",
119
+ { username, email, password }
120
+ );
121
+ return res.error || res.message && !res.success ? { success: false, error: res.error ?? res.message } : res;
122
+ } catch (err) {
123
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
124
+ }
125
+ }
126
+ /**
127
+ * Login with username + password.
128
+ * @example
129
+ * const { token, user, error } = await syntro.login('john', 'pass123');
130
+ */
131
+ async login(username, password) {
132
+ try {
133
+ const res = await this.post("/v1/auth/login", { username, password });
134
+ return res.error ? { success: false, error: res.error } : res;
135
+ } catch (err) {
136
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
137
+ }
138
+ }
139
+ /**
140
+ * Login with email + password.
141
+ */
142
+ async loginWithEmail(email, password) {
143
+ try {
144
+ const res = await this.post("/v1/auth/login", { email, password });
145
+ return res.error ? { success: false, error: res.error } : res;
146
+ } catch (err) {
147
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
148
+ }
149
+ }
150
+ /**
151
+ * Verify a JWT token and get the associated user.
152
+ * @example
153
+ * const { valid, user } = await syntro.verifyToken(token);
154
+ */
155
+ async verifyToken(token) {
156
+ try {
157
+ return await this.post("/v1/auth/verify", { token });
158
+ } catch (err) {
159
+ return { valid: false, error: err instanceof Error ? err.message : "Unknown error" };
160
+ }
161
+ }
162
+ /**
163
+ * Get a ProjectUser by ID.
164
+ * @example
165
+ * const { user } = await syntro.getUser(userId);
166
+ */
167
+ async getUser(userId) {
168
+ try {
169
+ return await this.get(`/v1/auth/users/${userId}`);
170
+ } catch (err) {
171
+ return { error: err instanceof Error ? err.message : "Unknown error" };
172
+ }
173
+ }
174
+ /**
175
+ * Get a user's username by ID.
176
+ * @example
177
+ * const username = await syntro.getUsername(userId);
178
+ */
179
+ async getUsername(userId) {
180
+ const { user, error } = await this.getUser(userId);
181
+ if (error || !user) return null;
182
+ return user.username ?? null;
183
+ }
184
+ /**
185
+ * Get a user's email by ID.
186
+ * @example
187
+ * const email = await syntro.getUserEmail(userId);
188
+ */
189
+ async getUserEmail(userId) {
190
+ const { user, error } = await this.getUser(userId);
191
+ if (error || !user) return null;
192
+ return user.email ?? null;
193
+ }
194
+ /**
195
+ * Get a user's metadata by ID.
196
+ * @example
197
+ * const meta = await syntro.getMetadata(userId);
198
+ * console.log(meta?.plan, meta?.role);
199
+ */
200
+ async getMetadata(userId) {
201
+ const { user, error } = await this.getUser(userId);
202
+ if (error || !user) return null;
203
+ return user.metadata ?? {};
204
+ }
205
+ /**
206
+ * Merge-update a user's metadata. Existing keys are preserved.
207
+ * @example
208
+ * await syntro.updateMetadata(userId, { plan: 'pro', role: 'admin' });
209
+ */
210
+ async updateMetadata(userId, metadata) {
211
+ try {
212
+ return await this.patch(`/v1/auth/users/${userId}/metadata`, metadata);
213
+ } catch (err) {
214
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
215
+ }
216
+ }
217
+ /**
218
+ * Update a user's username or fully replace their metadata.
219
+ * @example
220
+ * await syntro.updateUser(userId, { username: 'new_name' });
221
+ */
222
+ async updateUser(userId, data) {
223
+ try {
224
+ return await this.put(`/v1/auth/users/${userId}`, data);
225
+ } catch (err) {
226
+ return { error: err instanceof Error ? err.message : "Unknown error" };
227
+ }
228
+ }
229
+ /**
230
+ * Delete a ProjectUser permanently.
231
+ * @example
232
+ * const { success } = await syntro.deleteUser(userId);
233
+ */
234
+ async deleteUser(userId) {
235
+ try {
236
+ return await this.del(`/v1/auth/users/${userId}`);
237
+ } catch (err) {
238
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
239
+ }
240
+ }
241
+ /** List users with pagination. */
242
+ async listUsers(options) {
243
+ return this.get("/v1/auth/users", {
244
+ ...options?.limit !== void 0 ? { limit: options.limit } : {},
245
+ ...options?.skip !== void 0 ? { skip: options.skip } : {}
246
+ });
247
+ }
248
+ // ─── Billing ─────────────────────────────────────────────────────────────────
249
+ /**
250
+ * Create a Stripe Checkout payment session.
251
+ * Returns a `url` to redirect the customer to.
252
+ *
253
+ * @param name - Product/payment name
254
+ * @param amount - Amount in cents (e.g. 999 = $9.99)
255
+ * @param options - currency, successUrl, cancelUrl, customerEmail
256
+ *
257
+ * @example
258
+ * const { url, error } = await syntro.createPayment('Pro Plan', 999);
259
+ * if (url) window.location.href = url;
260
+ */
261
+ async createPayment(name, amount, options) {
262
+ try {
263
+ const res = await this.post("/v1/billing/create-payment", {
264
+ name,
265
+ amount,
266
+ currency: options?.currency ?? "usd",
267
+ successUrl: options?.successUrl,
268
+ cancelUrl: options?.cancelUrl,
269
+ customerEmail: options?.customerEmail
270
+ });
271
+ return res.error ? { error: res.error } : res;
272
+ } catch (err) {
273
+ return { error: err instanceof Error ? err.message : "Unknown error" };
274
+ }
275
+ }
276
+ /** List payment transactions. */
277
+ async listTransactions(options) {
278
+ return this.get("/v1/billing/transactions", {
279
+ ...options?.limit !== void 0 ? { limit: options.limit } : {},
280
+ ...options?.skip !== void 0 ? { skip: options.skip } : {},
281
+ ...options?.status ? { status: options.status } : {}
282
+ });
283
+ }
284
+ /**
285
+ * Verify if a customer has paid for a specific product or any product.
286
+ *
287
+ * @example
288
+ * const { paid, transaction } = await syntro.verifyPayment('customer@email.com');
289
+ * if (paid) console.log('Customer paid!', transaction.amount);
290
+ */
291
+ async verifyPayment(customerEmail, options) {
292
+ try {
293
+ return await this.get("/v1/billing/verify-payment", {
294
+ customerEmail,
295
+ ...options?.name ? { name: options.name } : {}
296
+ });
297
+ } catch (err) {
298
+ return { paid: false, error: err instanceof Error ? err.message : "Unknown error" };
299
+ }
300
+ }
301
+ };
302
+ // Annotate the CommonJS export names for ESM import in node:
303
+ 0 && (module.exports = {
304
+ Syntro
305
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,280 @@
1
+ // src/index.ts
2
+ var Syntro = class {
3
+ constructor(apiKey, config = {}) {
4
+ if (!apiKey) throw new Error("[Syntro] apiKey is required");
5
+ this.apiKey = apiKey;
6
+ this.baseUrl = (config.baseUrl ?? "https://api.syntro.run").replace(/\/$/, "");
7
+ }
8
+ // ─── Helpers ────────────────────────────────────────────────────────────────
9
+ get headers() {
10
+ return { "Content-Type": "application/json", Authorization: `Bearer ${this.apiKey}` };
11
+ }
12
+ async post(path, body) {
13
+ const res = await fetch(`${this.baseUrl}${path}`, {
14
+ method: "POST",
15
+ headers: this.headers,
16
+ body: JSON.stringify(body)
17
+ });
18
+ return res.json();
19
+ }
20
+ async put(path, body) {
21
+ const res = await fetch(`${this.baseUrl}${path}`, {
22
+ method: "PUT",
23
+ headers: this.headers,
24
+ body: JSON.stringify(body)
25
+ });
26
+ return res.json();
27
+ }
28
+ async patch(path, body) {
29
+ const res = await fetch(`${this.baseUrl}${path}`, {
30
+ method: "PATCH",
31
+ headers: this.headers,
32
+ body: JSON.stringify(body)
33
+ });
34
+ return res.json();
35
+ }
36
+ async del(path) {
37
+ const res = await fetch(`${this.baseUrl}${path}`, {
38
+ method: "DELETE",
39
+ headers: this.headers
40
+ });
41
+ return res.json();
42
+ }
43
+ async get(path, params) {
44
+ const url = new URL(`${this.baseUrl}${path}`);
45
+ if (params) Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, String(v)));
46
+ const res = await fetch(url.toString(), { method: "GET", headers: this.headers });
47
+ return res.json();
48
+ }
49
+ // ─── Events ─────────────────────────────────────────────────────────────────
50
+ /**
51
+ * Track an event.
52
+ * @param type - 'CUSTOM' | 'ERROR' | 'AUTH'
53
+ * @param name - Short event key, e.g. 'cart_add'
54
+ * @param message - Optional description
55
+ * @param metadata - Optional arbitrary JSON
56
+ * @param userId - Optional ProjectUser ID
57
+ */
58
+ async event(type, name, message, metadata, userId) {
59
+ try {
60
+ return await this.post("/v1/events", { type, name, message, metadata, userId });
61
+ } catch (err) {
62
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
63
+ }
64
+ }
65
+ /**
66
+ * Track an ERROR event.
67
+ * @example
68
+ * await syntro.sendError('checkout_failed', 'Error loading page', { code: 500 });
69
+ */
70
+ async sendError(name, message, metadata) {
71
+ return this.event("ERROR", name, message, metadata);
72
+ }
73
+ /** Get event statistics grouped by name (for analytics dashboard). */
74
+ async getStats(type) {
75
+ return this.get("/v1/events/stats", type ? { type } : {});
76
+ }
77
+ /** List raw events with pagination. */
78
+ async listEvents(options) {
79
+ return this.get("/v1/events", {
80
+ ...options?.type ? { type: options.type } : {},
81
+ ...options?.limit !== void 0 ? { limit: options.limit } : {},
82
+ ...options?.skip !== void 0 ? { skip: options.skip } : {}
83
+ });
84
+ }
85
+ // ─── Auth ────────────────────────────────────────────────────────────────────
86
+ /**
87
+ * Register a new end-user.
88
+ * @example
89
+ * const { user, token, error } = await syntro.register('john', 'john@email.com', 'pass123');
90
+ */
91
+ async register(username, email, password) {
92
+ try {
93
+ const res = await this.post(
94
+ "/v1/auth/register",
95
+ { username, email, password }
96
+ );
97
+ return res.error || res.message && !res.success ? { success: false, error: res.error ?? res.message } : res;
98
+ } catch (err) {
99
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
100
+ }
101
+ }
102
+ /**
103
+ * Login with username + password.
104
+ * @example
105
+ * const { token, user, error } = await syntro.login('john', 'pass123');
106
+ */
107
+ async login(username, password) {
108
+ try {
109
+ const res = await this.post("/v1/auth/login", { username, password });
110
+ return res.error ? { success: false, error: res.error } : res;
111
+ } catch (err) {
112
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
113
+ }
114
+ }
115
+ /**
116
+ * Login with email + password.
117
+ */
118
+ async loginWithEmail(email, password) {
119
+ try {
120
+ const res = await this.post("/v1/auth/login", { email, password });
121
+ return res.error ? { success: false, error: res.error } : res;
122
+ } catch (err) {
123
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
124
+ }
125
+ }
126
+ /**
127
+ * Verify a JWT token and get the associated user.
128
+ * @example
129
+ * const { valid, user } = await syntro.verifyToken(token);
130
+ */
131
+ async verifyToken(token) {
132
+ try {
133
+ return await this.post("/v1/auth/verify", { token });
134
+ } catch (err) {
135
+ return { valid: false, error: err instanceof Error ? err.message : "Unknown error" };
136
+ }
137
+ }
138
+ /**
139
+ * Get a ProjectUser by ID.
140
+ * @example
141
+ * const { user } = await syntro.getUser(userId);
142
+ */
143
+ async getUser(userId) {
144
+ try {
145
+ return await this.get(`/v1/auth/users/${userId}`);
146
+ } catch (err) {
147
+ return { error: err instanceof Error ? err.message : "Unknown error" };
148
+ }
149
+ }
150
+ /**
151
+ * Get a user's username by ID.
152
+ * @example
153
+ * const username = await syntro.getUsername(userId);
154
+ */
155
+ async getUsername(userId) {
156
+ const { user, error } = await this.getUser(userId);
157
+ if (error || !user) return null;
158
+ return user.username ?? null;
159
+ }
160
+ /**
161
+ * Get a user's email by ID.
162
+ * @example
163
+ * const email = await syntro.getUserEmail(userId);
164
+ */
165
+ async getUserEmail(userId) {
166
+ const { user, error } = await this.getUser(userId);
167
+ if (error || !user) return null;
168
+ return user.email ?? null;
169
+ }
170
+ /**
171
+ * Get a user's metadata by ID.
172
+ * @example
173
+ * const meta = await syntro.getMetadata(userId);
174
+ * console.log(meta?.plan, meta?.role);
175
+ */
176
+ async getMetadata(userId) {
177
+ const { user, error } = await this.getUser(userId);
178
+ if (error || !user) return null;
179
+ return user.metadata ?? {};
180
+ }
181
+ /**
182
+ * Merge-update a user's metadata. Existing keys are preserved.
183
+ * @example
184
+ * await syntro.updateMetadata(userId, { plan: 'pro', role: 'admin' });
185
+ */
186
+ async updateMetadata(userId, metadata) {
187
+ try {
188
+ return await this.patch(`/v1/auth/users/${userId}/metadata`, metadata);
189
+ } catch (err) {
190
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
191
+ }
192
+ }
193
+ /**
194
+ * Update a user's username or fully replace their metadata.
195
+ * @example
196
+ * await syntro.updateUser(userId, { username: 'new_name' });
197
+ */
198
+ async updateUser(userId, data) {
199
+ try {
200
+ return await this.put(`/v1/auth/users/${userId}`, data);
201
+ } catch (err) {
202
+ return { error: err instanceof Error ? err.message : "Unknown error" };
203
+ }
204
+ }
205
+ /**
206
+ * Delete a ProjectUser permanently.
207
+ * @example
208
+ * const { success } = await syntro.deleteUser(userId);
209
+ */
210
+ async deleteUser(userId) {
211
+ try {
212
+ return await this.del(`/v1/auth/users/${userId}`);
213
+ } catch (err) {
214
+ return { success: false, error: err instanceof Error ? err.message : "Unknown error" };
215
+ }
216
+ }
217
+ /** List users with pagination. */
218
+ async listUsers(options) {
219
+ return this.get("/v1/auth/users", {
220
+ ...options?.limit !== void 0 ? { limit: options.limit } : {},
221
+ ...options?.skip !== void 0 ? { skip: options.skip } : {}
222
+ });
223
+ }
224
+ // ─── Billing ─────────────────────────────────────────────────────────────────
225
+ /**
226
+ * Create a Stripe Checkout payment session.
227
+ * Returns a `url` to redirect the customer to.
228
+ *
229
+ * @param name - Product/payment name
230
+ * @param amount - Amount in cents (e.g. 999 = $9.99)
231
+ * @param options - currency, successUrl, cancelUrl, customerEmail
232
+ *
233
+ * @example
234
+ * const { url, error } = await syntro.createPayment('Pro Plan', 999);
235
+ * if (url) window.location.href = url;
236
+ */
237
+ async createPayment(name, amount, options) {
238
+ try {
239
+ const res = await this.post("/v1/billing/create-payment", {
240
+ name,
241
+ amount,
242
+ currency: options?.currency ?? "usd",
243
+ successUrl: options?.successUrl,
244
+ cancelUrl: options?.cancelUrl,
245
+ customerEmail: options?.customerEmail
246
+ });
247
+ return res.error ? { error: res.error } : res;
248
+ } catch (err) {
249
+ return { error: err instanceof Error ? err.message : "Unknown error" };
250
+ }
251
+ }
252
+ /** List payment transactions. */
253
+ async listTransactions(options) {
254
+ return this.get("/v1/billing/transactions", {
255
+ ...options?.limit !== void 0 ? { limit: options.limit } : {},
256
+ ...options?.skip !== void 0 ? { skip: options.skip } : {},
257
+ ...options?.status ? { status: options.status } : {}
258
+ });
259
+ }
260
+ /**
261
+ * Verify if a customer has paid for a specific product or any product.
262
+ *
263
+ * @example
264
+ * const { paid, transaction } = await syntro.verifyPayment('customer@email.com');
265
+ * if (paid) console.log('Customer paid!', transaction.amount);
266
+ */
267
+ async verifyPayment(customerEmail, options) {
268
+ try {
269
+ return await this.get("/v1/billing/verify-payment", {
270
+ customerEmail,
271
+ ...options?.name ? { name: options.name } : {}
272
+ });
273
+ } catch (err) {
274
+ return { paid: false, error: err instanceof Error ? err.message : "Unknown error" };
275
+ }
276
+ }
277
+ };
278
+ export {
279
+ Syntro
280
+ };
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "syntro-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Official JavaScript/TypeScript SDK for Syntro BaaS",
5
+ "types": "./dist/index.d.ts",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
17
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
18
+ "test": "node dist/index.cjs"
19
+ },
20
+ "keywords": ["syntro", "baas", "sdk", "analytics", "auth"],
21
+ "license": "MIT",
22
+ "devDependencies": {
23
+ "tsup": "^8.4.0",
24
+ "typescript": "^5.8.2",
25
+ "@types/node": "^22.14.0"
26
+ }
27
+ }