@rljson/bs 0.0.19 → 0.0.20

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/bs.js CHANGED
@@ -740,21 +740,23 @@ class BsPeerBridge {
740
740
  this._socket = _socket;
741
741
  }
742
742
  _eventHandlers = /* @__PURE__ */ new Map();
743
+ _handleConnectBound = this._handleConnect.bind(this);
744
+ _handleDisconnectBound = this._handleDisconnect.bind(this);
743
745
  /**
744
746
  * Starts the bridge by setting up connection event handlers and
745
747
  * automatically registering all Bs methods.
746
748
  */
747
749
  start() {
748
- this._socket.on("connect", () => this._handleConnect());
749
- this._socket.on("disconnect", () => this._handleDisconnect());
750
+ this._socket.on("connect", this._handleConnectBound);
751
+ this._socket.on("disconnect", this._handleDisconnectBound);
750
752
  this._registerBsMethods();
751
753
  }
752
754
  /**
753
755
  * Stops the bridge by removing all event handlers.
754
756
  */
755
757
  stop() {
756
- this._socket.off("connect", () => this._handleConnect());
757
- this._socket.off("disconnect", () => this._handleDisconnect());
758
+ this._socket.off("connect", this._handleConnectBound);
759
+ this._socket.off("disconnect", this._handleDisconnectBound);
758
760
  for (const [eventName, handler] of this._eventHandlers) {
759
761
  this._socket.off(eventName, handler);
760
762
  }
@@ -765,14 +767,11 @@ class BsPeerBridge {
765
767
  */
766
768
  _registerBsMethods() {
767
769
  const bsMethods = [
768
- "setBlob",
769
770
  "getBlob",
770
771
  "getBlobStream",
771
- "deleteBlob",
772
772
  "blobExists",
773
773
  "getBlobProperties",
774
- "listBlobs",
775
- "generateSignedUrl"
774
+ "listBlobs"
776
775
  ];
777
776
  for (const methodName of bsMethods) {
778
777
  this.registerEvent(methodName);
@@ -794,17 +793,17 @@ class BsPeerBridge {
794
793
  `Method "${methodName}" not found on Bs instance`
795
794
  );
796
795
  if (typeof callback === "function") {
797
- callback(null, error);
796
+ callback(error, null);
798
797
  }
799
798
  return;
800
799
  }
801
800
  bsMethod.apply(this._bs, methodArgs).then((result) => {
802
801
  if (typeof callback === "function") {
803
- callback(result, null);
802
+ callback(null, result);
804
803
  }
805
804
  }).catch((error) => {
806
805
  if (typeof callback === "function") {
807
- callback(null, error);
806
+ callback(error, null);
808
807
  }
809
808
  });
810
809
  };
@@ -852,9 +851,9 @@ class BsPeerBridge {
852
851
  throw new Error(`Method "${bsMethodName}" not found on Bs instance`);
853
852
  }
854
853
  const result = await bsMethod.apply(this._bs, args);
855
- this._socket.emit(socketEventName, result, null);
854
+ this._socket.emit(socketEventName, null, result);
856
855
  } catch (error) {
857
- this._socket.emit(socketEventName, null, error);
856
+ this._socket.emit(socketEventName, error, null);
858
857
  }
859
858
  }
860
859
  /* v8 ignore next -- @preserve */
@@ -1075,6 +1074,155 @@ class SocketMock {
1075
1074
  return new Map(this._onceListeners);
1076
1075
  }
1077
1076
  }
1077
+ function createSocketPair() {
1078
+ const socketA = new DirectionalSocketMock();
1079
+ const socketB = new DirectionalSocketMock();
1080
+ socketA._setPeer(socketB);
1081
+ socketB._setPeer(socketA);
1082
+ return [socketA, socketB];
1083
+ }
1084
+ class DirectionalSocketMock {
1085
+ connected = false;
1086
+ disconnected = true;
1087
+ _peer;
1088
+ _listeners = /* @__PURE__ */ new Map();
1089
+ _onceListeners = /* @__PURE__ */ new Map();
1090
+ _setPeer(peer) {
1091
+ this._peer = peer;
1092
+ }
1093
+ connect() {
1094
+ if (!this.connected) {
1095
+ this.connected = true;
1096
+ this.disconnected = false;
1097
+ this._triggerLocal("connect");
1098
+ if (this._peer) {
1099
+ this._peer._triggerLocal("connect");
1100
+ }
1101
+ }
1102
+ }
1103
+ disconnect() {
1104
+ if (this.connected) {
1105
+ this.connected = false;
1106
+ this.disconnected = true;
1107
+ this._triggerLocal("disconnect");
1108
+ if (this._peer) {
1109
+ this._peer._triggerLocal("disconnect");
1110
+ }
1111
+ }
1112
+ }
1113
+ on(eventName, listener) {
1114
+ if (!this._listeners.has(eventName)) {
1115
+ this._listeners.set(eventName, []);
1116
+ }
1117
+ this._listeners.get(eventName).push(listener);
1118
+ return this;
1119
+ }
1120
+ once(eventName, listener) {
1121
+ if (!this._onceListeners.has(eventName)) {
1122
+ this._onceListeners.set(eventName, []);
1123
+ }
1124
+ this._onceListeners.get(eventName).push(listener);
1125
+ return this;
1126
+ }
1127
+ off(eventName, listener) {
1128
+ if (listener) {
1129
+ const regularListeners = this._listeners.get(eventName);
1130
+ if (regularListeners) {
1131
+ const index = regularListeners.indexOf(listener);
1132
+ if (index > -1) regularListeners.splice(index, 1);
1133
+ }
1134
+ const onceListeners = this._onceListeners.get(eventName);
1135
+ if (onceListeners) {
1136
+ const index = onceListeners.indexOf(listener);
1137
+ if (index > -1) onceListeners.splice(index, 1);
1138
+ }
1139
+ } else {
1140
+ this._listeners.delete(eventName);
1141
+ this._onceListeners.delete(eventName);
1142
+ }
1143
+ return this;
1144
+ }
1145
+ /**
1146
+ * Emits an event to the PEER socket (cross-socket emission).
1147
+ * This is the key difference from SocketMock - emit() sends to the other side,
1148
+ * not to local listeners.
1149
+ *
1150
+ * Implements Socket.IO acknowledgement pattern: the last argument can be a callback
1151
+ * that the peer will invoke to send a response back.
1152
+ * @param eventName - The event name to emit
1153
+ * @param args - Arguments to pass with the event
1154
+ */
1155
+ emit(eventName, ...args) {
1156
+ if (!this._peer) {
1157
+ console.warn(
1158
+ `DirectionalSocketMock.emit: No peer connected for event ${String(eventName)}`
1159
+ );
1160
+ return false;
1161
+ }
1162
+ this._peer._triggerLocal(eventName, ...args);
1163
+ return true;
1164
+ }
1165
+ /**
1166
+ * Triggers listeners on THIS socket (local emission).
1167
+ * Used internally when receiving events from peer.
1168
+ * @param eventName - The event name to trigger
1169
+ * @param args - Arguments to pass to the listeners
1170
+ */
1171
+ _triggerLocal(eventName, ...args) {
1172
+ const regularListeners = this._listeners.get(eventName);
1173
+ if (regularListeners) {
1174
+ [...regularListeners].forEach((listener) => {
1175
+ try {
1176
+ listener(...args);
1177
+ } catch (error) {
1178
+ console.error(`Error in listener for ${String(eventName)}:`, error);
1179
+ }
1180
+ });
1181
+ }
1182
+ const onceListeners = this._onceListeners.get(eventName);
1183
+ if (onceListeners) {
1184
+ const listenersToCall = [...onceListeners];
1185
+ this._onceListeners.delete(eventName);
1186
+ listenersToCall.forEach((listener) => {
1187
+ try {
1188
+ listener(...args);
1189
+ } catch (error) {
1190
+ console.error(
1191
+ `Error in once listener for ${String(eventName)}:`,
1192
+ error
1193
+ );
1194
+ }
1195
+ });
1196
+ }
1197
+ }
1198
+ removeAllListeners(eventName) {
1199
+ if (eventName !== void 0) {
1200
+ this._listeners.delete(eventName);
1201
+ this._onceListeners.delete(eventName);
1202
+ } else {
1203
+ this._listeners.clear();
1204
+ this._onceListeners.clear();
1205
+ }
1206
+ return this;
1207
+ }
1208
+ listenerCount(eventName) {
1209
+ const regularCount = this._listeners.get(eventName)?.length || 0;
1210
+ const onceCount = this._onceListeners.get(eventName)?.length || 0;
1211
+ return regularCount + onceCount;
1212
+ }
1213
+ listeners(eventName) {
1214
+ const regularListeners = this._listeners.get(eventName) || [];
1215
+ const onceListeners = this._onceListeners.get(eventName) || [];
1216
+ return [...regularListeners, ...onceListeners];
1217
+ }
1218
+ eventNames() {
1219
+ const allEvents = /* @__PURE__ */ new Set([
1220
+ ...this._listeners.keys(),
1221
+ ...this._onceListeners.keys()
1222
+ ]);
1223
+ return Array.from(allEvents);
1224
+ }
1225
+ }
1078
1226
  export {
1079
1227
  BsMem,
1080
1228
  BsMulti,
@@ -1082,5 +1230,6 @@ export {
1082
1230
  BsPeerBridge,
1083
1231
  BsServer,
1084
1232
  PeerSocketMock,
1085
- SocketMock
1233
+ SocketMock,
1234
+ createSocketPair
1086
1235
  };
@@ -0,0 +1,10 @@
1
+ import { Socket } from './socket.ts';
2
+ /**
3
+ * Creates a pair of connected sockets that properly route messages between them.
4
+ * Unlike SocketMock, this maintains directionality - when socketA emits, only socketB's
5
+ * listeners fire, not socketA's own listeners.
6
+ *
7
+ * This is essential for client-server testing where both use the same socket instance
8
+ * but need separate event handling.
9
+ */
10
+ export declare function createSocketPair(): [Socket, Socket];
package/dist/index.d.ts CHANGED
@@ -7,4 +7,5 @@ export type { BsTestSetup } from './bs-test-setup.js';
7
7
  export type { BlobProperties, Bs, DownloadBlobOptions, ListBlobsOptions, ListBlobsResult, } from './bs.js';
8
8
  export { PeerSocketMock } from './peer-socket-mock.js';
9
9
  export { SocketMock } from './socket-mock.js';
10
+ export { createSocketPair } from './directional-socket-mock.js';
10
11
  export type { Socket } from './socket.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rljson/bs",
3
- "version": "0.0.19",
3
+ "version": "0.0.20",
4
4
  "description": "Blob storage interface and implementations for rljson",
5
5
  "homepage": "https://github.com/rljson/bs",
6
6
  "bugs": "https://github.com/rljson/bs/issues",
@@ -20,30 +20,30 @@
20
20
  ],
21
21
  "type": "module",
22
22
  "devDependencies": {
23
- "@types/node": "^25.0.10",
24
- "@typescript-eslint/eslint-plugin": "^8.53.1",
25
- "@typescript-eslint/parser": "^8.53.1",
26
- "@vitest/coverage-v8": "^4.0.17",
23
+ "@types/node": "^25.1.0",
24
+ "@typescript-eslint/eslint-plugin": "^8.54.0",
25
+ "@typescript-eslint/parser": "^8.54.0",
26
+ "@vitest/coverage-v8": "^4.0.18",
27
27
  "cross-env": "^10.1.0",
28
28
  "eslint": "^9.39.2",
29
- "eslint-plugin-jsdoc": "^62.3.0",
29
+ "eslint-plugin-jsdoc": "^62.5.0",
30
30
  "eslint-plugin-tsdoc": "^0.5.0",
31
- "globals": "^17.0.0",
31
+ "globals": "^17.2.0",
32
32
  "jsdoc": "^4.0.5",
33
33
  "read-pkg": "^10.0.0",
34
34
  "typescript": "~5.9.3",
35
- "typescript-eslint": "^8.53.1",
35
+ "typescript-eslint": "^8.54.0",
36
36
  "vite": "^7.3.1",
37
37
  "vite-node": "^5.3.0",
38
38
  "vite-plugin-dts": "^4.5.4",
39
- "vite-tsconfig-paths": "^6.0.4",
40
- "vitest": "^4.0.17",
39
+ "vite-tsconfig-paths": "^6.0.5",
40
+ "vitest": "^4.0.18",
41
41
  "vitest-dom": "^0.1.1"
42
42
  },
43
43
  "dependencies": {
44
44
  "@rljson/hash": "^0.0.18",
45
45
  "@rljson/json": "^0.0.23",
46
- "@rljson/rljson": "^0.0.74"
46
+ "@rljson/rljson": "^0.0.75"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "pnpx vite build && tsc && node scripts/copy-readme-to-dist.js && node scripts/deploy-conformance-tests.js",