@motherbase/client 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,121 @@
1
+ # @motherbase/client
2
+
3
+ Typed, isomorphic client SDK for the Motherbase client. Works in the browser,
4
+ Next.js (server & client), and Expo Go / React Native. Built on the global
5
+ `fetch` with zero runtime dependencies (`socket.io-client` is an optional peer
6
+ dependency, used only for realtime).
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm i @motherbase/client
12
+ # optional, only if you use realtime:
13
+ npm i socket.io-client
14
+ ```
15
+
16
+ ## Quickstart
17
+
18
+ ```ts
19
+ import { createClient, localStorageStorage } from '@motherbase/client';
20
+
21
+ const client = createClient('http://localhost:5050', {
22
+ storage: localStorageStorage(), // omit for in-memory (SSR-safe) sessions
23
+ });
24
+
25
+ await client.hydrate(); // restore a persisted session (optional)
26
+
27
+ // Auth
28
+ await client.auth.register({ email: 'me@example.com', password: 'secret123' });
29
+ const session = await client.auth.login({ email: 'me@example.com', password: 'secret123' });
30
+ const user = await client.auth.getUser();
31
+
32
+ // React to auth changes
33
+ const unsubscribe = client.auth.onAuthStateChange((event, session) => {
34
+ console.log(event, session?.user.email);
35
+ });
36
+
37
+ // Storage
38
+ const file = await client.storage.upload(myBlob, { visibility: 'public' });
39
+ const url = await client.storage.getUrl(file.id);
40
+
41
+ // Devices (push)
42
+ await client.devices.register({ token: pushToken, platform: 'ios' });
43
+
44
+ // REST API (policy-checked /rest/v1)
45
+ const products = await client.rest.from<{ id: string; name: string; price: number }>('products').list({
46
+ filters: { price: { op: 'lt', value: 50 } },
47
+ order: [{ field: 'name' }],
48
+ limit: 20,
49
+ });
50
+ await client.rest.insert('products', { name: 'Tea', price: 12 });
51
+
52
+ // Edge functions
53
+ const result = await client.edge.invoke<{ ok: boolean }>('hello', { name: 'Ada' });
54
+
55
+ // Realtime (requires socket.io-client)
56
+ await client.realtime.connect();
57
+ client.realtime.on('message', (msg) => console.log(msg));
58
+ ```
59
+
60
+ The SDK attaches the access token automatically and transparently refreshes it on
61
+ a 401 (single-flight: concurrent requests share one refresh). When the refresh
62
+ token is invalid it clears the session and emits a `signOut` event.
63
+
64
+ ## Storage adapters
65
+
66
+ | Runtime | Adapter |
67
+ | -------- | ------- |
68
+ | Browser | `localStorageStorage()` |
69
+ | SSR/Node | `memoryStorage()` (default) |
70
+ | Expo/RN | custom `{ get, set }` backed by `AsyncStorage` |
71
+
72
+ ```ts
73
+ import AsyncStorage from '@react-native-async-storage/async-storage';
74
+ import { createClient } from '@motherbase/client';
75
+
76
+ const client = createClient(API_URL, {
77
+ storage: {
78
+ async get() {
79
+ const raw = await AsyncStorage.getItem('session');
80
+ return raw ? JSON.parse(raw) : null;
81
+ },
82
+ async set(session) {
83
+ if (session) await AsyncStorage.setItem('session', JSON.stringify(session));
84
+ else await AsyncStorage.removeItem('session');
85
+ },
86
+ },
87
+ });
88
+ ```
89
+
90
+ ## API surface
91
+
92
+ - `client.auth` — register, login, loginWithEmail, validateEmail, refresh,
93
+ logout, forgotPassword, resetPassword, getUser, getSession, onAuthStateChange
94
+ - `client.users` — updateUsername, updateImage, deleteAccount
95
+ - `client.storage` — upload, presign, getUrl, remove
96
+ - `client.devices` — register, unregister
97
+ - `client.rest` — typed CRUD over `/rest/v1/:model` with policy checks
98
+ - `client.edge` — invoke public edge routes exposed at `/api/edge/:route`
99
+ - `client.realtime` — connect, on, off, emit, disconnect
100
+
101
+ All methods reject with a `clientError` (`message`, `httpStatus`, `apiStatus`)
102
+ on failure.
103
+
104
+ More examples live in [`docs/sdk.md`](../../docs/sdk.md). Studio also exposes a
105
+ project-specific Connect page with the exact API URL, WebSocket URL and project
106
+ ID to paste into `.env`.
107
+
108
+ ### REST filters
109
+
110
+ `client.rest` accepts the client query operators directly:
111
+
112
+ ```ts
113
+ await client.rest.from('products').list({
114
+ select: ['id', 'name'],
115
+ filters: {
116
+ price: { op: 'lte', value: 100 },
117
+ status: { op: 'in', value: ['active', 'draft'] },
118
+ },
119
+ order: [{ field: 'createdAt', direction: 'desc' }],
120
+ });
121
+ ```
package/dist/auth.d.ts ADDED
@@ -0,0 +1,61 @@
1
+ import type { HttpClient } from './http.js';
2
+ import type { SessionStore } from './session.js';
3
+ import type { AuthListener, Session, User } from './types.js';
4
+ interface RegisterParams {
5
+ email: string;
6
+ password: string;
7
+ username?: string;
8
+ }
9
+ interface LoginParams {
10
+ email: string;
11
+ password: string;
12
+ }
13
+ interface ResetPasswordParams {
14
+ email: string;
15
+ secretCode: string;
16
+ newPassword: string;
17
+ }
18
+ interface AppleAuthParams {
19
+ identityToken: string;
20
+ email?: string | null;
21
+ fullName?: {
22
+ givenName?: string | null;
23
+ familyName?: string | null;
24
+ } | null;
25
+ }
26
+ export declare class AuthModule {
27
+ private readonly http;
28
+ private readonly store;
29
+ constructor(http: HttpClient, store: SessionStore);
30
+ register(params: RegisterParams): Promise<Session>;
31
+ login(params: LoginParams): Promise<Session>;
32
+ /** Passwordless email flow: requests a verification code. */
33
+ loginWithEmail(params: {
34
+ email: string;
35
+ }): Promise<unknown>;
36
+ validateEmail(params: {
37
+ emailVerificationId: string;
38
+ secretCode: string;
39
+ }): Promise<Session>;
40
+ forgotPassword(params: {
41
+ email: string;
42
+ }): Promise<unknown>;
43
+ resetPassword(params: ResetPasswordParams): Promise<unknown>;
44
+ loginWithGoogle(params: {
45
+ idToken: string;
46
+ }): Promise<Session>;
47
+ google(idToken: string): Promise<Session>;
48
+ loginWithApple(params: AppleAuthParams): Promise<Session>;
49
+ apple(params: AppleAuthParams): Promise<Session>;
50
+ /** Rotate tokens using the stored refresh token. Returns true on success. */
51
+ refresh(): Promise<boolean>;
52
+ logout(): Promise<void>;
53
+ /** Fetch the current user from the API (requires a valid session). */
54
+ getUser(): Promise<User>;
55
+ /** The locally-held session, if any. */
56
+ getSession(): Session | null;
57
+ onAuthStateChange(listener: AuthListener): () => void;
58
+ private persist;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAE9D,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AACD,UAAU,mBAAmB;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AACD,UAAU,eAAe;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAC;CAC7E;AAeD,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;IAAc,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAxC,IAAI,EAAE,UAAU,EAAmB,KAAK,EAAE,YAAY;IAE7E,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IASlD,KAAK,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IASlD,6DAA6D;IAC7D,cAAc,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAIrD,aAAa,CAAC,MAAM,EAAE;QAAE,mBAAmB,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IASlG,cAAc,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI3D,aAAa,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,OAAO,CAAC;IAItD,eAAe,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IASpE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAInC,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAS/D,KAAK,CAAC,MAAM,EAAE,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC;IAIhD,6EAA6E;IACvE,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;IAgB3B,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB7B,sEAAsE;IACtE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAIxB,wCAAwC;IACxC,UAAU,IAAI,OAAO,GAAG,IAAI;IAI5B,iBAAiB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,IAAI;YAIvC,OAAO;CAKtB"}
package/dist/auth.js ADDED
@@ -0,0 +1,115 @@
1
+ /** Split the flattened auth payload into a clean Session. */
2
+ function toSession(payload) {
3
+ const { accessToken, refreshToken, userToken: _userToken, ...user } = payload;
4
+ return { accessToken, refreshToken, user: user };
5
+ }
6
+ export class AuthModule {
7
+ constructor(http, store) {
8
+ this.http = http;
9
+ this.store = store;
10
+ }
11
+ async register(params) {
12
+ const payload = await this.http.request('/api/auth/register', {
13
+ method: 'POST',
14
+ body: params,
15
+ anonymous: true,
16
+ });
17
+ return this.persist(payload);
18
+ }
19
+ async login(params) {
20
+ const payload = await this.http.request('/api/auth/login', {
21
+ method: 'POST',
22
+ body: params,
23
+ anonymous: true,
24
+ });
25
+ return this.persist(payload);
26
+ }
27
+ /** Passwordless email flow: requests a verification code. */
28
+ loginWithEmail(params) {
29
+ return this.http.request('/api/auth/email', { method: 'PUT', body: params, anonymous: true });
30
+ }
31
+ async validateEmail(params) {
32
+ const payload = await this.http.request('/api/auth/validate-email', {
33
+ method: 'PUT',
34
+ body: params,
35
+ anonymous: true,
36
+ });
37
+ return this.persist(payload);
38
+ }
39
+ forgotPassword(params) {
40
+ return this.http.request('/api/auth/forgot-password', { method: 'POST', body: params, anonymous: true });
41
+ }
42
+ resetPassword(params) {
43
+ return this.http.request('/api/auth/reset-password', { method: 'POST', body: params, anonymous: true });
44
+ }
45
+ async loginWithGoogle(params) {
46
+ const payload = await this.http.request('/api/auth/google', {
47
+ method: 'PUT',
48
+ body: params,
49
+ anonymous: true,
50
+ });
51
+ return this.persist(payload);
52
+ }
53
+ google(idToken) {
54
+ return this.loginWithGoogle({ idToken });
55
+ }
56
+ async loginWithApple(params) {
57
+ const payload = await this.http.request('/api/auth/apple', {
58
+ method: 'PUT',
59
+ body: params,
60
+ anonymous: true,
61
+ });
62
+ return this.persist(payload);
63
+ }
64
+ apple(params) {
65
+ return this.loginWithApple(params);
66
+ }
67
+ /** Rotate tokens using the stored refresh token. Returns true on success. */
68
+ async refresh() {
69
+ const session = this.store.get();
70
+ if (!session?.refreshToken)
71
+ return false;
72
+ try {
73
+ const tokens = await this.http.request('/api/auth/refresh', { method: 'POST', body: { refreshToken: session.refreshToken }, anonymous: true });
74
+ await this.store.setTokens(tokens.accessToken, tokens.refreshToken);
75
+ return true;
76
+ }
77
+ catch {
78
+ await this.store.set(null, 'signOut');
79
+ return false;
80
+ }
81
+ }
82
+ async logout() {
83
+ const session = this.store.get();
84
+ if (session?.refreshToken) {
85
+ try {
86
+ await this.http.request('/api/auth/logout', {
87
+ method: 'POST',
88
+ body: { refreshToken: session.refreshToken },
89
+ anonymous: true,
90
+ });
91
+ }
92
+ catch {
93
+ // best-effort; clear locally regardless
94
+ }
95
+ }
96
+ await this.store.set(null, 'signOut');
97
+ }
98
+ /** Fetch the current user from the API (requires a valid session). */
99
+ getUser() {
100
+ return this.http.request('/api/auth/');
101
+ }
102
+ /** The locally-held session, if any. */
103
+ getSession() {
104
+ return this.store.get();
105
+ }
106
+ onAuthStateChange(listener) {
107
+ return this.store.subscribe(listener);
108
+ }
109
+ async persist(payload) {
110
+ const session = toSession(payload);
111
+ await this.store.set(session, 'signIn');
112
+ return session;
113
+ }
114
+ }
115
+ //# 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,13 @@
1
+ import type { HttpClient } from './http.js';
2
+ import type { DevicePlatform } from './types.js';
3
+ export declare class DevicesModule {
4
+ private readonly http;
5
+ constructor(http: HttpClient);
6
+ /** Register a push token for the current user. */
7
+ register(params: {
8
+ token: string;
9
+ platform: DevicePlatform;
10
+ }): Promise<unknown>;
11
+ unregister(token: string): Promise<unknown>;
12
+ }
13
+ //# sourceMappingURL=devices.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.d.ts","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,kDAAkD;IAClD,QAAQ,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,cAAc,CAAA;KAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAI/E,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAG5C"}
@@ -0,0 +1,13 @@
1
+ export class DevicesModule {
2
+ constructor(http) {
3
+ this.http = http;
4
+ }
5
+ /** Register a push token for the current user. */
6
+ register(params) {
7
+ return this.http.request('/api/devices/', { method: 'POST', body: params });
8
+ }
9
+ unregister(token) {
10
+ return this.http.request('/api/devices/', { method: 'DELETE', body: { token } });
11
+ }
12
+ }
13
+ //# sourceMappingURL=devices.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"devices.js","sourceRoot":"","sources":["../src/devices.ts"],"names":[],"mappings":"AAGA,MAAM,OAAO,aAAa;IACxB,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,kDAAkD;IAClD,QAAQ,CAAC,MAAmD;QAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,UAAU,CAAC,KAAa;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;IACnF,CAAC;CACF"}
package/dist/edge.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ import type { HttpClient } from './http.js';
2
+ export declare class EdgeModule {
3
+ private readonly http;
4
+ constructor(http: HttpClient);
5
+ invoke<Result = unknown, Payload = Record<string, unknown>>(route: string, payload?: Payload): Promise<Result>;
6
+ }
7
+ //# sourceMappingURL=edge.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.d.ts","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAE5C,qBAAa,UAAU;IACT,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,MAAM,CAAC,MAAM,GAAG,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC;CAO/G"}
package/dist/edge.js ADDED
@@ -0,0 +1,13 @@
1
+ export class EdgeModule {
2
+ constructor(http) {
3
+ this.http = http;
4
+ }
5
+ invoke(route, payload) {
6
+ return this.http.request(`/api/edge/${encodeURIComponent(route)}`, {
7
+ method: 'POST',
8
+ body: payload ?? {},
9
+ anonymous: true,
10
+ });
11
+ }
12
+ }
13
+ //# sourceMappingURL=edge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"edge.js","sourceRoot":"","sources":["../src/edge.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,UAAU;IACrB,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,MAAM,CAAsD,KAAa,EAAE,OAAiB;QAC1F,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAS,aAAa,kBAAkB,CAAC,KAAK,CAAC,EAAE,EAAE;YACzE,MAAM,EAAE,MAAM;YACd,IAAI,EAAE,OAAO,IAAI,EAAE;YACnB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;IACL,CAAC;CACF"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { SessionStore } from './session.js';
2
+ /** Error thrown for any non-successful API response. */
3
+ export declare class BackendError extends Error {
4
+ /** HTTP status code (0 if the request never completed). */
5
+ readonly httpStatus: number;
6
+ /** The API envelope's `status` field when present. */
7
+ readonly apiStatus?: number;
8
+ constructor(message: string, httpStatus: number, apiStatus?: number);
9
+ }
10
+ export interface RequestOptions {
11
+ method?: string;
12
+ /** JSON body. Mutually exclusive with `form`. */
13
+ body?: unknown;
14
+ /** Multipart body. Mutually exclusive with `body`. */
15
+ form?: FormData;
16
+ /** Skip attaching the Authorization header (used by auth endpoints). */
17
+ anonymous?: boolean;
18
+ /** Internal: prevents infinite refresh recursion. */
19
+ _retry?: boolean;
20
+ }
21
+ /**
22
+ * fetch wrapper that injects the bearer token, unwraps the API envelope, maps
23
+ * failures to BackendError, and performs single-flight token refresh on 401.
24
+ */
25
+ export declare class HttpClient {
26
+ private readonly baseUrl;
27
+ private readonly store;
28
+ private readonly opts;
29
+ /** Performs the refresh; returns true on success. Injected to avoid a cycle. */
30
+ private readonly doRefresh;
31
+ private refreshPromise;
32
+ constructor(baseUrl: string, store: SessionStore, opts: {
33
+ autoRefresh: boolean;
34
+ fetch: typeof fetch;
35
+ },
36
+ /** Performs the refresh; returns true on success. Injected to avoid a cycle. */
37
+ doRefresh: () => Promise<boolean>);
38
+ request<T>(path: string, options?: RequestOptions): Promise<T>;
39
+ /** Shared single-flight refresh: concurrent callers await the same promise. */
40
+ private refreshOnce;
41
+ private send;
42
+ private parse;
43
+ private url;
44
+ }
45
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,wDAAwD;AACxD,qBAAa,YAAa,SAAQ,KAAK;IACrC,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,sDAAsD;IACtD,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;gBAEhB,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;CAMpE;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,iDAAiD;IACjD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,sDAAsD;IACtD,IAAI,CAAC,EAAE,QAAQ,CAAC;IAChB,wEAAwE;IACxE,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,qDAAqD;IACrD,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;GAGG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,OAAO;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB,gFAAgF;IAChF,OAAO,CAAC,QAAQ,CAAC,SAAS;IAP5B,OAAO,CAAC,cAAc,CAAiC;gBAGpC,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE;QAAE,WAAW,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,OAAO,KAAK,CAAA;KAAE;IACpE,gFAAgF;IAC/D,SAAS,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC;IAG9C,OAAO,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,CAAC,CAAC;IAaxE,+EAA+E;IAC/E,OAAO,CAAC,WAAW;YASL,IAAI;YAsBJ,KAAK;IAgBnB,OAAO,CAAC,GAAG;CAGZ"}
package/dist/http.js ADDED
@@ -0,0 +1,81 @@
1
+ /** Error thrown for any non-successful API response. */
2
+ export class BackendError extends Error {
3
+ constructor(message, httpStatus, apiStatus) {
4
+ super(message);
5
+ this.name = 'BackendError';
6
+ this.httpStatus = httpStatus;
7
+ this.apiStatus = apiStatus;
8
+ }
9
+ }
10
+ /**
11
+ * fetch wrapper that injects the bearer token, unwraps the API envelope, maps
12
+ * failures to BackendError, and performs single-flight token refresh on 401.
13
+ */
14
+ export class HttpClient {
15
+ constructor(baseUrl, store, opts,
16
+ /** Performs the refresh; returns true on success. Injected to avoid a cycle. */
17
+ doRefresh) {
18
+ this.baseUrl = baseUrl;
19
+ this.store = store;
20
+ this.opts = opts;
21
+ this.doRefresh = doRefresh;
22
+ this.refreshPromise = null;
23
+ }
24
+ async request(path, options = {}) {
25
+ const res = await this.send(path, options);
26
+ if (res.status === 401 && this.opts.autoRefresh && !options.anonymous && !options._retry) {
27
+ const refreshed = await this.refreshOnce();
28
+ if (refreshed) {
29
+ return this.request(path, { ...options, _retry: true });
30
+ }
31
+ }
32
+ return this.parse(res);
33
+ }
34
+ /** Shared single-flight refresh: concurrent callers await the same promise. */
35
+ refreshOnce() {
36
+ if (!this.refreshPromise) {
37
+ this.refreshPromise = this.doRefresh().finally(() => {
38
+ this.refreshPromise = null;
39
+ });
40
+ }
41
+ return this.refreshPromise;
42
+ }
43
+ async send(path, options) {
44
+ const headers = {};
45
+ const session = this.store.get();
46
+ if (!options.anonymous && session?.accessToken) {
47
+ headers['Authorization'] = `Bearer ${session.accessToken}`;
48
+ }
49
+ let body;
50
+ if (options.form) {
51
+ body = options.form; // browser/runtime sets the multipart boundary
52
+ }
53
+ else if (options.body !== undefined) {
54
+ headers['Content-Type'] = 'application/json';
55
+ body = JSON.stringify(options.body);
56
+ }
57
+ return this.opts.fetch(this.url(path), {
58
+ method: options.method ?? 'GET',
59
+ headers,
60
+ body,
61
+ });
62
+ }
63
+ async parse(res) {
64
+ let envelope;
65
+ try {
66
+ envelope = (await res.json());
67
+ }
68
+ catch {
69
+ // non-JSON response
70
+ }
71
+ if (!res.ok || (envelope && envelope.status === 0)) {
72
+ const message = envelope?.message ?? `Request failed with status ${res.status}`;
73
+ throw new BackendError(message, res.status, envelope?.status);
74
+ }
75
+ return (envelope?.data ?? envelope);
76
+ }
77
+ url(path) {
78
+ return `${this.baseUrl.replace(/\/$/, '')}${path}`;
79
+ }
80
+ }
81
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import { memoryStorage, localStorageStorage } from './session.js';
2
+ import { AuthModule } from './auth.js';
3
+ import { UsersModule } from './profile.js';
4
+ import { StorageModule } from './storage.js';
5
+ import { DevicesModule } from './devices.js';
6
+ import { RealtimeModule } from './realtime.js';
7
+ import { RestModule } from './rest.js';
8
+ import { EdgeModule } from './edge.js';
9
+ import type { ClientOptions } from './types.js';
10
+ export declare class BackendClient {
11
+ readonly auth: AuthModule;
12
+ readonly users: UsersModule;
13
+ readonly storage: StorageModule;
14
+ readonly devices: DevicesModule;
15
+ readonly realtime: RealtimeModule;
16
+ readonly rest: RestModule;
17
+ readonly edge: EdgeModule;
18
+ private readonly store;
19
+ constructor(apiUrl: string, options?: ClientOptions);
20
+ /** Load any persisted session before first use. Optional but recommended. */
21
+ hydrate(): Promise<unknown>;
22
+ }
23
+ /** Create a client for the given API base URL. */
24
+ export declare function createClient(apiUrl: string, options?: ClientOptions): BackendClient;
25
+ export { BackendError } from './http.js';
26
+ export { memoryStorage, localStorageStorage };
27
+ export type { ApiEnvelope, User, Tokens, Session, FileObject, FileVisibility, DevicePlatform, SessionStorageAdapter, ClientOptions, AuthEvent, AuthListener, } from './types.js';
28
+ export type { OrderDirection, RestFilters, RestFilterValue, RestListOptions, RestMutationOptions, RestOperator, } from './rest.js';
29
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,qBAAa,aAAa;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAC5B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC;IAChC,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAE1B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAe;gBAEzB,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB;IAwBvD,6EAA6E;IAC7E,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC;CAG5B;AAED,kDAAkD;AAClD,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,aAAa,CAEvF;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC;AAC9C,YAAY,EACV,WAAW,EACX,IAAI,EACJ,MAAM,EACN,OAAO,EACP,UAAU,EACV,cAAc,EACd,cAAc,EACd,qBAAqB,EACrB,aAAa,EACb,SAAS,EACT,YAAY,GACb,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,cAAc,EACd,WAAW,EACX,eAAe,EACf,eAAe,EACf,mBAAmB,EACnB,YAAY,GACb,MAAM,WAAW,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,42 @@
1
+ import { HttpClient } from './http.js';
2
+ import { SessionStore, memoryStorage, localStorageStorage } from './session.js';
3
+ import { AuthModule } from './auth.js';
4
+ import { UsersModule } from './profile.js';
5
+ import { StorageModule } from './storage.js';
6
+ import { DevicesModule } from './devices.js';
7
+ import { RealtimeModule } from './realtime.js';
8
+ import { RestModule } from './rest.js';
9
+ import { EdgeModule } from './edge.js';
10
+ export class BackendClient {
11
+ constructor(apiUrl, options = {}) {
12
+ const fetchImpl = options.fetch ?? globalThis.fetch;
13
+ if (!fetchImpl) {
14
+ throw new Error('No fetch implementation found. Pass `fetch` in options for this runtime.');
15
+ }
16
+ const autoRefresh = options.autoRefresh ?? true;
17
+ this.store = new SessionStore(options.storage ?? memoryStorage());
18
+ // auth needs the http client, and http needs auth.refresh for the 401 path,
19
+ // so wire them in two steps with a late-bound refresh callback.
20
+ let auth;
21
+ const http = new HttpClient(apiUrl, this.store, { autoRefresh, fetch: fetchImpl }, () => auth.refresh());
22
+ auth = new AuthModule(http, this.store);
23
+ this.auth = auth;
24
+ this.users = new UsersModule(http);
25
+ this.storage = new StorageModule(http);
26
+ this.devices = new DevicesModule(http);
27
+ this.realtime = new RealtimeModule(apiUrl, this.store);
28
+ this.rest = new RestModule(http);
29
+ this.edge = new EdgeModule(http);
30
+ }
31
+ /** Load any persisted session before first use. Optional but recommended. */
32
+ hydrate() {
33
+ return this.store.hydrate();
34
+ }
35
+ }
36
+ /** Create a client for the given API base URL. */
37
+ export function createClient(apiUrl, options = {}) {
38
+ return new BackendClient(apiUrl, options);
39
+ }
40
+ export { BackendError } from './http.js';
41
+ export { memoryStorage, localStorageStorage };
42
+ //# 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,aAAa,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAGvC,MAAM,OAAO,aAAa;IAWxB,YAAY,MAAc,EAAE,UAAyB,EAAE;QACrD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,IAAI,UAAU,CAAC,KAAK,CAAC;QACpD,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,MAAM,IAAI,KAAK,CAAC,0EAA0E,CAAC,CAAC;QAC9F,CAAC;QACD,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,IAAI,IAAI,CAAC;QAEhD,IAAI,CAAC,KAAK,GAAG,IAAI,YAAY,CAAC,OAAO,CAAC,OAAO,IAAI,aAAa,EAAE,CAAC,CAAC;QAElE,4EAA4E;QAC5E,gEAAgE;QAChE,IAAI,IAAiB,CAAC;QACtB,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;QACzG,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAExC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,IAAI,CAAC,OAAO,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,OAAO,GAAG,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC;QACvC,IAAI,CAAC,QAAQ,GAAG,IAAI,cAAc,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACvD,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,6EAA6E;IAC7E,OAAO;QACL,OAAO,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;IAC9B,CAAC;CACF;AAED,kDAAkD;AAClD,MAAM,UAAU,YAAY,CAAC,MAAc,EAAE,UAAyB,EAAE;IACtE,OAAO,IAAI,aAAa,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAC5C,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,CAAC"}
@@ -0,0 +1,16 @@
1
+ import type { HttpClient } from './http.js';
2
+ import type { User } from './types.js';
3
+ /** A file-ish value accepted by multipart uploads (browser File/Blob or RN asset). */
4
+ export type UploadInput = Blob | {
5
+ uri: string;
6
+ name?: string;
7
+ type?: string;
8
+ };
9
+ export declare class UsersModule {
10
+ private readonly http;
11
+ constructor(http: HttpClient);
12
+ updateUsername(username: string): Promise<User>;
13
+ updateImage(file: UploadInput): Promise<User>;
14
+ deleteAccount(): Promise<unknown>;
15
+ }
16
+ //# sourceMappingURL=profile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.d.ts","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAEvC,sFAAsF;AACtF,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAW/E,qBAAa,WAAW;IACV,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,cAAc,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,WAAW,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAM7C,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;CAGlC"}
@@ -0,0 +1,26 @@
1
+ function appendFile(form, field, file) {
2
+ if (typeof Blob !== 'undefined' && file instanceof Blob) {
3
+ form.append(field, file);
4
+ }
5
+ else {
6
+ // React Native style { uri, name, type }
7
+ form.append(field, file);
8
+ }
9
+ }
10
+ export class UsersModule {
11
+ constructor(http) {
12
+ this.http = http;
13
+ }
14
+ updateUsername(username) {
15
+ return this.http.request('/api/users/username', { method: 'PUT', body: { username } });
16
+ }
17
+ updateImage(file) {
18
+ const form = new FormData();
19
+ appendFile(form, 'image', file);
20
+ return this.http.request('/api/users/image', { method: 'PUT', form });
21
+ }
22
+ deleteAccount() {
23
+ return this.http.request('/api/users/', { method: 'DELETE' });
24
+ }
25
+ }
26
+ //# sourceMappingURL=profile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"profile.js","sourceRoot":"","sources":["../src/profile.ts"],"names":[],"mappings":"AAMA,SAAS,UAAU,CAAC,IAAc,EAAE,KAAa,EAAE,IAAiB;IAClE,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI,EAAE,CAAC;QACxD,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IAC3B,CAAC;SAAM,CAAC;QACN,yCAAyC;QACzC,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,IAAuB,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,MAAM,OAAO,WAAW;IACtB,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,cAAc,CAAC,QAAgB;QAC7B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAO,qBAAqB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC/F,CAAC;IAED,WAAW,CAAC,IAAiB;QAC3B,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QAChC,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAO,kBAAkB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9E,CAAC;IAED,aAAa;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IAChE,CAAC;CACF"}
@@ -0,0 +1,18 @@
1
+ import type { SessionStore } from './session.js';
2
+ /**
3
+ * Lazy wrapper over socket.io-client. The dependency is optional: it is only
4
+ * imported when `connect()` is called, so apps that never use realtime do not
5
+ * need to install it.
6
+ */
7
+ export declare class RealtimeModule {
8
+ private readonly baseUrl;
9
+ private readonly store;
10
+ private socket;
11
+ constructor(baseUrl: string, store: SessionStore);
12
+ connect(): Promise<void>;
13
+ on(event: string, cb: (...args: unknown[]) => void): void;
14
+ off(event: string, cb?: (...args: unknown[]) => void): void;
15
+ emit(event: string, ...args: unknown[]): void;
16
+ disconnect(): void;
17
+ }
18
+ //# sourceMappingURL=realtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAajD;;;;GAIG;AACH,qBAAa,cAAc;IAGb,OAAO,CAAC,QAAQ,CAAC,OAAO;IAAU,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFpE,OAAO,CAAC,MAAM,CAA2B;gBAEZ,OAAO,EAAE,MAAM,EAAmB,KAAK,EAAE,YAAY;IAE5E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB9B,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI;IAIzD,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,GAAG,IAAI;IAI3D,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAI7C,UAAU,IAAI,IAAI;CAInB"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Lazy wrapper over socket.io-client. The dependency is optional: it is only
3
+ * imported when `connect()` is called, so apps that never use realtime do not
4
+ * need to install it.
5
+ */
6
+ export class RealtimeModule {
7
+ constructor(baseUrl, store) {
8
+ this.baseUrl = baseUrl;
9
+ this.store = store;
10
+ this.socket = null;
11
+ }
12
+ async connect() {
13
+ if (this.socket?.connected)
14
+ return;
15
+ let io;
16
+ try {
17
+ // socket.io-client is an optional peer dependency; resolved at runtime only.
18
+ // @ts-ignore -- the module may not be installed at build time
19
+ ({ io } = (await import('socket.io-client')));
20
+ }
21
+ catch {
22
+ throw new Error('Realtime requires the optional dependency "socket.io-client". Install it: npm i socket.io-client');
23
+ }
24
+ const token = this.store.get()?.accessToken;
25
+ this.socket = io(this.baseUrl, {
26
+ path: '/ws',
27
+ transports: ['websocket'],
28
+ auth: token ? { token } : {},
29
+ });
30
+ }
31
+ on(event, cb) {
32
+ this.socket?.on(event, cb);
33
+ }
34
+ off(event, cb) {
35
+ this.socket?.off(event, cb);
36
+ }
37
+ emit(event, ...args) {
38
+ this.socket?.emit(event, ...args);
39
+ }
40
+ disconnect() {
41
+ this.socket?.disconnect();
42
+ this.socket = null;
43
+ }
44
+ }
45
+ //# sourceMappingURL=realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../src/realtime.ts"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,MAAM,OAAO,cAAc;IAGzB,YAA6B,OAAe,EAAmB,KAAmB;QAArD,YAAO,GAAP,OAAO,CAAQ;QAAmB,UAAK,GAAL,KAAK,CAAc;QAF1E,WAAM,GAAsB,IAAI,CAAC;IAE4C,CAAC;IAEtF,KAAK,CAAC,OAAO;QACX,IAAI,IAAI,CAAC,MAAM,EAAE,SAAS;YAAE,OAAO;QACnC,IAAI,EAAa,CAAC;QAClB,IAAI,CAAC;YACH,6EAA6E;YAC7E,8DAA8D;YAC9D,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAiC,CAAC,CAAC;QAChF,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CACb,kGAAkG,CACnG,CAAC;QACJ,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,WAAW,CAAC;QAC5C,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE;YAC7B,IAAI,EAAE,KAAK;YACX,UAAU,EAAE,CAAC,WAAW,CAAC;YACzB,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE;SAC7B,CAAC,CAAC;IACL,CAAC;IAED,EAAE,CAAC,KAAa,EAAE,EAAgC;QAChD,IAAI,CAAC,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC7B,CAAC;IAED,GAAG,CAAC,KAAa,EAAE,EAAiC;QAClD,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IAC9B,CAAC;IAED,IAAI,CAAC,KAAa,EAAE,GAAG,IAAe;QACpC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,CAAC;IACpC,CAAC;IAED,UAAU;QACR,IAAI,CAAC,MAAM,EAAE,UAAU,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;IACrB,CAAC;CACF"}
package/dist/rest.d.ts ADDED
@@ -0,0 +1,50 @@
1
+ import type { HttpClient } from './http.js';
2
+ type Primitive = string | number | boolean | null;
3
+ export type RestOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'is';
4
+ export type OrderDirection = 'asc' | 'desc';
5
+ export type RestFilterValue = Primitive | {
6
+ op: RestOperator;
7
+ value: Primitive | Primitive[];
8
+ };
9
+ export type RestFilters = Record<string, RestFilterValue>;
10
+ export interface RestListOptions {
11
+ select?: string[];
12
+ filters?: RestFilters;
13
+ order?: Array<{
14
+ field: string;
15
+ direction?: OrderDirection;
16
+ }>;
17
+ limit?: number;
18
+ offset?: number;
19
+ }
20
+ export interface RestMutationOptions {
21
+ filters?: RestFilters;
22
+ }
23
+ export declare class RestTable<Row extends object = Record<string, unknown>> {
24
+ private readonly http;
25
+ private readonly model;
26
+ constructor(http: HttpClient, model: string);
27
+ list(options?: RestListOptions): Promise<Row[]>;
28
+ insert(data: Partial<Row>): Promise<Row[]>;
29
+ update(data: Partial<Row>, options?: RestMutationOptions): Promise<Row[] | {
30
+ count: number;
31
+ }>;
32
+ remove(options?: RestMutationOptions): Promise<Row[] | {
33
+ count: number;
34
+ }>;
35
+ }
36
+ export declare class RestModule {
37
+ private readonly http;
38
+ constructor(http: HttpClient);
39
+ from<Row extends object = Record<string, unknown>>(model: string): RestTable<Row>;
40
+ list<Row extends object = Record<string, unknown>>(model: string, options?: RestListOptions): Promise<Row[]>;
41
+ insert<Row extends object = Record<string, unknown>>(model: string, data: Partial<Row>): Promise<Row[]>;
42
+ update<Row extends object = Record<string, unknown>>(model: string, data: Partial<Row>, options?: RestMutationOptions): Promise<Row[] | {
43
+ count: number;
44
+ }>;
45
+ remove<Row extends object = Record<string, unknown>>(model: string, options?: RestMutationOptions): Promise<Row[] | {
46
+ count: number;
47
+ }>;
48
+ }
49
+ export {};
50
+ //# sourceMappingURL=rest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"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"}
package/dist/rest.js ADDED
@@ -0,0 +1,75 @@
1
+ function encodeValue(value) {
2
+ if (Array.isArray(value))
3
+ return `(${value.map(encodeValue).join(',')})`;
4
+ if (value === null)
5
+ return 'null';
6
+ return String(value);
7
+ }
8
+ function encodeFilter(value) {
9
+ if (value && typeof value === 'object' && 'op' in value) {
10
+ return `${value.op}.${encodeValue(value.value)}`;
11
+ }
12
+ return encodeValue(value);
13
+ }
14
+ function toQuery(options = {}) {
15
+ const params = new URLSearchParams();
16
+ const listOptions = options;
17
+ if (listOptions.select?.length)
18
+ params.set('select', listOptions.select.join(','));
19
+ if (listOptions.order?.length) {
20
+ params.set('order', listOptions.order.map(({ field, direction = 'asc' }) => `${field}.${direction}`).join(','));
21
+ }
22
+ if (typeof listOptions.limit === 'number')
23
+ params.set('limit', String(listOptions.limit));
24
+ if (typeof listOptions.offset === 'number')
25
+ params.set('offset', String(listOptions.offset));
26
+ for (const [field, value] of Object.entries(options.filters ?? {})) {
27
+ params.set(field, encodeFilter(value));
28
+ }
29
+ const query = params.toString();
30
+ return query ? `?${query}` : '';
31
+ }
32
+ export class RestTable {
33
+ constructor(http, model) {
34
+ this.http = http;
35
+ this.model = model;
36
+ }
37
+ list(options = {}) {
38
+ return this.http.request(`/rest/v1/${encodeURIComponent(this.model)}${toQuery(options)}`);
39
+ }
40
+ insert(data) {
41
+ return this.http.request(`/rest/v1/${encodeURIComponent(this.model)}`, { method: 'POST', body: data });
42
+ }
43
+ update(data, options = {}) {
44
+ return this.http.request(`/rest/v1/${encodeURIComponent(this.model)}${toQuery(options)}`, {
45
+ method: 'PATCH',
46
+ body: data,
47
+ });
48
+ }
49
+ remove(options = {}) {
50
+ return this.http.request(`/rest/v1/${encodeURIComponent(this.model)}${toQuery(options)}`, {
51
+ method: 'DELETE',
52
+ });
53
+ }
54
+ }
55
+ export class RestModule {
56
+ constructor(http) {
57
+ this.http = http;
58
+ }
59
+ from(model) {
60
+ return new RestTable(this.http, model);
61
+ }
62
+ list(model, options = {}) {
63
+ return this.from(model).list(options);
64
+ }
65
+ insert(model, data) {
66
+ return this.from(model).insert(data);
67
+ }
68
+ update(model, data, options = {}) {
69
+ return this.from(model).update(data, options);
70
+ }
71
+ remove(model, options = {}) {
72
+ return this.from(model).remove(options);
73
+ }
74
+ }
75
+ //# sourceMappingURL=rest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest.js","sourceRoot":"","sources":["../src/rest.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { Session, SessionStorageAdapter, AuthEvent, AuthListener } from './types.js';
2
+ /** In-memory adapter. Safe everywhere (SSR, tests); does not survive reloads. */
3
+ export declare function memoryStorage(): SessionStorageAdapter;
4
+ /**
5
+ * Browser localStorage adapter. Falls back to memory when localStorage is
6
+ * unavailable (SSR, private mode), so it never throws on import or use.
7
+ */
8
+ export declare function localStorageStorage(key?: string): SessionStorageAdapter;
9
+ /**
10
+ * Holds the live session in memory, mirrors it to a storage adapter, and
11
+ * notifies listeners of auth changes. The in-memory copy is the source of truth
12
+ * for synchronous reads (e.g. attaching the bearer header).
13
+ */
14
+ export declare class SessionStore {
15
+ private readonly storage;
16
+ private current;
17
+ private readonly listeners;
18
+ private hydrated;
19
+ constructor(storage: SessionStorageAdapter);
20
+ /** Load the persisted session once. Idempotent. */
21
+ hydrate(): Promise<Session | null>;
22
+ get(): Session | null;
23
+ set(session: Session | null, event: AuthEvent): Promise<void>;
24
+ /** Replace only the tokens, keeping the existing user (used after refresh). */
25
+ setTokens(accessToken: string, refreshToken: string): Promise<void>;
26
+ subscribe(listener: AuthListener): () => void;
27
+ private emit;
28
+ }
29
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,qBAAqB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1F,iFAAiF;AACjF,wBAAgB,aAAa,IAAI,qBAAqB,CAQrD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,SAAuB,GAAG,qBAAqB,CAkBrF;AAED;;;;GAIG;AACH,qBAAa,YAAY;IAKX,OAAO,CAAC,QAAQ,CAAC,OAAO;IAJpC,OAAO,CAAC,OAAO,CAAwB;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA2B;IACrD,OAAO,CAAC,QAAQ,CAAS;gBAEI,OAAO,EAAE,qBAAqB;IAE3D,mDAAmD;IAC7C,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAOxC,GAAG,IAAI,OAAO,GAAG,IAAI;IAIf,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnE,+EAA+E;IACzE,SAAS,CAAC,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKzE,SAAS,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,IAAI;IAK7C,OAAO,CAAC,IAAI;CAGb"}
@@ -0,0 +1,83 @@
1
+ /** In-memory adapter. Safe everywhere (SSR, tests); does not survive reloads. */
2
+ export function memoryStorage() {
3
+ let current = null;
4
+ return {
5
+ get: () => current,
6
+ set: (s) => {
7
+ current = s;
8
+ },
9
+ };
10
+ }
11
+ /**
12
+ * Browser localStorage adapter. Falls back to memory when localStorage is
13
+ * unavailable (SSR, private mode), so it never throws on import or use.
14
+ */
15
+ export function localStorageStorage(key = 'motherbase.session') {
16
+ const ls = typeof globalThis !== 'undefined' ? globalThis.localStorage : undefined;
17
+ if (!ls)
18
+ return memoryStorage();
19
+ return {
20
+ get: () => {
21
+ const raw = ls.getItem(key);
22
+ if (!raw)
23
+ return null;
24
+ try {
25
+ return JSON.parse(raw);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ },
31
+ set: (s) => {
32
+ if (s)
33
+ ls.setItem(key, JSON.stringify(s));
34
+ else
35
+ ls.removeItem(key);
36
+ },
37
+ };
38
+ }
39
+ /**
40
+ * Holds the live session in memory, mirrors it to a storage adapter, and
41
+ * notifies listeners of auth changes. The in-memory copy is the source of truth
42
+ * for synchronous reads (e.g. attaching the bearer header).
43
+ */
44
+ export class SessionStore {
45
+ constructor(storage) {
46
+ this.storage = storage;
47
+ this.current = null;
48
+ this.listeners = new Set();
49
+ this.hydrated = false;
50
+ }
51
+ /** Load the persisted session once. Idempotent. */
52
+ async hydrate() {
53
+ if (this.hydrated)
54
+ return this.current;
55
+ this.current = (await this.storage.get()) ?? null;
56
+ this.hydrated = true;
57
+ return this.current;
58
+ }
59
+ get() {
60
+ return this.current;
61
+ }
62
+ async set(session, event) {
63
+ this.current = session;
64
+ this.hydrated = true;
65
+ await this.storage.set(session);
66
+ this.emit(event);
67
+ }
68
+ /** Replace only the tokens, keeping the existing user (used after refresh). */
69
+ async setTokens(accessToken, refreshToken) {
70
+ if (!this.current)
71
+ return;
72
+ await this.set({ ...this.current, accessToken, refreshToken }, 'tokenRefreshed');
73
+ }
74
+ subscribe(listener) {
75
+ this.listeners.add(listener);
76
+ return () => this.listeners.delete(listener);
77
+ }
78
+ emit(event) {
79
+ for (const l of this.listeners)
80
+ l(event, this.current);
81
+ }
82
+ }
83
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,29 @@
1
+ import type { HttpClient } from './http.js';
2
+ import type { FileObject, FileVisibility } from './types.js';
3
+ import type { UploadInput } from './profile.js';
4
+ interface UploadOptions {
5
+ visibility?: FileVisibility;
6
+ bucket?: string;
7
+ }
8
+ interface PresignParams {
9
+ filename: string;
10
+ mime?: string;
11
+ visibility?: FileVisibility;
12
+ bucket?: string;
13
+ }
14
+ export declare class StorageModule {
15
+ private readonly http;
16
+ constructor(http: HttpClient);
17
+ /** Multipart upload through the API (field name: "file"). */
18
+ upload(file: UploadInput, options?: UploadOptions): Promise<FileObject>;
19
+ /** Request a presigned PUT URL for direct-to-storage upload (S3 driver). */
20
+ presign(params: PresignParams): Promise<{
21
+ url: string;
22
+ [k: string]: unknown;
23
+ }>;
24
+ /** Get a (possibly signed) download URL for a stored file. */
25
+ getUrl(id: string): Promise<string>;
26
+ remove(id: string): Promise<unknown>;
27
+ }
28
+ export {};
29
+ //# sourceMappingURL=storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.d.ts","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAEhD,UAAU,aAAa;IACrB,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,cAAc,CAAC;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,UAAU;IAE7C,6DAA6D;IAC7D,MAAM,CAAC,IAAI,EAAE,WAAW,EAAE,OAAO,GAAE,aAAkB,GAAG,OAAO,CAAC,UAAU,CAAC;IAS3E,4EAA4E;IAC5E,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,CAAC,CAAC,EAAE,MAAM,GAAG,OAAO,CAAA;KAAE,CAAC;IAI9E,8DAA8D;IACxD,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAKzC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAGrC"}
@@ -0,0 +1,31 @@
1
+ export class StorageModule {
2
+ constructor(http) {
3
+ this.http = http;
4
+ }
5
+ /** Multipart upload through the API (field name: "file"). */
6
+ upload(file, options = {}) {
7
+ const form = new FormData();
8
+ if (typeof Blob !== 'undefined' && file instanceof Blob)
9
+ form.append('file', file);
10
+ else
11
+ form.append('file', file);
12
+ if (options.visibility)
13
+ form.append('visibility', options.visibility);
14
+ if (options.bucket)
15
+ form.append('bucket', options.bucket);
16
+ return this.http.request('/api/files/', { method: 'POST', form });
17
+ }
18
+ /** Request a presigned PUT URL for direct-to-storage upload (S3 driver). */
19
+ presign(params) {
20
+ return this.http.request('/api/files/presign', { method: 'POST', body: params });
21
+ }
22
+ /** Get a (possibly signed) download URL for a stored file. */
23
+ async getUrl(id) {
24
+ const res = await this.http.request(`/api/files/${id}/url`);
25
+ return res.url;
26
+ }
27
+ remove(id) {
28
+ return this.http.request(`/api/files/${id}`, { method: 'DELETE' });
29
+ }
30
+ }
31
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"AAgBA,MAAM,OAAO,aAAa;IACxB,YAA6B,IAAgB;QAAhB,SAAI,GAAJ,IAAI,CAAY;IAAG,CAAC;IAEjD,6DAA6D;IAC7D,MAAM,CAAC,IAAiB,EAAE,UAAyB,EAAE;QACnD,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,YAAY,IAAI;YAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;;YAC9E,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,IAAuB,CAAC,CAAC;QAClD,IAAI,OAAO,CAAC,UAAU;YAAE,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QACtE,IAAI,OAAO,CAAC,MAAM;YAAE,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC1D,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAa,aAAa,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IAED,4EAA4E;IAC5E,OAAO,CAAC,MAAqB;QAC3B,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACnF,CAAC;IAED,8DAA8D;IAC9D,KAAK,CAAC,MAAM,CAAC,EAAU;QACrB,MAAM,GAAG,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAkB,cAAc,EAAE,MAAM,CAAC,CAAC;QAC7E,OAAO,GAAG,CAAC,GAAG,CAAC;IACjB,CAAC;IAED,MAAM,CAAC,EAAU;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC;IACrE,CAAC;CACF"}
@@ -0,0 +1,56 @@
1
+ /** Shared types for the Motherbase client SDK. */
2
+ /** The envelope every API endpoint wraps its payload in. */
3
+ export interface ApiEnvelope<T = unknown> {
4
+ status: 0 | 1;
5
+ message: string;
6
+ data?: T;
7
+ pager?: Record<string, unknown>;
8
+ }
9
+ /** Authenticated user as returned by the API. Extra fields are preserved. */
10
+ export interface User {
11
+ id: string;
12
+ email: string;
13
+ username?: string | null;
14
+ role?: string;
15
+ [key: string]: unknown;
16
+ }
17
+ /** Token pair issued by login/register/refresh. */
18
+ export interface Tokens {
19
+ accessToken: string;
20
+ refreshToken: string;
21
+ }
22
+ /** The locally-held session: tokens plus the user they belong to. */
23
+ export interface Session extends Tokens {
24
+ user: User;
25
+ }
26
+ /** A stored file as returned by the storage endpoints. */
27
+ export interface FileObject {
28
+ id: string;
29
+ filename?: string;
30
+ mime?: string;
31
+ visibility?: 'public' | 'private';
32
+ bucket?: string;
33
+ url?: string;
34
+ [key: string]: unknown;
35
+ }
36
+ export type FileVisibility = 'public' | 'private';
37
+ export type DevicePlatform = 'ios' | 'android' | 'web';
38
+ /**
39
+ * Persists the session between runs. Implementations must be synchronous-or-async
40
+ * tolerant: every method may return a value or a promise of it.
41
+ */
42
+ export interface SessionStorageAdapter {
43
+ get(): Session | null | Promise<Session | null>;
44
+ set(session: Session | null): void | Promise<void>;
45
+ }
46
+ export interface ClientOptions {
47
+ /** Where to persist the session. Defaults to in-memory (SSR-safe). */
48
+ storage?: SessionStorageAdapter;
49
+ /** Refresh the access token automatically on a 401. Default: true. */
50
+ autoRefresh?: boolean;
51
+ /** Override the fetch implementation (for SSR or tests). */
52
+ fetch?: typeof fetch;
53
+ }
54
+ export type AuthEvent = 'signIn' | 'signOut' | 'tokenRefreshed';
55
+ export type AuthListener = (event: AuthEvent, session: Session | null) => void;
56
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,kDAAkD;AAElD,4DAA4D;AAC5D,MAAM,WAAW,WAAW,CAAC,CAAC,GAAG,OAAO;IACtC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,CAAC,CAAC;IACT,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,6EAA6E;AAC7E,MAAM,WAAW,IAAI;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,mDAAmD;AACnD,MAAM,WAAW,MAAM;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qEAAqE;AACrE,MAAM,WAAW,OAAQ,SAAQ,MAAM;IACrC,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,0DAA0D;AAC1D,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAClC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAClD,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,SAAS,GAAG,KAAK,CAAC;AAEvD;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,GAAG,IAAI,OAAO,GAAG,IAAI,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAAC;IAChD,GAAG,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAED,MAAM,WAAW,aAAa;IAC5B,sEAAsE;IACtE,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,sEAAsE;IACtE,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,4DAA4D;IAC5D,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;CACtB;AAED,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,gBAAgB,CAAC;AAChE,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,IAAI,KAAK,IAAI,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ /** Shared types for the Motherbase client SDK. */
2
+ export {};
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,kDAAkD"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@motherbase/client",
3
+ "version": "1.0.0",
4
+ "description": "Typed client SDK for Motherbase, the open source self-hosted backend platform.",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "README.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json",
24
+ "clean": "rm -rf dist"
25
+ },
26
+ "keywords": [
27
+ "sdk",
28
+ "client",
29
+ "auth",
30
+ "backend",
31
+ "motherbase",
32
+ "self-hosted"
33
+ ],
34
+ "license": "MIT",
35
+ "peerDependencies": {
36
+ "socket.io-client": "^4.0.0"
37
+ },
38
+ "peerDependenciesMeta": {
39
+ "socket.io-client": {
40
+ "optional": true
41
+ }
42
+ },
43
+ "devDependencies": {
44
+ "typescript": "^5.4.2"
45
+ }
46
+ }