lowlander 0.2.2 → 0.2.3
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 +41 -38
- package/build/client/client.d.ts +2 -2
- package/build/client/client.js +5 -2
- package/build/client/client.js.map +1 -1
- package/build/examples/helloworld/server/api.d.ts +4 -0
- package/build/examples/helloworld/server/api.d.ts.map +1 -1
- package/build/examples/helloworld/server/api.js +19 -2
- package/build/examples/helloworld/server/api.js.map +1 -1
- package/build/server/server.d.ts +1 -1
- package/build/server/server.js +1 -1
- package/build/tsconfig.client.tsbuildinfo +1 -1
- package/build/tsconfig.server.tsbuildinfo +1 -1
- package/client/client.ts +9 -5
- package/package.json +5 -4
- package/server/server.ts +1 -1
- package/skill/Connection.md +35 -0
- package/skill/Connection_pruneCommitIds.md +8 -0
- package/skill/SKILL.md +18 -193
- package/skill/ServerProxy.md +30 -0
- package/skill/Socket.md +22 -0
- package/skill/Socket_send.md +11 -0
- package/skill/Socket_subscribe.md +8 -0
- package/skill/createStreamType.md +44 -0
- package/skill/pushModel.md +14 -0
- package/skill/sendModel.md +14 -0
- package/skill/start.md +21 -0
- package/skill/warpsocket.md +3 -0
package/skill/SKILL.md
CHANGED
|
@@ -285,90 +285,29 @@ Set `EDINBURGH_LOG_LEVEL` similarly for Edinburgh internals.
|
|
|
285
285
|
|
|
286
286
|
The following is auto-generated from `server/server.ts`:
|
|
287
287
|
|
|
288
|
-
### createStreamType · function
|
|
288
|
+
### [createStreamType](createStreamType.md) · function
|
|
289
289
|
|
|
290
290
|
Creates a stream type for reactive model streaming to clients with automatic updates.
|
|
291
291
|
|
|
292
|
-
|
|
293
|
-
Supports nested linked models and type-safe field selection.
|
|
294
|
-
|
|
295
|
-
**Signature:** `<T, S extends FieldSelection<T>>(Model: typeof E.Model<unknown> & (new (...args: any[]) => T), selection: S & ValidateSelection<T, S>) => typeof StreamType`
|
|
296
|
-
|
|
297
|
-
**Type Parameters:**
|
|
298
|
-
|
|
299
|
-
- `T`
|
|
300
|
-
- `S extends FieldSelection<T>`
|
|
301
|
-
|
|
302
|
-
**Parameters:**
|
|
303
|
-
|
|
304
|
-
- `Model: ModelClass & (new (...args: any[]) => T)` - - The Edinburgh model class
|
|
305
|
-
- `selection: S & ValidateSelection<T, S>` - - Field selection: `true` for simple fields, nested object for linked models
|
|
306
|
-
|
|
307
|
-
**Returns:** Stream type class to instantiate in API functions
|
|
308
|
-
|
|
309
|
-
**Examples:**
|
|
310
|
-
|
|
311
|
-
```ts
|
|
312
|
-
|
|
313
|
-
### sendModel · function
|
|
292
|
+
### [sendModel](sendModel.md) · function
|
|
314
293
|
|
|
315
294
|
Sends (updated) data for `model` to `target`.
|
|
316
295
|
`target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
|
|
317
296
|
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
**Parameters:**
|
|
321
|
-
|
|
322
|
-
- `target: Uint8Array | number | number[]`
|
|
323
|
-
- `model: E.Model<any>`
|
|
324
|
-
- `commitId: number`
|
|
325
|
-
- `StreamType: typeof StreamTypeBase<any>`
|
|
326
|
-
- `changed?: E.Change`
|
|
327
|
-
|
|
328
|
-
### pushModel · function
|
|
297
|
+
### [pushModel](pushModel.md) · function
|
|
329
298
|
|
|
330
299
|
Subscribes `target` to this model, and sends initial data.
|
|
331
300
|
`target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
|
|
332
301
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
**Parameters:**
|
|
336
|
-
|
|
337
|
-
- `target: number | Uint8Array | number[]`
|
|
338
|
-
- `model: E.Model<any>`
|
|
339
|
-
- `commitId: number`
|
|
340
|
-
- `SubStreamType: typeof StreamTypeBase<any>`
|
|
341
|
-
- `delta: number`
|
|
342
|
-
|
|
343
|
-
### start · function
|
|
302
|
+
### [start](start.md) · function
|
|
344
303
|
|
|
345
304
|
Starts the Lowlander WebSocket server.
|
|
346
305
|
|
|
347
|
-
**Signature:** `(mainApiFile: string, opts?: { bind?: string; threads?: number; injectWarpSocket?: typeof import("/var/home/frank/projects/lowlander/node_modules/warpsocket/dist/src/index", { with: { "resolution-mode": "import" } }); }) => Promise<void>`
|
|
348
|
-
|
|
349
|
-
**Parameters:**
|
|
350
|
-
|
|
351
|
-
- `mainApiFile: string` - - Absolute path to the compiled API file exporting server functions
|
|
352
|
-
- `opts: {bind?: string, threads?: number, injectWarpSocket?: typeof realWarpsocket}` (optional)
|
|
353
|
-
|
|
354
|
-
**Examples:**
|
|
355
|
-
|
|
356
|
-
```ts
|
|
357
|
-
import { start } from 'lowlander/server';
|
|
358
|
-
import { fileURLToPath } from 'url';
|
|
359
|
-
import { resolve, dirname } from 'path';
|
|
360
|
-
|
|
361
|
-
const API_FILE = resolve(dirname(fileURLToPath(import.meta.url)), 'api.js');
|
|
362
|
-
start(API_FILE, { bind: '0.0.0.0:8080' });
|
|
363
|
-
```
|
|
364
|
-
|
|
365
306
|
### logLevel · constant
|
|
366
307
|
|
|
367
308
|
**Value:** `number`
|
|
368
309
|
|
|
369
|
-
### warpsocket · class
|
|
370
|
-
|
|
371
|
-
**Type:** `typeof import("/var/home/frank/projects/lowlander/node_modules/warpsocket/dist/src/index", { with: { "resolution-mode": "import" } })`
|
|
310
|
+
### [warpsocket](warpsocket.md) · class
|
|
372
311
|
|
|
373
312
|
### StreamTypeBase · abstract class
|
|
374
313
|
|
|
@@ -390,114 +329,47 @@ start(API_FILE, { bind: '0.0.0.0:8080' });
|
|
|
390
329
|
|
|
391
330
|
**Signature:** `() => string`
|
|
392
331
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
### ServerProxy · class
|
|
332
|
+
### [ServerProxy](ServerProxy.md) · class
|
|
397
333
|
|
|
398
334
|
Wraps a server-side API object to create a stateful, type-safe proxy accessible from clients.
|
|
399
335
|
Use for authentication, sessions, or any stateful context that persists across RPC calls.
|
|
400
336
|
|
|
401
|
-
**Type Parameters:**
|
|
402
|
-
|
|
403
|
-
- `API extends object`
|
|
404
|
-
- `RETURN`
|
|
405
|
-
|
|
406
|
-
**Examples:**
|
|
407
|
-
|
|
408
|
-
```ts
|
|
409
|
-
export class UserAPI {
|
|
410
|
-
constructor(public user: User) {}
|
|
411
|
-
getSecret() { return this.user.secret; }
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
export async function authenticate(token: string) {
|
|
415
|
-
const user = await validateToken(token);
|
|
416
|
-
return new ServerProxy(new UserAPI(user), user.name);
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
// Client: auth.value is user name, auth.serverProxy.getSecret() calls UserAPI method
|
|
420
|
-
```
|
|
421
|
-
|
|
422
|
-
**Constructor Parameters:**
|
|
423
|
-
|
|
424
|
-
- `api`: - Server-side API object exposed to the client
|
|
425
|
-
- `value`: - Value returned immediately to the client
|
|
426
|
-
|
|
427
337
|
#### serverProxy.toString · method
|
|
428
338
|
|
|
429
339
|
**Signature:** `() => string`
|
|
430
340
|
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
### Socket · class
|
|
341
|
+
### [Socket](Socket.md) · class
|
|
435
342
|
|
|
436
343
|
Server-side socket for pushing data to a client. Server functions with `Socket<T>` parameters
|
|
437
344
|
receive client callbacks on the client side.
|
|
438
345
|
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
- `T`
|
|
442
|
-
|
|
443
|
-
**Examples:**
|
|
444
|
-
|
|
445
|
-
```ts
|
|
446
|
-
// Server
|
|
447
|
-
export function streamNumbers(socket: Socket<number>) {
|
|
448
|
-
setInterval(() => {
|
|
449
|
-
if (!socket.send(Math.random())) clearInterval(interval);
|
|
450
|
-
}, 1000);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
// Client
|
|
454
|
-
api.streamNumbers(num => console.log(num));
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
#### socket.send · method
|
|
346
|
+
#### [socket.send](Socket_send.md) · method
|
|
458
347
|
|
|
459
348
|
Sends data to the client.
|
|
460
349
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
**Parameters:**
|
|
464
|
-
|
|
465
|
-
- `data: T` - - Data to send (automatically serialized)
|
|
466
|
-
|
|
467
|
-
**Returns:** `true` if sent, `false` if socket is closed
|
|
468
|
-
|
|
469
|
-
#### socket.subscribe · method
|
|
470
|
-
|
|
471
|
-
**Signature:** `(channel: Uint8Array<ArrayBufferLike>, delta?: number) => void`
|
|
472
|
-
|
|
473
|
-
**Parameters:**
|
|
474
|
-
|
|
475
|
-
- `channel: Uint8Array`
|
|
476
|
-
- `delta: any` (optional)
|
|
350
|
+
#### [socket.subscribe](Socket_subscribe.md) · method
|
|
477
351
|
|
|
478
352
|
#### socket.toString · method
|
|
479
353
|
|
|
480
354
|
**Signature:** `() => string`
|
|
481
355
|
|
|
482
|
-
**Parameters:**
|
|
483
|
-
|
|
484
|
-
|
|
485
356
|
#### socket.[Symbol.for('nodejs.util.inspect.custom')] · method
|
|
486
357
|
|
|
487
358
|
**Signature:** `() => string`
|
|
488
359
|
|
|
489
|
-
**Parameters:**
|
|
490
|
-
|
|
491
|
-
|
|
492
360
|
## Client API Reference
|
|
493
361
|
|
|
494
362
|
The following is auto-generated from `client/client.ts`:
|
|
495
363
|
|
|
496
|
-
###
|
|
364
|
+
### setLogLevel · function
|
|
497
365
|
|
|
498
|
-
Set to
|
|
366
|
+
Set to 0-3 for increasing verbosity.
|
|
499
367
|
|
|
500
|
-
**
|
|
368
|
+
**Signature:** `(level: number) => void`
|
|
369
|
+
|
|
370
|
+
**Parameters:**
|
|
371
|
+
|
|
372
|
+
- `level: number`
|
|
501
373
|
|
|
502
374
|
### ClientProxyObject · type
|
|
503
375
|
|
|
@@ -507,42 +379,11 @@ Transforms server-side API objects to client-side proxy objects with type-safe R
|
|
|
507
379
|
[K in keyof T]: ClientProxyFunction<T[K]>
|
|
508
380
|
}`
|
|
509
381
|
|
|
510
|
-
### Connection · class
|
|
382
|
+
### [Connection](Connection.md) · class
|
|
511
383
|
|
|
512
384
|
WebSocket connection to a Lowlander server with type-safe RPC, automatic reconnection,
|
|
513
385
|
and reactive updates.
|
|
514
386
|
|
|
515
|
-
**Type Parameters:**
|
|
516
|
-
|
|
517
|
-
- `T`
|
|
518
|
-
|
|
519
|
-
**Examples:**
|
|
520
|
-
|
|
521
|
-
```ts
|
|
522
|
-
import type * as API from './server/api.js';
|
|
523
|
-
const conn = new Connection<typeof API>('ws://localhost:8080/');
|
|
524
|
-
|
|
525
|
-
// Simple RPC - returns PromiseProxy
|
|
526
|
-
const sum = conn.api.add(1, 2);
|
|
527
|
-
|
|
528
|
-
// Server proxy for stateful APIs
|
|
529
|
-
const auth = conn.api.authenticate('token');
|
|
530
|
-
const secret = auth.serverProxy.getSecret();
|
|
531
|
-
|
|
532
|
-
// Streaming with callbacks
|
|
533
|
-
conn.api.streamData(data => console.log(data));
|
|
534
|
-
|
|
535
|
-
// Use within Aberdeen reactive scopes
|
|
536
|
-
$(() => {
|
|
537
|
-
dump(conn.isOnline());
|
|
538
|
-
dump(sum);
|
|
539
|
-
});
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
**Constructor Parameters:**
|
|
543
|
-
|
|
544
|
-
- `url`: - WebSocket URL (e.g., 'ws://localhost:8080/'), or a fake WebSocket object for testing
|
|
545
|
-
|
|
546
387
|
#### connection.ws · property
|
|
547
388
|
|
|
548
389
|
**Type:** `WebSocket`
|
|
@@ -577,29 +418,13 @@ Returns the current connection status. Reactive in Aberdeen scopes.
|
|
|
577
418
|
|
|
578
419
|
**Signature:** `() => boolean`
|
|
579
420
|
|
|
580
|
-
**Parameters:**
|
|
581
|
-
|
|
582
|
-
|
|
583
421
|
#### connection.connect · method
|
|
584
422
|
|
|
585
423
|
**Signature:** `() => void`
|
|
586
424
|
|
|
587
|
-
**Parameters:**
|
|
588
|
-
|
|
589
|
-
|
|
590
425
|
#### connection.reconnect · method
|
|
591
426
|
|
|
592
427
|
**Signature:** `() => void`
|
|
593
428
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
#### connection.pruneCommitIds · method
|
|
598
|
-
|
|
599
|
-
**Signature:** `(request: ActiveRequest, maxCommitId: number) => void`
|
|
600
|
-
|
|
601
|
-
**Parameters:**
|
|
602
|
-
|
|
603
|
-
- `request: ActiveRequest`
|
|
604
|
-
- `maxCommitId: number`
|
|
429
|
+
#### [connection.pruneCommitIds](Connection_pruneCommitIds.md) · method
|
|
605
430
|
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
### ServerProxy · class
|
|
2
|
+
|
|
3
|
+
Wraps a server-side API object to create a stateful, type-safe proxy accessible from clients.
|
|
4
|
+
Use for authentication, sessions, or any stateful context that persists across RPC calls.
|
|
5
|
+
|
|
6
|
+
**Type Parameters:**
|
|
7
|
+
|
|
8
|
+
- `API extends object`
|
|
9
|
+
- `RETURN`
|
|
10
|
+
|
|
11
|
+
**Examples:**
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
export class UserAPI {
|
|
15
|
+
constructor(public user: User) {}
|
|
16
|
+
getSecret() { return this.user.secret; }
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function authenticate(token: string) {
|
|
20
|
+
const user = await validateToken(token);
|
|
21
|
+
return new ServerProxy(new UserAPI(user), user.name);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Client: auth.value is user name, auth.serverProxy.getSecret() calls UserAPI method
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Constructor Parameters:**
|
|
28
|
+
|
|
29
|
+
- `api`: - Server-side API object exposed to the client
|
|
30
|
+
- `value`: - Value returned immediately to the client
|
package/skill/Socket.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
### Socket · class
|
|
2
|
+
|
|
3
|
+
Server-side socket for pushing data to a client. Server functions with `Socket<T>` parameters
|
|
4
|
+
receive client callbacks on the client side.
|
|
5
|
+
|
|
6
|
+
**Type Parameters:**
|
|
7
|
+
|
|
8
|
+
- `T`
|
|
9
|
+
|
|
10
|
+
**Examples:**
|
|
11
|
+
|
|
12
|
+
```ts
|
|
13
|
+
// Server
|
|
14
|
+
export function streamNumbers(socket: Socket<number>) {
|
|
15
|
+
setInterval(() => {
|
|
16
|
+
if (!socket.send(Math.random())) clearInterval(interval);
|
|
17
|
+
}, 1000);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Client
|
|
21
|
+
api.streamNumbers(num => console.log(num));
|
|
22
|
+
```
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
### createStreamType · function
|
|
2
|
+
|
|
3
|
+
Creates a stream type for reactive model streaming to clients with automatic updates.
|
|
4
|
+
|
|
5
|
+
Specify which fields to include; when they change, updates are pushed to subscribed clients.
|
|
6
|
+
Supports nested linked models and type-safe field selection.
|
|
7
|
+
|
|
8
|
+
**Signature:** `<T, S extends FieldSelection<T>>(Model: typeof E.Model<unknown> & (new (...args: any[]) => T), selection: S & ValidateSelection<T, S>) => typeof StreamType`
|
|
9
|
+
|
|
10
|
+
**Type Parameters:**
|
|
11
|
+
|
|
12
|
+
- `T`
|
|
13
|
+
- `S extends FieldSelection<T>`
|
|
14
|
+
|
|
15
|
+
**Parameters:**
|
|
16
|
+
|
|
17
|
+
- `Model: ModelClass & (new (...args: any[]) => T)` - - The Edinburgh model class
|
|
18
|
+
- `selection: S & ValidateSelection<T, S>` - - Field selection: `true` for simple fields, nested object for linked models
|
|
19
|
+
|
|
20
|
+
**Returns:** Stream type class to instantiate in API functions
|
|
21
|
+
|
|
22
|
+
**Examples:**
|
|
23
|
+
|
|
24
|
+
```ts
|
|
25
|
+
@E.registerModel
|
|
26
|
+
class Person extends Model {
|
|
27
|
+
name = field(string);
|
|
28
|
+
age = field(number);
|
|
29
|
+
password = field(string);
|
|
30
|
+
friends = field(array(link(Person)));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Exclude password, include friends' names
|
|
34
|
+
const PersonStream = createStreamType(Person, {
|
|
35
|
+
name: true,
|
|
36
|
+
age: true,
|
|
37
|
+
friends: { name: true }
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export function streamPerson() {
|
|
41
|
+
const person = Person.byName.get('Alice')!;
|
|
42
|
+
return new PersonStream(person);
|
|
43
|
+
}
|
|
44
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
### pushModel · function
|
|
2
|
+
|
|
3
|
+
Subscribes `target` to this model, and sends initial data.
|
|
4
|
+
`target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
|
|
5
|
+
|
|
6
|
+
**Signature:** `(target: number | Uint8Array<ArrayBufferLike> | number[], model: Model<any>, commitId: number, SubStreamType: typeof StreamTypeBase<any>, delta: number) => void`
|
|
7
|
+
|
|
8
|
+
**Parameters:**
|
|
9
|
+
|
|
10
|
+
- `target: number | Uint8Array | number[]`
|
|
11
|
+
- `model: E.Model<any>`
|
|
12
|
+
- `commitId: number`
|
|
13
|
+
- `SubStreamType: typeof StreamTypeBase<any>`
|
|
14
|
+
- `delta: number`
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
### sendModel · function
|
|
2
|
+
|
|
3
|
+
Sends (updated) data for `model` to `target`.
|
|
4
|
+
`target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
|
|
5
|
+
|
|
6
|
+
**Signature:** `(target: number | Uint8Array<ArrayBufferLike> | number[], model: Model<any>, commitId: number, StreamType: typeof StreamTypeBase<any>, changed?: Change) => void`
|
|
7
|
+
|
|
8
|
+
**Parameters:**
|
|
9
|
+
|
|
10
|
+
- `target: Uint8Array | number | number[]`
|
|
11
|
+
- `model: E.Model<any>`
|
|
12
|
+
- `commitId: number`
|
|
13
|
+
- `StreamType: typeof StreamTypeBase<any>`
|
|
14
|
+
- `changed?: E.Change`
|
package/skill/start.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
### start · function
|
|
2
|
+
|
|
3
|
+
Starts the Lowlander WebSocket server.
|
|
4
|
+
|
|
5
|
+
**Signature:** `(mainApiFile: string, opts?: { bind?: string; threads?: number; injectWarpSocket?: typeof import("/var/home/frank/projects/warpsocket/dist/src/index", { with: { "resolution-mode": "import" } }); }) => Promise<void>`
|
|
6
|
+
|
|
7
|
+
**Parameters:**
|
|
8
|
+
|
|
9
|
+
- `mainApiFile: string` - - Absolute path to the compiled API file exporting server functions
|
|
10
|
+
- `opts: {bind?: string, threads?: number, injectWarpSocket?: typeof realWarpsocket}` (optional)
|
|
11
|
+
|
|
12
|
+
**Examples:**
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { start } from 'lowlander/server';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
17
|
+
import { resolve, dirname } from 'path';
|
|
18
|
+
|
|
19
|
+
const API_FILE = resolve(dirname(fileURLToPath(import.meta.url)), 'api.js');
|
|
20
|
+
start(API_FILE, { bind: '0.0.0.0:8080' });
|
|
21
|
+
```
|