@firekid/forgedb 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/README.md ADDED
@@ -0,0 +1,191 @@
1
+ # @firekid/forgedb
2
+
3
+ [![npm version](https://img.shields.io/npm/v/@firekid/forgedb?style=flat-square&color=CB3837)](https://npmjs.com/package/@firekid/forgedb)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-22C55E?style=flat-square)](LICENSE)
5
+
6
+ Official JavaScript/TypeScript client for [ForgeDB](https://forgedb.name.ng). Powered by [`@firekid/hurl`](https://npmjs.com/package/@firekid/hurl) — zero extra dependencies.
7
+
8
+ ---
9
+
10
+ ## Install
11
+
12
+ ```bash
13
+ npm install @firekid/forgedb
14
+ pnpm add @firekid/forgedb
15
+ yarn add @firekid/forgedb
16
+ ```
17
+
18
+ ---
19
+
20
+ ## Quick Start
21
+
22
+ ```ts
23
+ import { createClient } from '@firekid/forgedb'
24
+
25
+ const db = createClient({
26
+ projectId: 'your-project-id',
27
+ apiKey: 'sk_live_...',
28
+ })
29
+ ```
30
+
31
+ Get your `projectId` and `apiKey` from the [ForgeDB dashboard](https://forgedb.name.ng/app/keys).
32
+
33
+ ---
34
+
35
+ ## Selecting Rows
36
+
37
+ ```ts
38
+ const { rows, total, totalPages } = await db.from('users').select()
39
+ ```
40
+
41
+ ### Filter
42
+
43
+ ```ts
44
+ // Equality shorthand
45
+ const { rows } = await db.from('users').where('active', true).select()
46
+
47
+ // With operator — supports: = | != | > | < | >= | <= | like | in
48
+ const { rows } = await db.from('users').where('age', '>', 18).select()
49
+
50
+ // Chain multiple filters (AND)
51
+ const { rows } = await db.from('users')
52
+ .where('active', true)
53
+ .where('age', '>=', 18)
54
+ .select()
55
+ ```
56
+
57
+ ### Pagination
58
+
59
+ ```ts
60
+ const { rows } = await db.from('users').page(2).limit(20).select()
61
+ ```
62
+
63
+ ### Search
64
+
65
+ ```ts
66
+ const { rows } = await db.from('users').search('john').select()
67
+ ```
68
+
69
+ ### Sort
70
+
71
+ ```ts
72
+ const { rows } = await db.from('users').orderBy('createdAt', 'desc').select()
73
+ ```
74
+
75
+ ### Get by ID
76
+
77
+ ```ts
78
+ const user = await db.from('users').selectById(1)
79
+ ```
80
+
81
+ ---
82
+
83
+ ## Mutating Rows
84
+
85
+ ### Insert
86
+
87
+ ```ts
88
+ const user = await db.from('users').insert({
89
+ name: 'John Doe',
90
+ email: 'john@example.com',
91
+ })
92
+ ```
93
+
94
+ ### Update
95
+
96
+ ```ts
97
+ const user = await db.from('users').update(1, { name: 'Jane Doe' })
98
+ ```
99
+
100
+ ### Delete
101
+
102
+ ```ts
103
+ await db.from('users').delete(1)
104
+ ```
105
+
106
+ ### Bulk Delete
107
+
108
+ ```ts
109
+ await db.from('users').bulkDelete([1, 2, 3])
110
+ ```
111
+
112
+ ---
113
+
114
+ ## Raw SQL
115
+
116
+ ```ts
117
+ // Single statement
118
+ const result = await db.sql('SELECT * FROM users WHERE age > 18')
119
+ console.log(result.rows)
120
+
121
+ // Multiple statements
122
+ const result = await db.sql(`
123
+ INSERT INTO logs (event) VALUES ('signup');
124
+ SELECT COUNT(*) as total FROM users;
125
+ `)
126
+ console.log(result.statements) // 2
127
+ console.log(result.message) // "2 statements executed · 1 row affected"
128
+ ```
129
+
130
+ ---
131
+
132
+ ## TypeScript
133
+
134
+ ```ts
135
+ interface User {
136
+ id: number
137
+ name: string
138
+ email: string
139
+ age: number
140
+ active: boolean
141
+ createdAt: string
142
+ updatedAt: string
143
+ }
144
+
145
+ const { rows } = await db.from<User>('users').select()
146
+ // rows → User[]
147
+
148
+ const user = await db.from<User>('users').selectById(1)
149
+ // user → User
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Environments
155
+
156
+ The client automatically detects the environment from your API key:
157
+
158
+ | Key prefix | Environment |
159
+ |-------------------|-------------|
160
+ | `sk_live_` / `fk_live_` | `prod` |
161
+ | `sk_test_` / `fk_test_` | `test` |
162
+
163
+ ```ts
164
+ console.log(db.environment) // 'prod' or 'test'
165
+ ```
166
+
167
+ ---
168
+
169
+ ## Error Handling
170
+
171
+ ```ts
172
+ import { createClient, ForgeDBError } from '@firekid/forgedb'
173
+
174
+ try {
175
+ await db.from('users').insert({ name: 'John' })
176
+ } catch (err) {
177
+ if (err instanceof ForgeDBError) {
178
+ console.log(err.message) // human-readable message
179
+ console.log(err.code) // e.g. 'VALIDATION_ERROR'
180
+ console.log(err.status) // HTTP status code
181
+ console.log(err.line) // SQL line (for query errors)
182
+ console.log(err.token) // offending token (for query errors)
183
+ }
184
+ }
185
+ ```
186
+
187
+ ---
188
+
189
+ ## License
190
+
191
+ MIT © [Firekid](https://github.com/Firekid-is-him)
@@ -0,0 +1,131 @@
1
+ import hurl from '@firekid/hurl';
2
+
3
+ interface ForgeDBConfig {
4
+ /** Your ForgeDB project ID */
5
+ projectId: string;
6
+ /** Your API key — sk_live_... / fk_live_... / sk_test_... / fk_test_... */
7
+ apiKey: string;
8
+ /** Override the API base URL (optional) */
9
+ baseUrl?: string;
10
+ }
11
+ interface Row {
12
+ id: number;
13
+ createdAt: string;
14
+ updatedAt: string;
15
+ [key: string]: unknown;
16
+ }
17
+ interface SelectResult<T> {
18
+ rows: T[];
19
+ total: number;
20
+ totalPages: number;
21
+ }
22
+ interface SqlResult {
23
+ rows: Record<string, unknown>[];
24
+ total: number;
25
+ rowsAffected: number;
26
+ executionTime: number;
27
+ message?: string;
28
+ statements?: number;
29
+ }
30
+ type FilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'like' | 'in';
31
+ declare class ForgeDBError extends Error {
32
+ readonly code: string;
33
+ readonly status: number;
34
+ readonly line?: number;
35
+ readonly token?: string;
36
+ constructor(message: string, code: string, status: number, meta?: {
37
+ line?: number;
38
+ token?: string;
39
+ });
40
+ }
41
+ declare class QueryBuilder<T extends Row = Row> {
42
+ private readonly http;
43
+ private readonly projectId;
44
+ private readonly env;
45
+ private readonly table;
46
+ private _filters;
47
+ private _page?;
48
+ private _limit?;
49
+ private _search?;
50
+ private _sort?;
51
+ private _order?;
52
+ constructor(http: ReturnType<typeof hurl.create>, projectId: string, env: string, table: string);
53
+ /** Filter rows — chain multiple for AND. Shorthand: .where('active', true) or .where('age', '>', 18) */
54
+ where(column: string, operatorOrValue: FilterOperator | unknown, value?: unknown): this;
55
+ page(n: number): this;
56
+ limit(n: number): this;
57
+ search(query: string): this;
58
+ orderBy(column: string, direction?: 'asc' | 'desc'): this;
59
+ private get basePath();
60
+ private buildQuery;
61
+ /** Fetch all rows matching the current filters */
62
+ select(): Promise<SelectResult<T>>;
63
+ /** Fetch a single row by ID */
64
+ selectById(id: number | string): Promise<T>;
65
+ /** Insert a new row */
66
+ insert(data: Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>): Promise<T>;
67
+ /** Update a row by ID */
68
+ update(id: number | string, data: Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>): Promise<T>;
69
+ /** Delete a row by ID */
70
+ delete(id: number | string): Promise<{
71
+ success: boolean;
72
+ }>;
73
+ /** Delete multiple rows by IDs */
74
+ bulkDelete(ids: number[]): Promise<{
75
+ success: boolean;
76
+ deleted: number;
77
+ }>;
78
+ }
79
+ declare class ForgeDBClient {
80
+ private readonly http;
81
+ private readonly _projectId;
82
+ private readonly _env;
83
+ constructor(config: ForgeDBConfig);
84
+ /**
85
+ * Select a table to query.
86
+ * @example
87
+ * const { rows } = await db.from('users').select()
88
+ * const user = await db.from<User>('users').where('active', true).limit(10).select()
89
+ */
90
+ from<T extends Row = Row>(table: string): QueryBuilder<T>;
91
+ /**
92
+ * Run a raw SQL query. Supports multiple statements separated by ;
93
+ * @example
94
+ * const result = await db.sql('SELECT * FROM users WHERE age > 18')
95
+ */
96
+ sql(query: string): Promise<SqlResult>;
97
+ get environment(): 'prod' | 'test';
98
+ get projectId(): string;
99
+ }
100
+ /**
101
+ * Create a ForgeDB client.
102
+ *
103
+ * @example
104
+ * import { createClient } from '@firekid/forgedb'
105
+ *
106
+ * const db = createClient({
107
+ * projectId: 'abc123',
108
+ * apiKey: 'sk_live_...',
109
+ * })
110
+ *
111
+ * // Select
112
+ * const { rows } = await db.from('users').select()
113
+ *
114
+ * // Filter + paginate
115
+ * const { rows } = await db.from('users').where('age', '>', 18).page(1).limit(20).select()
116
+ *
117
+ * // Insert
118
+ * const user = await db.from('users').insert({ name: 'John', email: 'john@example.com' })
119
+ *
120
+ * // Update
121
+ * await db.from('users').update(1, { name: 'Jane' })
122
+ *
123
+ * // Delete
124
+ * await db.from('users').delete(1)
125
+ *
126
+ * // Raw SQL
127
+ * const result = await db.sql('SELECT COUNT(*) as total FROM users')
128
+ */
129
+ declare function createClient(config: ForgeDBConfig): ForgeDBClient;
130
+
131
+ export { type FilterOperator, ForgeDBClient, type ForgeDBConfig, ForgeDBError, QueryBuilder, type Row, type SelectResult, type SqlResult, createClient, createClient as default };
@@ -0,0 +1,131 @@
1
+ import hurl from '@firekid/hurl';
2
+
3
+ interface ForgeDBConfig {
4
+ /** Your ForgeDB project ID */
5
+ projectId: string;
6
+ /** Your API key — sk_live_... / fk_live_... / sk_test_... / fk_test_... */
7
+ apiKey: string;
8
+ /** Override the API base URL (optional) */
9
+ baseUrl?: string;
10
+ }
11
+ interface Row {
12
+ id: number;
13
+ createdAt: string;
14
+ updatedAt: string;
15
+ [key: string]: unknown;
16
+ }
17
+ interface SelectResult<T> {
18
+ rows: T[];
19
+ total: number;
20
+ totalPages: number;
21
+ }
22
+ interface SqlResult {
23
+ rows: Record<string, unknown>[];
24
+ total: number;
25
+ rowsAffected: number;
26
+ executionTime: number;
27
+ message?: string;
28
+ statements?: number;
29
+ }
30
+ type FilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'like' | 'in';
31
+ declare class ForgeDBError extends Error {
32
+ readonly code: string;
33
+ readonly status: number;
34
+ readonly line?: number;
35
+ readonly token?: string;
36
+ constructor(message: string, code: string, status: number, meta?: {
37
+ line?: number;
38
+ token?: string;
39
+ });
40
+ }
41
+ declare class QueryBuilder<T extends Row = Row> {
42
+ private readonly http;
43
+ private readonly projectId;
44
+ private readonly env;
45
+ private readonly table;
46
+ private _filters;
47
+ private _page?;
48
+ private _limit?;
49
+ private _search?;
50
+ private _sort?;
51
+ private _order?;
52
+ constructor(http: ReturnType<typeof hurl.create>, projectId: string, env: string, table: string);
53
+ /** Filter rows — chain multiple for AND. Shorthand: .where('active', true) or .where('age', '>', 18) */
54
+ where(column: string, operatorOrValue: FilterOperator | unknown, value?: unknown): this;
55
+ page(n: number): this;
56
+ limit(n: number): this;
57
+ search(query: string): this;
58
+ orderBy(column: string, direction?: 'asc' | 'desc'): this;
59
+ private get basePath();
60
+ private buildQuery;
61
+ /** Fetch all rows matching the current filters */
62
+ select(): Promise<SelectResult<T>>;
63
+ /** Fetch a single row by ID */
64
+ selectById(id: number | string): Promise<T>;
65
+ /** Insert a new row */
66
+ insert(data: Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>): Promise<T>;
67
+ /** Update a row by ID */
68
+ update(id: number | string, data: Partial<Omit<T, 'id' | 'createdAt' | 'updatedAt'>>): Promise<T>;
69
+ /** Delete a row by ID */
70
+ delete(id: number | string): Promise<{
71
+ success: boolean;
72
+ }>;
73
+ /** Delete multiple rows by IDs */
74
+ bulkDelete(ids: number[]): Promise<{
75
+ success: boolean;
76
+ deleted: number;
77
+ }>;
78
+ }
79
+ declare class ForgeDBClient {
80
+ private readonly http;
81
+ private readonly _projectId;
82
+ private readonly _env;
83
+ constructor(config: ForgeDBConfig);
84
+ /**
85
+ * Select a table to query.
86
+ * @example
87
+ * const { rows } = await db.from('users').select()
88
+ * const user = await db.from<User>('users').where('active', true).limit(10).select()
89
+ */
90
+ from<T extends Row = Row>(table: string): QueryBuilder<T>;
91
+ /**
92
+ * Run a raw SQL query. Supports multiple statements separated by ;
93
+ * @example
94
+ * const result = await db.sql('SELECT * FROM users WHERE age > 18')
95
+ */
96
+ sql(query: string): Promise<SqlResult>;
97
+ get environment(): 'prod' | 'test';
98
+ get projectId(): string;
99
+ }
100
+ /**
101
+ * Create a ForgeDB client.
102
+ *
103
+ * @example
104
+ * import { createClient } from '@firekid/forgedb'
105
+ *
106
+ * const db = createClient({
107
+ * projectId: 'abc123',
108
+ * apiKey: 'sk_live_...',
109
+ * })
110
+ *
111
+ * // Select
112
+ * const { rows } = await db.from('users').select()
113
+ *
114
+ * // Filter + paginate
115
+ * const { rows } = await db.from('users').where('age', '>', 18).page(1).limit(20).select()
116
+ *
117
+ * // Insert
118
+ * const user = await db.from('users').insert({ name: 'John', email: 'john@example.com' })
119
+ *
120
+ * // Update
121
+ * await db.from('users').update(1, { name: 'Jane' })
122
+ *
123
+ * // Delete
124
+ * await db.from('users').delete(1)
125
+ *
126
+ * // Raw SQL
127
+ * const result = await db.sql('SELECT COUNT(*) as total FROM users')
128
+ */
129
+ declare function createClient(config: ForgeDBConfig): ForgeDBClient;
130
+
131
+ export { type FilterOperator, ForgeDBClient, type ForgeDBConfig, ForgeDBError, QueryBuilder, type Row, type SelectResult, type SqlResult, createClient, createClient as default };
package/dist/index.js ADDED
@@ -0,0 +1,226 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ ForgeDBClient: () => ForgeDBClient,
34
+ ForgeDBError: () => ForgeDBError,
35
+ QueryBuilder: () => QueryBuilder,
36
+ createClient: () => createClient,
37
+ default: () => index_default
38
+ });
39
+ module.exports = __toCommonJS(index_exports);
40
+ var import_hurl = __toESM(require("@firekid/hurl"));
41
+ var DEFAULT_BASE_URL = "https://api.forgedb.name.ng";
42
+ var ForgeDBError = class extends Error {
43
+ constructor(message, code, status, meta) {
44
+ super(message);
45
+ this.name = "ForgeDBError";
46
+ this.code = code;
47
+ this.status = status;
48
+ this.line = meta?.line;
49
+ this.token = meta?.token;
50
+ }
51
+ };
52
+ function wrapError(err) {
53
+ if (err instanceof import_hurl.HurlError) {
54
+ const body = err.data;
55
+ throw new ForgeDBError(
56
+ body?.error ?? err.message,
57
+ body?.code ?? err.type,
58
+ err.status ?? 500,
59
+ { line: body?.line, token: body?.token }
60
+ );
61
+ }
62
+ throw err;
63
+ }
64
+ var QueryBuilder = class {
65
+ constructor(http, projectId, env, table) {
66
+ this.http = http;
67
+ this.projectId = projectId;
68
+ this.env = env;
69
+ this.table = table;
70
+ this._filters = [];
71
+ }
72
+ /** Filter rows — chain multiple for AND. Shorthand: .where('active', true) or .where('age', '>', 18) */
73
+ where(column, operatorOrValue, value) {
74
+ if (value === void 0) {
75
+ this._filters.push({ column, op: "=", value: operatorOrValue });
76
+ } else {
77
+ this._filters.push({ column, op: operatorOrValue, value });
78
+ }
79
+ return this;
80
+ }
81
+ page(n) {
82
+ this._page = n;
83
+ return this;
84
+ }
85
+ limit(n) {
86
+ this._limit = n;
87
+ return this;
88
+ }
89
+ search(query) {
90
+ this._search = query;
91
+ return this;
92
+ }
93
+ orderBy(column, direction = "asc") {
94
+ this._sort = column;
95
+ this._order = direction;
96
+ return this;
97
+ }
98
+ get basePath() {
99
+ return `/v1/${this.projectId}/${this.env}/${this.table}`;
100
+ }
101
+ buildQuery() {
102
+ const q = {};
103
+ if (this._page) q["page"] = this._page;
104
+ if (this._limit) q["limit"] = this._limit;
105
+ if (this._search) q["search"] = this._search;
106
+ if (this._sort) q["sort"] = this._sort;
107
+ if (this._order) q["order"] = this._order;
108
+ this._filters.forEach((f, i) => {
109
+ q[`filter[${i}][col]`] = f.column;
110
+ q[`filter[${i}][op]`] = f.op;
111
+ q[`filter[${i}][val]`] = String(f.value);
112
+ });
113
+ return q;
114
+ }
115
+ /** Fetch all rows matching the current filters */
116
+ async select() {
117
+ try {
118
+ const res = await this.http.get(this.basePath, { query: this.buildQuery() });
119
+ return res.data;
120
+ } catch (e) {
121
+ wrapError(e);
122
+ }
123
+ }
124
+ /** Fetch a single row by ID */
125
+ async selectById(id) {
126
+ try {
127
+ const res = await this.http.get(`${this.basePath}/${id}`);
128
+ return res.data.row;
129
+ } catch (e) {
130
+ wrapError(e);
131
+ }
132
+ }
133
+ /** Insert a new row */
134
+ async insert(data) {
135
+ try {
136
+ const res = await this.http.post(this.basePath, data);
137
+ return res.data.row;
138
+ } catch (e) {
139
+ wrapError(e);
140
+ }
141
+ }
142
+ /** Update a row by ID */
143
+ async update(id, data) {
144
+ try {
145
+ const res = await this.http.put(`${this.basePath}/${id}`, data);
146
+ return res.data.row;
147
+ } catch (e) {
148
+ wrapError(e);
149
+ }
150
+ }
151
+ /** Delete a row by ID */
152
+ async delete(id) {
153
+ try {
154
+ const res = await this.http.delete(`${this.basePath}/${id}`);
155
+ return res.data;
156
+ } catch (e) {
157
+ wrapError(e);
158
+ }
159
+ }
160
+ /** Delete multiple rows by IDs */
161
+ async bulkDelete(ids) {
162
+ try {
163
+ const res = await this.http.delete(
164
+ `${this.basePath}/bulk-delete`,
165
+ { body: { ids } }
166
+ );
167
+ return res.data;
168
+ } catch (e) {
169
+ wrapError(e);
170
+ }
171
+ }
172
+ };
173
+ var ForgeDBClient = class {
174
+ constructor(config) {
175
+ this._projectId = config.projectId;
176
+ this._env = config.apiKey.includes("_test_") ? "test" : "prod";
177
+ this.http = import_hurl.default.create({
178
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
179
+ auth: { type: "bearer", token: config.apiKey },
180
+ timeout: 15e3,
181
+ retry: { count: 2, backoff: "exponential", on: [500, 502, 503, 504] }
182
+ });
183
+ }
184
+ /**
185
+ * Select a table to query.
186
+ * @example
187
+ * const { rows } = await db.from('users').select()
188
+ * const user = await db.from<User>('users').where('active', true).limit(10).select()
189
+ */
190
+ from(table) {
191
+ return new QueryBuilder(this.http, this._projectId, this._env, table);
192
+ }
193
+ /**
194
+ * Run a raw SQL query. Supports multiple statements separated by ;
195
+ * @example
196
+ * const result = await db.sql('SELECT * FROM users WHERE age > 18')
197
+ */
198
+ async sql(query) {
199
+ try {
200
+ const res = await this.http.post(
201
+ `/v1/${this._projectId}/${this._env}/query`,
202
+ { sql: query }
203
+ );
204
+ return res.data;
205
+ } catch (e) {
206
+ wrapError(e);
207
+ }
208
+ }
209
+ get environment() {
210
+ return this._env;
211
+ }
212
+ get projectId() {
213
+ return this._projectId;
214
+ }
215
+ };
216
+ function createClient(config) {
217
+ return new ForgeDBClient(config);
218
+ }
219
+ var index_default = createClient;
220
+ // Annotate the CommonJS export names for ESM import in node:
221
+ 0 && (module.exports = {
222
+ ForgeDBClient,
223
+ ForgeDBError,
224
+ QueryBuilder,
225
+ createClient
226
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,188 @@
1
+ // src/index.ts
2
+ import hurl, { HurlError } from "@firekid/hurl";
3
+ var DEFAULT_BASE_URL = "https://api.forgedb.name.ng";
4
+ var ForgeDBError = class extends Error {
5
+ constructor(message, code, status, meta) {
6
+ super(message);
7
+ this.name = "ForgeDBError";
8
+ this.code = code;
9
+ this.status = status;
10
+ this.line = meta?.line;
11
+ this.token = meta?.token;
12
+ }
13
+ };
14
+ function wrapError(err) {
15
+ if (err instanceof HurlError) {
16
+ const body = err.data;
17
+ throw new ForgeDBError(
18
+ body?.error ?? err.message,
19
+ body?.code ?? err.type,
20
+ err.status ?? 500,
21
+ { line: body?.line, token: body?.token }
22
+ );
23
+ }
24
+ throw err;
25
+ }
26
+ var QueryBuilder = class {
27
+ constructor(http, projectId, env, table) {
28
+ this.http = http;
29
+ this.projectId = projectId;
30
+ this.env = env;
31
+ this.table = table;
32
+ this._filters = [];
33
+ }
34
+ /** Filter rows — chain multiple for AND. Shorthand: .where('active', true) or .where('age', '>', 18) */
35
+ where(column, operatorOrValue, value) {
36
+ if (value === void 0) {
37
+ this._filters.push({ column, op: "=", value: operatorOrValue });
38
+ } else {
39
+ this._filters.push({ column, op: operatorOrValue, value });
40
+ }
41
+ return this;
42
+ }
43
+ page(n) {
44
+ this._page = n;
45
+ return this;
46
+ }
47
+ limit(n) {
48
+ this._limit = n;
49
+ return this;
50
+ }
51
+ search(query) {
52
+ this._search = query;
53
+ return this;
54
+ }
55
+ orderBy(column, direction = "asc") {
56
+ this._sort = column;
57
+ this._order = direction;
58
+ return this;
59
+ }
60
+ get basePath() {
61
+ return `/v1/${this.projectId}/${this.env}/${this.table}`;
62
+ }
63
+ buildQuery() {
64
+ const q = {};
65
+ if (this._page) q["page"] = this._page;
66
+ if (this._limit) q["limit"] = this._limit;
67
+ if (this._search) q["search"] = this._search;
68
+ if (this._sort) q["sort"] = this._sort;
69
+ if (this._order) q["order"] = this._order;
70
+ this._filters.forEach((f, i) => {
71
+ q[`filter[${i}][col]`] = f.column;
72
+ q[`filter[${i}][op]`] = f.op;
73
+ q[`filter[${i}][val]`] = String(f.value);
74
+ });
75
+ return q;
76
+ }
77
+ /** Fetch all rows matching the current filters */
78
+ async select() {
79
+ try {
80
+ const res = await this.http.get(this.basePath, { query: this.buildQuery() });
81
+ return res.data;
82
+ } catch (e) {
83
+ wrapError(e);
84
+ }
85
+ }
86
+ /** Fetch a single row by ID */
87
+ async selectById(id) {
88
+ try {
89
+ const res = await this.http.get(`${this.basePath}/${id}`);
90
+ return res.data.row;
91
+ } catch (e) {
92
+ wrapError(e);
93
+ }
94
+ }
95
+ /** Insert a new row */
96
+ async insert(data) {
97
+ try {
98
+ const res = await this.http.post(this.basePath, data);
99
+ return res.data.row;
100
+ } catch (e) {
101
+ wrapError(e);
102
+ }
103
+ }
104
+ /** Update a row by ID */
105
+ async update(id, data) {
106
+ try {
107
+ const res = await this.http.put(`${this.basePath}/${id}`, data);
108
+ return res.data.row;
109
+ } catch (e) {
110
+ wrapError(e);
111
+ }
112
+ }
113
+ /** Delete a row by ID */
114
+ async delete(id) {
115
+ try {
116
+ const res = await this.http.delete(`${this.basePath}/${id}`);
117
+ return res.data;
118
+ } catch (e) {
119
+ wrapError(e);
120
+ }
121
+ }
122
+ /** Delete multiple rows by IDs */
123
+ async bulkDelete(ids) {
124
+ try {
125
+ const res = await this.http.delete(
126
+ `${this.basePath}/bulk-delete`,
127
+ { body: { ids } }
128
+ );
129
+ return res.data;
130
+ } catch (e) {
131
+ wrapError(e);
132
+ }
133
+ }
134
+ };
135
+ var ForgeDBClient = class {
136
+ constructor(config) {
137
+ this._projectId = config.projectId;
138
+ this._env = config.apiKey.includes("_test_") ? "test" : "prod";
139
+ this.http = hurl.create({
140
+ baseUrl: config.baseUrl ?? DEFAULT_BASE_URL,
141
+ auth: { type: "bearer", token: config.apiKey },
142
+ timeout: 15e3,
143
+ retry: { count: 2, backoff: "exponential", on: [500, 502, 503, 504] }
144
+ });
145
+ }
146
+ /**
147
+ * Select a table to query.
148
+ * @example
149
+ * const { rows } = await db.from('users').select()
150
+ * const user = await db.from<User>('users').where('active', true).limit(10).select()
151
+ */
152
+ from(table) {
153
+ return new QueryBuilder(this.http, this._projectId, this._env, table);
154
+ }
155
+ /**
156
+ * Run a raw SQL query. Supports multiple statements separated by ;
157
+ * @example
158
+ * const result = await db.sql('SELECT * FROM users WHERE age > 18')
159
+ */
160
+ async sql(query) {
161
+ try {
162
+ const res = await this.http.post(
163
+ `/v1/${this._projectId}/${this._env}/query`,
164
+ { sql: query }
165
+ );
166
+ return res.data;
167
+ } catch (e) {
168
+ wrapError(e);
169
+ }
170
+ }
171
+ get environment() {
172
+ return this._env;
173
+ }
174
+ get projectId() {
175
+ return this._projectId;
176
+ }
177
+ };
178
+ function createClient(config) {
179
+ return new ForgeDBClient(config);
180
+ }
181
+ var index_default = createClient;
182
+ export {
183
+ ForgeDBClient,
184
+ ForgeDBError,
185
+ QueryBuilder,
186
+ createClient,
187
+ index_default as default
188
+ };
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@firekid/forgedb",
3
+ "version": "1.0.0",
4
+ "description": "Official JavaScript/TypeScript client for ForgeDB",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": ["dist", "README.md"],
16
+ "scripts": {
17
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean"
18
+ },
19
+ "keywords": ["forgedb", "database", "firekid", "sdk", "client"],
20
+ "author": "Firekid",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "@firekid/hurl": "latest"
24
+ },
25
+ "devDependencies": {
26
+ "tsup": "^8.0.0",
27
+ "typescript": "^5.0.0"
28
+ }
29
+ }