lowlander 0.2.1 → 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.
Files changed (61) hide show
  1. package/README.md +288 -40
  2. package/build/client/client.d.ts +153 -0
  3. package/build/client/client.js +320 -0
  4. package/build/client/client.js.map +1 -0
  5. package/build/examples/helloworld/client/js/admin.d.ts +11 -0
  6. package/build/examples/helloworld/client/js/admin.js +87 -0
  7. package/build/examples/helloworld/client/js/admin.js.map +1 -0
  8. package/build/examples/helloworld/client/js/base.d.ts +4 -0
  9. package/{examples/helloworld/client/js/base.ts → build/examples/helloworld/client/js/base.js} +13 -25
  10. package/build/examples/helloworld/client/js/base.js.map +1 -0
  11. package/build/examples/helloworld/server/api.d.ts +44 -0
  12. package/build/examples/helloworld/server/api.d.ts.map +1 -0
  13. package/build/examples/helloworld/server/api.js +163 -0
  14. package/build/examples/helloworld/server/api.js.map +1 -0
  15. package/build/examples/helloworld/server/main.d.ts +2 -0
  16. package/build/examples/helloworld/server/main.d.ts.map +1 -0
  17. package/{examples/helloworld/server/main.ts → build/examples/helloworld/server/main.js} +3 -8
  18. package/build/examples/helloworld/server/main.js.map +1 -0
  19. package/build/server/protocol.d.ts +12 -0
  20. package/build/server/protocol.d.ts.map +1 -0
  21. package/build/server/protocol.js +19 -0
  22. package/build/server/protocol.js.map +1 -0
  23. package/build/server/server.d.ts +191 -0
  24. package/build/server/server.d.ts.map +1 -0
  25. package/build/server/server.js +379 -0
  26. package/build/server/server.js.map +1 -0
  27. package/build/server/wshandler.d.ts +11 -0
  28. package/build/server/wshandler.d.ts.map +1 -0
  29. package/build/server/wshandler.js +126 -0
  30. package/build/server/wshandler.js.map +1 -0
  31. package/build/tsconfig.client.tsbuildinfo +1 -0
  32. package/build/tsconfig.server.tsbuildinfo +1 -0
  33. package/client/client.ts +9 -5
  34. package/package.json +16 -9
  35. package/server/server.ts +2 -1
  36. package/skill/Connection.md +35 -0
  37. package/skill/Connection_pruneCommitIds.md +8 -0
  38. package/skill/SKILL.md +430 -0
  39. package/skill/ServerProxy.md +30 -0
  40. package/skill/Socket.md +22 -0
  41. package/skill/Socket_send.md +11 -0
  42. package/skill/Socket_subscribe.md +8 -0
  43. package/skill/createStreamType.md +44 -0
  44. package/skill/pushModel.md +14 -0
  45. package/skill/sendModel.md +14 -0
  46. package/skill/start.md +21 -0
  47. package/skill/warpsocket.md +3 -0
  48. package/AGENTS.md +0 -2
  49. package/ROADMAP.md +0 -13
  50. package/bun.lock +0 -281
  51. package/examples/helloworld/client/js/admin.ts +0 -94
  52. package/examples/helloworld/package.json +0 -8
  53. package/examples/helloworld/server/api.ts +0 -154
  54. package/tests/fake-warpsocket.ts +0 -452
  55. package/tests/helloworld.test.ts +0 -151
  56. package/tsconfig.client.json +0 -18
  57. package/tsconfig.json +0 -24
  58. package/tsconfig.server.json +0 -17
  59. package/tsconfig.test.json +0 -13
  60. /package/{examples → build/examples}/helloworld/client/assets/style.css +0 -0
  61. /package/{examples → build/examples}/helloworld/client/index.html +0 -0
package/skill/SKILL.md ADDED
@@ -0,0 +1,430 @@
1
+ ---
2
+ name: lowlander
3
+ description: Expert guidance for building full-stack TypeScript apps with Lowlander. Covers type-safe RPCs, Edinburgh model streaming, reactive client sync, ServerProxy, Socket callbacks, and Connection setup.
4
+ ---
5
+
6
+ # Lowlander
7
+
8
+ An **experimental** TypeScript framework for data persistence and (partial) client synchronization.
9
+
10
+ This project is still under heavy development. **DO NOT USE** for anything serious. Early feedback is very welcome though!
11
+
12
+ To get an impression of what use of this framework currently looks like, check out the example project's...
13
+
14
+ - [server-side API](https://github.com/vanviegen/lowlander/blob/main/examples/helloworld/server/api.ts) and
15
+ - [client-side UI](https://github.com/vanviegen/lowlander/blob/main/examples/helloworld/client/js/base.ts) code.
16
+
17
+ ## Tech
18
+
19
+ This library is built on top of a number of libraries by the same author:
20
+
21
+ - [Edinburgh](https://github.com/vanviegen/edinburgh): use JavaScript objects as really fast ACID database records.
22
+ - [OLMDB](https://github.com/vanviegen/olmdb): a very fast on-disk key/value store with MVCC and optimistic transactions, used by Edinburgh for persistence.
23
+ - [WarpSocket](https://github.com/vanviegen/warpsocket): a high-performance WebSocket server written in Rust, that coordinates multiple JavaScript worker threads and provides an API for channel subscriptions.
24
+ - [Aberdeen](https://github.com/vanviegen/aberdeen): a reactive UI library for JavaScript. It features fine-grained updates, needs no virtual DOM, and uses Proxy for reactivity.
25
+
26
+ Lowlander glues these together and adds real-time partial data synchronization and type-safe RPCs to provide a framework for rapidly building performant full-stack (database included!) web applications.
27
+
28
+ ## Tutorial
29
+
30
+ ### Project Setup
31
+
32
+ ```bash
33
+ bun init
34
+ bun add lowlander aberdeen edinburgh
35
+ ```
36
+
37
+ (npm should also work for all of this.)
38
+
39
+ Create the project structure:
40
+
41
+ ```
42
+ server/
43
+ main.ts # starts the server
44
+ api.ts # exported functions = RPC endpoints
45
+ client/
46
+ app.ts # UI using Aberdeen + Connection
47
+ ```
48
+
49
+ If you use Claude Code, GitHub Copilot or another AI agent that supports Skills, Lowlander and its dependencies include `skill/` directories that provide specialized knowledge to the AI.
50
+
51
+ Symlink them into your project's `.claude/skills` directory:
52
+
53
+ ```bash
54
+ mkdir -p .claude/skills
55
+ ln -s ../../node_modules/lowlander/skill .claude/skills/lowlander
56
+ ln -s ../../node_modules/aberdeen/skill .claude/skills/aberdeen
57
+ ln -s ../../node_modules/edinburgh/skill .claude/skills/edinburgh
58
+ ```
59
+
60
+ ### Server Entry Point
61
+
62
+ The entry point starts the WarpSocket server and points it at the API file:
63
+
64
+ ```ts
65
+ // server/main.ts
66
+ import { start } from 'lowlander/server';
67
+ import { fileURLToPath } from 'url';
68
+ import { resolve, dirname } from 'path';
69
+
70
+ const API_FILE = resolve(dirname(fileURLToPath(import.meta.url)), 'api.js');
71
+ start(API_FILE, { bind: '0.0.0.0:8080' });
72
+ ```
73
+
74
+ Options: `bind` (address:port), `threads` (worker count).
75
+
76
+ ### Defining RPC Endpoints
77
+
78
+ Every exported function in the API file is callable from the client. No decorators or registration needed:
79
+
80
+ ```ts
81
+ // server/api.ts
82
+ export function add(a: number, b: number): number {
83
+ return a + b;
84
+ }
85
+ ```
86
+
87
+ Functions can be `async`. Thrown errors are sent to the client as error responses.
88
+
89
+ ### Edinburgh Models
90
+
91
+ Define persistent data models using Edinburgh. See [Edinburgh docs](https://github.com/vanviegen/edinburgh) for full details.
92
+
93
+ ```ts
94
+ import * as E from 'edinburgh';
95
+
96
+ @E.registerModel
97
+ class Person extends E.Model<Person> {
98
+ static byName = E.primary(Person, 'name');
99
+ name = E.field(E.string);
100
+ age = E.field(E.number);
101
+ friends = E.field(E.array(E.link(Person)));
102
+ password = E.field(E.string);
103
+ }
104
+ ```
105
+
106
+ Models are ACID, and RPC calls automatically run in transactions. When creating a `new Instance()` or updating props on an existing instance, changes are persisted to disk automatically. `E.link` objects are lazy-loaded.
107
+
108
+ ### Model Streaming with `createStreamType`
109
+
110
+ Stream a subset of model fields to clients with real-time updates. Changes are pushed automatically. First you need to create a stream type, by doing this once:
111
+
112
+ ```ts
113
+ import { createStreamType } from 'lowlander/server';
114
+
115
+ // Exclude password; include friends' names and ages
116
+ const PersonStream = createStreamType(Person, {
117
+ name: true,
118
+ age: true,
119
+ friends: { // nested linked model: specify sub-selection
120
+ name: true,
121
+ age: true,
122
+ }
123
+ });
124
+ ```
125
+
126
+ Use `true` for plain fields. For linked model fields, provide a nested selection object. To return a stream instance from an API function:
127
+
128
+ ```ts
129
+ export function streamPerson(name: string) {
130
+ const person = Person.byName.get(name)!;
131
+ return new PersonStream(person);
132
+ }
133
+ ```
134
+
135
+ On the client, this returns a reactive Aberdeen proxy that updates live when server data changes.
136
+
137
+ ### ServerProxy for Stateful APIs
138
+
139
+ Wrap a class instance to expose per-connection stateful methods:
140
+
141
+ ```ts
142
+ import { ServerProxy } from 'lowlander/server';
143
+
144
+ class UserAPI {
145
+ constructor(public userName: string) {}
146
+
147
+ get user(): Person {
148
+ return Person.byName.get(this.userName)!;
149
+ }
150
+
151
+ getBio() {
152
+ return `${this.user.name} is ${this.user.age} years old`;
153
+ }
154
+ }
155
+
156
+ export async function authenticate(token: string) {
157
+ const user = Person.byName.get(token);
158
+ if (!user) throw new Error('User not found');
159
+ return new ServerProxy(new UserAPI(token), 'secret-value');
160
+ }
161
+ ```
162
+
163
+ The client receives `'secret-value'` as `.value` and can call `UserAPI` methods via `.serverProxy`.
164
+
165
+ ### Socket Callbacks
166
+
167
+ Use `Socket<T>` parameters for server-push streaming. On the client, these become callback functions:
168
+
169
+ ```ts
170
+ import { Socket } from 'lowlander/server';
171
+
172
+ export function streamNumbers(socket: Socket<number>) {
173
+ const interval = setInterval(() => {
174
+ if (!socket.send(Math.random())) clearInterval(interval);
175
+ }, 1000);
176
+ }
177
+ ```
178
+
179
+ `socket.send()` returns falsy when the client disconnects.
180
+
181
+ ### Client Connection
182
+
183
+ Connect to the server with full type safety:
184
+
185
+ ```ts
186
+ import { Connection } from 'lowlander/client';
187
+ import type * as API from './server/api.js';
188
+
189
+ const conn = new Connection<typeof API>('ws://localhost:8080/');
190
+ const api = conn.api;
191
+ ```
192
+
193
+ All server exports are available on `conn.api` with matching types, except `Socket<T>` params become callbacks.
194
+
195
+ #### Simple RPC
196
+
197
+ ```ts
198
+ const sum = api.add(1, 2);
199
+ // sum is a PromiseProxy:
200
+ // - sum.value starts out as undefined, and reactively updates to the result when available
201
+ // - sum.error is an Error object if the call threw, or undefined otherwise
202
+ // - sum.promise can be awaited: `const val = await sum.promise;` - this throws on error
203
+ ```
204
+
205
+ #### Using ServerProxy
206
+
207
+ ```ts
208
+ const auth = api.authenticate('Frank');
209
+ // auth.value → 'secret-value' (after resolution)
210
+ // auth.serverProxy → typed proxy to UserAPI methods
211
+
212
+ const bio = auth.serverProxy.getBio();
213
+ // bio.value → "Frank is 45 years old"
214
+ ```
215
+
216
+ The server proxy is usable immediately—calls queue until authentication completes. If auth fails, queued calls fail too.
217
+
218
+ #### Model Streaming
219
+
220
+ ```ts
221
+ const person = api.streamPerson('Alice');
222
+ // person.value is a reactive proxy that auto-updates
223
+ ```
224
+
225
+ #### Socket Callbacks
226
+
227
+ ```ts
228
+ api.streamNumbers(num => console.log(num));
229
+ ```
230
+
231
+ On the server-side we should have a `export function streamNumbers(socket: Socket<number>)`.
232
+
233
+ #### Reactive Integration with Aberdeen
234
+
235
+ `PromiseProxy` results are reactive in Aberdeen scopes:
236
+
237
+ ```ts
238
+ import A from 'aberdeen';
239
+
240
+ const sum = api.add(1, 2);
241
+ A(() => {
242
+ if (sum.busy) A('span#Loading...');
243
+ else if (sum.error) A('span#Error: ' + sum.error.message);
244
+ else A('span#Result: ' + sum.value);
245
+ });
246
+ ```
247
+
248
+ Model streams are also reactive—nested data updates trigger fine-grained UI updates:
249
+
250
+ ```ts
251
+ const model = api.streamModel();
252
+ A(() => {
253
+ if (!model.value) return;
254
+ A('h2#' + model.value.name);
255
+ A('p#Owner: ' + model.value.owner.name);
256
+ });
257
+ ```
258
+
259
+ #### Connection Status
260
+
261
+ ```ts
262
+ A(() => {
263
+ A('span#' + (conn.isOnline() ? 'Connected' : 'Offline'));
264
+ });
265
+ ```
266
+
267
+ Reconnection is automatic with exponential backoff.
268
+
269
+ #### Cleanup
270
+
271
+ Aberdeen's `clean()` handles RPC lifecycle. When a reactive scope is destroyed, active requests and subscriptions are cancelled automatically.
272
+
273
+ ### Logging
274
+
275
+ Set the `LOWLANDER_LOG_LEVEL` environment variable to a number from 0 to 3:
276
+
277
+ - 0: no logging (default)
278
+ - 1: connections & lifecycle
279
+ - 2: RPC calls & responses
280
+ - 3: model streaming & internals
281
+
282
+ Set `EDINBURGH_LOG_LEVEL` similarly for Edinburgh internals.
283
+
284
+ ## Server API Reference
285
+
286
+ The following is auto-generated from `server/server.ts`:
287
+
288
+ ### [createStreamType](createStreamType.md) · function
289
+
290
+ Creates a stream type for reactive model streaming to clients with automatic updates.
291
+
292
+ ### [sendModel](sendModel.md) · function
293
+
294
+ Sends (updated) data for `model` to `target`.
295
+ `target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
296
+
297
+ ### [pushModel](pushModel.md) · function
298
+
299
+ Subscribes `target` to this model, and sends initial data.
300
+ `target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
301
+
302
+ ### [start](start.md) · function
303
+
304
+ Starts the Lowlander WebSocket server.
305
+
306
+ ### logLevel · constant
307
+
308
+ **Value:** `number`
309
+
310
+ ### [warpsocket](warpsocket.md) · class
311
+
312
+ ### StreamTypeBase · abstract class
313
+
314
+ [object Object],[object Object],[object Object]
315
+
316
+ **Type Parameters:**
317
+
318
+ - `T`
319
+
320
+ #### StreamTypeBase.fields · static property
321
+
322
+ **Type:** `{ [key: string]: number | true; }`
323
+
324
+ #### StreamTypeBase.id · static property
325
+
326
+ **Type:** `number`
327
+
328
+ #### streamTypeBase.toString · method
329
+
330
+ **Signature:** `() => string`
331
+
332
+ ### [ServerProxy](ServerProxy.md) · class
333
+
334
+ Wraps a server-side API object to create a stateful, type-safe proxy accessible from clients.
335
+ Use for authentication, sessions, or any stateful context that persists across RPC calls.
336
+
337
+ #### serverProxy.toString · method
338
+
339
+ **Signature:** `() => string`
340
+
341
+ ### [Socket](Socket.md) · class
342
+
343
+ Server-side socket for pushing data to a client. Server functions with `Socket<T>` parameters
344
+ receive client callbacks on the client side.
345
+
346
+ #### [socket.send](Socket_send.md) · method
347
+
348
+ Sends data to the client.
349
+
350
+ #### [socket.subscribe](Socket_subscribe.md) · method
351
+
352
+ #### socket.toString · method
353
+
354
+ **Signature:** `() => string`
355
+
356
+ #### socket.[Symbol.for('nodejs.util.inspect.custom')] · method
357
+
358
+ **Signature:** `() => string`
359
+
360
+ ## Client API Reference
361
+
362
+ The following is auto-generated from `client/client.ts`:
363
+
364
+ ### setLogLevel · function
365
+
366
+ Set to 0-3 for increasing verbosity.
367
+
368
+ **Signature:** `(level: number) => void`
369
+
370
+ **Parameters:**
371
+
372
+ - `level: number`
373
+
374
+ ### ClientProxyObject · type
375
+
376
+ Transforms server-side API objects to client-side proxy objects with type-safe RPC methods.
377
+
378
+ **Type:** `{
379
+ [K in keyof T]: ClientProxyFunction<T[K]>
380
+ }`
381
+
382
+ ### [Connection](Connection.md) · class
383
+
384
+ WebSocket connection to a Lowlander server with type-safe RPC, automatic reconnection,
385
+ and reactive updates.
386
+
387
+ #### connection.ws · property
388
+
389
+ **Type:** `WebSocket`
390
+
391
+ #### connection.activeRequests · property
392
+
393
+ **Type:** `Map<number, ActiveRequest>`
394
+
395
+ #### connection.requestCounter · property
396
+
397
+ **Type:** `number`
398
+
399
+ #### connection.reconnectAttempts · property
400
+
401
+ **Type:** `number`
402
+
403
+ #### connection.onlineProxy · property
404
+
405
+ **Type:** `ValueRef<boolean>`
406
+
407
+ #### connection.api · property
408
+
409
+ Type-safe proxy to the server-side API. Methods return `PromiseProxy` objects
410
+ that work reactively in Aberdeen scopes. `ServerProxy` returns include a
411
+ `.serverProxy` property for accessing stateful server APIs.
412
+
413
+ **Type:** `ClientProxyObject<T>`
414
+
415
+ #### connection.isOnline · method
416
+
417
+ Returns the current connection status. Reactive in Aberdeen scopes.
418
+
419
+ **Signature:** `() => boolean`
420
+
421
+ #### connection.connect · method
422
+
423
+ **Signature:** `() => void`
424
+
425
+ #### connection.reconnect · method
426
+
427
+ **Signature:** `() => void`
428
+
429
+ #### [connection.pruneCommitIds](Connection_pruneCommitIds.md) · method
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
@@ -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,11 @@
1
+ #### socket.send · method
2
+
3
+ Sends data to the client.
4
+
5
+ **Signature:** `(data: T) => number`
6
+
7
+ **Parameters:**
8
+
9
+ - `data: T` - - Data to send (automatically serialized)
10
+
11
+ **Returns:** `true` if sent, `false` if socket is closed
@@ -0,0 +1,8 @@
1
+ #### socket.subscribe · method
2
+
3
+ **Signature:** `(channel: Uint8Array<ArrayBufferLike>, delta?: number) => void`
4
+
5
+ **Parameters:**
6
+
7
+ - `channel: Uint8Array`
8
+ - `delta: any` (optional)
@@ -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
+ ```
@@ -0,0 +1,3 @@
1
+ ### warpsocket · class
2
+
3
+ **Type:** `typeof import("/var/home/frank/projects/warpsocket/dist/src/index", { with: { "resolution-mode": "import" } })`
package/AGENTS.md DELETED
@@ -1,2 +0,0 @@
1
- - In all interactions and commit messages, be extremely concise and sacrifice grammar for the sake of concision.
2
- - Before touching any frontend code (involving the `A` symbol) study the Aberdeen skill.
package/ROADMAP.md DELETED
@@ -1,13 +0,0 @@
1
- Semi-prioritized Roadmap
2
- ========================
3
-
4
- [ ] Integration with a component library
5
- [/] Integrate a WarpSocket state inspection tool?
6
- [ ] A simple built-in Sentry-like error logging system
7
- [ ] Model-streaming with edit permissions for specific fields
8
- [ ] Integration with an authentication library
9
- [ ] Form builder based on Edinburgh schemas
10
- [x] Database migrations
11
- [ ] Streaming replication and backups
12
- [ ] Federation primitives
13
- [ ] Merge WarpSocket, Edinburgh and OLMDB into Lowlander, to allow model streaming and serialization to be done in Rust/Neon