@irtio/protocol 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +57 -6
- package/dist/index.js +27 -6
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -128,6 +128,34 @@ declare const ErrorCode: {
|
|
|
128
128
|
readonly code: 20;
|
|
129
129
|
readonly message: "client cannot keep up with the stream; reconnect for a fresh snapshot";
|
|
130
130
|
};
|
|
131
|
+
readonly E_TOKEN_EXPIRED: {
|
|
132
|
+
readonly code: 21;
|
|
133
|
+
readonly message: "auth token expired";
|
|
134
|
+
};
|
|
135
|
+
readonly E_TOKEN_INVALID: {
|
|
136
|
+
readonly code: 22;
|
|
137
|
+
readonly message: "auth token signature is invalid";
|
|
138
|
+
};
|
|
139
|
+
readonly E_TOKEN_WRONG_PROJECT: {
|
|
140
|
+
readonly code: 23;
|
|
141
|
+
readonly message: "auth token is for another project";
|
|
142
|
+
};
|
|
143
|
+
readonly E_TOKEN_WRONG_ROOM: {
|
|
144
|
+
readonly code: 24;
|
|
145
|
+
readonly message: "auth token is for another room";
|
|
146
|
+
};
|
|
147
|
+
readonly E_TOKEN_MALFORMED: {
|
|
148
|
+
readonly code: 25;
|
|
149
|
+
readonly message: "auth token is malformed: {reason}";
|
|
150
|
+
};
|
|
151
|
+
readonly E_TOKEN_BAD_ISSUER: {
|
|
152
|
+
readonly code: 26;
|
|
153
|
+
readonly message: "auth token issuer {issuer} is not accepted";
|
|
154
|
+
};
|
|
155
|
+
readonly E_TOKEN_BAD_ALG: {
|
|
156
|
+
readonly code: 27;
|
|
157
|
+
readonly message: "auth token algorithm {alg} is not allowed";
|
|
158
|
+
};
|
|
131
159
|
};
|
|
132
160
|
type ErrorCodeName = keyof typeof ErrorCode;
|
|
133
161
|
interface ErrorCatalogueEntry {
|
|
@@ -153,6 +181,17 @@ type Credential = {
|
|
|
153
181
|
} | {
|
|
154
182
|
readonly kind: 'token';
|
|
155
183
|
readonly token: string;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Week 13 (D27): the project key AND a JWT. The key stays because the router routes on it —
|
|
187
|
+
* a HELLO carrying only a token has nothing to resolve a tenant from — and the token is
|
|
188
|
+
* verified in the guest supervisor (HS256, per-project secret), never by the router. Wire tag
|
|
189
|
+
* 2; the unused tag 1 (`token`) keeps its meaning of months rather than being repurposed.
|
|
190
|
+
*/
|
|
191
|
+
| {
|
|
192
|
+
readonly kind: 'jwt';
|
|
193
|
+
readonly key: string;
|
|
194
|
+
readonly token: string;
|
|
156
195
|
};
|
|
157
196
|
interface Hello {
|
|
158
197
|
readonly protocolVersion: number;
|
|
@@ -218,19 +257,31 @@ declare function deltaPayload(codecBytes: Uint8Array): Uint8Array;
|
|
|
218
257
|
declare function writePayload(codecBytes: Uint8Array): Uint8Array;
|
|
219
258
|
/**
|
|
220
259
|
* The CORRECT frame payload: the schema-codec delta bytes with the judged client write tick
|
|
221
|
-
* (u32) appended (week 8, D19)
|
|
222
|
-
*
|
|
223
|
-
*
|
|
260
|
+
* (u32) appended (week 8, D19), and after it the server tick that write was *applied* at (u32,
|
|
261
|
+
* bug 1). Additive in both steps: the codec's delta decoder never reads past the delta body, so
|
|
262
|
+
* a decoder that does not know about a suffix ignores it; a reader that does (`decodeDeltaFrom`)
|
|
263
|
+
* is left positioned exactly at the first one.
|
|
264
|
+
*
|
|
265
|
+
* The two ticks are on different clocks and that is the point. `clientTick` is the client's own
|
|
266
|
+
* stamp, opaque to the server and echoed verbatim; `appliedTick` is the room's tick counter when
|
|
267
|
+
* the write landed. A predicting client stamps on its *predicted* clock, which leads authority,
|
|
268
|
+
* so without the second number it has no way to learn where on the server's clock its intent
|
|
269
|
+
* actually took effect, and its replay puts it a whole prediction lead too late.
|
|
224
270
|
*/
|
|
225
|
-
declare function correctPayload(codecBytes: Uint8Array, clientTick: number): Uint8Array;
|
|
271
|
+
declare function correctPayload(codecBytes: Uint8Array, clientTick: number, appliedTick?: number): Uint8Array;
|
|
226
272
|
/**
|
|
227
273
|
* Reads the `clientTick` suffix a reader is positioned at after `decodeDeltaFrom`, or
|
|
228
274
|
* `undefined` for a pre-week-8 CORRECT payload with no suffix.
|
|
229
275
|
*/
|
|
230
276
|
declare function readCorrectClientTick(r: ByteReader): number | undefined;
|
|
277
|
+
/**
|
|
278
|
+
* Reads the `appliedTick` suffix that follows `clientTick`, or `undefined` from a server that
|
|
279
|
+
* does not send one. Call only after `readCorrectClientTick`, which leaves the reader on it.
|
|
280
|
+
*/
|
|
281
|
+
declare function readCorrectAppliedTick(r: ByteReader): number | undefined;
|
|
231
282
|
declare function encodeDeltaFrame(codecBytes: Uint8Array): Uint8Array;
|
|
232
283
|
declare function encodeWriteFrame(codecBytes: Uint8Array): Uint8Array;
|
|
233
|
-
declare function encodeCorrectFrame(codecBytes: Uint8Array, clientTick: number): Uint8Array;
|
|
284
|
+
declare function encodeCorrectFrame(codecBytes: Uint8Array, clientTick: number, appliedTick?: number): Uint8Array;
|
|
234
285
|
|
|
235
286
|
interface Call {
|
|
236
287
|
readonly reqId: number;
|
|
@@ -333,4 +384,4 @@ declare const relaySchema: Schema<{
|
|
|
333
384
|
declare const RELAY_HASH8: Uint8Array;
|
|
334
385
|
declare function isRelayHash8(hash8: Uint8Array): boolean;
|
|
335
386
|
|
|
336
|
-
export { type Call, type ClientCallable, type Credential, ERROR_CATALOGUE, type ErrorCatalogueEntry, ErrorCode, type ErrorCodeDef, type ErrorCodeName, type ErrorPayload, type Frame, FrameType, type Hello, type Msg, type MsgTarget, PRESENCE_COLLECTION, PROTOCOL_VERSION, type Ping, type Pong, type PresenceRecord, RELAY_HASH8, type Reply, type Welcome, builtinRpcs, correctPayload, decodeCall, decodeErrorPayload, decodeFrame, decodeHello, decodeMsg, decodePing, decodePong, decodeReply, decodeWelcome, deltaPayload, encodeCall, encodeCorrectFrame, encodeDeltaFrame, encodeErrorPayload, encodeFrame, encodeHello, encodeMsg, encodePing, encodePong, encodeReply, encodeWelcome, encodeWriteFrame, errorByCode, formatError, isFrameType, isRelayHash8, presenceEntity, readCorrectClientTick, relaySchema, requestOwnership, rpcByIdOf, rpcIdOf, rpcTable, withBuiltins, writePayload };
|
|
387
|
+
export { type Call, type ClientCallable, type Credential, ERROR_CATALOGUE, type ErrorCatalogueEntry, ErrorCode, type ErrorCodeDef, type ErrorCodeName, type ErrorPayload, type Frame, FrameType, type Hello, type Msg, type MsgTarget, PRESENCE_COLLECTION, PROTOCOL_VERSION, type Ping, type Pong, type PresenceRecord, RELAY_HASH8, type Reply, type Welcome, builtinRpcs, correctPayload, decodeCall, decodeErrorPayload, decodeFrame, decodeHello, decodeMsg, decodePing, decodePong, decodeReply, decodeWelcome, deltaPayload, encodeCall, encodeCorrectFrame, encodeDeltaFrame, encodeErrorPayload, encodeFrame, encodeHello, encodeMsg, encodePing, encodePong, encodeReply, encodeWelcome, encodeWriteFrame, errorByCode, formatError, isFrameType, isRelayHash8, presenceEntity, readCorrectAppliedTick, readCorrectClientTick, relaySchema, requestOwnership, rpcByIdOf, rpcIdOf, rpcTable, withBuiltins, writePayload };
|
package/dist/index.js
CHANGED
|
@@ -64,7 +64,17 @@ var ErrorCode = {
|
|
|
64
64
|
E_SLOW_CONSUMER: {
|
|
65
65
|
code: 20,
|
|
66
66
|
message: "client cannot keep up with the stream; reconnect for a fresh snapshot"
|
|
67
|
-
}
|
|
67
|
+
},
|
|
68
|
+
// Week 13 (D27): every JWT refusal has a name. `E_AUTH` stays the generic ("no secret
|
|
69
|
+
// configured", "wrong project key"); these say WHICH refusal a token hit, because week 12's
|
|
70
|
+
// staging sessions were survivable precisely because refusals said which they were.
|
|
71
|
+
E_TOKEN_EXPIRED: { code: 21, message: "auth token expired" },
|
|
72
|
+
E_TOKEN_INVALID: { code: 22, message: "auth token signature is invalid" },
|
|
73
|
+
E_TOKEN_WRONG_PROJECT: { code: 23, message: "auth token is for another project" },
|
|
74
|
+
E_TOKEN_WRONG_ROOM: { code: 24, message: "auth token is for another room" },
|
|
75
|
+
E_TOKEN_MALFORMED: { code: 25, message: "auth token is malformed: {reason}" },
|
|
76
|
+
E_TOKEN_BAD_ISSUER: { code: 26, message: "auth token issuer {issuer} is not accepted" },
|
|
77
|
+
E_TOKEN_BAD_ALG: { code: 27, message: "auth token algorithm {alg} is not allowed" }
|
|
68
78
|
};
|
|
69
79
|
var ERROR_CATALOGUE = Object.keys(ErrorCode).map((name) => ({ name, code: ErrorCode[name].code, message: ErrorCode[name].message }));
|
|
70
80
|
var BY_CODE = new Map(
|
|
@@ -100,9 +110,13 @@ function encodeHello(h) {
|
|
|
100
110
|
if (h.credential.kind === "key") {
|
|
101
111
|
w.u8(0);
|
|
102
112
|
w.str(h.credential.key);
|
|
103
|
-
} else {
|
|
113
|
+
} else if (h.credential.kind === "token") {
|
|
104
114
|
w.u8(1);
|
|
105
115
|
w.str(h.credential.token);
|
|
116
|
+
} else {
|
|
117
|
+
w.u8(2);
|
|
118
|
+
w.str(h.credential.key);
|
|
119
|
+
w.str(h.credential.token);
|
|
106
120
|
}
|
|
107
121
|
w.str(h.roomId);
|
|
108
122
|
w.bytes(h.schemaHash8);
|
|
@@ -123,6 +137,7 @@ function decodeHello(bytes) {
|
|
|
123
137
|
let credential;
|
|
124
138
|
if (credKind === 0) credential = { kind: "key", key: r.str() };
|
|
125
139
|
else if (credKind === 1) credential = { kind: "token", token: r.str() };
|
|
140
|
+
else if (credKind === 2) credential = { kind: "jwt", key: r.str(), token: r.str() };
|
|
126
141
|
else throw new Error(`decodeHello: unknown credential kind ${credKind}`);
|
|
127
142
|
const roomId = r.str();
|
|
128
143
|
const schemaHash8 = r.bytes(8);
|
|
@@ -207,23 +222,28 @@ function deltaPayload(codecBytes) {
|
|
|
207
222
|
function writePayload(codecBytes) {
|
|
208
223
|
return codecBytes;
|
|
209
224
|
}
|
|
210
|
-
function correctPayload(codecBytes, clientTick) {
|
|
211
|
-
const
|
|
225
|
+
function correctPayload(codecBytes, clientTick, appliedTick) {
|
|
226
|
+
const extra = appliedTick === void 0 ? 4 : 8;
|
|
227
|
+
const w = new ByteWriter(codecBytes.length + extra);
|
|
212
228
|
w.bytes(codecBytes);
|
|
213
229
|
w.u32(clientTick);
|
|
230
|
+
if (appliedTick !== void 0) w.u32(appliedTick);
|
|
214
231
|
return w.finish();
|
|
215
232
|
}
|
|
216
233
|
function readCorrectClientTick(r) {
|
|
217
234
|
return r.remaining >= 4 ? r.u32() : void 0;
|
|
218
235
|
}
|
|
236
|
+
function readCorrectAppliedTick(r) {
|
|
237
|
+
return r.remaining >= 4 ? r.u32() : void 0;
|
|
238
|
+
}
|
|
219
239
|
function encodeDeltaFrame(codecBytes) {
|
|
220
240
|
return encodeFrame(FrameType.DELTA, deltaPayload(codecBytes));
|
|
221
241
|
}
|
|
222
242
|
function encodeWriteFrame(codecBytes) {
|
|
223
243
|
return encodeFrame(FrameType.WRITE, writePayload(codecBytes));
|
|
224
244
|
}
|
|
225
|
-
function encodeCorrectFrame(codecBytes, clientTick) {
|
|
226
|
-
return encodeFrame(FrameType.CORRECT, correctPayload(codecBytes, clientTick));
|
|
245
|
+
function encodeCorrectFrame(codecBytes, clientTick, appliedTick) {
|
|
246
|
+
return encodeFrame(FrameType.CORRECT, correctPayload(codecBytes, clientTick, appliedTick));
|
|
227
247
|
}
|
|
228
248
|
|
|
229
249
|
// src/rpc.ts
|
|
@@ -421,6 +441,7 @@ export {
|
|
|
421
441
|
isFrameType,
|
|
422
442
|
isRelayHash8,
|
|
423
443
|
presenceEntity,
|
|
444
|
+
readCorrectAppliedTick,
|
|
424
445
|
readCorrectClientTick,
|
|
425
446
|
relaySchema,
|
|
426
447
|
requestOwnership,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irtio/protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "irtio wire protocol: frames, framing, session payloads, error codes, built-in presence",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
"dist"
|
|
21
21
|
],
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"@irtio/schema": "0.
|
|
23
|
+
"@irtio/schema": "0.3.0"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "tsup",
|