@ponxa/potatobase-client 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.js ADDED
@@ -0,0 +1,517 @@
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
+ PotatoBaseClient: () => PotatoBaseClient,
24
+ TableClient: () => TableClient,
25
+ between: () => between,
26
+ createClient: () => createClient,
27
+ daysAgo: () => daysAgo,
28
+ hoursAgo: () => hoursAgo,
29
+ lastDays: () => lastDays,
30
+ minutesAgo: () => minutesAgo,
31
+ since: () => since,
32
+ thisMonth: () => thisMonth,
33
+ thisYear: () => thisYear,
34
+ today: () => today,
35
+ until: () => until
36
+ });
37
+ module.exports = __toCommonJS(index_exports);
38
+
39
+ // src/table-client.ts
40
+ var TableClient = class {
41
+ constructor(apiUrl, apiKey, projectId, tableName, debug = false) {
42
+ this.apiUrl = apiUrl;
43
+ this.apiKey = apiKey;
44
+ this.projectId = projectId;
45
+ this.tableName = tableName;
46
+ this.debug = debug;
47
+ }
48
+ async callAPI(procedure, input, method = "GET") {
49
+ const url = `${this.apiUrl}/${procedure}`;
50
+ if (method === "GET") {
51
+ const params = new URLSearchParams({
52
+ input: JSON.stringify(input)
53
+ });
54
+ const response = await fetch(`${url}?${params}`, {
55
+ method: "GET",
56
+ headers: {
57
+ "x-api-key": this.apiKey
58
+ }
59
+ });
60
+ if (!response.ok) {
61
+ const error = await response.text();
62
+ throw new Error(`API error: ${error}`);
63
+ }
64
+ const data = await response.json();
65
+ return data.result?.data || data;
66
+ } else {
67
+ const response = await fetch(url, {
68
+ method: "POST",
69
+ headers: {
70
+ "Content-Type": "application/json",
71
+ "x-api-key": this.apiKey
72
+ },
73
+ body: JSON.stringify(input)
74
+ });
75
+ if (!response.ok) {
76
+ const error = await response.text();
77
+ throw new Error(`API error: ${error}`);
78
+ }
79
+ const data = await response.json();
80
+ return data.result?.data || data;
81
+ }
82
+ }
83
+ /**
84
+ * Query records from the table
85
+ *
86
+ * @param params - Query parameters (index, where, range, limit, cursor)
87
+ * @returns Query result with data and optional cursor
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * // Query by primary key
92
+ * const result = await products.query({ where: { id: 'prod-1' } });
93
+ *
94
+ * // Query by GSI
95
+ * const result = await products.query({
96
+ * index: 'byCategory',
97
+ * where: { category: 'clothing' },
98
+ * limit: 10
99
+ * });
100
+ *
101
+ * // Query with date range
102
+ * const result = await products.query({
103
+ * index: 'byCategory',
104
+ * where: { category: 'clothing' },
105
+ * range: {
106
+ * field: 'createdAt',
107
+ * gte: '2025-01-01',
108
+ * lte: '2025-01-31'
109
+ * }
110
+ * });
111
+ * ```
112
+ */
113
+ async query(params) {
114
+ if (this.debug) {
115
+ console.log(`[TableClient:${this.tableName}] query:`, params);
116
+ }
117
+ try {
118
+ const result = await this.callAPI("query.execute", {
119
+ projectId: this.projectId,
120
+ tableName: this.tableName,
121
+ index: params?.index,
122
+ where: params?.where,
123
+ range: params?.range,
124
+ limit: params?.limit,
125
+ cursor: params?.cursor
126
+ });
127
+ if (this.debug) {
128
+ console.log(`[TableClient:${this.tableName}] query result:`, {
129
+ count: result.data?.length || 0,
130
+ hasMore: !!result.cursor
131
+ });
132
+ }
133
+ return result.data || result;
134
+ } catch (error) {
135
+ console.error(`[TableClient:${this.tableName}] query error:`, error.message);
136
+ throw new Error(`Failed to query ${this.tableName}: ${error.message}`);
137
+ }
138
+ }
139
+ /**
140
+ * Get a single record by ID
141
+ *
142
+ * @param id - Record ID
143
+ * @returns Record or null if not found
144
+ *
145
+ * @example
146
+ * ```typescript
147
+ * const product = await products.get('prod-1');
148
+ * ```
149
+ */
150
+ async get(id) {
151
+ if (this.debug) {
152
+ console.log(`[TableClient:${this.tableName}] get:`, id);
153
+ }
154
+ try {
155
+ const result = await this.callAPI("query.get", {
156
+ projectId: this.projectId,
157
+ tableName: this.tableName,
158
+ recordId: id
159
+ });
160
+ return result || null;
161
+ } catch (error) {
162
+ if (error.message?.includes("not found")) {
163
+ return null;
164
+ }
165
+ console.error(`[TableClient:${this.tableName}] get error:`, error.message);
166
+ throw new Error(`Failed to get record from ${this.tableName}: ${error.message}`);
167
+ }
168
+ }
169
+ /**
170
+ * Insert a new record
171
+ *
172
+ * @param data - Record data (must include 'id' field)
173
+ * @returns Created record
174
+ *
175
+ * @example
176
+ * ```typescript
177
+ * const newProduct = await products.insert({
178
+ * id: 'prod-1',
179
+ * title: 'T-Shirt',
180
+ * price: 29.99,
181
+ * category: 'clothing'
182
+ * });
183
+ * ```
184
+ */
185
+ async insert(data) {
186
+ if (this.debug) {
187
+ console.log(`[TableClient:${this.tableName}] insert:`, data);
188
+ }
189
+ if (!data.id) {
190
+ throw new Error('Record must have an "id" field');
191
+ }
192
+ try {
193
+ const result = await this.callAPI("query.create", {
194
+ projectId: this.projectId,
195
+ tableName: this.tableName,
196
+ data
197
+ }, "POST");
198
+ if (this.debug) {
199
+ console.log(`[TableClient:${this.tableName}] insert success:`, result.id);
200
+ }
201
+ return result;
202
+ } catch (error) {
203
+ console.error(`[TableClient:${this.tableName}] insert error:`, error.message);
204
+ throw new Error(`Failed to insert into ${this.tableName}: ${error.message}`);
205
+ }
206
+ }
207
+ /**
208
+ * Update an existing record
209
+ *
210
+ * @param id - Record ID
211
+ * @param data - Fields to update
212
+ * @returns Updated record
213
+ *
214
+ * @example
215
+ * ```typescript
216
+ * const updated = await products.update('prod-1', {
217
+ * price: 24.99,
218
+ * stock: 100
219
+ * });
220
+ * ```
221
+ */
222
+ async update(id, data) {
223
+ if (this.debug) {
224
+ console.log(`[TableClient:${this.tableName}] update:`, { id, data });
225
+ }
226
+ try {
227
+ const result = await this.callAPI("query.update", {
228
+ projectId: this.projectId,
229
+ tableName: this.tableName,
230
+ recordId: id,
231
+ data
232
+ }, "POST");
233
+ if (this.debug) {
234
+ console.log(`[TableClient:${this.tableName}] update success`);
235
+ }
236
+ return result;
237
+ } catch (error) {
238
+ console.error(`[TableClient:${this.tableName}] update error:`, error.message);
239
+ throw new Error(`Failed to update record in ${this.tableName}: ${error.message}`);
240
+ }
241
+ }
242
+ /**
243
+ * Delete a record
244
+ *
245
+ * @param id - Record ID
246
+ * @returns Success status
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * await products.delete('prod-1');
251
+ * ```
252
+ */
253
+ async delete(id) {
254
+ if (this.debug) {
255
+ console.log(`[TableClient:${this.tableName}] delete:`, id);
256
+ }
257
+ try {
258
+ const result = await this.callAPI("query.delete", {
259
+ projectId: this.projectId,
260
+ tableName: this.tableName,
261
+ recordId: id
262
+ }, "POST");
263
+ if (this.debug) {
264
+ console.log(`[TableClient:${this.tableName}] delete success`);
265
+ }
266
+ return result;
267
+ } catch (error) {
268
+ console.error(`[TableClient:${this.tableName}] delete error:`, error.message);
269
+ throw new Error(`Failed to delete from ${this.tableName}: ${error.message}`);
270
+ }
271
+ }
272
+ /**
273
+ * Create a new record (alias for insert)
274
+ *
275
+ * @param data - Record data (must include 'id' field)
276
+ * @returns Created record
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * const newProduct = await products.create({
281
+ * id: 'prod-1',
282
+ * title: 'T-Shirt',
283
+ * price: 29.99,
284
+ * category: 'clothing'
285
+ * });
286
+ * ```
287
+ */
288
+ async create(data) {
289
+ return this.insert(data);
290
+ }
291
+ /**
292
+ * Alias for query() - chainable method
293
+ *
294
+ * @example
295
+ * ```typescript
296
+ * const items = await products.from().query({ limit: 10 });
297
+ * ```
298
+ */
299
+ from() {
300
+ return this;
301
+ }
302
+ };
303
+
304
+ // src/helpers.ts
305
+ function daysAgo(days) {
306
+ const date = new Date(Date.now() - days * 24 * 60 * 60 * 1e3);
307
+ return date.toISOString();
308
+ }
309
+ function hoursAgo(hours) {
310
+ const date = new Date(Date.now() - hours * 60 * 60 * 1e3);
311
+ return date.toISOString();
312
+ }
313
+ function minutesAgo(minutes) {
314
+ const date = new Date(Date.now() - minutes * 60 * 1e3);
315
+ return date.toISOString();
316
+ }
317
+ function lastDays(field, days) {
318
+ return {
319
+ field,
320
+ gte: daysAgo(days)
321
+ };
322
+ }
323
+ function between(field, startDate, endDate) {
324
+ return {
325
+ field,
326
+ between: [startDate, endDate]
327
+ };
328
+ }
329
+ function since(field, date) {
330
+ return {
331
+ field,
332
+ gte: date
333
+ };
334
+ }
335
+ function until(field, date) {
336
+ return {
337
+ field,
338
+ lte: date
339
+ };
340
+ }
341
+ function thisMonth(field) {
342
+ const now = /* @__PURE__ */ new Date();
343
+ const start = new Date(now.getFullYear(), now.getMonth(), 1);
344
+ const end = new Date(now.getFullYear(), now.getMonth() + 1, 0, 23, 59, 59, 999);
345
+ return {
346
+ field,
347
+ between: [start.toISOString(), end.toISOString()]
348
+ };
349
+ }
350
+ function thisYear(field) {
351
+ const now = /* @__PURE__ */ new Date();
352
+ const start = new Date(now.getFullYear(), 0, 1);
353
+ const end = new Date(now.getFullYear(), 11, 31, 23, 59, 59, 999);
354
+ return {
355
+ field,
356
+ between: [start.toISOString(), end.toISOString()]
357
+ };
358
+ }
359
+ function today(field) {
360
+ const now = /* @__PURE__ */ new Date();
361
+ const start = new Date(now.getFullYear(), now.getMonth(), now.getDate());
362
+ const end = new Date(now.getFullYear(), now.getMonth(), now.getDate(), 23, 59, 59, 999);
363
+ return {
364
+ field,
365
+ between: [start.toISOString(), end.toISOString()]
366
+ };
367
+ }
368
+
369
+ // src/index.ts
370
+ var PotatoBaseClient = class {
371
+ constructor(config) {
372
+ if (!config.projectId) {
373
+ throw new Error("PotatoBase: projectId is required");
374
+ }
375
+ if (!config.apiKey) {
376
+ throw new Error("PotatoBase: apiKey is required");
377
+ }
378
+ this.config = {
379
+ ...config,
380
+ apiUrl: config.apiUrl || "https://api.potatobase.com",
381
+ debug: config.debug || false
382
+ };
383
+ this.tableClients = /* @__PURE__ */ new Map();
384
+ if (this.config.debug) {
385
+ console.log("[PotatoBaseClient] Initializing with config:", {
386
+ projectId: this.config.projectId,
387
+ apiUrl: this.config.apiUrl,
388
+ apiKey: this.config.apiKey.substring(0, 20) + "..."
389
+ });
390
+ }
391
+ }
392
+ /**
393
+ * Access a table by name
394
+ *
395
+ * Returns a TableClient instance for performing CRUD operations.
396
+ * TableClient instances are cached for performance.
397
+ *
398
+ * @param tableName - Name of the table
399
+ * @returns TableClient for the specified table
400
+ *
401
+ * @example
402
+ * ```typescript
403
+ * const products = pb.table('products');
404
+ * const items = await products.query({ limit: 10 });
405
+ * ```
406
+ */
407
+ table(tableName) {
408
+ const key = tableName;
409
+ if (this.tableClients.has(key)) {
410
+ return this.tableClients.get(key);
411
+ }
412
+ if (this.config.debug) {
413
+ console.log(`[PotatoBaseClient] Creating TableClient for: ${key}`);
414
+ }
415
+ const tableClient = new TableClient(
416
+ this.config.apiUrl,
417
+ this.config.apiKey,
418
+ this.config.projectId,
419
+ key,
420
+ this.config.debug
421
+ );
422
+ this.tableClients.set(key, tableClient);
423
+ return tableClient;
424
+ }
425
+ /**
426
+ * Generate TypeScript types for your project
427
+ *
428
+ * Fetches the current schema and generates TypeScript interface definitions.
429
+ * Save the result to a .d.ts file in your project.
430
+ *
431
+ * @returns Generated TypeScript types as string
432
+ *
433
+ * @example
434
+ * ```typescript
435
+ * const types = await pb.generateTypes();
436
+ * fs.writeFileSync('./types/database.d.ts', types);
437
+ * ```
438
+ */
439
+ async generateTypes() {
440
+ if (this.config.debug) {
441
+ console.log("[PotatoBaseClient] Generating types for project:", this.config.projectId);
442
+ }
443
+ try {
444
+ const params = new URLSearchParams({
445
+ input: JSON.stringify(this.config.projectId)
446
+ });
447
+ const response = await fetch(`${this.config.apiUrl}/projects.generateTypes?${params}`, {
448
+ method: "GET",
449
+ headers: {
450
+ "x-api-key": this.config.apiKey
451
+ }
452
+ });
453
+ if (!response.ok) {
454
+ throw new Error(`Failed to generate types: ${response.statusText}`);
455
+ }
456
+ const data = await response.json();
457
+ const result = data.result?.data || data;
458
+ if (this.config.debug) {
459
+ console.log("[PotatoBaseClient] Types generated:", {
460
+ length: result.types.length
461
+ });
462
+ }
463
+ return result.types;
464
+ } catch (error) {
465
+ console.error("[PotatoBaseClient] Failed to generate types:", error.message);
466
+ throw new Error(`Failed to generate types: ${error.message}`);
467
+ }
468
+ }
469
+ /**
470
+ * Get the project ID
471
+ */
472
+ getProjectId() {
473
+ return this.config.projectId;
474
+ }
475
+ /**
476
+ * Get the API URL
477
+ */
478
+ getApiUrl() {
479
+ return this.config.apiUrl;
480
+ }
481
+ /**
482
+ * Clear all cached table clients
483
+ *
484
+ * Useful for testing or when you want to force fresh clients.
485
+ */
486
+ clearCache() {
487
+ if (this.config.debug) {
488
+ console.log("[PotatoBaseClient] Clearing table client cache");
489
+ }
490
+ this.tableClients.clear();
491
+ }
492
+ /**
493
+ * Enable/disable debug logging
494
+ */
495
+ setDebug(enabled) {
496
+ this.config.debug = enabled;
497
+ }
498
+ };
499
+ function createClient(config) {
500
+ return new PotatoBaseClient(config);
501
+ }
502
+ // Annotate the CommonJS export names for ESM import in node:
503
+ 0 && (module.exports = {
504
+ PotatoBaseClient,
505
+ TableClient,
506
+ between,
507
+ createClient,
508
+ daysAgo,
509
+ hoursAgo,
510
+ lastDays,
511
+ minutesAgo,
512
+ since,
513
+ thisMonth,
514
+ thisYear,
515
+ today,
516
+ until
517
+ });