@akanjs/cli 3.0.0-alpha.63 → 3.0.0-alpha.64
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/.build-stamp
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
91051910ed4a6b677266a1e02acdb28b3dd899f6d24b642112cf0de3dcb7c1f2
|
|
@@ -369,6 +369,15 @@ workflow changes.
|
|
|
369
369
|
- Slice-level `guards` only reach the generated query/mutation endpoints. A `pubsub`/`message` endpoint is unguarded unless it declares its own `guards` in its signal option.
|
|
370
370
|
- A pubsub room is authorized once, at subscribe. When a socket's credential changes the framework re-runs each room's guards and unsubscribes the ones that now fail (`SignalResolver.revalidateWsRooms`), so guards must stay side-effect free and safe to re-run.
|
|
371
371
|
- A websocket carries its credential in the handshake snapshot on `ws.data` (`AppWsData`); clients that hold the token in memory send it with `fetch.setJwt(...)`, which forwards an auth frame over the socket.
|
|
372
|
+
- **Never read the caller's IP off the socket or the request peer — take `.with(Ip)`.** Every app runs behind
|
|
373
|
+
the federation gateway, so `ws.remoteAddress` and the child's own peer are the *gateway* (`127.0.0.1`) for
|
|
374
|
+
every caller and for the whole life of every socket. `Ip` reads what the gateway recorded
|
|
375
|
+
(`x-real-ip`, then the first `x-forwarded-for` entry) and falls back to the peer only when nothing proxied
|
|
376
|
+
the call; `context.getClientIp()` is the same answer inside a guard or middleware. It arrives unwrapped from
|
|
377
|
+
its `::ffff:` IPv4-mapped form, so it can address a `udp4` socket as well as identify a caller, and it is
|
|
378
|
+
`null` rather than a placeholder when genuinely unknown — a loopback-looking address for an unknown caller
|
|
379
|
+
is the failure this replaced. The gateway also forwards the client port (`x-forwarded-port`, read with
|
|
380
|
+
`context.getClientPort()`) along with host and protocol.
|
|
372
381
|
- **Every socket carries a `socketId`, and only the framework mints one.** `AppWsData` assigns it at the handshake, and a `message` / `pubsub` handler reads it off the `Ws` internal arg — `.with(Ws)` hands `{ ws, socketId, subscribe, on, off }`. Never mint your own from `ws.data`: the room bookkeeping keys on the framework's id, and a second one fails to match it silently. It identifies a **connection**, not a caller — a reconnect gets a new id and the federation gateway's own socket is a different one — so per-user state keys on the account, never on this.
|
|
373
382
|
|
|
374
383
|
### Authorization Defaults
|
|
@@ -389,6 +398,28 @@ workflow changes.
|
|
|
389
398
|
- `.body(...)` / `.param(...)` args accept `ConstantFieldTypeInput` only: scalars, model refs, or `enumOf(...)`.
|
|
390
399
|
- Numbers must use `Int` or `Float` — `Number` is rejected (`pkgs/akanjs/signal/endpointInfo.ts`).
|
|
391
400
|
- `Upload` is valid only inside a mutation flagged for file upload: `mutation([cnst.File], { fileUpload: true }).body("files", [Upload])`, as the `file` module does. It is not a model field type.
|
|
401
|
+
- Bytes are `Binary`, never `Any` — see Scalar & Field Type Reference.
|
|
402
|
+
|
|
403
|
+
### Binary Pubsub
|
|
404
|
+
|
|
405
|
+
- **`pubsub(Binary)` sends its payload in a websocket binary frame**, skipping the JSON `{ type: "pub" }`
|
|
406
|
+
envelope and the base64 a JSON wire would need. The client's subscribe callback receives a `Uint8Array`.
|
|
407
|
+
Nothing else is declared: the return type is the whole switch, and text and binary frames coexist on one
|
|
408
|
+
socket, so every JSON endpoint is untouched. The optimization applies only when the **whole** return is
|
|
409
|
+
`Binary` — `[Binary]`, or a `Binary` inside a model, falls back to base64.
|
|
410
|
+
- **A declared `Binary` room coalesces under backpressure**: while a socket cannot keep up the room keeps only
|
|
411
|
+
its newest frame and drops the rest, which is what a telemetry or video stream wants. Declare
|
|
412
|
+
`pubsub(Binary, { backpressure: "queue" })` when the frames are a sequence a subscriber must see in full, such
|
|
413
|
+
as deltas against a base it already holds; the send buffer then grows with the slowest subscriber. Coalescing
|
|
414
|
+
is keyed by the endpoint, not carried with the frame, so this process, an IPC deliver and a Redis fan-out all
|
|
415
|
+
reach the same answer. Coalesced frames are counted in `pubsubCoalesceCount` on the replica's metrics report.
|
|
416
|
+
- **A `pubsub(Any)` that happens to carry bytes is framed too, and warns once.** `Any` passes a `Buffer` through
|
|
417
|
+
untouched, so the transport sees bytes and sends them whole rather than as a number array — an undeclared
|
|
418
|
+
publisher is fixed rather than left corrupt. It is still not the contract: it queues rather than coalescing
|
|
419
|
+
(nobody declared lossy delivery for that room), and a Redis fan-out still JSON-stringifies it. Declare
|
|
420
|
+
`Binary`.
|
|
421
|
+
- The federation gateway relays a binary frame unchanged, and Bun IPC carries bytes as bytes, so the only hop
|
|
422
|
+
that needed teaching was the socket itself. A cross-server (Redis) fan-out encodes it as protobuf `bytes`.
|
|
392
423
|
|
|
393
424
|
### Mutation HTTP Verb
|
|
394
425
|
|
|
@@ -634,10 +665,18 @@ commands, transcript compaction, and the built-in tool semantics: `get_guideline
|
|
|
634
665
|
|
|
635
666
|
### Scalar & Field Type Reference
|
|
636
667
|
|
|
637
|
-
- **Import from `akanjs/base`** (real classes/helpers, not globals): `Int`, `Float`, `ID`, `Any`, `Upload`, `enumOf`, and the `dayjs` factory. There is **no `JSON` scalar** — use `Any` for open/flexible payloads.
|
|
668
|
+
- **Import from `akanjs/base`** (real classes/helpers, not globals): `Int`, `Float`, `ID`, `Any`, `Binary`, `Upload`, `enumOf`, and the `dayjs` factory. There is **no `JSON` scalar** — use `Any` for open/flexible payloads.
|
|
638
669
|
- **Use the JS globals directly (no import needed)**: `String`, `Boolean`, `Date`. They are monkey-patched to behave like scalars, so `field(String)` typechecks.
|
|
639
670
|
- **`Number` is not a valid field/body type.** `NumberConstructor` is intentionally not augmented, so `field(Number)` / `.body("x", Number)` fails to typecheck. Use `Int` or `Float` instead.
|
|
640
671
|
- Runtime resolution of every scalar (globals included) goes through `PrimitiveRegistry` by `refName` (`pkgs/akanjs/base/primitiveRegistry.ts`).
|
|
672
|
+
- **`Binary` is raw bytes on the wire, and never a model field.** It is `Uint8Array` on both sides — a Node
|
|
673
|
+
`Buffer` is one, so a server handler may pass one straight in — and it accepts base64 in either direction, so
|
|
674
|
+
the same declaration serves a JSON body and a binary frame. It is **not storable**: the class build refuses
|
|
675
|
+
`field(Binary)` naming the `File` model instead, because every non-base field lives in the `_doc` JSON column
|
|
676
|
+
and bytes would sit there as base64 and ride every read of the row. Never send bytes as `Any` — `Any` passes a
|
|
677
|
+
`Buffer` through untouched and `JSON.stringify` then spells it `{ type: "Buffer", data: number[] }`, which
|
|
678
|
+
`JSON.parse` never restores; the result is 3.6x the wire and a shape that only breaks at the first byte-offset
|
|
679
|
+
read. MCP refuses a `Binary` return for the same reason it refuses `Any`.
|
|
641
680
|
|
|
642
681
|
### Text Search Fields — the `text` role
|
|
643
682
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akanjs/cli",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.64",
|
|
4
4
|
"sourceType": "module",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"@langchain/openai": "^1.4.6",
|
|
35
35
|
"@tailwindcss/node": "^4.3.0",
|
|
36
36
|
"@trapezedev/project": "^7.1.4",
|
|
37
|
-
"akanjs": "3.0.0-alpha.
|
|
37
|
+
"akanjs": "3.0.0-alpha.64",
|
|
38
38
|
"chalk": "^5.6.2",
|
|
39
39
|
"commander": "^14.0.3",
|
|
40
40
|
"dayjs": "^1.11.20",
|