@pairlobby/server-core 0.22.0 → 0.24.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 CHANGED
@@ -1,5 +1,5 @@
1
1
  # @pairlobby/server-core
2
2
 
3
- Part of [PairLobby](https://github.com/PairLobby/app), built from `packages/server-core` at version 0.22.0. See that repository for documentation.
3
+ Part of [PairLobby](https://github.com/PairLobby/app), built from `packages/server-core` at version 0.24.0. See that repository for documentation.
4
4
 
5
5
  Licensed under the [Elastic License 2.0](LICENSE): you may use, copy, modify and distribute it, but may not offer it to others as a hosted or managed service.
@@ -1,3 +1,4 @@
1
+ import type { RequestContext } from './ports.js';
1
2
  /** The header that carries a relay's create token, the same one a hosted account token travels in. */
2
3
  export declare const CREATE_TOKEN_HEADER = "x-pairlobby-account-token";
3
4
  export declare const CREATE_TOKEN_REQUIRED = "this relay needs its create token to start a room; set PAIRLOBBY_CREATE_TOKEN";
@@ -33,10 +34,15 @@ export type AdmissionOptions = {
33
34
  /** Absent means no rate limits. */
34
35
  limiter?: RateLimiter;
35
36
  limits?: Partial<AdmissionLimits>;
36
- /** The caller's address as the transport saw it; undefined when it cannot tell, and then every such caller shares one count. */
37
- clientAddress?: (request: Request) => string | undefined;
37
+ /**
38
+ * The header a proxy in front of the relay puts the caller's address in, such as `x-forwarded-for`.
39
+ * Absent means no such header is read, whoever sends it: a caller could write it themselves.
40
+ */
41
+ trustedProxyHeader?: string;
42
+ /** The caller's address; defaults to the proxy header when one is trusted, else the connection's address. Undefined makes every such caller share one count. */
43
+ clientAddress?: (request: Request, context: RequestContext) => string | undefined;
38
44
  /** Callers the limits do not apply to, such as this device on a relay that is not behind a proxy. */
39
- exempt?: (request: Request) => boolean;
45
+ exempt?: (request: Request, context: RequestContext) => boolean;
40
46
  };
41
47
  type MemoryRateLimiterOptions = {
42
48
  now?: () => number;
@@ -47,18 +53,20 @@ export declare function memoryRateLimiter({ now, maxKeys }?: MemoryRateLimiterOp
47
53
  /** Builds the checks a router runs before creating a room or redeeming an invite. */
48
54
  export declare function createAdmission(options: AdmissionOptions): {
49
55
  tokenMatches: (request: Request) => Promise<boolean>;
56
+ /** Creating a room needs the create token here. */
57
+ createTokenRequired: boolean;
50
58
  /** Refuses a room creation without the create token, then counts it against the caller. */
51
- beforeCreate(request: Request): Promise<void>;
59
+ beforeCreate(request: Request, context?: RequestContext): Promise<void>;
52
60
  /**
53
61
  * The team token check behind token-mode login: counted first, in its own bucket so
54
62
  * guessing cannot spend the create budget, then refused the same way whether the
55
63
  * token is missing or wrong.
56
64
  */
57
- beforeAccountCheck(request: Request): Promise<void>;
65
+ beforeAccountCheck(request: Request, context?: RequestContext): Promise<void>;
58
66
  /** Counts an invite redemption against the caller and the whole relay, before the code is looked up. */
59
- beforeRedeem(request: Request): Promise<void>;
67
+ beforeRedeem(request: Request, context?: RequestContext): Promise<void>;
60
68
  /** A code that matched nothing costs the caller once more, so guessing is slower than joining. */
61
- afterUnknownInvite(request: Request): Promise<void>;
69
+ afterUnknownInvite(request: Request, context?: RequestContext): Promise<void>;
62
70
  };
63
71
  export type Admission = ReturnType<typeof createAdmission>;
64
72
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"admission.d.ts","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"AAMA,sGAAsG;AACtG,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAE/D,eAAO,MAAM,qBAAqB,kFAAkF,CAAC;AAErH,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,8CAA8C,CAAC;AAE9E,MAAM,MAAM,SAAS,GAAG;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,CAAC;AAE5C,MAAM,MAAM,YAAY,GAAG;IAAC,EAAE,EAAE,IAAI,CAAA;CAAC,GAAG;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAC,CAAC;AAE1E,2HAA2H;AAC3H,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5E;AAED,MAAM,MAAM,eAAe,GAAG;IAC1B,6CAA6C;IAC7C,yBAAyB,EAAE,MAAM,CAAC;IAClC,iGAAiG;IACjG,yBAAyB,EAAE,MAAM,CAAC;IAClC,+EAA+E;IAC/E,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sHAAsH;IACtH,+BAA+B,EAAE,MAAM,CAAC;CAC3C,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAAiJ,CAAC;AAEzL,MAAM,MAAM,gBAAgB,GAAG;IAC3B,gHAAgH;IAChH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC,gIAAgI;IAChI,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACzD,qGAAqG;IACrG,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC;CAC1C,CAAC;AAIF,KAAK,wBAAwB,GAAG;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,CAAC;AAEvE,6GAA6G;AAC7G,wBAAgB,iBAAiB,CAAC,EAAC,GAAc,EAAE,OAAgB,EAAC,GAAE,wBAA6B,GAAG,WAAW,CAwBhH;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB;4BAchB,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;IAM3D,2FAA2F;0BAC/D,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMnD;;;;OAIG;gCAC+B,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAMzD,wGAAwG;0BAC5E,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAInD,kGAAkG;gCAChE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;EAOhE;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAInF"}
1
+ {"version":3,"file":"admission.d.ts","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,YAAY,CAAC;AAE/C,sGAAsG;AACtG,eAAO,MAAM,mBAAmB,8BAA8B,CAAC;AAE/D,eAAO,MAAM,qBAAqB,kFAAkF,CAAC;AAErH,gGAAgG;AAChG,eAAO,MAAM,kBAAkB,8CAA8C,CAAC;AAE9E,MAAM,MAAM,SAAS,GAAG;IAAC,SAAS,EAAE,MAAM,CAAA;CAAC,CAAC;AAE5C,MAAM,MAAM,YAAY,GAAG;IAAC,EAAE,EAAE,IAAI,CAAA;CAAC,GAAG;IAAC,EAAE,EAAE,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAC,CAAC;AAE1E,2HAA2H;AAC3H,MAAM,WAAW,WAAW;IACxB,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;CAC5E;AAED,MAAM,MAAM,eAAe,GAAG;IAC1B,6CAA6C;IAC7C,yBAAyB,EAAE,MAAM,CAAC;IAClC,iGAAiG;IACjG,yBAAyB,EAAE,MAAM,CAAC;IAClC,+EAA+E;IAC/E,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sHAAsH;IACtH,+BAA+B,EAAE,MAAM,CAAC;CAC3C,CAAC;AAEF,eAAO,MAAM,wBAAwB,EAAE,eAAiJ,CAAC;AAEzL,MAAM,MAAM,gBAAgB,GAAG;IAC3B,gHAAgH;IAChH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mCAAmC;IACnC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,MAAM,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAClC;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,gKAAgK;IAChK,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,KAAK,MAAM,GAAG,SAAS,CAAC;IAClF,qGAAqG;IACrG,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;CACnE,CAAC;AAIF,KAAK,wBAAwB,GAAG;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAC,CAAC;AAEvE,6GAA6G;AAC7G,wBAAgB,iBAAiB,CAAC,EAAC,GAAc,EAAE,OAAgB,EAAC,GAAE,wBAA6B,GAAG,WAAW,CAwBhH;AAED,qFAAqF;AACrF,wBAAgB,eAAe,CAAC,OAAO,EAAE,gBAAgB;4BAgBhB,OAAO,KAAG,OAAO,CAAC,OAAO,CAAC;IAM3D,mDAAmD;;IAEnD,2FAA2F;0BAC/D,OAAO,YAAW,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;IAMjF;;;;OAIG;gCAC+B,OAAO,YAAW,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;IAMvF,wGAAwG;0BAC5E,OAAO,YAAW,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;IAIjF,kGAAkG;gCAChE,OAAO,YAAW,cAAc,GAAQ,OAAO,CAAC,IAAI,CAAC;EAO9F;AAED,MAAM,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,eAAe,CAAC,CAAC;AAE3D;;;GAGG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAInF"}
package/dist/admission.js CHANGED
@@ -38,9 +38,11 @@ export function memoryRateLimiter({ now = Date.now, maxKeys = 10_000 } = {}) {
38
38
  export function createAdmission(options) {
39
39
  const limits = { ...DEFAULT_ADMISSION_LIMITS, ...options.limits };
40
40
  const tokenDigest = options.createToken ? hashCredential(options.createToken) : null;
41
- const address = (request) => options.clientAddress?.(request) ?? 'unknown';
42
- const take = async (request, key, cost, limit) => {
43
- if (!options.limiter || options.exempt?.(request)) {
41
+ const header = options.trustedProxyHeader;
42
+ const clientAddress = options.clientAddress ?? ((request, context) => (header ? proxiedAddress(request, header) : undefined) ?? context.peerAddress);
43
+ const address = (request, context) => clientAddress(request, context) ?? 'unknown';
44
+ const take = async (request, context, key, cost, limit) => {
45
+ if (!options.limiter || options.exempt?.(request, context)) {
44
46
  return;
45
47
  }
46
48
  const decision = await options.limiter.take(key, cost, limit);
@@ -55,35 +57,37 @@ export function createAdmission(options) {
55
57
  };
56
58
  return {
57
59
  tokenMatches,
60
+ /** Creating a room needs the create token here. */
61
+ createTokenRequired: tokenDigest !== null,
58
62
  /** Refuses a room creation without the create token, then counts it against the caller. */
59
- async beforeCreate(request) {
63
+ async beforeCreate(request, context = {}) {
60
64
  if (tokenDigest && !(await tokenMatches(request))) {
61
65
  throw new ProtocolError('unauthorized', CREATE_TOKEN_REQUIRED);
62
66
  }
63
- await take(request, `create:address:${address(request)}`, 1, { perMinute: limits.createPerMinutePerAddress });
67
+ await take(request, context, `create:address:${address(request, context)}`, 1, { perMinute: limits.createPerMinutePerAddress });
64
68
  },
65
69
  /**
66
70
  * The team token check behind token-mode login: counted first, in its own bucket so
67
71
  * guessing cannot spend the create budget, then refused the same way whether the
68
72
  * token is missing or wrong.
69
73
  */
70
- async beforeAccountCheck(request) {
71
- await take(request, `account-check:address:${address(request)}`, 1, { perMinute: limits.accountCheckPerMinutePerAddress });
74
+ async beforeAccountCheck(request, context = {}) {
75
+ await take(request, context, `account-check:address:${address(request, context)}`, 1, { perMinute: limits.accountCheckPerMinutePerAddress });
72
76
  if (!(await tokenMatches(request))) {
73
77
  throw new ProtocolError('unauthorized', TEAM_TOKEN_REFUSED);
74
78
  }
75
79
  },
76
80
  /** Counts an invite redemption against the caller and the whole relay, before the code is looked up. */
77
- async beforeRedeem(request) {
78
- await take(request, `redeem:address:${address(request)}`, 1, { perMinute: limits.redeemPerMinutePerAddress });
79
- await take(request, 'redeem:global', 1, { perMinute: limits.redeemPerMinuteGlobal });
81
+ async beforeRedeem(request, context = {}) {
82
+ await take(request, context, `redeem:address:${address(request, context)}`, 1, { perMinute: limits.redeemPerMinutePerAddress });
83
+ await take(request, context, 'redeem:global', 1, { perMinute: limits.redeemPerMinuteGlobal });
80
84
  },
81
85
  /** A code that matched nothing costs the caller once more, so guessing is slower than joining. */
82
- async afterUnknownInvite(request) {
83
- if (!options.limiter || options.exempt?.(request)) {
86
+ async afterUnknownInvite(request, context = {}) {
87
+ if (!options.limiter || options.exempt?.(request, context)) {
84
88
  return;
85
89
  }
86
- await options.limiter.take(`redeem:address:${address(request)}`, 1, { perMinute: limits.redeemPerMinutePerAddress });
90
+ await options.limiter.take(`redeem:address:${address(request, context)}`, 1, { perMinute: limits.redeemPerMinutePerAddress });
87
91
  }
88
92
  };
89
93
  }
@@ -1 +1 @@
1
- {"version":3,"file":"admission.js","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"admission.js","sourceRoot":"","sources":["../src/admission.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,61 @@
1
+ import type { HandoverRecord, InviteRecord, MessageRequest, ParticipantRecord, RequestPage, RoomEvent, RoomRecord } from '@pairlobby/protocol';
2
+ import type { Mutation, RoomView } from '@pairlobby/room-core';
3
+ import type { EventPage, FileStore, IdempotencyRecord, RoomStore } from './store.js';
4
+ type IdempotencyKey = Parameters<RoomStore['apply']>[1];
5
+ /** A registered wait: `changed` settles true on a change, false at the deadline or on `cancel`. */
6
+ export type ChangeWait = {
7
+ changed: Promise<boolean>;
8
+ cancel: () => void;
9
+ };
10
+ export type ChangeNotifierOptions = {
11
+ /** Waits held at once across the relay; past it, reads answer at once, as if polling. */
12
+ maxWaiters?: number;
13
+ };
14
+ export declare class ChangeNotifier {
15
+ private readonly waiters;
16
+ private readonly maxWaiters;
17
+ private held;
18
+ private closed;
19
+ constructor({ maxWaiters }?: ChangeNotifierOptions);
20
+ /** Waiting readers, for tests and metrics. */
21
+ get waiting(): number;
22
+ /**
23
+ * Starts waiting for a change to `roomId` for at most `ms`. Register before reading, so a
24
+ * write between the read and the wait still wakes it. Null when the relay already holds
25
+ * as many waits as it allows, or is shutting down.
26
+ */
27
+ wait(roomId: string, ms: number): ChangeWait | null;
28
+ changed(roomId: string): void;
29
+ /** Releases every wait, as unchanged, and refuses new ones: for shutting down. */
30
+ close(): void;
31
+ }
32
+ /** A store that tells `notifier` about every write to a room, after the write succeeds. */
33
+ export declare class NotifyingStore implements RoomStore {
34
+ private readonly store;
35
+ private readonly notifier;
36
+ constructor(store: RoomStore, notifier: ChangeNotifier);
37
+ get files(): FileStore | undefined;
38
+ createRoom(room: RoomRecord, participant: ParticipantRecord, event: RoomEvent): Promise<void>;
39
+ loadRoom(roomId: string): Promise<RoomView | null>;
40
+ apply(mutation: Mutation, idempotency: IdempotencyKey): Promise<void>;
41
+ readEvents(roomId: string, after: number, limit: number): Promise<EventPage>;
42
+ eventBySeq(roomId: string, seq: number): Promise<RoomEvent | null>;
43
+ eventById(roomId: string, eventId: string): Promise<RoomEvent | null>;
44
+ idempotencyRecord(roomId: string, key: string): Promise<IdempotencyRecord | null>;
45
+ putInvite(invite: InviteRecord): Promise<void>;
46
+ inviteByDigest(digest: string): Promise<InviteRecord | null>;
47
+ reserveInvite(digest: string, attemptId: string, credentialHash: string, expectedOccupantId: string | null): Promise<InviteRecord | null>;
48
+ completeInvite(digest: string, participantId: string): Promise<void>;
49
+ participantByCredential(roomId: string, credentialHash: string): Promise<ParticipantRecord | null>;
50
+ handovers(roomId: string): Promise<HandoverRecord[]>;
51
+ messageRequest(roomId: string, eventId: string): Promise<MessageRequest | null>;
52
+ groupRequests(roomId: string, conversationId: string): Promise<MessageRequest[]>;
53
+ updateTurns(room: RoomRecord, requests: MessageRequest[], expectedRevision: number): Promise<void>;
54
+ messageRequests(roomId: string, after: number, limit: number, recipientId?: string): Promise<RequestPage>;
55
+ setLifecycle(roomId: string, lifecycle: RoomRecord['lifecycle']): Promise<void>;
56
+ deleteRoom(roomId: string): Promise<void>;
57
+ dueAutoCloses(now: number, limit: number): Promise<string[]>;
58
+ nextAutoCloseAt(): Promise<number | null>;
59
+ }
60
+ export {};
61
+ //# sourceMappingURL=changes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.d.ts","sourceRoot":"","sources":["../src/changes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,145 @@
1
+ //! Long-poll support for a relay that runs in one process: readers wait on a room,
2
+ //! and every write to the store wakes them. A relay with several copies needs a
3
+ //! notifier they all share, which is why this lives behind the store and not in it.
4
+ export class ChangeNotifier {
5
+ waiters = new Map();
6
+ maxWaiters;
7
+ held = 0;
8
+ closed = false;
9
+ constructor({ maxWaiters = 1000 } = {}) {
10
+ this.maxWaiters = maxWaiters;
11
+ }
12
+ /** Waiting readers, for tests and metrics. */
13
+ get waiting() {
14
+ return this.held;
15
+ }
16
+ /**
17
+ * Starts waiting for a change to `roomId` for at most `ms`. Register before reading, so a
18
+ * write between the read and the wait still wakes it. Null when the relay already holds
19
+ * as many waits as it allows, or is shutting down.
20
+ */
21
+ wait(roomId, ms) {
22
+ if (this.closed || this.held >= this.maxWaiters) {
23
+ return null;
24
+ }
25
+ let settle = () => { };
26
+ const changed = new Promise((resolve) => {
27
+ let done = false;
28
+ const timer = setTimeout(() => settle(false), ms);
29
+ settle = (value) => {
30
+ if (done) {
31
+ return;
32
+ }
33
+ done = true;
34
+ clearTimeout(timer);
35
+ this.held--;
36
+ const room = this.waiters.get(roomId);
37
+ room?.delete(settle);
38
+ if (room?.size === 0) {
39
+ this.waiters.delete(roomId);
40
+ }
41
+ resolve(value);
42
+ };
43
+ });
44
+ this.held++;
45
+ const room = this.waiters.get(roomId) ?? new Set();
46
+ room.add(settle);
47
+ this.waiters.set(roomId, room);
48
+ return { changed, cancel: () => settle(false) };
49
+ }
50
+ changed(roomId) {
51
+ for (const settle of [...(this.waiters.get(roomId) ?? [])]) {
52
+ settle(true);
53
+ }
54
+ }
55
+ /** Releases every wait, as unchanged, and refuses new ones: for shutting down. */
56
+ close() {
57
+ this.closed = true;
58
+ for (const room of [...this.waiters.values()]) {
59
+ for (const settle of [...room]) {
60
+ settle(false);
61
+ }
62
+ }
63
+ }
64
+ }
65
+ /** A store that tells `notifier` about every write to a room, after the write succeeds. */
66
+ export class NotifyingStore {
67
+ store;
68
+ notifier;
69
+ constructor(store, notifier) {
70
+ this.store = store;
71
+ this.notifier = notifier;
72
+ }
73
+ get files() {
74
+ return this.store.files;
75
+ }
76
+ async createRoom(room, participant, event) {
77
+ await this.store.createRoom(room, participant, event);
78
+ this.notifier.changed(room.roomId);
79
+ }
80
+ loadRoom(roomId) {
81
+ return this.store.loadRoom(roomId);
82
+ }
83
+ async apply(mutation, idempotency) {
84
+ await this.store.apply(mutation, idempotency);
85
+ this.notifier.changed(mutation.room.roomId);
86
+ }
87
+ readEvents(roomId, after, limit) {
88
+ return this.store.readEvents(roomId, after, limit);
89
+ }
90
+ eventBySeq(roomId, seq) {
91
+ return this.store.eventBySeq(roomId, seq);
92
+ }
93
+ eventById(roomId, eventId) {
94
+ return this.store.eventById(roomId, eventId);
95
+ }
96
+ idempotencyRecord(roomId, key) {
97
+ return this.store.idempotencyRecord(roomId, key);
98
+ }
99
+ putInvite(invite) {
100
+ return this.store.putInvite(invite);
101
+ }
102
+ inviteByDigest(digest) {
103
+ return this.store.inviteByDigest(digest);
104
+ }
105
+ reserveInvite(digest, attemptId, credentialHash, expectedOccupantId) {
106
+ return this.store.reserveInvite(digest, attemptId, credentialHash, expectedOccupantId);
107
+ }
108
+ completeInvite(digest, participantId) {
109
+ return this.store.completeInvite(digest, participantId);
110
+ }
111
+ participantByCredential(roomId, credentialHash) {
112
+ return this.store.participantByCredential(roomId, credentialHash);
113
+ }
114
+ handovers(roomId) {
115
+ return this.store.handovers(roomId);
116
+ }
117
+ messageRequest(roomId, eventId) {
118
+ return this.store.messageRequest(roomId, eventId);
119
+ }
120
+ groupRequests(roomId, conversationId) {
121
+ return this.store.groupRequests(roomId, conversationId);
122
+ }
123
+ async updateTurns(room, requests, expectedRevision) {
124
+ await this.store.updateTurns(room, requests, expectedRevision);
125
+ this.notifier.changed(room.roomId);
126
+ }
127
+ messageRequests(roomId, after, limit, recipientId) {
128
+ return this.store.messageRequests(roomId, after, limit, recipientId);
129
+ }
130
+ async setLifecycle(roomId, lifecycle) {
131
+ await this.store.setLifecycle(roomId, lifecycle);
132
+ this.notifier.changed(roomId);
133
+ }
134
+ async deleteRoom(roomId) {
135
+ await this.store.deleteRoom(roomId);
136
+ this.notifier.changed(roomId);
137
+ }
138
+ dueAutoCloses(now, limit) {
139
+ return this.store.dueAutoCloses(now, limit);
140
+ }
141
+ nextAutoCloseAt() {
142
+ return this.store.nextAutoCloseAt();
143
+ }
144
+ }
145
+ //# sourceMappingURL=changes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"changes.js","sourceRoot":"","sources":["../src/changes.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
1
  export * from './admission.js';
2
+ export * from './ports.js';
3
+ export * from './relay.js';
2
4
  export * from './router.js';
3
5
  export * from './service.js';
4
6
  export * from './stable-json.js';
5
7
  export * from './store.js';
6
8
  export * from './auto-close-store.js';
9
+ export * from './changes.js';
7
10
  export * from './request-backfill.js';
8
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AAEtC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAE7B,cAAc,uBAAuB,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,11 @@
1
1
  export * from './admission.js';
2
+ export * from './ports.js';
3
+ export * from './relay.js';
2
4
  export * from './router.js';
3
5
  export * from './service.js';
4
6
  export * from './stable-json.js';
5
7
  export * from './store.js';
6
8
  export * from './auto-close-store.js';
9
+ export * from './changes.js';
7
10
  export * from './request-backfill.js';
8
11
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AAEtC,cAAc,uBAAuB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC;AACjC,cAAc,YAAY,CAAC;AAC3B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAE7B,cAAc,uBAAuB,CAAC"}
@@ -0,0 +1,34 @@
1
+ /** What the transport knows about a request and no header can claim. */
2
+ export type RequestContext = {
3
+ /** The address of the connection the request arrived on; absent when the platform cannot see it. */
4
+ peerAddress?: string;
5
+ };
6
+ /**
7
+ * Runs a room's work in order. `write` is false for a request that changes nothing, which may
8
+ * start once the writes queued before it are done but does not hold up the ones after it.
9
+ */
10
+ export interface RoomExecutor {
11
+ run<T>(roomId: string | null, write: boolean, action: () => Promise<T>): Promise<T>;
12
+ /** Resolves once every write already queued has finished. */
13
+ idle(): Promise<void>;
14
+ }
15
+ /** Wakes the kernel's `tick` at a time; `null` cancels the wake-up. A later call replaces an earlier one. */
16
+ export interface Scheduler {
17
+ plan(at: number | null): void;
18
+ }
19
+ /** Runs everything as it comes, for a platform that already orders a room's work, such as a Durable Object. */
20
+ export declare function directExecutor(): RoomExecutor;
21
+ /**
22
+ * One queue for every room, for a relay with one process over one store: two writes never
23
+ * interleave, so two cannot both choose the same next event sequence.
24
+ */
25
+ export declare function queueExecutor(): RoomExecutor;
26
+ type TimerSchedulerOptions = {
27
+ now?: () => number;
28
+ };
29
+ /** A `setTimeout` per wake-up that never keeps the process alive; `stop` cancels it for good. */
30
+ export declare function timerScheduler(wake: () => void, { now }?: TimerSchedulerOptions): Scheduler & {
31
+ stop(): void;
32
+ };
33
+ export {};
34
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.d.ts","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAIA,wEAAwE;AACxE,MAAM,MAAM,cAAc,GAAG;IACzB,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF;;;GAGG;AACH,MAAM,WAAW,YAAY;IACzB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACpF,6DAA6D;IAC7D,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACzB;AAED,6GAA6G;AAC7G,MAAM,WAAW,SAAS;IACtB,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;CACjC;AAED,+GAA+G;AAC/G,wBAAgB,cAAc,IAAI,YAAY,CAK7C;AAED;;;GAGG;AACH,wBAAgB,aAAa,IAAI,YAAY,CAY5C;AAED,KAAK,qBAAqB,GAAG;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAC,CAAC;AAElD,iGAAiG;AACjG,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,IAAI,EAAE,EAAC,GAAc,EAAC,GAAE,qBAA0B,GAAG,SAAS,GAAG;IAAC,IAAI,IAAI,IAAI,CAAA;CAAC,CAkBzH"}
package/dist/ports.js ADDED
@@ -0,0 +1,48 @@
1
+ //! What a relay's platform supplies to the shared kernel (`relay.ts`): how room work is
2
+ //! ordered, when the kernel is woken again, and what the transport saw of the caller.
3
+ //! Each platform implements these once; no protocol rule lives on that side.
4
+ /** Runs everything as it comes, for a platform that already orders a room's work, such as a Durable Object. */
5
+ export function directExecutor() {
6
+ return {
7
+ run: (_roomId, _write, action) => action(),
8
+ idle: () => Promise.resolve()
9
+ };
10
+ }
11
+ /**
12
+ * One queue for every room, for a relay with one process over one store: two writes never
13
+ * interleave, so two cannot both choose the same next event sequence.
14
+ */
15
+ export function queueExecutor() {
16
+ let writes = Promise.resolve();
17
+ return {
18
+ run(_roomId, write, action) {
19
+ const result = writes.then(action);
20
+ if (write) {
21
+ writes = result.catch(() => { });
22
+ }
23
+ return result;
24
+ },
25
+ idle: () => writes.then(() => { })
26
+ };
27
+ }
28
+ /** A `setTimeout` per wake-up that never keeps the process alive; `stop` cancels it for good. */
29
+ export function timerScheduler(wake, { now = Date.now } = {}) {
30
+ let timer;
31
+ let stopped = false;
32
+ return {
33
+ plan(at) {
34
+ clearTimeout(timer);
35
+ timer = undefined;
36
+ if (stopped || at === null) {
37
+ return;
38
+ }
39
+ timer = setTimeout(wake, Math.min(Math.max(0, at - now()), 2 ** 31 - 1));
40
+ timer.unref?.();
41
+ },
42
+ stop() {
43
+ stopped = true;
44
+ clearTimeout(timer);
45
+ }
46
+ };
47
+ }
48
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ports.js","sourceRoot":"","sources":["../src/ports.ts"],"names":[],"mappings":"AAAA,wFAAwF;AACxF,sFAAsF;AACtF,6EAA6E;AAuB7E,+GAA+G;AAC/G,MAAM,UAAU,cAAc;IAC1B,OAAO;QACH,GAAG,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,CAAC,MAAM,EAAE;QAC1C,IAAI,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE;KAChC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa;IACzB,IAAI,MAAM,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IACjD,OAAO;QACH,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM;YACtB,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;YACnC,IAAI,KAAK,EAAE,CAAC;gBACR,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,MAAM,CAAC;QAClB,CAAC;QACD,IAAI,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC;KACpC,CAAC;AACN,CAAC;AAID,iGAAiG;AACjG,MAAM,UAAU,cAAc,CAAC,IAAgB,EAAE,EAAC,GAAG,GAAG,IAAI,CAAC,GAAG,KAA2B,EAAE;IACzF,IAAI,KAAgD,CAAC;IACrD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,OAAO;QACH,IAAI,CAAC,EAAE;YACH,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,KAAK,GAAG,SAAS,CAAC;YAClB,IAAI,OAAO,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;gBACzB,OAAO;YACX,CAAC;YACD,KAAK,GAAG,UAAU,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,GAAG,GAAG,EAAE,CAAC,EAAE,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;YACxE,KAA8B,CAAC,KAAK,EAAE,EAAE,CAAC;QAC9C,CAAC;QACD,IAAI;YACA,OAAO,GAAG,IAAI,CAAC;YACf,YAAY,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC;KACJ,CAAC;AACN,CAAC"}
@@ -0,0 +1,128 @@
1
+ import type { LiveTransport, LocalRoom, RelayLimits, ServerDescription } from '@pairlobby/protocol';
2
+ import type { Admission } from './admission.js';
3
+ import type { ChangeNotifier } from './changes.js';
4
+ import type { RequestContext, RoomExecutor, Scheduler } from './ports.js';
5
+ import type { RoomService } from './service.js';
6
+ export interface RelayOptions {
7
+ service: RoomService;
8
+ /** Orders each room's work; defaults to running it as it comes, for a platform that orders it already. */
9
+ executor?: RoomExecutor;
10
+ /** Wakes `tick` for the next auto-close. Absent means the platform runs `tick` itself, such as from a Durable Object alarm. */
11
+ scheduler?: Scheduler;
12
+ /**
13
+ * The room an invite code's digest belongs to, which `edge` needs before any room sees the code.
14
+ * Defaults to the service's own store; a relay with one store per room passes its directory.
15
+ */
16
+ inviteRoom?: (digest: string) => Promise<string | null>;
17
+ /**
18
+ * True when the relay sits behind a proxy or a public address. Every caller then arrives from
19
+ * the proxy, so no connection proves a caller is local, and joining by name is refused.
20
+ */
21
+ behindProxy?: boolean;
22
+ /**
23
+ * Exact origins a browser may call from. A CLI sends no `Origin`, so an
24
+ * unexpected one means a web page is talking to us — including a malicious
25
+ * page reaching for loopback.
26
+ */
27
+ allowedOrigins?: string[];
28
+ /** `Host` values to accept, defeating DNS rebinding against a local server. */
29
+ allowedHosts?: string[] | ((host: string) => boolean);
30
+ /** What only the adapter knows for `GET /v1/server`: its share addresses and its build. Every relay answers the path. */
31
+ serverInfo?: () => ServerInfo;
32
+ /**
33
+ * Wakes held reads. Present means the relay offers long-poll: `GET .../events?wait=` waits for a change
34
+ * instead of answering empty. Only a relay whose writes all pass through one process can offer it.
35
+ */
36
+ changes?: ChangeNotifier;
37
+ /**
38
+ * How clients hear about new events, when the relay knows. Defaults to `long-poll` with `changes` and is
39
+ * otherwise left out, so a client keeps its own rule; a relay with a socket hub says `websocket`.
40
+ */
41
+ transport?: LiveTransport;
42
+ /** Features beyond those the router can see for itself. */
43
+ features?: string[];
44
+ /**
45
+ * Answers `GET /v1/invites/probe?prefix=`: whether any invite this server issued
46
+ * has a digest starting with that prefix. Lets a client on the same network find
47
+ * the relay behind a bare code; absent means the path is unknown.
48
+ */
49
+ inviteProbe?: (prefix: string) => Promise<boolean>;
50
+ /**
51
+ * Whether this request came from the relay's own device, its local network or its
52
+ * tailnet. Only a transport that sees the connection's real address can say; absent
53
+ * means never, so joining by name is refused.
54
+ */
55
+ peerIsLocal?: (request: Request, context: RequestContext) => boolean;
56
+ /** Open rooms that allow local joins, matching a name or all of them; answers `GET /v1/rooms/local[?name=]`. */
57
+ localRooms?: (name?: string) => Promise<LocalRoom[]>;
58
+ /** The create token and rate limits; absent means anyone who reaches the relay may create rooms and try codes freely. */
59
+ admission?: Admission;
60
+ /** Answers `GET /.well-known/pairlobby.json`, which tells `pairlobby setup` who this relay is and how to log in. */
61
+ discovery?: () => DiscoveryDocument;
62
+ /**
63
+ * Token-mode login: answers `GET /api/online/account` for a caller holding the team
64
+ * token (the create token). Absent means the path is unknown. Needs `admission`.
65
+ */
66
+ accountCheck?: (request: Request) => TeamAccount;
67
+ }
68
+ /** Where `edge` sends a request that needs a room: the room's id, and the request to run there. */
69
+ export type RoomRoute = {
70
+ roomId: string;
71
+ request: Request;
72
+ };
73
+ export interface Relay {
74
+ /** Both halves in one process. */
75
+ handle(request: Request, context?: RequestContext): Promise<Response>;
76
+ /** Admission and every route that needs no room. A request reaching `inRoom` must have come through here. */
77
+ edge(request: Request, context?: RequestContext): Promise<Response | RoomRoute>;
78
+ /** A request `edge` sent to `roomId`, run in that room's order. */
79
+ inRoom(request: Request, roomId: string, context?: RequestContext): Promise<Response>;
80
+ /** Closes rooms whose auto-close time has passed, then plans the next wake-up. */
81
+ tick(kind: 'auto-close'): Promise<void>;
82
+ }
83
+ /** What a deployment says about itself to `pairlobby setup`. */
84
+ export interface DiscoveryDocument {
85
+ version: 1;
86
+ name?: string;
87
+ login: {
88
+ mode: 'token' | 'none';
89
+ tokenHelp?: string;
90
+ verify?: string;
91
+ };
92
+ relays: string[];
93
+ features: string[];
94
+ support?: string;
95
+ }
96
+ /** The account a team token stands for: no person, just where its rooms go. */
97
+ export interface TeamAccount {
98
+ email: null;
99
+ userId: 'team';
100
+ name: string | null;
101
+ server: string;
102
+ handle: null;
103
+ }
104
+ export interface ServerInfo {
105
+ /** Addresses other devices can use to reach this server; empty when it only listens on this device. */
106
+ shareUrls: string[];
107
+ /** True when a device on the same network or tailnet can find this server from an invite code alone. */
108
+ discoverable?: boolean;
109
+ /** The relay's own version, so a client can tell when it runs an older build than the CLI. */
110
+ version?: string;
111
+ }
112
+ export declare function createRelay(options: RelayOptions): Relay;
113
+ /** The relay's own account of itself, from what this router was given: nothing is claimed that it was not. */
114
+ export declare function describe(options: RelayOptions): ServerDescription;
115
+ export type RelayDescriptionInput = {
116
+ limits: RelayLimits;
117
+ features: string[];
118
+ transport?: LiveTransport;
119
+ info?: ServerInfo;
120
+ };
121
+ /**
122
+ * `GET /v1/server` for any relay, including one that answers it outside this router, such as
123
+ * a Worker in front of Durable Objects. Every relay speaking this protocol has message stages.
124
+ */
125
+ export declare function describeRelay({ limits, features, transport, info }: RelayDescriptionInput): ServerDescription;
126
+ /** Refuses a request declaring another protocol version, before anything else looks at it. Null when it may go on. */
127
+ export declare function protocolVersionRefusal(request: Request): Response | null;
128
+ //# sourceMappingURL=relay.d.ts.map