kinetic-sql 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 +23 -0
- package/README.md +146 -0
- package/dist/cli/generate.cjs +43 -0
- package/dist/cli/generate.d.cts +1 -0
- package/dist/cli/generate.d.ts +1 -0
- package/dist/cli/generate.js +43 -0
- package/dist/index.cjs +23 -0
- package/dist/index.d.cts +65 -0
- package/dist/index.d.ts +65 -0
- package/dist/index.js +23 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kapil Kumar
|
|
4
|
+
Github Link: https://github.com/serial-committer
|
|
5
|
+
Portfolio Link: https://the-debug-knight.bio
|
|
6
|
+
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# ⚡ Kinetic SQL (`k-sql`)
|
|
2
|
+
|
|
3
|
+
> **The "Tailwind" of Database Clients.**
|
|
4
|
+
> Zero Config. Full Autocomplete. Realtime by default.
|
|
5
|
+
|
|
6
|
+
Kinetic SQL is a next-gen Node.js client that wraps **PostgreSQL** and **MySQL** with a developer experience similar to Supabase, but for your own backend.
|
|
7
|
+
|
|
8
|
+

|
|
9
|
+
## ✨ Features
|
|
10
|
+
|
|
11
|
+
- **🔮 Invisible Type Safety:** Run `npx k-sql gen` and your entire database schema is auto-injected into your client. No manual interfaces.
|
|
12
|
+
- **⚡ Realtime Subscriptions:** Listen to table changes (`INSERT`, `UPDATE`) with one line of code.
|
|
13
|
+
- **🛡️ RPC Wrapper:** Call Stored Procedures as native JavaScript functions.
|
|
14
|
+
- **🔌 Connection Pooling:** Built-in management for high-scale apps.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## 🚀 Quick Start
|
|
19
|
+
|
|
20
|
+
### 1. Install
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
|
|
24
|
+
# For PostgreSQL
|
|
25
|
+
|
|
26
|
+
npm install kinetic-sql drizzle-orm postgres
|
|
27
|
+
|
|
28
|
+
# For MySQL
|
|
29
|
+
|
|
30
|
+
npm install kinetic-sql drizzle-orm mysql2 @rodrigogs/mysql-events
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 2. Initialize
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
import { KineticClient } from 'kinetic-sql';
|
|
37
|
+
|
|
38
|
+
// Connects using your DATABASE_URL env var by default
|
|
39
|
+
const db = await KineticClient.create({
|
|
40
|
+
type: 'pg', // or 'mysql'
|
|
41
|
+
connectionString: process.env.DATABASE_URL,
|
|
42
|
+
realtimeEnabled: true
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 3. Generate Types (The Magic)
|
|
47
|
+
|
|
48
|
+
Run this command in your terminal. It reads your DB and patches the library automatically.
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
|
|
52
|
+
# PostgreSQL (Default)
|
|
53
|
+
|
|
54
|
+
npx k-sql gen --connection "postgres://..."
|
|
55
|
+
|
|
56
|
+
# MySQL
|
|
57
|
+
|
|
58
|
+
npx k-sql gen --type mysql --host localhost --user root --db mydb
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## 📚 Usage
|
|
64
|
+
|
|
65
|
+
### Realtime Subscriptions ✨
|
|
66
|
+
|
|
67
|
+
#### Listen to database events without setting up WebSockets.
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
// 'tasks' is auto-completed!
|
|
71
|
+
const sub = await db.subscribe('tasks', (event) => {
|
|
72
|
+
console.log(event.action); // 'INSERT' | 'UPDATE'
|
|
73
|
+
console.log(event.data.title); // Typed!
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Later...
|
|
77
|
+
await sub.unsubscribe();
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Calling RPC Functions ✨
|
|
81
|
+
|
|
82
|
+
#### Call stored procedures as native JS methods.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// 'add_todo' is auto-completed!
|
|
86
|
+
const { data, error } = await db.rpc('add_todo', {
|
|
87
|
+
p_title: "Build cool app", // Param names are checked!
|
|
88
|
+
p_user_id: 123
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Standard Queries (via Drizzle) ✨
|
|
93
|
+
|
|
94
|
+
#### We expose the full power of Drizzle ORM under the hood.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import { sql, eq } from 'kinetic-sql';
|
|
98
|
+
|
|
99
|
+
const users = await db.orm
|
|
100
|
+
.select()
|
|
101
|
+
.from(sql`users`)
|
|
102
|
+
.where(eq(sql`id`, 1));
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## ⚙️ Configuration
|
|
108
|
+
|
|
109
|
+
### PostgreSQL
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
const db = await KineticClient.create({
|
|
113
|
+
type: 'pg',
|
|
114
|
+
host: 'localhost',
|
|
115
|
+
port: 5432,
|
|
116
|
+
user: 'postgres',
|
|
117
|
+
password: 'password',
|
|
118
|
+
database: 'mydb',
|
|
119
|
+
realtimeEnabled: true
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### MySQL
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
const db = await KineticClient.create({
|
|
127
|
+
type: 'mysql',
|
|
128
|
+
host: 'localhost',
|
|
129
|
+
port: 3306,
|
|
130
|
+
user: 'root',
|
|
131
|
+
password: 'password',
|
|
132
|
+
database: 'mydb',
|
|
133
|
+
realtimeEnabled: true // Requires Binary Logging enabled on server
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## ⚠️ Requirements
|
|
138
|
+
|
|
139
|
+
- **Node.js:** 18+
|
|
140
|
+
- **PostgreSQL:** 12+ (Native `LISTEN/NOTIFY` used)
|
|
141
|
+
- **MySQL:** 5.7+
|
|
142
|
+
- *Note:* For Realtime features, your MySQL server must have **Binary Logging** enabled (`log_bin = ON`).
|
|
143
|
+
|
|
144
|
+
## 📄 License
|
|
145
|
+
|
|
146
|
+
MIT
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";var h=Object.create;var _=Object.defineProperty;var N=Object.getOwnPropertyDescriptor;var S=Object.getOwnPropertyNames;var O=Object.getPrototypeOf,M=Object.prototype.hasOwnProperty;var A=(t,n,e,r)=>{if(n&&typeof n=="object"||typeof n=="function")for(let s of S(n))!M.call(t,s)&&s!==e&&_(t,s,{get:()=>n[s],enumerable:!(r=N(n,s))||r.enumerable});return t};var u=(t,n,e)=>(e=t!=null?h(O(t)):{},A(n||!t||!t.__esModule?_(e,"default",{value:t,enumerable:!0}):e,t));var T=()=>typeof document>"u"?new URL(`file:${__filename}`).href:document.currentScript&&document.currentScript.tagName.toUpperCase()==="SCRIPT"?document.currentScript.src:new URL("main.js",document.baseURI).href,g=T();var b=u(require("postgres"),1),y=u(require("mysql2/promise"),1),l=u(require("fs"),1),c=u(require("path"),1),f=require("url"),B=(0,f.fileURLToPath)(g),L={text:"string",varchar:"string",char:"string",uuid:"string",int2:"number",int4:"number",int8:"number",float4:"number",float8:"number",numeric:"number",bool:"boolean",json:"any",jsonb:"any",date:"string",timestamp:"string",timestamptz:"string",bytea:"Buffer",_text:"string[]",_int4:"number[]"},w={varchar:"string",char:"string",text:"string",longtext:"string",int:"number",tinyint:"number",smallint:"number",mediumint:"number",bigint:"number",float:"number",double:"number",decimal:"number",datetime:"string",date:"string",timestamp:"string",json:"any",blob:"Buffer",longblob:"Buffer",tinyint1:"boolean"};function I(){let t=process.argv.slice(2),n={type:"pg"};for(let e=0;e<t.length;e++){let r=t[e];if(r.startsWith("--")){let s=r.replace(/^--/,"");if(s.includes("=")){let[i,a]=s.split("=");n[i]=a}else t[e+1]&&!t[e+1].startsWith("--")&&(n[s]=t[e+1],e++)}}return n}function P(t){let n=t;for(;n!==c.default.parse(n).root;){if(l.default.existsSync(c.default.join(n,"package.json")))return n;n=c.default.dirname(n)}return t}async function C(t){let n;t.connection?n=(0,b.default)(t.connection,{max:1}):n=(0,b.default)({host:t.host||"localhost",port:Number(t.port)||5432,user:t.user||"postgres",password:t.password||"",database:t.db||"postgres",max:1});try{let e=await n`
|
|
3
|
+
SELECT table_name, column_name, udt_name, is_nullable
|
|
4
|
+
FROM information_schema.columns
|
|
5
|
+
WHERE table_schema = 'public'
|
|
6
|
+
ORDER BY table_name, ordinal_position;`,r=await n`
|
|
7
|
+
SELECT p.proname as function_name,
|
|
8
|
+
pg_get_function_arguments(p.oid) as args_raw,
|
|
9
|
+
t.typname as return_type
|
|
10
|
+
FROM pg_proc p
|
|
11
|
+
JOIN pg_type t ON p.prorettype = t.oid
|
|
12
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
13
|
+
WHERE n.nspname = 'public';`;return{columns:e,functions:r,typeMap:L}}finally{await n.end()}}async function U(t){let n=await y.default.createConnection({host:t.host||"localhost",user:t.user||"root",password:t.password||"",database:t.db,port:Number(t.port)||3306});try{let[e]=await n.execute(`
|
|
14
|
+
SELECT TABLE_NAME as table_name, COLUMN_NAME as column_name, DATA_TYPE as udt_name, IS_NULLABLE as is_nullable
|
|
15
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
16
|
+
WHERE TABLE_SCHEMA = ?
|
|
17
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION`,[t.db]),[r]=await n.execute(`
|
|
18
|
+
SELECT ROUTINE_NAME as function_name
|
|
19
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
20
|
+
WHERE ROUTINE_SCHEMA = ?
|
|
21
|
+
AND ROUTINE_TYPE = 'PROCEDURE'`,[t.db]);return{columns:e,functions:r,typeMap:w}}finally{await n.end()}}async function x(){let t=I();console.log(`\u{1F52E} Kinetic SQL: Introspecting ${t.type==="mysql"?"MySQL":"PostgreSQL"}...`);try{let n;t.type==="mysql"?n=await U(t):n=await C(t);let{columns:e,functions:r,typeMap:s}=n,i={};for(let o of e){i[o.table_name]||(i[o.table_name]=[]);let m=s[o.udt_name]||"any";t.type==="mysql"&&o.udt_name==="tinyint"&&(m="number");let R=o.is_nullable==="YES"?"| null":"";i[o.table_name].push(` ${o.column_name}: ${m}${R};`)}let a=`// Auto-generated by Kinetic SQL \u26A0\uFE0F Do NOT import this file manually.
|
|
22
|
+
|
|
23
|
+
import 'kinetic-sql';
|
|
24
|
+
|
|
25
|
+
declare module 'kinetic-sql' {
|
|
26
|
+
export interface Register {
|
|
27
|
+
schema: {
|
|
28
|
+
tables: {
|
|
29
|
+
`;for(let[o,m]of Object.entries(i))a+=` ${o}: {
|
|
30
|
+
${m.join(`
|
|
31
|
+
`)}
|
|
32
|
+
};
|
|
33
|
+
`;a+=` };
|
|
34
|
+
functions: {
|
|
35
|
+
`;for(let o of r)a+=` ${o.function_name}: {
|
|
36
|
+
args: Record<string, any>;
|
|
37
|
+
returns: any;
|
|
38
|
+
};
|
|
39
|
+
`;a+=` };
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;let E=P(process.cwd()),p=c.default.resolve(E,"kinetic-schema","kinetic-env.d.ts"),d=c.default.dirname(p);l.default.existsSync(d)||l.default.mkdirSync(d,{recursive:!0}),l.default.writeFileSync(p,a),console.log(`\u2705 Generated types at: ${p}`)}catch(n){console.error("\u274C Generation failed:",n),process.exit(1)}}x().then(()=>console.log("Auto-complete types generated successfully \u2728"));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import b from"postgres";import g from"mysql2/promise";import m from"fs";import c from"path";import{fileURLToPath as d}from"url";var P=d(import.meta.url),f={text:"string",varchar:"string",char:"string",uuid:"string",int2:"number",int4:"number",int8:"number",float4:"number",float8:"number",numeric:"number",bool:"boolean",json:"any",jsonb:"any",date:"string",timestamp:"string",timestamptz:"string",bytea:"Buffer",_text:"string[]",_int4:"number[]"},E={varchar:"string",char:"string",text:"string",longtext:"string",int:"number",tinyint:"number",smallint:"number",mediumint:"number",bigint:"number",float:"number",double:"number",decimal:"number",datetime:"string",date:"string",timestamp:"string",json:"any",blob:"Buffer",longblob:"Buffer",tinyint1:"boolean"};function R(){let n=process.argv.slice(2),t={type:"pg"};for(let e=0;e<n.length;e++){let r=n[e];if(r.startsWith("--")){let i=r.replace(/^--/,"");if(i.includes("=")){let[a,o]=i.split("=");t[a]=o}else n[e+1]&&!n[e+1].startsWith("--")&&(t[i]=n[e+1],e++)}}return t}function N(n){let t=n;for(;t!==c.parse(t).root;){if(m.existsSync(c.join(t,"package.json")))return t;t=c.dirname(t)}return n}async function O(n){let t;n.connection?t=b(n.connection,{max:1}):t=b({host:n.host||"localhost",port:Number(n.port)||5432,user:n.user||"postgres",password:n.password||"",database:n.db||"postgres",max:1});try{let e=await t`
|
|
3
|
+
SELECT table_name, column_name, udt_name, is_nullable
|
|
4
|
+
FROM information_schema.columns
|
|
5
|
+
WHERE table_schema = 'public'
|
|
6
|
+
ORDER BY table_name, ordinal_position;`,r=await t`
|
|
7
|
+
SELECT p.proname as function_name,
|
|
8
|
+
pg_get_function_arguments(p.oid) as args_raw,
|
|
9
|
+
t.typname as return_type
|
|
10
|
+
FROM pg_proc p
|
|
11
|
+
JOIN pg_type t ON p.prorettype = t.oid
|
|
12
|
+
JOIN pg_namespace n ON p.pronamespace = n.oid
|
|
13
|
+
WHERE n.nspname = 'public';`;return{columns:e,functions:r,typeMap:f}}finally{await t.end()}}async function h(n){let t=await g.createConnection({host:n.host||"localhost",user:n.user||"root",password:n.password||"",database:n.db,port:Number(n.port)||3306});try{let[e]=await t.execute(`
|
|
14
|
+
SELECT TABLE_NAME as table_name, COLUMN_NAME as column_name, DATA_TYPE as udt_name, IS_NULLABLE as is_nullable
|
|
15
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
16
|
+
WHERE TABLE_SCHEMA = ?
|
|
17
|
+
ORDER BY TABLE_NAME, ORDINAL_POSITION`,[n.db]),[r]=await t.execute(`
|
|
18
|
+
SELECT ROUTINE_NAME as function_name
|
|
19
|
+
FROM INFORMATION_SCHEMA.ROUTINES
|
|
20
|
+
WHERE ROUTINE_SCHEMA = ?
|
|
21
|
+
AND ROUTINE_TYPE = 'PROCEDURE'`,[n.db]);return{columns:e,functions:r,typeMap:E}}finally{await t.end()}}async function M(){let n=R();console.log(`\u{1F52E} Kinetic SQL: Introspecting ${n.type==="mysql"?"MySQL":"PostgreSQL"}...`);try{let t;n.type==="mysql"?t=await h(n):t=await O(n);let{columns:e,functions:r,typeMap:i}=t,a={};for(let s of e){a[s.table_name]||(a[s.table_name]=[]);let l=i[s.udt_name]||"any";n.type==="mysql"&&s.udt_name==="tinyint"&&(l="number");let _=s.is_nullable==="YES"?"| null":"";a[s.table_name].push(` ${s.column_name}: ${l}${_};`)}let o=`// Auto-generated by Kinetic SQL \u26A0\uFE0F Do NOT import this file manually.
|
|
22
|
+
|
|
23
|
+
import 'kinetic-sql';
|
|
24
|
+
|
|
25
|
+
declare module 'kinetic-sql' {
|
|
26
|
+
export interface Register {
|
|
27
|
+
schema: {
|
|
28
|
+
tables: {
|
|
29
|
+
`;for(let[s,l]of Object.entries(a))o+=` ${s}: {
|
|
30
|
+
${l.join(`
|
|
31
|
+
`)}
|
|
32
|
+
};
|
|
33
|
+
`;o+=` };
|
|
34
|
+
functions: {
|
|
35
|
+
`;for(let s of r)o+=` ${s.function_name}: {
|
|
36
|
+
args: Record<string, any>;
|
|
37
|
+
returns: any;
|
|
38
|
+
};
|
|
39
|
+
`;o+=` };
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
`;let y=N(process.cwd()),u=c.resolve(y,"kinetic-schema","kinetic-env.d.ts"),p=c.dirname(u);m.existsSync(p)||m.mkdirSync(p,{recursive:!0}),m.writeFileSync(u,o),console.log(`\u2705 Generated types at: ${u}`)}catch(t){console.error("\u274C Generation failed:",t),process.exit(1)}}M().then(()=>console.log("Auto-complete types generated successfully \u2728"));
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";var S=Object.create;var p=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var O=Object.getOwnPropertyNames;var T=Object.getPrototypeOf,N=Object.prototype.hasOwnProperty;var v=(t,e)=>{for(var r in e)p(t,r,{get:e[r],enumerable:!0})},m=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of O(e))!N.call(t,i)&&i!==r&&p(t,i,{get:()=>e[i],enumerable:!(n=f(e,i))||n.enumerable});return t};var C=(t,e,r)=>(r=t!=null?S(T(t)):{},m(e||!t||!t.__esModule?p(r,"default",{value:t,enumerable:!0}):r,t)),x=t=>m(p({},"__esModule",{value:!0}),t);var D={};v(D,{KineticClient:()=>d,KineticError:()=>a,and:()=>s.and,asc:()=>s.asc,desc:()=>s.desc,eq:()=>s.eq,or:()=>s.or,sql:()=>s.sql});module.exports=x(D);var c=C(require("postgres"),1);var a=class t extends Error{code;details;constructor(e,r,n){super(r),this.name="KineticError",this.code=e,this.details=n,Object.setPrototypeOf(this,t.prototype)}};var g=`
|
|
2
|
+
CREATE OR REPLACE FUNCTION notify_changes() RETURNS TRIGGER AS $$
|
|
3
|
+
DECLARE
|
|
4
|
+
payload json;
|
|
5
|
+
BEGIN
|
|
6
|
+
payload = json_build_object(
|
|
7
|
+
'table', TG_TABLE_NAME,
|
|
8
|
+
'action', TG_OP,
|
|
9
|
+
'data', COALESCE(NEW, OLD)
|
|
10
|
+
);
|
|
11
|
+
PERFORM pg_notify('table_events', payload::text);
|
|
12
|
+
RETURN NEW;
|
|
13
|
+
END;
|
|
14
|
+
$$ LANGUAGE plpgsql;`,b=t=>`
|
|
15
|
+
DO $$
|
|
16
|
+
BEGIN
|
|
17
|
+
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'watch_${t}') THEN
|
|
18
|
+
CREATE TRIGGER watch_${t}
|
|
19
|
+
AFTER INSERT OR UPDATE OR DELETE ON "${t}"
|
|
20
|
+
FOR EACH ROW EXECUTE FUNCTION notify_changes();
|
|
21
|
+
END IF;
|
|
22
|
+
END
|
|
23
|
+
$$;`;var l=class{sql;config;realtimeEnabled;constructor(e){if(this.config=e,this.realtimeEnabled=e.realtimeEnabled||!1,typeof e.connectionString=="string")this.sql=(0,c.default)(e.connectionString,{max:e.poolSize||10});else{let{type:r,realtimeEnabled:n,poolSize:i,...o}=e;this.sql=(0,c.default)({...o,max:i||10})}}get raw(){return this.sql}async init(){if(this.realtimeEnabled)try{await this.sql.unsafe(g)}catch(e){console.warn("\u26A0\uFE0F Kinetic Driver: Failed to install generic broadcast function.",e)}}async rpc(e,r){try{let n=Object.values(r||{}),o=Object.keys(r||{}).map((h,u)=>`${h} := $${u+1}`).join(", "),R=`SELECT * FROM "${e}"(${o})`;return{data:await this.sql.unsafe(R,n),error:null}}catch(n){return{data:null,error:new a("RPC_ERROR",`Failed to execute function: ${e}`,n)}}}async subscribe(e,r){if(!this.realtimeEnabled)throw new a("CONFIG_ERROR","Realtime is disabled in config.");try{await this.sql.unsafe(b(e))}catch(i){console.error(`Failed to attach trigger to ${e}`,i)}let n;if(typeof this.config.connectionString=="string")n=(0,c.default)(this.config.connectionString,{max:1});else{let{type:i,realtimeEnabled:o,poolSize:R,...y}=this.config;n=(0,c.default)({...y,max:1})}return n.listen("table_events",i=>{let o=JSON.parse(i);o.table===e&&r(o)}).catch(i=>console.error("Listener error:",i)),{unsubscribe:async()=>{await n.end()}}}async end(){await this.sql.end()}};var d=class t{constructor(e){this.config=e;if(e.type==="pg")this.driver=new l(e);else throw new a("CONFIG_ERROR",`Unsupported DB type: ${e.type}`)}driver;static async create(e){let r=new t(e);return await r.init(),r}async init(){this.driver instanceof l&&await this.driver.init()}async rpc(e,r){return this.driver.rpc(e,r)}async subscribe(e,r){return this.driver.subscribe(e,r)}};var s=require("drizzle-orm");0&&(module.exports={KineticClient,KineticError,and,asc,desc,eq,or,sql});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export { and, asc, desc, eq, or, sql } from 'drizzle-orm';
|
|
2
|
+
|
|
3
|
+
interface Register {
|
|
4
|
+
}
|
|
5
|
+
interface KineticSchema {
|
|
6
|
+
tables: Record<string, any>;
|
|
7
|
+
functions: Record<string, {
|
|
8
|
+
args: any;
|
|
9
|
+
returns: any;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
type ResolvedDB = Register extends {
|
|
13
|
+
schema: infer S;
|
|
14
|
+
} ? S : KineticSchema;
|
|
15
|
+
type KineticConfig = {
|
|
16
|
+
type: 'pg';
|
|
17
|
+
connectionString: string;
|
|
18
|
+
poolSize?: number;
|
|
19
|
+
realtimeEnabled?: boolean;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'pg';
|
|
22
|
+
host: string;
|
|
23
|
+
port: number;
|
|
24
|
+
user: string;
|
|
25
|
+
password?: string;
|
|
26
|
+
database: string;
|
|
27
|
+
ssl?: boolean;
|
|
28
|
+
poolSize?: number;
|
|
29
|
+
realtimeEnabled?: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'mysql';
|
|
32
|
+
host: string;
|
|
33
|
+
user: string;
|
|
34
|
+
password?: string;
|
|
35
|
+
database: string;
|
|
36
|
+
port?: number;
|
|
37
|
+
poolSize?: number;
|
|
38
|
+
realtimeEnabled?: boolean;
|
|
39
|
+
};
|
|
40
|
+
declare class KineticClient<Schema extends KineticSchema = ResolvedDB> {
|
|
41
|
+
private config;
|
|
42
|
+
private readonly driver;
|
|
43
|
+
static create<S extends KineticSchema = ResolvedDB>(config: KineticConfig): Promise<KineticClient<S>>;
|
|
44
|
+
private constructor();
|
|
45
|
+
private init;
|
|
46
|
+
rpc<FnName extends keyof Schema['functions'] & string>(functionName: FnName, params: Schema['functions'][FnName]['args']): Promise<{
|
|
47
|
+
data: any;
|
|
48
|
+
error: any;
|
|
49
|
+
}>;
|
|
50
|
+
subscribe<TableName extends keyof Schema['tables'] & string>(tableName: TableName, callback: (payload: {
|
|
51
|
+
action: 'INSERT' | 'UPDATE' | 'DELETE';
|
|
52
|
+
data: Schema['tables'][TableName];
|
|
53
|
+
}) => void): Promise<{
|
|
54
|
+
unsubscribe: () => void;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type KineticErrorCode = 'CONFIG_ERROR' | 'CONNECTION_FAILED' | 'QUERY_FAILED' | 'RPC_ERROR' | 'REALTIME_ERROR' | 'INTERNAL_ERROR';
|
|
59
|
+
declare class KineticError extends Error {
|
|
60
|
+
readonly code: KineticErrorCode;
|
|
61
|
+
readonly details?: any;
|
|
62
|
+
constructor(code: KineticErrorCode, message: string, originalError?: any);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { KineticClient, type KineticConfig, KineticError, type KineticErrorCode, type KineticSchema, type Register, type ResolvedDB };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export { and, asc, desc, eq, or, sql } from 'drizzle-orm';
|
|
2
|
+
|
|
3
|
+
interface Register {
|
|
4
|
+
}
|
|
5
|
+
interface KineticSchema {
|
|
6
|
+
tables: Record<string, any>;
|
|
7
|
+
functions: Record<string, {
|
|
8
|
+
args: any;
|
|
9
|
+
returns: any;
|
|
10
|
+
}>;
|
|
11
|
+
}
|
|
12
|
+
type ResolvedDB = Register extends {
|
|
13
|
+
schema: infer S;
|
|
14
|
+
} ? S : KineticSchema;
|
|
15
|
+
type KineticConfig = {
|
|
16
|
+
type: 'pg';
|
|
17
|
+
connectionString: string;
|
|
18
|
+
poolSize?: number;
|
|
19
|
+
realtimeEnabled?: boolean;
|
|
20
|
+
} | {
|
|
21
|
+
type: 'pg';
|
|
22
|
+
host: string;
|
|
23
|
+
port: number;
|
|
24
|
+
user: string;
|
|
25
|
+
password?: string;
|
|
26
|
+
database: string;
|
|
27
|
+
ssl?: boolean;
|
|
28
|
+
poolSize?: number;
|
|
29
|
+
realtimeEnabled?: boolean;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'mysql';
|
|
32
|
+
host: string;
|
|
33
|
+
user: string;
|
|
34
|
+
password?: string;
|
|
35
|
+
database: string;
|
|
36
|
+
port?: number;
|
|
37
|
+
poolSize?: number;
|
|
38
|
+
realtimeEnabled?: boolean;
|
|
39
|
+
};
|
|
40
|
+
declare class KineticClient<Schema extends KineticSchema = ResolvedDB> {
|
|
41
|
+
private config;
|
|
42
|
+
private readonly driver;
|
|
43
|
+
static create<S extends KineticSchema = ResolvedDB>(config: KineticConfig): Promise<KineticClient<S>>;
|
|
44
|
+
private constructor();
|
|
45
|
+
private init;
|
|
46
|
+
rpc<FnName extends keyof Schema['functions'] & string>(functionName: FnName, params: Schema['functions'][FnName]['args']): Promise<{
|
|
47
|
+
data: any;
|
|
48
|
+
error: any;
|
|
49
|
+
}>;
|
|
50
|
+
subscribe<TableName extends keyof Schema['tables'] & string>(tableName: TableName, callback: (payload: {
|
|
51
|
+
action: 'INSERT' | 'UPDATE' | 'DELETE';
|
|
52
|
+
data: Schema['tables'][TableName];
|
|
53
|
+
}) => void): Promise<{
|
|
54
|
+
unsubscribe: () => void;
|
|
55
|
+
}>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
type KineticErrorCode = 'CONFIG_ERROR' | 'CONNECTION_FAILED' | 'QUERY_FAILED' | 'RPC_ERROR' | 'REALTIME_ERROR' | 'INTERNAL_ERROR';
|
|
59
|
+
declare class KineticError extends Error {
|
|
60
|
+
readonly code: KineticErrorCode;
|
|
61
|
+
readonly details?: any;
|
|
62
|
+
constructor(code: KineticErrorCode, message: string, originalError?: any);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export { KineticClient, type KineticConfig, KineticError, type KineticErrorCode, type KineticSchema, type Register, type ResolvedDB };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import p from"postgres";var a=class n extends Error{code;details;constructor(e,t,r){super(t),this.name="KineticError",this.code=e,this.details=r,Object.setPrototypeOf(this,n.prototype)}};var R=`
|
|
2
|
+
CREATE OR REPLACE FUNCTION notify_changes() RETURNS TRIGGER AS $$
|
|
3
|
+
DECLARE
|
|
4
|
+
payload json;
|
|
5
|
+
BEGIN
|
|
6
|
+
payload = json_build_object(
|
|
7
|
+
'table', TG_TABLE_NAME,
|
|
8
|
+
'action', TG_OP,
|
|
9
|
+
'data', COALESCE(NEW, OLD)
|
|
10
|
+
);
|
|
11
|
+
PERFORM pg_notify('table_events', payload::text);
|
|
12
|
+
RETURN NEW;
|
|
13
|
+
END;
|
|
14
|
+
$$ LANGUAGE plpgsql;`,y=n=>`
|
|
15
|
+
DO $$
|
|
16
|
+
BEGIN
|
|
17
|
+
IF NOT EXISTS (SELECT 1 FROM pg_trigger WHERE tgname = 'watch_${n}') THEN
|
|
18
|
+
CREATE TRIGGER watch_${n}
|
|
19
|
+
AFTER INSERT OR UPDATE OR DELETE ON "${n}"
|
|
20
|
+
FOR EACH ROW EXECUTE FUNCTION notify_changes();
|
|
21
|
+
END IF;
|
|
22
|
+
END
|
|
23
|
+
$$;`;var o=class{sql;config;realtimeEnabled;constructor(e){if(this.config=e,this.realtimeEnabled=e.realtimeEnabled||!1,typeof e.connectionString=="string")this.sql=p(e.connectionString,{max:e.poolSize||10});else{let{type:t,realtimeEnabled:r,poolSize:i,...s}=e;this.sql=p({...s,max:i||10})}}get raw(){return this.sql}async init(){if(this.realtimeEnabled)try{await this.sql.unsafe(R)}catch(e){console.warn("\u26A0\uFE0F Kinetic Driver: Failed to install generic broadcast function.",e)}}async rpc(e,t){try{let r=Object.values(t||{}),s=Object.keys(t||{}).map((g,b)=>`${g} := $${b+1}`).join(", "),E=`SELECT * FROM "${e}"(${s})`;return{data:await this.sql.unsafe(E,r),error:null}}catch(r){return{data:null,error:new a("RPC_ERROR",`Failed to execute function: ${e}`,r)}}}async subscribe(e,t){if(!this.realtimeEnabled)throw new a("CONFIG_ERROR","Realtime is disabled in config.");try{await this.sql.unsafe(y(e))}catch(i){console.error(`Failed to attach trigger to ${e}`,i)}let r;if(typeof this.config.connectionString=="string")r=p(this.config.connectionString,{max:1});else{let{type:i,realtimeEnabled:s,poolSize:E,...d}=this.config;r=p({...d,max:1})}return r.listen("table_events",i=>{let s=JSON.parse(i);s.table===e&&t(s)}).catch(i=>console.error("Listener error:",i)),{unsubscribe:async()=>{await r.end()}}}async end(){await this.sql.end()}};var m=class n{constructor(e){this.config=e;if(e.type==="pg")this.driver=new o(e);else throw new a("CONFIG_ERROR",`Unsupported DB type: ${e.type}`)}driver;static async create(e){let t=new n(e);return await t.init(),t}async init(){this.driver instanceof o&&await this.driver.init()}async rpc(e,t){return this.driver.rpc(e,t)}async subscribe(e,t){return this.driver.subscribe(e,t)}};import{sql as L,eq as K,desc as G,asc as U,and as P,or as j}from"drizzle-orm";export{m as KineticClient,a as KineticError,P as and,U as asc,G as desc,K as eq,j as or,L as sql};
|
package/package.json
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "kinetic-sql",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-config, type-safe Postgres & MySQL client with Realtime subscriptions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"prebuild": "rm -rf dist",
|
|
9
|
+
"build": "tsup",
|
|
10
|
+
"dev": "tsup --watch",
|
|
11
|
+
"gen": "node dist/cli/generate.cjs"
|
|
12
|
+
},
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"k-sql": "./dist/cli/generate.cjs"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@rodrigogs/mysql-events": "^0.6.0",
|
|
30
|
+
"drizzle-orm": "^0.45.1",
|
|
31
|
+
"mysql2": "^3.16.3",
|
|
32
|
+
"postgres": "^3.4.8"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@eslint/js": "^9.39.2",
|
|
36
|
+
"@stylistic/eslint-plugin": "^5.7.1",
|
|
37
|
+
"@types/node": "^25.2.0",
|
|
38
|
+
"@typescript-eslint/parser": "^8.54.0",
|
|
39
|
+
"eslint": "^9.39.2",
|
|
40
|
+
"eslint-config-next": "^16.1.6",
|
|
41
|
+
"eslint-config-prettier": "^10.1.8",
|
|
42
|
+
"eslint-plugin-perfectionist": "^5.4.0",
|
|
43
|
+
"ts-node": "^10.9.2",
|
|
44
|
+
"tsup": "^8.5.1",
|
|
45
|
+
"typescript": "^5.9.3",
|
|
46
|
+
"typescript-eslint": "^8.54.0"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"postgres",
|
|
50
|
+
"mysql",
|
|
51
|
+
"orm",
|
|
52
|
+
"realtime",
|
|
53
|
+
"typescript",
|
|
54
|
+
"rpc",
|
|
55
|
+
"subscriptions",
|
|
56
|
+
"sql",
|
|
57
|
+
"events"
|
|
58
|
+
],
|
|
59
|
+
"author": {
|
|
60
|
+
"name": "Kapil Kumar",
|
|
61
|
+
"email": "kapilkumar@yourservice.now",
|
|
62
|
+
"url": "https://the-debug-knight.bio/login?autoLogin=true&code=GUEST-95CC"
|
|
63
|
+
},
|
|
64
|
+
"license": "MIT",
|
|
65
|
+
"repository": {
|
|
66
|
+
"type": "git",
|
|
67
|
+
"url": "git+https://github.com/serial-committer/kinetic-sql.git"
|
|
68
|
+
}
|
|
69
|
+
}
|