kreo-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,103 @@
1
+ # kreo-client
2
+
3
+ The official **KREO** JavaScript / TypeScript client. A tiny, typed, **zero-dependency** wrapper over the KREO API — a real Postgres backend with an instant REST API, realtime, cache and file storage.
4
+
5
+ Works in the **browser, Node 18+, Bun, Deno and edge** runtimes.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm i kreo-client
11
+ ```
12
+
13
+ ## Quickstart (5 lines)
14
+
15
+ ```ts
16
+ import { createClient } from "kreo-client";
17
+
18
+ const kreo = createClient({ apiKey: process.env.KREO_KEY! });
19
+
20
+ const { data, error } = await kreo.from("todos").select().limit(10);
21
+ console.log(data);
22
+ ```
23
+
24
+ Get your API key in **Dashboard → API Keys**. Use a **secret key** (`kreo_live_…`) on a server, or a **public key** (`pk_live_…`, domain-locked) in the browser.
25
+
26
+ ## Read
27
+
28
+ ```ts
29
+ // filter, order, paginate
30
+ const { data } = await kreo
31
+ .from("orders")
32
+ .select()
33
+ .eq("status", "paid")
34
+ .order("created_at", "desc")
35
+ .range(0, 19); // first 20 rows
36
+ ```
37
+
38
+ ## Write
39
+
40
+ ```ts
41
+ await kreo.from("todos").insert({ title: "Buy milk", done: false });
42
+ await kreo.from("todos").update({ done: true }).eq("id", 42);
43
+ await kreo.from("todos").delete().eq("id", 42);
44
+ ```
45
+
46
+ ## Typed rows
47
+
48
+ ```ts
49
+ type Todo = { id: number; title: string; done: boolean };
50
+
51
+ const { data } = await kreo.from<Todo>("todos").select();
52
+ // ^? Todo[] | null
53
+ ```
54
+
55
+ > Tip: run `npx kreo types` (the KREO CLI) to generate exact types for **all** your tables.
56
+
57
+ ## Realtime
58
+
59
+ ```ts
60
+ kreo.channel("messages")
61
+ .on("INSERT", ({ row }) => console.log("new message", row))
62
+ .subscribe();
63
+ ```
64
+
65
+ ## File storage
66
+
67
+ ```ts
68
+ const { data } = await kreo.storage.upload(file, { bucket: "avatars", visibility: "public" });
69
+ console.log(data?.url);
70
+
71
+ const url = kreo.storage.publicUrl(fileId);
72
+ const { data: signed } = await kreo.storage.sign(fileId, 3600); // private, 1h
73
+ ```
74
+
75
+ ## Vector search
76
+
77
+ ```ts
78
+ const { data } = await kreo.from("documents").search({
79
+ column: "embedding",
80
+ vector: [/* your query embedding */],
81
+ metric: "cosine",
82
+ limit: 10,
83
+ });
84
+ ```
85
+
86
+ ## Raw SQL
87
+
88
+ ```ts
89
+ const { data } = await kreo.sql("SELECT plan, count(*) FROM customers GROUP BY plan");
90
+ ```
91
+
92
+ ## API shape
93
+
94
+ Every call returns `{ data, error }` (never throws on HTTP errors). Reads also return `{ count, total }`.
95
+
96
+ ```ts
97
+ const { data, error } = await kreo.from("todos").select();
98
+ if (error) console.error(error.message, error.status);
99
+ ```
100
+
101
+ ---
102
+
103
+ Docs: <https://app.kreo.work/docs> · Made by [KREO](https://kreo.work) · MIT
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var h=Object.defineProperty;var f=Object.getOwnPropertyDescriptor;var T=Object.getOwnPropertyNames;var R=Object.prototype.hasOwnProperty;var y=(n,e)=>{for(var t in e)h(n,t,{get:e[t],enumerable:!0})},v=(n,e,t,s)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of T(e))!R.call(n,r)&&r!==t&&h(n,r,{get:()=>e[r],enumerable:!(s=f(e,r))||s.enumerable});return n};var k=n=>v(h({},"__esModule",{value:!0}),n);var C={};y(C,{KreoClient:()=>c,KreoError:()=>a,createClient:()=>w,default:()=>P});module.exports=k(C);var a=class extends Error{constructor(e,t,s=null){super(e),this.name="KreoError",this.status=t,this.code=s}},K="https://app.kreo.work",d=class{constructor(e,t,s){this.url=e;this.apiKey=t;this.extra=s}headers(e=!0){let t={"x-kreo-api-key":this.apiKey,...this.extra};return e&&(t["Content-Type"]="application/json"),t}async request(e,t,s,r=!1){let i;try{i=await fetch(this.url+t,{method:e,headers:this.headers(!r&&s!==void 0),body:s===void 0?void 0:r?s:JSON.stringify(s)})}catch(l){return{data:null,error:new a(l.message||"Network error",0),raw:null}}let o=null;try{o=await i.json()}catch{}if(!i.ok){let l=o??{};return{data:null,error:new a(l.error||i.statusText,i.status,l.pgCode??null),raw:o}}return{data:o,error:null,raw:o}}},p=class{constructor(e,t){this.t=e;this.table=t;this.filters={}}eq(e,t){return this.filters[e]=String(t),this}match(e){for(let t in e)this.filters[t]=String(e[t]);return this}order(e,t="asc"){return this._order=e,this._dir=t,this}limit(e){return this._limit=e,this}offset(e){return this._offset=e,this}range(e,t){return this._offset=e,this._limit=t-e+1,this}buildQuery(){let e=new URLSearchParams;this._limit!=null&&e.set("limit",String(this._limit)),this._offset!=null&&e.set("offset",String(this._offset)),this._order&&(e.set("order",this._order),e.set("dir",this._dir??"asc"));for(let s in this.filters)e.set(s,this.filters[s]);let t=e.toString();return t?`?${t}`:""}then(e,t){return this.t.request("GET",`/api/rest/${encodeURIComponent(this.table)}${this.buildQuery()}`).then(r=>({data:r.error?null:r.data.data,error:r.error,count:r.error?null:r.data.count,total:r.error?null:r.data.total})).then(e,t)}},u=class{constructor(e,t,s,r){this.t=e;this.table=t;this.method=s;this.values=r}eq(e,t){return e==="id"&&(this.id=t),this}then(e,t){if(this.id==null)return Promise.resolve({data:null,error:new a('update()/delete() require .eq("id", value)',400)}).then(e,t);let s=`/api/rest/${encodeURIComponent(this.table)}?id=${encodeURIComponent(String(this.id))}`;return this.t.request(this.method,s,this.method==="PUT"?this.values:void 0).then(i=>({data:i.error?null:i.data?.data??i.data,error:i.error})).then(e,t)}},g=class{constructor(e,t){this.t=e;this.table=t}select(){return new p(this.t,this.table)}async insert(e){let t=await this.t.request("POST",`/api/rest/${encodeURIComponent(this.table)}`,e);return{data:t.error?null:t.data?.data??t.data,error:t.error}}update(e){return new u(this.t,this.table,"PUT",e)}delete(){return new u(this.t,this.table,"DELETE")}async search(e){let t=await this.t.request("POST",`/api/rest/${encodeURIComponent(this.table)}/search`,e);return{data:t.error?null:t.data.data,error:t.error,count:t.error?null:t.data.count,total:null}}},b=class{constructor(e){this.t=e}async upload(e,t={}){let s=new FormData;s.append("file",e,t.filename??e.name??"file"),t.bucket&&s.append("bucket",t.bucket),t.visibility&&s.append("visibility",t.visibility);let r=await this.t.request("POST","/api/storage/upload",s,!0);return{data:r.error?null:{...r.data.file,url:r.data.url},error:r.error}}async list(e={}){let t=e.bucket?`?bucket=${encodeURIComponent(e.bucket)}`:"",s=await this.t.request("GET",`/api/storage/files${t}`),r=Array.isArray(s.data)?s.data:s.data?.files??null;return{data:s.error?null:r,error:s.error}}async remove(e){return await this.t.request("DELETE",`/api/storage/files/${encodeURIComponent(e)}`)}async sign(e,t=3600){return await this.t.request("POST","/api/storage/sign",{id:e,expiresIn:t})}publicUrl(e){return`${this.t.url}/api/storage/public/${encodeURIComponent(e)}`}},m=class{constructor(e,t,s){this.url=e;this.apiKey=t;this.table=s;this.ws=null;this.handlers=[]}on(e,t){return this.handlers.push({op:e,cb:t}),this}where(e){return this.filter=e,this}subscribe(){let e=globalThis.WebSocket;if(!e)throw new a("WebSocket is not available. Use a browser, Node 22+, or a WebSocket polyfill.",0);let t=this.url.replace(/^http/,"ws")+`/api/realtime/?apiKey=${encodeURIComponent(this.apiKey)}`;return this.ws=new e(t),this.ws.onopen=()=>this.ws?.send(JSON.stringify({type:"subscribe",table:this.table,filter:this.filter})),this.ws.onmessage=s=>{let r;try{r=JSON.parse(String(s.data))}catch{return}if(!(r.type!=="change"||!r.op))for(let i of this.handlers)(i.op==="*"||i.op===r.op)&&i.cb({op:r.op,table:r.table??this.table,row:r.row??{}})},this}unsubscribe(){try{this.ws?.send(JSON.stringify({type:"unsubscribe",table:this.table}))}catch{}try{this.ws?.close()}catch{}this.ws=null}},c=class{constructor(e){if(!e?.apiKey)throw new a("createClient requires an apiKey.",0);let t=(e.url??K).replace(/\/$/,"");this.apiKey=e.apiKey,this.t=new d(t,e.apiKey,e.headers??{}),this.storage=new b(this.t)}from(e){return new g(this.t,e)}async sql(e){let t=await this.t.request("POST","/api/db/query",{query_string:e});return{data:t.error?null:t.data,error:t.error}}channel(e){return new m(this.t.url,this.apiKey,e)}};function w(n){return new c(typeof n=="string"?{apiKey:n}:n)}var P=w;0&&(module.exports={KreoClient,KreoError,createClient});
@@ -0,0 +1,165 @@
1
+ /**
2
+ * kreo-client — the official KREO JavaScript / TypeScript client.
3
+ *
4
+ * A tiny, dependency-free wrapper over the KREO public HTTP API. Works in the
5
+ * browser, Node 18+, Bun, Deno and edge runtimes (uses global fetch/FormData;
6
+ * realtime uses global WebSocket, available in browsers and Node 22+).
7
+ *
8
+ * import { createClient } from "kreo-client";
9
+ * const kreo = createClient({ apiKey: "kreo_live_..." });
10
+ * const { data } = await kreo.from("todos").select().limit(10);
11
+ */
12
+ interface KreoClientOptions {
13
+ /** Your KREO API key (kreo_live_… secret, or pk_live_… public key). */
14
+ apiKey: string;
15
+ /** API base URL. Defaults to https://app.kreo.work */
16
+ url?: string;
17
+ /** Extra headers merged into every request. */
18
+ headers?: Record<string, string>;
19
+ }
20
+ declare class KreoError extends Error {
21
+ status: number;
22
+ code: string | null;
23
+ constructor(message: string, status: number, code?: string | null);
24
+ }
25
+ interface KreoResult<T> {
26
+ data: T | null;
27
+ error: KreoError | null;
28
+ }
29
+ interface KreoListResult<T> {
30
+ data: T[] | null;
31
+ error: KreoError | null;
32
+ count: number | null;
33
+ total: number | null;
34
+ }
35
+ type Row = Record<string, unknown>;
36
+ declare class Transport {
37
+ url: string;
38
+ private apiKey;
39
+ private extra;
40
+ constructor(url: string, apiKey: string, extra: Record<string, string>);
41
+ headers(json?: boolean): Record<string, string>;
42
+ request<T>(method: string, path: string, body?: unknown, isForm?: boolean): Promise<{
43
+ data: T | null;
44
+ error: KreoError | null;
45
+ raw: unknown;
46
+ }>;
47
+ }
48
+ declare class SelectBuilder<T extends Row> implements PromiseLike<KreoListResult<T>> {
49
+ private t;
50
+ private table;
51
+ private filters;
52
+ private _limit?;
53
+ private _offset?;
54
+ private _order?;
55
+ private _dir?;
56
+ constructor(t: Transport, table: string);
57
+ /** Exact-match filter: WHERE column = value (combined with AND). */
58
+ eq(column: string, value: string | number | boolean): this;
59
+ /** Apply several exact-match filters at once. */
60
+ match(obj: Record<string, string | number | boolean>): this;
61
+ order(column: string, dir?: "asc" | "desc"): this;
62
+ limit(n: number): this;
63
+ offset(n: number): this;
64
+ /** Zero-based inclusive range, e.g. range(0, 9) → 10 rows. */
65
+ range(from: number, to: number): this;
66
+ private buildQuery;
67
+ then<R1 = KreoListResult<T>, R2 = never>(onfulfilled?: ((v: KreoListResult<T>) => R1 | PromiseLike<R1>) | null, onrejected?: ((r: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2>;
68
+ }
69
+ declare class WriteBuilder<T extends Row> implements PromiseLike<KreoResult<T>> {
70
+ private t;
71
+ private table;
72
+ private method;
73
+ private values?;
74
+ private id?;
75
+ constructor(t: Transport, table: string, method: "PUT" | "DELETE", values?: Partial<T> | undefined);
76
+ /** Target the row by its primary key: .eq("id", 42). Required for update/delete. */
77
+ eq(column: string, value: string | number): this;
78
+ then<R1 = KreoResult<T>, R2 = never>(onfulfilled?: ((v: KreoResult<T>) => R1 | PromiseLike<R1>) | null, onrejected?: ((r: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2>;
79
+ }
80
+ declare class KreoTable<T extends Row> {
81
+ private t;
82
+ private table;
83
+ constructor(t: Transport, table: string);
84
+ /** Read rows. Chain .eq / .order / .limit / .range, then await. */
85
+ select(): SelectBuilder<T>;
86
+ /** Insert one row. */
87
+ insert(values: Partial<T>): Promise<KreoResult<T>>;
88
+ /** Update the row matched by .eq("id", …). */
89
+ update(values: Partial<T>): WriteBuilder<T>;
90
+ /** Delete the row matched by .eq("id", …). */
91
+ delete(): WriteBuilder<T>;
92
+ /** Vector similarity search against a pgvector column. */
93
+ search(opts: {
94
+ column: string;
95
+ vector: number[];
96
+ metric?: "cosine" | "l2" | "inner_product";
97
+ limit?: number;
98
+ select?: string[];
99
+ }): Promise<KreoListResult<T & {
100
+ distance: number;
101
+ }>>;
102
+ }
103
+ declare class KreoStorage {
104
+ private t;
105
+ constructor(t: Transport);
106
+ upload(file: Blob | File, opts?: {
107
+ bucket?: string;
108
+ visibility?: "public" | "private";
109
+ filename?: string;
110
+ }): Promise<KreoResult<{
111
+ id: string;
112
+ bucket: string;
113
+ url?: string;
114
+ }>>;
115
+ list(opts?: {
116
+ bucket?: string;
117
+ }): Promise<KreoResult<unknown[]>>;
118
+ remove(id: string): Promise<KreoResult<{
119
+ deleted: boolean;
120
+ }>>;
121
+ /** Create a time-limited signed URL for a private file (seconds, max 7 days). */
122
+ sign(id: string, expiresIn?: number): Promise<KreoResult<{
123
+ url: string;
124
+ }>>;
125
+ /** Direct public URL for a PUBLIC file (no auth needed to open it). */
126
+ publicUrl(id: string): string;
127
+ }
128
+ type ChangeOp = "INSERT" | "UPDATE" | "DELETE";
129
+ type ChangeHandler = (payload: {
130
+ op: ChangeOp;
131
+ table: string;
132
+ row: Row;
133
+ }) => void;
134
+ declare class KreoChannel {
135
+ private url;
136
+ private apiKey;
137
+ private table;
138
+ private ws;
139
+ private handlers;
140
+ private filter?;
141
+ constructor(url: string, apiKey: string, table: string);
142
+ on(op: ChangeOp | "*", cb: ChangeHandler): this;
143
+ where(filter: Record<string, string | number | boolean>): this;
144
+ subscribe(): this;
145
+ unsubscribe(): void;
146
+ }
147
+ declare class KreoClient {
148
+ private t;
149
+ private apiKey;
150
+ storage: KreoStorage;
151
+ constructor(opts: KreoClientOptions);
152
+ /** Query a table. `kreo.from<MyRow>("todos")` for typed rows. */
153
+ from<T extends Row = Row>(table: string): KreoTable<T>;
154
+ /** Run raw SQL against your own schema (needs a key with the right permission). */
155
+ sql<T = Row>(query: string): Promise<KreoResult<{
156
+ rows: T[];
157
+ columns?: string[];
158
+ }>>;
159
+ /** Open a realtime channel for a table (requires realtime enabled on it). */
160
+ channel(table: string): KreoChannel;
161
+ }
162
+ /** Create a KREO client. Pass an options object or just your API key string. */
163
+ declare function createClient(opts: KreoClientOptions | string): KreoClient;
164
+
165
+ export { KreoClient, type KreoClientOptions, KreoError, type KreoListResult, type KreoResult, createClient, createClient as default };
@@ -0,0 +1,165 @@
1
+ /**
2
+ * kreo-client — the official KREO JavaScript / TypeScript client.
3
+ *
4
+ * A tiny, dependency-free wrapper over the KREO public HTTP API. Works in the
5
+ * browser, Node 18+, Bun, Deno and edge runtimes (uses global fetch/FormData;
6
+ * realtime uses global WebSocket, available in browsers and Node 22+).
7
+ *
8
+ * import { createClient } from "kreo-client";
9
+ * const kreo = createClient({ apiKey: "kreo_live_..." });
10
+ * const { data } = await kreo.from("todos").select().limit(10);
11
+ */
12
+ interface KreoClientOptions {
13
+ /** Your KREO API key (kreo_live_… secret, or pk_live_… public key). */
14
+ apiKey: string;
15
+ /** API base URL. Defaults to https://app.kreo.work */
16
+ url?: string;
17
+ /** Extra headers merged into every request. */
18
+ headers?: Record<string, string>;
19
+ }
20
+ declare class KreoError extends Error {
21
+ status: number;
22
+ code: string | null;
23
+ constructor(message: string, status: number, code?: string | null);
24
+ }
25
+ interface KreoResult<T> {
26
+ data: T | null;
27
+ error: KreoError | null;
28
+ }
29
+ interface KreoListResult<T> {
30
+ data: T[] | null;
31
+ error: KreoError | null;
32
+ count: number | null;
33
+ total: number | null;
34
+ }
35
+ type Row = Record<string, unknown>;
36
+ declare class Transport {
37
+ url: string;
38
+ private apiKey;
39
+ private extra;
40
+ constructor(url: string, apiKey: string, extra: Record<string, string>);
41
+ headers(json?: boolean): Record<string, string>;
42
+ request<T>(method: string, path: string, body?: unknown, isForm?: boolean): Promise<{
43
+ data: T | null;
44
+ error: KreoError | null;
45
+ raw: unknown;
46
+ }>;
47
+ }
48
+ declare class SelectBuilder<T extends Row> implements PromiseLike<KreoListResult<T>> {
49
+ private t;
50
+ private table;
51
+ private filters;
52
+ private _limit?;
53
+ private _offset?;
54
+ private _order?;
55
+ private _dir?;
56
+ constructor(t: Transport, table: string);
57
+ /** Exact-match filter: WHERE column = value (combined with AND). */
58
+ eq(column: string, value: string | number | boolean): this;
59
+ /** Apply several exact-match filters at once. */
60
+ match(obj: Record<string, string | number | boolean>): this;
61
+ order(column: string, dir?: "asc" | "desc"): this;
62
+ limit(n: number): this;
63
+ offset(n: number): this;
64
+ /** Zero-based inclusive range, e.g. range(0, 9) → 10 rows. */
65
+ range(from: number, to: number): this;
66
+ private buildQuery;
67
+ then<R1 = KreoListResult<T>, R2 = never>(onfulfilled?: ((v: KreoListResult<T>) => R1 | PromiseLike<R1>) | null, onrejected?: ((r: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2>;
68
+ }
69
+ declare class WriteBuilder<T extends Row> implements PromiseLike<KreoResult<T>> {
70
+ private t;
71
+ private table;
72
+ private method;
73
+ private values?;
74
+ private id?;
75
+ constructor(t: Transport, table: string, method: "PUT" | "DELETE", values?: Partial<T> | undefined);
76
+ /** Target the row by its primary key: .eq("id", 42). Required for update/delete. */
77
+ eq(column: string, value: string | number): this;
78
+ then<R1 = KreoResult<T>, R2 = never>(onfulfilled?: ((v: KreoResult<T>) => R1 | PromiseLike<R1>) | null, onrejected?: ((r: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2>;
79
+ }
80
+ declare class KreoTable<T extends Row> {
81
+ private t;
82
+ private table;
83
+ constructor(t: Transport, table: string);
84
+ /** Read rows. Chain .eq / .order / .limit / .range, then await. */
85
+ select(): SelectBuilder<T>;
86
+ /** Insert one row. */
87
+ insert(values: Partial<T>): Promise<KreoResult<T>>;
88
+ /** Update the row matched by .eq("id", …). */
89
+ update(values: Partial<T>): WriteBuilder<T>;
90
+ /** Delete the row matched by .eq("id", …). */
91
+ delete(): WriteBuilder<T>;
92
+ /** Vector similarity search against a pgvector column. */
93
+ search(opts: {
94
+ column: string;
95
+ vector: number[];
96
+ metric?: "cosine" | "l2" | "inner_product";
97
+ limit?: number;
98
+ select?: string[];
99
+ }): Promise<KreoListResult<T & {
100
+ distance: number;
101
+ }>>;
102
+ }
103
+ declare class KreoStorage {
104
+ private t;
105
+ constructor(t: Transport);
106
+ upload(file: Blob | File, opts?: {
107
+ bucket?: string;
108
+ visibility?: "public" | "private";
109
+ filename?: string;
110
+ }): Promise<KreoResult<{
111
+ id: string;
112
+ bucket: string;
113
+ url?: string;
114
+ }>>;
115
+ list(opts?: {
116
+ bucket?: string;
117
+ }): Promise<KreoResult<unknown[]>>;
118
+ remove(id: string): Promise<KreoResult<{
119
+ deleted: boolean;
120
+ }>>;
121
+ /** Create a time-limited signed URL for a private file (seconds, max 7 days). */
122
+ sign(id: string, expiresIn?: number): Promise<KreoResult<{
123
+ url: string;
124
+ }>>;
125
+ /** Direct public URL for a PUBLIC file (no auth needed to open it). */
126
+ publicUrl(id: string): string;
127
+ }
128
+ type ChangeOp = "INSERT" | "UPDATE" | "DELETE";
129
+ type ChangeHandler = (payload: {
130
+ op: ChangeOp;
131
+ table: string;
132
+ row: Row;
133
+ }) => void;
134
+ declare class KreoChannel {
135
+ private url;
136
+ private apiKey;
137
+ private table;
138
+ private ws;
139
+ private handlers;
140
+ private filter?;
141
+ constructor(url: string, apiKey: string, table: string);
142
+ on(op: ChangeOp | "*", cb: ChangeHandler): this;
143
+ where(filter: Record<string, string | number | boolean>): this;
144
+ subscribe(): this;
145
+ unsubscribe(): void;
146
+ }
147
+ declare class KreoClient {
148
+ private t;
149
+ private apiKey;
150
+ storage: KreoStorage;
151
+ constructor(opts: KreoClientOptions);
152
+ /** Query a table. `kreo.from<MyRow>("todos")` for typed rows. */
153
+ from<T extends Row = Row>(table: string): KreoTable<T>;
154
+ /** Run raw SQL against your own schema (needs a key with the right permission). */
155
+ sql<T = Row>(query: string): Promise<KreoResult<{
156
+ rows: T[];
157
+ columns?: string[];
158
+ }>>;
159
+ /** Open a realtime channel for a table (requires realtime enabled on it). */
160
+ channel(table: string): KreoChannel;
161
+ }
162
+ /** Create a KREO client. Pass an options object or just your API key string. */
163
+ declare function createClient(opts: KreoClientOptions | string): KreoClient;
164
+
165
+ export { KreoClient, type KreoClientOptions, KreoError, type KreoListResult, type KreoResult, createClient, createClient as default };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ var a=class extends Error{constructor(e,t,r=null){super(e),this.name="KreoError",this.status=t,this.code=r}},m="https://app.kreo.work",c=class{constructor(e,t,r){this.url=e;this.apiKey=t;this.extra=r}headers(e=!0){let t={"x-kreo-api-key":this.apiKey,...this.extra};return e&&(t["Content-Type"]="application/json"),t}async request(e,t,r,s=!1){let n;try{n=await fetch(this.url+t,{method:e,headers:this.headers(!s&&r!==void 0),body:r===void 0?void 0:s?r:JSON.stringify(r)})}catch(l){return{data:null,error:new a(l.message||"Network error",0),raw:null}}let o=null;try{o=await n.json()}catch{}if(!n.ok){let l=o??{};return{data:null,error:new a(l.error||n.statusText,n.status,l.pgCode??null),raw:o}}return{data:o,error:null,raw:o}}},h=class{constructor(e,t){this.t=e;this.table=t;this.filters={}}eq(e,t){return this.filters[e]=String(t),this}match(e){for(let t in e)this.filters[t]=String(e[t]);return this}order(e,t="asc"){return this._order=e,this._dir=t,this}limit(e){return this._limit=e,this}offset(e){return this._offset=e,this}range(e,t){return this._offset=e,this._limit=t-e+1,this}buildQuery(){let e=new URLSearchParams;this._limit!=null&&e.set("limit",String(this._limit)),this._offset!=null&&e.set("offset",String(this._offset)),this._order&&(e.set("order",this._order),e.set("dir",this._dir??"asc"));for(let r in this.filters)e.set(r,this.filters[r]);let t=e.toString();return t?`?${t}`:""}then(e,t){return this.t.request("GET",`/api/rest/${encodeURIComponent(this.table)}${this.buildQuery()}`).then(s=>({data:s.error?null:s.data.data,error:s.error,count:s.error?null:s.data.count,total:s.error?null:s.data.total})).then(e,t)}},u=class{constructor(e,t,r,s){this.t=e;this.table=t;this.method=r;this.values=s}eq(e,t){return e==="id"&&(this.id=t),this}then(e,t){if(this.id==null)return Promise.resolve({data:null,error:new a('update()/delete() require .eq("id", value)',400)}).then(e,t);let r=`/api/rest/${encodeURIComponent(this.table)}?id=${encodeURIComponent(String(this.id))}`;return this.t.request(this.method,r,this.method==="PUT"?this.values:void 0).then(n=>({data:n.error?null:n.data?.data??n.data,error:n.error})).then(e,t)}},d=class{constructor(e,t){this.t=e;this.table=t}select(){return new h(this.t,this.table)}async insert(e){let t=await this.t.request("POST",`/api/rest/${encodeURIComponent(this.table)}`,e);return{data:t.error?null:t.data?.data??t.data,error:t.error}}update(e){return new u(this.t,this.table,"PUT",e)}delete(){return new u(this.t,this.table,"DELETE")}async search(e){let t=await this.t.request("POST",`/api/rest/${encodeURIComponent(this.table)}/search`,e);return{data:t.error?null:t.data.data,error:t.error,count:t.error?null:t.data.count,total:null}}},p=class{constructor(e){this.t=e}async upload(e,t={}){let r=new FormData;r.append("file",e,t.filename??e.name??"file"),t.bucket&&r.append("bucket",t.bucket),t.visibility&&r.append("visibility",t.visibility);let s=await this.t.request("POST","/api/storage/upload",r,!0);return{data:s.error?null:{...s.data.file,url:s.data.url},error:s.error}}async list(e={}){let t=e.bucket?`?bucket=${encodeURIComponent(e.bucket)}`:"",r=await this.t.request("GET",`/api/storage/files${t}`),s=Array.isArray(r.data)?r.data:r.data?.files??null;return{data:r.error?null:s,error:r.error}}async remove(e){return await this.t.request("DELETE",`/api/storage/files/${encodeURIComponent(e)}`)}async sign(e,t=3600){return await this.t.request("POST","/api/storage/sign",{id:e,expiresIn:t})}publicUrl(e){return`${this.t.url}/api/storage/public/${encodeURIComponent(e)}`}},g=class{constructor(e,t,r){this.url=e;this.apiKey=t;this.table=r;this.ws=null;this.handlers=[]}on(e,t){return this.handlers.push({op:e,cb:t}),this}where(e){return this.filter=e,this}subscribe(){let e=globalThis.WebSocket;if(!e)throw new a("WebSocket is not available. Use a browser, Node 22+, or a WebSocket polyfill.",0);let t=this.url.replace(/^http/,"ws")+`/api/realtime/?apiKey=${encodeURIComponent(this.apiKey)}`;return this.ws=new e(t),this.ws.onopen=()=>this.ws?.send(JSON.stringify({type:"subscribe",table:this.table,filter:this.filter})),this.ws.onmessage=r=>{let s;try{s=JSON.parse(String(r.data))}catch{return}if(!(s.type!=="change"||!s.op))for(let n of this.handlers)(n.op==="*"||n.op===s.op)&&n.cb({op:s.op,table:s.table??this.table,row:s.row??{}})},this}unsubscribe(){try{this.ws?.send(JSON.stringify({type:"unsubscribe",table:this.table}))}catch{}try{this.ws?.close()}catch{}this.ws=null}},b=class{constructor(e){if(!e?.apiKey)throw new a("createClient requires an apiKey.",0);let t=(e.url??m).replace(/\/$/,"");this.apiKey=e.apiKey,this.t=new c(t,e.apiKey,e.headers??{}),this.storage=new p(this.t)}from(e){return new d(this.t,e)}async sql(e){let t=await this.t.request("POST","/api/db/query",{query_string:e});return{data:t.error?null:t.data,error:t.error}}channel(e){return new g(this.t.url,this.apiKey,e)}};function w(i){return new b(typeof i=="string"?{apiKey:i}:i)}var f=w;export{b as KreoClient,a as KreoError,w as createClient,f as default};
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "kreo-client",
3
+ "version": "0.1.0",
4
+ "description": "Official KREO JavaScript / TypeScript client — a tiny, typed, dependency-free wrapper over the KREO API (Postgres + instant REST, auth, realtime, cache & storage).",
5
+ "keywords": ["kreo", "baas", "backend", "postgres", "rest", "realtime", "supabase-alternative", "firebase-alternative"],
6
+ "license": "MIT",
7
+ "homepage": "https://kreo.work",
8
+ "repository": { "type": "git", "url": "https://github.com/kreo-work/kreo-js" },
9
+ "bugs": { "url": "mailto:contact@kreo.work" },
10
+ "author": "KREO",
11
+ "type": "module",
12
+ "sideEffects": false,
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "exports": {
17
+ ".": {
18
+ "types": "./dist/index.d.ts",
19
+ "import": "./dist/index.js",
20
+ "require": "./dist/index.cjs"
21
+ }
22
+ },
23
+ "files": ["dist", "README.md"],
24
+ "scripts": {
25
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean --minify",
26
+ "prepublishOnly": "npm run build"
27
+ },
28
+ "devDependencies": {
29
+ "tsup": "^8.0.0",
30
+ "typescript": "^5.4.0"
31
+ },
32
+ "engines": { "node": ">=18" },
33
+ "publishConfig": { "access": "public" }
34
+ }