npm package: @launchdb/js
Description: LaunchDB JavaScript/TypeScript SDK.
Latest version: 0.1.2

# @launchdb/js

LaunchDB JavaScript/TypeScript SDK.

## Install

```bash
npm install @launchdb/js
```

## Usage

```ts
import { createClient } from '@launchdb/js';

const db = createClient('https://api.example.com', 'anon_key', {
  projectId: 'proj_1234abcd5678ef90',
});

const { data, error } = await db.from('todos').select('*');
```

## Realtime (Phase 1)

Phase 1 requires an explicit realtime token callback.

```ts
const db = createClient(baseUrl, apiKey, {
  projectId,
  realtime: {
    getRealtimeToken: async () => {
      // Return a token string OR a token payload object.
      return {
        token: await fetchRealtimeJwt(),
        realtime_url: 'wss://api.example.com/realtime/connection/websocket', // optional
      };
    },
    // Optional override when callback returns token string only:
    realtimeUrl: 'wss://api.example.com/realtime/connection/websocket',
    // Optional, defaults to true:
    autoConnect: true,
  },
});

const todosChannel = db.realtime.table('todos', { schema: 'public' });
await todosChannel.subscribe({
  onInsert: (payload) => console.log('INSERT', payload),
  onUpdate: (payload) => console.log('UPDATE', payload),
  onDelete: (payload) => console.log('DELETE', payload),
  onError: (error) => console.error(error),
});

const broadcastChannel = db.realtime.broadcast('notifications');
await broadcastChannel.subscribe({
  onBroadcast: (payload) => console.log('broadcast', payload),
});

await db.realtime.disconnect();
```

Notes:
- Phase 1 does not include first-party token issuance. `getRealtimeToken` is the source of truth.
- `table('todos')` maps to `db:{project_id}:public:todos`.
- `broadcast('notifications')` maps to `broadcast:{project_id}:notifications`.

## Storage

```ts
const storage = db.storage.from('images');

// Existing multipart upload API (backward compatible)
await storage.upload('avatars/user-1.png', file);

// Strategy selector (auto | multipart | stream | resumable)
await storage.uploadObject('uploads/video.mp4', file, {
  strategy: 'auto', // optional override
});

// Explicit resumable lifecycle
const session = await storage.createResumableSession('uploads/big.bin', {
  uploadLength: file.size,
  contentType: file.type || 'application/octet-stream',
});

if (session.data) {
  let offset = session.data.upload_offset;
  while (offset < file.size) {
    const chunk = file.slice(offset, offset + 8 * 1024 * 1024);
    const append = await storage.appendResumableChunk(session.data.upload_id, chunk, {
      uploadOffset: offset,
      recoverOffsetMismatch: true, // retries once using server offset
    });
    if (!append.data) break;
    offset = append.data.upload_offset;
  }

  await storage.completeResumableSession(session.data.upload_id);
}

// HEAD probe + abort
await storage.getResumableUploadState('upload_id');
await storage.abortResumableSession('upload_id');
```

## Storage URL Builders

```ts
const objectUrl = storage.getPublicUrl('avatars/user-1.png');

const signedUrl = storage.getSignedObjectUrl('private/report.pdf', {
  token: 'signed_token',
});

const transformUrl = storage.getTransformUrl('avatars/user-1.png', {
  width: 512,
  height: 512,
  format: 'webp',
  quality: 82,
});
```

## Storage Strategy Defaults

```ts
const db = createClient(baseUrl, apiKey, {
  projectId,
  storage: {
    publicUrlBase: 'https://cdn.example.com/storage/proj_1234abcd5678ef90',
    uploadStrategy: {
      defaultStrategy: 'auto',
      multipartMaxBytes: 8 * 1024 * 1024,
      resumableMinBytes: 64 * 1024 * 1024,
      resumableChunkSizeBytes: 8 * 1024 * 1024,
    },
  },
});
```

Migration notes:
- `storage.upload(path, file)` behavior is unchanged and remains multipart.
- Use `storage.uploadObject(...)` to enable strategy selection and resumable fallback.
- `storage.getPublicUrl(...)` still works as before; `publicUrlBase` continues to provide CDN host/path overrides.

## Development

```bash
npm install
npm run build
npm test
```

Integration tests require a running LaunchDB stack:

```bash
npm run build
LAUNCHDB_BASE_URL=http://localhost:8000 \
LAUNCHDB_API_KEY=... \
LAUNCHDB_PROJECT_ID=... \
npm run test:integration
```

Optional realtime smoke (Phase 1 callback mode):

```bash
npm run build
LAUNCHDB_SDK_INTEGRATION_SCOPE=core \
LAUNCHDB_SDK_REALTIME_SMOKE=1 \
LAUNCHDB_REALTIME_TOKEN=... \
LAUNCHDB_REALTIME_URL=wss://api.example.com/realtime/connection/websocket \
LAUNCHDB_BASE_URL=http://localhost:8000 \
LAUNCHDB_API_KEY=... \
LAUNCHDB_PROJECT_ID=... \
npm run test:integration
```

Dedicated realtime smoke gate (Phase 1):

```bash
# Optional: set LAUNCHDB_REALTIME_URL to force a websocket endpoint override.
LAUNCHDB_SDK_INTEGRATION_MODE=live \
LAUNCHDB_BASE_URL=http://localhost:8000 \
LAUNCHDB_API_KEY=... \
LAUNCHDB_PROJECT_ID=... \
LAUNCHDB_REALTIME_TOKEN=... \
LAUNCHDB_REALTIME_URL=wss://api.example.com/realtime/connection/websocket \
bash testing/sdk/test-phase1-realtime.sh
```

Phase 5 parity smoke gate:

```bash
# Deterministic local gate (mock mode)
LAUNCHDB_SDK_INTEGRATION_MODE=mock \
bash testing/sdk/test-phase5-sdk.sh

# Live stack gate (requires runtime env)
LAUNCHDB_SDK_INTEGRATION_MODE=live \
LAUNCHDB_BASE_URL=http://localhost:8000 \
LAUNCHDB_API_KEY=... \
LAUNCHDB_PROJECT_ID=... \
bash testing/sdk/test-phase5-sdk.sh
```

Prereqs:
- Target project has a `todos` table with RLS policy (see `docs/starter/QUICKSTART.md`).
- Storage bucket `images` exists (or set `LAUNCHDB_TEST_BUCKET`).

If email verification is enabled, set `LAUNCHDB_TEST_EMAIL` and `LAUNCHDB_TEST_PASSWORD` to a pre-verified user.