@zintrust/socket 0.4.59 → 0.4.63

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,14 +1,14 @@
1
1
  {
2
2
  "name": "@zintrust/socket",
3
- "version": "0.4.59",
4
- "buildDate": "2026-04-04T22:09:08.837Z",
3
+ "version": "0.4.63",
4
+ "buildDate": "2026-04-05T13:13:05.363Z",
5
5
  "buildEnvironment": {
6
6
  "node": "v22.22.1",
7
7
  "platform": "darwin",
8
8
  "arch": "arm64"
9
9
  },
10
10
  "git": {
11
- "commit": "99e4d331",
11
+ "commit": "533afc24",
12
12
  "branch": "release"
13
13
  },
14
14
  "package": {
@@ -22,16 +22,16 @@
22
22
  },
23
23
  "files": {
24
24
  "build-manifest.json": {
25
- "size": 947,
26
- "sha256": "2e619502513f9b465881fdb6cb9cda78bb082e6df9bc20d4f1d62a1f7173174b"
25
+ "size": 1086,
26
+ "sha256": "a55e096a80190637fe7d56c36e53865451f623c88ceb5ccb7b9124e4a14335bb"
27
27
  },
28
28
  "index.d.ts": {
29
- "size": 932,
30
- "sha256": "6432952783fd7eacfc46813fcbd6e96672ff94c73fb0bad8e2f20fc278c64377"
29
+ "size": 1393,
30
+ "sha256": "c3b8e8d96234c34a42f08dd14fadab504cb6f2bd76c15bce5fb23e5acde4941f"
31
31
  },
32
32
  "index.js": {
33
- "size": 35358,
34
- "sha256": "6a43f6cdc1591b1dc7304e5ccd66be1e41aaa7419546eb0258a280b8ea9b8642"
33
+ "size": 38284,
34
+ "sha256": "cf683327a9a0d2ba1b349cc059c2113c76e36e130b56e613932fdccd5c4f435f"
35
35
  },
36
36
  "register.d.ts": {
37
37
  "size": 16,
package/dist/index.d.ts CHANGED
@@ -1,4 +1,19 @@
1
- import { type IRouter, type SocketRouteRegistrar } from '@zintrust/core';
1
+ import { type IRequest, type IRouter, type SocketRouteRegistrar } from '@zintrust/core';
2
+ type ServerSideSocketPublishInput = Readonly<{
3
+ channels: readonly string[];
4
+ event: string;
5
+ data: unknown;
6
+ socketId?: string;
7
+ request?: IRequest;
8
+ user?: unknown;
9
+ }>;
10
+ declare const publishSocketEventFromServer: (input: ServerSideSocketPublishInput) => Promise<{
11
+ ok: true;
12
+ transport: "node" | "cloudflare";
13
+ channels: readonly string[];
14
+ event: string;
15
+ deliveries: number;
16
+ }>;
2
17
  declare const socketRuntime: any;
3
18
  declare const registerSocketRoutes: (router: IRouter) => void;
4
19
  declare const socketRouteRegistrar: SocketRouteRegistrar;
@@ -16,5 +31,5 @@ export declare class ZintrustSocketHub {
16
31
  private handlePublishRequest;
17
32
  }
18
33
  export declare const publishSocketEvent: (channels: string[], event: string, data: unknown, excludeSocketId?: string) => number;
19
- export { registerSocketRoutes, socketRouteRegistrar, socketRuntime };
34
+ export { publishSocketEventFromServer, registerSocketRoutes, socketRouteRegistrar, socketRuntime };
20
35
  export default SocketPackage;
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { Cloudflare, broadcastConfig, ErrorFactory, isArray, isNonEmptyString, Logger, middlewareConfig, Router, SocketFeature, } from '@zintrust/core';
1
+ import { broadcastConfig, Cloudflare, ErrorFactory, isArray, isNonEmptyString, Logger, middlewareConfig, Router, SocketFeature, } from '@zintrust/core';
2
2
  const encoder = new TextEncoder();
3
3
  const websocketGuid = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11';
4
4
  const socketHubBindingName = 'ZT_SOCKET_HUB';
@@ -46,7 +46,11 @@ const normalizeSocketPath = (value) => {
46
46
  if (trimmed === '' || trimmed === '/')
47
47
  return '/app';
48
48
  const normalized = trimmed.startsWith('/') ? trimmed : `/${trimmed}`;
49
- return normalized.length > 1 ? normalized.replace(/\/+$/, '') : normalized;
49
+ let end = normalized.length;
50
+ while (end > 1 && normalized[end - 1] === '/') {
51
+ end -= 1;
52
+ }
53
+ return end === normalized.length ? normalized : normalized.slice(0, end);
50
54
  };
51
55
  const pickFirstNonEmpty = (...values) => {
52
56
  for (const value of values) {
@@ -491,7 +495,7 @@ const shouldUseCloudflareHub = (settings) => {
491
495
  };
492
496
  const parseJsonResponse = async (response) => {
493
497
  try {
494
- return (await response.clone().json());
498
+ return await response.clone().json();
495
499
  }
496
500
  catch {
497
501
  try {
@@ -658,6 +662,76 @@ const normalizePublishDecisionPayload = (payload, decision) => {
658
662
  })(),
659
663
  };
660
664
  };
665
+ const createServerSidePublishRequest = (input) => {
666
+ return {
667
+ getBody: () => null,
668
+ getHeader: () => undefined,
669
+ getParam: () => undefined,
670
+ user: input.user ?? null,
671
+ };
672
+ };
673
+ const getResponseDeliveries = (payload) => {
674
+ if (typeof payload !== 'object' || payload === null) {
675
+ return 0;
676
+ }
677
+ const deliveries = payload.deliveries;
678
+ return typeof deliveries === 'number' && Number.isFinite(deliveries) ? deliveries : 0;
679
+ };
680
+ const publishSocketEventFromServer = async (input) => {
681
+ const settings = getSocketRuntimeSettings(Cloudflare.getWorkersEnv());
682
+ if (!settings.enabled || settings.appKey.trim() === '') {
683
+ throw ErrorFactory.createConfigError('Socket runtime is not enabled.');
684
+ }
685
+ const request = input.request ?? createServerSidePublishRequest(input);
686
+ const payload = {
687
+ channels: input.channels.map((channel) => channel.trim()),
688
+ event: input.event.trim(),
689
+ data: input.data ?? {},
690
+ ...(isNonEmptyString(input.socketId) ? { socket_id: input.socketId.trim() } : {}),
691
+ };
692
+ const publishPolicy = resolveSocketPublishPolicy();
693
+ const decision = await publishPolicy.authorize(request, {
694
+ appId: settings.appId,
695
+ channels: payload.channels,
696
+ event: payload.event,
697
+ data: payload.data,
698
+ socketId: payload.socket_id,
699
+ user: input.user ?? request.user ?? null,
700
+ });
701
+ if (decision.allowed !== true) {
702
+ throw ErrorFactory.createForbiddenError(decision.message ?? 'Forbidden', {
703
+ statusCode: decision.statusCode ?? 403,
704
+ });
705
+ }
706
+ const allowedPayload = normalizePublishDecisionPayload(payload, decision);
707
+ if (allowedPayload === null) {
708
+ throw ErrorFactory.createValidationError('Socket publish policy must return a non-empty event and channels.');
709
+ }
710
+ if (shouldUseCloudflareHub(settings)) {
711
+ const response = await forwardPublishToHub(settings, allowedPayload, Cloudflare.getWorkersEnv());
712
+ const responseBody = await parseJsonResponse(response);
713
+ if (!response.ok) {
714
+ throw ErrorFactory.createTryCatchError(`Socket publish request failed (${response.status})`, {
715
+ status: response.status,
716
+ body: responseBody,
717
+ });
718
+ }
719
+ return {
720
+ ok: true,
721
+ transport: 'cloudflare',
722
+ channels: allowedPayload.channels,
723
+ event: allowedPayload.event,
724
+ deliveries: getResponseDeliveries(responseBody),
725
+ };
726
+ }
727
+ return {
728
+ ok: true,
729
+ transport: 'node',
730
+ channels: allowedPayload.channels,
731
+ event: allowedPayload.event,
732
+ deliveries: publishToChannels(getNodeSocketState(), allowedPayload.channels, allowedPayload.event, allowedPayload.data, allowedPayload.socket_id),
733
+ };
734
+ };
661
735
  const forwardPublishToHub = async (settings, payload, envSource) => {
662
736
  const stub = getSocketHubStub(settings, envSource);
663
737
  if (stub === null) {
@@ -839,15 +913,13 @@ const publishEvent = async (req, res) => {
839
913
  const registerSocketRoutes = (router) => {
840
914
  const settings = getSocketRuntimeSettings();
841
915
  const allowAuthRouteOverride = isSocketAuthRouteOverrideEnabled();
916
+ const hasExistingAuthRoute = routeExists(router, 'POST', '/broadcasting/auth');
842
917
  assertReservedSocketRouteAvailable(router, 'GET', `${settings.path}/:appKey`);
843
- assertReservedSocketRouteAvailable(router, 'POST', '/broadcasting/auth', {
844
- allowOverride: true,
845
- });
846
918
  assertReservedSocketRouteAvailable(router, 'POST', '/apps/:appId/events');
847
919
  Router.get(router, `${settings.path}/:appKey`, respondUpgradeRequired);
848
- if (allowAuthRouteOverride) {
849
- if (!routeExists(router, 'POST', '/broadcasting/auth')) {
850
- Logger.warn('SOCKET_ALLOW_AUTH_ROUTE_OVERRIDE=true but POST /broadcasting/auth is not registered by the application.');
920
+ if (hasExistingAuthRoute) {
921
+ if (!allowAuthRouteOverride) {
922
+ Logger.info('Detected existing application-owned POST /broadcasting/auth route; preserving it while sockets are enabled.');
851
923
  }
852
924
  }
853
925
  else {
@@ -926,5 +998,5 @@ export class ZintrustSocketHub {
926
998
  export const publishSocketEvent = (channels, event, data, excludeSocketId) => {
927
999
  return publishToChannels(getNodeSocketState(), channels, event, data, excludeSocketId);
928
1000
  };
929
- export { registerSocketRoutes, socketRouteRegistrar, socketRuntime };
1001
+ export { publishSocketEventFromServer, registerSocketRoutes, socketRouteRegistrar, socketRuntime };
930
1002
  export default SocketPackage;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/socket",
3
- "version": "0.4.59",
3
+ "version": "0.4.63",
4
4
  "description": "Unified socket runtime for ZinTrust.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "node": ">=20.0.0"
24
24
  },
25
25
  "peerDependencies": {
26
- "@zintrust/core": "^0.4.59"
26
+ "@zintrust/core": "^0.4.63"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"