@airostack/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,85 @@
1
+ # @airostack/client
2
+
3
+ Isomorphic client for [AiroStack](https://console.airoxlab.com) projects — Supabase-style database queries and end-user auth. Zero dependencies, works in **Node.js**, **browsers**, and **React Native / Expo**.
4
+
5
+ ```bash
6
+ npm install @airostack/client
7
+ ```
8
+
9
+ ## Setup
10
+
11
+ ```ts
12
+ import { createClient } from '@airostack/client';
13
+
14
+ const client = createClient(
15
+ 'https://<project-ref>.db.airoxlab.com', // project URL (dashboard → Connect)
16
+ '<publishable-key>', // API settings → publishable key
17
+ );
18
+ ```
19
+
20
+ **React Native:** pass AsyncStorage so sessions persist:
21
+
22
+ ```ts
23
+ import AsyncStorage from '@react-native-async-storage/async-storage';
24
+
25
+ const client = createClient(url, key, { auth: { storage: AsyncStorage } });
26
+ ```
27
+
28
+ ## Database
29
+
30
+ ```ts
31
+ // read
32
+ const { data, error } = await client
33
+ .from('orders')
34
+ .select('id, item, qty')
35
+ .eq('status', 'paid')
36
+ .order('created_at', { ascending: false })
37
+ .limit(20);
38
+
39
+ // one row
40
+ const { data: order } = await client.from('orders').select().eq('id', id).single();
41
+
42
+ // insert (returns the created rows)
43
+ const { data: created } = await client.from('orders').insert({ item: 'Burger', qty: 2 });
44
+
45
+ // update / delete
46
+ await client.from('orders').update({ status: 'done' }).eq('id', id);
47
+ await client.from('orders').delete().eq('id', id);
48
+
49
+ // count
50
+ const { count } = await client.from('orders').select('*', { count: 'exact' }).limit(1);
51
+ ```
52
+
53
+ Filters: `eq neq gt gte lt lte like ilike`, plus the generic `.filter(col, op, value)`.
54
+ Pagination: `.range(from, to)`. Errors never throw — check `error` on the result.
55
+
56
+ ## Auth
57
+
58
+ ```ts
59
+ await client.auth.signUp({ email, password, options: { data: { name: 'Ada' } } });
60
+ await client.auth.signInWithPassword({ email, password });
61
+ const { data: { user } } = await client.auth.getUser();
62
+ await client.auth.signOut();
63
+
64
+ client.auth.onAuthStateChange((event, session) => {
65
+ // 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED'
66
+ });
67
+ ```
68
+
69
+ Sessions persist automatically and access tokens refresh on demand. While a
70
+ user is signed in, every `.from()` query carries their JWT — so your
71
+ **Row Level Security** policies (`auth.uid()` etc.) apply per user, exactly
72
+ like Supabase.
73
+
74
+ ## Server-side with the secret key
75
+
76
+ ```ts
77
+ const admin = createClient(url, process.env.AIROSTACK_SECRET_KEY!, {
78
+ auth: { persistSession: false },
79
+ });
80
+ // full access, bypasses RLS — server only, never ship this key to a client
81
+ ```
82
+
83
+ ## License
84
+
85
+ MIT
package/dist/auth.d.ts ADDED
@@ -0,0 +1,62 @@
1
+ import { ApiError, type AuthResult, type Session, type StorageAdapter, type User } from './types.js';
2
+ type AuthEvent = 'SIGNED_IN' | 'SIGNED_OUT' | 'TOKEN_REFRESHED';
3
+ type AuthListener = (event: AuthEvent, session: Session | null) => void;
4
+ /**
5
+ * End-user authentication against the project's Auth API (GoTrue-style).
6
+ * Sessions persist via the storage adapter (localStorage by default; pass
7
+ * AsyncStorage in React Native) and access tokens auto-refresh on read.
8
+ */
9
+ export declare class AuthClient {
10
+ private url;
11
+ private key;
12
+ private fetcher;
13
+ private storage;
14
+ private storageKey;
15
+ private persist;
16
+ private cached;
17
+ private listeners;
18
+ constructor(url: string, key: string, fetcher: typeof fetch, opts?: {
19
+ persistSession?: boolean;
20
+ storage?: StorageAdapter;
21
+ storageKey?: string;
22
+ });
23
+ private saveSession;
24
+ private readSession;
25
+ private request;
26
+ private toResult;
27
+ signUp(creds: {
28
+ email: string;
29
+ password: string;
30
+ options?: {
31
+ data?: Record<string, unknown>;
32
+ };
33
+ }): Promise<AuthResult>;
34
+ signInWithPassword(creds: {
35
+ email: string;
36
+ password: string;
37
+ }): Promise<AuthResult>;
38
+ signOut(): Promise<{
39
+ error: ApiError | null;
40
+ }>;
41
+ /** Current session — refreshes automatically when the access token expired. */
42
+ getSession(): Promise<{
43
+ data: {
44
+ session: Session | null;
45
+ };
46
+ error: ApiError | null;
47
+ }>;
48
+ /** Current user, validated against the server. */
49
+ getUser(): Promise<{
50
+ data: {
51
+ user: User | null;
52
+ };
53
+ error: ApiError | null;
54
+ }>;
55
+ onAuthStateChange(callback: AuthListener): {
56
+ unsubscribe: () => void;
57
+ };
58
+ /** Valid access token for Data API requests, or null (anon). */
59
+ accessToken(): Promise<string | null>;
60
+ }
61
+ export {};
62
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,UAAU,EAAE,KAAK,OAAO,EAAE,KAAK,cAAc,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAC;AAqBrG,KAAK,SAAS,GAAG,WAAW,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAChE,KAAK,YAAY,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC;AAExE;;;;GAIG;AACH,qBAAa,UAAU;IAQnB,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,GAAG;IACX,OAAO,CAAC,OAAO;IATjB,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,MAAM,CAAwB;IACtC,OAAO,CAAC,SAAS,CAA2B;gBAGlC,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,KAAK,EAC7B,IAAI,CAAC,EAAE;QAAE,cAAc,CAAC,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,cAAc,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,CAAA;KAAE;YAQtE,WAAW;YASX,WAAW;YAYX,OAAO;IASrB,OAAO,CAAC,QAAQ;IAWV,MAAM,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAUrH,kBAAkB,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAUnF,OAAO,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;KAAE,CAAC;IAMpD,+EAA+E;IACzE,UAAU,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE;YAAE,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;SAAE,CAAC;QAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;KAAE,CAAC;IAmB1F,kDAAkD;IAC5C,OAAO,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE;YAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAA;SAAE,CAAC;QAAC,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;KAAE,CAAC;IAWjF,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE;IAKtE,gEAAgE;IAC1D,WAAW,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CAI5C"}
package/dist/auth.js ADDED
@@ -0,0 +1,155 @@
1
+ import { ApiError } from './types.js';
2
+ const memoryStore = () => {
3
+ const m = new Map();
4
+ return {
5
+ getItem: (k) => m.get(k) ?? null,
6
+ setItem: (k, v) => void m.set(k, v),
7
+ removeItem: (k) => void m.delete(k),
8
+ };
9
+ };
10
+ function defaultStorage() {
11
+ try {
12
+ const ls = globalThis.localStorage;
13
+ if (ls)
14
+ return ls;
15
+ }
16
+ catch {
17
+ // SSR / React Native — fall through
18
+ }
19
+ return memoryStore();
20
+ }
21
+ /**
22
+ * End-user authentication against the project's Auth API (GoTrue-style).
23
+ * Sessions persist via the storage adapter (localStorage by default; pass
24
+ * AsyncStorage in React Native) and access tokens auto-refresh on read.
25
+ */
26
+ export class AuthClient {
27
+ url;
28
+ key;
29
+ fetcher;
30
+ storage;
31
+ storageKey;
32
+ persist;
33
+ cached = null;
34
+ listeners = new Set();
35
+ constructor(url, key, fetcher, opts) {
36
+ this.url = url;
37
+ this.key = key;
38
+ this.fetcher = fetcher;
39
+ this.persist = opts?.persistSession !== false;
40
+ this.storage = opts?.storage ?? defaultStorage();
41
+ this.storageKey = opts?.storageKey ?? 'airostack.auth.token';
42
+ }
43
+ // ── session plumbing ─────────────────────────────────────────────────
44
+ async saveSession(session, event) {
45
+ this.cached = session;
46
+ if (this.persist) {
47
+ if (session)
48
+ await this.storage.setItem(this.storageKey, JSON.stringify(session));
49
+ else
50
+ await this.storage.removeItem(this.storageKey);
51
+ }
52
+ for (const l of this.listeners)
53
+ l(event, session);
54
+ }
55
+ async readSession() {
56
+ if (this.cached)
57
+ return this.cached;
58
+ if (!this.persist)
59
+ return null;
60
+ try {
61
+ const raw = await this.storage.getItem(this.storageKey);
62
+ this.cached = raw ? JSON.parse(raw) : null;
63
+ }
64
+ catch {
65
+ this.cached = null;
66
+ }
67
+ return this.cached;
68
+ }
69
+ async request(path, init) {
70
+ const res = await this.fetcher(`${this.url}${path}`, {
71
+ ...init,
72
+ headers: { apikey: this.key, 'content-type': 'application/json', ...init.headers },
73
+ });
74
+ const body = res.status === 204 ? null : await res.json().catch(() => null);
75
+ return { ok: res.ok, status: res.status, body };
76
+ }
77
+ toResult(ok, status, body) {
78
+ if (!ok) {
79
+ const msg = body?.message ?? 'authentication failed';
80
+ return { data: { user: null, session: null }, error: new ApiError(String(msg), status) };
81
+ }
82
+ const session = body;
83
+ session.expires_at = Math.floor(Date.now() / 1000) + (session.expires_in ?? 3600);
84
+ return { data: { user: session.user, session }, error: null };
85
+ }
86
+ // ── public API (supabase-shaped) ─────────────────────────────────────
87
+ async signUp(creds) {
88
+ const { ok, status, body } = await this.request('/auth/v1/signup', {
89
+ method: 'POST',
90
+ body: JSON.stringify({ email: creds.email, password: creds.password, data: creds.options?.data }),
91
+ });
92
+ const result = this.toResult(ok, status, body);
93
+ if (result.data.session)
94
+ await this.saveSession(result.data.session, 'SIGNED_IN');
95
+ return result;
96
+ }
97
+ async signInWithPassword(creds) {
98
+ const { ok, status, body } = await this.request('/auth/v1/token?grant_type=password', {
99
+ method: 'POST',
100
+ body: JSON.stringify(creds),
101
+ });
102
+ const result = this.toResult(ok, status, body);
103
+ if (result.data.session)
104
+ await this.saveSession(result.data.session, 'SIGNED_IN');
105
+ return result;
106
+ }
107
+ async signOut() {
108
+ await this.request('/auth/v1/logout', { method: 'POST' }).catch(() => null);
109
+ await this.saveSession(null, 'SIGNED_OUT');
110
+ return { error: null };
111
+ }
112
+ /** Current session — refreshes automatically when the access token expired. */
113
+ async getSession() {
114
+ let session = await this.readSession();
115
+ if (session?.expires_at && session.expires_at - 30 < Math.floor(Date.now() / 1000)) {
116
+ const { ok, status, body } = await this.request('/auth/v1/token?grant_type=refresh_token', {
117
+ method: 'POST',
118
+ body: JSON.stringify({ refresh_token: session.refresh_token }),
119
+ });
120
+ const refreshed = this.toResult(ok, status, body);
121
+ if (refreshed.data.session) {
122
+ session = refreshed.data.session;
123
+ await this.saveSession(session, 'TOKEN_REFRESHED');
124
+ }
125
+ else {
126
+ await this.saveSession(null, 'SIGNED_OUT');
127
+ return { data: { session: null }, error: refreshed.error };
128
+ }
129
+ }
130
+ return { data: { session }, error: null };
131
+ }
132
+ /** Current user, validated against the server. */
133
+ async getUser() {
134
+ const { data } = await this.getSession();
135
+ if (!data.session)
136
+ return { data: { user: null }, error: new ApiError('not signed in', 401) };
137
+ const { ok, status, body } = await this.request('/auth/v1/user', {
138
+ method: 'GET',
139
+ headers: { authorization: `Bearer ${data.session.access_token}` },
140
+ });
141
+ if (!ok)
142
+ return { data: { user: null }, error: new ApiError('invalid or expired token', status) };
143
+ return { data: { user: body }, error: null };
144
+ }
145
+ onAuthStateChange(callback) {
146
+ this.listeners.add(callback);
147
+ return { unsubscribe: () => this.listeners.delete(callback) };
148
+ }
149
+ /** Valid access token for Data API requests, or null (anon). */
150
+ async accessToken() {
151
+ const { data } = await this.getSession();
152
+ return data.session?.access_token ?? null;
153
+ }
154
+ }
155
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { AuthClient } from './auth.js';
2
+ import { QueryBuilder } from './query-builder.js';
3
+ import type { ClientOptions } from './types.js';
4
+ export { ApiError } from './types.js';
5
+ export type { ApiResult, AuthResult, ClientOptions, Session, StorageAdapter, User } from './types.js';
6
+ export { AuthClient } from './auth.js';
7
+ export { QueryBuilder } from './query-builder.js';
8
+ /**
9
+ * AiroStack client — Supabase-style API over your project's Data API + Auth.
10
+ *
11
+ * import { createClient } from '@airostack/client';
12
+ * const client = createClient(process.env.AIROSTACK_URL!, process.env.AIROSTACK_KEY!);
13
+ *
14
+ * const { data, error } = await client.from('orders').select().eq('status', 'paid');
15
+ * await client.auth.signInWithPassword({ email, password });
16
+ */
17
+ export declare class AiroStackClient {
18
+ private url;
19
+ private key;
20
+ readonly auth: AuthClient;
21
+ private fetcher;
22
+ constructor(url: string, key: string, options?: ClientOptions);
23
+ /** Query a table through the Data API (RLS applies per the caller's key/JWT). */
24
+ from<T = Record<string, unknown>>(table: string): QueryBuilder<T>;
25
+ }
26
+ export declare function createClient(url: string, key: string, options?: ClientOptions): AiroStackClient;
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACtG,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;;;;GAQG;AACH,qBAAa,eAAe;IAId,OAAO,CAAC,GAAG;IAAU,OAAO,CAAC,GAAG;IAH5C,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,OAAO,CAAC,OAAO,CAAe;gBAEV,GAAG,EAAE,MAAM,EAAU,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa;IAO7E,iFAAiF;IACjF,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,YAAY,CAAC,CAAC,CAAC;CAMlE;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,eAAe,CAE/F"}
package/dist/index.js ADDED
@@ -0,0 +1,37 @@
1
+ import { AuthClient } from './auth.js';
2
+ import { QueryBuilder } from './query-builder.js';
3
+ export { ApiError } from './types.js';
4
+ export { AuthClient } from './auth.js';
5
+ export { QueryBuilder } from './query-builder.js';
6
+ /**
7
+ * AiroStack client — Supabase-style API over your project's Data API + Auth.
8
+ *
9
+ * import { createClient } from '@airostack/client';
10
+ * const client = createClient(process.env.AIROSTACK_URL!, process.env.AIROSTACK_KEY!);
11
+ *
12
+ * const { data, error } = await client.from('orders').select().eq('status', 'paid');
13
+ * await client.auth.signInWithPassword({ email, password });
14
+ */
15
+ export class AiroStackClient {
16
+ url;
17
+ key;
18
+ auth;
19
+ fetcher;
20
+ constructor(url, key, options) {
21
+ this.url = url;
22
+ this.key = key;
23
+ if (!url || !key)
24
+ throw new Error('createClient(url, key) — both are required');
25
+ this.url = url.replace(/\/+$/, '');
26
+ this.fetcher = options?.fetch ?? ((...args) => fetch(...args));
27
+ this.auth = new AuthClient(this.url, key, this.fetcher, options?.auth);
28
+ }
29
+ /** Query a table through the Data API (RLS applies per the caller's key/JWT). */
30
+ from(table) {
31
+ return new QueryBuilder({ url: this.url, key: this.key, fetch: this.fetcher, accessToken: () => this.auth.accessToken() }, table);
32
+ }
33
+ }
34
+ export function createClient(url, key, options) {
35
+ return new AiroStackClient(url, key, options);
36
+ }
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAGlD,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEtC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;;;;GAQG;AACH,MAAM,OAAO,eAAe;IAIN;IAAqB;IAHhC,IAAI,CAAa;IAClB,OAAO,CAAe;IAE9B,YAAoB,GAAW,EAAU,GAAW,EAAE,OAAuB;QAAzD,QAAG,GAAH,GAAG,CAAQ;QAAU,QAAG,GAAH,GAAG,CAAQ;QAClD,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAChF,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,OAAO,EAAE,KAAK,IAAI,CAAC,CAAC,GAAG,IAA8B,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QACzF,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;IACzE,CAAC;IAED,iFAAiF;IACjF,IAAI,CAA8B,KAAa;QAC7C,OAAO,IAAI,YAAY,CACrB,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,EACjG,KAAK,CACN,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,YAAY,CAAC,GAAW,EAAE,GAAW,EAAE,OAAuB;IAC5E,OAAO,IAAI,eAAe,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;AAChD,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { type ApiResult } from './types.js';
2
+ type Fetcher = typeof fetch;
3
+ type TokenProvider = () => Promise<string | null>;
4
+ interface Ctx {
5
+ url: string;
6
+ key: string;
7
+ fetch: Fetcher;
8
+ accessToken: TokenProvider;
9
+ }
10
+ /**
11
+ * Supabase-style fluent query builder over the AiroStack Data API
12
+ * (PostgREST dialect). Thenable — `await` it anywhere in the chain.
13
+ *
14
+ * const { data, error } = await client.from('orders')
15
+ * .select('id, item, qty').eq('status', 'paid')
16
+ * .order('created_at', { ascending: false }).limit(20);
17
+ */
18
+ export declare class QueryBuilder<T = Record<string, unknown>> implements PromiseLike<ApiResult<T[]>> {
19
+ private ctx;
20
+ private table;
21
+ private params;
22
+ private method;
23
+ private body;
24
+ private wantCount;
25
+ private takeSingle;
26
+ constructor(ctx: Ctx, table: string);
27
+ select(columns?: string, opts?: {
28
+ count?: 'exact';
29
+ }): this;
30
+ order(column: string, opts?: {
31
+ ascending?: boolean;
32
+ }): this;
33
+ limit(n: number): this;
34
+ range(from: number, to: number): this;
35
+ /** resolve a single row — errors if none found */
36
+ single(): this;
37
+ /** resolve a single row or null */
38
+ maybeSingle(): this;
39
+ eq(column: string, value: unknown): this;
40
+ neq(column: string, value: unknown): this;
41
+ gt(column: string, value: unknown): this;
42
+ gte(column: string, value: unknown): this;
43
+ lt(column: string, value: unknown): this;
44
+ lte(column: string, value: unknown): this;
45
+ like(column: string, pattern: string): this;
46
+ ilike(column: string, pattern: string): this;
47
+ /** generic escape hatch: .filter('qty', 'gte', 10) */
48
+ filter(column: string, operator: string, value: unknown): this;
49
+ insert(values: Partial<T> | Partial<T>[]): this;
50
+ update(values: Partial<T>): this;
51
+ delete(): this;
52
+ then<R1 = ApiResult<T[]>, R2 = never>(onfulfilled?: ((value: ApiResult<T[]>) => R1 | PromiseLike<R1>) | null, onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null): PromiseLike<R1 | R2>;
53
+ private execute;
54
+ }
55
+ export {};
56
+ //# sourceMappingURL=query-builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../src/query-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,132 @@
1
+ import { ApiError } from './types.js';
2
+ /**
3
+ * Supabase-style fluent query builder over the AiroStack Data API
4
+ * (PostgREST dialect). Thenable — `await` it anywhere in the chain.
5
+ *
6
+ * const { data, error } = await client.from('orders')
7
+ * .select('id, item, qty').eq('status', 'paid')
8
+ * .order('created_at', { ascending: false }).limit(20);
9
+ */
10
+ export class QueryBuilder {
11
+ ctx;
12
+ table;
13
+ params = new URLSearchParams();
14
+ method = 'GET';
15
+ body = undefined;
16
+ wantCount = false;
17
+ takeSingle = null;
18
+ constructor(ctx, table) {
19
+ this.ctx = ctx;
20
+ this.table = table;
21
+ }
22
+ // ── shape ────────────────────────────────────────────────────────────
23
+ select(columns = '*', opts) {
24
+ if (columns && columns !== '*')
25
+ this.params.set('select', columns.replace(/\s+/g, ''));
26
+ if (opts?.count === 'exact')
27
+ this.wantCount = true;
28
+ return this;
29
+ }
30
+ order(column, opts) {
31
+ this.params.set('order', `${column}.${opts?.ascending === false ? 'desc' : 'asc'}`);
32
+ return this;
33
+ }
34
+ limit(n) {
35
+ this.params.set('limit', String(n));
36
+ return this;
37
+ }
38
+ range(from, to) {
39
+ this.params.set('offset', String(from));
40
+ this.params.set('limit', String(to - from + 1));
41
+ return this;
42
+ }
43
+ /** resolve a single row — errors if none found */
44
+ single() {
45
+ this.takeSingle = 'strict';
46
+ this.params.set('limit', '2');
47
+ return this;
48
+ }
49
+ /** resolve a single row or null */
50
+ maybeSingle() {
51
+ this.takeSingle = 'maybe';
52
+ this.params.set('limit', '2');
53
+ return this;
54
+ }
55
+ // ── filters (PostgREST operators) ────────────────────────────────────
56
+ eq(column, value) { return this.filter(column, 'eq', value); }
57
+ neq(column, value) { return this.filter(column, 'neq', value); }
58
+ gt(column, value) { return this.filter(column, 'gt', value); }
59
+ gte(column, value) { return this.filter(column, 'gte', value); }
60
+ lt(column, value) { return this.filter(column, 'lt', value); }
61
+ lte(column, value) { return this.filter(column, 'lte', value); }
62
+ like(column, pattern) { return this.filter(column, 'like', pattern); }
63
+ ilike(column, pattern) { return this.filter(column, 'ilike', pattern); }
64
+ /** generic escape hatch: .filter('qty', 'gte', 10) */
65
+ filter(column, operator, value) {
66
+ this.params.append(column, `${operator}.${String(value)}`);
67
+ return this;
68
+ }
69
+ // ── writes ───────────────────────────────────────────────────────────
70
+ insert(values) {
71
+ this.method = 'POST';
72
+ this.body = values;
73
+ return this;
74
+ }
75
+ update(values) {
76
+ this.method = 'PATCH';
77
+ this.body = values;
78
+ return this;
79
+ }
80
+ delete() {
81
+ this.method = 'DELETE';
82
+ return this;
83
+ }
84
+ // ── execution ────────────────────────────────────────────────────────
85
+ then(onfulfilled, onrejected) {
86
+ return this.execute().then(onfulfilled, onrejected);
87
+ }
88
+ async execute() {
89
+ try {
90
+ const qs = this.params.toString();
91
+ const url = `${this.ctx.url}/rest/v1/${this.table}${qs ? `?${qs}` : ''}`;
92
+ const headers = { apikey: this.ctx.key };
93
+ const token = await this.ctx.accessToken();
94
+ if (token)
95
+ headers.authorization = `Bearer ${token}`;
96
+ if (this.wantCount)
97
+ headers.prefer = 'count=exact';
98
+ if (this.body !== undefined)
99
+ headers['content-type'] = 'application/json';
100
+ const res = await this.ctx.fetch(url, {
101
+ method: this.method,
102
+ headers,
103
+ body: this.body !== undefined ? JSON.stringify(this.body) : undefined,
104
+ });
105
+ if (!res.ok) {
106
+ const body = (await res.json().catch(() => ({})));
107
+ const msg = typeof body.message === 'string' ? body.message : `request failed (${res.status})`;
108
+ return { data: null, error: new ApiError(msg, res.status), count: null, status: res.status };
109
+ }
110
+ let count = null;
111
+ const range = res.headers.get('content-range');
112
+ if (range?.includes('/'))
113
+ count = Number(range.split('/')[1]) || 0;
114
+ let rows = (res.status === 204 ? [] : (await res.json())) ?? [];
115
+ if (!Array.isArray(rows))
116
+ rows = [rows];
117
+ if (this.takeSingle) {
118
+ const row = rows[0] ?? null;
119
+ if (this.takeSingle === 'strict' && !row) {
120
+ return { data: null, error: new ApiError('no rows returned', 406), count, status: res.status };
121
+ }
122
+ // .single() resolves an object, not an array — cast keeps the thenable type simple
123
+ return { data: row, error: null, count, status: res.status };
124
+ }
125
+ return { data: rows, error: null, count, status: res.status };
126
+ }
127
+ catch (err) {
128
+ return { data: null, error: new ApiError(err.message, 0), count: null, status: 0 };
129
+ }
130
+ }
131
+ }
132
+ //# sourceMappingURL=query-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-builder.js","sourceRoot":"","sources":["../src/query-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /** Shape of every data response — supabase-style: never throws, always
2
+ * `{ data, error }`. */
3
+ export interface ApiResult<T> {
4
+ data: T | null;
5
+ error: ApiError | null;
6
+ /** total row count — present when `count: 'exact'` was requested */
7
+ count: number | null;
8
+ status: number;
9
+ }
10
+ export declare class ApiError extends Error {
11
+ status: number;
12
+ constructor(message: string, status: number);
13
+ }
14
+ export interface User {
15
+ id: string;
16
+ email: string;
17
+ user_metadata?: Record<string, unknown>;
18
+ created_at?: string;
19
+ }
20
+ export interface Session {
21
+ access_token: string;
22
+ refresh_token: string;
23
+ token_type: 'bearer';
24
+ expires_in: number;
25
+ /** unix seconds — computed client-side when the session is stored */
26
+ expires_at?: number;
27
+ user: User;
28
+ }
29
+ export interface AuthResult {
30
+ data: {
31
+ user: User | null;
32
+ session: Session | null;
33
+ };
34
+ error: ApiError | null;
35
+ }
36
+ /** Pluggable persistence — localStorage in browsers (default), AsyncStorage in
37
+ * React Native, or anything with this shape. */
38
+ export interface StorageAdapter {
39
+ getItem(key: string): string | null | Promise<string | null>;
40
+ setItem(key: string, value: string): void | Promise<void>;
41
+ removeItem(key: string): void | Promise<void>;
42
+ }
43
+ export interface ClientOptions {
44
+ /** custom fetch implementation (defaults to globalThis.fetch) */
45
+ fetch?: typeof fetch;
46
+ auth?: {
47
+ /** persist the session (default true) */
48
+ persistSession?: boolean;
49
+ /** where to persist it — pass AsyncStorage in React Native */
50
+ storage?: StorageAdapter;
51
+ /** storage key (default 'airostack.auth.token') */
52
+ storageKey?: string;
53
+ };
54
+ }
55
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;yBACyB;AACzB,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,GAAG,IAAI,CAAC;IACf,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;IACvB,oEAAoE;IACpE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,QAAS,SAAQ,KAAK;IACjC,MAAM,EAAE,MAAM,CAAC;gBACH,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CAK5C;AAED,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,OAAO;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,QAAQ,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE;QAAE,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;KAAE,CAAC;IACrD,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAC;CACxB;AAED;iDACiD;AACjD,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAC7D,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/C;AAED,MAAM,WAAW,aAAa;IAC5B,iEAAiE;IACjE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,IAAI,CAAC,EAAE;QACL,yCAAyC;QACzC,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,8DAA8D;QAC9D,OAAO,CAAC,EAAE,cAAc,CAAC;QACzB,mDAAmD;QACnD,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;CACH"}
package/dist/types.js ADDED
@@ -0,0 +1,9 @@
1
+ export class ApiError extends Error {
2
+ status;
3
+ constructor(message, status) {
4
+ super(message);
5
+ this.name = 'ApiError';
6
+ this.status = status;
7
+ }
8
+ }
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAUA,MAAM,OAAO,QAAS,SAAQ,KAAK;IACjC,MAAM,CAAS;IACf,YAAY,OAAe,EAAE,MAAc;QACzC,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,UAAU,CAAC;QACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACvB,CAAC;CACF"}
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@airostack/client",
3
+ "version": "0.1.0",
4
+ "description": "Isomorphic client for AiroStack projects — Supabase-style Data API queries and Auth. Works in Node.js, browsers, and React Native.",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/airoxlab/baseway.git",
9
+ "directory": "packages/client"
10
+ },
11
+ "keywords": ["airostack", "postgrest", "database", "auth", "baas", "react-native"],
12
+ "type": "module",
13
+ "main": "./dist/index.js",
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
+ "default": "./dist/index.js"
21
+ }
22
+ },
23
+ "files": ["dist", "README.md"],
24
+ "sideEffects": false,
25
+ "scripts": {
26
+ "build": "tsc -p tsconfig.build.json",
27
+ "typecheck": "tsc --noEmit",
28
+ "clean": "rimraf dist .turbo",
29
+ "prepublishOnly": "npm run build"
30
+ },
31
+ "devDependencies": {
32
+ "typescript": "^5.7.2"
33
+ }
34
+ }