@logux/server 0.12.2 → 0.12.3

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.
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  LoguxSubscribeAction,
3
3
  SyncMapChangeAction,
4
4
  SyncMapCreateAction,
@@ -6,9 +6,8 @@ import {
6
6
  SyncMapValues,
7
7
  SyncMapTypes
8
8
  } from '@logux/actions'
9
-
10
- import { BaseServer, ServerMeta } from '../base-server/index.js'
11
- import { Context } from '../context/index.js'
9
+ import type { BaseServer, ServerMeta } from '../base-server/index.js'
10
+ import type { Context } from '../context/index.js'
12
11
 
13
12
  declare const WITH_TIME: unique symbol
14
13
 
@@ -81,7 +80,7 @@ interface SyncMapOperations<Value extends SyncMapValues> {
81
80
  since: number | undefined,
82
81
  action: LoguxSubscribeAction,
83
82
  meta: ServerMeta
84
- ): Promise<SyncMapData<Value>> | SyncMapData<Value> | false | Promise<false>
83
+ ): Promise<SyncMapData<Value> | false> | SyncMapData<Value> | false
85
84
 
86
85
  create?(
87
86
  ctx: Context,
@@ -1,4 +1,4 @@
1
- import {
1
+ import type {
2
2
  Action,
3
3
  AnyAction,
4
4
  ID,
@@ -8,17 +8,16 @@ import {
8
8
  LogStore,
9
9
  TestTime
10
10
  } from '@logux/core'
11
- import { Server as HTTPServer, ServerResponse, IncomingMessage } from 'http'
12
- import {
11
+ import type { Server as HTTPServer, ServerResponse, IncomingMessage } from 'http'
12
+ import type {
13
13
  LoguxUnsubscribeAction,
14
14
  AbstractActionCreator,
15
15
  LoguxSubscribeAction
16
16
  } from '@logux/actions'
17
- import { Unsubscribe } from 'nanoevents'
18
- import { LogFn } from 'pino'
19
-
20
- import { Context, ChannelContext } from '../context/index.js'
21
- import { ServerClient } from '../server-client/index.js'
17
+ import type { Unsubscribe } from 'nanoevents'
18
+ import type { LogFn } from 'pino'
19
+ import type { Context, ChannelContext } from '../context/index.js'
20
+ import type { ServerClient } from '../server-client/index.js'
22
21
 
23
22
  export interface ServerMeta extends Meta {
24
23
  /**
@@ -110,13 +109,13 @@ export interface BaseServerOptions {
110
109
 
111
110
  /**
112
111
  * Timeout in milliseconds to disconnect connection.
113
- * Default is `20000`.
112
+ * Default is `70000`.
114
113
  */
115
114
  timeout?: number
116
115
 
117
116
  /**
118
117
  * Milliseconds since last message to test connection by sending ping.
119
- * Default is `10000`.
118
+ * Default is `20000`.
120
119
  */
121
120
  ping?: number
122
121
 
@@ -720,7 +719,7 @@ export class BaseServer<
720
719
  subscribers: {
721
720
  [channel: string]: {
722
721
  [nodeId: string]: {
723
- filter: ChannelFilter<{}> | true
722
+ filters: Record<string, ChannelFilter<{}> | true>
724
723
  unsubscribe?: (action: LoguxUnsubscribeAction, meta: ServerMeta) => void
725
724
  }
726
725
  }
@@ -94,6 +94,10 @@ function normalizeChannelCallbacks(pattern, callbacks) {
94
94
  }
95
95
  }
96
96
 
97
+ function subscriberFilterId(action) {
98
+ return JSON.stringify(action.filter || {})
99
+ }
100
+
97
101
  export class BaseServer {
98
102
  constructor(opts = {}) {
99
103
  this.options = opts
@@ -140,12 +144,6 @@ export class BaseServer {
140
144
  log = this.options.time.nextLog({ store, nodeId: this.nodeId })
141
145
  } else {
142
146
  log = new Log({ store, nodeId: this.nodeId })
143
- if (typeof this.options.timeout === 'undefined') {
144
- this.options.timeout = 20000
145
- }
146
- if (typeof this.options.ping === 'undefined') {
147
- this.options.ping = 10000
148
- }
149
147
  }
150
148
 
151
149
  this.logger = console
@@ -554,7 +552,7 @@ export class BaseServer {
554
552
  if (!this.subscribers[channel]) {
555
553
  this.subscribers[channel] = {}
556
554
  }
557
- this.subscribers[channel][nodeId] = { filter: true }
555
+ this.subscribers[channel][nodeId] = { filters: { '{}': true } }
558
556
  this.log.add({ type: 'logux/subscribed', channel }, { nodes: [nodeId] })
559
557
  }
560
558
  }
@@ -599,7 +597,6 @@ export class BaseServer {
599
597
  }
600
598
 
601
599
  if (meta.channels) {
602
- let ctx
603
600
  for (let channel of meta.channels) {
604
601
  if (this.subscribers[channel]) {
605
602
  for (let nodeId in this.subscribers[channel]) {
@@ -607,15 +604,16 @@ export class BaseServer {
607
604
  if (!ignoreClients.has(clientId)) {
608
605
  let subscriber = this.subscribers[channel][nodeId]
609
606
  if (subscriber) {
610
- let filter = subscriber.filter
611
- if (typeof filter === 'function') {
612
- if (!ctx) ctx = this.createContext(action, meta)
613
- filter = await filter(ctx, action, meta)
614
- }
607
+ let ctx = this.createContext(action, meta)
615
608
  let client = this.clientIds.get(clientId)
616
- if (filter && client) {
617
- ignoreClients.add(clientId)
618
- client.node.onAdd(action, meta)
609
+ for (let filter of Object.values(subscriber.filters)) {
610
+ filter = typeof filter === 'function'
611
+ ? await filter(ctx, action, meta)
612
+ : filter
613
+ if (filter && client) {
614
+ ignoreClients.add(clientId)
615
+ client.node.onAdd(action, meta)
616
+ }
619
617
  }
620
618
  }
621
619
  }
@@ -747,8 +745,13 @@ export class BaseServer {
747
745
  return
748
746
  }
749
747
 
750
- let filter = true
751
- if (channel.filter) filter = await channel.filter(ctx, action, meta)
748
+ let filterId = subscriberFilterId(action)
749
+ let filters = { [filterId]: true }
750
+
751
+ if (channel.filter) {
752
+ let filter = await channel.filter(ctx, action, meta)
753
+ filters = { [filterId]: filter }
754
+ }
752
755
 
753
756
  this.emitter.emit('report', 'subscribed', {
754
757
  actionId: meta.id,
@@ -759,8 +762,12 @@ export class BaseServer {
759
762
  this.subscribers[action.channel] = {}
760
763
  this.emitter.emit('subscribing', action, meta)
761
764
  }
765
+ let subscriber = this.subscribers[action.channel][ctx.nodeId]
766
+ if (subscriber) {
767
+ filters = { ...subscriber.filters, ...filters }
768
+ }
762
769
  this.subscribers[action.channel][ctx.nodeId] = {
763
- filter,
770
+ filters,
764
771
  unsubscribe: channel.unsubscribe
765
772
  ? (unsubscribeAction, unsubscribeMeta) =>
766
773
  channel.unsubscribe(ctx, unsubscribeAction, unsubscribeMeta)
@@ -810,7 +817,11 @@ export class BaseServer {
810
817
  subscriber.unsubscribe(action, meta)
811
818
  this.contexts.delete(action)
812
819
  }
813
- delete this.subscribers[action.channel][clientNodeId]
820
+ let filterId = subscriberFilterId(action)
821
+ delete subscriber.filters[filterId]
822
+ if (Object.keys(subscriber.filters).length === 0) {
823
+ delete this.subscribers[action.channel][clientNodeId]
824
+ }
814
825
  if (Object.keys(this.subscribers[action.channel]).length === 0) {
815
826
  delete this.subscribers[action.channel]
816
827
  }
@@ -1,7 +1,6 @@
1
- import { AnyAction } from '@logux/core'
2
-
3
- import { ServerMeta } from '../base-server/index.js'
4
- import { Server } from '../server/index.js'
1
+ import type { AnyAction } from '@logux/core'
2
+ import type { ServerMeta } from '../base-server/index.js'
3
+ import type { Server } from '../server/index.js'
5
4
 
6
5
  /**
7
6
  * Action context.
@@ -1,4 +1,4 @@
1
- import { ServerMeta } from '../base-server/index.js'
1
+ import type { ServerMeta } from '../base-server/index.js'
2
2
 
3
3
  /**
4
4
  * Remove all non-allowed keys from meta.
@@ -1,3 +1,4 @@
1
+ import { it, expect, describe } from 'vitest'
1
2
  import { resolve } from 'path'
2
3
 
3
4
  import { loadOptions, oneOf, number } from './index.js'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@logux/server",
3
- "version": "0.12.2",
3
+ "version": "0.12.3",
4
4
  "description": "Build own Logux server or make proxy between WebSocket and HTTP backend on any language",
5
5
  "keywords": [
6
6
  "logux",
@@ -28,23 +28,23 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@logux/actions": "^0.3.0",
31
- "@logux/core": "^0.8.0",
31
+ "@logux/core": "^0.8.1",
32
32
  "JSONStream": "^1.3.5",
33
33
  "cookie": "^0.5.0",
34
- "dotenv": "^16.0.2",
35
- "fast-glob": "^3.2.11",
34
+ "dotenv": "^16.0.3",
35
+ "fast-glob": "^3.2.12",
36
36
  "ip": "^1.1.8",
37
37
  "nanodelay": "^2.0.2",
38
38
  "nanoevents": "^7.0.1",
39
- "nanoid": "^4.0.0",
40
- "node-fetch": "^2.6.7",
39
+ "nanoid": "^4.0.2",
40
+ "node-fetch": "^3.3.1",
41
41
  "picocolors": "^1.0.0",
42
- "pino": "8.5.0",
42
+ "pino": "8.14.1",
43
43
  "pino-abstract-transport": "^1.0.0",
44
- "semver": "^7.3.7",
44
+ "semver": "^7.5.1",
45
45
  "strip-ansi": "^7.0.1",
46
46
  "url-pattern": "^1.0.3",
47
- "ws": "^8.8.1",
47
+ "ws": "^8.13.0",
48
48
  "yyyy-mm-dd": "^1.0.2"
49
49
  }
50
50
  }
@@ -1,4 +1,5 @@
1
- import fetch, { RequestInit } from 'node-fetch'
1
+ import type { RequestInit } from 'node-fetch'
2
+ import type fetch from 'node-fetch'
2
3
 
3
4
  /**
4
5
  * Throwing this error in `accessAndProcess` or `accessAndLoad`
@@ -1,4 +1,5 @@
1
- import { jest } from '@jest/globals'
1
+ import { it, expect } from 'vitest'
2
+ import { spy } from 'nanospy'
2
3
 
3
4
  import { ResponseError, request, patch, post, get, put, del } from '../index.js'
4
5
 
@@ -43,7 +44,7 @@ it('parses JSON body', async () => {
43
44
  })
44
45
 
45
46
  it('has shortcut for GET', async () => {
46
- let fetch = jest.fn(async () => ({
47
+ let fetch = spy(async () => ({
47
48
  status: 200,
48
49
  async json() {
49
50
  return { answer: '1' }
@@ -51,46 +52,71 @@ it('has shortcut for GET', async () => {
51
52
  }))
52
53
 
53
54
  expect(await get('/a', { headers: { a: 1 } }, fetch)).toEqual({ answer: '1' })
54
- expect(fetch).toHaveBeenCalledWith('/a', {
55
- method: 'GET',
56
- headers: { a: 1 }
57
- })
55
+ expect(fetch.calls).toEqual([
56
+ [
57
+ '/a',
58
+ {
59
+ method: 'GET',
60
+ headers: { a: 1 }
61
+ }
62
+ ]
63
+ ])
58
64
  })
59
65
 
60
66
  it('has shortcut for POST', async () => {
61
- let fetch = jest.fn(async () => ({ status: 204 }))
67
+ let fetch = spy(async () => ({ status: 204 }))
62
68
  await post('/a', { headers: { a: 1 } }, fetch)
63
- expect(fetch).toHaveBeenCalledWith('/a', {
64
- method: 'POST',
65
- headers: { a: 1 }
66
- })
69
+ expect(fetch.calls).toEqual([
70
+ [
71
+ '/a',
72
+ {
73
+ method: 'POST',
74
+ headers: { a: 1 }
75
+ }
76
+ ]
77
+ ])
67
78
  })
68
79
 
69
80
  it('has shortcut for PUT', async () => {
70
- let fetch = jest.fn(async () => ({ status: 204 }))
81
+ let fetch = spy(async () => ({ status: 204 }))
71
82
  await put('/a', { headers: { a: 1 } }, fetch)
72
- expect(fetch).toHaveBeenCalledWith('/a', {
73
- method: 'PUT',
74
- headers: { a: 1 }
75
- })
83
+ expect(fetch.calls).toEqual([
84
+ [
85
+ '/a',
86
+ {
87
+ method: 'PUT',
88
+ headers: { a: 1 }
89
+ }
90
+ ]
91
+ ])
76
92
  })
77
93
 
78
94
  it('has shortcut for PATCH', async () => {
79
- let fetch = jest.fn(async () => ({ status: 204 }))
95
+ let fetch = spy(async () => ({ status: 204 }))
80
96
  await patch('/a', { headers: { a: 1 } }, fetch)
81
- expect(fetch).toHaveBeenCalledWith('/a', {
82
- method: 'PATCH',
83
- headers: { a: 1 }
84
- })
97
+ expect(fetch.calls).toEqual([
98
+ [
99
+ '/a',
100
+ {
101
+ method: 'PATCH',
102
+ headers: { a: 1 }
103
+ }
104
+ ]
105
+ ])
85
106
  })
86
107
 
87
108
  it('has shortcut for DELETE', async () => {
88
- let fetch = jest.fn(async () => ({ status: 204 }))
109
+ let fetch = spy(async () => ({ status: 204 }))
89
110
  await del('/a', { headers: { a: 1 } }, fetch)
90
- expect(fetch).toHaveBeenCalledWith('/a', {
91
- method: 'DELETE',
92
- headers: { a: 1 }
93
- })
111
+ expect(fetch.calls).toEqual([
112
+ [
113
+ '/a',
114
+ {
115
+ method: 'DELETE',
116
+ headers: { a: 1 }
117
+ }
118
+ ]
119
+ ])
94
120
  })
95
121
 
96
122
  it('works without option object', async () => {
package/server/index.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import { BaseServer, Logger, BaseServerOptions } from '../base-server/index.js'
1
+ import type { Logger, BaseServerOptions } from '../base-server/index.js'
2
+
3
+ import { BaseServer } from '../base-server/index.js'
2
4
 
3
5
  export interface LoggerOptions {
4
6
  /**
@@ -1,6 +1,5 @@
1
- import { ServerConnection, ServerNode } from '@logux/core'
2
-
3
- import { BaseServer } from '../base-server/index.js'
1
+ import type { ServerConnection, ServerNode } from '@logux/core'
2
+ import type { BaseServer } from '../base-server/index.js'
4
3
 
5
4
  /**
6
5
  * Logux client connected to server.
@@ -1,8 +1,7 @@
1
- import { LoguxSubscribeAction, LoguxUnsubscribeAction } from '@logux/actions'
2
- import { Action, AnyAction, TestLog, TestPair } from '@logux/core'
3
-
4
- import { ServerMeta } from '../base-server/index.js'
5
- import { TestServer } from '../test-server/index.js'
1
+ import type { LoguxSubscribeAction, LoguxUnsubscribeAction } from '@logux/actions'
2
+ import type { Action, AnyAction, TestLog, TestPair } from '@logux/core'
3
+ import type { ServerMeta } from '../base-server/index.js'
4
+ import type { TestServer } from '../test-server/index.js'
6
5
 
7
6
  export class LoguxActionError extends Error {
8
7
  action: Action
@@ -1,13 +1,13 @@
1
- import { TestTime, TestLog } from '@logux/core'
2
-
3
- import { TestClient, TestClientOptions } from '../test-client/index.js'
4
- import {
1
+ import type { TestTime, TestLog } from '@logux/core'
2
+ import type { TestClient, TestClientOptions } from '../test-client/index.js'
3
+ import type {
5
4
  BaseServerOptions,
6
- BaseServer,
7
5
  ServerMeta,
8
6
  Logger
9
7
  } from '../base-server/index.js'
10
- import { LoggerOptions } from '../server/index.js'
8
+ import type { LoggerOptions } from '../server/index.js'
9
+
10
+ import { BaseServer } from '../base-server/index.js'
11
11
 
12
12
  export interface TestServerOptions
13
13
  extends Omit<BaseServerOptions, 'subprotocol' | 'supports'> {
package/vite.config.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from 'vitest/config'
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: 'node',
6
+ exclude: ['node_modules', 'test/servers'],
7
+ coverage: {
8
+ provider: 'c8',
9
+ lines: 100,
10
+ exclude: ['node_modules', 'server/index.js', '*/*.test.*']
11
+ }
12
+ }
13
+ })