hotpipe 0.1.0 → 0.10.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.
@@ -1,124 +1,136 @@
1
1
  'use client';
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createContext, useCallback, useContext, useEffect, useRef, useState } from 'react';
4
- import { ConnectionManager } from './connection';
5
- const DEFAULT_BROKER_URL = 'wss://api.hotpipe.dev';
4
+ import { ConnectionManager, } from './connection';
5
+ const DEFAULT_API_URL = 'wss://api.hotpipe.dev';
6
+ const DEFAULT_BASE_PATH = '/api/realtime';
7
+ const globalCache = globalThis;
6
8
  /**
7
9
  * Creates a typed real-time client bound to your event schemas.
8
10
  *
9
11
  * Usage:
10
12
  * ```ts
11
- * const { RealtimeProvider, useChannel, useEvent } = createRealtimeClient({
13
+ * const { PipeProvider, usePipe } = createPipeClient({
12
14
  * events: realtimeEvents,
13
- * auth: { endpoint: '/api/realtime/auth' },
14
15
  * });
15
16
  * ```
16
17
  */
17
- export function createRealtimeClient(config) {
18
- const RealtimeContext = createContext(null);
18
+ export function createPipeClient(config) {
19
+ const apiUrl = config.apiUrl ?? DEFAULT_API_URL;
20
+ const authEndpoint = `${config.basePath ?? DEFAULT_BASE_PATH}/auth`;
21
+ const connectionConfig = {
22
+ apiUrl,
23
+ authEndpoint,
24
+ authHeaders: config.authHeaders,
25
+ onPipeRevoked: config.onPipeRevoked,
26
+ onAllPipesRevoked: config.onAllPipesRevoked,
27
+ };
28
+ // Reuse existing manager in dev to survive HMR module re-evaluation
29
+ const cacheKey = `__hotpipe_${apiUrl}_${authEndpoint}`;
30
+ let manager;
31
+ if (globalCache[cacheKey]) {
32
+ manager = globalCache[cacheKey];
33
+ manager.updateConfig(connectionConfig);
34
+ }
35
+ else {
36
+ manager = new ConnectionManager(connectionConfig);
37
+ }
38
+ if (process.env.NODE_ENV !== 'production') {
39
+ globalCache[cacheKey] = manager;
40
+ }
41
+ const PipeContext = createContext(null);
19
42
  function useManager() {
20
- const manager = useContext(RealtimeContext);
21
- if (!manager) {
22
- throw new Error('hotpipe: useChannel/useEvent must be used within <RealtimeProvider>');
43
+ const ctx = useContext(PipeContext);
44
+ if (!ctx) {
45
+ throw new Error('hotpipe: usePipe must be used within <PipeProvider>');
23
46
  }
24
- return manager;
47
+ return ctx;
25
48
  }
26
49
  /**
27
50
  * Wrap your app (or a subtree) with this provider to establish the
28
- * WebSocket connection to the broker.
51
+ * WebSocket connection to the hotpipe API.
29
52
  */
30
- function RealtimeProvider({ children }) {
31
- const managerRef = useRef(null);
32
- if (!managerRef.current) {
33
- managerRef.current = new ConnectionManager({
34
- brokerUrl: config.brokerUrl ?? DEFAULT_BROKER_URL,
35
- authEndpoint: config.auth.endpoint,
36
- });
37
- }
53
+ function PipeProvider({ children }) {
38
54
  useEffect(() => {
39
- managerRef.current?.connect();
40
- return () => managerRef.current?.disconnect();
55
+ manager.retain();
56
+ if (process.env.NODE_ENV !== 'production' && manager.getProviderRefCount() > 1) {
57
+ console.warn('[hotpipe] Multiple <PipeProvider> instances detected. ' +
58
+ 'Use a single provider at the root of your app to avoid redundant connections.');
59
+ }
60
+ return () => manager.release();
41
61
  }, []);
42
- return (_jsx(RealtimeContext.Provider, { value: managerRef.current, children: children }));
62
+ return _jsx(PipeContext.Provider, { value: manager, children: children });
43
63
  }
44
64
  /**
45
- * Subscribe to events on a channel. Returns connection status and a
65
+ * Subscribe to events on a pipe. Returns connection status and a
46
66
  * typed publish function.
47
67
  *
48
68
  * ```tsx
49
- * const { status, publish } = useChannel('general', {
69
+ * const { status, publish } = usePipe('global', {
50
70
  * 'message.created': (data) => addMessage(data),
51
71
  * });
52
72
  * ```
53
73
  */
54
- function useChannel(channel, handlers) {
74
+ function usePipe(pipe, handlers, options) {
55
75
  const manager = useManager();
56
76
  const handlersRef = useRef(handlers);
57
77
  handlersRef.current = handlers;
78
+ const serverMsgRef = useRef(options?.onServerMessage);
79
+ serverMsgRef.current = options?.onServerMessage;
58
80
  const [status, setStatus] = useState(manager.getStatus());
59
81
  useEffect(() => {
60
82
  return manager.onStatusChange(setStatus);
61
83
  }, [manager]);
62
84
  useEffect(() => {
63
- const listener = (event, data) => {
64
- const handler = handlersRef.current[event];
65
- if (handler) {
66
- handler(data);
85
+ if (!serverMsgRef.current)
86
+ return;
87
+ return manager.onServerMessage((msg) => {
88
+ serverMsgRef.current?.(msg);
89
+ });
90
+ }, [manager]);
91
+ useEffect(() => {
92
+ const listener = (event, data, metadata) => {
93
+ const handler = handlersRef.current?.[event];
94
+ if (!handler)
95
+ return;
96
+ const schema = config.events[event];
97
+ if (schema) {
98
+ const result = schema.safeParse(data);
99
+ if (!result.success)
100
+ return;
67
101
  }
102
+ handler(data, metadata);
68
103
  };
69
- manager.addChannelListener(channel, listener);
70
- manager.subscribe(channel);
104
+ manager.addPipeListener(pipe, listener);
105
+ manager.subscribe(pipe);
71
106
  return () => {
72
- manager.unsubscribe(channel);
73
- manager.removeChannelListener(channel, listener);
107
+ manager.unsubscribe(pipe);
108
+ manager.removePipeListener(pipe, listener);
74
109
  };
75
- }, [channel, manager]);
110
+ }, [pipe, manager]);
76
111
  const publish = useCallback((event, data) => {
77
- // Validate against schema before sending
78
112
  const schema = config.events[event];
79
113
  if (schema) {
80
114
  const result = schema.safeParse(data);
81
115
  if (!result.success) {
82
- if (process.env.NODE_ENV !== 'production') {
83
- console.error(`[hotpipe] Invalid data for "${event}":`, result.error.format());
84
- }
85
- return;
116
+ throw new Error(`[hotpipe] Invalid data for "${event}": ${JSON.stringify(result.error.format())}`);
86
117
  }
87
118
  }
88
- manager.publish(channel, event, data);
89
- }, [channel, manager]);
119
+ return manager.publish(pipe, event, data);
120
+ }, [pipe, manager]);
90
121
  return { status, publish };
91
122
  }
92
123
  /**
93
- * Subscribe to a single event type on a channel.
124
+ * Trigger a mid-session token refresh. Call this when a user's permissions
125
+ * change (e.g., they join a new chat room) and the connection should update
126
+ * in place without disconnecting.
94
127
  *
95
- * ```tsx
96
- * useEvent('general', 'message.created', (data) => addMessage(data));
97
- * ```
128
+ * Callable from anywhere — event handlers, server action callbacks, etc.
129
+ * Does not require React context.
98
130
  */
99
- function useEvent(channel, event, handler) {
100
- const manager = useManager();
101
- const handlerRef = useRef(handler);
102
- handlerRef.current = handler;
103
- const [status, setStatus] = useState(manager.getStatus());
104
- useEffect(() => {
105
- return manager.onStatusChange(setStatus);
106
- }, [manager]);
107
- useEffect(() => {
108
- const listener = (evt, data) => {
109
- if (evt === event) {
110
- handlerRef.current(data);
111
- }
112
- };
113
- manager.addChannelListener(channel, listener);
114
- manager.subscribe(channel);
115
- return () => {
116
- manager.unsubscribe(channel);
117
- manager.removeChannelListener(channel, listener);
118
- };
119
- }, [channel, event, manager]);
120
- return { status };
131
+ function refreshPipeAuth() {
132
+ return manager.refreshToken();
121
133
  }
122
- return { RealtimeProvider, useChannel, useEvent };
134
+ return { PipeProvider, usePipe, refreshPipeAuth };
123
135
  }
124
136
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Default JWT token expiry in seconds. Used by createPipeHandler for token
3
+ * generation and by createPipeAdmin for revocation TTL defaults.
4
+ *
5
+ * Must stay in sync with the API-side constant (apps/hotpipe-api/src/constants.ts).
6
+ */
7
+ export declare const DEFAULT_TOKEN_EXPIRY_SECONDS = 3600;
8
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,OAAO,CAAC"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Default JWT token expiry in seconds. Used by createPipeHandler for token
3
+ * generation and by createPipeAdmin for revocation TTL defaults.
4
+ *
5
+ * Must stay in sync with the API-side constant (apps/hotpipe-api/src/constants.ts).
6
+ */
7
+ export const DEFAULT_TOKEN_EXPIRY_SECONDS = 3600;
8
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC"}
@@ -1,71 +1,152 @@
1
1
  import type { z } from 'zod';
2
2
  type EventMap = Record<string, z.ZodType>;
3
+ /**
4
+ * Pipe permissions — which operations a user can perform.
5
+ */
6
+ interface PipePermissions {
7
+ subscribe?: boolean;
8
+ publish?: boolean;
9
+ }
3
10
  /**
4
11
  * The shape returned by your authorize function.
5
12
  */
6
13
  interface AuthResult {
7
14
  userId: string;
8
- channels: Record<string, ('subscribe' | 'publish')[]>;
15
+ pipes: Record<string, PipePermissions>;
9
16
  }
10
17
  /**
11
- * Creates a typed server-side publisher for emitting events to the broker
18
+ * Options for server-side publish calls.
19
+ */
20
+ interface PublishOptions {
21
+ /**
22
+ * An optional sender identity to attach to this event. Receiving clients
23
+ * see this in `metadata.sender`. When omitted, `sender` is `null`,
24
+ * indicating the event originated from the server with no specific user.
25
+ */
26
+ sender?: string;
27
+ }
28
+ /**
29
+ * Creates a typed server-side publisher for emitting events to the hotpipe API
12
30
  * from API routes, server actions, webhooks, cron jobs, etc.
13
31
  *
14
32
  * ```ts
15
- * const realtime = createRealtimePublisher({
16
- * apiKey: process.env.HOTPIPE_API_KEY!,
33
+ * const realtime = createPipePublisher({
34
+ * secret: process.env.HOTPIPE_SECRET!,
17
35
  * events: realtimeEvents,
18
36
  * });
19
37
  *
20
- * await realtime.publish('general', 'message.created', { ... });
38
+ * const teamChat = realtime.pipe('team-chat');
39
+ * await teamChat.publish('message.created', { ... });
21
40
  * ```
22
41
  */
23
- export declare function createRealtimePublisher<T extends EventMap>(config: {
24
- apiKey: string;
42
+ export declare function createPipePublisher<T extends EventMap>(config: {
43
+ secret: string;
25
44
  events: T;
26
- /** Override the broker URL. Defaults to `https://api.hotpipe.dev`. */
27
- brokerUrl?: string;
45
+ /** @internal Override the API URL for local development. */
46
+ apiUrl?: string;
28
47
  }): {
29
48
  /**
30
- * Publish a single event to a channel.
49
+ * Get a reference to a pipe. Returns a `{ publish }` object with
50
+ * the same signature as the client-side `usePipe` publish function.
51
+ *
52
+ * ```ts
53
+ * const teamChat = realtime.pipe('team-chat');
54
+ * await teamChat.publish('message.created', { ... });
55
+ * ```
31
56
  */
32
- publish<E extends keyof T & string>(channel: string, event: E, data: z.infer<T[E]>): Promise<void>;
33
- /**
34
- * Publish multiple events in a single HTTP request.
35
- */
36
- publishBatch(events: Array<{
37
- channel: string;
38
- event: keyof T & string;
39
- data: unknown;
40
- }>): Promise<void>;
57
+ pipe(pipe: string): {
58
+ publish<E extends keyof T & string>(event: E, data: z.infer<T[E]>, options?: PublishOptions): Promise<void>;
59
+ };
41
60
  };
42
61
  /**
43
- * Creates a Next.js-compatible route handler for client auth.
44
- * The client SDK calls this endpoint to get a signed token before
45
- * opening the WebSocket connection to the broker.
62
+ * Creates a Next.js-compatible catch-all route handler for hotpipe.
63
+ * Mount this at `app/api/realtime/[...all]/route.ts` and the SDK
64
+ * handles the rest.
46
65
  *
47
66
  * ```ts
48
- * // app/api/realtime/auth/route.ts
49
- * export const POST = createAuthHandler({
50
- * secret: process.env.BROKER_SIGNING_SECRET!,
67
+ * // app/api/realtime/[...all]/route.ts
68
+ * export const { POST } = createPipeHandler({
69
+ * secret: process.env.HOTPIPE_SECRET!,
51
70
  * authorize: async (req) => {
52
- * const session = await getServerSession();
71
+ * const { session } = await getAuth(req);
53
72
  * if (!session) return null;
54
73
  * return {
55
- * userId: session.user.id,
56
- * channels: {
57
- * general: ['subscribe', 'publish'],
58
- * [`user-${session.user.id}`]: ['subscribe'],
74
+ * userId: session.userId,
75
+ * pipes: {
76
+ * global: { subscribe: true, publish: true },
77
+ * [`user-${session.userId}`]: { subscribe: true },
59
78
  * },
60
79
  * };
61
80
  * },
62
81
  * });
63
82
  * ```
64
83
  */
65
- export declare function createAuthHandler(handlerConfig: {
84
+ export declare function createPipeHandler(handlerConfig: {
66
85
  secret: string;
67
86
  authorize: (req: Request) => Promise<AuthResult | null>;
68
87
  tokenExpiry?: number;
69
- }): (req: Request) => Promise<Response>;
70
- export type { AuthResult, EventMap };
88
+ }): {
89
+ POST: (req: Request) => Promise<Response>;
90
+ };
91
+ /**
92
+ * Options for revocation methods.
93
+ */
94
+ interface RevokeOptions {
95
+ /** TTL in seconds for the revocation entry. Defaults to the API's token expiry. */
96
+ ttl?: number;
97
+ }
98
+ /**
99
+ * A single revocation target for batch operations.
100
+ */
101
+ interface RevocationEntry {
102
+ userId: string;
103
+ pipe: string;
104
+ }
105
+ /**
106
+ * Creates a server-side admin client for revoking pipe access.
107
+ *
108
+ * ```ts
109
+ * const pipeAdmin = createPipeAdmin({
110
+ * secret: process.env.HOTPIPE_SECRET!,
111
+ * });
112
+ *
113
+ * await pipeAdmin.revoke('user-123', 'room-xyz');
114
+ * await pipeAdmin.revokeAll('user-123');
115
+ * ```
116
+ */
117
+ export declare function createPipeAdmin(config: {
118
+ secret: string;
119
+ /** @internal Override the API URL for local development. */
120
+ apiUrl?: string;
121
+ }): {
122
+ /**
123
+ * Revoke access to one or more pipes for a user.
124
+ *
125
+ * ```ts
126
+ * await pipeAdmin.revoke('user-123', 'room-xyz');
127
+ * await pipeAdmin.revoke('user-123', ['room-xyz', 'room-abc']);
128
+ * ```
129
+ */
130
+ revoke(userId: string, pipe: string | string[], options?: RevokeOptions): Promise<void>;
131
+ /**
132
+ * Revoke all pipe access for a user (e.g., account suspension).
133
+ *
134
+ * ```ts
135
+ * await pipeAdmin.revokeAll('user-123');
136
+ * ```
137
+ */
138
+ revokeAll(userId: string, options?: RevokeOptions): Promise<void>;
139
+ /**
140
+ * Batch-revoke access for multiple user/pipe combinations.
141
+ *
142
+ * ```ts
143
+ * await pipeAdmin.revokeBatch([
144
+ * { userId: 'user-123', pipe: 'room-xyz' },
145
+ * { userId: 'user-456', pipe: 'room-xyz' },
146
+ * ]);
147
+ * ```
148
+ */
149
+ revokeBatch(revocations: RevocationEntry[], options?: RevokeOptions): Promise<void>;
150
+ };
151
+ export type { AuthResult, EventMap, PipePermissions, PublishOptions, RevokeOptions };
71
152
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;AAE1C;;GAEG;AACH,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,WAAW,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC;CACvD;AAID;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,CAAC;IACV,sEAAsE;IACtE,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;IASG;;OAEG;YACW,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,WAC7B,MAAM,SACR,CAAC,QACF,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAClB,OAAO,CAAC,IAAI,CAAC;IAkBhB;;OAEG;yBAEO,KAAK,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC,GAAG,MAAM,CAAC;QACxB,IAAI,EAAE,OAAO,CAAC;KACf,CAAC,GACD,OAAO,CAAC,IAAI,CAAC;EAYnB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB,IAIe,KAAK,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAuB/C;AAED,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAM7B,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC;AAE1C;;GAEG;AACH,UAAU,eAAe;IACvB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;GAEG;AACH,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;CACxC;AAED;;GAEG;AACH,UAAU,cAAc;IACtB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAwBD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,QAAQ,EAAE,MAAM,EAAE;IAC9D,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,CAAC,CAAC;IACV,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;IAgDG;;;;;;;;OAQG;eACQ,MAAM;gBAEC,CAAC,SAAS,MAAM,CAAC,GAAG,MAAM,SAC/B,CAAC,QACF,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,YACT,cAAc,GACvB,OAAO,CAAC,IAAI,CAAC;;EAMvB;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,iBAAiB,CAAC,aAAa,EAAE;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;gBAyC6B,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC;EAWxD;AAED;;GAEG;AACH,UAAU,aAAa;IACrB,mFAAmF;IACnF,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,UAAU,eAAe;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;IA2CG;;;;;;;OAOG;mBACkB,MAAM,QAAQ,MAAM,GAAG,MAAM,EAAE,YAAY,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAS7F;;;;;;OAMG;sBACqB,MAAM,YAAY,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE;;;;;;;;;OASG;6BAC4B,eAAe,EAAE,YAAY,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;EAI5F;AAED,YAAY,EAAE,UAAU,EAAE,QAAQ,EAAE,eAAe,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC"}