@portalsdk/wire-protocol 0.2.0 → 0.4.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/README.md +1 -1
- package/dist/index.cjs +7 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +105 -19
- package/dist/index.d.ts +105 -19
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -66,7 +66,7 @@ rather than a silent 4xx.
|
|
|
66
66
|
```ts
|
|
67
67
|
import { PROTOCOL_VERSION, UPGRADE_PARAMS } from "@portalsdk/wire-protocol";
|
|
68
68
|
|
|
69
|
-
const url = new URL(`wss://realtime.useportal.co/channels/${channelId}`);
|
|
69
|
+
const url = new URL(`wss://realtime.useportal.co/v1/channels/${channelId}`);
|
|
70
70
|
url.searchParams.set(UPGRADE_PARAMS.version, String(PROTOCOL_VERSION));
|
|
71
71
|
url.searchParams.set(UPGRADE_PARAMS.token, jwt);
|
|
72
72
|
```
|
package/dist/index.cjs
CHANGED
|
@@ -5,6 +5,7 @@ var PROTOCOL_VERSION = 1;
|
|
|
5
5
|
var UPGRADE_PARAMS = {
|
|
6
6
|
version: "v",
|
|
7
7
|
token: "token",
|
|
8
|
+
key: "key",
|
|
8
9
|
leaf: "leaf",
|
|
9
10
|
meta: "meta",
|
|
10
11
|
last: "last"
|
|
@@ -36,11 +37,11 @@ var arrayOf = (v, check) => Array.isArray(v) && v.every(check);
|
|
|
36
37
|
var isStrRec = (v) => isRec(v) && Object.values(v).every(isStr);
|
|
37
38
|
var isMention = (v) => isRec(v) && isStr(v["userId"]);
|
|
38
39
|
var isWireSender = (v) => isRec(v) && isStr(v["id"]) && isBool(v["anon"]) && opt(v["username"], isStr);
|
|
39
|
-
var isWireMessage = (v) => isRec(v) && isStr(v["id"]) && (v["seq"] === null || isNum(v["seq"])) && isStr(v["type"]) && isStr(v["kind"]) && "content" in v && isWireSender(v["sender"]) && isNum(v["timestamp"]) && opt(v["to"], isStr) && (v["mentions"] === void 0 || arrayOf(v["mentions"], isMention)) && isBool(v["retracted"]) && isBool(v["ephemeral"]);
|
|
40
|
+
var isWireMessage = (v) => isRec(v) && isStr(v["id"]) && (v["seq"] === null || isNum(v["seq"])) && isStr(v["type"]) && isStr(v["kind"]) && "content" in v && isWireSender(v["sender"]) && isNum(v["timestamp"]) && opt(v["to"], isStr) && (v["mentions"] === void 0 || arrayOf(v["mentions"], isMention)) && isBool(v["retracted"]) && isBool(v["ephemeral"]) && opt(v["threadParentId"], isStr) && opt(v["threadSeq"], isNum);
|
|
40
41
|
var isChannelInfo = (v) => isRec(v) && isStr(v["id"]) && (v["mode"] === "standard" || v["mode"] === "broadcast") && opt(v["name"], isStr) && opt(v["meta"], isRec);
|
|
41
42
|
var isCapabilities = (v) => isRec(v) && opt(v["publish"], isBool) && opt(v["sendDirect"], isBool);
|
|
42
43
|
var isMeInfo = (v) => isRec(v) && isStr(v["id"]) && isBool(v["anon"]) && isRec(v["claims"]) && isCapabilities(v["capabilities"]);
|
|
43
|
-
var isParticipant = (v) => isRec(v) && isStr(v["
|
|
44
|
+
var isParticipant = (v) => isRec(v) && isStr(v["id"]) && isBool(v["anon"]) && opt(v["username"], isStr) && opt(v["metadata"], isRec);
|
|
44
45
|
var isReadyPresence = (v) => {
|
|
45
46
|
if (!isRec(v)) return false;
|
|
46
47
|
if (v["mode"] === "detailed") {
|
|
@@ -51,7 +52,7 @@ var isReadyPresence = (v) => {
|
|
|
51
52
|
}
|
|
52
53
|
return false;
|
|
53
54
|
};
|
|
54
|
-
var isInboxEntryWire = (v) => isRec(v) && isStr(v["id"]) && opt(v["name"], isStr) && opt(v["meta"], isRec) && opt(v["latest"], isLatest) && isNum(v["unread"]) && isBool(v["muted"]) && isNum(v["at"]);
|
|
55
|
+
var isInboxEntryWire = (v) => isRec(v) && isStr(v["id"]) && opt(v["threadId"], isStr) && opt(v["parentThreadId"], isStr) && opt(v["rootThreadId"], isStr) && opt(v["name"], isStr) && opt(v["meta"], isRec) && opt(v["latest"], isLatest) && isNum(v["unread"]) && isBool(v["muted"]) && isNum(v["at"]);
|
|
55
56
|
var isLatest = (v) => isRec(v) && isStr(v["text"]) && isRec(v["sender"]) && isStr(v["sender"]["id"]) && isNum(v["at"]);
|
|
56
57
|
var isInboxItemWire = (v) => isRec(v) && isStr(v["id"]) && isStr(v["type"]) && opt(v["title"], isStr) && "data" in v && opt(v["channelId"], isStr) && isNum(v["at"]) && isBool(v["read"]);
|
|
57
58
|
var CHANNEL_SERVER_FRAMES = {
|
|
@@ -60,7 +61,7 @@ var CHANNEL_SERVER_FRAMES = {
|
|
|
60
61
|
retract: (v) => isStr(v["id"]) && isNum(v["seq"]) && opt(v["reason"], isStr),
|
|
61
62
|
presence: (v) => {
|
|
62
63
|
if (v["mode"] === "detailed") {
|
|
63
|
-
return arrayOf(v["joined"], isParticipant) && arrayOf(v["left"],
|
|
64
|
+
return arrayOf(v["joined"], isParticipant) && arrayOf(v["left"], isStr) && isNum(v["count"]);
|
|
64
65
|
}
|
|
65
66
|
if (v["mode"] === "aggregate") {
|
|
66
67
|
return isNum(v["count"]) && Array.isArray(v["recent"]);
|
|
@@ -88,10 +89,10 @@ var CHANNEL_CLIENT_FRAMES = {
|
|
|
88
89
|
ping: () => true
|
|
89
90
|
};
|
|
90
91
|
var INBOX_CLIENT_FRAMES = {
|
|
91
|
-
read: (v) => isStr(v["channelId"]),
|
|
92
|
+
read: (v) => isStr(v["channelId"]) && opt(v["threadId"], isStr),
|
|
92
93
|
"item.read": (v) => isStr(v["id"]),
|
|
93
94
|
"read.all": () => true,
|
|
94
|
-
mute: (v) => isStr(v["channelId"]) && isBool(v["muted"]),
|
|
95
|
+
mute: (v) => isStr(v["channelId"]) && isBool(v["muted"]) && opt(v["threadId"], isStr),
|
|
95
96
|
ping: () => true
|
|
96
97
|
};
|
|
97
98
|
var validatorFor = (table, t) => Object.hasOwn(table, t) ? table[t] : void 0;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/parse.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Protocol version carried by every upgrade as `?v=` (§1.1, §6).\n *\n * An unknown version is refused at the upgrade with HTTP 426 `unsupported_version`.\n * Within v1 the protocol evolves additively only; a breaking change bumps this.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Upgrade query-parameter names (§1.1). Build upgrade URLs from these rather than\n * string literals, so a rename is a compile error rather than a silent 4xx.\n *\n * - `version` — required on every upgrade; unknown → 426.\n * - `token` — the signed JWT. Identifies the user; the apiKey is resolved from it.\n * - `leaf` — opaque reconnect token; echo back what `ready` gave you, unchanged.\n * - `meta` — initial presence metadata, base64 JSON (standard channels; ≤1KB decoded).\n * - `last` — highest contiguous seq held, sent on reconnect to request replay (§1.4).\n */\nexport const UPGRADE_PARAMS = {\n version: \"v\",\n token: \"token\",\n leaf: \"leaf\",\n meta: \"meta\",\n last: \"last\",\n} as const;\n\n/**\n * Response header carrying the refusal code on a refused upgrade (§1.1).\n *\n * Duplicates `code` from the body so a client behind a body-eating proxy can still\n * tell why the socket never opened.\n */\nexport const PORTAL_ERROR_HEADER = \"x-portal-error\";\n","/**\n * Why an upgrade was refused (§1.1).\n *\n * Refusals happen at the HTTP upgrade — **the socket never opens**. They are therefore\n * disjoint from {@link PublishErrorCode} (HTTP publish rejections) and from the\n * in-session `error` frame, which both presuppose a working connection.\n */\nexport type RefusalCode =\n | \"invalid_token\"\n | \"token_expired\"\n | \"invalid_api_key\"\n | \"not_member\"\n | \"banned\"\n | \"anonymous_not_allowed\"\n | \"unknown_channel\"\n | \"unsupported_version\"\n | \"channel_at_capacity\";\n\n/**\n * The HTTP status each refusal is delivered with (§1.1 table).\n *\n * The mapping is many-to-one: status alone does not identify the cause, so read\n * `code` from the body (or the `x-portal-error` header) rather than branching on status.\n */\nexport const REFUSAL_STATUS = {\n invalid_token: 401,\n token_expired: 401,\n invalid_api_key: 403,\n not_member: 403,\n banned: 403,\n anonymous_not_allowed: 403,\n unknown_channel: 404,\n unsupported_version: 426,\n channel_at_capacity: 429,\n} as const satisfies Record<RefusalCode, number>;\n\nconst REFUSAL_CODES: ReadonlySet<string> = new Set(Object.keys(REFUSAL_STATUS));\n\n/**\n * Whether a value is a refusal code this version knows (§1.1).\n *\n * A refusal body arriving with an unrecognised code is not a refusal this client can\n * reason about — treat it as an opaque failure rather than coercing it.\n */\nexport const isRefusalCode = (value: unknown): value is RefusalCode =>\n typeof value === \"string\" && REFUSAL_CODES.has(value);\n\n/** Body of a refused upgrade (§1.1). */\nexport type RefusalBody = {\n code: RefusalCode;\n reason?: string;\n /**\n * Seconds to wait before retrying. Documented for `channel_at_capacity` (429) only\n * (§1.1); absent on every other refusal.\n */\n retryAfter?: number;\n};\n\n/**\n * Why an HTTP publish was rejected (§3.1).\n *\n * Deliberately NOT merged into {@link RefusalCode}: these arrive on a live connection\n * in response to `POST /v1/channels/{id}/messages`, and a client reacts to them per\n * send, not per connection.\n *\n * `blocked_by_middleware` carries user-visible copy in `reason`.\n */\nexport type PublishErrorCode =\n | \"not_permitted\"\n | \"blocked_by_middleware\"\n | \"content_too_large\"\n | \"rate_limited\";\n\n/**\n * Body of a rejected publish (§3.1).\n *\n * SPEC: §3.1 specifies the shape as `4xx { code, reason? }` but does not map each code\n * to a status, so no status record is exported here (unlike {@link REFUSAL_STATUS}).\n */\nexport type PublishErrorBody = {\n code: PublishErrorCode;\n reason?: string;\n};\n","import type {\n ActivityFrame,\n BatchFrame,\n ChannelClientFrame,\n ChannelReadyFrame,\n ChannelServerFrame,\n Capabilities,\n ChannelInfo,\n DirectFrame,\n ErrorFrame,\n MeInfo,\n PongFrame,\n PresenceFrame,\n ReadyPresence,\n ReassignFrame,\n RetractFrame,\n WirePresenceParticipant,\n} from \"./channel.js\";\nimport type {\n InboxClientFrame,\n InboxCounterFrame,\n InboxEntryFrame,\n InboxEntryWire,\n InboxItemFrame,\n InboxItemWire,\n InboxReadyFrame,\n InboxServerFrame,\n} from \"./inbox.js\";\nimport type {\n AnyFrame,\n ParsedChannelClientFrame,\n ParsedChannelFrame,\n ParsedInboxClientFrame,\n ParsedInboxFrame,\n UnknownFrame,\n} from \"./frames.js\";\nimport type { Mention, WireMessage, WireSender } from \"./message.js\";\n\n// ============================================================\n// Primitives\n// ============================================================\n\ntype Rec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is Rec =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nconst isStr = (v: unknown): v is string => typeof v === \"string\";\nconst isBool = (v: unknown): v is boolean => typeof v === \"boolean\";\n\n/** JSON has no NaN/Infinity, so any non-finite number means a hand-built object. */\nconst isNum = (v: unknown): v is number =>\n typeof v === \"number\" && Number.isFinite(v);\n\n/** An absent optional field is valid; a present one must match. */\nconst opt = <T>(v: unknown, check: (x: unknown) => x is T): boolean =>\n v === undefined || check(v);\n\nconst arrayOf = <T>(v: unknown, check: (x: unknown) => x is T): v is T[] =>\n Array.isArray(v) && v.every(check);\n\nconst isStrRec = (v: unknown): v is Record<string, string> =>\n isRec(v) && Object.values(v).every(isStr);\n\n// ============================================================\n// Shape validators\n//\n// Each validator checks exactly the fields its type declares required — no more, no\n// less. Unknown fields are ignored and survive untouched (§6), but a frame missing a\n// required field is rejected rather than returned as a type that lies about it.\n// ============================================================\n\nconst isMention = (v: unknown): v is Mention => isRec(v) && isStr(v[\"userId\"]);\n\nconst isWireSender = (v: unknown): v is WireSender =>\n isRec(v) && isStr(v[\"id\"]) && isBool(v[\"anon\"]) && opt(v[\"username\"], isStr);\n\nconst isWireMessage = (v: unknown): v is WireMessage =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"seq\"] === null || isNum(v[\"seq\"])) &&\n isStr(v[\"type\"]) &&\n isStr(v[\"kind\"]) &&\n \"content\" in v &&\n isWireSender(v[\"sender\"]) &&\n isNum(v[\"timestamp\"]) &&\n opt(v[\"to\"], isStr) &&\n (v[\"mentions\"] === undefined || arrayOf(v[\"mentions\"], isMention)) &&\n isBool(v[\"retracted\"]) &&\n isBool(v[\"ephemeral\"]);\n\nconst isChannelInfo = (v: unknown): v is ChannelInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"mode\"] === \"standard\" || v[\"mode\"] === \"broadcast\") &&\n opt(v[\"name\"], isStr) &&\n opt(v[\"meta\"], isRec);\n\nconst isCapabilities = (v: unknown): v is Capabilities =>\n isRec(v) && opt(v[\"publish\"], isBool) && opt(v[\"sendDirect\"], isBool);\n\nconst isMeInfo = (v: unknown): v is MeInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isBool(v[\"anon\"]) &&\n isRec(v[\"claims\"]) &&\n isCapabilities(v[\"capabilities\"]);\n\nconst isParticipant = (v: unknown): v is WirePresenceParticipant =>\n isRec(v) && isStr(v[\"userId\"]) && isRec(v[\"claims\"]);\n\nconst isReadyPresence = (v: unknown): v is ReadyPresence => {\n if (!isRec(v)) return false;\n if (v[\"mode\"] === \"detailed\") {\n return arrayOf(v[\"participants\"], isParticipant) && isNum(v[\"count\"]);\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && (v[\"recent\"] === undefined || Array.isArray(v[\"recent\"]));\n }\n return false;\n};\n\nconst isInboxEntryWire = (v: unknown): v is InboxEntryWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n opt(v[\"name\"], isStr) &&\n opt(v[\"meta\"], isRec) &&\n opt(v[\"latest\"], isLatest) &&\n isNum(v[\"unread\"]) &&\n isBool(v[\"muted\"]) &&\n isNum(v[\"at\"]);\n\nconst isLatest = (v: unknown): v is InboxEntryWire[\"latest\"] =>\n isRec(v) &&\n isStr(v[\"text\"]) &&\n isRec(v[\"sender\"]) &&\n isStr((v[\"sender\"] as Rec)[\"id\"]) &&\n isNum(v[\"at\"]);\n\nconst isInboxItemWire = (v: unknown): v is InboxItemWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isStr(v[\"type\"]) &&\n opt(v[\"title\"], isStr) &&\n \"data\" in v &&\n opt(v[\"channelId\"], isStr) &&\n isNum(v[\"at\"]) &&\n isBool(v[\"read\"]);\n\n// ============================================================\n// Frame tables — the single source of truth for \"known `t`\"\n// ============================================================\n\ntype Validator = (v: Rec) => boolean;\n\nconst CHANNEL_SERVER_FRAMES: Record<ChannelServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n isChannelInfo(v[\"channel\"]) &&\n isMeInfo(v[\"me\"]) &&\n isNum(v[\"seq\"]) &&\n isStr(v[\"leaf\"]) &&\n isReadyPresence(v[\"presence\"]) &&\n opt(v[\"watermark\"], isNum) &&\n opt(v[\"ext\"], isRec) &&\n opt(v[\"bindings\"], isStrRec),\n batch: (v) => arrayOf(v[\"msgs\"], isWireMessage),\n retract: (v) => isStr(v[\"id\"]) && isNum(v[\"seq\"]) && opt(v[\"reason\"], isStr),\n presence: (v) => {\n if (v[\"mode\"] === \"detailed\") {\n return (\n arrayOf(v[\"joined\"], isParticipant) &&\n arrayOf(v[\"left\"], isParticipant) &&\n isNum(v[\"count\"])\n );\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && Array.isArray(v[\"recent\"]);\n }\n return false;\n },\n activity: (v) => isStr(v[\"userId\"]) && isStr(v[\"kind\"]) && isNum(v[\"since\"]),\n direct: (v) => isWireMessage(v[\"msg\"]),\n reassign: (v) => isStr(v[\"leaf\"]),\n error: (v) => isStr(v[\"code\"]) && opt(v[\"reason\"], isStr) && opt(v[\"ref\"], isStr),\n pong: () => true,\n};\n\nconst INBOX_SERVER_FRAMES: Record<InboxServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n arrayOf(v[\"entries\"], isInboxEntryWire) &&\n arrayOf(v[\"items\"], isInboxItemWire) &&\n isNum(v[\"counter\"]),\n entry: (v) => isInboxEntryWire(v[\"entry\"]),\n item: (v) => isInboxItemWire(v[\"item\"]),\n counter: (v) => isNum(v[\"n\"]),\n pong: () => true,\n};\n\n// C→S tables. A server (or the test mock) receives these; the same totality contract\n// applies as for the S→C tables. `ping` and `activity` also appear in S→C sets with\n// different shapes — the families are disjoint, so each is validated against its own\n// direction's table.\nconst CHANNEL_CLIENT_FRAMES: Record<ChannelClientFrame[\"t\"], Validator> = {\n ephemeral: (v) => isStr(v[\"cl\"]) && isStr(v[\"type\"]) && \"content\" in v,\n activity: (v) => isStr(v[\"kind\"]),\n watermark: (v) => isNum(v[\"seq\"]),\n meta: (v) => isRec(v[\"metadata\"]),\n ping: () => true,\n};\n\nconst INBOX_CLIENT_FRAMES: Record<InboxClientFrame[\"t\"], Validator> = {\n read: (v) => isStr(v[\"channelId\"]),\n \"item.read\": (v) => isStr(v[\"id\"]),\n \"read.all\": () => true,\n mute: (v) => isStr(v[\"channelId\"]) && isBool(v[\"muted\"]),\n ping: () => true,\n};\n\nconst validatorFor = <T extends string>(\n table: Record<T, Validator>,\n t: string,\n): Validator | undefined =>\n Object.hasOwn(table, t) ? table[t as T] : undefined;\n\n// ============================================================\n// Parse / serialize\n// ============================================================\n\nconst parseJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * A frame-shaped value: an object carrying a string `t`. Anything else is not a frame.\n */\nconst asFrameShape = (raw: string): (Rec & { t: string }) | null => {\n const value = parseJson(raw);\n if (!isRec(value) || !isStr(value[\"t\"])) return null;\n return value as Rec & { t: string };\n};\n\n/**\n * Parse a text frame from a **channel** socket (S→C).\n *\n * Total and non-throwing:\n * - malformed JSON, a non-object, or a missing/non-string `t` → `null`\n * - a known `t` whose shape does not match → `null`\n * - an unknown `t` → the frame is returned intact as an {@link UnknownFrame} (§6),\n * because forward compatibility says ignore it, not lose it\n *\n * Unknown *fields* on known frames are preserved, so `parse` → {@link serializeFrame}\n * round-trips without dropping anything.\n */\nexport function parseChannelFrame(raw: string): ParsedChannelFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelServerFrame) : null;\n}\n\n/**\n * Parse a text frame from an **inbox** socket (S→C).\n *\n * Same contract as {@link parseChannelFrame}. The two families are disjoint: an inbox\n * `ready` and a channel `ready` share a `t` but not a shape, so a frame must be parsed\n * with the function matching the socket it arrived on.\n */\nexport function parseInboxFrame(raw: string): ParsedInboxFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxServerFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on a **channel** socket (C→S).\n *\n * The upstream counterpart of {@link parseChannelFrame}, for a server or test mock\n * receiving the frames a client sends. Same totality contract: `null` on malformed JSON\n * / missing `t` / a known `t` with a bad shape, and an {@link UnknownFrame} passthrough\n * for an unrecognised `t` (§6).\n */\nexport function parseChannelClientFrame(\n raw: string,\n): ParsedChannelClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelClientFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on an **inbox** socket (C→S).\n *\n * Same contract as {@link parseChannelClientFrame}.\n */\nexport function parseInboxClientFrame(raw: string): ParsedInboxClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxClientFrame) : null;\n}\n\n/**\n * Serialize a frame to a JSON text frame.\n *\n * Primarily for C→S sends. It accepts any frame so that a parsed S→C frame can be\n * re-serialized intact — the round-trip that proves unknown fields survive (§6).\n */\nexport function serializeFrame(frame: AnyFrame): string {\n return JSON.stringify(frame);\n}\n\n// ============================================================\n// Guards\n//\n// These narrow a frame that has already been through parseChannelFrame /\n// parseInboxFrame, which is what makes a `t` check sufficient: the parser has already\n// validated the shape, so narrowing on the discriminator cannot lie.\n// ============================================================\n\n/** True when the parser recognised this channel frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownChannelFrame = (\n frame: ParsedChannelFrame,\n): frame is ChannelServerFrame => Object.hasOwn(CHANNEL_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this inbox frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownInboxFrame = (\n frame: ParsedInboxFrame,\n): frame is InboxServerFrame => Object.hasOwn(INBOX_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S channel frame (not an `UnknownFrame`). */\nexport const isKnownChannelClientFrame = (\n frame: ParsedChannelClientFrame,\n): frame is ChannelClientFrame => Object.hasOwn(CHANNEL_CLIENT_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S inbox frame (not an `UnknownFrame`). */\nexport const isKnownInboxClientFrame = (\n frame: ParsedInboxClientFrame,\n): frame is InboxClientFrame => Object.hasOwn(INBOX_CLIENT_FRAMES, frame.t);\n\n// ── Channel S→C ─────────────────────────────────────────────\n\nexport const isChannelReady = (f: ParsedChannelFrame): f is ChannelReadyFrame =>\n f.t === \"ready\";\nexport const isBatch = (f: ParsedChannelFrame): f is BatchFrame => f.t === \"batch\";\nexport const isRetract = (f: ParsedChannelFrame): f is RetractFrame =>\n f.t === \"retract\";\nexport const isPresence = (f: ParsedChannelFrame): f is PresenceFrame =>\n f.t === \"presence\";\nexport const isActivity = (f: ParsedChannelFrame): f is ActivityFrame =>\n f.t === \"activity\";\nexport const isDirect = (f: ParsedChannelFrame): f is DirectFrame => f.t === \"direct\";\nexport const isReassign = (f: ParsedChannelFrame): f is ReassignFrame =>\n f.t === \"reassign\";\nexport const isError = (f: ParsedChannelFrame): f is ErrorFrame => f.t === \"error\";\nexport const isPong = (f: ParsedChannelFrame): f is PongFrame => f.t === \"pong\";\n\n// ── Inbox S→C ───────────────────────────────────────────────\n\nexport const isInboxReady = (f: ParsedInboxFrame): f is InboxReadyFrame =>\n f.t === \"ready\";\nexport const isInboxEntry = (f: ParsedInboxFrame): f is InboxEntryFrame =>\n f.t === \"entry\";\nexport const isInboxItem = (f: ParsedInboxFrame): f is InboxItemFrame =>\n f.t === \"item\";\nexport const isInboxCounter = (f: ParsedInboxFrame): f is InboxCounterFrame =>\n f.t === \"counter\";\nexport const isInboxPong = (f: ParsedInboxFrame): f is PongFrame => f.t === \"pong\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/parse.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Protocol version carried by every upgrade as `?v=` (§1.1, §6).\n *\n * An unknown version is refused at the upgrade with HTTP 426 `unsupported_version`.\n * Within v1 the protocol evolves additively only; a breaking change bumps this.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Upgrade query-parameter names (§1.1). Build upgrade URLs from these rather than\n * string literals, so a rename is a compile error rather than a silent 4xx.\n *\n * - `version` — required on every upgrade; unknown → 426.\n * - `token` — the signed JWT. Identifies the user.\n * - `key` — the publishable apiKey, identifying the app (§1 credential transport).\n * - `leaf` — opaque reconnect token; echo back what `ready` gave you, unchanged.\n * - `meta` — initial presence metadata, base64 JSON (standard channels; ≤1KB decoded).\n * - `last` — highest contiguous seq held, sent on reconnect to request replay (§1.4).\n */\nexport const UPGRADE_PARAMS = {\n version: \"v\",\n token: \"token\",\n key: \"key\",\n leaf: \"leaf\",\n meta: \"meta\",\n last: \"last\",\n} as const;\n\n/**\n * Response header carrying the refusal code on a refused upgrade (§1.1).\n *\n * Duplicates `code` from the body so a client behind a body-eating proxy can still\n * tell why the socket never opened.\n */\nexport const PORTAL_ERROR_HEADER = \"x-portal-error\";\n","/**\n * Why an upgrade was refused (§1.1).\n *\n * Refusals happen at the HTTP upgrade — **the socket never opens**. They are therefore\n * disjoint from {@link PublishErrorCode} (HTTP publish rejections) and from the\n * in-session `error` frame, which both presuppose a working connection.\n */\nexport type RefusalCode =\n | \"invalid_token\"\n | \"token_expired\"\n | \"invalid_api_key\"\n | \"not_member\"\n | \"banned\"\n | \"anonymous_not_allowed\"\n | \"unknown_channel\"\n | \"unsupported_version\"\n | \"channel_at_capacity\";\n\n/**\n * The HTTP status each refusal is delivered with (§1.1 table).\n *\n * The mapping is many-to-one: status alone does not identify the cause, so read\n * `code` from the body (or the `x-portal-error` header) rather than branching on status.\n */\nexport const REFUSAL_STATUS = {\n invalid_token: 401,\n token_expired: 401,\n invalid_api_key: 403,\n not_member: 403,\n banned: 403,\n anonymous_not_allowed: 403,\n unknown_channel: 404,\n unsupported_version: 426,\n channel_at_capacity: 429,\n} as const satisfies Record<RefusalCode, number>;\n\nconst REFUSAL_CODES: ReadonlySet<string> = new Set(Object.keys(REFUSAL_STATUS));\n\n/**\n * Whether a value is a refusal code this version knows (§1.1).\n *\n * A refusal body arriving with an unrecognised code is not a refusal this client can\n * reason about — treat it as an opaque failure rather than coercing it.\n */\nexport const isRefusalCode = (value: unknown): value is RefusalCode =>\n typeof value === \"string\" && REFUSAL_CODES.has(value);\n\n/** Body of a refused upgrade (§1.1). */\nexport type RefusalBody = {\n code: RefusalCode;\n reason?: string;\n /**\n * Seconds to wait before retrying. Documented for `channel_at_capacity` (429) only\n * (§1.1); absent on every other refusal.\n */\n retryAfter?: number;\n};\n\n/**\n * Why an HTTP publish was rejected (§3.1).\n *\n * Deliberately NOT merged into {@link RefusalCode}: these arrive on a live connection\n * in response to `POST /v1/channels/{id}/messages`, and a client reacts to them per\n * send, not per connection.\n *\n * `blocked_by_middleware` carries user-visible copy in `reason`.\n */\nexport type PublishErrorCode =\n | \"not_permitted\"\n | \"blocked_by_middleware\"\n | \"content_too_large\"\n | \"rate_limited\"\n | \"validation_failed\";\n\n/**\n * Why a publish failed validation — the `reason` carried by a `validation_failed`\n * rejection.\n *\n * - `thread_depth_exceeded` — the `threadParentId` would nest the reply deeper than the\n * platform allows.\n */\nexport type ValidationFailedReason = \"thread_depth_exceeded\";\n\n/**\n * Body of a rejected publish (§3.1).\n *\n * SPEC: §3.1 specifies the shape as `4xx { code, reason? }` but does not map each code\n * to a status, so no status record is exported here (unlike {@link REFUSAL_STATUS}).\n *\n * A `validation_failed` body carries a machine-readable {@link ValidationFailedReason};\n * every other code's `reason` is free-form copy.\n */\nexport type PublishErrorBody =\n | { code: \"validation_failed\"; reason?: ValidationFailedReason }\n | { code: Exclude<PublishErrorCode, \"validation_failed\">; reason?: string };\n\n","import type {\n ActivityFrame,\n BatchFrame,\n ChannelClientFrame,\n ChannelReadyFrame,\n ChannelServerFrame,\n Capabilities,\n ChannelInfo,\n DirectFrame,\n ErrorFrame,\n MeInfo,\n PongFrame,\n PresenceFrame,\n ReadyPresence,\n ReassignFrame,\n RetractFrame,\n WirePresenceParticipant,\n} from \"./channel.js\";\nimport type {\n InboxClientFrame,\n InboxCounterFrame,\n InboxEntryFrame,\n InboxEntryWire,\n InboxItemFrame,\n InboxItemWire,\n InboxReadyFrame,\n InboxServerFrame,\n} from \"./inbox.js\";\nimport type {\n AnyFrame,\n ParsedChannelClientFrame,\n ParsedChannelFrame,\n ParsedInboxClientFrame,\n ParsedInboxFrame,\n UnknownFrame,\n} from \"./frames.js\";\nimport type { Mention, WireMessage, WireSender } from \"./message.js\";\n\n// ============================================================\n// Primitives\n// ============================================================\n\ntype Rec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is Rec =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nconst isStr = (v: unknown): v is string => typeof v === \"string\";\nconst isBool = (v: unknown): v is boolean => typeof v === \"boolean\";\n\n/** JSON has no NaN/Infinity, so any non-finite number means a hand-built object. */\nconst isNum = (v: unknown): v is number =>\n typeof v === \"number\" && Number.isFinite(v);\n\n/** An absent optional field is valid; a present one must match. */\nconst opt = <T>(v: unknown, check: (x: unknown) => x is T): boolean =>\n v === undefined || check(v);\n\nconst arrayOf = <T>(v: unknown, check: (x: unknown) => x is T): v is T[] =>\n Array.isArray(v) && v.every(check);\n\nconst isStrRec = (v: unknown): v is Record<string, string> =>\n isRec(v) && Object.values(v).every(isStr);\n\n// ============================================================\n// Shape validators\n//\n// Each validator checks exactly the fields its type declares required — no more, no\n// less. Unknown fields are ignored and survive untouched (§6), but a frame missing a\n// required field is rejected rather than returned as a type that lies about it.\n// ============================================================\n\nconst isMention = (v: unknown): v is Mention => isRec(v) && isStr(v[\"userId\"]);\n\nconst isWireSender = (v: unknown): v is WireSender =>\n isRec(v) && isStr(v[\"id\"]) && isBool(v[\"anon\"]) && opt(v[\"username\"], isStr);\n\nconst isWireMessage = (v: unknown): v is WireMessage =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"seq\"] === null || isNum(v[\"seq\"])) &&\n isStr(v[\"type\"]) &&\n isStr(v[\"kind\"]) &&\n \"content\" in v &&\n isWireSender(v[\"sender\"]) &&\n isNum(v[\"timestamp\"]) &&\n opt(v[\"to\"], isStr) &&\n (v[\"mentions\"] === undefined || arrayOf(v[\"mentions\"], isMention)) &&\n isBool(v[\"retracted\"]) &&\n isBool(v[\"ephemeral\"]) &&\n opt(v[\"threadParentId\"], isStr) &&\n opt(v[\"threadSeq\"], isNum);\n\n\nconst isChannelInfo = (v: unknown): v is ChannelInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"mode\"] === \"standard\" || v[\"mode\"] === \"broadcast\") &&\n opt(v[\"name\"], isStr) &&\n opt(v[\"meta\"], isRec);\n\nconst isCapabilities = (v: unknown): v is Capabilities =>\n isRec(v) && opt(v[\"publish\"], isBool) && opt(v[\"sendDirect\"], isBool);\n\nconst isMeInfo = (v: unknown): v is MeInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isBool(v[\"anon\"]) &&\n isRec(v[\"claims\"]) &&\n isCapabilities(v[\"capabilities\"]);\n\nconst isParticipant = (v: unknown): v is WirePresenceParticipant =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isBool(v[\"anon\"]) &&\n opt(v[\"username\"], isStr) &&\n opt(v[\"metadata\"], isRec);\n\nconst isReadyPresence = (v: unknown): v is ReadyPresence => {\n if (!isRec(v)) return false;\n if (v[\"mode\"] === \"detailed\") {\n return arrayOf(v[\"participants\"], isParticipant) && isNum(v[\"count\"]);\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && (v[\"recent\"] === undefined || Array.isArray(v[\"recent\"]));\n }\n return false;\n};\n\nconst isInboxEntryWire = (v: unknown): v is InboxEntryWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n opt(v[\"threadId\"], isStr) &&\n opt(v[\"parentThreadId\"], isStr) &&\n opt(v[\"rootThreadId\"], isStr) &&\n opt(v[\"name\"], isStr) &&\n\n opt(v[\"meta\"], isRec) &&\n opt(v[\"latest\"], isLatest) &&\n isNum(v[\"unread\"]) &&\n isBool(v[\"muted\"]) &&\n isNum(v[\"at\"]);\n\nconst isLatest = (v: unknown): v is InboxEntryWire[\"latest\"] =>\n isRec(v) &&\n isStr(v[\"text\"]) &&\n isRec(v[\"sender\"]) &&\n isStr((v[\"sender\"] as Rec)[\"id\"]) &&\n isNum(v[\"at\"]);\n\nconst isInboxItemWire = (v: unknown): v is InboxItemWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isStr(v[\"type\"]) &&\n opt(v[\"title\"], isStr) &&\n \"data\" in v &&\n opt(v[\"channelId\"], isStr) &&\n isNum(v[\"at\"]) &&\n isBool(v[\"read\"]);\n\n// ============================================================\n// Frame tables — the single source of truth for \"known `t`\"\n// ============================================================\n\ntype Validator = (v: Rec) => boolean;\n\nconst CHANNEL_SERVER_FRAMES: Record<ChannelServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n isChannelInfo(v[\"channel\"]) &&\n isMeInfo(v[\"me\"]) &&\n isNum(v[\"seq\"]) &&\n isStr(v[\"leaf\"]) &&\n isReadyPresence(v[\"presence\"]) &&\n opt(v[\"watermark\"], isNum) &&\n opt(v[\"ext\"], isRec) &&\n opt(v[\"bindings\"], isStrRec),\n batch: (v) => arrayOf(v[\"msgs\"], isWireMessage),\n retract: (v) => isStr(v[\"id\"]) && isNum(v[\"seq\"]) && opt(v[\"reason\"], isStr),\n presence: (v) => {\n if (v[\"mode\"] === \"detailed\") {\n return (\n arrayOf(v[\"joined\"], isParticipant) &&\n arrayOf(v[\"left\"], isStr) &&\n isNum(v[\"count\"])\n );\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && Array.isArray(v[\"recent\"]);\n }\n return false;\n },\n activity: (v) => isStr(v[\"userId\"]) && isStr(v[\"kind\"]) && isNum(v[\"since\"]),\n direct: (v) => isWireMessage(v[\"msg\"]),\n reassign: (v) => isStr(v[\"leaf\"]),\n error: (v) => isStr(v[\"code\"]) && opt(v[\"reason\"], isStr) && opt(v[\"ref\"], isStr),\n pong: () => true,\n};\n\nconst INBOX_SERVER_FRAMES: Record<InboxServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n arrayOf(v[\"entries\"], isInboxEntryWire) &&\n arrayOf(v[\"items\"], isInboxItemWire) &&\n isNum(v[\"counter\"]),\n entry: (v) => isInboxEntryWire(v[\"entry\"]),\n item: (v) => isInboxItemWire(v[\"item\"]),\n counter: (v) => isNum(v[\"n\"]),\n pong: () => true,\n};\n\n// C→S tables. A server (or the test mock) receives these; the same totality contract\n// applies as for the S→C tables. `ping` and `activity` also appear in S→C sets with\n// different shapes — the families are disjoint, so each is validated against its own\n// direction's table.\nconst CHANNEL_CLIENT_FRAMES: Record<ChannelClientFrame[\"t\"], Validator> = {\n ephemeral: (v) => isStr(v[\"cl\"]) && isStr(v[\"type\"]) && \"content\" in v,\n activity: (v) => isStr(v[\"kind\"]),\n watermark: (v) => isNum(v[\"seq\"]),\n meta: (v) => isRec(v[\"metadata\"]),\n ping: () => true,\n};\n\nconst INBOX_CLIENT_FRAMES: Record<InboxClientFrame[\"t\"], Validator> = {\n read: (v) => isStr(v[\"channelId\"]) && opt(v[\"threadId\"], isStr),\n \"item.read\": (v) => isStr(v[\"id\"]),\n \"read.all\": () => true,\n mute: (v) => isStr(v[\"channelId\"]) && isBool(v[\"muted\"]) && opt(v[\"threadId\"], isStr),\n\n ping: () => true,\n};\n\nconst validatorFor = <T extends string>(\n table: Record<T, Validator>,\n t: string,\n): Validator | undefined =>\n Object.hasOwn(table, t) ? table[t as T] : undefined;\n\n// ============================================================\n// Parse / serialize\n// ============================================================\n\nconst parseJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * A frame-shaped value: an object carrying a string `t`. Anything else is not a frame.\n */\nconst asFrameShape = (raw: string): (Rec & { t: string }) | null => {\n const value = parseJson(raw);\n if (!isRec(value) || !isStr(value[\"t\"])) return null;\n return value as Rec & { t: string };\n};\n\n/**\n * Parse a text frame from a **channel** socket (S→C).\n *\n * Total and non-throwing:\n * - malformed JSON, a non-object, or a missing/non-string `t` → `null`\n * - a known `t` whose shape does not match → `null`\n * - an unknown `t` → the frame is returned intact as an {@link UnknownFrame} (§6),\n * because forward compatibility says ignore it, not lose it\n *\n * Unknown *fields* on known frames are preserved, so `parse` → {@link serializeFrame}\n * round-trips without dropping anything.\n */\nexport function parseChannelFrame(raw: string): ParsedChannelFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelServerFrame) : null;\n}\n\n/**\n * Parse a text frame from an **inbox** socket (S→C).\n *\n * Same contract as {@link parseChannelFrame}. The two families are disjoint: an inbox\n * `ready` and a channel `ready` share a `t` but not a shape, so a frame must be parsed\n * with the function matching the socket it arrived on.\n */\nexport function parseInboxFrame(raw: string): ParsedInboxFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxServerFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on a **channel** socket (C→S).\n *\n * The upstream counterpart of {@link parseChannelFrame}, for a server or test mock\n * receiving the frames a client sends. Same totality contract: `null` on malformed JSON\n * / missing `t` / a known `t` with a bad shape, and an {@link UnknownFrame} passthrough\n * for an unrecognised `t` (§6).\n */\nexport function parseChannelClientFrame(\n raw: string,\n): ParsedChannelClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelClientFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on an **inbox** socket (C→S).\n *\n * Same contract as {@link parseChannelClientFrame}.\n */\nexport function parseInboxClientFrame(raw: string): ParsedInboxClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxClientFrame) : null;\n}\n\n/**\n * Serialize a frame to a JSON text frame.\n *\n * Primarily for C→S sends. It accepts any frame so that a parsed S→C frame can be\n * re-serialized intact — the round-trip that proves unknown fields survive (§6).\n */\nexport function serializeFrame(frame: AnyFrame): string {\n return JSON.stringify(frame);\n}\n\n// ============================================================\n// Guards\n//\n// These narrow a frame that has already been through parseChannelFrame /\n// parseInboxFrame, which is what makes a `t` check sufficient: the parser has already\n// validated the shape, so narrowing on the discriminator cannot lie.\n// ============================================================\n\n/** True when the parser recognised this channel frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownChannelFrame = (\n frame: ParsedChannelFrame,\n): frame is ChannelServerFrame => Object.hasOwn(CHANNEL_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this inbox frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownInboxFrame = (\n frame: ParsedInboxFrame,\n): frame is InboxServerFrame => Object.hasOwn(INBOX_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S channel frame (not an `UnknownFrame`). */\nexport const isKnownChannelClientFrame = (\n frame: ParsedChannelClientFrame,\n): frame is ChannelClientFrame => Object.hasOwn(CHANNEL_CLIENT_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S inbox frame (not an `UnknownFrame`). */\nexport const isKnownInboxClientFrame = (\n frame: ParsedInboxClientFrame,\n): frame is InboxClientFrame => Object.hasOwn(INBOX_CLIENT_FRAMES, frame.t);\n\n// ── Channel S→C ─────────────────────────────────────────────\n\nexport const isChannelReady = (f: ParsedChannelFrame): f is ChannelReadyFrame =>\n f.t === \"ready\";\nexport const isBatch = (f: ParsedChannelFrame): f is BatchFrame => f.t === \"batch\";\nexport const isRetract = (f: ParsedChannelFrame): f is RetractFrame =>\n f.t === \"retract\";\nexport const isPresence = (f: ParsedChannelFrame): f is PresenceFrame =>\n f.t === \"presence\";\nexport const isActivity = (f: ParsedChannelFrame): f is ActivityFrame =>\n f.t === \"activity\";\nexport const isDirect = (f: ParsedChannelFrame): f is DirectFrame => f.t === \"direct\";\nexport const isReassign = (f: ParsedChannelFrame): f is ReassignFrame =>\n f.t === \"reassign\";\nexport const isError = (f: ParsedChannelFrame): f is ErrorFrame => f.t === \"error\";\nexport const isPong = (f: ParsedChannelFrame): f is PongFrame => f.t === \"pong\";\n\n// ── Inbox S→C ───────────────────────────────────────────────\n\nexport const isInboxReady = (f: ParsedInboxFrame): f is InboxReadyFrame =>\n f.t === \"ready\";\nexport const isInboxEntry = (f: ParsedInboxFrame): f is InboxEntryFrame =>\n f.t === \"entry\";\nexport const isInboxItem = (f: ParsedInboxFrame): f is InboxItemFrame =>\n f.t === \"item\";\nexport const isInboxCounter = (f: ParsedInboxFrame): f is InboxCounterFrame =>\n f.t === \"counter\";\nexport const isInboxPong = (f: ParsedInboxFrame): f is PongFrame => f.t === \"pong\";\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -10,7 +10,8 @@ declare const PROTOCOL_VERSION = 1;
|
|
|
10
10
|
* string literals, so a rename is a compile error rather than a silent 4xx.
|
|
11
11
|
*
|
|
12
12
|
* - `version` — required on every upgrade; unknown → 426.
|
|
13
|
-
* - `token` — the signed JWT. Identifies the user
|
|
13
|
+
* - `token` — the signed JWT. Identifies the user.
|
|
14
|
+
* - `key` — the publishable apiKey, identifying the app (§1 credential transport).
|
|
14
15
|
* - `leaf` — opaque reconnect token; echo back what `ready` gave you, unchanged.
|
|
15
16
|
* - `meta` — initial presence metadata, base64 JSON (standard channels; ≤1KB decoded).
|
|
16
17
|
* - `last` — highest contiguous seq held, sent on reconnect to request replay (§1.4).
|
|
@@ -18,6 +19,7 @@ declare const PROTOCOL_VERSION = 1;
|
|
|
18
19
|
declare const UPGRADE_PARAMS: {
|
|
19
20
|
readonly version: "v";
|
|
20
21
|
readonly token: "token";
|
|
22
|
+
readonly key: "key";
|
|
21
23
|
readonly leaf: "leaf";
|
|
22
24
|
readonly meta: "meta";
|
|
23
25
|
readonly last: "last";
|
|
@@ -81,15 +83,29 @@ type RefusalBody = {
|
|
|
81
83
|
*
|
|
82
84
|
* `blocked_by_middleware` carries user-visible copy in `reason`.
|
|
83
85
|
*/
|
|
84
|
-
type PublishErrorCode = "not_permitted" | "blocked_by_middleware" | "content_too_large" | "rate_limited";
|
|
86
|
+
type PublishErrorCode = "not_permitted" | "blocked_by_middleware" | "content_too_large" | "rate_limited" | "validation_failed";
|
|
87
|
+
/**
|
|
88
|
+
* Why a publish failed validation — the `reason` carried by a `validation_failed`
|
|
89
|
+
* rejection.
|
|
90
|
+
*
|
|
91
|
+
* - `thread_depth_exceeded` — the `threadParentId` would nest the reply deeper than the
|
|
92
|
+
* platform allows.
|
|
93
|
+
*/
|
|
94
|
+
type ValidationFailedReason = "thread_depth_exceeded";
|
|
85
95
|
/**
|
|
86
96
|
* Body of a rejected publish (§3.1).
|
|
87
97
|
*
|
|
88
98
|
* SPEC: §3.1 specifies the shape as `4xx { code, reason? }` but does not map each code
|
|
89
99
|
* to a status, so no status record is exported here (unlike {@link REFUSAL_STATUS}).
|
|
100
|
+
*
|
|
101
|
+
* A `validation_failed` body carries a machine-readable {@link ValidationFailedReason};
|
|
102
|
+
* every other code's `reason` is free-form copy.
|
|
90
103
|
*/
|
|
91
104
|
type PublishErrorBody = {
|
|
92
|
-
code:
|
|
105
|
+
code: "validation_failed";
|
|
106
|
+
reason?: ValidationFailedReason;
|
|
107
|
+
} | {
|
|
108
|
+
code: Exclude<PublishErrorCode, "validation_failed">;
|
|
93
109
|
reason?: string;
|
|
94
110
|
};
|
|
95
111
|
|
|
@@ -147,6 +163,21 @@ type WireMessage = {
|
|
|
147
163
|
retracted: boolean;
|
|
148
164
|
/** Ephemeral messages are not persisted and have `seq: null`. */
|
|
149
165
|
ephemeral: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Present on a reply: the id of the message it replies to, which is also the id of the
|
|
168
|
+
* thread it belongs to. The parent may itself be a reply (nested threads). A thread comes
|
|
169
|
+
* into existence with its first reply — there is no separate create step.
|
|
170
|
+
*
|
|
171
|
+
* Replies travel in the channel's ordinary `batch` frames to every channel connection;
|
|
172
|
+
* grouping them into threads is the client's job.
|
|
173
|
+
*/
|
|
174
|
+
threadParentId?: string;
|
|
175
|
+
/**
|
|
176
|
+
* Dense per-thread position, present alongside `threadParentId`. Like `seq` this is
|
|
177
|
+
* transport: `GET /history?threadParentId=` pages a thread by it, and it is stripped a
|
|
178
|
+
* layer up.
|
|
179
|
+
*/
|
|
180
|
+
threadSeq?: number;
|
|
150
181
|
};
|
|
151
182
|
|
|
152
183
|
/** Channel mode. Decides presence shape and whether `sender.username` is populated. */
|
|
@@ -190,18 +221,17 @@ type MeInfo = {
|
|
|
190
221
|
capabilities: Capabilities;
|
|
191
222
|
};
|
|
192
223
|
/**
|
|
193
|
-
* A participant as it appears **on the wire** (§1.2 snapshot, §2.1 deltas).
|
|
194
|
-
*
|
|
195
|
-
* SPEC: provisional. `{ userId, claims }` is what the current fixtures prove the wire
|
|
196
|
-
* carries (`channel_ready` presence, and the `presence` frame's `joined`), so it is what
|
|
197
|
-
* this type says — the type follows recorded evidence, never an anticipated change.
|
|
224
|
+
* A participant as it appears **on the wire** (§1.2 snapshot, §2.1 `joined` deltas).
|
|
198
225
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
226
|
+
* `username` is present when the participant has one; `metadata` is the session presence
|
|
227
|
+
* metadata (`?meta=` / a `meta` frame). This carries no `claims` — the token's claim bag is
|
|
228
|
+
* the connected user's own (`me.claims` in `ready`), never another participant's.
|
|
201
229
|
*/
|
|
202
230
|
type WirePresenceParticipant = {
|
|
203
|
-
|
|
204
|
-
|
|
231
|
+
id: string;
|
|
232
|
+
anon: boolean;
|
|
233
|
+
username?: string;
|
|
234
|
+
metadata?: Record<string, unknown>;
|
|
205
235
|
};
|
|
206
236
|
/** Presence snapshot on a standard channel, carried by `ready` (§1.2). */
|
|
207
237
|
type DetailedPresenceSnapshot = {
|
|
@@ -284,15 +314,14 @@ type RetractFrame = {
|
|
|
284
314
|
/**
|
|
285
315
|
* Presence delta on a standard channel (§2.1).
|
|
286
316
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
* element shape is assumed symmetric and remains **unverified**.
|
|
317
|
+
* `joined` carries full participants; `left` carries bare participant ids (a departing
|
|
318
|
+
* participant is identified, not re-described).
|
|
290
319
|
*/
|
|
291
320
|
type DetailedPresenceFrame = {
|
|
292
321
|
t: "presence";
|
|
293
322
|
mode: "detailed";
|
|
294
323
|
joined: WirePresenceParticipant[];
|
|
295
|
-
left:
|
|
324
|
+
left: string[];
|
|
296
325
|
count: number;
|
|
297
326
|
};
|
|
298
327
|
/**
|
|
@@ -415,6 +444,16 @@ type ChannelClientFrame = EphemeralFrame | ActivityUpFrame | WatermarkFrame | Me
|
|
|
415
444
|
type InboxEntryWire = {
|
|
416
445
|
/** The channel id this row tracks. */
|
|
417
446
|
id: string;
|
|
447
|
+
/**
|
|
448
|
+
* Present on a thread row. A thread row is a sibling of its channel's row (same `id`,
|
|
449
|
+
* distinct `threadId`) with its own `latest`, `unread`, read position, and `muted`; no
|
|
450
|
+
* row's state ever reflects another row's traffic. Identity is `(id, threadId)`.
|
|
451
|
+
*/
|
|
452
|
+
threadId?: string;
|
|
453
|
+
/** The enclosing thread, on a nested thread row. Absent on a root thread row. */
|
|
454
|
+
parentThreadId?: string;
|
|
455
|
+
/** The top-level thread this row descends from; equals `threadId` on a root thread row. */
|
|
456
|
+
rootThreadId?: string;
|
|
418
457
|
name?: string;
|
|
419
458
|
meta?: Record<string, unknown>;
|
|
420
459
|
/** Preview of the most recent message. Absent on large channels (seq-only tier). */
|
|
@@ -486,6 +525,8 @@ type InboxServerFrame = InboxReadyFrame | InboxEntryFrame | InboxItemFrame | Inb
|
|
|
486
525
|
type InboxReadFrame = {
|
|
487
526
|
t: "read";
|
|
488
527
|
channelId: string;
|
|
528
|
+
/** Address a thread row instead of the channel row. */
|
|
529
|
+
threadId?: string;
|
|
489
530
|
};
|
|
490
531
|
/** Flip one item's read flag (§5). Never cascades to older items. */
|
|
491
532
|
type InboxItemReadFrame = {
|
|
@@ -501,6 +542,8 @@ type InboxMuteFrame = {
|
|
|
501
542
|
t: "mute";
|
|
502
543
|
channelId: string;
|
|
503
544
|
muted: boolean;
|
|
545
|
+
/** Address a thread row instead of the channel row. */
|
|
546
|
+
threadId?: string;
|
|
504
547
|
};
|
|
505
548
|
/** The complete upstream set for an inbox socket (§5). */
|
|
506
549
|
type InboxClientFrame = InboxReadFrame | InboxItemReadFrame | InboxReadAllFrame | InboxMuteFrame | PingFrame;
|
|
@@ -524,6 +567,12 @@ type PublishBody = {
|
|
|
524
567
|
to?: string;
|
|
525
568
|
/** Declared by the sender; the platform verifies, dedupes, and caps them. */
|
|
526
569
|
mentions?: Mention[];
|
|
570
|
+
/**
|
|
571
|
+
* Reply into a thread: the id of the message being replied to (the thread id). The first
|
|
572
|
+
* reply to a message creates its thread. Nesting beyond the platform's depth cap is
|
|
573
|
+
* rejected with `validation_failed` / `thread_depth_exceeded`.
|
|
574
|
+
*/
|
|
575
|
+
threadParentId?: string;
|
|
527
576
|
};
|
|
528
577
|
/**
|
|
529
578
|
* A successful publish (§3.1) — the wire form of the SendAck.
|
|
@@ -543,14 +592,51 @@ type SendAckWire = {
|
|
|
543
592
|
/**
|
|
544
593
|
* `GET /v1/channels/{channelId}/history` (§3.2).
|
|
545
594
|
*
|
|
546
|
-
* One endpoint serves initial backfill, scroll-up paging (`?before=&limit=`),
|
|
547
|
-
*
|
|
595
|
+
* One endpoint serves initial backfill, scroll-up paging (`?before=&limit=`), gap-fill
|
|
596
|
+
* ranges (`?from=&to=`), and thread paging (`?threadParentId=&before=&limit=`, where
|
|
597
|
+
* `before` is a `threadSeq`). Retracted messages come back as tombstoned envelopes,
|
|
548
598
|
* consistent with live rendering.
|
|
549
599
|
*/
|
|
550
600
|
type HistoryResponse = {
|
|
551
601
|
msgs: WireMessage[];
|
|
552
602
|
hasMore: boolean;
|
|
553
603
|
};
|
|
604
|
+
/**
|
|
605
|
+
* One thread in a channel's thread registry, as returned by
|
|
606
|
+
* `GET /v1/channels/{channelId}/threads`.
|
|
607
|
+
*/
|
|
608
|
+
type ThreadNodeWire = {
|
|
609
|
+
/** The thread id — the id of the message whose first reply created it. */
|
|
610
|
+
id: string;
|
|
611
|
+
/** The enclosing thread; absent on a root thread. */
|
|
612
|
+
parentThreadId?: string;
|
|
613
|
+
/** The top-level ancestor; equals `id` on a root thread. */
|
|
614
|
+
rootThreadId: string;
|
|
615
|
+
/** Nesting level; a root thread is depth `0`. */
|
|
616
|
+
depth: number;
|
|
617
|
+
/** Channel `seq` of the message this thread hangs off. */
|
|
618
|
+
spawnSeq: number;
|
|
619
|
+
/** Who sent the message this thread hangs off. */
|
|
620
|
+
spawnedBy: {
|
|
621
|
+
id: string;
|
|
622
|
+
};
|
|
623
|
+
/** Channel `seq` of the most recent reply. */
|
|
624
|
+
latestSeq: number;
|
|
625
|
+
/** Highest `threadSeq` in the thread — the reply count. */
|
|
626
|
+
threadSeq: number;
|
|
627
|
+
/** Epoch milliseconds. */
|
|
628
|
+
createdAt: number;
|
|
629
|
+
};
|
|
630
|
+
/**
|
|
631
|
+
* `GET /v1/channels/{channelId}/threads?parent={id|""}|root={id}&before=&limit=`.
|
|
632
|
+
*
|
|
633
|
+
* `parent=""` lists root threads; `parent={id}` lists the threads nested directly under
|
|
634
|
+
* one; `root={id}` lists every thread descending from a root.
|
|
635
|
+
*/
|
|
636
|
+
type ThreadsResponse = {
|
|
637
|
+
threads: ThreadNodeWire[];
|
|
638
|
+
hasMore: boolean;
|
|
639
|
+
};
|
|
554
640
|
/** One row of the member directory (§3.3). */
|
|
555
641
|
type MemberRow = {
|
|
556
642
|
userId: string;
|
|
@@ -669,4 +755,4 @@ declare const isInboxItem: (f: ParsedInboxFrame) => f is InboxItemFrame;
|
|
|
669
755
|
declare const isInboxCounter: (f: ParsedInboxFrame) => f is InboxCounterFrame;
|
|
670
756
|
declare const isInboxPong: (f: ParsedInboxFrame) => f is PongFrame;
|
|
671
757
|
|
|
672
|
-
export { type ActivityFrame, type ActivityUpFrame, type AggregatePresenceFrame, type AggregatePresenceSnapshot, type AnyFrame, type BatchFrame, type Capabilities, type ChannelClientFrame, type ChannelInfo, type ChannelMode, type ChannelReadyFrame, type ChannelServerFrame, type DetailedPresenceFrame, type DetailedPresenceSnapshot, type DirectFrame, type EphemeralFrame, type ErrorFrame, type HistoryResponse, type InboxClientFrame, type InboxCounterFrame, type InboxEntryFrame, type InboxEntryWire, type InboxItemFrame, type InboxItemReadFrame, type InboxItemWire, type InboxMuteFrame, type InboxReadAllFrame, type InboxReadFrame, type InboxReadyFrame, type InboxServerFrame, type MeInfo, type MemberRow, type MembersResponse, type Mention, type MetaFrame, PORTAL_ERROR_HEADER, PROTOCOL_VERSION, type ParsedChannelClientFrame, type ParsedChannelFrame, type ParsedInboxClientFrame, type ParsedInboxFrame, type PingFrame, type PongFrame, type PresenceFrame, type PublishBody, type PublishErrorBody, type PublishErrorCode, REFUSAL_STATUS, type ReadyPresence, type ReassignFrame, type RefusalBody, type RefusalCode, type RetractFrame, type SendAckWire, UPGRADE_PARAMS, type UnknownFrame, type WatermarkFrame, type WireMessage, type WirePresenceParticipant, type WireSender, isActivity, isBatch, isChannelReady, isDirect, isError, isInboxCounter, isInboxEntry, isInboxItem, isInboxPong, isInboxReady, isKnownChannelClientFrame, isKnownChannelFrame, isKnownInboxClientFrame, isKnownInboxFrame, isPong, isPresence, isReassign, isRefusalCode, isRetract, parseChannelClientFrame, parseChannelFrame, parseInboxClientFrame, parseInboxFrame, serializeFrame };
|
|
758
|
+
export { type ActivityFrame, type ActivityUpFrame, type AggregatePresenceFrame, type AggregatePresenceSnapshot, type AnyFrame, type BatchFrame, type Capabilities, type ChannelClientFrame, type ChannelInfo, type ChannelMode, type ChannelReadyFrame, type ChannelServerFrame, type DetailedPresenceFrame, type DetailedPresenceSnapshot, type DirectFrame, type EphemeralFrame, type ErrorFrame, type HistoryResponse, type InboxClientFrame, type InboxCounterFrame, type InboxEntryFrame, type InboxEntryWire, type InboxItemFrame, type InboxItemReadFrame, type InboxItemWire, type InboxMuteFrame, type InboxReadAllFrame, type InboxReadFrame, type InboxReadyFrame, type InboxServerFrame, type MeInfo, type MemberRow, type MembersResponse, type Mention, type MetaFrame, PORTAL_ERROR_HEADER, PROTOCOL_VERSION, type ParsedChannelClientFrame, type ParsedChannelFrame, type ParsedInboxClientFrame, type ParsedInboxFrame, type PingFrame, type PongFrame, type PresenceFrame, type PublishBody, type PublishErrorBody, type PublishErrorCode, REFUSAL_STATUS, type ReadyPresence, type ReassignFrame, type RefusalBody, type RefusalCode, type RetractFrame, type SendAckWire, type ThreadNodeWire, type ThreadsResponse, UPGRADE_PARAMS, type UnknownFrame, type ValidationFailedReason, type WatermarkFrame, type WireMessage, type WirePresenceParticipant, type WireSender, isActivity, isBatch, isChannelReady, isDirect, isError, isInboxCounter, isInboxEntry, isInboxItem, isInboxPong, isInboxReady, isKnownChannelClientFrame, isKnownChannelFrame, isKnownInboxClientFrame, isKnownInboxFrame, isPong, isPresence, isReassign, isRefusalCode, isRetract, parseChannelClientFrame, parseChannelFrame, parseInboxClientFrame, parseInboxFrame, serializeFrame };
|
package/dist/index.d.ts
CHANGED
|
@@ -10,7 +10,8 @@ declare const PROTOCOL_VERSION = 1;
|
|
|
10
10
|
* string literals, so a rename is a compile error rather than a silent 4xx.
|
|
11
11
|
*
|
|
12
12
|
* - `version` — required on every upgrade; unknown → 426.
|
|
13
|
-
* - `token` — the signed JWT. Identifies the user
|
|
13
|
+
* - `token` — the signed JWT. Identifies the user.
|
|
14
|
+
* - `key` — the publishable apiKey, identifying the app (§1 credential transport).
|
|
14
15
|
* - `leaf` — opaque reconnect token; echo back what `ready` gave you, unchanged.
|
|
15
16
|
* - `meta` — initial presence metadata, base64 JSON (standard channels; ≤1KB decoded).
|
|
16
17
|
* - `last` — highest contiguous seq held, sent on reconnect to request replay (§1.4).
|
|
@@ -18,6 +19,7 @@ declare const PROTOCOL_VERSION = 1;
|
|
|
18
19
|
declare const UPGRADE_PARAMS: {
|
|
19
20
|
readonly version: "v";
|
|
20
21
|
readonly token: "token";
|
|
22
|
+
readonly key: "key";
|
|
21
23
|
readonly leaf: "leaf";
|
|
22
24
|
readonly meta: "meta";
|
|
23
25
|
readonly last: "last";
|
|
@@ -81,15 +83,29 @@ type RefusalBody = {
|
|
|
81
83
|
*
|
|
82
84
|
* `blocked_by_middleware` carries user-visible copy in `reason`.
|
|
83
85
|
*/
|
|
84
|
-
type PublishErrorCode = "not_permitted" | "blocked_by_middleware" | "content_too_large" | "rate_limited";
|
|
86
|
+
type PublishErrorCode = "not_permitted" | "blocked_by_middleware" | "content_too_large" | "rate_limited" | "validation_failed";
|
|
87
|
+
/**
|
|
88
|
+
* Why a publish failed validation — the `reason` carried by a `validation_failed`
|
|
89
|
+
* rejection.
|
|
90
|
+
*
|
|
91
|
+
* - `thread_depth_exceeded` — the `threadParentId` would nest the reply deeper than the
|
|
92
|
+
* platform allows.
|
|
93
|
+
*/
|
|
94
|
+
type ValidationFailedReason = "thread_depth_exceeded";
|
|
85
95
|
/**
|
|
86
96
|
* Body of a rejected publish (§3.1).
|
|
87
97
|
*
|
|
88
98
|
* SPEC: §3.1 specifies the shape as `4xx { code, reason? }` but does not map each code
|
|
89
99
|
* to a status, so no status record is exported here (unlike {@link REFUSAL_STATUS}).
|
|
100
|
+
*
|
|
101
|
+
* A `validation_failed` body carries a machine-readable {@link ValidationFailedReason};
|
|
102
|
+
* every other code's `reason` is free-form copy.
|
|
90
103
|
*/
|
|
91
104
|
type PublishErrorBody = {
|
|
92
|
-
code:
|
|
105
|
+
code: "validation_failed";
|
|
106
|
+
reason?: ValidationFailedReason;
|
|
107
|
+
} | {
|
|
108
|
+
code: Exclude<PublishErrorCode, "validation_failed">;
|
|
93
109
|
reason?: string;
|
|
94
110
|
};
|
|
95
111
|
|
|
@@ -147,6 +163,21 @@ type WireMessage = {
|
|
|
147
163
|
retracted: boolean;
|
|
148
164
|
/** Ephemeral messages are not persisted and have `seq: null`. */
|
|
149
165
|
ephemeral: boolean;
|
|
166
|
+
/**
|
|
167
|
+
* Present on a reply: the id of the message it replies to, which is also the id of the
|
|
168
|
+
* thread it belongs to. The parent may itself be a reply (nested threads). A thread comes
|
|
169
|
+
* into existence with its first reply — there is no separate create step.
|
|
170
|
+
*
|
|
171
|
+
* Replies travel in the channel's ordinary `batch` frames to every channel connection;
|
|
172
|
+
* grouping them into threads is the client's job.
|
|
173
|
+
*/
|
|
174
|
+
threadParentId?: string;
|
|
175
|
+
/**
|
|
176
|
+
* Dense per-thread position, present alongside `threadParentId`. Like `seq` this is
|
|
177
|
+
* transport: `GET /history?threadParentId=` pages a thread by it, and it is stripped a
|
|
178
|
+
* layer up.
|
|
179
|
+
*/
|
|
180
|
+
threadSeq?: number;
|
|
150
181
|
};
|
|
151
182
|
|
|
152
183
|
/** Channel mode. Decides presence shape and whether `sender.username` is populated. */
|
|
@@ -190,18 +221,17 @@ type MeInfo = {
|
|
|
190
221
|
capabilities: Capabilities;
|
|
191
222
|
};
|
|
192
223
|
/**
|
|
193
|
-
* A participant as it appears **on the wire** (§1.2 snapshot, §2.1 deltas).
|
|
194
|
-
*
|
|
195
|
-
* SPEC: provisional. `{ userId, claims }` is what the current fixtures prove the wire
|
|
196
|
-
* carries (`channel_ready` presence, and the `presence` frame's `joined`), so it is what
|
|
197
|
-
* this type says — the type follows recorded evidence, never an anticipated change.
|
|
224
|
+
* A participant as it appears **on the wire** (§1.2 snapshot, §2.1 `joined` deltas).
|
|
198
225
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
226
|
+
* `username` is present when the participant has one; `metadata` is the session presence
|
|
227
|
+
* metadata (`?meta=` / a `meta` frame). This carries no `claims` — the token's claim bag is
|
|
228
|
+
* the connected user's own (`me.claims` in `ready`), never another participant's.
|
|
201
229
|
*/
|
|
202
230
|
type WirePresenceParticipant = {
|
|
203
|
-
|
|
204
|
-
|
|
231
|
+
id: string;
|
|
232
|
+
anon: boolean;
|
|
233
|
+
username?: string;
|
|
234
|
+
metadata?: Record<string, unknown>;
|
|
205
235
|
};
|
|
206
236
|
/** Presence snapshot on a standard channel, carried by `ready` (§1.2). */
|
|
207
237
|
type DetailedPresenceSnapshot = {
|
|
@@ -284,15 +314,14 @@ type RetractFrame = {
|
|
|
284
314
|
/**
|
|
285
315
|
* Presence delta on a standard channel (§2.1).
|
|
286
316
|
*
|
|
287
|
-
*
|
|
288
|
-
*
|
|
289
|
-
* element shape is assumed symmetric and remains **unverified**.
|
|
317
|
+
* `joined` carries full participants; `left` carries bare participant ids (a departing
|
|
318
|
+
* participant is identified, not re-described).
|
|
290
319
|
*/
|
|
291
320
|
type DetailedPresenceFrame = {
|
|
292
321
|
t: "presence";
|
|
293
322
|
mode: "detailed";
|
|
294
323
|
joined: WirePresenceParticipant[];
|
|
295
|
-
left:
|
|
324
|
+
left: string[];
|
|
296
325
|
count: number;
|
|
297
326
|
};
|
|
298
327
|
/**
|
|
@@ -415,6 +444,16 @@ type ChannelClientFrame = EphemeralFrame | ActivityUpFrame | WatermarkFrame | Me
|
|
|
415
444
|
type InboxEntryWire = {
|
|
416
445
|
/** The channel id this row tracks. */
|
|
417
446
|
id: string;
|
|
447
|
+
/**
|
|
448
|
+
* Present on a thread row. A thread row is a sibling of its channel's row (same `id`,
|
|
449
|
+
* distinct `threadId`) with its own `latest`, `unread`, read position, and `muted`; no
|
|
450
|
+
* row's state ever reflects another row's traffic. Identity is `(id, threadId)`.
|
|
451
|
+
*/
|
|
452
|
+
threadId?: string;
|
|
453
|
+
/** The enclosing thread, on a nested thread row. Absent on a root thread row. */
|
|
454
|
+
parentThreadId?: string;
|
|
455
|
+
/** The top-level thread this row descends from; equals `threadId` on a root thread row. */
|
|
456
|
+
rootThreadId?: string;
|
|
418
457
|
name?: string;
|
|
419
458
|
meta?: Record<string, unknown>;
|
|
420
459
|
/** Preview of the most recent message. Absent on large channels (seq-only tier). */
|
|
@@ -486,6 +525,8 @@ type InboxServerFrame = InboxReadyFrame | InboxEntryFrame | InboxItemFrame | Inb
|
|
|
486
525
|
type InboxReadFrame = {
|
|
487
526
|
t: "read";
|
|
488
527
|
channelId: string;
|
|
528
|
+
/** Address a thread row instead of the channel row. */
|
|
529
|
+
threadId?: string;
|
|
489
530
|
};
|
|
490
531
|
/** Flip one item's read flag (§5). Never cascades to older items. */
|
|
491
532
|
type InboxItemReadFrame = {
|
|
@@ -501,6 +542,8 @@ type InboxMuteFrame = {
|
|
|
501
542
|
t: "mute";
|
|
502
543
|
channelId: string;
|
|
503
544
|
muted: boolean;
|
|
545
|
+
/** Address a thread row instead of the channel row. */
|
|
546
|
+
threadId?: string;
|
|
504
547
|
};
|
|
505
548
|
/** The complete upstream set for an inbox socket (§5). */
|
|
506
549
|
type InboxClientFrame = InboxReadFrame | InboxItemReadFrame | InboxReadAllFrame | InboxMuteFrame | PingFrame;
|
|
@@ -524,6 +567,12 @@ type PublishBody = {
|
|
|
524
567
|
to?: string;
|
|
525
568
|
/** Declared by the sender; the platform verifies, dedupes, and caps them. */
|
|
526
569
|
mentions?: Mention[];
|
|
570
|
+
/**
|
|
571
|
+
* Reply into a thread: the id of the message being replied to (the thread id). The first
|
|
572
|
+
* reply to a message creates its thread. Nesting beyond the platform's depth cap is
|
|
573
|
+
* rejected with `validation_failed` / `thread_depth_exceeded`.
|
|
574
|
+
*/
|
|
575
|
+
threadParentId?: string;
|
|
527
576
|
};
|
|
528
577
|
/**
|
|
529
578
|
* A successful publish (§3.1) — the wire form of the SendAck.
|
|
@@ -543,14 +592,51 @@ type SendAckWire = {
|
|
|
543
592
|
/**
|
|
544
593
|
* `GET /v1/channels/{channelId}/history` (§3.2).
|
|
545
594
|
*
|
|
546
|
-
* One endpoint serves initial backfill, scroll-up paging (`?before=&limit=`),
|
|
547
|
-
*
|
|
595
|
+
* One endpoint serves initial backfill, scroll-up paging (`?before=&limit=`), gap-fill
|
|
596
|
+
* ranges (`?from=&to=`), and thread paging (`?threadParentId=&before=&limit=`, where
|
|
597
|
+
* `before` is a `threadSeq`). Retracted messages come back as tombstoned envelopes,
|
|
548
598
|
* consistent with live rendering.
|
|
549
599
|
*/
|
|
550
600
|
type HistoryResponse = {
|
|
551
601
|
msgs: WireMessage[];
|
|
552
602
|
hasMore: boolean;
|
|
553
603
|
};
|
|
604
|
+
/**
|
|
605
|
+
* One thread in a channel's thread registry, as returned by
|
|
606
|
+
* `GET /v1/channels/{channelId}/threads`.
|
|
607
|
+
*/
|
|
608
|
+
type ThreadNodeWire = {
|
|
609
|
+
/** The thread id — the id of the message whose first reply created it. */
|
|
610
|
+
id: string;
|
|
611
|
+
/** The enclosing thread; absent on a root thread. */
|
|
612
|
+
parentThreadId?: string;
|
|
613
|
+
/** The top-level ancestor; equals `id` on a root thread. */
|
|
614
|
+
rootThreadId: string;
|
|
615
|
+
/** Nesting level; a root thread is depth `0`. */
|
|
616
|
+
depth: number;
|
|
617
|
+
/** Channel `seq` of the message this thread hangs off. */
|
|
618
|
+
spawnSeq: number;
|
|
619
|
+
/** Who sent the message this thread hangs off. */
|
|
620
|
+
spawnedBy: {
|
|
621
|
+
id: string;
|
|
622
|
+
};
|
|
623
|
+
/** Channel `seq` of the most recent reply. */
|
|
624
|
+
latestSeq: number;
|
|
625
|
+
/** Highest `threadSeq` in the thread — the reply count. */
|
|
626
|
+
threadSeq: number;
|
|
627
|
+
/** Epoch milliseconds. */
|
|
628
|
+
createdAt: number;
|
|
629
|
+
};
|
|
630
|
+
/**
|
|
631
|
+
* `GET /v1/channels/{channelId}/threads?parent={id|""}|root={id}&before=&limit=`.
|
|
632
|
+
*
|
|
633
|
+
* `parent=""` lists root threads; `parent={id}` lists the threads nested directly under
|
|
634
|
+
* one; `root={id}` lists every thread descending from a root.
|
|
635
|
+
*/
|
|
636
|
+
type ThreadsResponse = {
|
|
637
|
+
threads: ThreadNodeWire[];
|
|
638
|
+
hasMore: boolean;
|
|
639
|
+
};
|
|
554
640
|
/** One row of the member directory (§3.3). */
|
|
555
641
|
type MemberRow = {
|
|
556
642
|
userId: string;
|
|
@@ -669,4 +755,4 @@ declare const isInboxItem: (f: ParsedInboxFrame) => f is InboxItemFrame;
|
|
|
669
755
|
declare const isInboxCounter: (f: ParsedInboxFrame) => f is InboxCounterFrame;
|
|
670
756
|
declare const isInboxPong: (f: ParsedInboxFrame) => f is PongFrame;
|
|
671
757
|
|
|
672
|
-
export { type ActivityFrame, type ActivityUpFrame, type AggregatePresenceFrame, type AggregatePresenceSnapshot, type AnyFrame, type BatchFrame, type Capabilities, type ChannelClientFrame, type ChannelInfo, type ChannelMode, type ChannelReadyFrame, type ChannelServerFrame, type DetailedPresenceFrame, type DetailedPresenceSnapshot, type DirectFrame, type EphemeralFrame, type ErrorFrame, type HistoryResponse, type InboxClientFrame, type InboxCounterFrame, type InboxEntryFrame, type InboxEntryWire, type InboxItemFrame, type InboxItemReadFrame, type InboxItemWire, type InboxMuteFrame, type InboxReadAllFrame, type InboxReadFrame, type InboxReadyFrame, type InboxServerFrame, type MeInfo, type MemberRow, type MembersResponse, type Mention, type MetaFrame, PORTAL_ERROR_HEADER, PROTOCOL_VERSION, type ParsedChannelClientFrame, type ParsedChannelFrame, type ParsedInboxClientFrame, type ParsedInboxFrame, type PingFrame, type PongFrame, type PresenceFrame, type PublishBody, type PublishErrorBody, type PublishErrorCode, REFUSAL_STATUS, type ReadyPresence, type ReassignFrame, type RefusalBody, type RefusalCode, type RetractFrame, type SendAckWire, UPGRADE_PARAMS, type UnknownFrame, type WatermarkFrame, type WireMessage, type WirePresenceParticipant, type WireSender, isActivity, isBatch, isChannelReady, isDirect, isError, isInboxCounter, isInboxEntry, isInboxItem, isInboxPong, isInboxReady, isKnownChannelClientFrame, isKnownChannelFrame, isKnownInboxClientFrame, isKnownInboxFrame, isPong, isPresence, isReassign, isRefusalCode, isRetract, parseChannelClientFrame, parseChannelFrame, parseInboxClientFrame, parseInboxFrame, serializeFrame };
|
|
758
|
+
export { type ActivityFrame, type ActivityUpFrame, type AggregatePresenceFrame, type AggregatePresenceSnapshot, type AnyFrame, type BatchFrame, type Capabilities, type ChannelClientFrame, type ChannelInfo, type ChannelMode, type ChannelReadyFrame, type ChannelServerFrame, type DetailedPresenceFrame, type DetailedPresenceSnapshot, type DirectFrame, type EphemeralFrame, type ErrorFrame, type HistoryResponse, type InboxClientFrame, type InboxCounterFrame, type InboxEntryFrame, type InboxEntryWire, type InboxItemFrame, type InboxItemReadFrame, type InboxItemWire, type InboxMuteFrame, type InboxReadAllFrame, type InboxReadFrame, type InboxReadyFrame, type InboxServerFrame, type MeInfo, type MemberRow, type MembersResponse, type Mention, type MetaFrame, PORTAL_ERROR_HEADER, PROTOCOL_VERSION, type ParsedChannelClientFrame, type ParsedChannelFrame, type ParsedInboxClientFrame, type ParsedInboxFrame, type PingFrame, type PongFrame, type PresenceFrame, type PublishBody, type PublishErrorBody, type PublishErrorCode, REFUSAL_STATUS, type ReadyPresence, type ReassignFrame, type RefusalBody, type RefusalCode, type RetractFrame, type SendAckWire, type ThreadNodeWire, type ThreadsResponse, UPGRADE_PARAMS, type UnknownFrame, type ValidationFailedReason, type WatermarkFrame, type WireMessage, type WirePresenceParticipant, type WireSender, isActivity, isBatch, isChannelReady, isDirect, isError, isInboxCounter, isInboxEntry, isInboxItem, isInboxPong, isInboxReady, isKnownChannelClientFrame, isKnownChannelFrame, isKnownInboxClientFrame, isKnownInboxFrame, isPong, isPresence, isReassign, isRefusalCode, isRetract, parseChannelClientFrame, parseChannelFrame, parseInboxClientFrame, parseInboxFrame, serializeFrame };
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ var PROTOCOL_VERSION = 1;
|
|
|
3
3
|
var UPGRADE_PARAMS = {
|
|
4
4
|
version: "v",
|
|
5
5
|
token: "token",
|
|
6
|
+
key: "key",
|
|
6
7
|
leaf: "leaf",
|
|
7
8
|
meta: "meta",
|
|
8
9
|
last: "last"
|
|
@@ -34,11 +35,11 @@ var arrayOf = (v, check) => Array.isArray(v) && v.every(check);
|
|
|
34
35
|
var isStrRec = (v) => isRec(v) && Object.values(v).every(isStr);
|
|
35
36
|
var isMention = (v) => isRec(v) && isStr(v["userId"]);
|
|
36
37
|
var isWireSender = (v) => isRec(v) && isStr(v["id"]) && isBool(v["anon"]) && opt(v["username"], isStr);
|
|
37
|
-
var isWireMessage = (v) => isRec(v) && isStr(v["id"]) && (v["seq"] === null || isNum(v["seq"])) && isStr(v["type"]) && isStr(v["kind"]) && "content" in v && isWireSender(v["sender"]) && isNum(v["timestamp"]) && opt(v["to"], isStr) && (v["mentions"] === void 0 || arrayOf(v["mentions"], isMention)) && isBool(v["retracted"]) && isBool(v["ephemeral"]);
|
|
38
|
+
var isWireMessage = (v) => isRec(v) && isStr(v["id"]) && (v["seq"] === null || isNum(v["seq"])) && isStr(v["type"]) && isStr(v["kind"]) && "content" in v && isWireSender(v["sender"]) && isNum(v["timestamp"]) && opt(v["to"], isStr) && (v["mentions"] === void 0 || arrayOf(v["mentions"], isMention)) && isBool(v["retracted"]) && isBool(v["ephemeral"]) && opt(v["threadParentId"], isStr) && opt(v["threadSeq"], isNum);
|
|
38
39
|
var isChannelInfo = (v) => isRec(v) && isStr(v["id"]) && (v["mode"] === "standard" || v["mode"] === "broadcast") && opt(v["name"], isStr) && opt(v["meta"], isRec);
|
|
39
40
|
var isCapabilities = (v) => isRec(v) && opt(v["publish"], isBool) && opt(v["sendDirect"], isBool);
|
|
40
41
|
var isMeInfo = (v) => isRec(v) && isStr(v["id"]) && isBool(v["anon"]) && isRec(v["claims"]) && isCapabilities(v["capabilities"]);
|
|
41
|
-
var isParticipant = (v) => isRec(v) && isStr(v["
|
|
42
|
+
var isParticipant = (v) => isRec(v) && isStr(v["id"]) && isBool(v["anon"]) && opt(v["username"], isStr) && opt(v["metadata"], isRec);
|
|
42
43
|
var isReadyPresence = (v) => {
|
|
43
44
|
if (!isRec(v)) return false;
|
|
44
45
|
if (v["mode"] === "detailed") {
|
|
@@ -49,7 +50,7 @@ var isReadyPresence = (v) => {
|
|
|
49
50
|
}
|
|
50
51
|
return false;
|
|
51
52
|
};
|
|
52
|
-
var isInboxEntryWire = (v) => isRec(v) && isStr(v["id"]) && opt(v["name"], isStr) && opt(v["meta"], isRec) && opt(v["latest"], isLatest) && isNum(v["unread"]) && isBool(v["muted"]) && isNum(v["at"]);
|
|
53
|
+
var isInboxEntryWire = (v) => isRec(v) && isStr(v["id"]) && opt(v["threadId"], isStr) && opt(v["parentThreadId"], isStr) && opt(v["rootThreadId"], isStr) && opt(v["name"], isStr) && opt(v["meta"], isRec) && opt(v["latest"], isLatest) && isNum(v["unread"]) && isBool(v["muted"]) && isNum(v["at"]);
|
|
53
54
|
var isLatest = (v) => isRec(v) && isStr(v["text"]) && isRec(v["sender"]) && isStr(v["sender"]["id"]) && isNum(v["at"]);
|
|
54
55
|
var isInboxItemWire = (v) => isRec(v) && isStr(v["id"]) && isStr(v["type"]) && opt(v["title"], isStr) && "data" in v && opt(v["channelId"], isStr) && isNum(v["at"]) && isBool(v["read"]);
|
|
55
56
|
var CHANNEL_SERVER_FRAMES = {
|
|
@@ -58,7 +59,7 @@ var CHANNEL_SERVER_FRAMES = {
|
|
|
58
59
|
retract: (v) => isStr(v["id"]) && isNum(v["seq"]) && opt(v["reason"], isStr),
|
|
59
60
|
presence: (v) => {
|
|
60
61
|
if (v["mode"] === "detailed") {
|
|
61
|
-
return arrayOf(v["joined"], isParticipant) && arrayOf(v["left"],
|
|
62
|
+
return arrayOf(v["joined"], isParticipant) && arrayOf(v["left"], isStr) && isNum(v["count"]);
|
|
62
63
|
}
|
|
63
64
|
if (v["mode"] === "aggregate") {
|
|
64
65
|
return isNum(v["count"]) && Array.isArray(v["recent"]);
|
|
@@ -86,10 +87,10 @@ var CHANNEL_CLIENT_FRAMES = {
|
|
|
86
87
|
ping: () => true
|
|
87
88
|
};
|
|
88
89
|
var INBOX_CLIENT_FRAMES = {
|
|
89
|
-
read: (v) => isStr(v["channelId"]),
|
|
90
|
+
read: (v) => isStr(v["channelId"]) && opt(v["threadId"], isStr),
|
|
90
91
|
"item.read": (v) => isStr(v["id"]),
|
|
91
92
|
"read.all": () => true,
|
|
92
|
-
mute: (v) => isStr(v["channelId"]) && isBool(v["muted"]),
|
|
93
|
+
mute: (v) => isStr(v["channelId"]) && isBool(v["muted"]) && opt(v["threadId"], isStr),
|
|
93
94
|
ping: () => true
|
|
94
95
|
};
|
|
95
96
|
var validatorFor = (table, t) => Object.hasOwn(table, t) ? table[t] : void 0;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/parse.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Protocol version carried by every upgrade as `?v=` (§1.1, §6).\n *\n * An unknown version is refused at the upgrade with HTTP 426 `unsupported_version`.\n * Within v1 the protocol evolves additively only; a breaking change bumps this.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Upgrade query-parameter names (§1.1). Build upgrade URLs from these rather than\n * string literals, so a rename is a compile error rather than a silent 4xx.\n *\n * - `version` — required on every upgrade; unknown → 426.\n * - `token` — the signed JWT. Identifies the user; the apiKey is resolved from it.\n * - `leaf` — opaque reconnect token; echo back what `ready` gave you, unchanged.\n * - `meta` — initial presence metadata, base64 JSON (standard channels; ≤1KB decoded).\n * - `last` — highest contiguous seq held, sent on reconnect to request replay (§1.4).\n */\nexport const UPGRADE_PARAMS = {\n version: \"v\",\n token: \"token\",\n leaf: \"leaf\",\n meta: \"meta\",\n last: \"last\",\n} as const;\n\n/**\n * Response header carrying the refusal code on a refused upgrade (§1.1).\n *\n * Duplicates `code` from the body so a client behind a body-eating proxy can still\n * tell why the socket never opened.\n */\nexport const PORTAL_ERROR_HEADER = \"x-portal-error\";\n","/**\n * Why an upgrade was refused (§1.1).\n *\n * Refusals happen at the HTTP upgrade — **the socket never opens**. They are therefore\n * disjoint from {@link PublishErrorCode} (HTTP publish rejections) and from the\n * in-session `error` frame, which both presuppose a working connection.\n */\nexport type RefusalCode =\n | \"invalid_token\"\n | \"token_expired\"\n | \"invalid_api_key\"\n | \"not_member\"\n | \"banned\"\n | \"anonymous_not_allowed\"\n | \"unknown_channel\"\n | \"unsupported_version\"\n | \"channel_at_capacity\";\n\n/**\n * The HTTP status each refusal is delivered with (§1.1 table).\n *\n * The mapping is many-to-one: status alone does not identify the cause, so read\n * `code` from the body (or the `x-portal-error` header) rather than branching on status.\n */\nexport const REFUSAL_STATUS = {\n invalid_token: 401,\n token_expired: 401,\n invalid_api_key: 403,\n not_member: 403,\n banned: 403,\n anonymous_not_allowed: 403,\n unknown_channel: 404,\n unsupported_version: 426,\n channel_at_capacity: 429,\n} as const satisfies Record<RefusalCode, number>;\n\nconst REFUSAL_CODES: ReadonlySet<string> = new Set(Object.keys(REFUSAL_STATUS));\n\n/**\n * Whether a value is a refusal code this version knows (§1.1).\n *\n * A refusal body arriving with an unrecognised code is not a refusal this client can\n * reason about — treat it as an opaque failure rather than coercing it.\n */\nexport const isRefusalCode = (value: unknown): value is RefusalCode =>\n typeof value === \"string\" && REFUSAL_CODES.has(value);\n\n/** Body of a refused upgrade (§1.1). */\nexport type RefusalBody = {\n code: RefusalCode;\n reason?: string;\n /**\n * Seconds to wait before retrying. Documented for `channel_at_capacity` (429) only\n * (§1.1); absent on every other refusal.\n */\n retryAfter?: number;\n};\n\n/**\n * Why an HTTP publish was rejected (§3.1).\n *\n * Deliberately NOT merged into {@link RefusalCode}: these arrive on a live connection\n * in response to `POST /v1/channels/{id}/messages`, and a client reacts to them per\n * send, not per connection.\n *\n * `blocked_by_middleware` carries user-visible copy in `reason`.\n */\nexport type PublishErrorCode =\n | \"not_permitted\"\n | \"blocked_by_middleware\"\n | \"content_too_large\"\n | \"rate_limited\";\n\n/**\n * Body of a rejected publish (§3.1).\n *\n * SPEC: §3.1 specifies the shape as `4xx { code, reason? }` but does not map each code\n * to a status, so no status record is exported here (unlike {@link REFUSAL_STATUS}).\n */\nexport type PublishErrorBody = {\n code: PublishErrorCode;\n reason?: string;\n};\n","import type {\n ActivityFrame,\n BatchFrame,\n ChannelClientFrame,\n ChannelReadyFrame,\n ChannelServerFrame,\n Capabilities,\n ChannelInfo,\n DirectFrame,\n ErrorFrame,\n MeInfo,\n PongFrame,\n PresenceFrame,\n ReadyPresence,\n ReassignFrame,\n RetractFrame,\n WirePresenceParticipant,\n} from \"./channel.js\";\nimport type {\n InboxClientFrame,\n InboxCounterFrame,\n InboxEntryFrame,\n InboxEntryWire,\n InboxItemFrame,\n InboxItemWire,\n InboxReadyFrame,\n InboxServerFrame,\n} from \"./inbox.js\";\nimport type {\n AnyFrame,\n ParsedChannelClientFrame,\n ParsedChannelFrame,\n ParsedInboxClientFrame,\n ParsedInboxFrame,\n UnknownFrame,\n} from \"./frames.js\";\nimport type { Mention, WireMessage, WireSender } from \"./message.js\";\n\n// ============================================================\n// Primitives\n// ============================================================\n\ntype Rec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is Rec =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nconst isStr = (v: unknown): v is string => typeof v === \"string\";\nconst isBool = (v: unknown): v is boolean => typeof v === \"boolean\";\n\n/** JSON has no NaN/Infinity, so any non-finite number means a hand-built object. */\nconst isNum = (v: unknown): v is number =>\n typeof v === \"number\" && Number.isFinite(v);\n\n/** An absent optional field is valid; a present one must match. */\nconst opt = <T>(v: unknown, check: (x: unknown) => x is T): boolean =>\n v === undefined || check(v);\n\nconst arrayOf = <T>(v: unknown, check: (x: unknown) => x is T): v is T[] =>\n Array.isArray(v) && v.every(check);\n\nconst isStrRec = (v: unknown): v is Record<string, string> =>\n isRec(v) && Object.values(v).every(isStr);\n\n// ============================================================\n// Shape validators\n//\n// Each validator checks exactly the fields its type declares required — no more, no\n// less. Unknown fields are ignored and survive untouched (§6), but a frame missing a\n// required field is rejected rather than returned as a type that lies about it.\n// ============================================================\n\nconst isMention = (v: unknown): v is Mention => isRec(v) && isStr(v[\"userId\"]);\n\nconst isWireSender = (v: unknown): v is WireSender =>\n isRec(v) && isStr(v[\"id\"]) && isBool(v[\"anon\"]) && opt(v[\"username\"], isStr);\n\nconst isWireMessage = (v: unknown): v is WireMessage =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"seq\"] === null || isNum(v[\"seq\"])) &&\n isStr(v[\"type\"]) &&\n isStr(v[\"kind\"]) &&\n \"content\" in v &&\n isWireSender(v[\"sender\"]) &&\n isNum(v[\"timestamp\"]) &&\n opt(v[\"to\"], isStr) &&\n (v[\"mentions\"] === undefined || arrayOf(v[\"mentions\"], isMention)) &&\n isBool(v[\"retracted\"]) &&\n isBool(v[\"ephemeral\"]);\n\nconst isChannelInfo = (v: unknown): v is ChannelInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"mode\"] === \"standard\" || v[\"mode\"] === \"broadcast\") &&\n opt(v[\"name\"], isStr) &&\n opt(v[\"meta\"], isRec);\n\nconst isCapabilities = (v: unknown): v is Capabilities =>\n isRec(v) && opt(v[\"publish\"], isBool) && opt(v[\"sendDirect\"], isBool);\n\nconst isMeInfo = (v: unknown): v is MeInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isBool(v[\"anon\"]) &&\n isRec(v[\"claims\"]) &&\n isCapabilities(v[\"capabilities\"]);\n\nconst isParticipant = (v: unknown): v is WirePresenceParticipant =>\n isRec(v) && isStr(v[\"userId\"]) && isRec(v[\"claims\"]);\n\nconst isReadyPresence = (v: unknown): v is ReadyPresence => {\n if (!isRec(v)) return false;\n if (v[\"mode\"] === \"detailed\") {\n return arrayOf(v[\"participants\"], isParticipant) && isNum(v[\"count\"]);\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && (v[\"recent\"] === undefined || Array.isArray(v[\"recent\"]));\n }\n return false;\n};\n\nconst isInboxEntryWire = (v: unknown): v is InboxEntryWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n opt(v[\"name\"], isStr) &&\n opt(v[\"meta\"], isRec) &&\n opt(v[\"latest\"], isLatest) &&\n isNum(v[\"unread\"]) &&\n isBool(v[\"muted\"]) &&\n isNum(v[\"at\"]);\n\nconst isLatest = (v: unknown): v is InboxEntryWire[\"latest\"] =>\n isRec(v) &&\n isStr(v[\"text\"]) &&\n isRec(v[\"sender\"]) &&\n isStr((v[\"sender\"] as Rec)[\"id\"]) &&\n isNum(v[\"at\"]);\n\nconst isInboxItemWire = (v: unknown): v is InboxItemWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isStr(v[\"type\"]) &&\n opt(v[\"title\"], isStr) &&\n \"data\" in v &&\n opt(v[\"channelId\"], isStr) &&\n isNum(v[\"at\"]) &&\n isBool(v[\"read\"]);\n\n// ============================================================\n// Frame tables — the single source of truth for \"known `t`\"\n// ============================================================\n\ntype Validator = (v: Rec) => boolean;\n\nconst CHANNEL_SERVER_FRAMES: Record<ChannelServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n isChannelInfo(v[\"channel\"]) &&\n isMeInfo(v[\"me\"]) &&\n isNum(v[\"seq\"]) &&\n isStr(v[\"leaf\"]) &&\n isReadyPresence(v[\"presence\"]) &&\n opt(v[\"watermark\"], isNum) &&\n opt(v[\"ext\"], isRec) &&\n opt(v[\"bindings\"], isStrRec),\n batch: (v) => arrayOf(v[\"msgs\"], isWireMessage),\n retract: (v) => isStr(v[\"id\"]) && isNum(v[\"seq\"]) && opt(v[\"reason\"], isStr),\n presence: (v) => {\n if (v[\"mode\"] === \"detailed\") {\n return (\n arrayOf(v[\"joined\"], isParticipant) &&\n arrayOf(v[\"left\"], isParticipant) &&\n isNum(v[\"count\"])\n );\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && Array.isArray(v[\"recent\"]);\n }\n return false;\n },\n activity: (v) => isStr(v[\"userId\"]) && isStr(v[\"kind\"]) && isNum(v[\"since\"]),\n direct: (v) => isWireMessage(v[\"msg\"]),\n reassign: (v) => isStr(v[\"leaf\"]),\n error: (v) => isStr(v[\"code\"]) && opt(v[\"reason\"], isStr) && opt(v[\"ref\"], isStr),\n pong: () => true,\n};\n\nconst INBOX_SERVER_FRAMES: Record<InboxServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n arrayOf(v[\"entries\"], isInboxEntryWire) &&\n arrayOf(v[\"items\"], isInboxItemWire) &&\n isNum(v[\"counter\"]),\n entry: (v) => isInboxEntryWire(v[\"entry\"]),\n item: (v) => isInboxItemWire(v[\"item\"]),\n counter: (v) => isNum(v[\"n\"]),\n pong: () => true,\n};\n\n// C→S tables. A server (or the test mock) receives these; the same totality contract\n// applies as for the S→C tables. `ping` and `activity` also appear in S→C sets with\n// different shapes — the families are disjoint, so each is validated against its own\n// direction's table.\nconst CHANNEL_CLIENT_FRAMES: Record<ChannelClientFrame[\"t\"], Validator> = {\n ephemeral: (v) => isStr(v[\"cl\"]) && isStr(v[\"type\"]) && \"content\" in v,\n activity: (v) => isStr(v[\"kind\"]),\n watermark: (v) => isNum(v[\"seq\"]),\n meta: (v) => isRec(v[\"metadata\"]),\n ping: () => true,\n};\n\nconst INBOX_CLIENT_FRAMES: Record<InboxClientFrame[\"t\"], Validator> = {\n read: (v) => isStr(v[\"channelId\"]),\n \"item.read\": (v) => isStr(v[\"id\"]),\n \"read.all\": () => true,\n mute: (v) => isStr(v[\"channelId\"]) && isBool(v[\"muted\"]),\n ping: () => true,\n};\n\nconst validatorFor = <T extends string>(\n table: Record<T, Validator>,\n t: string,\n): Validator | undefined =>\n Object.hasOwn(table, t) ? table[t as T] : undefined;\n\n// ============================================================\n// Parse / serialize\n// ============================================================\n\nconst parseJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * A frame-shaped value: an object carrying a string `t`. Anything else is not a frame.\n */\nconst asFrameShape = (raw: string): (Rec & { t: string }) | null => {\n const value = parseJson(raw);\n if (!isRec(value) || !isStr(value[\"t\"])) return null;\n return value as Rec & { t: string };\n};\n\n/**\n * Parse a text frame from a **channel** socket (S→C).\n *\n * Total and non-throwing:\n * - malformed JSON, a non-object, or a missing/non-string `t` → `null`\n * - a known `t` whose shape does not match → `null`\n * - an unknown `t` → the frame is returned intact as an {@link UnknownFrame} (§6),\n * because forward compatibility says ignore it, not lose it\n *\n * Unknown *fields* on known frames are preserved, so `parse` → {@link serializeFrame}\n * round-trips without dropping anything.\n */\nexport function parseChannelFrame(raw: string): ParsedChannelFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelServerFrame) : null;\n}\n\n/**\n * Parse a text frame from an **inbox** socket (S→C).\n *\n * Same contract as {@link parseChannelFrame}. The two families are disjoint: an inbox\n * `ready` and a channel `ready` share a `t` but not a shape, so a frame must be parsed\n * with the function matching the socket it arrived on.\n */\nexport function parseInboxFrame(raw: string): ParsedInboxFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxServerFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on a **channel** socket (C→S).\n *\n * The upstream counterpart of {@link parseChannelFrame}, for a server or test mock\n * receiving the frames a client sends. Same totality contract: `null` on malformed JSON\n * / missing `t` / a known `t` with a bad shape, and an {@link UnknownFrame} passthrough\n * for an unrecognised `t` (§6).\n */\nexport function parseChannelClientFrame(\n raw: string,\n): ParsedChannelClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelClientFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on an **inbox** socket (C→S).\n *\n * Same contract as {@link parseChannelClientFrame}.\n */\nexport function parseInboxClientFrame(raw: string): ParsedInboxClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxClientFrame) : null;\n}\n\n/**\n * Serialize a frame to a JSON text frame.\n *\n * Primarily for C→S sends. It accepts any frame so that a parsed S→C frame can be\n * re-serialized intact — the round-trip that proves unknown fields survive (§6).\n */\nexport function serializeFrame(frame: AnyFrame): string {\n return JSON.stringify(frame);\n}\n\n// ============================================================\n// Guards\n//\n// These narrow a frame that has already been through parseChannelFrame /\n// parseInboxFrame, which is what makes a `t` check sufficient: the parser has already\n// validated the shape, so narrowing on the discriminator cannot lie.\n// ============================================================\n\n/** True when the parser recognised this channel frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownChannelFrame = (\n frame: ParsedChannelFrame,\n): frame is ChannelServerFrame => Object.hasOwn(CHANNEL_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this inbox frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownInboxFrame = (\n frame: ParsedInboxFrame,\n): frame is InboxServerFrame => Object.hasOwn(INBOX_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S channel frame (not an `UnknownFrame`). */\nexport const isKnownChannelClientFrame = (\n frame: ParsedChannelClientFrame,\n): frame is ChannelClientFrame => Object.hasOwn(CHANNEL_CLIENT_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S inbox frame (not an `UnknownFrame`). */\nexport const isKnownInboxClientFrame = (\n frame: ParsedInboxClientFrame,\n): frame is InboxClientFrame => Object.hasOwn(INBOX_CLIENT_FRAMES, frame.t);\n\n// ── Channel S→C ─────────────────────────────────────────────\n\nexport const isChannelReady = (f: ParsedChannelFrame): f is ChannelReadyFrame =>\n f.t === \"ready\";\nexport const isBatch = (f: ParsedChannelFrame): f is BatchFrame => f.t === \"batch\";\nexport const isRetract = (f: ParsedChannelFrame): f is RetractFrame =>\n f.t === \"retract\";\nexport const isPresence = (f: ParsedChannelFrame): f is PresenceFrame =>\n f.t === \"presence\";\nexport const isActivity = (f: ParsedChannelFrame): f is ActivityFrame =>\n f.t === \"activity\";\nexport const isDirect = (f: ParsedChannelFrame): f is DirectFrame => f.t === \"direct\";\nexport const isReassign = (f: ParsedChannelFrame): f is ReassignFrame =>\n f.t === \"reassign\";\nexport const isError = (f: ParsedChannelFrame): f is ErrorFrame => f.t === \"error\";\nexport const isPong = (f: ParsedChannelFrame): f is PongFrame => f.t === \"pong\";\n\n// ── Inbox S→C ───────────────────────────────────────────────\n\nexport const isInboxReady = (f: ParsedInboxFrame): f is InboxReadyFrame =>\n f.t === \"ready\";\nexport const isInboxEntry = (f: ParsedInboxFrame): f is InboxEntryFrame =>\n f.t === \"entry\";\nexport const isInboxItem = (f: ParsedInboxFrame): f is InboxItemFrame =>\n f.t === \"item\";\nexport const isInboxCounter = (f: ParsedInboxFrame): f is InboxCounterFrame =>\n f.t === \"counter\";\nexport const isInboxPong = (f: ParsedInboxFrame): f is PongFrame => f.t === \"pong\";\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/constants.ts","../src/errors.ts","../src/parse.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Protocol version carried by every upgrade as `?v=` (§1.1, §6).\n *\n * An unknown version is refused at the upgrade with HTTP 426 `unsupported_version`.\n * Within v1 the protocol evolves additively only; a breaking change bumps this.\n */\nexport const PROTOCOL_VERSION = 1;\n\n/**\n * Upgrade query-parameter names (§1.1). Build upgrade URLs from these rather than\n * string literals, so a rename is a compile error rather than a silent 4xx.\n *\n * - `version` — required on every upgrade; unknown → 426.\n * - `token` — the signed JWT. Identifies the user.\n * - `key` — the publishable apiKey, identifying the app (§1 credential transport).\n * - `leaf` — opaque reconnect token; echo back what `ready` gave you, unchanged.\n * - `meta` — initial presence metadata, base64 JSON (standard channels; ≤1KB decoded).\n * - `last` — highest contiguous seq held, sent on reconnect to request replay (§1.4).\n */\nexport const UPGRADE_PARAMS = {\n version: \"v\",\n token: \"token\",\n key: \"key\",\n leaf: \"leaf\",\n meta: \"meta\",\n last: \"last\",\n} as const;\n\n/**\n * Response header carrying the refusal code on a refused upgrade (§1.1).\n *\n * Duplicates `code` from the body so a client behind a body-eating proxy can still\n * tell why the socket never opened.\n */\nexport const PORTAL_ERROR_HEADER = \"x-portal-error\";\n","/**\n * Why an upgrade was refused (§1.1).\n *\n * Refusals happen at the HTTP upgrade — **the socket never opens**. They are therefore\n * disjoint from {@link PublishErrorCode} (HTTP publish rejections) and from the\n * in-session `error` frame, which both presuppose a working connection.\n */\nexport type RefusalCode =\n | \"invalid_token\"\n | \"token_expired\"\n | \"invalid_api_key\"\n | \"not_member\"\n | \"banned\"\n | \"anonymous_not_allowed\"\n | \"unknown_channel\"\n | \"unsupported_version\"\n | \"channel_at_capacity\";\n\n/**\n * The HTTP status each refusal is delivered with (§1.1 table).\n *\n * The mapping is many-to-one: status alone does not identify the cause, so read\n * `code` from the body (or the `x-portal-error` header) rather than branching on status.\n */\nexport const REFUSAL_STATUS = {\n invalid_token: 401,\n token_expired: 401,\n invalid_api_key: 403,\n not_member: 403,\n banned: 403,\n anonymous_not_allowed: 403,\n unknown_channel: 404,\n unsupported_version: 426,\n channel_at_capacity: 429,\n} as const satisfies Record<RefusalCode, number>;\n\nconst REFUSAL_CODES: ReadonlySet<string> = new Set(Object.keys(REFUSAL_STATUS));\n\n/**\n * Whether a value is a refusal code this version knows (§1.1).\n *\n * A refusal body arriving with an unrecognised code is not a refusal this client can\n * reason about — treat it as an opaque failure rather than coercing it.\n */\nexport const isRefusalCode = (value: unknown): value is RefusalCode =>\n typeof value === \"string\" && REFUSAL_CODES.has(value);\n\n/** Body of a refused upgrade (§1.1). */\nexport type RefusalBody = {\n code: RefusalCode;\n reason?: string;\n /**\n * Seconds to wait before retrying. Documented for `channel_at_capacity` (429) only\n * (§1.1); absent on every other refusal.\n */\n retryAfter?: number;\n};\n\n/**\n * Why an HTTP publish was rejected (§3.1).\n *\n * Deliberately NOT merged into {@link RefusalCode}: these arrive on a live connection\n * in response to `POST /v1/channels/{id}/messages`, and a client reacts to them per\n * send, not per connection.\n *\n * `blocked_by_middleware` carries user-visible copy in `reason`.\n */\nexport type PublishErrorCode =\n | \"not_permitted\"\n | \"blocked_by_middleware\"\n | \"content_too_large\"\n | \"rate_limited\"\n | \"validation_failed\";\n\n/**\n * Why a publish failed validation — the `reason` carried by a `validation_failed`\n * rejection.\n *\n * - `thread_depth_exceeded` — the `threadParentId` would nest the reply deeper than the\n * platform allows.\n */\nexport type ValidationFailedReason = \"thread_depth_exceeded\";\n\n/**\n * Body of a rejected publish (§3.1).\n *\n * SPEC: §3.1 specifies the shape as `4xx { code, reason? }` but does not map each code\n * to a status, so no status record is exported here (unlike {@link REFUSAL_STATUS}).\n *\n * A `validation_failed` body carries a machine-readable {@link ValidationFailedReason};\n * every other code's `reason` is free-form copy.\n */\nexport type PublishErrorBody =\n | { code: \"validation_failed\"; reason?: ValidationFailedReason }\n | { code: Exclude<PublishErrorCode, \"validation_failed\">; reason?: string };\n\n","import type {\n ActivityFrame,\n BatchFrame,\n ChannelClientFrame,\n ChannelReadyFrame,\n ChannelServerFrame,\n Capabilities,\n ChannelInfo,\n DirectFrame,\n ErrorFrame,\n MeInfo,\n PongFrame,\n PresenceFrame,\n ReadyPresence,\n ReassignFrame,\n RetractFrame,\n WirePresenceParticipant,\n} from \"./channel.js\";\nimport type {\n InboxClientFrame,\n InboxCounterFrame,\n InboxEntryFrame,\n InboxEntryWire,\n InboxItemFrame,\n InboxItemWire,\n InboxReadyFrame,\n InboxServerFrame,\n} from \"./inbox.js\";\nimport type {\n AnyFrame,\n ParsedChannelClientFrame,\n ParsedChannelFrame,\n ParsedInboxClientFrame,\n ParsedInboxFrame,\n UnknownFrame,\n} from \"./frames.js\";\nimport type { Mention, WireMessage, WireSender } from \"./message.js\";\n\n// ============================================================\n// Primitives\n// ============================================================\n\ntype Rec = Record<string, unknown>;\n\nconst isRec = (v: unknown): v is Rec =>\n typeof v === \"object\" && v !== null && !Array.isArray(v);\n\nconst isStr = (v: unknown): v is string => typeof v === \"string\";\nconst isBool = (v: unknown): v is boolean => typeof v === \"boolean\";\n\n/** JSON has no NaN/Infinity, so any non-finite number means a hand-built object. */\nconst isNum = (v: unknown): v is number =>\n typeof v === \"number\" && Number.isFinite(v);\n\n/** An absent optional field is valid; a present one must match. */\nconst opt = <T>(v: unknown, check: (x: unknown) => x is T): boolean =>\n v === undefined || check(v);\n\nconst arrayOf = <T>(v: unknown, check: (x: unknown) => x is T): v is T[] =>\n Array.isArray(v) && v.every(check);\n\nconst isStrRec = (v: unknown): v is Record<string, string> =>\n isRec(v) && Object.values(v).every(isStr);\n\n// ============================================================\n// Shape validators\n//\n// Each validator checks exactly the fields its type declares required — no more, no\n// less. Unknown fields are ignored and survive untouched (§6), but a frame missing a\n// required field is rejected rather than returned as a type that lies about it.\n// ============================================================\n\nconst isMention = (v: unknown): v is Mention => isRec(v) && isStr(v[\"userId\"]);\n\nconst isWireSender = (v: unknown): v is WireSender =>\n isRec(v) && isStr(v[\"id\"]) && isBool(v[\"anon\"]) && opt(v[\"username\"], isStr);\n\nconst isWireMessage = (v: unknown): v is WireMessage =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"seq\"] === null || isNum(v[\"seq\"])) &&\n isStr(v[\"type\"]) &&\n isStr(v[\"kind\"]) &&\n \"content\" in v &&\n isWireSender(v[\"sender\"]) &&\n isNum(v[\"timestamp\"]) &&\n opt(v[\"to\"], isStr) &&\n (v[\"mentions\"] === undefined || arrayOf(v[\"mentions\"], isMention)) &&\n isBool(v[\"retracted\"]) &&\n isBool(v[\"ephemeral\"]) &&\n opt(v[\"threadParentId\"], isStr) &&\n opt(v[\"threadSeq\"], isNum);\n\n\nconst isChannelInfo = (v: unknown): v is ChannelInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n (v[\"mode\"] === \"standard\" || v[\"mode\"] === \"broadcast\") &&\n opt(v[\"name\"], isStr) &&\n opt(v[\"meta\"], isRec);\n\nconst isCapabilities = (v: unknown): v is Capabilities =>\n isRec(v) && opt(v[\"publish\"], isBool) && opt(v[\"sendDirect\"], isBool);\n\nconst isMeInfo = (v: unknown): v is MeInfo =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isBool(v[\"anon\"]) &&\n isRec(v[\"claims\"]) &&\n isCapabilities(v[\"capabilities\"]);\n\nconst isParticipant = (v: unknown): v is WirePresenceParticipant =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isBool(v[\"anon\"]) &&\n opt(v[\"username\"], isStr) &&\n opt(v[\"metadata\"], isRec);\n\nconst isReadyPresence = (v: unknown): v is ReadyPresence => {\n if (!isRec(v)) return false;\n if (v[\"mode\"] === \"detailed\") {\n return arrayOf(v[\"participants\"], isParticipant) && isNum(v[\"count\"]);\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && (v[\"recent\"] === undefined || Array.isArray(v[\"recent\"]));\n }\n return false;\n};\n\nconst isInboxEntryWire = (v: unknown): v is InboxEntryWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n opt(v[\"threadId\"], isStr) &&\n opt(v[\"parentThreadId\"], isStr) &&\n opt(v[\"rootThreadId\"], isStr) &&\n opt(v[\"name\"], isStr) &&\n\n opt(v[\"meta\"], isRec) &&\n opt(v[\"latest\"], isLatest) &&\n isNum(v[\"unread\"]) &&\n isBool(v[\"muted\"]) &&\n isNum(v[\"at\"]);\n\nconst isLatest = (v: unknown): v is InboxEntryWire[\"latest\"] =>\n isRec(v) &&\n isStr(v[\"text\"]) &&\n isRec(v[\"sender\"]) &&\n isStr((v[\"sender\"] as Rec)[\"id\"]) &&\n isNum(v[\"at\"]);\n\nconst isInboxItemWire = (v: unknown): v is InboxItemWire =>\n isRec(v) &&\n isStr(v[\"id\"]) &&\n isStr(v[\"type\"]) &&\n opt(v[\"title\"], isStr) &&\n \"data\" in v &&\n opt(v[\"channelId\"], isStr) &&\n isNum(v[\"at\"]) &&\n isBool(v[\"read\"]);\n\n// ============================================================\n// Frame tables — the single source of truth for \"known `t`\"\n// ============================================================\n\ntype Validator = (v: Rec) => boolean;\n\nconst CHANNEL_SERVER_FRAMES: Record<ChannelServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n isChannelInfo(v[\"channel\"]) &&\n isMeInfo(v[\"me\"]) &&\n isNum(v[\"seq\"]) &&\n isStr(v[\"leaf\"]) &&\n isReadyPresence(v[\"presence\"]) &&\n opt(v[\"watermark\"], isNum) &&\n opt(v[\"ext\"], isRec) &&\n opt(v[\"bindings\"], isStrRec),\n batch: (v) => arrayOf(v[\"msgs\"], isWireMessage),\n retract: (v) => isStr(v[\"id\"]) && isNum(v[\"seq\"]) && opt(v[\"reason\"], isStr),\n presence: (v) => {\n if (v[\"mode\"] === \"detailed\") {\n return (\n arrayOf(v[\"joined\"], isParticipant) &&\n arrayOf(v[\"left\"], isStr) &&\n isNum(v[\"count\"])\n );\n }\n if (v[\"mode\"] === \"aggregate\") {\n return isNum(v[\"count\"]) && Array.isArray(v[\"recent\"]);\n }\n return false;\n },\n activity: (v) => isStr(v[\"userId\"]) && isStr(v[\"kind\"]) && isNum(v[\"since\"]),\n direct: (v) => isWireMessage(v[\"msg\"]),\n reassign: (v) => isStr(v[\"leaf\"]),\n error: (v) => isStr(v[\"code\"]) && opt(v[\"reason\"], isStr) && opt(v[\"ref\"], isStr),\n pong: () => true,\n};\n\nconst INBOX_SERVER_FRAMES: Record<InboxServerFrame[\"t\"], Validator> = {\n ready: (v) =>\n arrayOf(v[\"entries\"], isInboxEntryWire) &&\n arrayOf(v[\"items\"], isInboxItemWire) &&\n isNum(v[\"counter\"]),\n entry: (v) => isInboxEntryWire(v[\"entry\"]),\n item: (v) => isInboxItemWire(v[\"item\"]),\n counter: (v) => isNum(v[\"n\"]),\n pong: () => true,\n};\n\n// C→S tables. A server (or the test mock) receives these; the same totality contract\n// applies as for the S→C tables. `ping` and `activity` also appear in S→C sets with\n// different shapes — the families are disjoint, so each is validated against its own\n// direction's table.\nconst CHANNEL_CLIENT_FRAMES: Record<ChannelClientFrame[\"t\"], Validator> = {\n ephemeral: (v) => isStr(v[\"cl\"]) && isStr(v[\"type\"]) && \"content\" in v,\n activity: (v) => isStr(v[\"kind\"]),\n watermark: (v) => isNum(v[\"seq\"]),\n meta: (v) => isRec(v[\"metadata\"]),\n ping: () => true,\n};\n\nconst INBOX_CLIENT_FRAMES: Record<InboxClientFrame[\"t\"], Validator> = {\n read: (v) => isStr(v[\"channelId\"]) && opt(v[\"threadId\"], isStr),\n \"item.read\": (v) => isStr(v[\"id\"]),\n \"read.all\": () => true,\n mute: (v) => isStr(v[\"channelId\"]) && isBool(v[\"muted\"]) && opt(v[\"threadId\"], isStr),\n\n ping: () => true,\n};\n\nconst validatorFor = <T extends string>(\n table: Record<T, Validator>,\n t: string,\n): Validator | undefined =>\n Object.hasOwn(table, t) ? table[t as T] : undefined;\n\n// ============================================================\n// Parse / serialize\n// ============================================================\n\nconst parseJson = (raw: string): unknown => {\n try {\n return JSON.parse(raw) as unknown;\n } catch {\n return undefined;\n }\n};\n\n/**\n * A frame-shaped value: an object carrying a string `t`. Anything else is not a frame.\n */\nconst asFrameShape = (raw: string): (Rec & { t: string }) | null => {\n const value = parseJson(raw);\n if (!isRec(value) || !isStr(value[\"t\"])) return null;\n return value as Rec & { t: string };\n};\n\n/**\n * Parse a text frame from a **channel** socket (S→C).\n *\n * Total and non-throwing:\n * - malformed JSON, a non-object, or a missing/non-string `t` → `null`\n * - a known `t` whose shape does not match → `null`\n * - an unknown `t` → the frame is returned intact as an {@link UnknownFrame} (§6),\n * because forward compatibility says ignore it, not lose it\n *\n * Unknown *fields* on known frames are preserved, so `parse` → {@link serializeFrame}\n * round-trips without dropping anything.\n */\nexport function parseChannelFrame(raw: string): ParsedChannelFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelServerFrame) : null;\n}\n\n/**\n * Parse a text frame from an **inbox** socket (S→C).\n *\n * Same contract as {@link parseChannelFrame}. The two families are disjoint: an inbox\n * `ready` and a channel `ready` share a `t` but not a shape, so a frame must be parsed\n * with the function matching the socket it arrived on.\n */\nexport function parseInboxFrame(raw: string): ParsedInboxFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_SERVER_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxServerFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on a **channel** socket (C→S).\n *\n * The upstream counterpart of {@link parseChannelFrame}, for a server or test mock\n * receiving the frames a client sends. Same totality contract: `null` on malformed JSON\n * / missing `t` / a known `t` with a bad shape, and an {@link UnknownFrame} passthrough\n * for an unrecognised `t` (§6).\n */\nexport function parseChannelClientFrame(\n raw: string,\n): ParsedChannelClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(CHANNEL_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as ChannelClientFrame) : null;\n}\n\n/**\n * Parse a text frame a client sent on an **inbox** socket (C→S).\n *\n * Same contract as {@link parseChannelClientFrame}.\n */\nexport function parseInboxClientFrame(raw: string): ParsedInboxClientFrame | null {\n const frame = asFrameShape(raw);\n if (frame === null) return null;\n\n const validate = validatorFor(INBOX_CLIENT_FRAMES, frame.t);\n if (validate === undefined) return frame as UnknownFrame;\n\n return validate(frame) ? (frame as InboxClientFrame) : null;\n}\n\n/**\n * Serialize a frame to a JSON text frame.\n *\n * Primarily for C→S sends. It accepts any frame so that a parsed S→C frame can be\n * re-serialized intact — the round-trip that proves unknown fields survive (§6).\n */\nexport function serializeFrame(frame: AnyFrame): string {\n return JSON.stringify(frame);\n}\n\n// ============================================================\n// Guards\n//\n// These narrow a frame that has already been through parseChannelFrame /\n// parseInboxFrame, which is what makes a `t` check sufficient: the parser has already\n// validated the shape, so narrowing on the discriminator cannot lie.\n// ============================================================\n\n/** True when the parser recognised this channel frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownChannelFrame = (\n frame: ParsedChannelFrame,\n): frame is ChannelServerFrame => Object.hasOwn(CHANNEL_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this inbox frame — i.e. it is not an `UnknownFrame`. */\nexport const isKnownInboxFrame = (\n frame: ParsedInboxFrame,\n): frame is InboxServerFrame => Object.hasOwn(INBOX_SERVER_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S channel frame (not an `UnknownFrame`). */\nexport const isKnownChannelClientFrame = (\n frame: ParsedChannelClientFrame,\n): frame is ChannelClientFrame => Object.hasOwn(CHANNEL_CLIENT_FRAMES, frame.t);\n\n/** True when the parser recognised this C→S inbox frame (not an `UnknownFrame`). */\nexport const isKnownInboxClientFrame = (\n frame: ParsedInboxClientFrame,\n): frame is InboxClientFrame => Object.hasOwn(INBOX_CLIENT_FRAMES, frame.t);\n\n// ── Channel S→C ─────────────────────────────────────────────\n\nexport const isChannelReady = (f: ParsedChannelFrame): f is ChannelReadyFrame =>\n f.t === \"ready\";\nexport const isBatch = (f: ParsedChannelFrame): f is BatchFrame => f.t === \"batch\";\nexport const isRetract = (f: ParsedChannelFrame): f is RetractFrame =>\n f.t === \"retract\";\nexport const isPresence = (f: ParsedChannelFrame): f is PresenceFrame =>\n f.t === \"presence\";\nexport const isActivity = (f: ParsedChannelFrame): f is ActivityFrame =>\n f.t === \"activity\";\nexport const isDirect = (f: ParsedChannelFrame): f is DirectFrame => f.t === \"direct\";\nexport const isReassign = (f: ParsedChannelFrame): f is ReassignFrame =>\n f.t === \"reassign\";\nexport const isError = (f: ParsedChannelFrame): f is ErrorFrame => f.t === \"error\";\nexport const isPong = (f: ParsedChannelFrame): f is PongFrame => f.t === \"pong\";\n\n// ── Inbox S→C ───────────────────────────────────────────────\n\nexport const isInboxReady = (f: ParsedInboxFrame): f is InboxReadyFrame =>\n f.t === \"ready\";\nexport const isInboxEntry = (f: ParsedInboxFrame): f is InboxEntryFrame =>\n f.t === \"entry\";\nexport const isInboxItem = (f: ParsedInboxFrame): f is InboxItemFrame =>\n f.t === \"item\";\nexport const isInboxCounter = (f: ParsedInboxFrame): f is InboxCounterFrame =>\n f.t === \"counter\";\nexport const isInboxPong = (f: ParsedInboxFrame): f is PongFrame => f.t === \"pong\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@portalsdk/wire-protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Wire format types and codecs for the Portal protocol.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,16 +27,16 @@
|
|
|
27
27
|
"url": "git+https://github.com/useportal/portal-sdk.git",
|
|
28
28
|
"directory": "packages/wire-protocol"
|
|
29
29
|
},
|
|
30
|
-
"scripts": {
|
|
31
|
-
"build": "tsup",
|
|
32
|
-
"test": "vitest run",
|
|
33
|
-
"typecheck": "tsc --noEmit",
|
|
34
|
-
"lint": "tsc --noEmit"
|
|
35
|
-
},
|
|
36
30
|
"devDependencies": {
|
|
37
31
|
"@types/node": "^22.18.8",
|
|
38
32
|
"tsup": "^8.5.0",
|
|
39
33
|
"typescript": "^5.9.3",
|
|
40
34
|
"vitest": "^3.2.4"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsup",
|
|
38
|
+
"test": "vitest run",
|
|
39
|
+
"typecheck": "tsc --noEmit",
|
|
40
|
+
"lint": "tsc --noEmit"
|
|
41
41
|
}
|
|
42
|
-
}
|
|
42
|
+
}
|