kalam-link 0.2.0-alpha1

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/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ Copyright 2025 KalamDB Team
6
+
7
+ Licensed under the Apache License, Version 2.0 (the "License");
8
+ you may not use this file except in compliance with the License.
9
+ You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing, software
14
+ distributed under the License is distributed on an "AS IS" BASIS,
15
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ See the License for the specific language governing permissions and
17
+ limitations under the License.
package/README.md ADDED
@@ -0,0 +1,423 @@
1
+ # kalam-link
2
+
3
+ Official TypeScript/JavaScript client for KalamDB.
4
+
5
+ - Execute SQL over HTTP
6
+ - Subscribe to real-time changes over WebSocket
7
+ - Works in modern browsers and Node.js (>= 18)
8
+
9
+ ## Installation
10
+
11
+ ```bash
12
+ npm i kalam-link
13
+ ```
14
+
15
+ ## Quick Start
16
+
17
+ ```ts
18
+ import { createClient, Auth, MessageType, ChangeType } from 'kalam-link';
19
+
20
+ const client = createClient({
21
+ url: 'http://localhost:8080',
22
+ auth: Auth.basic('admin', 'admin'),
23
+ });
24
+
25
+ await client.connect();
26
+
27
+ // Query
28
+ const result = await client.query('SELECT 1 AS ok');
29
+ console.log(result.results[0]);
30
+
31
+ // Subscribe (returns an unsubscribe function)
32
+ const unsubscribe = await client.subscribe('app.messages', (event) => {
33
+ switch (event.type) {
34
+ case MessageType.Change:
35
+ if (event.change_type === ChangeType.Insert) {
36
+ console.log('New row:', event.rows);
37
+ }
38
+ break;
39
+ case MessageType.InitialDataBatch:
40
+ console.log('Initial data:', event.rows);
41
+ break;
42
+ case MessageType.Error:
43
+ console.error('Subscription error:', event.message);
44
+ break;
45
+ }
46
+ });
47
+
48
+ // Later
49
+ await unsubscribe();
50
+ await client.disconnect();
51
+ ```
52
+
53
+ ## Authentication
54
+
55
+ ```ts
56
+ import { createClient, Auth } from 'kalam-link';
57
+
58
+ // Username/password
59
+ createClient({ url: 'http://localhost:8080', auth: Auth.basic('user', 'pass') });
60
+
61
+ // JWT token
62
+ createClient({ url: 'http://localhost:8080', auth: Auth.jwt('eyJhbGciOiJIUzI1NiIs...') });
63
+
64
+ // No auth (for local/dev setups that allow it)
65
+ createClient({ url: 'http://localhost:8080', auth: Auth.none() });
66
+ ```
67
+
68
+ ## API Reference (Complete)
69
+
70
+ Everything below is exported from `kalam-link` unless noted otherwise.
71
+
72
+ ### Imports
73
+
74
+ ```ts
75
+ import KalamDBClient, {
76
+ createClient,
77
+ Auth,
78
+ // Enums
79
+ MessageType,
80
+ ChangeType,
81
+ BatchStatus,
82
+ // Types
83
+ type AuthCredentials,
84
+ type BasicAuthCredentials,
85
+ type JwtAuthCredentials,
86
+ type NoAuthCredentials,
87
+ type ClientOptions,
88
+ type ClientOptionsWithAuth,
89
+ type ClientOptionsLegacy,
90
+ type ConnectionOptions,
91
+ type SubscriptionOptions,
92
+ type SubscribeOptions,
93
+ type QueryResponse,
94
+ type QueryResult,
95
+ type SchemaField,
96
+ type ErrorDetail,
97
+ type ServerMessage,
98
+ type BatchControl,
99
+ type SubscriptionCallback,
100
+ type SubscriptionInfo,
101
+ type Unsubscribe,
102
+ type WasmKalamClient,
103
+ // Helpers
104
+ buildAuthHeader,
105
+ encodeBasicAuth,
106
+ isAuthenticated,
107
+ isBasicAuth,
108
+ isJwtAuth,
109
+ isNoAuth,
110
+ } from 'kalam-link';
111
+ ```
112
+
113
+ ### Factory
114
+
115
+ #### `createClient(options: ClientOptions): KalamDBClient`
116
+
117
+ ```ts
118
+ type ClientOptions = ClientOptionsWithAuth | ClientOptionsLegacy;
119
+
120
+ interface ClientOptionsWithAuth {
121
+ url: string;
122
+ auth: AuthCredentials;
123
+ }
124
+
125
+ interface ClientOptionsLegacy {
126
+ url: string;
127
+ username: string;
128
+ password: string;
129
+ }
130
+ ```
131
+
132
+ ### Class: `KalamDBClient` (default export)
133
+
134
+ #### Constructors
135
+
136
+ ```ts
137
+ new KalamDBClient(options: ClientOptionsWithAuth)
138
+
139
+ // Legacy (deprecated)
140
+ new KalamDBClient(url: string, username: string, password: string)
141
+ ```
142
+
143
+ #### Lifecycle
144
+
145
+ ```ts
146
+ initialize(): Promise<void>
147
+ connect(): Promise<void>
148
+ disconnect(): Promise<void>
149
+ isConnected(): boolean
150
+ ```
151
+
152
+ #### Authentication
153
+
154
+ ```ts
155
+ getAuthType(): 'basic' | 'jwt' | 'none'
156
+ ```
157
+
158
+ #### Queries
159
+
160
+ ```ts
161
+ query(sql: string, params?: any[]): Promise<QueryResponse>
162
+ ```
163
+
164
+ #### Convenience DML
165
+
166
+ ```ts
167
+ insert(tableName: string, data: Record<string, any>): Promise<QueryResponse>
168
+ delete(tableName: string, rowId: string | number): Promise<void>
169
+ ```
170
+
171
+ #### Subscriptions
172
+
173
+ ```ts
174
+ subscribe(
175
+ tableName: string,
176
+ callback: SubscriptionCallback,
177
+ options?: SubscriptionOptions
178
+ ): Promise<Unsubscribe>
179
+
180
+ subscribeWithSql(
181
+ sql: string,
182
+ callback: SubscriptionCallback,
183
+ options?: SubscriptionOptions
184
+ ): Promise<Unsubscribe>
185
+
186
+ unsubscribe(subscriptionId: string): Promise<void>
187
+ unsubscribeAll(): Promise<void>
188
+ ```
189
+
190
+ Subscription helpers:
191
+
192
+ ```ts
193
+ getSubscriptionCount(): number
194
+ getSubscriptions(): SubscriptionInfo[]
195
+ isSubscribedTo(tableNameOrSql: string): boolean
196
+ getLastSeqId(subscriptionId: string): string | undefined
197
+ ```
198
+
199
+ #### Reconnection controls
200
+
201
+ ```ts
202
+ setAutoReconnect(enabled: boolean): void
203
+ setReconnectDelay(initialDelayMs: number, maxDelayMs: number): void
204
+ setMaxReconnectAttempts(maxAttempts: number): void
205
+ getReconnectAttempts(): number
206
+ isReconnecting(): boolean
207
+ ```
208
+
209
+ ### Auth API
210
+
211
+ #### Types
212
+
213
+ ```ts
214
+ interface BasicAuthCredentials { type: 'basic'; username: string; password: string }
215
+ interface JwtAuthCredentials { type: 'jwt'; token: string }
216
+ interface NoAuthCredentials { type: 'none' }
217
+
218
+ type AuthCredentials =
219
+ | BasicAuthCredentials
220
+ | JwtAuthCredentials
221
+ | NoAuthCredentials;
222
+ ```
223
+
224
+ #### Factories
225
+
226
+ ```ts
227
+ Auth.basic(username: string, password: string): BasicAuthCredentials
228
+ Auth.jwt(token: string): JwtAuthCredentials
229
+ Auth.none(): NoAuthCredentials
230
+ ```
231
+
232
+ #### Helpers
233
+
234
+ ```ts
235
+ encodeBasicAuth(username: string, password: string): string
236
+ buildAuthHeader(auth: AuthCredentials): string | undefined
237
+
238
+ isBasicAuth(auth: AuthCredentials): auth is BasicAuthCredentials
239
+ isJwtAuth(auth: AuthCredentials): auth is JwtAuthCredentials
240
+ isNoAuth(auth: AuthCredentials): auth is NoAuthCredentials
241
+ isAuthenticated(auth: AuthCredentials): auth is BasicAuthCredentials | JwtAuthCredentials
242
+ ```
243
+
244
+ ### Query result types
245
+
246
+ ```ts
247
+ interface SchemaField {
248
+ name: string;
249
+ data_type: string;
250
+ index: number;
251
+ }
252
+
253
+ interface QueryResult {
254
+ schema: SchemaField[];
255
+ rows?: unknown[][];
256
+ row_count: number;
257
+ message?: string;
258
+ }
259
+
260
+ interface QueryResponse {
261
+ status: 'success' | 'error';
262
+ results: QueryResult[];
263
+ took?: number;
264
+ error?: ErrorDetail;
265
+ }
266
+
267
+ interface ErrorDetail {
268
+ code: string;
269
+ message: string;
270
+ details?: any;
271
+ }
272
+ ```
273
+
274
+ ### Enums
275
+
276
+ ```ts
277
+ // Message type for WebSocket subscription events
278
+ enum MessageType {
279
+ SubscriptionAck = 'subscription_ack',
280
+ InitialDataBatch = 'initial_data_batch',
281
+ Change = 'change',
282
+ Error = 'error',
283
+ }
284
+
285
+ // Change type for live subscription change events
286
+ enum ChangeType {
287
+ Insert = 'insert',
288
+ Update = 'update',
289
+ Delete = 'delete',
290
+ }
291
+
292
+ // Batch loading status
293
+ enum BatchStatus {
294
+ Loading = 'loading',
295
+ LoadingBatch = 'loading_batch',
296
+ Ready = 'ready',
297
+ }
298
+ ```
299
+
300
+ ### Live subscription event types
301
+
302
+ ```ts
303
+ type ServerMessage =
304
+ | {
305
+ type: MessageType.SubscriptionAck;
306
+ subscription_id: string;
307
+ total_rows: number;
308
+ batch_control: BatchControl;
309
+ schema: SchemaField[];
310
+ }
311
+ | {
312
+ type: MessageType.InitialDataBatch;
313
+ subscription_id: string;
314
+ rows: Record<string, any>[];
315
+ batch_control: BatchControl;
316
+ }
317
+ | {
318
+ type: MessageType.Change;
319
+ subscription_id: string;
320
+ change_type: ChangeType;
321
+ rows?: Record<string, any>[];
322
+ old_values?: Record<string, any>[];
323
+ }
324
+ | {
325
+ type: MessageType.Error;
326
+ subscription_id: string;
327
+ code: string;
328
+ message: string;
329
+ };
330
+
331
+ interface BatchControl {
332
+ batch_num: number;
333
+ has_more: boolean;
334
+ status: BatchStatus;
335
+ last_seq_id?: string;
336
+ snapshot_end_seq?: string;
337
+ }
338
+
339
+ type SubscriptionCallback = (event: ServerMessage) => void;
340
+ type Unsubscribe = () => Promise<void>;
341
+
342
+ interface SubscriptionInfo {
343
+ id: string;
344
+ tableName: string;
345
+ createdAt: Date;
346
+ }
347
+ ```
348
+
349
+ ### Options
350
+
351
+ ```ts
352
+ interface ConnectionOptions {
353
+ auto_reconnect?: boolean;
354
+ reconnect_delay_ms?: number;
355
+ max_reconnect_delay_ms?: number;
356
+ max_reconnect_attempts?: number;
357
+ }
358
+
359
+ interface SubscriptionOptions {
360
+ batch_size?: number;
361
+ last_rows?: number;
362
+ from_seq_id?: string;
363
+ }
364
+
365
+ type SubscribeOptions = SubscriptionOptions;
366
+ ```
367
+
368
+ ### Advanced: WASM entrypoint (`kalam-link/wasm`)
369
+
370
+ ```ts
371
+ import init, {
372
+ KalamClient,
373
+ WasmTimestampFormatter,
374
+ parseIso8601,
375
+ timestampNow,
376
+ initSync,
377
+ } from 'kalam-link/wasm';
378
+ ```
379
+
380
+ Exports:
381
+
382
+ - `default init(moduleOrPath?) => Promise<InitOutput>`
383
+ - `initSync(moduleOrBytes) => InitOutput`
384
+ - `class KalamClient` (low-level WASM client)
385
+ - `class WasmTimestampFormatter`
386
+ - `parseIso8601(isoString: string): number`
387
+ - `timestampNow(): number`
388
+
389
+ ## Notes (Browser/Node)
390
+
391
+ This package includes a small `.wasm` runtime under the hood.
392
+
393
+ - In browsers with bundlers (Vite/Webpack/Rollup), it should “just work”.
394
+ - If you see WASM loading errors, ensure your build serves/copies:
395
+ `node_modules/kalam-link/dist/wasm/kalam_link_bg.wasm`
396
+
397
+ ## Links
398
+
399
+ - KalamDB repository: https://github.com/jamals86/KalamDB
400
+ - Issues: https://github.com/jamals86/KalamDB/issues
401
+
402
+ ## Development (Build From This Repo)
403
+
404
+ Only needed if you are contributing to the SDK.
405
+
406
+ Prerequisites:
407
+
408
+ - Node.js `>=18`
409
+ - Rust toolchain + `wasm-pack`
410
+
411
+ ```bash
412
+ cd link/sdks/typescript
413
+ npm install
414
+ npm run build
415
+ ```
416
+
417
+ ## License
418
+
419
+ Apache-2.0 (see the repository root).
420
+
421
+ ## Contributing
422
+
423
+ Issues/PRs are welcome: https://github.com/jamals86/KalamDB/issues
package/dist/auth.d.ts ADDED
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Authentication types and utilities for KalamDB client
3
+ *
4
+ * Provides a type-safe authentication API with support for:
5
+ * - Basic Auth (username/password)
6
+ * - JWT Token Auth
7
+ * - Anonymous (no auth - for localhost bypass)
8
+ */
9
+ /**
10
+ * Basic authentication credentials (username/password)
11
+ */
12
+ export interface BasicAuthCredentials {
13
+ type: 'basic';
14
+ username: string;
15
+ password: string;
16
+ }
17
+ /**
18
+ * JWT token authentication credentials
19
+ */
20
+ export interface JwtAuthCredentials {
21
+ type: 'jwt';
22
+ token: string;
23
+ }
24
+ /**
25
+ * No authentication (anonymous access)
26
+ */
27
+ export interface NoAuthCredentials {
28
+ type: 'none';
29
+ }
30
+ /**
31
+ * Union type for all authentication credential types
32
+ */
33
+ export type AuthCredentials = BasicAuthCredentials | JwtAuthCredentials | NoAuthCredentials;
34
+ /**
35
+ * Type guard to check if credentials are Basic Auth
36
+ */
37
+ export declare function isBasicAuth(auth: AuthCredentials): auth is BasicAuthCredentials;
38
+ /**
39
+ * Type guard to check if credentials are JWT Auth
40
+ */
41
+ export declare function isJwtAuth(auth: AuthCredentials): auth is JwtAuthCredentials;
42
+ /**
43
+ * Type guard to check if credentials are No Auth
44
+ */
45
+ export declare function isNoAuth(auth: AuthCredentials): auth is NoAuthCredentials;
46
+ /**
47
+ * Type guard to check if any authentication is configured
48
+ */
49
+ export declare function isAuthenticated(auth: AuthCredentials): auth is BasicAuthCredentials | JwtAuthCredentials;
50
+ /**
51
+ * Encode username and password for Basic Auth header
52
+ *
53
+ * @param username - Username
54
+ * @param password - Password
55
+ * @returns Base64 encoded credentials string
56
+ */
57
+ export declare function encodeBasicAuth(username: string, password: string): string;
58
+ /**
59
+ * Build the Authorization header value for the given credentials
60
+ *
61
+ * @param auth - Authentication credentials
62
+ * @returns Authorization header value or undefined for no auth
63
+ */
64
+ export declare function buildAuthHeader(auth: AuthCredentials): string | undefined;
65
+ /**
66
+ * Auth factory for creating type-safe authentication credentials
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * import { createClient, Auth } from 'kalam-link';
71
+ *
72
+ * // Basic Auth
73
+ * const client = createClient({
74
+ * url: 'http://localhost:8080',
75
+ * auth: Auth.basic('admin', 'admin')
76
+ * });
77
+ *
78
+ * // JWT Token
79
+ * const jwtClient = createClient({
80
+ * url: 'http://localhost:8080',
81
+ * auth: Auth.jwt('eyJhbGciOiJIUzI1NiIs...')
82
+ * });
83
+ *
84
+ * // Anonymous (no auth)
85
+ * const anonClient = createClient({
86
+ * url: 'http://localhost:8080',
87
+ * auth: Auth.none()
88
+ * });
89
+ * ```
90
+ */
91
+ export declare const Auth: {
92
+ /**
93
+ * Create Basic Auth credentials
94
+ *
95
+ * @param username - Username for authentication
96
+ * @param password - Password for authentication
97
+ * @returns BasicAuthCredentials object
98
+ */
99
+ readonly basic: (username: string, password: string) => BasicAuthCredentials;
100
+ /**
101
+ * Create JWT Auth credentials
102
+ *
103
+ * @param token - JWT token string
104
+ * @returns JwtAuthCredentials object
105
+ */
106
+ readonly jwt: (token: string) => JwtAuthCredentials;
107
+ /**
108
+ * Create No Auth credentials (anonymous access)
109
+ *
110
+ * @returns NoAuthCredentials object
111
+ */
112
+ readonly none: () => NoAuthCredentials;
113
+ };
package/dist/auth.js ADDED
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Authentication types and utilities for KalamDB client
3
+ *
4
+ * Provides a type-safe authentication API with support for:
5
+ * - Basic Auth (username/password)
6
+ * - JWT Token Auth
7
+ * - Anonymous (no auth - for localhost bypass)
8
+ */
9
+ /**
10
+ * Type guard to check if credentials are Basic Auth
11
+ */
12
+ export function isBasicAuth(auth) {
13
+ return auth.type === 'basic';
14
+ }
15
+ /**
16
+ * Type guard to check if credentials are JWT Auth
17
+ */
18
+ export function isJwtAuth(auth) {
19
+ return auth.type === 'jwt';
20
+ }
21
+ /**
22
+ * Type guard to check if credentials are No Auth
23
+ */
24
+ export function isNoAuth(auth) {
25
+ return auth.type === 'none';
26
+ }
27
+ /**
28
+ * Type guard to check if any authentication is configured
29
+ */
30
+ export function isAuthenticated(auth) {
31
+ return auth.type !== 'none';
32
+ }
33
+ /**
34
+ * Base64 encode a string (works in both Node.js and browser)
35
+ */
36
+ function base64Encode(str) {
37
+ if (typeof btoa === 'function') {
38
+ // Browser environment
39
+ return btoa(str);
40
+ }
41
+ else if (typeof Buffer !== 'undefined') {
42
+ // Node.js environment
43
+ return Buffer.from(str).toString('base64');
44
+ }
45
+ throw new Error('No base64 encoding available');
46
+ }
47
+ /**
48
+ * Encode username and password for Basic Auth header
49
+ *
50
+ * @param username - Username
51
+ * @param password - Password
52
+ * @returns Base64 encoded credentials string
53
+ */
54
+ export function encodeBasicAuth(username, password) {
55
+ return base64Encode(`${username}:${password}`);
56
+ }
57
+ /**
58
+ * Build the Authorization header value for the given credentials
59
+ *
60
+ * @param auth - Authentication credentials
61
+ * @returns Authorization header value or undefined for no auth
62
+ */
63
+ export function buildAuthHeader(auth) {
64
+ switch (auth.type) {
65
+ case 'basic':
66
+ return `Basic ${encodeBasicAuth(auth.username, auth.password)}`;
67
+ case 'jwt':
68
+ return `Bearer ${auth.token}`;
69
+ case 'none':
70
+ return undefined;
71
+ default:
72
+ // Exhaustiveness check
73
+ const _exhaustive = auth;
74
+ throw new Error(`Unknown auth type: ${_exhaustive.type}`);
75
+ }
76
+ }
77
+ /**
78
+ * Auth factory for creating type-safe authentication credentials
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { createClient, Auth } from 'kalam-link';
83
+ *
84
+ * // Basic Auth
85
+ * const client = createClient({
86
+ * url: 'http://localhost:8080',
87
+ * auth: Auth.basic('admin', 'admin')
88
+ * });
89
+ *
90
+ * // JWT Token
91
+ * const jwtClient = createClient({
92
+ * url: 'http://localhost:8080',
93
+ * auth: Auth.jwt('eyJhbGciOiJIUzI1NiIs...')
94
+ * });
95
+ *
96
+ * // Anonymous (no auth)
97
+ * const anonClient = createClient({
98
+ * url: 'http://localhost:8080',
99
+ * auth: Auth.none()
100
+ * });
101
+ * ```
102
+ */
103
+ export const Auth = {
104
+ /**
105
+ * Create Basic Auth credentials
106
+ *
107
+ * @param username - Username for authentication
108
+ * @param password - Password for authentication
109
+ * @returns BasicAuthCredentials object
110
+ */
111
+ basic(username, password) {
112
+ return { type: 'basic', username, password };
113
+ },
114
+ /**
115
+ * Create JWT Auth credentials
116
+ *
117
+ * @param token - JWT token string
118
+ * @returns JwtAuthCredentials object
119
+ */
120
+ jwt(token) {
121
+ return { type: 'jwt', token };
122
+ },
123
+ /**
124
+ * Create No Auth credentials (anonymous access)
125
+ *
126
+ * @returns NoAuthCredentials object
127
+ */
128
+ none() {
129
+ return { type: 'none' };
130
+ }
131
+ };