@molecule/api-database 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/LICENSE ADDED
@@ -0,0 +1,115 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work.
38
+
39
+ "Derivative Works" shall mean any work, whether in Source or Object
40
+ form, that is based on (or derived from) the Work and for which the
41
+ editorial revisions, annotations, elaborations, or other modifications
42
+ represent, as a whole, an original work of authorship.
43
+
44
+ "Contribution" shall mean any work of authorship, including the
45
+ original version of the Work and any modifications or additions
46
+ to that Work, that is intentionally submitted to the Licensor for
47
+ inclusion in the Work by the copyright owner or by an individual or
48
+ Legal Entity authorized to submit on behalf of the copyright owner.
49
+
50
+ "Contributor" shall mean Licensor and any individual or Legal Entity
51
+ on behalf of whom a Contribution has been received by the Licensor and
52
+ subsequently incorporated within the Work.
53
+
54
+ 2. Grant of Copyright License. Subject to the terms and conditions of
55
+ this License, each Contributor hereby grants to You a perpetual,
56
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
57
+ copyright license to reproduce, prepare Derivative Works of,
58
+ publicly display, publicly perform, sublicense, and distribute the
59
+ Work and such Derivative Works in Source or Object form.
60
+
61
+ 3. Grant of Patent License. Subject to the terms and conditions of
62
+ this License, each Contributor hereby grants to You a perpetual,
63
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
64
+ patent license to make, have made, use, offer to sell, sell, import,
65
+ and otherwise transfer the Work.
66
+
67
+ 4. Redistribution. You may reproduce and distribute copies of the
68
+ Work or Derivative Works thereof in any medium, with or without
69
+ modifications, and in Source or Object form, provided that You
70
+ meet the following conditions:
71
+
72
+ (a) You must give any other recipients of the Work or
73
+ Derivative Works a copy of this License; and
74
+
75
+ (b) You must cause any modified files to carry prominent notices
76
+ stating that You changed the files; and
77
+
78
+ (c) You must retain, in the Source form of any Derivative Works
79
+ that You distribute, all copyright, patent, trademark, and
80
+ attribution notices from the Source form of the Work,
81
+ excluding those notices that do not pertain to any part of
82
+ the Derivative Works; and
83
+
84
+ (d) If the Work includes a "NOTICE" text file as part of its
85
+ distribution, then any Derivative Works that You distribute must
86
+ include a readable copy of the attribution notices contained
87
+ within such NOTICE file.
88
+
89
+ 5. Submission of Contributions.
90
+
91
+ 6. Trademarks. This License does not grant permission to use the trade
92
+ names, trademarks, service marks, or product names of the Licensor.
93
+
94
+ 7. Disclaimer of Warranty. Unless required by applicable law or
95
+ agreed to in writing, Licensor provides the Work on an "AS IS" BASIS,
96
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND.
97
+
98
+ 8. Limitation of Liability. In no event and under no legal theory shall
99
+ any Contributor be liable to You for damages.
100
+
101
+ 9. Accepting Warranty or Additional Liability.
102
+
103
+ Copyright 2026 Molecule Dev, Inc.
104
+
105
+ Licensed under the Apache License, Version 2.0 (the "License");
106
+ you may not use this file except in compliance with the License.
107
+ You may obtain a copy of the License at
108
+
109
+ http://www.apache.org/licenses/LICENSE-2.0
110
+
111
+ Unless required by applicable law or agreed to in writing, software
112
+ distributed under the License is distributed on an "AS IS" BASIS,
113
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
114
+ See the License for the specific language governing permissions and
115
+ limitations under the License.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=browser-guard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-guard.d.ts","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAuBA,OAAO,EAAE,CAAA"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Browser guard — `@molecule/api-database` is SERVER-ONLY.
3
+ *
4
+ * Generated by scripts/gen-browser-guards.mjs (workspace root) — edit THAT, not this.
5
+ * Evaluating a server package in a browser bundle is always an import-graph mistake
6
+ * (node APIs, secrets); without this guard it surfaces as a cryptic downstream crash
7
+ * ("Buffer is not defined") far from the culprit. Throwing here names the package and
8
+ * the fix at the exact moment the client bundle evaluates it. jsdom tests and SSR are
9
+ * unaffected: the throw requires browser globals AND the absence of a node runtime.
10
+ */
11
+ const g = globalThis;
12
+ if (g.window !== undefined && g.document !== undefined && !g.process?.versions?.node) {
13
+ throw new Error('@molecule/api-database is SERVER-ONLY: it was bundled into browser/client code. Import it only ' +
14
+ 'from server code (a server route/function or your API), or dynamic-import it inside ' +
15
+ 'the server handler — never from components or shared client modules, and never ' +
16
+ 'polyfill Buffer/process to silence this.');
17
+ }
18
+ export {};
19
+ //# sourceMappingURL=browser-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-guard.js","sourceRoot":"","sources":["../src/browser-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,GAAG,UAIT,CAAA;AACD,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,CAAC,CAAC,QAAQ,KAAK,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACrF,MAAM,IAAI,KAAK,CACb,iGAAiG;QAC/F,sFAAsF;QACtF,iFAAiF;QACjF,0CAA0C,CAC7C,CAAA;AACH,CAAC"}
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Database core interface for molecule.dev.
3
+ *
4
+ * Defines the standard interface for database providers, including both
5
+ * raw connection pools and the database-agnostic DataStore abstraction.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { setStore, findById, findMany, create, updateById, deleteById } from '@molecule/api-database'
10
+ * import { store } from '@molecule/api-database-postgresql'
11
+ *
12
+ * // Wire the DataStore at app startup
13
+ * setStore(store)
14
+ *
15
+ * // CRUD operations — database-agnostic
16
+ * const user = await findById<User>('users', userId)
17
+ *
18
+ * const activeUsers = await findMany<User>('users', {
19
+ * where: [
20
+ * { field: 'status', operator: '=', value: 'active' },
21
+ * ],
22
+ * orderBy: [{ field: 'createdAt', direction: 'desc' }],
23
+ * limit: 50,
24
+ * })
25
+ *
26
+ * await create('users', { id, username, email })
27
+ * await updateById('users', id, { name: 'New Name' })
28
+ * await deleteById('users', id)
29
+ * ```
30
+ *
31
+ * @remarks
32
+ * Data-access contract — the rules code generators most often get wrong:
33
+ *
34
+ * - **Scope EVERY query by owner — the #1 security rule.** A user must never read or write
35
+ * another user's rows. Add `{ field: 'user_id', operator: '=', value: getUserId(res) }` to
36
+ * every list/read `where`, and for a single-row route load the row and 404 if it isn't the
37
+ * caller's (404, not 403 — don't leak existence). An unscoped `findById`/`updateById`/
38
+ * `deleteById` on a client-supplied id is an IDOR (one user edits another's data). See the
39
+ * `auth` skill.
40
+ * - **CRUD goes through the exported data functions** (`findById`, `findOne`,
41
+ * `findMany`, `count`, `create`, `updateById`, `deleteById`). Filter with a
42
+ * `where` ARRAY of `{ field, operator, value }`:
43
+ * `findMany('plants', { where: [{ field: 'speciesId', operator: '=', value: id }], limit: 50 })`.
44
+ * A bare key object — `findMany('plants', { speciesId: id })` — is NOT a valid
45
+ * `FindManyOptions` and will not filter.
46
+ * - **Raw SQL: import the standalone `query(sql, values)` from this package** —
47
+ * NOT `getStore().query()`. The DataStore has no `query`; raw parameterized
48
+ * query lives on the pool/provider. The store also has NO `.exec()`,
49
+ * `.prepare()`, `.run()`, `.get()`, or `.all()` — those are driver methods
50
+ * (better-sqlite3 / pg) and calling them fails the type-check.
51
+ * - **Atomic multi-write = a MANUAL transaction on ONE connection; the store
52
+ * helpers do NOT join it.** When several writes must be all-or-nothing (move
53
+ * funds between two accounts, create an order AND decrement its stock, accept a
54
+ * booking AND mark the slot taken), acquire a dedicated connection and drive
55
+ * raw SQL on IT: `const conn = await connect()` then
56
+ * `try { await conn.query('BEGIN'); …writes…; await conn.query('COMMIT') }`
57
+ * `catch (e) { await conn.query('ROLLBACK').catch(() => {}); throw e }`
58
+ * `finally { conn.release() }`. **The load-bearing gotcha:** the exported
59
+ * `create`/`updateById`/`findMany`/`deleteById` run on the POOL's own
60
+ * connection and commit INDEPENDENTLY — calling them between your `BEGIN` and
61
+ * `COMMIT` does NOT enroll them in the transaction, so a mid-way failure leaves
62
+ * those writes committed while the rest rolls back (a half-applied transfer).
63
+ * Inside a transaction do every read AND write with `conn.query(sql, values)`
64
+ * on that same `conn`, and ALWAYS `release()` in `finally` — a leaked
65
+ * connection permanently drains the pool. Prefer this only for true multi-row
66
+ * invariants; a single-row update is already atomic via `updateById`.
67
+ * - **Create tables only via timestamped `.sql` files in `migrations/`** (the
68
+ * migration runner applies them on startup) — NEVER programmatically through
69
+ * the store. Seed rows with `create()`.
70
+ * - **Every `id` is a UUID string** (the resource layer sets `id = id || uuid()`
71
+ * on create). So every primary key AND foreign key is a UUID-string column:
72
+ * `id TEXT PRIMARY KEY` (SQLite) / `id UUID PRIMARY KEY` (Postgres); FKs
73
+ * likewise (`user_id TEXT` / `user_id UUID`). NEVER `INTEGER PRIMARY KEY
74
+ * AUTOINCREMENT` or `SERIAL` — inserting a UUID string into an integer key
75
+ * fails at runtime (`datatype mismatch`) and breaks every create endpoint.
76
+ * - **`like` vs `ilike` — pick by who controls the wildcards.** Both are
77
+ * case-insensitive on every bond (identical results across postgresql/
78
+ * mysql/sqlite — see the `WhereCondition['operator']` JSDoc for exactly how
79
+ * each bond gets there). `like` passes `value` through as a raw SQL
80
+ * pattern — YOU write the `%`/`_`; use it when you already built the
81
+ * pattern (e.g. a fixed prefix search `'admin_%'`). `ilike` treats `value`
82
+ * as a literal substring, escapes it, and wraps `%…%` for you — use it for
83
+ * ANY human-typed search box (`{ operator: 'ilike', value: userInput }`),
84
+ * never `like` with a caller-built `%${userInput}%` string.
85
+ *
86
+ * @module
87
+ */
88
+ export * from './browser-guard.js';
89
+ export * from './types.js';
90
+ export * from './provider.js';
91
+ export * from './store.js';
92
+ export * from './store-provider.js';
93
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AAGH,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAG1B,cAAc,eAAe,CAAA;AAG7B,cAAc,YAAY,CAAA;AAC1B,cAAc,qBAAqB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Database core interface for molecule.dev.
3
+ *
4
+ * Defines the standard interface for database providers, including both
5
+ * raw connection pools and the database-agnostic DataStore abstraction.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { setStore, findById, findMany, create, updateById, deleteById } from '@molecule/api-database'
10
+ * import { store } from '@molecule/api-database-postgresql'
11
+ *
12
+ * // Wire the DataStore at app startup
13
+ * setStore(store)
14
+ *
15
+ * // CRUD operations — database-agnostic
16
+ * const user = await findById<User>('users', userId)
17
+ *
18
+ * const activeUsers = await findMany<User>('users', {
19
+ * where: [
20
+ * { field: 'status', operator: '=', value: 'active' },
21
+ * ],
22
+ * orderBy: [{ field: 'createdAt', direction: 'desc' }],
23
+ * limit: 50,
24
+ * })
25
+ *
26
+ * await create('users', { id, username, email })
27
+ * await updateById('users', id, { name: 'New Name' })
28
+ * await deleteById('users', id)
29
+ * ```
30
+ *
31
+ * @remarks
32
+ * Data-access contract — the rules code generators most often get wrong:
33
+ *
34
+ * - **Scope EVERY query by owner — the #1 security rule.** A user must never read or write
35
+ * another user's rows. Add `{ field: 'user_id', operator: '=', value: getUserId(res) }` to
36
+ * every list/read `where`, and for a single-row route load the row and 404 if it isn't the
37
+ * caller's (404, not 403 — don't leak existence). An unscoped `findById`/`updateById`/
38
+ * `deleteById` on a client-supplied id is an IDOR (one user edits another's data). See the
39
+ * `auth` skill.
40
+ * - **CRUD goes through the exported data functions** (`findById`, `findOne`,
41
+ * `findMany`, `count`, `create`, `updateById`, `deleteById`). Filter with a
42
+ * `where` ARRAY of `{ field, operator, value }`:
43
+ * `findMany('plants', { where: [{ field: 'speciesId', operator: '=', value: id }], limit: 50 })`.
44
+ * A bare key object — `findMany('plants', { speciesId: id })` — is NOT a valid
45
+ * `FindManyOptions` and will not filter.
46
+ * - **Raw SQL: import the standalone `query(sql, values)` from this package** —
47
+ * NOT `getStore().query()`. The DataStore has no `query`; raw parameterized
48
+ * query lives on the pool/provider. The store also has NO `.exec()`,
49
+ * `.prepare()`, `.run()`, `.get()`, or `.all()` — those are driver methods
50
+ * (better-sqlite3 / pg) and calling them fails the type-check.
51
+ * - **Atomic multi-write = a MANUAL transaction on ONE connection; the store
52
+ * helpers do NOT join it.** When several writes must be all-or-nothing (move
53
+ * funds between two accounts, create an order AND decrement its stock, accept a
54
+ * booking AND mark the slot taken), acquire a dedicated connection and drive
55
+ * raw SQL on IT: `const conn = await connect()` then
56
+ * `try { await conn.query('BEGIN'); …writes…; await conn.query('COMMIT') }`
57
+ * `catch (e) { await conn.query('ROLLBACK').catch(() => {}); throw e }`
58
+ * `finally { conn.release() }`. **The load-bearing gotcha:** the exported
59
+ * `create`/`updateById`/`findMany`/`deleteById` run on the POOL's own
60
+ * connection and commit INDEPENDENTLY — calling them between your `BEGIN` and
61
+ * `COMMIT` does NOT enroll them in the transaction, so a mid-way failure leaves
62
+ * those writes committed while the rest rolls back (a half-applied transfer).
63
+ * Inside a transaction do every read AND write with `conn.query(sql, values)`
64
+ * on that same `conn`, and ALWAYS `release()` in `finally` — a leaked
65
+ * connection permanently drains the pool. Prefer this only for true multi-row
66
+ * invariants; a single-row update is already atomic via `updateById`.
67
+ * - **Create tables only via timestamped `.sql` files in `migrations/`** (the
68
+ * migration runner applies them on startup) — NEVER programmatically through
69
+ * the store. Seed rows with `create()`.
70
+ * - **Every `id` is a UUID string** (the resource layer sets `id = id || uuid()`
71
+ * on create). So every primary key AND foreign key is a UUID-string column:
72
+ * `id TEXT PRIMARY KEY` (SQLite) / `id UUID PRIMARY KEY` (Postgres); FKs
73
+ * likewise (`user_id TEXT` / `user_id UUID`). NEVER `INTEGER PRIMARY KEY
74
+ * AUTOINCREMENT` or `SERIAL` — inserting a UUID string into an integer key
75
+ * fails at runtime (`datatype mismatch`) and breaks every create endpoint.
76
+ * - **`like` vs `ilike` — pick by who controls the wildcards.** Both are
77
+ * case-insensitive on every bond (identical results across postgresql/
78
+ * mysql/sqlite — see the `WhereCondition['operator']` JSDoc for exactly how
79
+ * each bond gets there). `like` passes `value` through as a raw SQL
80
+ * pattern — YOU write the `%`/`_`; use it when you already built the
81
+ * pattern (e.g. a fixed prefix search `'admin_%'`). `ilike` treats `value`
82
+ * as a literal substring, escapes it, and wraps `%…%` for you — use it for
83
+ * ANY human-typed search box (`{ operator: 'ilike', value: userInput }`),
84
+ * never `like` with a caller-built `%${userInput}%` string.
85
+ *
86
+ * @module
87
+ */
88
+ // Type exports
89
+ export * from './browser-guard.js';
90
+ export * from './types.js';
91
+ // Provider exports (raw query access)
92
+ export * from './provider.js';
93
+ // DataStore exports (abstract CRUD)
94
+ export * from './store.js';
95
+ export * from './store-provider.js';
96
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsFG;AAEH,eAAe;AACf,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA;AAE1B,sCAAsC;AACtC,cAAc,eAAe,CAAA;AAE7B,oCAAoC;AACpC,cAAc,YAAY,CAAA;AAC1B,cAAc,qBAAqB,CAAA"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Database pool bond accessor and convenience functions for raw SQL queries.
3
+ *
4
+ * Bond packages (e.g. `@molecule/api-database-postgresql`) call `setPool()` during setup.
5
+ * Application code uses `query()` for raw SQL, or the DataStore functions from
6
+ * `store-provider.ts` for database-agnostic CRUD.
7
+ *
8
+ * @module
9
+ */
10
+ import type { DatabaseConnection, DatabasePool, QueryResult } from './types.js';
11
+ /**
12
+ * Registers a database connection pool as the active singleton. Called by
13
+ * bond packages during application startup.
14
+ *
15
+ * @param pool - The database pool implementation to bond.
16
+ */
17
+ export declare const setPool: (pool: DatabasePool) => void;
18
+ /**
19
+ * Retrieves the bonded database pool, throwing if none is configured.
20
+ *
21
+ * @returns The bonded database pool.
22
+ * @throws {Error} If no database pool has been bonded.
23
+ */
24
+ export declare const getPool: () => DatabasePool;
25
+ /**
26
+ * Checks whether a database pool is currently bonded.
27
+ *
28
+ * @returns `true` if a database pool is bonded.
29
+ */
30
+ export declare const hasPool: () => boolean;
31
+ /**
32
+ * Executes a parameterized SQL query using the bonded pool.
33
+ *
34
+ * @param text - SQL query text with placeholders (`$1`, `$2`, etc.).
35
+ * @param values - Parameter values corresponding to the placeholders.
36
+ * @returns The query result containing `rows`, `rowCount`, and optional `fields`.
37
+ * @throws {Error} If no database pool has been bonded.
38
+ */
39
+ export declare const query: <T = Record<string, unknown>>(text: string, values?: unknown[]) => Promise<QueryResult<T>>;
40
+ /**
41
+ * Acquires a dedicated connection from the bonded pool. The connection must
42
+ * be released after use by calling `connection.release()`.
43
+ *
44
+ * @returns A database connection that must be released after use.
45
+ * @throws {Error} If no database pool has been bonded.
46
+ */
47
+ export declare const connect: () => Promise<DatabaseConnection>;
48
+ /**
49
+ * Closes all connections in the bonded pool. Call during graceful shutdown.
50
+ *
51
+ * @returns A promise that resolves when all connections have been closed.
52
+ * @throws {Error} If no database pool has been bonded.
53
+ */
54
+ export declare const end: () => Promise<void>;
55
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAK/E;;;;;GAKG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,YAAY,KAAG,IAE5C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,OAAO,QAAO,YAW1B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,QAAO,OAE1B,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,GAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrD,MAAM,MAAM,EACZ,SAAS,OAAO,EAAE,KACjB,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAExB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,QAAa,OAAO,CAAC,kBAAkB,CAE1D,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,GAAG,QAAa,OAAO,CAAC,IAAI,CAExC,CAAA"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Database pool bond accessor and convenience functions for raw SQL queries.
3
+ *
4
+ * Bond packages (e.g. `@molecule/api-database-postgresql`) call `setPool()` during setup.
5
+ * Application code uses `query()` for raw SQL, or the DataStore functions from
6
+ * `store-provider.ts` for database-agnostic CRUD.
7
+ *
8
+ * @module
9
+ */
10
+ import { bond, expectBond, isBonded, require as bondRequire } from '@molecule/api-bond';
11
+ import { t } from '@molecule/api-i18n';
12
+ const BOND_TYPE = 'database';
13
+ expectBond(BOND_TYPE);
14
+ /**
15
+ * Registers a database connection pool as the active singleton. Called by
16
+ * bond packages during application startup.
17
+ *
18
+ * @param pool - The database pool implementation to bond.
19
+ */
20
+ export const setPool = (pool) => {
21
+ bond(BOND_TYPE, pool);
22
+ };
23
+ /**
24
+ * Retrieves the bonded database pool, throwing if none is configured.
25
+ *
26
+ * @returns The bonded database pool.
27
+ * @throws {Error} If no database pool has been bonded.
28
+ */
29
+ export const getPool = () => {
30
+ try {
31
+ return bondRequire(BOND_TYPE);
32
+ }
33
+ catch (error) {
34
+ throw new Error(t('database.error.noProvider', undefined, {
35
+ defaultValue: 'Database pool not configured. Call setPool() first.',
36
+ }), { cause: error });
37
+ }
38
+ };
39
+ /**
40
+ * Checks whether a database pool is currently bonded.
41
+ *
42
+ * @returns `true` if a database pool is bonded.
43
+ */
44
+ export const hasPool = () => {
45
+ return isBonded(BOND_TYPE);
46
+ };
47
+ /**
48
+ * Executes a parameterized SQL query using the bonded pool.
49
+ *
50
+ * @param text - SQL query text with placeholders (`$1`, `$2`, etc.).
51
+ * @param values - Parameter values corresponding to the placeholders.
52
+ * @returns The query result containing `rows`, `rowCount`, and optional `fields`.
53
+ * @throws {Error} If no database pool has been bonded.
54
+ */
55
+ export const query = async (text, values) => {
56
+ return getPool().query(text, values);
57
+ };
58
+ /**
59
+ * Acquires a dedicated connection from the bonded pool. The connection must
60
+ * be released after use by calling `connection.release()`.
61
+ *
62
+ * @returns A database connection that must be released after use.
63
+ * @throws {Error} If no database pool has been bonded.
64
+ */
65
+ export const connect = async () => {
66
+ return getPool().connect();
67
+ };
68
+ /**
69
+ * Closes all connections in the bonded pool. Call during graceful shutdown.
70
+ *
71
+ * @returns A promise that resolves when all connections have been closed.
72
+ * @throws {Error} If no database pool has been bonded.
73
+ */
74
+ export const end = async () => {
75
+ return getPool().end();
76
+ };
77
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,oBAAoB,CAAA;AACvF,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAItC,MAAM,SAAS,GAAG,UAAU,CAAA;AAC5B,UAAU,CAAC,SAAS,CAAC,CAAA;AAErB;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,IAAkB,EAAQ,EAAE;IAClD,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAA;AACvB,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,GAAiB,EAAE;IACxC,IAAI,CAAC;QACH,OAAO,WAAW,CAAe,SAAS,CAAC,CAAA;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,CAAC,CAAC,2BAA2B,EAAE,SAAS,EAAE;YACxC,YAAY,EAAE,qDAAqD;SACpE,CAAC,EACF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;IACH,CAAC;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,GAAY,EAAE;IACnC,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAA;AAC5B,CAAC,CAAA;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG,KAAK,EACxB,IAAY,EACZ,MAAkB,EACO,EAAE;IAC3B,OAAO,OAAO,EAAE,CAAC,KAAK,CAAI,IAAI,EAAE,MAAM,CAAC,CAAA;AACzC,CAAC,CAAA;AAED;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,OAAO,GAAG,KAAK,IAAiC,EAAE;IAC7D,OAAO,OAAO,EAAE,CAAC,OAAO,EAAE,CAAA;AAC5B,CAAC,CAAA;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,IAAmB,EAAE;IAC3C,OAAO,OAAO,EAAE,CAAC,GAAG,EAAE,CAAA;AACxB,CAAC,CAAA"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * DataStore bond accessor and convenience functions for database-agnostic CRUD.
3
+ *
4
+ * These functions delegate to the bonded `DataStore` implementation, allowing
5
+ * resource handlers and application code to perform CRUD operations without
6
+ * knowing which database engine is in use.
7
+ *
8
+ * @module
9
+ */
10
+ import type { DataStore, FindManyOptions, MutationResult, WhereCondition } from './store.js';
11
+ /**
12
+ * Registers a DataStore implementation as the active singleton. Called by
13
+ * bond packages during application startup.
14
+ *
15
+ * @param store - The DataStore implementation to bond.
16
+ */
17
+ export declare const setStore: (store: DataStore) => void;
18
+ /**
19
+ * Retrieves the bonded DataStore, throwing if none is configured.
20
+ *
21
+ * @returns The bonded DataStore implementation.
22
+ * @throws {Error} If no DataStore has been bonded.
23
+ */
24
+ export declare const getStore: () => DataStore;
25
+ /**
26
+ * Checks whether a DataStore is currently bonded.
27
+ *
28
+ * @returns `true` if a DataStore is bonded.
29
+ */
30
+ export declare const hasStore: () => boolean;
31
+ /**
32
+ * Finds a single record by its primary key (`id` column).
33
+ *
34
+ * @param table - The database table name.
35
+ * @param id - The primary key value to look up.
36
+ * @returns The matching record cast to `T`, or `null` if not found.
37
+ * @throws {Error} If no DataStore has been bonded.
38
+ */
39
+ export declare const findById: <T = Record<string, unknown>>(table: string, id: string | number) => Promise<T | null>;
40
+ /**
41
+ * Finds a single record matching the given filter conditions. Returns the
42
+ * first match if multiple rows satisfy the conditions.
43
+ *
44
+ * @param table - The database table name.
45
+ * @param where - Array of filter conditions to match against.
46
+ * @returns The matching record cast to `T`, or `null` if none found.
47
+ * @throws {Error} If no DataStore has been bonded.
48
+ */
49
+ export declare const findOne: <T = Record<string, unknown>>(table: string, where: WhereCondition[]) => Promise<T | null>;
50
+ /**
51
+ * Finds multiple records with optional filtering, sorting, pagination,
52
+ * and column selection.
53
+ *
54
+ * @param table - The database table name.
55
+ * @param options - Query options including `where`, `orderBy`, `limit`, `offset`, and `select`.
56
+ * @returns Array of matching records cast to `T`.
57
+ * @throws {Error} If no DataStore has been bonded.
58
+ */
59
+ export declare const findMany: <T = Record<string, unknown>>(table: string, options?: FindManyOptions) => Promise<T[]>;
60
+ /**
61
+ * Counts records matching the given filter conditions, or all records
62
+ * if no conditions are specified.
63
+ *
64
+ * @param table - The database table name.
65
+ * @param where - Optional filter conditions.
66
+ * @returns The number of matching records.
67
+ * @throws {Error} If no DataStore has been bonded.
68
+ */
69
+ export declare const count: (table: string, where?: WhereCondition[]) => Promise<number>;
70
+ /**
71
+ * Inserts a new record into the table. Returns the inserted row and
72
+ * the number of affected rows (always 1 on success).
73
+ *
74
+ * @param table - The database table name.
75
+ * @param data - The column values to insert as key-value pairs.
76
+ * @returns A `MutationResult` with the inserted row and affected count.
77
+ * @throws {Error} If no DataStore has been bonded.
78
+ */
79
+ export declare const create: <T = Record<string, unknown>>(table: string, data: Record<string, unknown>) => Promise<MutationResult<T>>;
80
+ /**
81
+ * Updates a record identified by its primary key. Returns the updated row
82
+ * and the number of affected rows.
83
+ *
84
+ * @param table - The database table name.
85
+ * @param id - The primary key value of the record to update.
86
+ * @param data - The column values to update as key-value pairs.
87
+ * @returns A `MutationResult` with the updated row and affected count.
88
+ * @throws {Error} If no DataStore has been bonded.
89
+ */
90
+ export declare const updateById: <T = Record<string, unknown>>(table: string, id: string | number, data: Record<string, unknown>) => Promise<MutationResult<T>>;
91
+ /**
92
+ * Updates all records matching the given filter conditions.
93
+ *
94
+ * @param table - The database table name.
95
+ * @param where - Filter conditions to select records to update.
96
+ * @param data - The column values to update as key-value pairs.
97
+ * @returns A `MutationResult` with the affected count.
98
+ * @throws {Error} If no DataStore has been bonded.
99
+ */
100
+ export declare const updateMany: (table: string, where: WhereCondition[], data: Record<string, unknown>) => Promise<MutationResult>;
101
+ /**
102
+ * Deletes a record identified by its primary key.
103
+ *
104
+ * @param table - The database table name.
105
+ * @param id - The primary key value of the record to delete.
106
+ * @returns A `MutationResult` with the affected count (1 if deleted, 0 if not found).
107
+ * @throws {Error} If no DataStore has been bonded.
108
+ */
109
+ export declare const deleteById: (table: string, id: string | number) => Promise<MutationResult>;
110
+ /**
111
+ * Deletes all records matching the given filter conditions.
112
+ *
113
+ * @param table - The database table name.
114
+ * @param where - Filter conditions to select records to delete.
115
+ * @returns A `MutationResult` with the number of deleted rows.
116
+ * @throws {Error} If no DataStore has been bonded.
117
+ */
118
+ export declare const deleteMany: (table: string, where: WhereCondition[]) => Promise<MutationResult>;
119
+ //# sourceMappingURL=store-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-provider.d.ts","sourceRoot":"","sources":["../src/store-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAK5F;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,GAAI,OAAO,SAAS,KAAG,IAE3C,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,QAAO,SAe3B,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,QAAQ,QAAO,OAE3B,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,QAAQ,GAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxD,OAAO,MAAM,EACb,IAAI,MAAM,GAAG,MAAM,KAClB,OAAO,CAAC,CAAC,GAAG,IAAI,CAElB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACvD,OAAO,MAAM,EACb,OAAO,cAAc,EAAE,KACtB,OAAO,CAAC,CAAC,GAAG,IAAI,CAElB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,QAAQ,GAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACxD,OAAO,MAAM,EACb,UAAU,eAAe,KACxB,OAAO,CAAC,CAAC,EAAE,CAEb,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,KAAK,GAAU,OAAO,MAAM,EAAE,QAAQ,cAAc,EAAE,KAAG,OAAO,CAAC,MAAM,CAEnF,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,MAAM,GAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtD,OAAO,MAAM,EACb,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAE3B,CAAA;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,UAAU,GAAU,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1D,OAAO,MAAM,EACb,IAAI,MAAM,GAAG,MAAM,EACnB,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAE3B,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,MAAM,EACb,OAAO,cAAc,EAAE,EACvB,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,OAAO,CAAC,cAAc,CAExB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAU,OAAO,MAAM,EAAE,IAAI,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,cAAc,CAE3F,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GACrB,OAAO,MAAM,EACb,OAAO,cAAc,EAAE,KACtB,OAAO,CAAC,cAAc,CAExB,CAAA"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * DataStore bond accessor and convenience functions for database-agnostic CRUD.
3
+ *
4
+ * These functions delegate to the bonded `DataStore` implementation, allowing
5
+ * resource handlers and application code to perform CRUD operations without
6
+ * knowing which database engine is in use.
7
+ *
8
+ * @module
9
+ */
10
+ import { bond, expectBond, isBonded, require as bondRequire } from '@molecule/api-bond';
11
+ import { t } from '@molecule/api-i18n';
12
+ const BOND_TYPE = 'datastore';
13
+ expectBond(BOND_TYPE);
14
+ /**
15
+ * Registers a DataStore implementation as the active singleton. Called by
16
+ * bond packages during application startup.
17
+ *
18
+ * @param store - The DataStore implementation to bond.
19
+ */
20
+ export const setStore = (store) => {
21
+ bond(BOND_TYPE, store);
22
+ };
23
+ /**
24
+ * Retrieves the bonded DataStore, throwing if none is configured.
25
+ *
26
+ * @returns The bonded DataStore implementation.
27
+ * @throws {Error} If no DataStore has been bonded.
28
+ */
29
+ export const getStore = () => {
30
+ try {
31
+ return bondRequire(BOND_TYPE);
32
+ }
33
+ catch (error) {
34
+ // Same actionable shape as getPool(): the generic bond error suggests
35
+ // `bond('datastore', provider)`, but the API this package documents is
36
+ // `setStore(store)` — point the caller at the actual fix. The locale key
37
+ // already existed in @molecule/api-locales-database; it was never wired.
38
+ throw new Error(t('database.error.storeNotConfigured', undefined, {
39
+ defaultValue: 'DataStore not configured. Call setStore() first.',
40
+ }), { cause: error });
41
+ }
42
+ };
43
+ /**
44
+ * Checks whether a DataStore is currently bonded.
45
+ *
46
+ * @returns `true` if a DataStore is bonded.
47
+ */
48
+ export const hasStore = () => {
49
+ return isBonded(BOND_TYPE);
50
+ };
51
+ /**
52
+ * Finds a single record by its primary key (`id` column).
53
+ *
54
+ * @param table - The database table name.
55
+ * @param id - The primary key value to look up.
56
+ * @returns The matching record cast to `T`, or `null` if not found.
57
+ * @throws {Error} If no DataStore has been bonded.
58
+ */
59
+ export const findById = async (table, id) => {
60
+ return getStore().findById(table, id);
61
+ };
62
+ /**
63
+ * Finds a single record matching the given filter conditions. Returns the
64
+ * first match if multiple rows satisfy the conditions.
65
+ *
66
+ * @param table - The database table name.
67
+ * @param where - Array of filter conditions to match against.
68
+ * @returns The matching record cast to `T`, or `null` if none found.
69
+ * @throws {Error} If no DataStore has been bonded.
70
+ */
71
+ export const findOne = async (table, where) => {
72
+ return getStore().findOne(table, where);
73
+ };
74
+ /**
75
+ * Finds multiple records with optional filtering, sorting, pagination,
76
+ * and column selection.
77
+ *
78
+ * @param table - The database table name.
79
+ * @param options - Query options including `where`, `orderBy`, `limit`, `offset`, and `select`.
80
+ * @returns Array of matching records cast to `T`.
81
+ * @throws {Error} If no DataStore has been bonded.
82
+ */
83
+ export const findMany = async (table, options) => {
84
+ return getStore().findMany(table, options);
85
+ };
86
+ /**
87
+ * Counts records matching the given filter conditions, or all records
88
+ * if no conditions are specified.
89
+ *
90
+ * @param table - The database table name.
91
+ * @param where - Optional filter conditions.
92
+ * @returns The number of matching records.
93
+ * @throws {Error} If no DataStore has been bonded.
94
+ */
95
+ export const count = async (table, where) => {
96
+ return getStore().count(table, where);
97
+ };
98
+ /**
99
+ * Inserts a new record into the table. Returns the inserted row and
100
+ * the number of affected rows (always 1 on success).
101
+ *
102
+ * @param table - The database table name.
103
+ * @param data - The column values to insert as key-value pairs.
104
+ * @returns A `MutationResult` with the inserted row and affected count.
105
+ * @throws {Error} If no DataStore has been bonded.
106
+ */
107
+ export const create = async (table, data) => {
108
+ return getStore().create(table, data);
109
+ };
110
+ /**
111
+ * Updates a record identified by its primary key. Returns the updated row
112
+ * and the number of affected rows.
113
+ *
114
+ * @param table - The database table name.
115
+ * @param id - The primary key value of the record to update.
116
+ * @param data - The column values to update as key-value pairs.
117
+ * @returns A `MutationResult` with the updated row and affected count.
118
+ * @throws {Error} If no DataStore has been bonded.
119
+ */
120
+ export const updateById = async (table, id, data) => {
121
+ return getStore().updateById(table, id, data);
122
+ };
123
+ /**
124
+ * Updates all records matching the given filter conditions.
125
+ *
126
+ * @param table - The database table name.
127
+ * @param where - Filter conditions to select records to update.
128
+ * @param data - The column values to update as key-value pairs.
129
+ * @returns A `MutationResult` with the affected count.
130
+ * @throws {Error} If no DataStore has been bonded.
131
+ */
132
+ export const updateMany = async (table, where, data) => {
133
+ return getStore().updateMany(table, where, data);
134
+ };
135
+ /**
136
+ * Deletes a record identified by its primary key.
137
+ *
138
+ * @param table - The database table name.
139
+ * @param id - The primary key value of the record to delete.
140
+ * @returns A `MutationResult` with the affected count (1 if deleted, 0 if not found).
141
+ * @throws {Error} If no DataStore has been bonded.
142
+ */
143
+ export const deleteById = async (table, id) => {
144
+ return getStore().deleteById(table, id);
145
+ };
146
+ /**
147
+ * Deletes all records matching the given filter conditions.
148
+ *
149
+ * @param table - The database table name.
150
+ * @param where - Filter conditions to select records to delete.
151
+ * @returns A `MutationResult` with the number of deleted rows.
152
+ * @throws {Error} If no DataStore has been bonded.
153
+ */
154
+ export const deleteMany = async (table, where) => {
155
+ return getStore().deleteMany(table, where);
156
+ };
157
+ //# sourceMappingURL=store-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store-provider.js","sourceRoot":"","sources":["../src/store-provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * Abstract data store interface.
3
+ *
4
+ * Database-agnostic CRUD operations. Each database bond
5
+ * (PostgreSQL, MongoDB, DynamoDB, etc.) implements these
6
+ * methods using its native query language.
7
+ *
8
+ * For complex queries not covered by this interface,
9
+ * use the raw DatabasePool.query() method instead.
10
+ *
11
+ * @module
12
+ */
13
+ /**
14
+ * Filter condition for queries.
15
+ */
16
+ export interface WhereCondition {
17
+ field: string;
18
+ operator: '=' | '!=' | '>' | '<' | '>=' | '<=' | 'in' | 'not_in'
19
+ /**
20
+ * Case-insensitive SQL pattern match. `value` is used AS-IS as the raw
21
+ * LIKE pattern: `%` matches any run of characters, `_` matches any
22
+ * single character, and the CALLER is responsible for escaping any
23
+ * literal `%` / `_` / `\` they don't want treated as wildcards. All
24
+ * three first-party bonds (postgresql, mysql, sqlite) implement `like`
25
+ * case-INSENSITIVELY — postgres via `ILIKE`, mysql/sqlite via
26
+ * `LOWER()` on both sides — specifically so this operator has
27
+ * IDENTICAL results across every bond regardless of column collation
28
+ * or `PRAGMA case_sensitive_like`. Prior to this contract the postgres
29
+ * bond escaped the value (making `like` an exact-match operator there
30
+ * only) while sqlite/mysql passed it through raw — the same `{
31
+ * operator: 'like', value: `%${search}%` }` filter silently matched
32
+ * nothing on postgres and worked everywhere else. For human-typed
33
+ * search input where the value should be a literal substring (not a
34
+ * pattern the caller controls), use `ilike` instead — it escapes
35
+ * wildcards and wraps `%…%` for you, so it is injection-safe for
36
+ * arbitrary user text.
37
+ */
38
+ | 'like'
39
+ /**
40
+ * Case-insensitive substring match. The value is treated as a
41
+ * literal substring (LIKE wildcards in the value are escaped) and
42
+ * the bond wraps it with `%…%` to perform a contains-style match.
43
+ * Use this for human-typed search input where case shouldn't matter.
44
+ */
45
+ | 'ilike' | 'is_null' | 'is_not_null';
46
+ value?: unknown;
47
+ }
48
+ /**
49
+ * Column sort order (field name and ascending/descending direction).
50
+ */
51
+ export interface OrderBy {
52
+ field: string;
53
+ direction: 'asc' | 'desc';
54
+ }
55
+ /**
56
+ * Options for findMany queries.
57
+ */
58
+ export interface FindManyOptions {
59
+ where?: WhereCondition[];
60
+ orderBy?: OrderBy[];
61
+ limit?: number;
62
+ offset?: number;
63
+ select?: string[];
64
+ }
65
+ /**
66
+ * Result of a mutation operation.
67
+ */
68
+ export interface MutationResult<T = Record<string, unknown>> {
69
+ /** The affected/returned row, if any. */
70
+ data: T | null;
71
+ /** Number of rows affected. */
72
+ affected: number;
73
+ }
74
+ /**
75
+ * Abstract data store interface.
76
+ *
77
+ * Provides database-agnostic CRUD methods. Each database bond
78
+ * implements these using its native query language.
79
+ */
80
+ export interface DataStore {
81
+ /**
82
+ * Find a single record by its primary key.
83
+ */
84
+ findById<T = Record<string, unknown>>(table: string, id: string | number): Promise<T | null>;
85
+ /**
86
+ * Find a single record matching conditions.
87
+ */
88
+ findOne<T = Record<string, unknown>>(table: string, where: WhereCondition[]): Promise<T | null>;
89
+ /**
90
+ * Find many records with filtering, sorting, and pagination.
91
+ */
92
+ findMany<T = Record<string, unknown>>(table: string, options?: FindManyOptions): Promise<T[]>;
93
+ /**
94
+ * Count records matching conditions.
95
+ */
96
+ count(table: string, where?: WhereCondition[]): Promise<number>;
97
+ /**
98
+ * Insert a new record. Returns the inserted row.
99
+ */
100
+ create<T = Record<string, unknown>>(table: string, data: Record<string, unknown>): Promise<MutationResult<T>>;
101
+ /**
102
+ * Update a record by primary key. Returns the updated row.
103
+ */
104
+ updateById<T = Record<string, unknown>>(table: string, id: string | number, data: Record<string, unknown>): Promise<MutationResult<T>>;
105
+ /**
106
+ * Update records matching conditions.
107
+ */
108
+ updateMany(table: string, where: WhereCondition[], data: Record<string, unknown>): Promise<MutationResult>;
109
+ /**
110
+ * Delete a record by primary key.
111
+ */
112
+ deleteById(table: string, id: string | number): Promise<MutationResult>;
113
+ /**
114
+ * Delete records matching conditions.
115
+ */
116
+ deleteMany(table: string, where: WhereCondition[]): Promise<MutationResult>;
117
+ }
118
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"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"}
package/dist/store.js ADDED
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Abstract data store interface.
3
+ *
4
+ * Database-agnostic CRUD operations. Each database bond
5
+ * (PostgreSQL, MongoDB, DynamoDB, etc.) implements these
6
+ * methods using its native query language.
7
+ *
8
+ * For complex queries not covered by this interface,
9
+ * use the raw DatabasePool.query() method instead.
10
+ *
11
+ * @module
12
+ */
13
+ export {};
14
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../src/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG"}
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Type definitions for database core interface.
3
+ *
4
+ * @module
5
+ */
6
+ /**
7
+ * Query result with rows and metadata.
8
+ */
9
+ export interface QueryResult<T = Record<string, unknown>> {
10
+ /**
11
+ * Array of rows returned by the query.
12
+ */
13
+ rows: T[];
14
+ /**
15
+ * Number of rows affected (for INSERT, UPDATE, DELETE).
16
+ */
17
+ rowCount: number | null;
18
+ /**
19
+ * Column metadata (optional, provider-dependent).
20
+ */
21
+ fields?: Array<{
22
+ name: string;
23
+ dataTypeID?: number;
24
+ }>;
25
+ }
26
+ /**
27
+ * Database connection interface.
28
+ */
29
+ export interface DatabaseConnection {
30
+ /**
31
+ * Executes a parameterized query.
32
+ *
33
+ * @param text - SQL query text with placeholders ($1, $2, etc.)
34
+ * @param values - Parameter values
35
+ */
36
+ query<T = Record<string, unknown>>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
37
+ /**
38
+ * Releases the connection back to the pool.
39
+ */
40
+ release(): void;
41
+ }
42
+ /**
43
+ * Database transaction with commit and rollback (extends DatabaseConnection).
44
+ */
45
+ export interface DatabaseTransaction extends DatabaseConnection {
46
+ /**
47
+ * Commits the transaction.
48
+ */
49
+ commit(): Promise<void>;
50
+ /**
51
+ * Rolls back the transaction.
52
+ */
53
+ rollback(): Promise<void>;
54
+ }
55
+ /**
56
+ * Database pool interface.
57
+ *
58
+ * All database providers must implement this interface.
59
+ */
60
+ export interface DatabasePool {
61
+ /**
62
+ * Executes a parameterized query using a pool connection.
63
+ *
64
+ * @param text - SQL query text with placeholders ($1, $2, etc.)
65
+ * @param values - Parameter values
66
+ */
67
+ query<T = Record<string, unknown>>(text: string, values?: unknown[]): Promise<QueryResult<T>>;
68
+ /**
69
+ * Acquires a connection from the pool.
70
+ */
71
+ connect(): Promise<DatabaseConnection>;
72
+ /**
73
+ * Begins a transaction.
74
+ */
75
+ transaction?(): Promise<DatabaseTransaction>;
76
+ /**
77
+ * Closes all connections in the pool.
78
+ */
79
+ end(): Promise<void>;
80
+ /**
81
+ * Returns pool statistics (optional).
82
+ */
83
+ stats?(): {
84
+ total: number;
85
+ idle: number;
86
+ waiting: number;
87
+ };
88
+ }
89
+ /**
90
+ * Database provider interface.
91
+ */
92
+ export interface DatabaseProvider {
93
+ /**
94
+ * The connection pool.
95
+ */
96
+ pool: DatabasePool;
97
+ /**
98
+ * Creates a new pool with the given configuration.
99
+ */
100
+ createPool?(config: DatabaseConfig): DatabasePool;
101
+ }
102
+ /**
103
+ * Database connection options (host, port, credentials, pool size, timeouts, SSL).
104
+ */
105
+ export interface DatabaseConfig {
106
+ /**
107
+ * Database host.
108
+ */
109
+ host?: string;
110
+ /**
111
+ * Database port.
112
+ */
113
+ port?: number;
114
+ /**
115
+ * Database name.
116
+ */
117
+ database?: string;
118
+ /**
119
+ * Database user.
120
+ */
121
+ user?: string;
122
+ /**
123
+ * Database password.
124
+ */
125
+ password?: string;
126
+ /**
127
+ * Connection string (alternative to individual fields).
128
+ */
129
+ connectionString?: string;
130
+ /**
131
+ * Maximum number of connections in the pool.
132
+ */
133
+ max?: number;
134
+ /**
135
+ * Minimum number of connections in the pool.
136
+ */
137
+ min?: number;
138
+ /**
139
+ * Connection timeout in milliseconds.
140
+ */
141
+ connectionTimeoutMillis?: number;
142
+ /**
143
+ * Idle timeout in milliseconds.
144
+ */
145
+ idleTimeoutMillis?: number;
146
+ /**
147
+ * Enable SSL.
148
+ */
149
+ ssl?: boolean | {
150
+ rejectUnauthorized?: boolean;
151
+ ca?: string;
152
+ key?: string;
153
+ cert?: string;
154
+ };
155
+ }
156
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACtD;;OAEG;IACH,IAAI,EAAE,CAAC,EAAE,CAAA;IAET;;OAEG;IACH,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IAEvB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAC,CAAA;CACH;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;;OAKG;IACH,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IAE7F;;OAEG;IACH,OAAO,IAAI,IAAI,CAAA;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC7D;;OAEG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEvB;;OAEG;IACH,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,YAAY;IAC3B;;;;;OAKG;IACH,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAA;IAE7F;;OAEG;IACH,OAAO,IAAI,OAAO,CAAC,kBAAkB,CAAC,CAAA;IAEtC;;OAEG;IACH,WAAW,CAAC,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAAA;IAE5C;;OAEG;IACH,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IAEpB;;OAEG;IACH,KAAK,CAAC,IAAI;QACR,KAAK,EAAE,MAAM,CAAA;QACb,IAAI,EAAE,MAAM,CAAA;QACZ,OAAO,EAAE,MAAM,CAAA;KAChB,CAAA;CACF;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;OAEG;IACH,IAAI,EAAE,YAAY,CAAA;IAElB;;OAEG;IACH,UAAU,CAAC,CAAC,MAAM,EAAE,cAAc,GAAG,YAAY,CAAA;CAClD;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAA;IAEb;;OAEG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IAEjB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAA;IAEzB;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAA;IAEZ;;OAEG;IACH,uBAAuB,CAAC,EAAE,MAAM,CAAA;IAEhC;;OAEG;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAE1B;;OAEG;IACH,GAAG,CAAC,EACA,OAAO,GACP;QACE,kBAAkB,CAAC,EAAE,OAAO,CAAA;QAC5B,EAAE,CAAC,EAAE,MAAM,CAAA;QACX,GAAG,CAAC,EAAE,MAAM,CAAA;QACZ,IAAI,CAAC,EAAE,MAAM,CAAA;KACd,CAAA;CACN"}
package/dist/types.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Type definitions for database core interface.
3
+ *
4
+ * @module
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@molecule/api-database",
3
+ "version": "1.0.0",
4
+ "description": "Database core interface for molecule.dev",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "test": "vitest run",
11
+ "test:watch": "vitest"
12
+ },
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist"
21
+ ],
22
+ "keywords": [
23
+ "molecule",
24
+ "database",
25
+ "sql",
26
+ "postgresql",
27
+ "mysql"
28
+ ],
29
+ "license": "Apache-2.0",
30
+ "peerDependencies": {
31
+ "@molecule/api-bond": "^1.0.0",
32
+ "@molecule/api-i18n": "^1.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@molecule/api-bond": "1.0.0",
36
+ "@types/node": "26.1.2",
37
+ "typescript": "6.0.3",
38
+ "vitest": "4.1.10"
39
+ },
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/molecule-dev/molecule.git",
43
+ "directory": "packages/api/core/database"
44
+ },
45
+ "homepage": "https://github.com/molecule-dev/molecule/tree/main/packages/api/core/database",
46
+ "bugs": "https://github.com/molecule-dev/molecule/issues",
47
+ "publishConfig": {
48
+ "access": "public"
49
+ }
50
+ }