@kumiai/hub-server 0.9.0 → 0.10.1

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
@@ -16,6 +16,10 @@ client registry for push fan-out, and an in-memory `HubStore` for development an
16
16
  `DEFAULT_HUB_ACCESS_RULES`.
17
17
  - `HubStoreErrorEvent`, `HubStoreErrorHook` — the `onStoreError` event and hook types.
18
18
 
19
+ `createHub` accepts `replay` with a cache and tuning (`maxAge`, `leeway`, `maxEntries`).
20
+ The hub always enables replay checks and rejects stale tokens. Supply a persistent `replay.cache`
21
+ to keep replay protection across restarts; omitted, it uses a per-process memory cache.
22
+
19
23
  ```ts
20
24
  import { createHub, createMemoryStore } from '@kumiai/hub-server'
21
25
 
package/lib/hub.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { ServerTransportOf } from '@enkaku/protocol';
2
- import type { AccessRules, ResourceLimits, Server } from '@enkaku/server';
2
+ import type { AccessRules, ReplayOptions, ResourceLimits, Server } from '@enkaku/server';
3
3
  import type { Identity } from '@kokuin/token';
4
4
  import type { HubProtocol, HubStore, WakeRegistry, WakeSender } from '@kumiai/hub-protocol';
5
5
  import { type AuthorizeHook, type HubRateLimits, type HubStoreErrorHook, type KeyPackageFetchLimits } from './handlers.js';
@@ -20,6 +20,8 @@ export type HubPurgeOptions = {
20
20
  */
21
21
  olderThan?: number;
22
22
  };
23
+ /** Replay settings a host may tune. The hub always enables replay checks and rejects stale tokens. */
24
+ export type HubReplayOptions = Omit<ReplayOptions, 'enabled' | 'rejectStale'>;
23
25
  export type CreateHubParams = {
24
26
  transport: ServerTransportOf<HubProtocol>;
25
27
  store: HubStore;
@@ -30,6 +32,8 @@ export type CreateHubParams = {
30
32
  identity: Identity;
31
33
  /** Access rules enforced by the server. Defaults to {@link DEFAULT_HUB_ACCESS_RULES}. */
32
34
  accessRules?: AccessRules;
35
+ /** Omitted: per-process memory cache. Only a persistent `cache` survives a hub restart. */
36
+ replay?: HubReplayOptions;
33
37
  /**
34
38
  * Per-action authorization hook. Consulted for publish, subscribe, topic/fetch, keypackage/*,
35
39
  * wake/*, and receive — the coarse `receive` gate at channel open plus a per-frame
package/lib/hub.js CHANGED
@@ -56,6 +56,11 @@ export function createHub(params) {
56
56
  transport: params.transport,
57
57
  identity: params.identity,
58
58
  accessRules: params.accessRules ?? DEFAULT_HUB_ACCESS_RULES,
59
+ replay: {
60
+ ...params.replay,
61
+ enabled: true,
62
+ rejectStale: true
63
+ },
59
64
  limits
60
65
  });
61
66
  if (params.purge !== false) {
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export type { AuthorizeDecision, AuthorizeHook, AuthorizeRequest, CreateHandlersParams, HubRateLimits, HubStoreErrorEvent, HubStoreErrorHook, KeyPackageFetchLimits, } from './handlers.js';
2
2
  export { createHandlers, DEFAULT_KEYPACKAGE_FETCH_LIMITS, DEFAULT_RATE_LIMITS, DEFAULT_RECEIVE_BUFFER_LIMIT, } from './handlers.js';
3
- export type { CreateHubParams, HubInstance, HubPurgeOptions } from './hub.js';
3
+ export type { CreateHubParams, HubInstance, HubPurgeOptions, HubReplayOptions } from './hub.js';
4
4
  export { createHub, DEFAULT_HUB_ACCESS_RULES } from './hub.js';
5
5
  export { createMemoryStore, type MemoryStoreOptions } from './memoryStore.js';
6
6
  export { createRateLimiter, type RateLimitConfig, type RateLimiter } from './rateLimit.js';
@@ -241,7 +241,8 @@ function isLive(entry, nowSeconds) {
241
241
  startIndex = 0;
242
242
  } else {
243
243
  for(let index = 0; index < pending.length; index++){
244
- if (pending[index] > params.after) {
244
+ const sequenceID = pending[index];
245
+ if (sequenceID !== undefined && sequenceID > params.after) {
245
246
  startIndex = index;
246
247
  break;
247
248
  }
@@ -258,7 +259,7 @@ function isLive(entry, nowSeconds) {
258
259
  resultMessages.push(asStoredMessage(entry));
259
260
  }
260
261
  }
261
- const cursor = resultMessages.length > 0 ? resultMessages[resultMessages.length - 1].sequenceID : null;
262
+ const cursor = resultMessages.at(-1)?.sequenceID ?? null;
262
263
  const result = {
263
264
  messages: resultMessages,
264
265
  cursor
@@ -294,7 +295,7 @@ function isLive(entry, nowSeconds) {
294
295
  return {
295
296
  messages,
296
297
  head: heads.get(params.topicID) ?? null,
297
- oldest: log.length > 0 ? log[0] : null
298
+ oldest: log[0] ?? null
298
299
  };
299
300
  },
300
301
  async trim (params) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kumiai/hub-server",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "description": "Hub server for Enkaku E2EE group message routing",
5
5
  "keywords": [
6
6
  "hub",
@@ -25,19 +25,19 @@
25
25
  "lib/*"
26
26
  ],
27
27
  "dependencies": {
28
- "@enkaku/protocol": "^0.21.1",
29
- "@enkaku/server": "^0.21.2",
28
+ "@enkaku/protocol": "^0.21.3",
29
+ "@enkaku/server": "^0.21.3",
30
30
  "@kokuin/token": "^0.5.0",
31
- "@kumiai/hub-protocol": "^0.9.0",
31
+ "@kumiai/hub-protocol": "^0.10.0",
32
32
  "@sozai/codec": "^0.4.0",
33
33
  "@sozai/event": "^0.1.3",
34
34
  "@sozai/log": "^0.3.1"
35
35
  },
36
36
  "devDependencies": {
37
- "@enkaku/client": "^0.21.2",
38
- "@enkaku/transport": "^0.21.0",
39
- "@kumiai/hub-conformance": "^0.9.0",
40
- "@kumiai/hub-wake": "^0.9.0"
37
+ "@enkaku/client": "^0.21.3",
38
+ "@enkaku/transport": "^0.21.3",
39
+ "@kumiai/hub-conformance": "^0.10.0",
40
+ "@kumiai/hub-wake": "^0.10.0"
41
41
  },
42
42
  "publishConfig": {
43
43
  "access": "public"
@@ -45,8 +45,8 @@
45
45
  "scripts": {
46
46
  "build": "pnpm run build:clean && pnpm run build:js && pnpm run build:types",
47
47
  "build:clean": "del lib",
48
- "build:js": "swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
49
- "build:types": "tsc --emitDeclarationOnly --skipLibCheck",
48
+ "build:js": "del 'lib/**/*.js' && swc src -d ./lib --config-file ../../node_modules/@kigu/dev/swc.json --strip-leading-paths",
49
+ "build:types": "del 'lib/**/*.d.ts' 'lib/**/*.d.ts.map' && tsc --emitDeclarationOnly --skipLibCheck",
50
50
  "test": "pnpm run test:types && pnpm run test:unit",
51
51
  "test:types": "tsc --noEmit --skipLibCheck -p tsconfig.test.json",
52
52
  "test:unit": "vitest run"