@mauroandre/velojs 0.0.60 → 0.0.61
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/README.md +128 -0
- package/agent/AGENTS.md +63 -0
- package/agent/skills/velojs-actions/SKILL.md +32 -0
- package/agent/skills/velojs-hooks/SKILL.md +26 -0
- package/agent/skills/velojs-live-actions/SKILL.md +211 -0
- package/agent/skills/velojs-telemetry/SKILL.md +6 -1
- package/dist/channels.d.ts +117 -11
- package/dist/channels.js +459 -284
- package/dist/hooks.d.ts +2 -0
- package/dist/hooks.js +34 -32
- package/dist/live-action.d.ts +86 -0
- package/dist/live-action.js +52 -0
- package/dist/server.d.ts +1 -1
- package/dist/server.js +165 -143
- package/dist/telemetry-cli.d.ts +1 -0
- package/dist/telemetry-cli.js +116 -113
- package/dist/telemetry.d.ts +3 -3
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +500 -453
- package/dist/testing/subscription.d.ts +7 -0
- package/dist/testing/types.d.ts +43 -2
- package/dist/types.d.ts +12 -0
- package/dist/vite.d.ts +11 -1
- package/dist/vite.js +506 -439
- package/package.json +1 -1
package/dist/channels.d.ts
CHANGED
|
@@ -7,6 +7,14 @@
|
|
|
7
7
|
* family as `/_action` and `/_event` — which inherits the middlewares of the
|
|
8
8
|
* module's route node and serves the module's loader as a stream of snapshots.
|
|
9
9
|
*
|
|
10
|
+
* The tab-facing twin is the live action: a module declares
|
|
11
|
+
* `liveActions = { actionName: [channel, …] }` and the framework derives
|
|
12
|
+
* `GET /_live/{moduleId}/{actionName}` — a subscription whose args travel in
|
|
13
|
+
* `?_args=` and whose source is the action itself (a parameterized read,
|
|
14
|
+
* re-executed with those args). The subscription enters the SAME groups by
|
|
15
|
+
* (channel, partition): one `emit` by name wakes the loader connections and
|
|
16
|
+
* the live subscriptions of the partition, each re-executing its own source.
|
|
17
|
+
*
|
|
10
18
|
* The app owns the partition policy in `app/channels.ts`, a name → resolver map:
|
|
11
19
|
*
|
|
12
20
|
* ```ts
|
|
@@ -15,7 +23,8 @@
|
|
|
15
23
|
* };
|
|
16
24
|
* ```
|
|
17
25
|
*
|
|
18
|
-
* On subscribe the resolver receives `{ user, params }
|
|
26
|
+
* On subscribe the resolver receives `{ user, params }` (a loader connection)
|
|
27
|
+
* or `{ user, args }` (a live action subscription): `user` is the
|
|
19
28
|
* materialized principal (`c.get("user")`, the house key — `undefined` when the
|
|
20
29
|
* app does not materialize one, so a public channel ignores it and an
|
|
21
30
|
* authenticated one returns `null`, which denies the subscription with 403) and
|
|
@@ -56,7 +65,7 @@
|
|
|
56
65
|
* dropped and closed instead of receiving data of a group it left.
|
|
57
66
|
*/
|
|
58
67
|
import type { Context, Hono, MiddlewareHandler } from "hono";
|
|
59
|
-
import type { LoaderArgs, RouteModule } from "./types.js";
|
|
68
|
+
import type { ActionArgs, LoaderArgs, RouteModule } from "./types.js";
|
|
60
69
|
import { type TelemetryStatus, type TelemetryTrace } from "./telemetry.js";
|
|
61
70
|
/** Result of a partition resolver. `null`/`undefined` denies the subscription. */
|
|
62
71
|
export type ChannelScopeResult = string | null | undefined;
|
|
@@ -76,6 +85,12 @@ export type ChannelScopeResult = string | null | undefined;
|
|
|
76
85
|
export interface ChannelScopeContext {
|
|
77
86
|
user?: any;
|
|
78
87
|
params?: Record<string, any>;
|
|
88
|
+
/**
|
|
89
|
+
* The declared arguments of a **live action** subscription (`kind: "live"`).
|
|
90
|
+
* The tab-facing twin of `params`: the address the subscription itself
|
|
91
|
+
* declared, not something the route declares.
|
|
92
|
+
*/
|
|
93
|
+
args?: Record<string, any> | undefined;
|
|
79
94
|
}
|
|
80
95
|
/**
|
|
81
96
|
* What `onGroupOpen` receives: the partition key of the group that just got its
|
|
@@ -86,14 +101,27 @@ export interface ChannelScopeContext {
|
|
|
86
101
|
* partitioned by principal, declared in a module with `:params`, mixes
|
|
87
102
|
* resources in the same group and the hook's params are the first connection's
|
|
88
103
|
* — arbitrary for the rest; such a channel must not consume them here.
|
|
104
|
+
*
|
|
105
|
+
* `args` is the live-action half of the same address: when the connection that
|
|
106
|
+
* opened the group is a live action subscription, they are the args that
|
|
107
|
+
* subscription declared (with the same arbitrariness caveat when the partition
|
|
108
|
+
* does not derive from them).
|
|
89
109
|
*/
|
|
90
110
|
export interface ChannelGroupOpenContext {
|
|
91
111
|
partition: string;
|
|
92
112
|
params: Record<string, string>;
|
|
113
|
+
/** The declared args of the connection that opened the group (live actions). */
|
|
114
|
+
args?: Record<string, unknown> | undefined;
|
|
93
115
|
}
|
|
94
|
-
/**
|
|
116
|
+
/**
|
|
117
|
+
* What `onGroupClose` receives: the partition key of the group being torn
|
|
118
|
+
* down and, when the group was opened by a live action subscription, the args
|
|
119
|
+
* of that connection — the same fields `onGroupOpen` saw.
|
|
120
|
+
*/
|
|
95
121
|
export interface ChannelGroupCloseContext {
|
|
96
122
|
partition: string;
|
|
123
|
+
/** The declared args of the connection that opened the group (live actions). */
|
|
124
|
+
args?: Record<string, unknown> | undefined;
|
|
97
125
|
}
|
|
98
126
|
/**
|
|
99
127
|
* One entry of the app's channel map (`app/channels.ts`).
|
|
@@ -206,13 +234,31 @@ export interface ChannelConnection {
|
|
|
206
234
|
/** Partition key resolved at subscribe time — never from client input. */
|
|
207
235
|
partition: string;
|
|
208
236
|
moduleId: string;
|
|
237
|
+
/**
|
|
238
|
+
* Which surface the connection feeds: `"loader"` (the page's live-loader
|
|
239
|
+
* connection — the default) or `"live"` (a live action subscription, the
|
|
240
|
+
* tab-facing twin).
|
|
241
|
+
*/
|
|
242
|
+
kind?: "loader" | "live";
|
|
209
243
|
/**
|
|
210
244
|
* The principal materialized at subscribe (`c.get("user")`), captured for
|
|
211
245
|
* the partition revalidation of every emit — the scope re-runs against it.
|
|
212
246
|
*/
|
|
213
247
|
principal: unknown;
|
|
214
|
-
loader
|
|
215
|
-
|
|
248
|
+
/** The module's loader — the data source of a `"loader"` connection. */
|
|
249
|
+
loader?: (args: LoaderArgs) => Promise<unknown>;
|
|
250
|
+
/** The live action — the data source of a `"live"` connection. */
|
|
251
|
+
action?: (args: ActionArgs<any>) => Promise<unknown>;
|
|
252
|
+
/** The action name without the `action_` prefix (`"live"` connections). */
|
|
253
|
+
actionName?: string | undefined;
|
|
254
|
+
/** The subscription's declared args (`"live"` connections) — the tab's address. */
|
|
255
|
+
args?: Record<string, unknown> | undefined;
|
|
256
|
+
/**
|
|
257
|
+
* Identifies one live action subscription across the N group entries it
|
|
258
|
+
* holds (one per declared channel) — the inspector's key.
|
|
259
|
+
*/
|
|
260
|
+
liveId?: string | undefined;
|
|
261
|
+
/** The subscribing request's context — the loader/action re-runs against it. */
|
|
216
262
|
c: Context;
|
|
217
263
|
params: Record<string, string>;
|
|
218
264
|
query: Record<string, string>;
|
|
@@ -260,25 +306,47 @@ export interface ChannelInspectWindow {
|
|
|
260
306
|
channel: string;
|
|
261
307
|
partition: string;
|
|
262
308
|
}
|
|
309
|
+
/** One live action subscription, as the inspector reports it. */
|
|
310
|
+
export interface ChannelInspectLiveAction {
|
|
311
|
+
/** The route module holding the action (`metadata.moduleId`). */
|
|
312
|
+
moduleId: string;
|
|
313
|
+
/** The action name, without the `action_` prefix. */
|
|
314
|
+
action: string;
|
|
315
|
+
/** Every (channel, partition) group the subscription entered. */
|
|
316
|
+
channels: {
|
|
317
|
+
channel: string;
|
|
318
|
+
partition: string;
|
|
319
|
+
}[];
|
|
320
|
+
/** The args the subscription declared — its address. */
|
|
321
|
+
args: Record<string, unknown> | null;
|
|
322
|
+
/** ISO timestamp of the subscription's last delivery; `null` when none yet. */
|
|
323
|
+
lastDeliveryAt: string | null;
|
|
324
|
+
}
|
|
263
325
|
/** The full live state of the channel registry, as `inspectChannels()` sees it. */
|
|
264
326
|
export interface ChannelInspectReport {
|
|
265
327
|
channels: ChannelInspectChannel[];
|
|
266
328
|
/** Open coalescing windows, per (channel, partition). */
|
|
267
329
|
openCoalesceWindows: ChannelInspectWindow[];
|
|
330
|
+
/** The live action subscriptions, per (moduleId, action, args). */
|
|
331
|
+
liveActions: ChannelInspectLiveAction[];
|
|
268
332
|
totalConnections: number;
|
|
269
333
|
}
|
|
270
334
|
/**
|
|
271
335
|
* Server-side snapshot of the live channels: per channel, the groups
|
|
272
336
|
* identified by (moduleId, partition key), each with its connection count and
|
|
273
|
-
* the timestamp of its last delivery; the coalescing windows still open;
|
|
274
|
-
*
|
|
275
|
-
*
|
|
337
|
+
* the timestamp of its last delivery; the coalescing windows still open; the
|
|
338
|
+
* live action subscriptions (module, action, args and the groups they hold);
|
|
339
|
+
* and the grand totals — where each live subscription counts once, however
|
|
340
|
+
* many channel groups it entered. Always available — the app decides how (and
|
|
341
|
+
* with what guard) to expose it; the dev server exposes it as
|
|
342
|
+
* `GET /_channel-inspect`.
|
|
276
343
|
*/
|
|
277
344
|
export declare function inspectChannels(): ChannelInspectReport;
|
|
278
345
|
/** Test-only. Drops every live connection, group and open window. */
|
|
279
346
|
export declare function __resetChannels(): void;
|
|
280
347
|
/**
|
|
281
|
-
* Executes the connection's
|
|
348
|
+
* Executes the connection's data source (the loader of a page connection, the
|
|
349
|
+
* action of a live subscription) with the connection's own principal and
|
|
282
350
|
* pushes the result as a snapshot. Per-connection execution (rather than one
|
|
283
351
|
* execution per partition) is deliberate: every subscriber always receives the
|
|
284
352
|
* snapshot computed with its own scope, which makes the homogeneous-partition
|
|
@@ -286,8 +354,8 @@ export declare function __resetChannels(): void;
|
|
|
286
354
|
* amortizes is the number of *rounds*, not the executions inside one — a single
|
|
287
355
|
* computation serving the whole partition is a later slice.
|
|
288
356
|
*
|
|
289
|
-
* A
|
|
290
|
-
* instead of dying silently; a
|
|
357
|
+
* A source that throws reports the failure on the connection (`error` event)
|
|
358
|
+
* instead of dying silently; a source that returns a `Response` (a redirect
|
|
291
359
|
* short-circuit) has no state to send.
|
|
292
360
|
*/
|
|
293
361
|
export declare function pushSnapshot(conn: ChannelConnection): Promise<TelemetryStatus>;
|
|
@@ -375,3 +443,41 @@ export declare function parseRouteParamsQuery(raw: string | undefined | null, de
|
|
|
375
443
|
* whose partition no longer holds.
|
|
376
444
|
*/
|
|
377
445
|
export declare function registerChannelRoute(app: Hono, path: string, channel: string, module: RouteModule, moduleId: string, middlewares: MiddlewareHandler[]): void;
|
|
446
|
+
/** Query key carrying the subscription's declared args. */
|
|
447
|
+
export declare const LIVE_ARGS_QUERY = "_args";
|
|
448
|
+
/**
|
|
449
|
+
* The serialized-args ceiling of a live action subscription, in bytes. Args
|
|
450
|
+
* travel on the URL — the ceiling keeps the address an address, not a payload;
|
|
451
|
+
* a bigger read wants an action call plus channels on the page.
|
|
452
|
+
*/
|
|
453
|
+
export declare const LIVE_ARGS_MAX_BYTES = 4096;
|
|
454
|
+
/**
|
|
455
|
+
* Parses and validates the `?_args=` payload of a live action subscription.
|
|
456
|
+
*
|
|
457
|
+
* The client declares the args of the tab it opened; they are the subscription's
|
|
458
|
+
* identity (a change reconnects) and the `body` of every re-execution. An
|
|
459
|
+
* absent payload is the empty object — a live action may take no arguments.
|
|
460
|
+
*/
|
|
461
|
+
export declare function parseLiveArgsQuery(raw: string | undefined | null): {
|
|
462
|
+
args: Record<string, unknown>;
|
|
463
|
+
error: string | null;
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* Registers the internal SSE route of one declared live action:
|
|
467
|
+
* `GET /_live/{moduleId}/{actionName}`, inheriting the route node's
|
|
468
|
+
* middlewares — the subscription is guarded exactly like the action call and
|
|
469
|
+
* the page are.
|
|
470
|
+
*
|
|
471
|
+
* The action runs once on subscribe (the snapshot of connection is the whole
|
|
472
|
+
* loading gesture — no initial POST exists) and once per emit in any of the
|
|
473
|
+
* declared channels, always with the args declared in the subscription as
|
|
474
|
+
* `body`. The subscription enters the groups of every declared channel: an
|
|
475
|
+
* emit in any one delivers.
|
|
476
|
+
*
|
|
477
|
+
* Guards are loud: an action that does not exist in the module, or a channel
|
|
478
|
+
* with no entry in `app/channels.ts`, is an explicit error — never a silent
|
|
479
|
+
* inert route. Args above `LIVE_ARGS_MAX_BYTES` reject the subscription with
|
|
480
|
+
* 400, naming the action and the limit; a scope that resolves to nothing (or
|
|
481
|
+
* throws) denies it with 403.
|
|
482
|
+
*/
|
|
483
|
+
export declare function registerLiveActionRoute(app: Hono, path: string, actionName: string, channels: readonly string[], module: RouteModule, moduleId: string, middlewares: MiddlewareHandler[]): void;
|