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
@@ -0,0 +1,320 @@
1
+ import A from 'aberdeen';
2
+ import DataPack from 'edinburgh/datapack';
3
+ import { SERVER_MESSAGES, CLIENT_MESSAGES } from '../server/protocol.js';
4
+ let logLevel = 0;
5
+ /** Set to 0-3 for increasing verbosity. */
6
+ export function setLogLevel(level) {
7
+ logLevel = level;
8
+ }
9
+ // Sentinel key in commitIds entries: on initial creation, only DEFAULT_COMMIT is set
10
+ // (applies to all keys). Per-key entries are added on subsequent updates and take
11
+ // precedence. On deletion, DEFAULT_COMMIT guards against stale re-creates.
12
+ const DEFAULT_COMMIT = Symbol();
13
+ /**
14
+ * WebSocket connection to a Lowlander server with type-safe RPC, automatic reconnection,
15
+ * and reactive updates.
16
+ *
17
+ * @typeParam T - The server-side API type (import from your server API file)
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * import type * as API from './server/api.js';
22
+ * const conn = new Connection<typeof API>('ws://localhost:8080/');
23
+ *
24
+ * // Simple RPC - returns PromiseProxy
25
+ * const sum = conn.api.add(1, 2);
26
+ *
27
+ * // Server proxy for stateful APIs
28
+ * const auth = conn.api.authenticate('token');
29
+ * const secret = auth.serverProxy.getSecret();
30
+ *
31
+ * // Streaming with callbacks
32
+ * conn.api.streamData(data => console.log(data));
33
+ *
34
+ * // Use within Aberdeen reactive scopes
35
+ * $(() => {
36
+ * dump(conn.isOnline());
37
+ * dump(sum);
38
+ * });
39
+ * ```
40
+ */
41
+ export class Connection {
42
+ url;
43
+ ws;
44
+ activeRequests = new Map();
45
+ requestCounter = 0;
46
+ reconnectAttempts = 0;
47
+ /** @internal */
48
+ _proxyCounter = 0;
49
+ onlineProxy = A.proxy(false);
50
+ /**
51
+ * Type-safe proxy to the server-side API. Methods return `PromiseProxy` objects
52
+ * that work reactively in Aberdeen scopes. `ServerProxy` returns include a
53
+ * `.serverProxy` property for accessing stateful server APIs.
54
+ */
55
+ api;
56
+ /**
57
+ * @param url - WebSocket URL (e.g., 'ws://localhost:8080/'), or a fake WebSocket object for testing
58
+ */
59
+ constructor(url) {
60
+ this.url = url;
61
+ this.api = new Proxy({ connection: this, requestId: undefined }, proxyHandlers);
62
+ this.connect();
63
+ }
64
+ /**
65
+ * Returns the current connection status. Reactive in Aberdeen scopes.
66
+ */
67
+ isOnline() { return this.onlineProxy.value; }
68
+ connect() {
69
+ const ws = this.ws = typeof this.url === 'string'
70
+ ? new WebSocket(this.url)
71
+ : this.url();
72
+ ws.binaryType = "arraybuffer";
73
+ if (logLevel >= 1)
74
+ console.log(`[lowlander] Connecting to WebSocket at ${typeof this.url === 'string' ? this.url : '[custom WebSocket]'}`);
75
+ ws.onopen = () => {
76
+ if (ws !== this.ws)
77
+ return; // No longer the current connection
78
+ if (logLevel >= 1)
79
+ console.log('[lowlander] WebSocket connected');
80
+ this.onlineProxy.value = true;
81
+ this.reconnectAttempts = 0;
82
+ for (const request of this.activeRequests.values()) {
83
+ request.resultProxy.busy = true;
84
+ this.ws.send(request.requestBuffer);
85
+ }
86
+ };
87
+ ws.onclose = () => {
88
+ if (ws !== this.ws)
89
+ return; // No longer the current connection
90
+ if (logLevel >= 1)
91
+ console.log('[lowlander] WebSocket disconnected');
92
+ this.reconnect();
93
+ };
94
+ ws.onerror = (error) => {
95
+ if (ws !== this.ws)
96
+ return; // No longer the current connection
97
+ console.error('WebSocket error:', error);
98
+ this.reconnect();
99
+ };
100
+ ws.onmessage = (event) => {
101
+ if (ws !== this.ws)
102
+ return; // No longer the current connection
103
+ const pack = new DataPack(new Uint8Array(event.data));
104
+ // console.log(`onmessage: ${pack}`);
105
+ const requestId = pack.readPositiveInt();
106
+ const request = this.activeRequests.get(requestId);
107
+ if (!request)
108
+ return; // Raced
109
+ const result = A.unproxy(request.resultProxy);
110
+ const type = pack.read();
111
+ if (typeof type === 'number') {
112
+ // It's a callback invocation
113
+ const callback = request.callbacks?.[type];
114
+ const args = [];
115
+ while (pack.readAvailable()) {
116
+ args.push(pack.read());
117
+ }
118
+ callback(...args);
119
+ return;
120
+ }
121
+ // This packet type does not represent the result for a request
122
+ if (type === SERVER_MESSAGES.model_data) {
123
+ request.database ||= new Map();
124
+ request.commitIds ||= new Map();
125
+ const dbKeyHash = pack.readNumber();
126
+ const commitId = pack.readNumber();
127
+ const delta = pack.read({ model: function (linkHash) {
128
+ const linkedModel = request.database.get(linkHash);
129
+ if (!linkedModel)
130
+ console.error('Unknown linked model hash ' + linkHash);
131
+ return linkedModel;
132
+ } });
133
+ if (logLevel >= 3)
134
+ console.log('[lowlander] incoming model_data', requestId, dbKeyHash, commitId, delta);
135
+ // Schedule cleanup: after 15s, all out-of-order messages for this commitId
136
+ // must have arrived, so we can prune tracking entries at or below it.
137
+ setTimeout(() => this.pruneCommitIds(request, commitId), 15000);
138
+ let prevCommitIds = request.commitIds.get(dbKeyHash);
139
+ if (!delta) {
140
+ // Stale delete: some key was already updated past this commitId
141
+ if (prevCommitIds && commitId < Math.max(...prevCommitIds.values()))
142
+ return;
143
+ request.database.delete(dbKeyHash);
144
+ // Record delete's commitId so stale creates arriving later are rejected
145
+ request.commitIds.set(dbKeyHash, new Map([[DEFAULT_COMMIT, commitId]]));
146
+ return;
147
+ }
148
+ let org = request.database.get(dbKeyHash);
149
+ if (org) {
150
+ // Update existing object
151
+ if (!prevCommitIds) {
152
+ prevCommitIds = new Map();
153
+ request.commitIds.set(dbKeyHash, prevCommitIds);
154
+ }
155
+ for (const key of Object.keys(delta)) {
156
+ if (commitId < (prevCommitIds.get(key) ?? prevCommitIds.get(DEFAULT_COMMIT) ?? -1))
157
+ continue;
158
+ if (delta[key] && typeof delta[key] === 'object') {
159
+ A.copy(org, key, delta[key]);
160
+ }
161
+ else {
162
+ org[key] = delta[key];
163
+ }
164
+ prevCommitIds.set(key, commitId);
165
+ }
166
+ }
167
+ else {
168
+ // Create new object
169
+ if (prevCommitIds && commitId < (prevCommitIds.get(DEFAULT_COMMIT) ?? -1))
170
+ return; // Stale create
171
+ request.database.set(dbKeyHash, A.proxy(delta));
172
+ request.commitIds.set(dbKeyHash, new Map([[DEFAULT_COMMIT, commitId]]));
173
+ }
174
+ return;
175
+ }
176
+ // Each request should get one of these packet types as a response
177
+ if (type === SERVER_MESSAGES.error) {
178
+ const errorMessage = pack.readString();
179
+ request.resultProxy.error = new Error(errorMessage);
180
+ if (logLevel >= 2)
181
+ console.log(`[lowlander] incoming error requestId=${requestId} message=${errorMessage}`);
182
+ }
183
+ else if (type === SERVER_MESSAGES.response || type === SERVER_MESSAGES.response_proxy) {
184
+ request.resultProxy.value = pack.read();
185
+ request.virtualSocketIds = pack.read();
186
+ request.hasServerProxy = type === SERVER_MESSAGES.response_proxy;
187
+ if (logLevel >= 2)
188
+ console.log(`[lowlander] incoming response requestId=${requestId} value=${result.value} virtualSocketIds=${request.virtualSocketIds} hasServerProxy=${request.hasServerProxy}`);
189
+ }
190
+ else if (type === SERVER_MESSAGES.response_model) {
191
+ request.virtualSocketIds = pack.read(); // There must be at least one, for the model stream
192
+ const dbKey = pack.readNumber();
193
+ const obj = request.database?.get(dbKey);
194
+ if (logLevel >= 2)
195
+ console.log(`[lowlander] incoming response_model requestId=${requestId} dbKey=${dbKey} obj=${obj}`);
196
+ if (obj) {
197
+ request.resultProxy.value = A.proxy(obj);
198
+ }
199
+ else {
200
+ request.resultProxy.error = new Error('Unknown database key ' + dbKey);
201
+ }
202
+ }
203
+ else {
204
+ throw new Error('Unknown message type ' + type);
205
+ }
206
+ // Common cleanup for all response types
207
+ if (!request.hasServerProxy && !request.virtualSocketIds?.length) {
208
+ this.activeRequests.delete(requestId);
209
+ }
210
+ if (!request.hasServerProxy) {
211
+ delete request.resultProxy.serverProxy;
212
+ }
213
+ request.resultProxy.busy = false;
214
+ if (request.resolve) {
215
+ // This does not happen on reconnect
216
+ if (result.error != null) {
217
+ console.error(result.error);
218
+ request.reject(result.error);
219
+ }
220
+ else {
221
+ request.resolve(result.value);
222
+ }
223
+ delete request.resolve;
224
+ delete request.reject;
225
+ }
226
+ };
227
+ }
228
+ reconnect() {
229
+ this.ws = undefined;
230
+ this.onlineProxy.value = false;
231
+ if (typeof this.url !== 'string')
232
+ return; // No reconnect in test mode
233
+ // Reconnect with exponential backoff
234
+ const delay = Math.min(500 * Math.pow(2, this.reconnectAttempts), 20000);
235
+ this.reconnectAttempts++;
236
+ if (logLevel >= 1)
237
+ console.log(`[lowlander] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
238
+ setTimeout(this.connect.bind(this), delay);
239
+ }
240
+ pruneCommitIds(request, maxCommitId) {
241
+ if (!request.commitIds)
242
+ return;
243
+ for (const [hash, entry] of request.commitIds) {
244
+ for (const [key, cid] of entry) {
245
+ if (cid <= maxCommitId)
246
+ entry.delete(key);
247
+ }
248
+ if (!entry.size)
249
+ request.commitIds.delete(hash);
250
+ }
251
+ }
252
+ /** @internal */
253
+ _createMethodStub(methodName, proxyId) {
254
+ return (...params) => {
255
+ const result = { busy: true };
256
+ const resultProxy = A.proxy(result);
257
+ const requestId = ++this.requestCounter;
258
+ const pack = new DataPack();
259
+ pack.write(requestId).write(CLIENT_MESSAGES.call).write(proxyId).write(methodName);
260
+ let callbacks;
261
+ pack.writeCollection("array", () => {
262
+ for (const param of params) {
263
+ if (typeof param === 'function') {
264
+ callbacks ||= [];
265
+ pack.writeCustom('cb', callbacks.length);
266
+ callbacks.push(param);
267
+ }
268
+ else {
269
+ pack.write(param);
270
+ }
271
+ }
272
+ });
273
+ const request = { resultProxy, requestBuffer: pack.toUint8Array(true), requestId, connection: this, callbacks };
274
+ result.serverProxy = new Proxy(request, proxyHandlers);
275
+ result.promise = new Promise((resolve, reject) => {
276
+ request.resolve = resolve;
277
+ request.reject = reject;
278
+ });
279
+ this.activeRequests.set(requestId, request);
280
+ if (logLevel >= 2)
281
+ console.log(`[lowlander] outgoing call requestId=${requestId} method=${methodName} params=`, params);
282
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
283
+ this.ws.send(request.requestBuffer);
284
+ }
285
+ A.clean(() => {
286
+ this.activeRequests.delete(requestId);
287
+ if (request.virtualSocketIds?.length || request.hasServerProxy) {
288
+ if (logLevel >= 2)
289
+ console.log(`[lowlander] outgoing cancel requestId=${request.requestId} virtualSocketIds=${request.virtualSocketIds} hasServerProxy=${request.hasServerProxy}`);
290
+ const data = DataPack.createUint8Array(++this.requestCounter, CLIENT_MESSAGES.cancel, request.requestId, request.virtualSocketIds);
291
+ this.ws?.send(data);
292
+ }
293
+ });
294
+ return resultProxy;
295
+ };
296
+ }
297
+ }
298
+ const proxyHandlers = {
299
+ get(target, prop) {
300
+ if (target.serverProxyCache?.has(prop)) {
301
+ return target.serverProxyCache.get(prop);
302
+ }
303
+ if (prop === 'then' || prop === 'catch' || prop === 'finally') {
304
+ // When someone awaits the proxy, we should not treat it as a remote method call
305
+ return undefined;
306
+ }
307
+ if (prop === 'constructor') {
308
+ return { name: 'ServerProxy-' + target.requestId };
309
+ }
310
+ const result = target.connection._createMethodStub(prop, target.requestId);
311
+ if (!target.serverProxyCache)
312
+ target.serverProxyCache = new Map();
313
+ target.serverProxyCache.set(prop, result);
314
+ return result;
315
+ },
316
+ has(_target, prop) {
317
+ return typeof prop === 'string' || prop === A.NO_COPY;
318
+ }
319
+ };
320
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../client/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import 'mdui/mdui.css';
2
+ import 'mdui/components/button.js';
3
+ import 'mdui/components/dialog.js';
4
+ import 'mdui/components/tabs.js';
5
+ import 'mdui/components/tab.js';
6
+ import 'mdui/components/tab-panel.js';
7
+ import { ClientProxyObject } from 'lowlander/client';
8
+ import type * as ServerAPI from '../../server/api.js';
9
+ type API = ClientProxyObject<typeof ServerAPI>;
10
+ export declare function showAdminModal(api: API): void;
11
+ export {};
@@ -0,0 +1,87 @@
1
+ import 'mdui/mdui.css';
2
+ import 'mdui/components/button.js';
3
+ import 'mdui/components/dialog.js';
4
+ import 'mdui/components/tabs.js';
5
+ import 'mdui/components/tab.js';
6
+ import 'mdui/components/tab-panel.js';
7
+ import A from "aberdeen";
8
+ const TOPICS = ['sockets', 'channels', 'workers', 'kv'];
9
+ function formatDecodedBytes(value) {
10
+ let text = '';
11
+ for (const byte of value) {
12
+ const codePoint = byte;
13
+ const char = String.fromCodePoint(codePoint);
14
+ const printableAscii = codePoint >= 0x20 && codePoint <= 0x7e;
15
+ const whitespace = codePoint === 0x09 || codePoint === 0x0a || codePoint === 0x0d;
16
+ text += printableAscii || whitespace ? char : `<${codePoint}>`;
17
+ }
18
+ return text;
19
+ }
20
+ const tableStyle = A.insertCss({
21
+ '&': 'border-collapse:collapse text-align:left box-shadow: 0 0 20px rgba(0, 0, 0, 0.15);',
22
+ 'tr:nth-child(even)': 'background-color:#0002;',
23
+ 'td,th': 'padding: $1 $2; border-left: 1px solid #0002;',
24
+ 'td:first-child,th:first-child': 'border-left:none',
25
+ });
26
+ export function showAdminModal(api) {
27
+ const refreshes = A.proxy(0);
28
+ const closed = A.proxy(false);
29
+ A.mount(document.body, () => {
30
+ if (A.peek(closed, 'value') || closed.value)
31
+ return;
32
+ A('mdui-dialog open= close-on-overlay-click= close-on-esc= headline="Lowlander Admin panel" closed=', () => closed.value = true, () => {
33
+ A('span slot=description mdui-tabs value=', TOPICS[0], () => {
34
+ for (const topic of TOPICS) {
35
+ A('mdui-tab value=', topic, 'text=', topic);
36
+ }
37
+ for (const topic of TOPICS) {
38
+ A('mdui-tab-panel slot=panel value=', topic, () => {
39
+ const sockets = api.getDebugState(topic);
40
+ refreshes.value;
41
+ A(() => {
42
+ const data = A.unproxy(sockets.value);
43
+ if (!data)
44
+ return;
45
+ const keySet = new Set();
46
+ for (const [, obj] of Object.entries(data)) {
47
+ if (obj && typeof obj === 'object' && !(obj instanceof Uint8Array))
48
+ for (const k of Object.keys(obj))
49
+ keySet.add(k);
50
+ }
51
+ const cols = [...keySet];
52
+ A('table', tableStyle, () => {
53
+ A('tr', () => {
54
+ A('th text=#');
55
+ for (const k of cols)
56
+ A('th text=', k);
57
+ });
58
+ for (const [idx, obj] of Object.entries(data)) {
59
+ A('tr', () => {
60
+ A('td text=', idx);
61
+ for (const k of cols) {
62
+ const value = obj?.[k];
63
+ let text = '';
64
+ if (value instanceof Uint8Array) {
65
+ text = formatDecodedBytes(value);
66
+ }
67
+ else if (value !== null && value !== undefined) {
68
+ text = typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean'
69
+ ? String(value)
70
+ : JSON.stringify(value);
71
+ }
72
+ A('td text=', text);
73
+ }
74
+ });
75
+ }
76
+ });
77
+ });
78
+ });
79
+ }
80
+ });
81
+ const dialog = A();
82
+ A('mdui-button slot=action variant=text text=Update click=', () => refreshes.value++);
83
+ A('mdui-button slot=action variant=text text=Close click=', () => dialog.open = false);
84
+ });
85
+ });
86
+ }
87
+ //# sourceMappingURL=admin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin.js","sourceRoot":"","sources":["../../../../../examples/helloworld/client/js/admin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import 'mdui/mdui.css';
2
+ import 'mdui/components/button.js';
3
+ import 'mdui/components/fab.js';
4
+ import 'mdui/components/text-field.js';
@@ -3,81 +3,69 @@ import 'mdui/components/button.js';
3
3
  import 'mdui/components/fab.js';
4
4
  import 'mdui/components/text-field.js';
5
5
  import { alert } from 'mdui/functions/alert.js';
6
- import type * as API from '../../server/api.js';
7
6
  import { Connection } from 'lowlander/client';
8
7
  import A from 'aberdeen';
9
-
10
8
  import { setColorScheme } from 'mdui/functions/setColorScheme.js';
11
9
  // Generate a color scheme based on #0061a4 and set the <html> element to that color scheme
12
10
  setColorScheme('#ef6b00');
13
-
14
11
  A.setSpacingCssVars();
15
-
16
12
  const WEBSOCKET_URL = `ws://${location.hostname}:8080/`;
17
-
18
13
  // Create a WebSocket connection with type-safe RPC to the server API.
19
- const connection = new Connection<typeof API>(WEBSOCKET_URL);
14
+ const connection = new Connection(WEBSOCKET_URL);
20
15
  const api = connection.api;
21
- A('h2#Online')
16
+ A('h2#Online');
22
17
  // Create a reactive scope to render online status
23
18
  A(() => {
24
19
  A(connection.isOnline() ? 'text=yes' : 'text=no');
25
20
  });
26
-
27
21
  // Simple RPC call - returns a PromiseProxy that resolves to the result.
28
22
  const sum = api.add(1234, 6753);
29
- A('h2#Answer')
23
+ A('h2#Answer');
30
24
  A.dump(sum);
31
-
32
25
  // Server proxy example - authenticate returns both a value and a stateful API object.
33
26
  const auth = api.authenticate('Frank');
34
27
  A('h2#AuthToken');
35
28
  A.dump(auth); // auth.value will become "secret" once authentication completes
36
-
37
-
38
29
  // Access the server-side UserAPI through the .serverProxy property.
39
30
  // Note that this proxy is usable immediately, even though the authentication
40
31
  // call is still in progress - the server will process requests in-order.
41
32
  // If authentication were to fail, so would any calls on the server proxy.
42
33
  const userApi = auth.serverProxy;
43
-
44
34
  // Call methods on the server proxy (returns PromiseProxy like regular RPC).
45
35
  const bio = userApi.getBio();
46
36
  A('h2#Bio');
47
37
  A.dump(bio); // bio.value will become "1:Frank"
48
-
49
38
  // Model streaming - returns a reactive proxy that updates when server-side model changes.
50
39
  const model = api.streamModel();
51
40
  A('h2#Model');
52
41
  A.dump(model); // Live model (and nested linked models) will appear in model.value
53
-
54
42
  A('h2#Toggle friend');
55
43
  const formBusy = A.proxy(false);
56
44
  const friendName = A.proxy('');
57
- A('form display:flex gap:$2', 'submit=', async (e: any) => {
45
+ A('form display:flex gap:$2', 'submit=', async (e) => {
58
46
  e.preventDefault();
59
47
  formBusy.value = true;
60
48
  const found = await userApi.toggleFriend(friendName.value).promise;
61
49
  formBusy.value = false;
62
- if (!found) alert({description: 'No such person: ' + friendName.value});
50
+ if (!found)
51
+ alert({ description: 'No such person: ' + friendName.value });
63
52
  }, () => {
64
53
  A('mdui-text-field label="Person name" bind=', friendName);
65
54
  A(() => {
66
- if (formBusy.value) A('mdui-circular-progress');
67
- else A('mdui-button type=submit #Toggle friend');
68
- })
55
+ if (formBusy.value)
56
+ A('mdui-circular-progress');
57
+ else
58
+ A('mdui-button type=submit #Toggle friend');
59
+ });
69
60
  });
70
-
71
-
72
-
73
61
  // Socket streaming - server pushes data via callbacks.
74
- const data = A.proxy([] as number[]);
62
+ const data = A.proxy([]);
75
63
  let dataIndex = 0;
76
64
  api.streamSomething(item => data[dataIndex++ % 20] = item);
77
65
  A('h2#Streamed data');
78
66
  A.dump(data);
79
-
80
67
  A('mdui-fab icon=admin_panel_settings text="Lowlander Admin" click=', async () => {
81
68
  const { showAdminModal } = await import('./admin');
82
69
  showAdminModal(api);
83
70
  });
71
+ //# sourceMappingURL=base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base.js","sourceRoot":"","sources":["../../../../../examples/helloworld/client/js/base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import * as E from "edinburgh";
2
+ import { ServerProxy, Socket } from "lowlander/server";
3
+ export declare const getDebugState: typeof import("warpsocket/addon-loader").getDebugState;
4
+ export declare function add(a: number, b: number): number;
5
+ export declare class UserAPI {
6
+ userName: string;
7
+ constructor(userName: string);
8
+ get user(): Person;
9
+ getBio(): string;
10
+ toggleFriend(friendName: string): boolean;
11
+ }
12
+ export declare function authenticate(auth: string): Promise<ServerProxy<UserAPI, string>>;
13
+ declare class Person extends E.Model<Person> {
14
+ static byName: E.PrimaryIndex<typeof Person, ["name"]>;
15
+ name: string;
16
+ age: number;
17
+ friends: Person[];
18
+ password: string;
19
+ }
20
+ export declare function resetTestData(deleteEverything: boolean): Promise<void>;
21
+ export declare function streamModel(): {
22
+ _instance: E.Model<any> & {
23
+ name: string;
24
+ owner: {
25
+ name: string;
26
+ age: number;
27
+ friends: readonly {
28
+ name: string;
29
+ age: number;
30
+ }[];
31
+ };
32
+ createdAt: Date;
33
+ meta: Record<string | number, number>;
34
+ };
35
+ toString(): string;
36
+ };
37
+ export declare function incrOwnerAge(delta: number): Promise<void>;
38
+ export declare function setOwnerAge(age: number): void;
39
+ export declare function setModelName(name: string): void;
40
+ export declare function setMeta(key: string, value: number): void;
41
+ export declare function deleteMeta(key: string): void;
42
+ export declare function streamSomething(socket: Socket<number>): void;
43
+ export {};
44
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../../examples/helloworld/server/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,CAAC,MAAM,WAAW,CAAC;AAC/B,OAAO,EAAE,WAAW,EAAoB,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAGzE,eAAO,MAAM,aAAa,wDAA2B,CAAC;AAGtD,wBAAgB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAEhD;AAGD,qBAAa,OAAO;IACG,QAAQ,EAAE,MAAM;gBAAhB,QAAQ,EAAE,MAAM;IAEnC,IAAI,IAAI,IAAI,MAAM,CAIjB;IAED,MAAM;IAIN,YAAY,CAAC,UAAU,EAAE,MAAM;CAmBlC;AAGD,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,yCAM9C;AAID,cACM,MAAO,SAAQ,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC;IAChC,MAAM,CAAC,MAAM,0CAA6B;IAC1C,IAAI,SAAqB;IACzB,GAAG,SAAqB;IACxB,OAAO,WAAoC;IAC3C,QAAQ,SAAqB;CAChC;AAgBD,wBAAsB,aAAa,CAAC,gBAAgB,EAAE,OAAO,iBAiB5D;AA4BD,wBAAgB,WAAW;;;;;;;;;;;;;;;EAG1B;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,iBAK/C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,QAGtC;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,QAGxC;AAED,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAGjD;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,QAKrC;AAKD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QASrD"}