@velarscript/cli 0.18.0 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +8 -8
- package/skill/ai-skill-node.md +5 -0
- package/skill/ai-skill-server.md +59 -1
- package/skill/ai-skill-web.md +45 -0
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@velarscript/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.0",
|
|
4
4
|
"description": "The VelarScript command-line compiler, dev server, test runner, and language server.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -30,13 +30,13 @@
|
|
|
30
30
|
"velar": "./dist/cli.js"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@velarscript/compiler": "0.
|
|
34
|
-
"@velarscript/core": "0.
|
|
35
|
-
"@velarscript/desktop": "0.
|
|
36
|
-
"@velarscript/node": "0.
|
|
37
|
-
"@velarscript/server": "0.
|
|
38
|
-
"@velarscript/web": "0.
|
|
39
|
-
"create-velar": "0.
|
|
33
|
+
"@velarscript/compiler": "0.19.0",
|
|
34
|
+
"@velarscript/core": "0.19.0",
|
|
35
|
+
"@velarscript/desktop": "0.19.0",
|
|
36
|
+
"@velarscript/node": "0.19.0",
|
|
37
|
+
"@velarscript/server": "0.19.0",
|
|
38
|
+
"@velarscript/web": "0.19.0",
|
|
39
|
+
"create-velar": "0.19.0",
|
|
40
40
|
"esbuild": "^0.28.1",
|
|
41
41
|
"playwright": "^1.58.2"
|
|
42
42
|
}
|
package/skill/ai-skill-node.md
CHANGED
|
@@ -337,6 +337,11 @@ canonical HTTP/HTTPS origins. The default rejects any upgrade carrying
|
|
|
337
337
|
intentional unrestricted policy. A rejected browser origin receives 403 before
|
|
338
338
|
it consumes connection-queue capacity. Connections are pull-based. Always
|
|
339
339
|
consume each connection's `next()`, handle backpressure, and stop the server.
|
|
340
|
+
`closeInfo()` waits for the terminal handshake and returns the actual typed
|
|
341
|
+
`WebSocketClose {code, reason}`; `next()` returning `null` alone does not expose
|
|
342
|
+
why the connection ended. Application sessions should normally use
|
|
343
|
+
server-target `velar/realtime.realtimeSession` instead of rebuilding their own writer and
|
|
344
|
+
cleanup loops over this transport.
|
|
340
345
|
|
|
341
346
|
## Tests
|
|
342
347
|
|
package/skill/ai-skill-server.md
CHANGED
|
@@ -13,7 +13,7 @@ browser application activates `@velarscript/web`:
|
|
|
13
13
|
```json
|
|
14
14
|
{
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@velarscript/server": "0.
|
|
16
|
+
"@velarscript/server": "0.19.0"
|
|
17
17
|
}
|
|
18
18
|
}
|
|
19
19
|
```
|
|
@@ -177,6 +177,64 @@ dialect behavior, retry rules, and credentials remain independently installed
|
|
|
177
177
|
application dependencies. Do not add SQLite, PostgreSQL, an ORM, or model
|
|
178
178
|
syntax to `@velarscript/server` or `@velarscript/node`.
|
|
179
179
|
|
|
180
|
+
## Typed realtime sessions
|
|
181
|
+
|
|
182
|
+
Keep the physical socket and the application session separate. A declarative
|
|
183
|
+
`@websocket` route receives `WebSocketConnection`; pass it to
|
|
184
|
+
`realtimeSession` with the codec from the application's shared protocol
|
|
185
|
+
package:
|
|
186
|
+
|
|
187
|
+
```velar fragment
|
|
188
|
+
import {Bytes} from "velar/binary"
|
|
189
|
+
import {RealtimeFailure, RealtimeFailureAction, RealtimePeer, realtimeSession} from "velar/realtime"
|
|
190
|
+
import {WebSocketConnection} from "velar/websocket"
|
|
191
|
+
|
|
192
|
+
type Command:
|
|
193
|
+
operation: string
|
|
194
|
+
|
|
195
|
+
type ServerEvent:
|
|
196
|
+
event: string
|
|
197
|
+
|
|
198
|
+
def decode(message: string | Bytes) -> Command:
|
|
199
|
+
if message is string: return Json.parse(message, Command)
|
|
200
|
+
throw Error("Binary commands are not supported")
|
|
201
|
+
|
|
202
|
+
def encode(event: ServerEvent) -> string | Bytes:
|
|
203
|
+
return Json.stringify(event)
|
|
204
|
+
|
|
205
|
+
async def receive(command: Command, peer: RealtimePeer<ServerEvent>):
|
|
206
|
+
await peer.send({event: command.operation})
|
|
207
|
+
|
|
208
|
+
async def failed(failure: RealtimeFailure, _peer: RealtimePeer<ServerEvent>):
|
|
209
|
+
print(failure.error)
|
|
210
|
+
return RealtimeFailureAction.close
|
|
211
|
+
|
|
212
|
+
async def serveSession(connection: WebSocketConnection):
|
|
213
|
+
await realtimeSession(
|
|
214
|
+
connection,
|
|
215
|
+
{decode, encode},
|
|
216
|
+
receive,
|
|
217
|
+
failed=failed,
|
|
218
|
+
options={maxQueuedMessages: 64, maxQueuedBytes: 1_048_576, drainTimeout: 5s},
|
|
219
|
+
)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The session runs one sequential inbound handler and one writer over a bounded
|
|
223
|
+
outbound mailbox. `peer.send(event)` waits for its own transport send;
|
|
224
|
+
`peer.trySend(event)` returns `false` instead of waiting when the mailbox is
|
|
225
|
+
full. `opened` may install a subscription and return an async cleanup function;
|
|
226
|
+
cleanup runs exactly once before the writer finishes draining. `closed`
|
|
227
|
+
receives the actual `WebSocketClose` code and reason.
|
|
228
|
+
|
|
229
|
+
`RealtimeFailureAction.continue` skips one failed decode or command and keeps
|
|
230
|
+
the session alive. Unrecoverable setup, transport, encode, and send failures
|
|
231
|
+
close the session. Route authentication, authorization, room membership,
|
|
232
|
+
subscription ownership, and application delivery semantics remain application
|
|
233
|
+
policy. Do not report a command as durable merely because `send` completed:
|
|
234
|
+
WebSocket is ordered but application delivery is still at-most-once unless the
|
|
235
|
+
shared protocol adds message IDs, acknowledgements, resume cursors, and
|
|
236
|
+
idempotent handling.
|
|
237
|
+
|
|
180
238
|
## Custom shared HTTP/WebSocket startup
|
|
181
239
|
|
|
182
240
|
A service whose route table contains `@websocket` declarations may load the
|
package/skill/ai-skill-web.md
CHANGED
|
@@ -153,6 +153,51 @@ property unconditionally to override the component's outright, and declare it
|
|
|
153
153
|
under a condition to refine that condition alone and leave the component's
|
|
154
154
|
other values standing.
|
|
155
155
|
|
|
156
|
+
## Realtime client
|
|
157
|
+
|
|
158
|
+
`velar/websocket.connect` is the sole raw socket transport.
|
|
159
|
+
`velar/realtime.realtimeClient` is the application layer: a typed codec,
|
|
160
|
+
explicit lifecycle, finite reconnect policy, connection generations, and one
|
|
161
|
+
callback where subscriptions can be rebuilt after reconnect.
|
|
162
|
+
|
|
163
|
+
```velar fragment
|
|
164
|
+
import {Bytes} from "velar/binary"
|
|
165
|
+
import {RealtimeClient, RealtimeClientFailureAction, RealtimeOpen, realtimeClient} from "velar/realtime"
|
|
166
|
+
|
|
167
|
+
type ServerEvent:
|
|
168
|
+
event: string
|
|
169
|
+
|
|
170
|
+
type Command:
|
|
171
|
+
operation: string
|
|
172
|
+
|
|
173
|
+
def decode(message: string | Bytes) -> ServerEvent:
|
|
174
|
+
if message is string: return Json.parse(message, ServerEvent)
|
|
175
|
+
throw Error("Binary events are not supported")
|
|
176
|
+
|
|
177
|
+
def encode(command: Command) -> string | Bytes: return Json.stringify(command)
|
|
178
|
+
|
|
179
|
+
async def opened(client: RealtimeClient<Command>, open: RealtimeOpen):
|
|
180
|
+
if open.reconnected: await client.send({operation: "resync"})
|
|
181
|
+
|
|
182
|
+
export def liveClient(url: string) -> RealtimeClient<Command>:
|
|
183
|
+
return realtimeClient(
|
|
184
|
+
url,
|
|
185
|
+
{decode, encode},
|
|
186
|
+
async (event, _client) => print(event.event),
|
|
187
|
+
opened=opened,
|
|
188
|
+
failed=async (_failure, _client) => RealtimeClientFailureAction.reconnect,
|
|
189
|
+
options={reconnectDelays: [0ms, 1s, 2s, 5s], reconnectJitter: 0.2},
|
|
190
|
+
)
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Call `await client.start()` from owned startup and `await client.close()` from
|
|
194
|
+
cleanup. `whenOpen()` waits for the current or next generation;
|
|
195
|
+
`whenClosed()` waits for terminal shutdown. The client does not queue or replay
|
|
196
|
+
commands across a disconnect. Put message IDs, acknowledgements, resume
|
|
197
|
+
cursors, and idempotency in the shared application protocol when stronger
|
|
198
|
+
delivery is required. A URL function may refresh a signed URL on every attempt.
|
|
199
|
+
Initial retry is off unless `retryInitial: true` is explicit.
|
|
200
|
+
|
|
156
201
|
## Storage and tests
|
|
157
202
|
|
|
158
203
|
`velar/storage` stores JSON and validates on read. A generic type spelling is
|