lowlander 0.2.0 → 0.2.2

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 (48) hide show
  1. package/README.md +282 -37
  2. package/build/client/client.d.ts +153 -0
  3. package/build/client/client.js +317 -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 +40 -0
  12. package/build/examples/helloworld/server/api.d.ts.map +1 -0
  13. package/{examples/helloworld/server/api.ts → build/examples/helloworld/server/api.js} +58 -66
  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/package.json +15 -8
  34. package/server/server.ts +1 -0
  35. package/skill/SKILL.md +605 -0
  36. package/AGENTS.md +0 -2
  37. package/ROADMAP.md +0 -13
  38. package/bun.lock +0 -281
  39. package/examples/helloworld/client/js/admin.ts +0 -94
  40. package/examples/helloworld/package.json +0 -8
  41. package/tests/fake-warpsocket.ts +0 -452
  42. package/tests/helloworld.test.ts +0 -151
  43. package/tsconfig.client.json +0 -18
  44. package/tsconfig.json +0 -24
  45. package/tsconfig.server.json +0 -17
  46. package/tsconfig.test.json +0 -13
  47. /package/{examples → build/examples}/helloworld/client/assets/style.css +0 -0
  48. /package/{examples → build/examples}/helloworld/client/index.html +0 -0
@@ -1,110 +1,107 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var Person_1, MyModel_1;
1
8
  import * as E from "edinburgh";
2
- import { ServerProxy, createStreamType, Socket } from "lowlander/server";
9
+ import { ServerProxy, createStreamType } from "lowlander/server";
3
10
  import * as warpsocket from "warpsocket";
4
-
5
11
  export const getDebugState = warpsocket.getDebugState;
6
-
7
12
  // Simple RPC function example
8
- export function add(a: number, b: number): number {
13
+ export function add(a, b) {
9
14
  return a + b;
10
15
  }
11
-
12
16
  // Example of a stateful server-side API that's exposed via ServerProxy
13
17
  export class UserAPI {
14
- constructor(public userName: string) {}
15
-
16
- get user(): Person {
18
+ userName;
19
+ constructor(userName) {
20
+ this.userName = userName;
21
+ }
22
+ get user() {
17
23
  const result = Person.byName.get(this.userName);
18
- if (!result) throw new Error(`User '${this.userName}' not found`);
24
+ if (!result)
25
+ throw new Error(`User '${this.userName}' not found`);
19
26
  return result;
20
27
  }
21
-
22
28
  getBio() {
23
29
  return `${this.user.name} is ${this.user.age} years old and has ${this.user.friends.length} friend(s).`;
24
30
  }
25
-
26
- toggleFriend(friendName: string) {
27
- for(const [idx, val] of Object.entries(this.user.friends)) {
31
+ toggleFriend(friendName) {
32
+ for (const [idx, val] of Object.entries(this.user.friends)) {
28
33
  if (val.name === friendName) {
29
34
  this.user.friends.splice(Number(idx), 1);
30
35
  return true;
31
36
  }
32
37
  }
33
38
  const friend = Person.byName.get(friendName);
34
- if (!friend) return false;
39
+ if (!friend)
40
+ return false;
35
41
  this.user.friends.push(friend);
36
42
  return true;
37
43
  }
38
-
39
- // admin() {
40
- // if (this.user.name !== 'Frank') {
41
- // throw new Error('Access denied');
42
- // }
43
- // return new ServerProxy(admin);
44
- // }
45
44
  }
46
-
47
45
  // Authentication example - returns a ServerProxy with both a value and API object
48
- export async function authenticate(auth: string) {
46
+ export async function authenticate(auth) {
49
47
  await new Promise(resolve => setTimeout(resolve, 1000));
50
48
  const user = Person.byName.get(auth);
51
- if (!user) throw new Error('User not found');
49
+ if (!user)
50
+ throw new Error('User not found');
52
51
  // Client receives 'secret' as .value and UserAPI methods via .serverProxy
53
52
  return new ServerProxy(new UserAPI(auth), 'secret');
54
53
  }
55
-
56
-
57
54
  // Edinburgh model definitions
58
- @E.registerModel
59
- class Person extends E.Model<Person> {
60
- static byName = E.primary(Person, 'name');
55
+ let Person = class Person extends E.Model {
56
+ static { Person_1 = this; }
57
+ static byName = E.primary(Person_1, 'name');
61
58
  name = E.field(E.string);
62
59
  age = E.field(E.number);
63
- friends = E.field(E.array(E.link(Person)));
60
+ friends = E.field(E.array(E.link(Person_1)));
64
61
  password = E.field(E.string);
65
- }
66
-
67
- @E.registerModel
68
- class MyModel extends E.Model<MyModel> {
62
+ };
63
+ Person = Person_1 = __decorate([
64
+ E.registerModel
65
+ ], Person);
66
+ let MyModel = class MyModel extends E.Model {
67
+ static { MyModel_1 = this; }
69
68
  id = E.field(E.identifier);
70
69
  name = E.field(E.string);
71
- next = E.field(E.opt(E.link(MyModel)));
70
+ next = E.field(E.opt(E.link(MyModel_1)));
72
71
  owner = E.field(E.link(Person));
73
72
  createdAt = E.field(E.dateTime);
74
-
75
- static byId = E.primary(MyModel, 'id');
76
- static byName = E.unique(MyModel, 'name');
77
- }
78
-
79
- let ids: {p1: string, p2: string, m1: string, m2: string};
80
- export async function resetTestData(deleteEverything: boolean) {
73
+ static byId = E.primary(MyModel_1, 'id');
74
+ static byName = E.unique(MyModel_1, 'name');
75
+ };
76
+ MyModel = MyModel_1 = __decorate([
77
+ E.registerModel
78
+ ], MyModel);
79
+ let ids;
80
+ export async function resetTestData(deleteEverything) {
81
81
  if (deleteEverything) {
82
82
  await E.deleteEverything();
83
83
  }
84
-
85
84
  // Initialize some test data. Even if we are already running in a transaction, we need to do this
86
85
  // in a new (nested) transaction, as deleteEverything will have done *its* work in separate transactions
87
86
  // as well, and we need access to its results.
88
87
  await E.transact(() => {
89
- let p1 = Person.byName.get('Frank') || new Person({name: 'Frank', age: 45, password: 'secret'});
90
- let p2 = Person.byName.get('Alice') || new Person({name: 'Alice', age: 25, password: 'hidden', friends: [p1]});
91
- let p3 = Person.byName.get('Bob') || new Person({name: 'Bob', age: 65, password: 'himom', friends: [p1, p2]});
92
- if (p1.getState() === "created") p1.friends = [p2, p3];
93
- let m1 = MyModel.byName.get('Test') || new MyModel({name: 'Test', owner: p1});
94
- let m2 = MyModel.byName.get('Another') || new MyModel({name: 'Another', owner: p2, next: m1});
95
- ids = {p1: p1.name, p2: p2.name, m1: m1.id, m2: m2.id};
88
+ let p1 = Person.byName.get('Frank') || new Person({ name: 'Frank', age: 45, password: 'secret' });
89
+ let p2 = Person.byName.get('Alice') || new Person({ name: 'Alice', age: 25, password: 'hidden', friends: [p1] });
90
+ let p3 = Person.byName.get('Bob') || new Person({ name: 'Bob', age: 65, password: 'himom', friends: [p1, p2] });
91
+ if (p1.getState() === "created")
92
+ p1.friends = [p2, p3];
93
+ let m1 = MyModel.byName.get('Test') || new MyModel({ name: 'Test', owner: p1 });
94
+ let m2 = MyModel.byName.get('Another') || new MyModel({ name: 'Another', owner: p2, next: m1 });
95
+ ids = { p1: p1.name, p2: p2.name, m1: m1.id, m2: m2.id };
96
96
  });
97
97
  }
98
98
  resetTestData(false);
99
-
100
99
  await E.transact(() => {
101
100
  E.dump();
102
- for(const p of Person.findAll()) {
101
+ for (const p of Person.findAll()) {
103
102
  console.log('Person:', p.name, 'age', p.age, 'friends', p.friends.map(f => f.name).join(','), 'password', p.password);
104
103
  }
105
104
  });
106
-
107
-
108
105
  // Create a stream type that specifies which fields to send to clients
109
106
  // Note: password is excluded for security, and we include nested linked model fields
110
107
  const MyStream = createStreamType(MyModel, {
@@ -119,29 +116,24 @@ const MyStream = createStreamType(MyModel, {
119
116
  }
120
117
  }
121
118
  });
122
-
123
119
  // Example of model streaming - returns a reactive proxy that auto-updates on changes
124
120
  export function streamModel() {
125
- const m1 = MyModel.byId.get(ids.m1)!;
121
+ const m1 = MyModel.byId.get(ids.m1);
126
122
  return new MyStream(m1);
127
123
  }
128
-
129
- export async function incrOwnerAge(delta: number) {
130
- const m1 = MyModel.byId.get(ids.m1)!;
124
+ export async function incrOwnerAge(delta) {
125
+ const m1 = MyModel.byId.get(ids.m1);
131
126
  const current = m1.owner.age;
132
127
  await new Promise(resolve => setTimeout(resolve, 50));
133
128
  m1.owner.age = current + delta;
134
129
  }
135
-
136
- export function setOwnerAge(age: number) {
137
- const m1 = MyModel.byId.get(ids.m1)!;
130
+ export function setOwnerAge(age) {
131
+ const m1 = MyModel.byId.get(ids.m1);
138
132
  m1.owner.age = age;
139
133
  }
140
-
141
-
142
134
  // Example of server-push streaming via Socket callback
143
135
  // Client provides a callback; server pushes data by calling socket.send()
144
- export function streamSomething(socket: Socket<number>) {
136
+ export function streamSomething(socket) {
145
137
  let interval = setInterval(() => {
146
138
  console.log('Sending ping');
147
139
  // socket.send() returns false when client disconnects
@@ -151,4 +143,4 @@ export function streamSomething(socket: Socket<number>) {
151
143
  }
152
144
  }, 2000);
153
145
  }
154
-
146
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../../../examples/helloworld/server/api.ts"],"names":[],"mappings":";;;;;;;AAAA,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAU,MAAM,kBAAkB,CAAC;AACzE,OAAO,KAAK,UAAU,MAAM,YAAY,CAAC;AAEzC,MAAM,CAAC,MAAM,aAAa,GAAG,UAAU,CAAC,aAAa,CAAC;AAEtD,8BAA8B;AAC9B,MAAM,UAAU,GAAG,CAAC,CAAS,EAAE,CAAS;IACpC,OAAO,CAAC,GAAG,CAAC,CAAC;AACjB,CAAC;AAED,uEAAuE;AACvE,MAAM,OAAO,OAAO;IACG;IAAnB,YAAmB,QAAgB;QAAhB,aAAQ,GAAR,QAAQ,CAAQ;IAAG,CAAC;IAEvC,IAAI,IAAI;QACJ,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QAChD,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,KAAK,CAAC,SAAS,IAAI,CAAC,QAAQ,aAAa,CAAC,CAAC;QAClE,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,MAAM;QACF,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,OAAO,IAAI,CAAC,IAAI,CAAC,GAAG,sBAAsB,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,aAAa,CAAC;IAC5G,CAAC;IAED,YAAY,CAAC,UAAkB;QAC3B,KAAI,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;YACxD,IAAI,GAAG,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;gBAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;gBACzC,OAAO,IAAI,CAAC;YAChB,CAAC;QACL,CAAC;QACD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC7C,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAC;QAC1B,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/B,OAAO,IAAI,CAAC;IAChB,CAAC;CAQJ;AAED,kFAAkF;AAClF,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAY;IAC3C,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACrC,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC7C,0EAA0E;IAC1E,OAAO,IAAI,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC;AACxD,CAAC;AAGD,8BAA8B;AAE9B,IAAM,MAAM,GAAZ,MAAM,MAAO,SAAQ,CAAC,CAAC,KAAa;;IAChC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,OAAO,CAAC,QAAM,EAAE,MAAM,CAAC,CAAC;IAC1C,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACzB,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,QAAM,CAAC,CAAC,CAAC,CAAC;IAC3C,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;;AAL3B,MAAM;IADX,CAAC,CAAC,aAAa;GACV,MAAM,CAMX;AAGD,IAAM,OAAO,GAAb,MAAM,OAAQ,SAAQ,CAAC,CAAC,KAAc;;IAClC,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;IAC3B,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACzB,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,SAAO,CAAC,CAAC,CAAC,CAAC;IACvC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC;IAChC,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;IAEhC,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,SAAO,EAAE,IAAI,CAAC,CAAC;IACvC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,SAAO,EAAE,MAAM,CAAC,CAAC;;AARxC,OAAO;IADZ,CAAC,CAAC,aAAa;GACV,OAAO,CASZ;AAED,IAAI,GAAqD,CAAC;AAC1D,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,gBAAyB;IACzD,IAAI,gBAAgB,EAAE,CAAC;QACnB,MAAM,CAAC,CAAC,gBAAgB,EAAE,CAAC;IAC/B,CAAC;IAED,iGAAiG;IACjG,wGAAwG;IACxG,8CAA8C;IAC9C,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE;QAClB,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAC,CAAC,CAAC;QAChG,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,EAAC,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC,EAAC,CAAC,CAAC;QAC/G,IAAI,EAAE,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,IAAI,MAAM,CAAC,EAAC,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAC,CAAC,CAAC;QAC9G,IAAI,EAAE,CAAC,QAAQ,EAAE,KAAK,SAAS;YAAE,EAAE,CAAC,OAAO,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;QACvD,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,OAAO,CAAC,EAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAC,CAAC,CAAC;QAC9E,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,IAAI,OAAO,CAAC,EAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAC,CAAC,CAAC;QAC9F,GAAG,GAAG,EAAC,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,EAAE,EAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACP,CAAC;AACD,aAAa,CAAC,KAAK,CAAC,CAAC;AAErB,MAAM,CAAC,CAAC,QAAQ,CAAC,GAAG,EAAE;IAClB,CAAC,CAAC,IAAI,EAAE,CAAC;IACT,KAAI,MAAM,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;QAC9B,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC;IAC1H,CAAC;AACL,CAAC,CAAC,CAAC;AAGH,sEAAsE;AACtE,qFAAqF;AACrF,MAAM,QAAQ,GAAG,gBAAgB,CAAC,OAAO,EAAE;IACvC,IAAI,EAAE,IAAI;IACV,SAAS,EAAE,IAAI;IACf,KAAK,EAAE;QACH,IAAI,EAAE,IAAI;QACV,GAAG,EAAE,IAAI;QACT,OAAO,EAAE;YACL,IAAI,EAAE,IAAI;YACV,GAAG,EAAE,IAAI;SACZ;KACJ;CACJ,CAAC,CAAC;AAEH,qFAAqF;AACrF,MAAM,UAAU,WAAW;IACvB,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACrC,OAAO,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa;IAC5C,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACrC,MAAM,OAAO,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;IAC7B,MAAM,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;IACtD,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,OAAO,GAAG,KAAK,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACnC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAE,CAAC;IACrC,EAAE,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC;AACvB,CAAC;AAGD,uDAAuD;AACvD,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,MAAsB;IAClD,IAAI,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5B,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAC5B,sDAAsD;QACtD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;YAClC,aAAa,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IACL,CAAC,EAAE,IAAI,CAAC,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ import './api';
2
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../../../examples/helloworld/server/main.ts"],"names":[],"mappings":"AA0BA,OAAO,OAAO,CAAC"}
@@ -1,27 +1,22 @@
1
1
  // This example is Bun-only, as it conveniently transpiles client-files on the fly.
2
2
  // For Node.js, you'd have to add a build step for client-files, and serve them as
3
3
  // static files (e.g. using express.static).
4
-
5
4
  import { fileURLToPath } from 'url';
6
5
  import { dirname, resolve } from 'path';
7
6
  import * as lowlander from "lowlander/server";
8
7
  import index from "../client/index.html";
9
-
10
8
  const apiFile = resolve(dirname(fileURLToPath(import.meta.url)), 'api.js');
11
-
12
9
  // Start the WebSocket server with the given API handler
13
- lowlander.start(apiFile, {threads: 1});
14
-
10
+ lowlander.start(apiFile, { threads: 1 });
15
11
  // Serve index.html on /
16
12
  Bun.serve({
17
13
  port: process.env.PORT || 3000,
18
14
  routes: {
19
15
  '/': index,
20
- },
16
+ },
21
17
  development: true,
22
18
  });
23
-
24
-
25
19
  // We're only doing this import here such that Bun knows to reload when api.ts changes
26
20
  // when in --watch mode.
27
21
  import './api';
22
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../../../examples/helloworld/server/main.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,kFAAkF;AAClF,4CAA4C;AAE5C,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACxC,OAAO,KAAK,SAAS,MAAM,kBAAkB,CAAC;AAC9C,OAAO,KAAK,MAAM,sBAAsB,CAAC;AAEzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;AAE3E,wDAAwD;AACxD,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,EAAC,OAAO,EAAE,CAAC,EAAC,CAAC,CAAC;AAEvC,wBAAwB;AACxB,GAAG,CAAC,KAAK,CAAC;IACN,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI;IAC9B,MAAM,EAAE;QACJ,GAAG,EAAE,KAAK;KACb;IACD,WAAW,EAAE,IAAI;CACpB,CAAC,CAAC;AAGH,sFAAsF;AACtF,wBAAwB;AACxB,OAAO,OAAO,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare const SERVER_MESSAGES: {
2
+ error: string;
3
+ response: string;
4
+ response_proxy: string;
5
+ response_model: string;
6
+ model_data: string;
7
+ };
8
+ export declare const CLIENT_MESSAGES: {
9
+ call: number;
10
+ cancel: number;
11
+ };
12
+ //# sourceMappingURL=protocol.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../server/protocol.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,eAAe;;;;;;CAM3B,CAAC;AAOF,eAAO,MAAM,eAAe;;;CAG3B,CAAC"}
@@ -0,0 +1,19 @@
1
+ // requestId + callbackIndex + callback arguments
2
+ // or
3
+ // requestId + one of these types:
4
+ export const SERVER_MESSAGES = {
5
+ error: 'e', // followed by errorMessage
6
+ response: 'r', // followed by result + virtualSocketIds
7
+ response_proxy: 'p', // followed by result + virtualSocketIds (like above, but indicate that a ServerProxy has been created for this request)
8
+ response_model: 'm', // followed by virtualSocketIds + dbKey
9
+ model_data: 'd', // followed by dbKey + commitId + delta
10
+ };
11
+ // The virtualSocketIds in a response or response_model is stored by the client and must
12
+ // be provided to 'cancel'.
13
+ // dbKey is a stochastically unique identifier for a subset of a model instance, defined as primaryKeyHash + steamType.id
14
+ // requestId + one of these types:
15
+ export const CLIENT_MESSAGES = {
16
+ call: 1, // followed by proxyId/undefined + methodName + params + ...
17
+ cancel: 2, // followed by cancelRequestId + virtualSocketIds/undefined
18
+ };
19
+ //# sourceMappingURL=protocol.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"protocol.js","sourceRoot":"","sources":["../../server/protocol.ts"],"names":[],"mappings":"AAAA,iDAAiD;AAEjD,KAAK;AAEL,kCAAkC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG;IAC3B,KAAK,EAAE,GAAG,EAAE,2BAA2B;IACvC,QAAQ,EAAE,GAAG,EAAE,wCAAwC;IACvD,cAAc,EAAE,GAAG,EAAE,wHAAwH;IAC7I,cAAc,EAAE,GAAG,EAAE,uCAAuC;IAC5D,UAAU,EAAE,GAAG,EAAE,uCAAuC;CAC3D,CAAC;AAEF,wFAAwF;AACxF,2BAA2B;AAC3B,yHAAyH;AAEzH,kCAAkC;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG;IAC3B,IAAI,EAAE,CAAC,EAAE,4DAA4D;IACrE,MAAM,EAAE,CAAC,EAAE,2DAA2D;CACzE,CAAC"}
@@ -0,0 +1,191 @@
1
+ import * as E from "edinburgh";
2
+ import * as realWarpsocket from 'warpsocket';
3
+ export declare const logLevel: number;
4
+ /** @internal Warpsocket implementation; swapped to FakeWarpSocket in test mode. */
5
+ export declare let warpsocket: typeof realWarpsocket;
6
+ /** @internal Type alias for Edinburgh model classes */
7
+ type ModelClass = typeof E.Model<unknown>;
8
+ /**
9
+ * Base class for stream types created by {@link createStreamType}.
10
+ * @typeParam T - The projected model type
11
+ * @internal
12
+ */
13
+ export declare abstract class StreamTypeBase<T> {
14
+ _instance: E.Model<any> & T;
15
+ /** @internal */
16
+ static fields: {
17
+ [key: string]: true | number;
18
+ };
19
+ /** @internal */
20
+ static id: number;
21
+ /** @internal */
22
+ constructor(_instance: E.Model<any> & T);
23
+ toString(): string;
24
+ }
25
+ /**
26
+ * Type-safe selector for specifying which model fields to stream to clients.
27
+ * Use `true` to include a field, or an object to select nested fields in linked models.
28
+ *
29
+ * @typeParam T - The model type
30
+ */
31
+ type FieldSelection<T> = T extends ReadonlyArray<infer U> ? true | FieldSelection<U> : T extends Array<infer U> ? true | FieldSelection<U> : T extends object ? true | {
32
+ [K in keyof T]?: FieldSelection<T[K]>;
33
+ } : true;
34
+ /**
35
+ * Validates field selection compatibility at compile time.
36
+ * @internal
37
+ */
38
+ type ValidateSelection<T, S> = T extends ReadonlyArray<infer U> ? S extends true ? true : ValidateSelection<U, S> : T extends Array<infer U> ? S extends true ? true : ValidateSelection<U, S> : T extends object ? S extends true ? true : S extends object ? {
39
+ [K in keyof S]-?: K extends keyof T ? ValidateSelection<T[K], S[K]> : never;
40
+ } : never : S extends true ? true : never;
41
+ /**
42
+ * Computes the resulting type after applying a field selection.
43
+ * @internal
44
+ */
45
+ type Project<T, S> = S extends true ? T : T extends ReadonlyArray<infer U> ? ReadonlyArray<Project<U, S>> : T extends Array<infer U> ? Array<Project<U, S>> : T extends object ? {
46
+ [K in Extract<keyof S, keyof T>]: Project<T[K], S[K & keyof T]>;
47
+ } : T;
48
+ /**
49
+ * Creates a stream type for reactive model streaming to clients with automatic updates.
50
+ *
51
+ * Specify which fields to include; when they change, updates are pushed to subscribed clients.
52
+ * Supports nested linked models and type-safe field selection.
53
+ *
54
+ * @typeParam T - The model type
55
+ * @typeParam S - The field selection
56
+ *
57
+ * @param Model - The Edinburgh model class
58
+ * @param selection - Field selection: `true` for simple fields, nested object for linked models
59
+ * @returns Stream type class to instantiate in API functions
60
+ *
61
+ * @example
62
+ * ```ts
63
+ * @registerModel
64
+ * class Person extends Model {
65
+ * name = field(string);
66
+ * age = field(number);
67
+ * password = field(string);
68
+ * friends = field(array(link(Person)));
69
+ * }
70
+ *
71
+ * // Exclude password, include friends' names
72
+ * const PersonStream = createStreamType(Person, {
73
+ * name: true,
74
+ * age: true,
75
+ * friends: { name: true }
76
+ * });
77
+ *
78
+ * export function streamPerson() {
79
+ * const person = Person.byName.get('Alice')!;
80
+ * return new PersonStream(person);
81
+ * }
82
+ * ```
83
+ */
84
+ export declare function createStreamType<T, S extends FieldSelection<T>>(Model: ModelClass & (new (...args: any[]) => T), selection: S & ValidateSelection<T, S>): {
85
+ new (_instance: E.Model<any> & Project<T, S>): {
86
+ _instance: E.Model<any> & Project<T, S>;
87
+ toString(): string;
88
+ };
89
+ fields: Record<string, number | true>;
90
+ id: number;
91
+ };
92
+ /**
93
+ * Sends (updated) data for `model` to `target`.
94
+ * `target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
95
+ */
96
+ export declare function sendModel(target: Uint8Array | number | number[], model: E.Model<any>, commitId: number, StreamType: typeof StreamTypeBase<any>, changed?: E.Change): void;
97
+ /**
98
+ * Subscribes `target` to this model, and sends initial data.
99
+ * `target` is a virtual socket with a requestId+'d' user prefix, or a channel that subscribes such virtual sockets.
100
+ */
101
+ export declare function pushModel(target: number | Uint8Array | number[], model: E.Model<any>, commitId: number, SubStreamType: typeof StreamTypeBase<any>, delta: number): void;
102
+ /**
103
+ * Wraps a server-side API object to create a stateful, type-safe proxy accessible from clients.
104
+ * Use for authentication, sessions, or any stateful context that persists across RPC calls.
105
+ *
106
+ * @typeParam API - The server-side API object type
107
+ * @typeParam RETURN - The value type returned to the client
108
+ *
109
+ * @example
110
+ * ```ts
111
+ * export class UserAPI {
112
+ * constructor(public user: User) {}
113
+ * getSecret() { return this.user.secret; }
114
+ * }
115
+ *
116
+ * export async function authenticate(token: string) {
117
+ * const user = await validateToken(token);
118
+ * return new ServerProxy(new UserAPI(user), user.name);
119
+ * }
120
+ *
121
+ * // Client: auth.value is user name, auth.serverProxy.getSecret() calls UserAPI method
122
+ * ```
123
+ */
124
+ export declare class ServerProxy<API extends object, RETURN> {
125
+ api: API;
126
+ value?: RETURN | undefined;
127
+ /**
128
+ * @param api - Server-side API object exposed to the client
129
+ * @param value - Value returned immediately to the client
130
+ */
131
+ constructor(api: API, value?: RETURN | undefined);
132
+ toString(): string;
133
+ }
134
+ /**
135
+ * Server-side socket for pushing data to a client. Server functions with `Socket<T>` parameters
136
+ * receive client callbacks on the client side.
137
+ *
138
+ * @typeParam T - Data type sent through the socket
139
+ *
140
+ * @example
141
+ * ```ts
142
+ * // Server
143
+ * export function streamNumbers(socket: Socket<number>) {
144
+ * setInterval(() => {
145
+ * if (!socket.send(Math.random())) clearInterval(interval);
146
+ * }, 1000);
147
+ * }
148
+ *
149
+ * // Client
150
+ * api.streamNumbers(num => console.log(num));
151
+ * ```
152
+ */
153
+ export declare class Socket<T> {
154
+ virtualSocketId: number;
155
+ /** @internal */
156
+ constructor(virtualSocketId: number);
157
+ /**
158
+ * Sends data to the client.
159
+ * @param data - Data to send (automatically serialized)
160
+ * @returns `true` if sent, `false` if socket is closed
161
+ */
162
+ send(data: T): number;
163
+ /** @internal */
164
+ subscribe(channel: Uint8Array, delta?: number): void;
165
+ toString(): string;
166
+ }
167
+ /**
168
+ * Starts the Lowlander WebSocket server.
169
+ *
170
+ * @param mainApiFile - Absolute path to the compiled API file exporting server functions
171
+ * @param opts.bind - Address and port (default: '0.0.0.0:8080')
172
+ * @param opts.threads - Worker thread count (default: auto)
173
+ * @param opts.injectWarpSocket - For testing: inject a custom WarpSocket implementation (e.g. FakeWarpSocket)
174
+ *
175
+ * @example
176
+ * ```ts
177
+ * import { start } from 'lowlander/server';
178
+ * import { fileURLToPath } from 'url';
179
+ * import { resolve, dirname } from 'path';
180
+ *
181
+ * const API_FILE = resolve(dirname(fileURLToPath(import.meta.url)), 'api.js');
182
+ * start(API_FILE, { bind: '0.0.0.0:8080' });
183
+ * ```
184
+ */
185
+ export declare function start(mainApiFile: string, opts?: {
186
+ bind?: string;
187
+ threads?: number;
188
+ injectWarpSocket?: typeof realWarpsocket;
189
+ }): Promise<void>;
190
+ export {};
191
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../server/server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAE/B,OAAO,KAAK,cAAc,MAAM,YAAY,CAAC;AAO7C,eAAO,MAAM,QAAQ,QAAwD,CAAC;AAE9E,mFAAmF;AACnF,eAAO,IAAI,UAAU,EAAE,OAAO,cAA+B,CAAC;AAa9D,uDAAuD;AACvD,KAAK,UAAU,GAAG,OAAO,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;AAE1C;;;;GAIG;AACH,8BAAsB,cAAc,CAAC,CAAC;IAMf,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;IAL9C,gBAAgB;IAChB,MAAM,CAAC,MAAM,EAAE;QAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAC,MAAM,CAAA;KAAE,CAAC;IAC9C,gBAAgB;IAChB,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC;IAClB,gBAAgB;gBACG,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;IAE9C,QAAQ;CAIX;AAED;;;;;GAKG;AACH,KAAK,cAAc,CAAC,CAAC,IACnB,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAC5B,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,GACxB,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACtB,IAAI,GAAG,cAAc,CAAC,CAAC,CAAC,GACxB,CAAC,SAAS,MAAM,GACd,IAAI,GAAG;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,GAChD,IAAI,CAAC;AAEf;;;GAGG;AACH,KAAK,iBAAiB,CAAC,CAAC,EAAE,CAAC,IACzB,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAC5B,CAAC,SAAS,IAAI,GAAG,IAAI,GAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC/C,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACtB,CAAC,SAAS,IAAI,GAAG,IAAI,GAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC,GAC/C,CAAC,SAAS,MAAM,GACd,CAAC,SAAS,IAAI,GACZ,IAAI,GACJ,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAAG,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CAAE,GAC/E,KAAK,GACT,CAAC,SAAS,IAAI,GAAG,IAAI,GAAG,KAAK,CAAC;AAExC;;;GAGG;AACH,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,IACf,CAAC,SAAS,IAAI,GACV,CAAC,GACD,CAAC,SAAS,aAAa,CAAC,MAAM,CAAC,CAAC,GAC9B,aAAa,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAC5B,CAAC,SAAS,KAAK,CAAC,MAAM,CAAC,CAAC,GACtB,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GACpB,CAAC,SAAS,MAAM,GACd;KAAG,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;CAAE,GACnE,CAAC,CAAC;AAiCd;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,cAAc,CAAC,CAAC,CAAC,EAC7D,KAAK,EAAE,UAAU,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC,EAC/C,SAAS,EAAE,CAAC,GAAG,iBAAiB,CAAC,CAAC,EAAE,CAAC,CAAC;;;;;;;EA6BvC;AAiED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,OAAO,cAAc,CAAC,GAAG,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,QAkDlK;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,cAAc,CAAC,GAAG,CAAC,EAAE,KAAK,EAAE,MAAM,QAOhK;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,qBAAa,WAAW,CAAC,GAAG,SAAS,MAAM,EAAE,MAAM;IAK5B,GAAG,EAAE,GAAG;IAAS,KAAK,CAAC,EAAE,MAAM;IAJlD;;;OAGG;gBACgB,GAAG,EAAE,GAAG,EAAS,KAAK,CAAC,EAAE,MAAM,YAAA;IAClD,QAAQ;CAGX;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,MAAM,CAAC,CAAC;IAEE,eAAe,EAAE,MAAM;IAD1C,gBAAgB;gBACG,eAAe,EAAE,MAAM;IAE1C;;;;OAIG;IACH,IAAI,CAAC,IAAI,EAAE,CAAC;IAKZ,gBAAgB;IAChB,SAAS,CAAC,OAAO,EAAE,UAAU,EAAE,KAAK,SAAE;IAOtC,QAAQ;CAOX;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,KAAK,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,GAAE;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,cAAc,CAAA;CAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAUtJ"}