lowlander 0.2.1 → 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 +251 -6
  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 +14 -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
@@ -0,0 +1,317 @@
1
+ import A from 'aberdeen';
2
+ import DataPack from 'edinburgh/datapack';
3
+ import { SERVER_MESSAGES, CLIENT_MESSAGES } from '../server/protocol.js';
4
+ /** Set to 1-3 for increasing verbosity. */
5
+ export let logLevel = 0;
6
+ // Sentinel key in commitIds entries: on initial creation, only DEFAULT_COMMIT is set
7
+ // (applies to all keys). Per-key entries are added on subsequent updates and take
8
+ // precedence. On deletion, DEFAULT_COMMIT guards against stale re-creates.
9
+ const DEFAULT_COMMIT = Symbol();
10
+ /**
11
+ * WebSocket connection to a Lowlander server with type-safe RPC, automatic reconnection,
12
+ * and reactive updates.
13
+ *
14
+ * @typeParam T - The server-side API type (import from your server API file)
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * import type * as API from './server/api.js';
19
+ * const conn = new Connection<typeof API>('ws://localhost:8080/');
20
+ *
21
+ * // Simple RPC - returns PromiseProxy
22
+ * const sum = conn.api.add(1, 2);
23
+ *
24
+ * // Server proxy for stateful APIs
25
+ * const auth = conn.api.authenticate('token');
26
+ * const secret = auth.serverProxy.getSecret();
27
+ *
28
+ * // Streaming with callbacks
29
+ * conn.api.streamData(data => console.log(data));
30
+ *
31
+ * // Use within Aberdeen reactive scopes
32
+ * $(() => {
33
+ * dump(conn.isOnline());
34
+ * dump(sum);
35
+ * });
36
+ * ```
37
+ */
38
+ export class Connection {
39
+ url;
40
+ ws;
41
+ activeRequests = new Map();
42
+ requestCounter = 0;
43
+ reconnectAttempts = 0;
44
+ /** @internal */
45
+ _proxyCounter = 0;
46
+ onlineProxy = A.proxy(false);
47
+ /**
48
+ * Type-safe proxy to the server-side API. Methods return `PromiseProxy` objects
49
+ * that work reactively in Aberdeen scopes. `ServerProxy` returns include a
50
+ * `.serverProxy` property for accessing stateful server APIs.
51
+ */
52
+ api;
53
+ /**
54
+ * @param url - WebSocket URL (e.g., 'ws://localhost:8080/'), or a fake WebSocket object for testing
55
+ */
56
+ constructor(url) {
57
+ this.url = url;
58
+ this.api = new Proxy({ connection: this, requestId: undefined }, proxyHandlers);
59
+ this.connect();
60
+ }
61
+ /**
62
+ * Returns the current connection status. Reactive in Aberdeen scopes.
63
+ */
64
+ isOnline() { return this.onlineProxy.value; }
65
+ connect() {
66
+ const ws = this.ws = typeof this.url === 'string'
67
+ ? new WebSocket(this.url)
68
+ : this.url();
69
+ ws.binaryType = "arraybuffer";
70
+ if (logLevel >= 1)
71
+ console.log(`[lowlander] Connecting to WebSocket at ${typeof this.url === 'string' ? this.url : '[custom WebSocket]'}`);
72
+ ws.onopen = () => {
73
+ if (ws !== this.ws)
74
+ return; // No longer the current connection
75
+ if (logLevel >= 1)
76
+ console.log('[lowlander] WebSocket connected');
77
+ this.onlineProxy.value = true;
78
+ this.reconnectAttempts = 0;
79
+ for (const request of this.activeRequests.values()) {
80
+ request.resultProxy.busy = true;
81
+ this.ws.send(request.requestBuffer);
82
+ }
83
+ };
84
+ ws.onclose = () => {
85
+ if (ws !== this.ws)
86
+ return; // No longer the current connection
87
+ if (logLevel >= 1)
88
+ console.log('[lowlander] WebSocket disconnected');
89
+ this.reconnect();
90
+ };
91
+ ws.onerror = (error) => {
92
+ if (ws !== this.ws)
93
+ return; // No longer the current connection
94
+ console.error('WebSocket error:', error);
95
+ this.reconnect();
96
+ };
97
+ ws.onmessage = (event) => {
98
+ if (ws !== this.ws)
99
+ return; // No longer the current connection
100
+ const pack = new DataPack(new Uint8Array(event.data));
101
+ // console.log(`onmessage: ${pack}`);
102
+ const requestId = pack.readPositiveInt();
103
+ const request = this.activeRequests.get(requestId);
104
+ if (!request)
105
+ return; // Raced
106
+ const result = A.unproxy(request.resultProxy);
107
+ const type = pack.read();
108
+ if (typeof type === 'number') {
109
+ // It's a callback invocation
110
+ const callback = request.callbacks?.[type];
111
+ const args = [];
112
+ while (pack.readAvailable()) {
113
+ args.push(pack.read());
114
+ }
115
+ callback(...args);
116
+ return;
117
+ }
118
+ // This packet type does not represent the result for a request
119
+ if (type === SERVER_MESSAGES.model_data) {
120
+ request.database ||= new Map();
121
+ request.commitIds ||= new Map();
122
+ const dbKeyHash = pack.readNumber();
123
+ const commitId = pack.readNumber();
124
+ const delta = pack.read({ model: function (linkHash) {
125
+ const linkedModel = request.database.get(linkHash);
126
+ if (!linkedModel)
127
+ console.error('Unknown linked model hash ' + linkHash);
128
+ return linkedModel;
129
+ } });
130
+ if (logLevel >= 3)
131
+ console.log('[lowlander] incoming model_data', requestId, dbKeyHash, commitId, delta);
132
+ // Schedule cleanup: after 15s, all out-of-order messages for this commitId
133
+ // must have arrived, so we can prune tracking entries at or below it.
134
+ setTimeout(() => this.pruneCommitIds(request, commitId), 15000);
135
+ let prevCommitIds = request.commitIds.get(dbKeyHash);
136
+ if (!delta) {
137
+ // Stale delete: some key was already updated past this commitId
138
+ if (prevCommitIds && commitId < Math.max(...prevCommitIds.values()))
139
+ return;
140
+ request.database.delete(dbKeyHash);
141
+ // Record delete's commitId so stale creates arriving later are rejected
142
+ request.commitIds.set(dbKeyHash, new Map([[DEFAULT_COMMIT, commitId]]));
143
+ return;
144
+ }
145
+ let org = request.database.get(dbKeyHash);
146
+ if (org) {
147
+ // Update existing object
148
+ if (!prevCommitIds) {
149
+ prevCommitIds = new Map();
150
+ request.commitIds.set(dbKeyHash, prevCommitIds);
151
+ }
152
+ for (const key of Object.keys(delta)) {
153
+ if (commitId < (prevCommitIds.get(key) ?? prevCommitIds.get(DEFAULT_COMMIT) ?? -1))
154
+ continue;
155
+ if (delta[key] && typeof delta[key] === 'object') {
156
+ A.copy(org, key, delta[key]);
157
+ }
158
+ else {
159
+ org[key] = delta[key];
160
+ }
161
+ prevCommitIds.set(key, commitId);
162
+ }
163
+ }
164
+ else {
165
+ // Create new object
166
+ if (prevCommitIds && commitId < (prevCommitIds.get(DEFAULT_COMMIT) ?? -1))
167
+ return; // Stale create
168
+ request.database.set(dbKeyHash, A.proxy(delta));
169
+ request.commitIds.set(dbKeyHash, new Map([[DEFAULT_COMMIT, commitId]]));
170
+ }
171
+ return;
172
+ }
173
+ // Each request should get one of these packet types as a response
174
+ if (type === SERVER_MESSAGES.error) {
175
+ const errorMessage = pack.readString();
176
+ request.resultProxy.error = new Error(errorMessage);
177
+ if (logLevel >= 2)
178
+ console.log(`[lowlander] incoming error requestId=${requestId} message=${errorMessage}`);
179
+ }
180
+ else if (type === SERVER_MESSAGES.response || type === SERVER_MESSAGES.response_proxy) {
181
+ request.resultProxy.value = pack.read();
182
+ request.virtualSocketIds = pack.read();
183
+ request.hasServerProxy = type === SERVER_MESSAGES.response_proxy;
184
+ if (logLevel >= 2)
185
+ console.log(`[lowlander] incoming response requestId=${requestId} value=${result.value} virtualSocketIds=${request.virtualSocketIds} hasServerProxy=${request.hasServerProxy}`);
186
+ }
187
+ else if (type === SERVER_MESSAGES.response_model) {
188
+ request.virtualSocketIds = pack.read(); // There must be at least one, for the model stream
189
+ const dbKey = pack.readNumber();
190
+ const obj = request.database?.get(dbKey);
191
+ if (logLevel >= 2)
192
+ console.log(`[lowlander] incoming response_model requestId=${requestId} dbKey=${dbKey} obj=${obj}`);
193
+ if (obj) {
194
+ request.resultProxy.value = A.proxy(obj);
195
+ }
196
+ else {
197
+ request.resultProxy.error = new Error('Unknown database key ' + dbKey);
198
+ }
199
+ }
200
+ else {
201
+ throw new Error('Unknown message type ' + type);
202
+ }
203
+ // Common cleanup for all response types
204
+ if (!request.hasServerProxy && !request.virtualSocketIds?.length) {
205
+ this.activeRequests.delete(requestId);
206
+ }
207
+ if (!request.hasServerProxy) {
208
+ delete request.resultProxy.serverProxy;
209
+ }
210
+ request.resultProxy.busy = false;
211
+ if (request.resolve) {
212
+ // This does not happen on reconnect
213
+ if (result.error != null) {
214
+ console.error(result.error);
215
+ request.reject(result.error);
216
+ }
217
+ else {
218
+ request.resolve(result.value);
219
+ }
220
+ delete request.resolve;
221
+ delete request.reject;
222
+ }
223
+ };
224
+ }
225
+ reconnect() {
226
+ this.ws = undefined;
227
+ this.onlineProxy.value = false;
228
+ if (typeof this.url !== 'string')
229
+ return; // No reconnect in test mode
230
+ // Reconnect with exponential backoff
231
+ const delay = Math.min(500 * Math.pow(2, this.reconnectAttempts), 20000);
232
+ this.reconnectAttempts++;
233
+ if (logLevel >= 1)
234
+ console.log(`[lowlander] Reconnecting in ${delay}ms (attempt ${this.reconnectAttempts})`);
235
+ setTimeout(this.connect.bind(this), delay);
236
+ }
237
+ pruneCommitIds(request, maxCommitId) {
238
+ if (!request.commitIds)
239
+ return;
240
+ for (const [hash, entry] of request.commitIds) {
241
+ for (const [key, cid] of entry) {
242
+ if (cid <= maxCommitId)
243
+ entry.delete(key);
244
+ }
245
+ if (!entry.size)
246
+ request.commitIds.delete(hash);
247
+ }
248
+ }
249
+ /** @internal */
250
+ _createMethodStub(methodName, proxyId) {
251
+ return (...params) => {
252
+ const result = { busy: true };
253
+ const resultProxy = A.proxy(result);
254
+ const requestId = ++this.requestCounter;
255
+ const pack = new DataPack();
256
+ pack.write(requestId).write(CLIENT_MESSAGES.call).write(proxyId).write(methodName);
257
+ let callbacks;
258
+ pack.writeCollection("array", () => {
259
+ for (const param of params) {
260
+ if (typeof param === 'function') {
261
+ callbacks ||= [];
262
+ pack.writeCustom('cb', callbacks.length);
263
+ callbacks.push(param);
264
+ }
265
+ else {
266
+ pack.write(param);
267
+ }
268
+ }
269
+ });
270
+ const request = { resultProxy, requestBuffer: pack.toUint8Array(true), requestId, connection: this, callbacks };
271
+ result.serverProxy = new Proxy(request, proxyHandlers);
272
+ result.promise = new Promise((resolve, reject) => {
273
+ request.resolve = resolve;
274
+ request.reject = reject;
275
+ });
276
+ this.activeRequests.set(requestId, request);
277
+ if (logLevel >= 2)
278
+ console.log(`[lowlander] outgoing call requestId=${requestId} method=${methodName} params=`, params);
279
+ if (this.ws && this.ws.readyState === WebSocket.OPEN) {
280
+ this.ws.send(request.requestBuffer);
281
+ }
282
+ A.clean(() => {
283
+ this.activeRequests.delete(requestId);
284
+ if (request.virtualSocketIds?.length || request.hasServerProxy) {
285
+ if (logLevel >= 2)
286
+ console.log(`[lowlander] outgoing cancel requestId=${request.requestId} virtualSocketIds=${request.virtualSocketIds} hasServerProxy=${request.hasServerProxy}`);
287
+ const data = DataPack.createUint8Array(++this.requestCounter, CLIENT_MESSAGES.cancel, request.requestId, request.virtualSocketIds);
288
+ this.ws?.send(data);
289
+ }
290
+ });
291
+ return resultProxy;
292
+ };
293
+ }
294
+ }
295
+ const proxyHandlers = {
296
+ get(target, prop) {
297
+ if (target.serverProxyCache?.has(prop)) {
298
+ return target.serverProxyCache.get(prop);
299
+ }
300
+ if (prop === 'then' || prop === 'catch' || prop === 'finally') {
301
+ // When someone awaits the proxy, we should not treat it as a remote method call
302
+ return undefined;
303
+ }
304
+ if (prop === 'constructor') {
305
+ return { name: 'ServerProxy-' + target.requestId };
306
+ }
307
+ const result = target.connection._createMethodStub(prop, target.requestId);
308
+ if (!target.serverProxyCache)
309
+ target.serverProxyCache = new Map();
310
+ target.serverProxyCache.set(prop, result);
311
+ return result;
312
+ },
313
+ has(_target, prop) {
314
+ return typeof prop === 'string' || prop === A.NO_COPY;
315
+ }
316
+ };
317
+ //# 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":"AAAA,OAAO,eAAe,CAAC;AACvB,OAAO,2BAA2B,CAAC;AACnC,OAAO,wBAAwB,CAAC;AAChC,OAAO,+BAA+B,CAAC;AACvC,OAAO,EAAE,KAAK,EAAE,MAAM,yBAAyB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,CAAC,MAAM,UAAU,CAAC;AAEzB,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAC;AAClE,2FAA2F;AAC3F,cAAc,CAAC,SAAS,CAAC,CAAC;AAE1B,CAAC,CAAC,iBAAiB,EAAE,CAAC;AAEtB,MAAM,aAAa,GAAG,QAAQ,QAAQ,CAAC,QAAQ,QAAQ,CAAC;AAExD,sEAAsE;AACtE,MAAM,UAAU,GAAG,IAAI,UAAU,CAAa,aAAa,CAAC,CAAC;AAC7D,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,CAAC;AAC3B,CAAC,CAAC,WAAW,CAAC,CAAA;AACd,kDAAkD;AAClD,CAAC,CAAC,GAAG,EAAE;IACH,CAAC,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;AACtD,CAAC,CAAC,CAAC;AAEH,wEAAwE;AACxE,MAAM,GAAG,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAChC,CAAC,CAAC,WAAW,CAAC,CAAA;AACd,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAEZ,sFAAsF;AACtF,MAAM,IAAI,GAAG,GAAG,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;AACvC,CAAC,CAAC,cAAc,CAAC,CAAC;AAClB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,gEAAgE;AAG9E,oEAAoE;AACpE,6EAA6E;AAC7E,yEAAyE;AACzE,0EAA0E;AAC1E,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC;AAEjC,4EAA4E;AAC5E,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;AAC7B,CAAC,CAAC,QAAQ,CAAC,CAAC;AACZ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,kCAAkC;AAE/C,0FAA0F;AAC1F,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;AAChC,CAAC,CAAC,UAAU,CAAC,CAAC;AACd,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,mEAAmE;AAElF,CAAC,CAAC,kBAAkB,CAAC,CAAC;AACtB,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;AAChC,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;AAC/B,CAAC,CAAC,0BAA0B,EAAE,SAAS,EAAE,KAAK,EAAE,CAAM,EAAE,EAAE;IACtD,CAAC,CAAC,cAAc,EAAE,CAAC;IACnB,QAAQ,CAAC,KAAK,GAAG,IAAI,CAAC;IACtB,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,YAAY,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC;IACnE,QAAQ,CAAC,KAAK,GAAG,KAAK,CAAC;IACvB,IAAI,CAAC,KAAK;QAAE,KAAK,CAAC,EAAC,WAAW,EAAE,kBAAkB,GAAG,UAAU,CAAC,KAAK,EAAC,CAAC,CAAC;AAC5E,CAAC,EAAE,GAAG,EAAE;IACJ,CAAC,CAAC,2CAA2C,EAAE,UAAU,CAAC,CAAC;IAC3D,CAAC,CAAC,GAAG,EAAE;QACH,IAAI,QAAQ,CAAC,KAAK;YAAE,CAAC,CAAC,wBAAwB,CAAC,CAAC;;YAC3C,CAAC,CAAC,wCAAwC,CAAC,CAAC;IACrD,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAC;AAIH,uDAAuD;AACvD,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,EAAc,CAAC,CAAC;AACrC,IAAI,SAAS,GAAG,CAAC,CAAC;AAClB,GAAG,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC;AAC3D,CAAC,CAAC,kBAAkB,CAAC,CAAC;AACtB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAEb,CAAC,CAAC,kEAAkE,EAAE,KAAK,IAAI,EAAE;IAC7E,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;IACnD,cAAc,CAAC,GAAG,CAAC,CAAC;AACxB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,40 @@
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
+ };
34
+ toString(): string;
35
+ };
36
+ export declare function incrOwnerAge(delta: number): Promise<void>;
37
+ export declare function setOwnerAge(age: number): void;
38
+ export declare function streamSomething(socket: Socket<number>): void;
39
+ export {};
40
+ //# 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;AAeD,wBAAsB,aAAa,CAAC,gBAAgB,EAAE,OAAO,iBAiB5D;AA2BD,wBAAgB,WAAW;;;;;;;;;;;;;;EAG1B;AAED,wBAAsB,YAAY,CAAC,KAAK,EAAE,MAAM,iBAK/C;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,QAGtC;AAKD,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,QASrD"}