@sovaria/sdk 0.0.0-stage → 0.5.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.
@@ -0,0 +1,102 @@
1
+ export interface SovariaClientOptions {
2
+ accessToken?: string;
3
+ refreshToken?: string;
4
+ onTokenRefresh?: (tokens: TokenResponse) => void;
5
+ }
6
+ export { SovariaError } from './errors.js';
7
+ export interface TokenResponse {
8
+ access_token: string;
9
+ refresh_token: string;
10
+ expires_in: number;
11
+ token_type?: string;
12
+ }
13
+ export interface UserProfile {
14
+ user_id?: string;
15
+ email?: string;
16
+ org_id?: string;
17
+ project_id?: string;
18
+ roles?: string[];
19
+ [key: string]: unknown;
20
+ }
21
+ export interface NodeResponse {
22
+ node_id?: string;
23
+ id?: string;
24
+ type?: string;
25
+ name?: string;
26
+ metadata?: Record<string, unknown>;
27
+ [key: string]: unknown;
28
+ }
29
+ export interface ColumnDefinition {
30
+ name: string;
31
+ type: string;
32
+ nullable?: boolean;
33
+ primary_key?: boolean;
34
+ default?: unknown;
35
+ }
36
+ export interface SqlQueryResult {
37
+ rows: Record<string, unknown>[];
38
+ row_count?: number;
39
+ }
40
+ export interface DocumentPutResponse {
41
+ ok?: boolean;
42
+ key?: string;
43
+ [key: string]: unknown;
44
+ }
45
+ export interface DocumentGetResponse {
46
+ key?: string;
47
+ value?: Record<string, unknown>;
48
+ [key: string]: unknown;
49
+ }
50
+ export interface DocumentScanResult {
51
+ items?: Array<Record<string, unknown>>;
52
+ count?: number;
53
+ [key: string]: unknown;
54
+ }
55
+ export interface StorageObject {
56
+ path: string;
57
+ size?: number;
58
+ content_type?: string;
59
+ metadata?: Record<string, unknown>;
60
+ [key: string]: unknown;
61
+ }
62
+ export interface Bucket {
63
+ name: string;
64
+ public?: boolean;
65
+ metadata?: Record<string, unknown>;
66
+ [key: string]: unknown;
67
+ }
68
+ export interface BucketListResponse {
69
+ buckets: Bucket[];
70
+ }
71
+ export interface ObjectListResponse {
72
+ objects: StorageObject[];
73
+ }
74
+ export interface FunctionDeployResponse {
75
+ deployment_id?: string;
76
+ status?: string;
77
+ [key: string]: unknown;
78
+ }
79
+ export interface FunctionInvokeResponse {
80
+ output?: unknown;
81
+ status?: string;
82
+ [key: string]: unknown;
83
+ }
84
+ export interface RealtimeEvent {
85
+ type: string;
86
+ channel: string;
87
+ payload: unknown;
88
+ }
89
+ export type RealtimeCallback = (event: RealtimeEvent) => void;
90
+ export interface RealtimeSubscription {
91
+ unsubscribe: () => void;
92
+ }
93
+ export interface TimelineLogResponse {
94
+ timeline?: string;
95
+ events?: Array<Record<string, unknown>>;
96
+ [key: string]: unknown;
97
+ }
98
+ export interface TimelineJumpResponse {
99
+ timeline?: string;
100
+ target_hex?: string;
101
+ [key: string]: unknown;
102
+ }
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export { SovariaError } from './errors.js';
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Web push for browsers (Chrome, Edge, Firefox, Safari 16.4+ including iOS home-screen web apps), with the project's
3
+ * own VAPID keys — no Firebase project needed.
4
+ *
5
+ * ```ts
6
+ * // 1. Serve the service worker at your site's root: copy `serviceWorkerSource` into /sovaria-sw.js
7
+ * // (or importScripts it from your own worker).
8
+ * // 2. After the user signs in, from a click handler (browsers require a user gesture):
9
+ * await sov.push.register({ topics: ['news'] })
10
+ * // Later: await sov.push.unregister()
11
+ * ```
12
+ *
13
+ * `register()` asks for notification permission, subscribes with the project's public key
14
+ * (`GET /api/v1/push/webpush-key`) and registers the subscription as the signed-in user's push device
15
+ * (`platform: web`, `provider: webpush`). Sends from the console, `sov push send` or messaging reach it like any
16
+ * phone; `link` in a send opens that URL on click.
17
+ */
18
+ export interface WebPushRegisterOptions {
19
+ /** Path of the service worker that shows notifications (default `/sovaria-sw.js`). */
20
+ serviceWorker?: string;
21
+ /** Use a registration you already have instead of registering `serviceWorker`. */
22
+ registration?: ServiceWorkerRegistration;
23
+ topics?: string[];
24
+ deviceId?: string;
25
+ }
26
+ type Requester = <T>(method: string, path: string, options?: {
27
+ body?: unknown;
28
+ requiresScope?: boolean;
29
+ }) => Promise<T>;
30
+ export declare class WebPushClient {
31
+ private readonly request;
32
+ constructor(request: Requester);
33
+ /** Whether this browser can receive web push. */
34
+ get supported(): boolean;
35
+ /** Subscribe this browser and register it for the signed-in user. Returns the device row. */
36
+ register(options?: WebPushRegisterOptions): Promise<Record<string, unknown>>;
37
+ /** Unsubscribe this browser and remove its push device. */
38
+ unregister(registration?: ServiceWorkerRegistration): Promise<number>;
39
+ }
40
+ export declare function urlBase64ToUint8Array(base64: string): Uint8Array;
41
+ /**
42
+ * The service worker that shows Sovaria pushes and opens `link` on click. Serve it as `/sovaria-sw.js`, or
43
+ * `importScripts('/sovaria-sw.js')` from your own worker. Payload: `{title, body, image?, link?, tag?, data?}`.
44
+ */
45
+ export declare const serviceWorkerSource = "/* Sovaria web push service worker */\nself.addEventListener('push', (event) => {\n let p = {}\n try { p = event.data ? event.data.json() : {} } catch (e) { p = { title: event.data && event.data.text() } }\n const options = { body: p.body || '', image: p.image, icon: p.icon, tag: p.tag, data: { link: p.link, data: p.data || {} } }\n event.waitUntil(self.registration.showNotification(p.title || 'Notification', options))\n})\nself.addEventListener('notificationclick', (event) => {\n event.notification.close()\n const link = event.notification.data && event.notification.data.link\n if (!link) return\n event.waitUntil(self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((wins) => {\n const url = new URL(link, self.location.origin).href\n for (const w of wins) if (w.url === url && 'focus' in w) return w.focus()\n return self.clients.openWindow(url)\n }))\n})\n";
46
+ export {};
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Web push for browsers (Chrome, Edge, Firefox, Safari 16.4+ including iOS home-screen web apps), with the project's
3
+ * own VAPID keys — no Firebase project needed.
4
+ *
5
+ * ```ts
6
+ * // 1. Serve the service worker at your site's root: copy `serviceWorkerSource` into /sovaria-sw.js
7
+ * // (or importScripts it from your own worker).
8
+ * // 2. After the user signs in, from a click handler (browsers require a user gesture):
9
+ * await sov.push.register({ topics: ['news'] })
10
+ * // Later: await sov.push.unregister()
11
+ * ```
12
+ *
13
+ * `register()` asks for notification permission, subscribes with the project's public key
14
+ * (`GET /api/v1/push/webpush-key`) and registers the subscription as the signed-in user's push device
15
+ * (`platform: web`, `provider: webpush`). Sends from the console, `sov push send` or messaging reach it like any
16
+ * phone; `link` in a send opens that URL on click.
17
+ */
18
+ export class WebPushClient {
19
+ request;
20
+ constructor(request) {
21
+ this.request = request;
22
+ }
23
+ /** Whether this browser can receive web push. */
24
+ get supported() {
25
+ const g = globalThis;
26
+ return Boolean(g.navigator?.serviceWorker && g.PushManager && g.Notification);
27
+ }
28
+ /** Subscribe this browser and register it for the signed-in user. Returns the device row. */
29
+ async register(options = {}) {
30
+ const g = globalThis;
31
+ if (!this.supported)
32
+ throw new Error('This browser does not support web push');
33
+ const permission = g.Notification.permission === 'granted' ? 'granted' : await g.Notification.requestPermission();
34
+ if (permission !== 'granted')
35
+ throw new Error('Notifications were not allowed');
36
+ const { public_key: publicKey } = await this.request('GET', '/api/v1/push/webpush-key', { requiresScope: false });
37
+ const registration = options.registration ?? (await g.navigator.serviceWorker.register(options.serviceWorker ?? '/sovaria-sw.js'));
38
+ let subscription = await registration.pushManager.getSubscription();
39
+ const key = urlBase64ToUint8Array(publicKey);
40
+ if (subscription && !sameKey(subscription.options?.applicationServerKey ?? null, key)) {
41
+ await subscription.unsubscribe(); // the project rotated its keys
42
+ subscription = null;
43
+ }
44
+ subscription ??= await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: key });
45
+ return this.request('POST', '/api/v1/push/devices', {
46
+ requiresScope: false,
47
+ body: { token: JSON.stringify(subscription), platform: 'web', provider: 'webpush', topics: options.topics,
48
+ device_id: options.deviceId, locale: g.navigator?.language },
49
+ });
50
+ }
51
+ /** Unsubscribe this browser and remove its push device. */
52
+ async unregister(registration) {
53
+ const g = globalThis;
54
+ const reg = registration ?? (await g.navigator?.serviceWorker?.getRegistration?.());
55
+ const subscription = await reg?.pushManager.getSubscription();
56
+ if (!subscription)
57
+ return 0;
58
+ const token = JSON.stringify(subscription);
59
+ await subscription.unsubscribe();
60
+ const out = await this.request('POST', '/api/v1/push/devices/unregister', { body: { token }, requiresScope: false });
61
+ return out.removed ?? 0;
62
+ }
63
+ }
64
+ export function urlBase64ToUint8Array(base64) {
65
+ const padded = (base64 + '='.repeat((4 - (base64.length % 4)) % 4)).replace(/-/g, '+').replace(/_/g, '/');
66
+ const raw = atob(padded);
67
+ const out = new Uint8Array(raw.length);
68
+ for (let i = 0; i < raw.length; i++)
69
+ out[i] = raw.charCodeAt(i);
70
+ return out;
71
+ }
72
+ function sameKey(a, b) {
73
+ if (!a)
74
+ return false;
75
+ const x = new Uint8Array(a);
76
+ return x.length === b.length && x.every((v, i) => v === b[i]);
77
+ }
78
+ /**
79
+ * The service worker that shows Sovaria pushes and opens `link` on click. Serve it as `/sovaria-sw.js`, or
80
+ * `importScripts('/sovaria-sw.js')` from your own worker. Payload: `{title, body, image?, link?, tag?, data?}`.
81
+ */
82
+ export const serviceWorkerSource = `/* Sovaria web push service worker */
83
+ self.addEventListener('push', (event) => {
84
+ let p = {}
85
+ try { p = event.data ? event.data.json() : {} } catch (e) { p = { title: event.data && event.data.text() } }
86
+ const options = { body: p.body || '', image: p.image, icon: p.icon, tag: p.tag, data: { link: p.link, data: p.data || {} } }
87
+ event.waitUntil(self.registration.showNotification(p.title || 'Notification', options))
88
+ })
89
+ self.addEventListener('notificationclick', (event) => {
90
+ event.notification.close()
91
+ const link = event.notification.data && event.notification.data.link
92
+ if (!link) return
93
+ event.waitUntil(self.clients.matchAll({ type: 'window', includeUncontrolled: true }).then((wins) => {
94
+ const url = new URL(link, self.location.origin).href
95
+ for (const w of wins) if (w.url === url && 'focus' in w) return w.focus()
96
+ return self.clients.openWindow(url)
97
+ }))
98
+ })
99
+ `;
package/package.json CHANGED
@@ -1,6 +1,76 @@
1
1
  {
2
2
  "name": "@sovaria/sdk",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.5.0",
4
+ "description": "Official JavaScript/TypeScript SDK for Sovaria: database (SQL and documents), auth, storage, realtime, functions and the full Sovaria API",
5
+ "keywords": [
6
+ "sovaria",
7
+ "sdk",
8
+ "baas",
9
+ "backend",
10
+ "database",
11
+ "postgres",
12
+ "auth",
13
+ "storage",
14
+ "realtime",
15
+ "firestore",
16
+ "supabase-alternative",
17
+ "firebase-alternative"
18
+ ],
19
+ "homepage": "https://sovaria.in/docs/javascript-setup",
20
+ "bugs": {
21
+ "url": "https://sovaria.in/contact"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Sovaria (https://sovaria.in)",
25
+ "type": "module",
26
+ "main": "dist/index.js",
27
+ "types": "dist/index.d.ts",
28
+ "typesVersions": {
29
+ "*": {
30
+ "firestore": [
31
+ "./dist/firestore.d.ts"
32
+ ]
33
+ }
34
+ },
35
+ "exports": {
36
+ ".": {
37
+ "types": "./dist/index.d.ts",
38
+ "import": "./dist/index.js",
39
+ "default": "./dist/index.js"
40
+ },
41
+ "./firestore": {
42
+ "types": "./dist/firestore.d.ts",
43
+ "import": "./dist/firestore.js",
44
+ "default": "./dist/firestore.js"
45
+ },
46
+ "./package.json": "./package.json"
47
+ },
48
+ "files": [
49
+ "dist",
50
+ "README.md",
51
+ "LICENSE"
52
+ ],
53
+ "engines": {
54
+ "node": ">=20"
55
+ },
56
+ "scripts": {
57
+ "build": "rm -rf dist && tsc -p tsconfig.build.json",
58
+ "test": "vitest run src",
59
+ "lint": "tsc --noEmit -p tsconfig.json",
60
+ "clean": "rm -rf dist",
61
+ "docs:api": "typedoc --options typedoc.json",
62
+ "prepublishOnly": "npm run build && npm test -- --maxWorkers=2"
63
+ },
64
+ "dependencies": {
65
+ "hash-wasm": "4.12.0"
66
+ },
67
+ "devDependencies": {
68
+ "typedoc": "^0.28.14",
69
+ "typedoc-plugin-markdown": "^4.9.0",
70
+ "typescript": "^5.3.3",
71
+ "vitest": "^4.1.4"
72
+ },
73
+ "publishConfig": {
74
+ "access": "public"
75
+ }
76
+ }