@ignex/nova 0.1.3 → 0.1.5

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 (98) hide show
  1. package/README.md +4 -1
  2. package/docs/ai/TREE.md +69 -9
  3. package/docs/architecture.md +75 -27
  4. package/docs/events.md +83 -1
  5. package/docs/generic-bindings.md +10 -0
  6. package/docs/wire-format.md +65 -18
  7. package/package.json +2 -1
  8. package/prebuilds/linux-x64/libignex_ffi.so +0 -0
  9. package/public/generate.ts +97 -3
  10. package/public/server.ts +10 -0
  11. package/rust/src/generated/backend.rs +503 -0
  12. package/rust/src/transcode/generated.rs +376 -17
  13. package/src/bridge/nats/inbound.ts +46 -0
  14. package/src/bridge/nats/index.ts +131 -0
  15. package/src/bridge/nats/real-transport.ts +133 -0
  16. package/src/bridge/nats/types.ts +80 -0
  17. package/src/codegen/constants.ts +14 -4
  18. package/src/codegen/direct-gen.ts +20 -6
  19. package/src/codegen/registry-gen.ts +10 -6
  20. package/src/codegen/rust-glue-gen.ts +10 -3
  21. package/src/codegen/schema-model.ts +28 -3
  22. package/src/codegen/ts-ser-gen.ts +12 -3
  23. package/src/core/auth.ts +65 -4
  24. package/src/core/client-rpc.ts +75 -0
  25. package/src/core/client-state.ts +42 -0
  26. package/src/core/client-wire.ts +142 -8
  27. package/src/core/client.ts +72 -3
  28. package/src/core/groups.ts +5 -0
  29. package/src/core/metrics.ts +38 -21
  30. package/src/core/outbound.ts +50 -6
  31. package/src/core/rate-limit.ts +69 -0
  32. package/src/core/replay.ts +41 -1
  33. package/src/core/resume.ts +181 -0
  34. package/src/core/rooms.ts +10 -3
  35. package/src/core/routing.ts +128 -5
  36. package/src/core/server/client-info.ts +37 -0
  37. package/src/core/server/http-routes.ts +59 -0
  38. package/src/core/{server.ts → server/index.ts} +112 -120
  39. package/src/core/server/metrics-view.ts +53 -0
  40. package/src/core/server/socket-lifecycle.ts +57 -0
  41. package/src/core/state.ts +73 -1
  42. package/src/core/topic-log.ts +86 -0
  43. package/src/events/clients.ts +18 -0
  44. package/src/events/cluster/dedupe.ts +43 -0
  45. package/src/events/cluster/envelope.ts +149 -0
  46. package/src/events/cluster/index.ts +50 -0
  47. package/src/events/cluster/keys.ts +33 -0
  48. package/src/events/cluster/kinds.ts +32 -0
  49. package/src/events/cluster/presence-table.ts +99 -0
  50. package/src/events/cluster/presence.ts +53 -0
  51. package/src/events/cluster/redis-client.ts +50 -0
  52. package/src/events/cluster/store-memory.ts +67 -0
  53. package/src/events/cluster/store-redis.ts +44 -0
  54. package/src/events/cluster/subjects.ts +30 -0
  55. package/src/events/cluster/sync.ts +476 -0
  56. package/src/events/cluster/transport-nats.ts +24 -0
  57. package/src/events/cluster/transport-redis.ts +120 -0
  58. package/src/events/cluster-rpc.ts +196 -0
  59. package/src/events/delivery.ts +83 -0
  60. package/src/events/emit.ts +57 -11
  61. package/src/events/hub/context-factory.ts +79 -0
  62. package/src/events/hub/dispatch.ts +86 -0
  63. package/src/events/hub/index.ts +536 -0
  64. package/src/events/hub/internal.ts +31 -0
  65. package/src/events/hub/metrics-snapshot.ts +84 -0
  66. package/src/events/hub/resolve-cluster.ts +49 -0
  67. package/src/events/queue.ts +36 -9
  68. package/src/events/registry.ts +90 -54
  69. package/src/events/schedule.ts +73 -0
  70. package/src/events/trace.ts +283 -0
  71. package/src/events/types/client.ts +68 -0
  72. package/src/events/types/cluster.ts +40 -0
  73. package/src/events/types/context.ts +50 -0
  74. package/src/events/types/emit-target.ts +29 -0
  75. package/src/events/types/groups.ts +35 -0
  76. package/src/events/types/hub.ts +124 -0
  77. package/src/events/types/index.ts +30 -0
  78. package/src/events/types/metrics.ts +52 -0
  79. package/src/events/types/options.ts +62 -0
  80. package/src/generated/direct-ser.ts +146 -59
  81. package/src/generated/fbs/backend.fbs +23 -0
  82. package/src/generated/registry.ts +92 -33
  83. package/src/generated/rust/backend_generated.rs +503 -0
  84. package/src/generated/ts/backend.ts +4 -0
  85. package/src/generated/ts/resume.ts +74 -0
  86. package/src/generated/ts/resumed.ts +88 -0
  87. package/src/generated/ts/rpc-call.ts +112 -0
  88. package/src/generated/ts/rpc-result.ts +126 -0
  89. package/src/generated/ts/snapshot-request.ts +19 -5
  90. package/src/generated/ts-ser.ts +109 -16
  91. package/src/generated/wire-registry.json +7 -3
  92. package/src/schema/index.ts +45 -1
  93. package/src/transport/transport.ts +117 -77
  94. package/src/bridge/nats.ts +0 -309
  95. package/src/events/cluster.ts +0 -732
  96. package/src/events/hub.ts +0 -481
  97. package/src/events/types.ts +0 -378
  98. package/src/transport/stats.ts +0 -48
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Events-layer metrics snapshot type (folded into `server.getMetrics().events`).
3
+ *
4
+ * Type-only module (part of the `src/events/types` barrel). The assembly
5
+ * runtime lives in `src/events/hub/metrics-snapshot.ts`.
6
+ */
7
+ import type { EmitTargetKind } from "./emit-target";
8
+
9
+ /** Events-layer counters (folded into `server.getMetrics().events`). */
10
+ export interface EventsMetricsSnapshot {
11
+ /** emit calls (all targets) */
12
+ emitted: number;
13
+ emittedByTarget: Record<EmitTargetKind, number>;
14
+ /** frames written to local sockets by the events layer */
15
+ deliveredLocal: number;
16
+ /** frames handed to the cluster transport */
17
+ clusterPublished: number;
18
+ /** frames received from other instances */
19
+ clusterReceived: number;
20
+ /** frames dropped because they originated on this instance */
21
+ clusterDroppedSelf: number;
22
+ /** duplicate cluster messages dropped (broker redelivery dedupe window) */
23
+ clusterDroppedDupe: number;
24
+ /** targeted emits routed ONLY to owning instances instead of the full mesh */
25
+ clusterRouted: number;
26
+ /** cluster transport / decode failures */
27
+ clusterErrors: number;
28
+ /** offload-queue: tasks accepted */
29
+ queueQueued: number;
30
+ /** offload-queue: tasks dropped (overflow) */
31
+ queueDropped: number;
32
+ /** offload-queue: task failures */
33
+ queueErrors: number;
34
+ /** handler exceptions (caught + isolated) */
35
+ handlerErrors: number;
36
+ /** handler retries scheduled by the reliability layer (`handlers.retries`) */
37
+ handlerRetries: number;
38
+ /** events dead-lettered after exhausting the retry budget */
39
+ dlqCount: number;
40
+ /** emits currently scheduled via `hub.schedule` */
41
+ scheduledActive: number;
42
+ /** cross-instance rpc calls sent / received */
43
+ rpcSent: number;
44
+ rpcReceived: number;
45
+ /** local connected clients */
46
+ connectedClients: number;
47
+ /** remote clients known via cluster presence */
48
+ remoteClients: number;
49
+ /** active client groups / user groups */
50
+ clientGroups: number;
51
+ userGroups: number;
52
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Events-layer option types — `createServer({ events })` input.
3
+ *
4
+ * Type-only module (part of the `src/events/types` barrel). Resolved by
5
+ * `createEventsHub` (`src/events/hub/index.ts`).
6
+ */
7
+ import type { Bindings, DefaultBindings, EventNameOf } from "../../bindings/types";
8
+ import type { NatsBridgeOptions, NatsBridge } from "../../bridge/nats";
9
+ import type { ClusterTransport, ClusterStateStore, RedisConnectionOptions } from "./cluster";
10
+ import type { EventClient } from "./client";
11
+ import type { DeliveryPolicy } from "../delivery";
12
+
13
+ /**
14
+ * `createServer({ events: EventsOptions })` — enables the events layer and
15
+ * exposes it as `server.events`. The module-global `emit` / `on` singleton is
16
+ * bound by default (`global: false` to opt out).
17
+ */
18
+ export interface EventsOptions<B extends Bindings = DefaultBindings> {
19
+ /**
20
+ * App events clients are allowed to send to the hub. Default: every event
21
+ * registered via `hub.on(...)` is auto-allowed on first use.
22
+ */
23
+ inbound?: EventNameOf<B>[];
24
+ /** bind the module-global `emit`/`on` singleton (default true) */
25
+ global?: boolean;
26
+ /** called after a connection's client record is attached (seed `data`, …) */
27
+ onConnect?: (client: EventClient) => void;
28
+ /** called after a connection's client record is detached (cleanup) */
29
+ onDisconnect?: (client: EventClient) => void;
30
+ /** handler reliability: retries with backoff + dead-letter sink (default off) */
31
+ handlers?: DeliveryPolicy;
32
+ /** horizontal-scaling sync (server ⇄ server) */
33
+ cluster?: EventsClusterOptions;
34
+ /** offload-queue limits (the queue keeps cluster/state work off hot paths) */
35
+ queue?: { workers?: number; maxPending?: number };
36
+ }
37
+
38
+ export interface EventsClusterOptions {
39
+ /** stable id of this instance (default: random UUID) — self-dedupe */
40
+ instanceId?: string;
41
+ /** subject prefix (default: bindings subject prefix or "ignex") */
42
+ prefix?: string;
43
+ /**
44
+ * NATS-based cluster messaging. `true` reuses the server's NATS bridge
45
+ * connection (set `options.nats` too); pass `NatsBridgeOptions` to create a
46
+ * dedicated bridge; pass a `NatsBridge` to reuse an existing one.
47
+ */
48
+ nats?: boolean | NatsBridgeOptions | NatsBridge;
49
+ /** Redis pub/sub cluster messaging (lazy `ioredis`; subjects mirror NATS) */
50
+ redis?: RedisConnectionOptions;
51
+ /** pluggable messaging transport (tests / custom brokers) */
52
+ transport?: ClusterTransport;
53
+ /**
54
+ * Shared-state store for presence / cluster group membership / client data.
55
+ * Default: per-instance memory. Production: `createRedisStateStore(...)`.
56
+ */
57
+ state?: ClusterStateStore;
58
+ /** presence re-announce + prune cadence (ms, default 15_000) */
59
+ heartbeatMs?: number;
60
+ /** remote presence TTL (ms, default 60_000) */
61
+ presenceTtlMs?: number;
62
+ }
@@ -11,62 +11,78 @@ export type DirectCall = (...args: unknown[]) => number;
11
11
  * output tail `(ptr, usize)` is upgraded to `(buffer, buffer_length)` by
12
12
  * the `abi()` transformer in `src/native/ffi.ts` when supported). */
13
13
  export const directSymbols = {
14
- fb_quote_serialize: {
14
+ "fb_quote_serialize": {
15
15
  args: ["cstring", "f64", "f64", "i64", "i64", "i64", "ptr", "usize"],
16
16
  returns: "u64_fast",
17
17
  },
18
- fb_trade_serialize: {
18
+ "fb_trade_serialize": {
19
19
  args: ["cstring", "f64", "i64", "u32", "i64", "ptr", "usize"],
20
20
  returns: "u64_fast",
21
21
  },
22
- fb_portfolio_serialize: {
22
+ "fb_portfolio_serialize": {
23
23
  args: ["cstring", "buffer", "usize", "f64", "f64", "i64", "cstring", "ptr", "usize"],
24
24
  returns: "u64_fast",
25
25
  },
26
- fb_complex_serialize: {
26
+ "fb_complex_serialize": {
27
27
  args: ["cstring", "buffer", "usize", "buffer", "usize", "buffer", "usize", "buffer", "usize", "buffer", "usize", "u8", "f64", "i64", "ptr", "usize"],
28
28
  returns: "u64_fast",
29
29
  },
30
- fb_big_val_serialize: {
30
+ "fb_big_val_serialize": {
31
31
  args: ["cstring", "i64", "i64", "ptr", "usize"],
32
32
  returns: "u64_fast",
33
33
  },
34
- fb_hello_serialize: {
34
+ "fb_hello_serialize": {
35
35
  args: ["i64", "buffer", "usize", "i64", "ptr", "usize"],
36
36
  returns: "u64_fast",
37
37
  },
38
- fb_welcome_serialize: {
38
+ "fb_welcome_serialize": {
39
39
  args: ["cstring", "buffer", "usize", "ptr", "usize"],
40
40
  returns: "u64_fast",
41
41
  },
42
- fb_subscribe_serialize: {
42
+ "fb_subscribe_serialize": {
43
43
  args: ["cstring", "ptr", "usize"],
44
44
  returns: "u64_fast",
45
45
  },
46
- fb_unsubscribe_serialize: {
46
+ "fb_unsubscribe_serialize": {
47
47
  args: ["cstring", "ptr", "usize"],
48
48
  returns: "u64_fast",
49
49
  },
50
- fb_join_group_serialize: {
50
+ "fb_join_group_serialize": {
51
51
  args: ["cstring", "ptr", "usize"],
52
52
  returns: "u64_fast",
53
53
  },
54
- fb_leave_group_serialize: {
54
+ "fb_leave_group_serialize": {
55
55
  args: ["cstring", "ptr", "usize"],
56
56
  returns: "u64_fast",
57
57
  },
58
- fb_snapshot_request_serialize: {
59
- args: ["cstring", "ptr", "usize"],
58
+ "fb_snapshot_request_serialize": {
59
+ args: ["cstring", "i64", "ptr", "usize"],
60
60
  returns: "u64_fast",
61
61
  },
62
- fb_ping_serialize: {
62
+ "fb_ping_serialize": {
63
63
  args: ["i64", "ptr", "usize"],
64
64
  returns: "u64_fast",
65
65
  },
66
- fb_pong_serialize: {
66
+ "fb_pong_serialize": {
67
67
  args: ["i64", "ptr", "usize"],
68
68
  returns: "u64_fast",
69
69
  },
70
+ "fb_resume_serialize": {
71
+ args: ["i64", "ptr", "usize"],
72
+ returns: "u64_fast",
73
+ },
74
+ "fb_resumed_serialize": {
75
+ args: ["u8", "i64", "ptr", "usize"],
76
+ returns: "u64_fast",
77
+ },
78
+ "fb_rpc_call_serialize": {
79
+ args: ["cstring", "cstring", "cstring", "ptr", "usize"],
80
+ returns: "u64_fast",
81
+ },
82
+ "fb_rpc_result_serialize": {
83
+ args: ["cstring", "u8", "cstring", "cstring", "ptr", "usize"],
84
+ returns: "u64_fast",
85
+ },
70
86
  } as const;
71
87
 
72
88
  /** sample objects for the bind-time self-test (representative values). */
@@ -82,9 +98,13 @@ const directSamples: Record<string, unknown> = {
82
98
  "unsubscribe": { "topic": "SELFTEST" },
83
99
  "joinGroup": { "group": "SELFTEST" },
84
100
  "leaveGroup": { "group": "SELFTEST" },
85
- "snapshotRequest": { "topic": "SELFTEST" },
101
+ "snapshotRequest": { "topic": "SELFTEST", "fromSeq": 123456 },
86
102
  "ping": { "ts": 123456 },
87
103
  "pong": { "ts": 123456 },
104
+ "resume": { "lastSeq": 123456 },
105
+ "resumed": { "ok": true, "from": 123456 },
106
+ "rpcCall": { "id": "SELFTEST", "name": "SELFTEST", "payloadB64": "SELFTEST" },
107
+ "rpcResult": { "id": "SELFTEST", "ok": true, "err": "SELFTEST", "payloadB64": "SELFTEST" },
88
108
  } as const;
89
109
 
90
110
  /** grow the packer scratch, write the u32 count, return the DataView. */
@@ -292,7 +312,8 @@ export function encodeLeaveGroup(call: DirectCall, o: unknown, out: Uint8Array):
292
312
  /** zero-alloc encoder: fields straight into the (reusable) out buffer. */
293
313
  export function encodeSnapshotRequest(call: DirectCall, o: unknown, out: Uint8Array): number {
294
314
  const p = o as ControlEvents["snapshotRequest"];
295
- return call(p.topic, out, out) as number;
315
+ checkInt64("snapshotRequest.fromSeq", p.fromSeq);
316
+ return call(p.topic, p.fromSeq, out, out) as number;
296
317
  }
297
318
 
298
319
  /** zero-alloc encoder: fields straight into the (reusable) out buffer. */
@@ -309,6 +330,32 @@ export function encodePong(call: DirectCall, o: unknown, out: Uint8Array): numbe
309
330
  return call(p.ts, out, out) as number;
310
331
  }
311
332
 
333
+ /** zero-alloc encoder: fields straight into the (reusable) out buffer. */
334
+ export function encodeResume(call: DirectCall, o: unknown, out: Uint8Array): number {
335
+ const p = o as ControlEvents["resume"];
336
+ checkInt64("resume.lastSeq", p.lastSeq);
337
+ return call(p.lastSeq, out, out) as number;
338
+ }
339
+
340
+ /** zero-alloc encoder: fields straight into the (reusable) out buffer. */
341
+ export function encodeResumed(call: DirectCall, o: unknown, out: Uint8Array): number {
342
+ const p = o as ControlEvents["resumed"];
343
+ checkInt64("resumed.from", p.from);
344
+ return call(p.ok ? 1 : 0, p.from, out, out) as number;
345
+ }
346
+
347
+ /** zero-alloc encoder: fields straight into the (reusable) out buffer. */
348
+ export function encodeRpcCall(call: DirectCall, o: unknown, out: Uint8Array): number {
349
+ const p = o as ControlEvents["rpcCall"];
350
+ return call(p.id, p.name, p.payloadB64, out, out) as number;
351
+ }
352
+
353
+ /** zero-alloc encoder: fields straight into the (reusable) out buffer. */
354
+ export function encodeRpcResult(call: DirectCall, o: unknown, out: Uint8Array): number {
355
+ const p = o as ControlEvents["rpcResult"];
356
+ return call(p.id, p.ok ? 1 : 0, p.err, p.payloadB64, out, out) as number;
357
+ }
358
+
312
359
  function hasNulQuote(o: unknown): boolean {
313
360
  const p = o as Events["quote"];
314
361
  return (
@@ -407,58 +454,98 @@ function hasNulPong(o: unknown): boolean {
407
454
  return false;
408
455
  }
409
456
 
457
+ function hasNulResume(o: unknown): boolean {
458
+ const p = o as ControlEvents["resume"];
459
+ return false;
460
+ }
461
+
462
+ function hasNulResumed(o: unknown): boolean {
463
+ const p = o as ControlEvents["resumed"];
464
+ return false;
465
+ }
466
+
467
+ function hasNulRpcCall(o: unknown): boolean {
468
+ const p = o as ControlEvents["rpcCall"];
469
+ return (
470
+ (typeof p.id === "string" && p.id.includes("\0")) ||
471
+ (typeof p.name === "string" && p.name.includes("\0")) ||
472
+ (typeof p.payloadB64 === "string" && p.payloadB64.includes("\0"))
473
+ );
474
+ }
475
+
476
+ function hasNulRpcResult(o: unknown): boolean {
477
+ const p = o as ControlEvents["rpcResult"];
478
+ return (
479
+ (typeof p.id === "string" && p.id.includes("\0")) ||
480
+ (typeof p.err === "string" && p.err.includes("\0")) ||
481
+ (typeof p.payloadB64 === "string" && p.payloadB64.includes("\0"))
482
+ );
483
+ }
484
+
410
485
  /** per-event NUL pre-scan — true routes the payload to the JSON path (cstring truncates `\0`). */
411
486
  export const hasNulEncoders: Partial<Record<AnyEventName, (o: unknown) => boolean>> = {
412
- quote: hasNulQuote,
413
- trade: hasNulTrade,
414
- portfolio: hasNulPortfolio,
415
- complex: hasNulComplex,
416
- bigVal: hasNulBigVal,
417
- hello: hasNulHello,
418
- welcome: hasNulWelcome,
419
- subscribe: hasNulSubscribe,
420
- unsubscribe: hasNulUnsubscribe,
421
- joinGroup: hasNulJoinGroup,
422
- leaveGroup: hasNulLeaveGroup,
423
- snapshotRequest: hasNulSnapshotRequest,
424
- ping: hasNulPing,
425
- pong: hasNulPong,
487
+ "quote": hasNulQuote,
488
+ "trade": hasNulTrade,
489
+ "portfolio": hasNulPortfolio,
490
+ "complex": hasNulComplex,
491
+ "bigVal": hasNulBigVal,
492
+ "hello": hasNulHello,
493
+ "welcome": hasNulWelcome,
494
+ "subscribe": hasNulSubscribe,
495
+ "unsubscribe": hasNulUnsubscribe,
496
+ "joinGroup": hasNulJoinGroup,
497
+ "leaveGroup": hasNulLeaveGroup,
498
+ "snapshotRequest": hasNulSnapshotRequest,
499
+ "ping": hasNulPing,
500
+ "pong": hasNulPong,
501
+ "resume": hasNulResume,
502
+ "resumed": hasNulResumed,
503
+ "rpcCall": hasNulRpcCall,
504
+ "rpcResult": hasNulRpcResult,
426
505
  };
427
506
 
428
507
  export const directSymbolNames: Partial<Record<AnyEventName, string>> = {
429
- quote: "fb_quote_serialize",
430
- trade: "fb_trade_serialize",
431
- portfolio: "fb_portfolio_serialize",
432
- complex: "fb_complex_serialize",
433
- bigVal: "fb_big_val_serialize",
434
- hello: "fb_hello_serialize",
435
- welcome: "fb_welcome_serialize",
436
- subscribe: "fb_subscribe_serialize",
437
- unsubscribe: "fb_unsubscribe_serialize",
438
- joinGroup: "fb_join_group_serialize",
439
- leaveGroup: "fb_leave_group_serialize",
440
- snapshotRequest: "fb_snapshot_request_serialize",
441
- ping: "fb_ping_serialize",
442
- pong: "fb_pong_serialize",
508
+ "quote": "fb_quote_serialize",
509
+ "trade": "fb_trade_serialize",
510
+ "portfolio": "fb_portfolio_serialize",
511
+ "complex": "fb_complex_serialize",
512
+ "bigVal": "fb_big_val_serialize",
513
+ "hello": "fb_hello_serialize",
514
+ "welcome": "fb_welcome_serialize",
515
+ "subscribe": "fb_subscribe_serialize",
516
+ "unsubscribe": "fb_unsubscribe_serialize",
517
+ "joinGroup": "fb_join_group_serialize",
518
+ "leaveGroup": "fb_leave_group_serialize",
519
+ "snapshotRequest": "fb_snapshot_request_serialize",
520
+ "ping": "fb_ping_serialize",
521
+ "pong": "fb_pong_serialize",
522
+ "resume": "fb_resume_serialize",
523
+ "resumed": "fb_resumed_serialize",
524
+ "rpcCall": "fb_rpc_call_serialize",
525
+ "rpcResult": "fb_rpc_result_serialize",
443
526
  };
444
527
 
445
528
  export type DirectEncoder = (call: DirectCall, o: unknown, out: Uint8Array) => number;
446
529
 
447
530
  export const directEncoders: Partial<Record<AnyEventName, DirectEncoder>> = {
448
- quote: encodeQuote,
449
- trade: encodeTrade,
450
- portfolio: encodePortfolio,
451
- complex: encodeComplex,
452
- bigVal: encodeBigVal,
453
- hello: encodeHello,
454
- welcome: encodeWelcome,
455
- subscribe: encodeSubscribe,
456
- unsubscribe: encodeUnsubscribe,
457
- joinGroup: encodeJoinGroup,
458
- leaveGroup: encodeLeaveGroup,
459
- snapshotRequest: encodeSnapshotRequest,
460
- ping: encodePing,
461
- pong: encodePong,
531
+ "quote": encodeQuote,
532
+ "trade": encodeTrade,
533
+ "portfolio": encodePortfolio,
534
+ "complex": encodeComplex,
535
+ "bigVal": encodeBigVal,
536
+ "hello": encodeHello,
537
+ "welcome": encodeWelcome,
538
+ "subscribe": encodeSubscribe,
539
+ "unsubscribe": encodeUnsubscribe,
540
+ "joinGroup": encodeJoinGroup,
541
+ "leaveGroup": encodeLeaveGroup,
542
+ "snapshotRequest": encodeSnapshotRequest,
543
+ "ping": encodePing,
544
+ "pong": encodePong,
545
+ "resume": encodeResume,
546
+ "resumed": encodeResumed,
547
+ "rpcCall": encodeRpcCall,
548
+ "rpcResult": encodeRpcResult,
462
549
  };
463
550
 
464
551
  /**
@@ -128,6 +128,7 @@ table LeaveGroup {
128
128
 
129
129
  table SnapshotRequest {
130
130
  topic:string;
131
+ from_seq:int64;
131
132
  }
132
133
 
133
134
  table Ping {
@@ -137,3 +138,25 @@ table Ping {
137
138
  table Pong {
138
139
  ts:int64;
139
140
  }
141
+
142
+ table Resume {
143
+ last_seq:int64;
144
+ }
145
+
146
+ table Resumed {
147
+ ok:bool;
148
+ from:int64;
149
+ }
150
+
151
+ table RpcCall {
152
+ id:string;
153
+ name:string;
154
+ payload_b64:string;
155
+ }
156
+
157
+ table RpcResult {
158
+ id:string;
159
+ ok:bool;
160
+ err:string;
161
+ payload_b64:string;
162
+ }
@@ -23,6 +23,10 @@ import {
23
23
  SnapshotRequest,
24
24
  Ping,
25
25
  Pong,
26
+ Resume,
27
+ Resumed,
28
+ RpcCall,
29
+ RpcResult,
26
30
  type QuoteT,
27
31
  type TradeT,
28
32
  type PortfolioSnapshotT,
@@ -42,51 +46,63 @@ import {
42
46
  type SnapshotRequestT,
43
47
  type PingT,
44
48
  type PongT,
49
+ type ResumeT,
50
+ type ResumedT,
51
+ type RpcCallT,
52
+ type RpcResultT,
45
53
  } from "./ts/backend";
46
54
  import type { AnyEventName, ControlEventName, ControlEvents, Events, EventName } from "../schema";
47
55
 
48
- export const WIRE_VERSION = 1;
49
- export const WIRE_HEADER_LEN = 5; // [version:1][event_id:u32 LE]
50
- export const SCHEMA_FINGERPRINT = 1656642724; // fnv1a32(canonical model)
56
+ export const WIRE_VERSION = 2;
57
+ export const WIRE_HEADER_LEN = 14; // [version:1][event_id:u32 LE]
58
+ export const SCHEMA_FINGERPRINT = 3647747841; // fnv1a32(canonical model)
51
59
 
52
60
  export const eventNameToId: Record<EventName, number> = {
53
- quote: 2995289047, // fnv1a32("quote")
54
- trade: 1362521689, // fnv1a32("trade")
55
- portfolio: 464274147, // fnv1a32("portfolio")
56
- complex: 2318166199, // fnv1a32("complex")
57
- order: 1932267671, // fnv1a32("order")
58
- bigVal: 3211729064, // fnv1a32("bigVal")
61
+ "quote": 2995289047, // fnv1a32("quote")
62
+ "trade": 1362521689, // fnv1a32("trade")
63
+ "portfolio": 464274147, // fnv1a32("portfolio")
64
+ "complex": 2318166199, // fnv1a32("complex")
65
+ "order": 1932267671, // fnv1a32("order")
66
+ "bigVal": 3211729064, // fnv1a32("bigVal")
59
67
  };
60
68
 
61
69
  export const controlEventNameToId: Record<ControlEventName, number> = {
62
- hello: 1335831723, // fnv1a32("hello")
63
- welcome: 669089267, // fnv1a32("welcome")
64
- subscribe: 2946386435, // fnv1a32("subscribe")
65
- unsubscribe: 4190043798, // fnv1a32("unsubscribe")
66
- joinGroup: 3026600144, // fnv1a32("joinGroup")
67
- leaveGroup: 3755061443, // fnv1a32("leaveGroup")
68
- snapshotRequest: 1249175018, // fnv1a32("snapshotRequest")
69
- ping: 375255177, // fnv1a32("ping")
70
- pong: 2061178551, // fnv1a32("pong")
70
+ "hello": 1335831723, // fnv1a32("hello")
71
+ "welcome": 669089267, // fnv1a32("welcome")
72
+ "subscribe": 2946386435, // fnv1a32("subscribe")
73
+ "unsubscribe": 4190043798, // fnv1a32("unsubscribe")
74
+ "joinGroup": 3026600144, // fnv1a32("joinGroup")
75
+ "leaveGroup": 3755061443, // fnv1a32("leaveGroup")
76
+ "snapshotRequest": 1249175018, // fnv1a32("snapshotRequest")
77
+ "ping": 375255177, // fnv1a32("ping")
78
+ "pong": 2061178551, // fnv1a32("pong")
79
+ "resume": 4281967466, // fnv1a32("resume")
80
+ "resumed": 3585847050, // fnv1a32("resumed")
81
+ "rpcCall": 986404564, // fnv1a32("rpcCall")
82
+ "rpcResult": 648609069, // fnv1a32("rpcResult")
71
83
  };
72
84
 
73
85
  /** merged app + control registry (used by encodeToScratch / JS encoder). */
74
86
  export const anyEventNameToId: Record<string, number> = {
75
- quote: 2995289047,
76
- trade: 1362521689,
77
- portfolio: 464274147,
78
- complex: 2318166199,
79
- order: 1932267671,
80
- bigVal: 3211729064,
81
- hello: 1335831723,
82
- welcome: 669089267,
83
- subscribe: 2946386435,
84
- unsubscribe: 4190043798,
85
- joinGroup: 3026600144,
86
- leaveGroup: 3755061443,
87
- snapshotRequest: 1249175018,
88
- ping: 375255177,
89
- pong: 2061178551,
87
+ "quote": 2995289047,
88
+ "trade": 1362521689,
89
+ "portfolio": 464274147,
90
+ "complex": 2318166199,
91
+ "order": 1932267671,
92
+ "bigVal": 3211729064,
93
+ "hello": 1335831723,
94
+ "welcome": 669089267,
95
+ "subscribe": 2946386435,
96
+ "unsubscribe": 4190043798,
97
+ "joinGroup": 3026600144,
98
+ "leaveGroup": 3755061443,
99
+ "snapshotRequest": 1249175018,
100
+ "ping": 375255177,
101
+ "pong": 2061178551,
102
+ "resume": 4281967466,
103
+ "resumed": 3585847050,
104
+ "rpcCall": 986404564,
105
+ "rpcResult": 648609069,
90
106
  };
91
107
 
92
108
  export const idToEventName: Record<number, EventName> = {
@@ -115,6 +131,10 @@ export const idToAnyEventName: Record<number, AnyEventName> = {
115
131
  1249175018: "snapshotRequest",
116
132
  375255177: "ping",
117
133
  2061178551: "pong",
134
+ 4281967466: "resume",
135
+ 3585847050: "resumed",
136
+ 986404564: "rpcCall",
137
+ 648609069: "rpcResult",
118
138
  };
119
139
 
120
140
  export type DecodeFn = (bb: flatbuffers.ByteBuffer) => unknown;
@@ -135,6 +155,10 @@ export const eventIdToDecoder: Record<number, DecodeFn> = {
135
155
  1249175018: (bb) => snapshot_requestToPlain(SnapshotRequest.getSizePrefixedRootAsSnapshotRequest(bb).unpack()),
136
156
  375255177: (bb) => pingToPlain(Ping.getSizePrefixedRootAsPing(bb).unpack()),
137
157
  2061178551: (bb) => pongToPlain(Pong.getSizePrefixedRootAsPong(bb).unpack()),
158
+ 4281967466: (bb) => resumeToPlain(Resume.getSizePrefixedRootAsResume(bb).unpack()),
159
+ 3585847050: (bb) => resumedToPlain(Resumed.getSizePrefixedRootAsResumed(bb).unpack()),
160
+ 986404564: (bb) => rpc_callToPlain(RpcCall.getSizePrefixedRootAsRpcCall(bb).unpack()),
161
+ 648609069: (bb) => rpc_resultToPlain(RpcResult.getSizePrefixedRootAsRpcResult(bb).unpack()),
138
162
  };
139
163
 
140
164
  const controlIds = new Set<number>([
@@ -147,6 +171,10 @@ const controlIds = new Set<number>([
147
171
  1249175018,
148
172
  375255177,
149
173
  2061178551,
174
+ 4281967466,
175
+ 3585847050,
176
+ 986404564,
177
+ 648609069,
150
178
  ]);
151
179
  export function isControlId(id: number): boolean { return controlIds.has(id); }
152
180
 
@@ -327,6 +355,7 @@ function leave_groupToPlain(o: LeaveGroupT): ControlEvents["leaveGroup"] {
327
355
  function snapshot_requestToPlain(o: SnapshotRequestT): ControlEvents["snapshotRequest"] {
328
356
  return {
329
357
  topic: str(o.topic),
358
+ fromSeq: Number(o.fromSeq),
330
359
  };
331
360
  }
332
361
 
@@ -341,3 +370,33 @@ function pongToPlain(o: PongT): ControlEvents["pong"] {
341
370
  ts: Number(o.ts),
342
371
  };
343
372
  }
373
+
374
+ function resumeToPlain(o: ResumeT): ControlEvents["resume"] {
375
+ return {
376
+ lastSeq: Number(o.lastSeq),
377
+ };
378
+ }
379
+
380
+ function resumedToPlain(o: ResumedT): ControlEvents["resumed"] {
381
+ return {
382
+ ok: o.ok,
383
+ from: Number(o.from),
384
+ };
385
+ }
386
+
387
+ function rpc_callToPlain(o: RpcCallT): ControlEvents["rpcCall"] {
388
+ return {
389
+ id: str(o.id),
390
+ name: str(o.name),
391
+ payloadB64: str(o.payloadB64),
392
+ };
393
+ }
394
+
395
+ function rpc_resultToPlain(o: RpcResultT): ControlEvents["rpcResult"] {
396
+ return {
397
+ id: str(o.id),
398
+ ok: o.ok,
399
+ err: str(o.err),
400
+ payloadB64: str(o.payloadB64),
401
+ };
402
+ }