@jcbuisson/express-x-client 1.6.1 → 2.0.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/package.json CHANGED
@@ -1,25 +1,15 @@
1
1
  {
2
2
  "name": "@jcbuisson/express-x-client",
3
- "version": "1.6.1",
3
+ "version": "2.0.0",
4
4
  "type": "module",
5
5
  "description": "Client library for ExpressX framework",
6
6
  "main": "src/index.mjs",
7
7
  "test": "node --test",
8
8
  "scripts": {
9
- "test": "node --test",
10
- "generate": "npx prisma generate",
11
- "migrate": "npx prisma migrate dev --name init"
12
9
  },
13
10
  "keywords": [],
14
11
  "author": "",
15
12
  "license": "ISC",
16
13
  "dependencies": {
17
- "@jcbuisson/express-x": "^1.4.0",
18
- "@prisma/client": "^4.16.1",
19
- "axios": "^1.4.0",
20
- "body-parser": "^1.20.2",
21
- "prisma": "^4.16.1",
22
- "socket.io-client": "^4.7.1",
23
- "uuid": "^9.0.0"
24
14
  }
25
15
  }
package/src/index.mjs CHANGED
@@ -1,102 +1,51 @@
1
-
2
- export default function expressXClient(socket, options={}) {
3
- if (options.debug === undefined) options.debug = false
4
- if (options.timeout === undefined) options.timeout = 5000
5
-
6
- const waitingPromisesByUid = {}
7
- const action2service2handlers = {}
8
- const type2appHandlers = {}
9
- let onConnectionCallback = null
10
- let onReconnectionCallback = null
11
- let onDisconnectionCallback = null
12
- let nodeCnxId
13
-
14
- const setConnectionCallback = (callback) => {
15
- onConnectionCallback = callback
16
- }
17
1
 
18
- const setReconnectionCallback = (callback) => {
19
- onReconnectionCallback = callback
20
- }
2
+ function generateUID(length) {
3
+ const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
4
+ let uid = '';
21
5
 
22
- const setDisconnectionCallback = (callback) => {
23
- onDisconnectionCallback = callback
6
+ for (let i = 0; i < length; i++) {
7
+ const randomIndex = Math.floor(Math.random() * characters.length)
8
+ uid += characters.charAt(randomIndex)
24
9
  }
10
+ return uid
11
+ }
25
12
 
26
- function _getCnxId() {
27
- if (typeof sessionStorage !== 'undefined') return sessionStorage.getItem('expressx-cnx-id')
28
- return nodeCnxId
29
- }
30
13
 
31
- function _setCnxId(id) {
32
- if (typeof sessionStorage !== 'undefined') {
33
- sessionStorage.setItem('expressx-cnx-id', id)
34
- } else {
35
- nodeCnxId = id
36
- }
37
- }
14
+ export default function expressXClient(socket, options={}) {
15
+ if (options.debug === undefined) options.debug = false
16
+ if (options.timeout === undefined) options.timeout = 5000
38
17
 
39
- // on connection
40
- socket.on("connected", async (connectionId) => {
41
- if (options.debug) console.log('connected', connectionId)
42
- // look for a previously stored connection id
43
- const prevConnectionId = _getCnxId()
44
- if (prevConnectionId) {
45
- // it's a reconnection
46
- if (prevConnectionId < 0) {
47
- // ask server to transfer all data from connection `prevConnectionId` to connection `connectionId`
48
- if (options.debug) console.log('cnx-transfer', -prevConnectionId, 'to', connectionId)
49
- socket.emit('cnx-transfer', {
50
- from: -prevConnectionId,
51
- to: connectionId,
52
- })
53
- // set/update connection id
54
- _setCnxId(connectionId)
55
- } else {
56
- if (options.debug) console.log('Error, previous connection id should be negative', prevConnectionId)
57
- }
18
+ const waitingPromisesByUid = {}
19
+ const action2service2handlers = {}
20
+ const socketConnectionState = {}
58
21
 
59
- } else {
60
- // set/update connection id
61
- _setCnxId(connectionId)
22
+ socket.on("connect", async () => {
23
+ console.log("socket connected", socket.id)
24
+ if (socketConnectionState.resolve) {
25
+ socketConnectionState.resolve('ok')
26
+ socketConnectionState.status = 'connected'
62
27
  }
63
- // call user-defined connection callback
64
- if (onConnectionCallback) onConnectionCallback(connectionId)
65
28
  })
66
29
 
67
- socket.on("cnx-transfer-ack", async (connection) => {
68
- if (options.debug) console.log('cnx-transfer-ack', connection)
69
- _setCnxId(connection.id)
70
- // call user-defined reconnection callback
71
- if (onReconnectionCallback) onReconnectionCallback(connection)
72
- })
73
-
74
-
75
- // A negative value for the connexion id means that the connection with the server has been lost
76
- // Requests must wait until it goes positive again
77
-
78
- // disconnection due to network issues
79
- socket.on("disconnect", async (cause) => {
80
- const id = _getCnxId()
81
- if (id > 0) {
82
- _setCnxId(-id)
83
- } else {
84
- if (options.debug) console.log('Error (disconnect), connection id should be negative', id)
30
+ socket.on("error", async () => {
31
+ console.log("socket connection error", socket.id)
32
+ if (socketConnectionState.reject) {
33
+ socketConnectionState.reject(err)
34
+ socketConnectionState.status = 'error'
85
35
  }
86
36
  })
87
37
 
88
- // disconnection due to a page reload
89
- if (typeof window !== 'undefined' && 'addEventListener' in window) {
90
- window.addEventListener('unload', () => {
91
- const id = _getCnxId()
92
- if (id > 0) {
93
- _setCnxId(-id)
94
- } else {
95
- if (options.debug) console.log('Error (unload), connection id should be negative', id)
96
- }
38
+ async function socketConnection() {
39
+ const promise = new Promise((resolve, reject) => {
40
+ socketConnectionState.resolve = resolve
41
+ socketConnectionState.reject = reject
97
42
  })
43
+ return promise
98
44
  }
99
45
 
46
+ function socketStatus() {
47
+ return socketConnectionState.status
48
+ }
100
49
 
101
50
  // on receiving response from service request
102
51
  socket.on('client-response', ({ uid, error, result }) => {
@@ -127,21 +76,8 @@ export default function expressXClient(socket, options={}) {
127
76
  const handler = type2appHandlers[type]
128
77
  if (handler) handler(value)
129
78
  })
130
-
131
- function wait(ms) {
132
- return new Promise(resolve => setTimeout(resolve, ms));
133
- }
134
-
79
+
135
80
  async function serviceMethodRequest(name, action, ...args) {
136
- // wait while stored connexion id is negative (= connection is lost with server)
137
- let retries = 10
138
- while (retries-- > 0) {
139
- const id = _getCnxId()
140
- if (id > 0) break
141
- await wait(200)
142
- }
143
- if (retries === 0) throw new Error(`Timeout waiting for reconnection`)
144
-
145
81
  // create a promise which will resolve or reject by an event 'client-response'
146
82
  const uid = generateUID(20)
147
83
  const promise = new Promise((resolve, reject) => {
@@ -163,12 +99,6 @@ export default function expressXClient(socket, options={}) {
163
99
  return promise
164
100
  }
165
101
 
166
- // define application events handlers
167
- function on(type, handler) {
168
- if (!type2appHandlers[type]) type2appHandlers[type] = {}
169
- type2appHandlers[type] = handler
170
- }
171
-
172
102
  function service(name) {
173
103
  const service = {
174
104
  // associate a handler to a pub/sub event for this service
@@ -192,22 +122,9 @@ export default function expressXClient(socket, options={}) {
192
122
  }
193
123
 
194
124
  return {
195
- setConnectionCallback,
196
- setReconnectionCallback,
197
- setDisconnectionCallback,
125
+ socketConnection,
126
+ socketStatus,
198
127
  service,
199
- on,
200
128
  }
201
129
  }
202
130
 
203
-
204
- function generateUID(length) {
205
- const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
206
- let uid = '';
207
-
208
- for (let i = 0; i < length; i++) {
209
- const randomIndex = Math.floor(Math.random() * characters.length)
210
- uid += characters.charAt(randomIndex)
211
- }
212
- return uid
213
- }
@@ -1,130 +0,0 @@
1
-
2
- import bodyParser from 'body-parser'
3
- import axios from 'axios'
4
- import io from 'socket.io-client'
5
- import { PrismaClient } from '@prisma/client'
6
-
7
-
8
- import { describe, it, before, after } from 'node:test'
9
- import { strict as assert } from 'node:assert'
10
-
11
- import { expressX } from '@jcbuisson/express-x'
12
- import expressXClient from '../src/index.mjs'
13
-
14
- const prisma = new PrismaClient()
15
-
16
- // `app` is a regular express application, enhanced with services and real-time features
17
- const app = expressX(prisma)
18
-
19
-
20
- describe('HTTP/REST API', () => {
21
-
22
- let chris
23
-
24
- before(async () => {
25
- console.log("before")
26
- // add body parsers for http requests
27
- app.use(bodyParser.json())
28
- app.use(bodyParser.urlencoded({ extended: false }))
29
-
30
- app.createDatabaseService('User')
31
- app.createDatabaseService('Post')
32
-
33
- await app.service('User').deleteMany()
34
- await app.service('Post').deleteMany()
35
-
36
- // add http/rest endpoints
37
- app.addHttpRest('/api/user', app.service('User'))
38
- app.addHttpRest('/api/post', app.service('Post'))
39
-
40
- await new Promise((resolve) => {
41
- app.server.listen(8008, () => {
42
- console.log(`App listening at http://localhost:8008`)
43
- resolve()
44
- })
45
- })
46
- })
47
-
48
- after(() => {
49
- console.log("after")
50
- app.server.close()
51
- })
52
-
53
- it("can create a user", async () => {
54
- const res = await axios.post('http://localhost:8008/api/user', {
55
- name: "chris",
56
- email: 'chris@mail.fr'
57
- })
58
- assert(res?.data?.name === 'chris')
59
- })
60
-
61
- it("can find users", async () => {
62
- const res = await axios.get('http://localhost:8008/api/user')
63
- assert(res?.data?.length > 0)
64
- })
65
-
66
- it("can find a user by name", async () => {
67
- const res = await axios.get('http://localhost:8008/api/user?name=chris')
68
- assert(res?.data?.length > 0)
69
- chris = res.data[0]
70
- })
71
-
72
- it("can find a user by id", async () => {
73
- const res = await axios.get(`http://localhost:8008/api/user/${chris.id}`)
74
- assert(res?.data?.id === chris.id)
75
- })
76
-
77
- it("can patch a user", async () => {
78
- const res = await axios.patch(`http://localhost:8008/api/user/${chris.id}`, {
79
- name: "Christophe",
80
- })
81
- assert(res?.data?.name === "Christophe")
82
- })
83
-
84
- it("can delete a user", async () => {
85
- const res = await axios.delete(`http://localhost:8008/api/user/${chris.id}`)
86
- assert(res?.data?.name === "Christophe")
87
- })
88
-
89
- after(async () => {
90
- app.server.close()
91
- })
92
- })
93
-
94
-
95
- // test compatibility with client API
96
- describe('Client API', () => {
97
-
98
- let clientApp, socket
99
-
100
- before(async () => {
101
- await new Promise((resolve) => {
102
- app.server.listen(8008, () => {
103
- console.log(`App listening at http://localhost:8008`)
104
- resolve()
105
- })
106
- })
107
-
108
- socket = io('http://localhost:8008', { transports: ["websocket"] })
109
- clientApp = expressXClient(socket)
110
- })
111
-
112
- after(async () => {
113
- app.server.close()
114
- })
115
-
116
- it("can create a user", async () => {
117
- const user = await clientApp.service('User').create({
118
- data: {
119
- name: "chris",
120
- email: 'chris@mail.fr'
121
- },
122
- })
123
- assert(user.name === 'chris')
124
- })
125
-
126
- after(async () => {
127
- await socket.close()
128
- await app.server.close()
129
- })
130
- })