@bunworks/inngest-realtime 0.1.0 → 0.1.5
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/CHANGELOG.md +6 -6
- package/README.md +1 -1
- package/_virtual/_rolldown/runtime.cjs +29 -0
- package/api.cjs +45 -0
- package/api.cjs.map +1 -0
- package/api.mjs +44 -0
- package/api.mjs.map +1 -0
- package/channel.cjs +67 -0
- package/channel.cjs.map +1 -0
- package/channel.d.cts +19 -0
- package/channel.d.cts.map +1 -0
- package/channel.d.mts +19 -0
- package/channel.d.mts.map +1 -0
- package/channel.mjs +66 -0
- package/channel.mjs.map +1 -0
- package/env.cjs +72 -0
- package/env.cjs.map +1 -0
- package/env.mjs +71 -0
- package/env.mjs.map +1 -0
- package/hooks.cjs +147 -0
- package/hooks.cjs.map +1 -0
- package/hooks.d.cts +70 -0
- package/hooks.d.cts.map +1 -0
- package/hooks.d.mts +70 -0
- package/hooks.d.mts.map +1 -0
- package/hooks.mjs +144 -0
- package/hooks.mjs.map +1 -0
- package/index.cjs +19 -0
- package/index.d.cts +5 -0
- package/index.d.mts +6 -0
- package/index.mjs +7 -0
- package/middleware.cjs +41 -0
- package/middleware.cjs.map +1 -0
- package/middleware.d.cts +29 -0
- package/middleware.d.cts.map +1 -0
- package/middleware.d.mts +29 -0
- package/middleware.d.mts.map +1 -0
- package/middleware.mjs +34 -0
- package/middleware.mjs.map +1 -0
- package/package.json +6 -5
- package/subscribe/StreamFanout.cjs +47 -0
- package/subscribe/StreamFanout.cjs.map +1 -0
- package/subscribe/StreamFanout.mjs +46 -0
- package/subscribe/StreamFanout.mjs.map +1 -0
- package/subscribe/TokenSubscription.cjs +438 -0
- package/subscribe/TokenSubscription.cjs.map +1 -0
- package/subscribe/TokenSubscription.mjs +436 -0
- package/subscribe/TokenSubscription.mjs.map +1 -0
- package/subscribe/helpers.cjs +41 -0
- package/subscribe/helpers.cjs.map +1 -0
- package/subscribe/helpers.d.cts +64 -0
- package/subscribe/helpers.d.cts.map +1 -0
- package/subscribe/helpers.d.mts +64 -0
- package/subscribe/helpers.d.mts.map +1 -0
- package/subscribe/helpers.mjs +40 -0
- package/subscribe/helpers.mjs.map +1 -0
- package/subscribe/index.cjs +1 -0
- package/subscribe/index.d.mts +1 -0
- package/subscribe/index.mjs +3 -0
- package/topic.cjs +30 -0
- package/topic.cjs.map +1 -0
- package/topic.d.cts +19 -0
- package/topic.d.cts.map +1 -0
- package/topic.d.mts +19 -0
- package/topic.d.mts.map +1 -0
- package/topic.mjs +28 -0
- package/topic.mjs.map +1 -0
- package/types.cjs +45 -0
- package/types.cjs.map +1 -0
- package/types.d.cts +241 -0
- package/types.d.cts.map +1 -0
- package/types.d.mts +241 -0
- package/types.d.mts.map +1 -0
- package/types.mjs +39 -0
- package/types.mjs.map +1 -0
- package/util.cjs +72 -0
- package/util.cjs.map +1 -0
- package/util.mjs +69 -0
- package/util.mjs.map +1 -0
- package/CLAUDE.md +0 -69
- package/bun.lock +0 -527
- package/eslint.config.mjs +0 -19
- package/src/api.ts +0 -83
- package/src/channel.ts +0 -122
- package/src/env.d.ts +0 -15
- package/src/env.ts +0 -152
- package/src/hooks.ts +0 -256
- package/src/index.test.ts +0 -840
- package/src/index.ts +0 -4
- package/src/middleware.ts +0 -68
- package/src/subscribe/StreamFanout.ts +0 -82
- package/src/subscribe/TokenSubscription.ts +0 -524
- package/src/subscribe/helpers.ts +0 -153
- package/src/subscribe/index.ts +0 -1
- package/src/topic.ts +0 -51
- package/src/types.ts +0 -499
- package/src/util.ts +0 -104
- package/tsconfig.build.json +0 -7
- package/tsconfig.json +0 -114
- package/tsdown.config.ts +0 -18
- package/vitest.config.ts +0 -22
package/hooks.cjs
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_runtime = require('./_virtual/_rolldown/runtime.cjs');
|
|
3
|
+
const require_helpers = require('./subscribe/helpers.cjs');
|
|
4
|
+
require('./subscribe/index.cjs');
|
|
5
|
+
let react = require("react");
|
|
6
|
+
|
|
7
|
+
//#region src/hooks.ts
|
|
8
|
+
let InngestSubscriptionState = /* @__PURE__ */ function(InngestSubscriptionState) {
|
|
9
|
+
InngestSubscriptionState["Closed"] = "closed";
|
|
10
|
+
InngestSubscriptionState["Error"] = "error";
|
|
11
|
+
InngestSubscriptionState["RefreshingToken"] = "refresh_token";
|
|
12
|
+
InngestSubscriptionState["Connecting"] = "connecting";
|
|
13
|
+
InngestSubscriptionState["Active"] = "active";
|
|
14
|
+
InngestSubscriptionState["Closing"] = "closing";
|
|
15
|
+
return InngestSubscriptionState;
|
|
16
|
+
}({});
|
|
17
|
+
/**
|
|
18
|
+
* React hook for subscribing to Inngest realtime channels
|
|
19
|
+
*/
|
|
20
|
+
function useInngestSubscription({ token: tokenInput, refreshToken, key, enabled = true, bufferInterval = 0 }) {
|
|
21
|
+
const [token, setToken] = (0, react.useState)(tokenInput);
|
|
22
|
+
const [data, setData] = (0, react.useState)([]);
|
|
23
|
+
const [freshData, setFreshData] = (0, react.useState)([]);
|
|
24
|
+
const [error, setError] = (0, react.useState)(null);
|
|
25
|
+
const [state, setState] = (0, react.useState)(InngestSubscriptionState.Closed);
|
|
26
|
+
const subscriptionRef = (0, react.useRef)(null);
|
|
27
|
+
const readerRef = (0, react.useRef)(null);
|
|
28
|
+
const messageBuffer = (0, react.useRef)([]);
|
|
29
|
+
const bufferIntervalRef = (0, react.useRef)(bufferInterval);
|
|
30
|
+
(0, react.useEffect)(() => {
|
|
31
|
+
if (tokenInput) setToken(tokenInput);
|
|
32
|
+
}, [tokenInput]);
|
|
33
|
+
(0, react.useEffect)(() => {
|
|
34
|
+
if (!token && enabled) if (refreshToken) {
|
|
35
|
+
setState(InngestSubscriptionState.RefreshingToken);
|
|
36
|
+
refreshToken().then((newToken) => setToken(newToken)).catch((err) => {
|
|
37
|
+
setError(err);
|
|
38
|
+
setState(InngestSubscriptionState.Error);
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
setError(/* @__PURE__ */ new Error("No token provided and no refreshToken handler."));
|
|
42
|
+
setState(InngestSubscriptionState.Error);
|
|
43
|
+
}
|
|
44
|
+
}, []);
|
|
45
|
+
(0, react.useEffect)(() => {
|
|
46
|
+
setError(null);
|
|
47
|
+
if (!enabled || !token) return;
|
|
48
|
+
let cancelled = false;
|
|
49
|
+
const start = async () => {
|
|
50
|
+
try {
|
|
51
|
+
setState(InngestSubscriptionState.Connecting);
|
|
52
|
+
const stream = await require_helpers.subscribe({ ...token });
|
|
53
|
+
if (cancelled) return;
|
|
54
|
+
subscriptionRef.current = stream;
|
|
55
|
+
setState(InngestSubscriptionState.Active);
|
|
56
|
+
const reader = stream.getReader();
|
|
57
|
+
readerRef.current = reader;
|
|
58
|
+
try {
|
|
59
|
+
while (!cancelled) {
|
|
60
|
+
const { done, value } = await reader.read();
|
|
61
|
+
if (done || cancelled) break;
|
|
62
|
+
if (bufferIntervalRef.current === 0) {
|
|
63
|
+
setFreshData([value]);
|
|
64
|
+
setData((prev) => [...prev, value]);
|
|
65
|
+
} else messageBuffer.current.push(value);
|
|
66
|
+
}
|
|
67
|
+
} finally {
|
|
68
|
+
try {
|
|
69
|
+
reader.releaseLock();
|
|
70
|
+
} catch {}
|
|
71
|
+
readerRef.current = null;
|
|
72
|
+
}
|
|
73
|
+
if (!cancelled) {
|
|
74
|
+
setState(InngestSubscriptionState.Closed);
|
|
75
|
+
if (enabled) start();
|
|
76
|
+
}
|
|
77
|
+
} catch (err) {
|
|
78
|
+
if (cancelled) return;
|
|
79
|
+
if (refreshToken) {
|
|
80
|
+
setState(InngestSubscriptionState.RefreshingToken);
|
|
81
|
+
refreshToken().then((newToken) => setToken(newToken)).catch((e) => {
|
|
82
|
+
setError(e);
|
|
83
|
+
setState(InngestSubscriptionState.Error);
|
|
84
|
+
});
|
|
85
|
+
} else {
|
|
86
|
+
setError(err);
|
|
87
|
+
setState(InngestSubscriptionState.Error);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
start();
|
|
92
|
+
return () => {
|
|
93
|
+
cancelled = true;
|
|
94
|
+
const cleanup = async () => {
|
|
95
|
+
const readerToRemove = readerRef.current;
|
|
96
|
+
const subToRemove = subscriptionRef.current;
|
|
97
|
+
readerRef.current = null;
|
|
98
|
+
subscriptionRef.current = null;
|
|
99
|
+
try {
|
|
100
|
+
await readerToRemove?.cancel();
|
|
101
|
+
} catch {}
|
|
102
|
+
try {
|
|
103
|
+
readerToRemove?.releaseLock();
|
|
104
|
+
} catch {}
|
|
105
|
+
try {
|
|
106
|
+
await subToRemove?.cancel();
|
|
107
|
+
} catch {}
|
|
108
|
+
};
|
|
109
|
+
cleanup().catch((err) => {
|
|
110
|
+
console.error("Error cleaning up Inngest subscription", err);
|
|
111
|
+
}).finally(() => {
|
|
112
|
+
setState(InngestSubscriptionState.Closed);
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
}, [
|
|
116
|
+
token,
|
|
117
|
+
enabled,
|
|
118
|
+
key
|
|
119
|
+
]);
|
|
120
|
+
(0, react.useEffect)(() => {
|
|
121
|
+
bufferIntervalRef.current = bufferInterval;
|
|
122
|
+
let interval = null;
|
|
123
|
+
if (bufferInterval > 0) interval = setInterval(() => {
|
|
124
|
+
if (messageBuffer.current.length > 0) {
|
|
125
|
+
const buffered = [...messageBuffer.current];
|
|
126
|
+
messageBuffer.current = [];
|
|
127
|
+
setFreshData(buffered);
|
|
128
|
+
setData((prev) => [...prev, ...buffered]);
|
|
129
|
+
}
|
|
130
|
+
}, bufferInterval);
|
|
131
|
+
return () => {
|
|
132
|
+
if (interval) clearInterval(interval);
|
|
133
|
+
};
|
|
134
|
+
}, [bufferInterval]);
|
|
135
|
+
return {
|
|
136
|
+
data,
|
|
137
|
+
latestData: data[data.length - 1] ?? null,
|
|
138
|
+
freshData,
|
|
139
|
+
error,
|
|
140
|
+
state
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
//#endregion
|
|
145
|
+
exports.InngestSubscriptionState = InngestSubscriptionState;
|
|
146
|
+
exports.useInngestSubscription = useInngestSubscription;
|
|
147
|
+
//# sourceMappingURL=hooks.cjs.map
|
package/hooks.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.cjs","names":["subscribe"],"sources":["../src/hooks.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { subscribe } from \"./subscribe\";\r\nimport { type Realtime } from \"./types\";\r\n\r\nexport enum InngestSubscriptionState {\r\n Closed = \"closed\",\r\n Error = \"error\",\r\n RefreshingToken = \"refresh_token\",\r\n Connecting = \"connecting\",\r\n Active = \"active\",\r\n Closing = \"closing\",\r\n}\r\n\r\n/**\r\n * Inngest subscription state and data\r\n */\r\nexport interface InngestSubscription<TToken extends Realtime.Subscribe.Token> {\r\n /**\r\n * All received messages\r\n */\r\n data: Realtime.Subscribe.Token.InferMessage<TToken>[];\r\n\r\n /**\r\n * Most recent message\r\n */\r\n latestData: Realtime.Subscribe.Token.InferMessage<TToken> | null;\r\n\r\n /**\r\n * Messages received since last render\r\n */\r\n freshData: Realtime.Subscribe.Token.InferMessage<TToken>[];\r\n\r\n /**\r\n * Connection or subscription error\r\n */\r\n error: Error | null;\r\n\r\n /**\r\n * Current subscription state\r\n */\r\n state: InngestSubscriptionState;\r\n}\r\n\r\n/**\r\n * React hook for subscribing to Inngest realtime channels\r\n */\r\nexport function useInngestSubscription<\r\n const TToken extends Realtime.Subscribe.Token | null | undefined,\r\n>({\r\n token: tokenInput,\r\n refreshToken,\r\n key,\r\n enabled = true,\r\n bufferInterval = 0,\r\n}: {\r\n /**\r\n * Subscription token\r\n */\r\n token?: TToken;\r\n\r\n /**\r\n * Function to refresh token when needed\r\n */\r\n refreshToken?: () => Promise<TToken>;\r\n\r\n /**\r\n * Unique key to force reconnection\r\n */\r\n key?: string;\r\n\r\n /**\r\n * Whether subscription is enabled\r\n */\r\n enabled?: boolean;\r\n\r\n /**\r\n * Interval in ms to buffer messages (0 = no buffering)\r\n */\r\n bufferInterval?: number;\r\n}): InngestSubscription<NonNullable<TToken>> {\r\n const [token, setToken] = useState<TToken | null | undefined>(tokenInput);\r\n const [data, setData] = useState<Realtime.Message[]>([]);\r\n const [freshData, setFreshData] = useState<Realtime.Message[]>([]);\r\n const [error, setError] = useState<Error | null>(null);\r\n const [state, setState] = useState<InngestSubscriptionState>(\r\n InngestSubscriptionState.Closed,\r\n );\r\n\r\n const subscriptionRef = useRef<Realtime.Subscribe.StreamSubscription | null>(\r\n null,\r\n );\r\n const readerRef =\r\n useRef<ReadableStreamDefaultReader<Realtime.Message> | null>(null);\r\n const messageBuffer = useRef<Realtime.Message[]>([]);\r\n const bufferIntervalRef = useRef<number>(bufferInterval);\r\n\r\n // Sync token if tokenInput prop changes\r\n useEffect(() => {\r\n if (tokenInput) setToken(tokenInput);\r\n }, [tokenInput]);\r\n\r\n // Token fetch fallback on mount\r\n useEffect(() => {\r\n if (!token && enabled) {\r\n if (refreshToken) {\r\n setState(InngestSubscriptionState.RefreshingToken);\r\n refreshToken()\r\n .then((newToken) => setToken(newToken))\r\n .catch((err) => {\r\n setError(err);\r\n setState(InngestSubscriptionState.Error);\r\n });\r\n } else {\r\n setError(new Error(\"No token provided and no refreshToken handler.\"));\r\n setState(InngestSubscriptionState.Error);\r\n }\r\n }\r\n }, []);\r\n\r\n // Subscription management\r\n useEffect(() => {\r\n setError(null);\r\n if (!enabled || !token) return;\r\n let cancelled = false;\r\n\r\n const start = async () => {\r\n try {\r\n setState(InngestSubscriptionState.Connecting);\r\n const stream = await subscribe({ ...token });\r\n if (cancelled) return;\r\n\r\n subscriptionRef.current = stream;\r\n setState(InngestSubscriptionState.Active);\r\n\r\n // Explicitly get and manage the reader so that we can manually release\r\n // the lock if anything goes wrong or we're done with it.\r\n //\r\n // Especially when this is unmounted.\r\n const reader = stream.getReader();\r\n readerRef.current = reader;\r\n try {\r\n while (!cancelled) {\r\n const { done, value } = await reader.read();\r\n if (done || cancelled) break;\r\n\r\n if (bufferIntervalRef.current === 0) {\r\n setFreshData([value]);\r\n setData((prev) => [...prev, value]);\r\n } else {\r\n messageBuffer.current.push(value);\r\n }\r\n }\r\n } finally {\r\n try {\r\n reader.releaseLock();\r\n } catch {\r\n // Reader might already be released\r\n }\r\n\r\n readerRef.current = null;\r\n }\r\n\r\n // Stream has closed cleanly\r\n if (!cancelled) {\r\n setState(InngestSubscriptionState.Closed);\r\n if (enabled) start();\r\n }\r\n } catch (err) {\r\n if (cancelled) return;\r\n if (refreshToken) {\r\n setState(InngestSubscriptionState.RefreshingToken);\r\n refreshToken()\r\n .then((newToken) => setToken(newToken))\r\n .catch((e) => {\r\n setError(e);\r\n setState(InngestSubscriptionState.Error);\r\n });\r\n } else {\r\n setError(err as Error);\r\n setState(InngestSubscriptionState.Error);\r\n }\r\n }\r\n };\r\n\r\n start();\r\n\r\n return () => {\r\n cancelled = true;\r\n\r\n const cleanup = async () => {\r\n const readerToRemove = readerRef.current;\r\n const subToRemove = subscriptionRef.current;\r\n\r\n readerRef.current = null;\r\n subscriptionRef.current = null;\r\n\r\n try {\r\n await readerToRemove?.cancel();\r\n } catch {\r\n // Reader might already be cancelled\r\n }\r\n\r\n try {\r\n readerToRemove?.releaseLock();\r\n } catch {\r\n // Reader might already be released\r\n }\r\n\r\n try {\r\n await subToRemove?.cancel();\r\n } catch {\r\n // Subscription might already be cancelled\r\n }\r\n };\r\n\r\n cleanup()\r\n .catch((err) => {\r\n console.error(\"Error cleaning up Inngest subscription\", err);\r\n })\r\n .finally(() => {\r\n // Ensure state is always updated even if cleanup fails\r\n setState(InngestSubscriptionState.Closed);\r\n });\r\n };\r\n }, [token, enabled, key]);\r\n\r\n // Buffer flushing\r\n useEffect(() => {\r\n bufferIntervalRef.current = bufferInterval;\r\n let interval: NodeJS.Timeout | null = null;\r\n\r\n if (bufferInterval > 0) {\r\n interval = setInterval(() => {\r\n if (messageBuffer.current.length > 0) {\r\n const buffered = [...messageBuffer.current];\r\n messageBuffer.current = [];\r\n\r\n setFreshData(buffered);\r\n setData((prev) => [...prev, ...buffered]);\r\n }\r\n }, bufferInterval);\r\n }\r\n\r\n return () => {\r\n if (interval) clearInterval(interval);\r\n };\r\n }, [bufferInterval]);\r\n\r\n return {\r\n data,\r\n latestData: data[data.length - 1] ?? null,\r\n freshData,\r\n error,\r\n state,\r\n } as unknown as InngestSubscription<NonNullable<TToken>>;\r\n}\r\n"],"mappings":";;;;;;;AAIA,IAAY,8EAAL;AACL;AACA;AACA;AACA;AACA;AACA;;;;;;AAoCF,SAAgB,uBAEd,EACA,OAAO,YACP,cACA,KACA,UAAU,MACV,iBAAiB,KA0B0B;CAC3C,MAAM,CAAC,OAAO,gCAAgD,WAAW;CACzE,MAAM,CAAC,MAAM,+BAAwC,EAAE,CAAC;CACxD,MAAM,CAAC,WAAW,oCAA6C,EAAE,CAAC;CAClE,MAAM,CAAC,OAAO,gCAAmC,KAAK;CACtD,MAAM,CAAC,OAAO,gCACZ,yBAAyB,OAC1B;CAED,MAAM,oCACJ,KACD;CACD,MAAM,8BACyD,KAAK;CACpE,MAAM,kCAA2C,EAAE,CAAC;CACpD,MAAM,sCAAmC,eAAe;AAGxD,4BAAgB;AACd,MAAI,WAAY,UAAS,WAAW;IACnC,CAAC,WAAW,CAAC;AAGhB,4BAAgB;AACd,MAAI,CAAC,SAAS,QACZ,KAAI,cAAc;AAChB,YAAS,yBAAyB,gBAAgB;AAClD,iBAAc,CACX,MAAM,aAAa,SAAS,SAAS,CAAC,CACtC,OAAO,QAAQ;AACd,aAAS,IAAI;AACb,aAAS,yBAAyB,MAAM;KACxC;SACC;AACL,4BAAS,IAAI,MAAM,iDAAiD,CAAC;AACrE,YAAS,yBAAyB,MAAM;;IAG3C,EAAE,CAAC;AAGN,4BAAgB;AACd,WAAS,KAAK;AACd,MAAI,CAAC,WAAW,CAAC,MAAO;EACxB,IAAI,YAAY;EAEhB,MAAM,QAAQ,YAAY;AACxB,OAAI;AACF,aAAS,yBAAyB,WAAW;IAC7C,MAAM,SAAS,MAAMA,0BAAU,EAAE,GAAG,OAAO,CAAC;AAC5C,QAAI,UAAW;AAEf,oBAAgB,UAAU;AAC1B,aAAS,yBAAyB,OAAO;IAMzC,MAAM,SAAS,OAAO,WAAW;AACjC,cAAU,UAAU;AACpB,QAAI;AACF,YAAO,CAAC,WAAW;MACjB,MAAM,EAAE,MAAM,UAAU,MAAM,OAAO,MAAM;AAC3C,UAAI,QAAQ,UAAW;AAEvB,UAAI,kBAAkB,YAAY,GAAG;AACnC,oBAAa,CAAC,MAAM,CAAC;AACrB,gBAAS,SAAS,CAAC,GAAG,MAAM,MAAM,CAAC;YAEnC,eAAc,QAAQ,KAAK,MAAM;;cAG7B;AACR,SAAI;AACF,aAAO,aAAa;aACd;AAIR,eAAU,UAAU;;AAItB,QAAI,CAAC,WAAW;AACd,cAAS,yBAAyB,OAAO;AACzC,SAAI,QAAS,QAAO;;YAEf,KAAK;AACZ,QAAI,UAAW;AACf,QAAI,cAAc;AAChB,cAAS,yBAAyB,gBAAgB;AAClD,mBAAc,CACX,MAAM,aAAa,SAAS,SAAS,CAAC,CACtC,OAAO,MAAM;AACZ,eAAS,EAAE;AACX,eAAS,yBAAyB,MAAM;OACxC;WACC;AACL,cAAS,IAAa;AACtB,cAAS,yBAAyB,MAAM;;;;AAK9C,SAAO;AAEP,eAAa;AACX,eAAY;GAEZ,MAAM,UAAU,YAAY;IAC1B,MAAM,iBAAiB,UAAU;IACjC,MAAM,cAAc,gBAAgB;AAEpC,cAAU,UAAU;AACpB,oBAAgB,UAAU;AAE1B,QAAI;AACF,WAAM,gBAAgB,QAAQ;YACxB;AAIR,QAAI;AACF,qBAAgB,aAAa;YACvB;AAIR,QAAI;AACF,WAAM,aAAa,QAAQ;YACrB;;AAKV,YAAS,CACN,OAAO,QAAQ;AACd,YAAQ,MAAM,0CAA0C,IAAI;KAC5D,CACD,cAAc;AAEb,aAAS,yBAAyB,OAAO;KACzC;;IAEL;EAAC;EAAO;EAAS;EAAI,CAAC;AAGzB,4BAAgB;AACd,oBAAkB,UAAU;EAC5B,IAAI,WAAkC;AAEtC,MAAI,iBAAiB,EACnB,YAAW,kBAAkB;AAC3B,OAAI,cAAc,QAAQ,SAAS,GAAG;IACpC,MAAM,WAAW,CAAC,GAAG,cAAc,QAAQ;AAC3C,kBAAc,UAAU,EAAE;AAE1B,iBAAa,SAAS;AACtB,aAAS,SAAS,CAAC,GAAG,MAAM,GAAG,SAAS,CAAC;;KAE1C,eAAe;AAGpB,eAAa;AACX,OAAI,SAAU,eAAc,SAAS;;IAEtC,CAAC,eAAe,CAAC;AAEpB,QAAO;EACL;EACA,YAAY,KAAK,KAAK,SAAS,MAAM;EACrC;EACA;EACA;EACD"}
|
package/hooks.d.cts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Realtime } from "./types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks.d.ts
|
|
4
|
+
declare enum InngestSubscriptionState {
|
|
5
|
+
Closed = "closed",
|
|
6
|
+
Error = "error",
|
|
7
|
+
RefreshingToken = "refresh_token",
|
|
8
|
+
Connecting = "connecting",
|
|
9
|
+
Active = "active",
|
|
10
|
+
Closing = "closing"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Inngest subscription state and data
|
|
14
|
+
*/
|
|
15
|
+
interface InngestSubscription<TToken extends Realtime.Subscribe.Token> {
|
|
16
|
+
/**
|
|
17
|
+
* All received messages
|
|
18
|
+
*/
|
|
19
|
+
data: Realtime.Subscribe.Token.InferMessage<TToken>[];
|
|
20
|
+
/**
|
|
21
|
+
* Most recent message
|
|
22
|
+
*/
|
|
23
|
+
latestData: Realtime.Subscribe.Token.InferMessage<TToken> | null;
|
|
24
|
+
/**
|
|
25
|
+
* Messages received since last render
|
|
26
|
+
*/
|
|
27
|
+
freshData: Realtime.Subscribe.Token.InferMessage<TToken>[];
|
|
28
|
+
/**
|
|
29
|
+
* Connection or subscription error
|
|
30
|
+
*/
|
|
31
|
+
error: Error | null;
|
|
32
|
+
/**
|
|
33
|
+
* Current subscription state
|
|
34
|
+
*/
|
|
35
|
+
state: InngestSubscriptionState;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* React hook for subscribing to Inngest realtime channels
|
|
39
|
+
*/
|
|
40
|
+
declare function useInngestSubscription<const TToken extends Realtime.Subscribe.Token | null | undefined>({
|
|
41
|
+
token: tokenInput,
|
|
42
|
+
refreshToken,
|
|
43
|
+
key,
|
|
44
|
+
enabled,
|
|
45
|
+
bufferInterval
|
|
46
|
+
}: {
|
|
47
|
+
/**
|
|
48
|
+
* Subscription token
|
|
49
|
+
*/
|
|
50
|
+
token?: TToken;
|
|
51
|
+
/**
|
|
52
|
+
* Function to refresh token when needed
|
|
53
|
+
*/
|
|
54
|
+
refreshToken?: () => Promise<TToken>;
|
|
55
|
+
/**
|
|
56
|
+
* Unique key to force reconnection
|
|
57
|
+
*/
|
|
58
|
+
key?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Whether subscription is enabled
|
|
61
|
+
*/
|
|
62
|
+
enabled?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Interval in ms to buffer messages (0 = no buffering)
|
|
65
|
+
*/
|
|
66
|
+
bufferInterval?: number;
|
|
67
|
+
}): InngestSubscription<NonNullable<TToken>>;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { InngestSubscription, InngestSubscriptionState, useInngestSubscription };
|
|
70
|
+
//# sourceMappingURL=hooks.d.cts.map
|
package/hooks.d.cts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.cts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;aAIY,wBAAA;EACV,MAAA;EACA,KAAA;EACA,eAAA;EACA,UAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;UAMe,mBAAA,gBAAmC,QAAA,CAAS,SAAA,CAAU,KAAA;EAN9D;;AAMT;EAIE,IAAA,EAAM,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,YAAA,CAAa,MAAA;EAJV;;;EASlC,UAAA,EAAY,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,YAAA,CAAa,MAAA;EAAA;;;EAKlD,SAAA,EAAW,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,YAAA,CAAa,MAAA;EAK1C;;;EAAP,KAAA,EAAO,KAAA;EAnB4B;;;EAwBnC,KAAA,EAAO,wBAAA;AAAA;;;;iBAMO,sBAAA,sBACO,QAAA,CAAS,SAAA,CAAU,KAAA,oBAAA,CAAA;EAExC,KAAA,EAAO,UAAA;EACP,YAAA;EACA,GAAA;EACA,OAAA;EACA;AAAA;EAjC4C;;;EAsC5C,KAAA,GAAQ,MAAA;EAjCuB;;;EAsC/B,YAAA,SAAqB,OAAA,CAAQ,MAAA;EAjClB;;;EAsCX,GAAA;EAtCiD;;;EA2CjD,OAAA;EAjCO;;;EAsCP,cAAA;AAAA,IACE,mBAAA,CAAoB,WAAA,CAAY,MAAA"}
|
package/hooks.d.mts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { Realtime } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/hooks.d.ts
|
|
4
|
+
declare enum InngestSubscriptionState {
|
|
5
|
+
Closed = "closed",
|
|
6
|
+
Error = "error",
|
|
7
|
+
RefreshingToken = "refresh_token",
|
|
8
|
+
Connecting = "connecting",
|
|
9
|
+
Active = "active",
|
|
10
|
+
Closing = "closing"
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Inngest subscription state and data
|
|
14
|
+
*/
|
|
15
|
+
interface InngestSubscription<TToken extends Realtime.Subscribe.Token> {
|
|
16
|
+
/**
|
|
17
|
+
* All received messages
|
|
18
|
+
*/
|
|
19
|
+
data: Realtime.Subscribe.Token.InferMessage<TToken>[];
|
|
20
|
+
/**
|
|
21
|
+
* Most recent message
|
|
22
|
+
*/
|
|
23
|
+
latestData: Realtime.Subscribe.Token.InferMessage<TToken> | null;
|
|
24
|
+
/**
|
|
25
|
+
* Messages received since last render
|
|
26
|
+
*/
|
|
27
|
+
freshData: Realtime.Subscribe.Token.InferMessage<TToken>[];
|
|
28
|
+
/**
|
|
29
|
+
* Connection or subscription error
|
|
30
|
+
*/
|
|
31
|
+
error: Error | null;
|
|
32
|
+
/**
|
|
33
|
+
* Current subscription state
|
|
34
|
+
*/
|
|
35
|
+
state: InngestSubscriptionState;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* React hook for subscribing to Inngest realtime channels
|
|
39
|
+
*/
|
|
40
|
+
declare function useInngestSubscription<const TToken extends Realtime.Subscribe.Token | null | undefined>({
|
|
41
|
+
token: tokenInput,
|
|
42
|
+
refreshToken,
|
|
43
|
+
key,
|
|
44
|
+
enabled,
|
|
45
|
+
bufferInterval
|
|
46
|
+
}: {
|
|
47
|
+
/**
|
|
48
|
+
* Subscription token
|
|
49
|
+
*/
|
|
50
|
+
token?: TToken;
|
|
51
|
+
/**
|
|
52
|
+
* Function to refresh token when needed
|
|
53
|
+
*/
|
|
54
|
+
refreshToken?: () => Promise<TToken>;
|
|
55
|
+
/**
|
|
56
|
+
* Unique key to force reconnection
|
|
57
|
+
*/
|
|
58
|
+
key?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Whether subscription is enabled
|
|
61
|
+
*/
|
|
62
|
+
enabled?: boolean;
|
|
63
|
+
/**
|
|
64
|
+
* Interval in ms to buffer messages (0 = no buffering)
|
|
65
|
+
*/
|
|
66
|
+
bufferInterval?: number;
|
|
67
|
+
}): InngestSubscription<NonNullable<TToken>>;
|
|
68
|
+
//#endregion
|
|
69
|
+
export { InngestSubscription, InngestSubscriptionState, useInngestSubscription };
|
|
70
|
+
//# sourceMappingURL=hooks.d.mts.map
|
package/hooks.d.mts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.d.mts","names":[],"sources":["../src/hooks.ts"],"mappings":";;;aAIY,wBAAA;EACV,MAAA;EACA,KAAA;EACA,eAAA;EACA,UAAA;EACA,MAAA;EACA,OAAA;AAAA;;;;UAMe,mBAAA,gBAAmC,QAAA,CAAS,SAAA,CAAU,KAAA;EAN9D;;AAMT;EAIE,IAAA,EAAM,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,YAAA,CAAa,MAAA;EAJV;;;EASlC,UAAA,EAAY,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,YAAA,CAAa,MAAA;EAAA;;;EAKlD,SAAA,EAAW,QAAA,CAAS,SAAA,CAAU,KAAA,CAAM,YAAA,CAAa,MAAA;EAK1C;;;EAAP,KAAA,EAAO,KAAA;EAnB4B;;;EAwBnC,KAAA,EAAO,wBAAA;AAAA;;;;iBAMO,sBAAA,sBACO,QAAA,CAAS,SAAA,CAAU,KAAA,oBAAA,CAAA;EAExC,KAAA,EAAO,UAAA;EACP,YAAA;EACA,GAAA;EACA,OAAA;EACA;AAAA;EAjC4C;;;EAsC5C,KAAA,GAAQ,MAAA;EAjCuB;;;EAsC/B,YAAA,SAAqB,OAAA,CAAQ,MAAA;EAjClB;;;EAsCX,GAAA;EAtCiD;;;EA2CjD,OAAA;EAjCO;;;EAsCP,cAAA;AAAA,IACE,mBAAA,CAAoB,WAAA,CAAY,MAAA"}
|
package/hooks.mjs
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { subscribe } from "./subscribe/helpers.mjs";
|
|
2
|
+
import "./subscribe/index.mjs";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
4
|
+
|
|
5
|
+
//#region src/hooks.ts
|
|
6
|
+
let InngestSubscriptionState = /* @__PURE__ */ function(InngestSubscriptionState) {
|
|
7
|
+
InngestSubscriptionState["Closed"] = "closed";
|
|
8
|
+
InngestSubscriptionState["Error"] = "error";
|
|
9
|
+
InngestSubscriptionState["RefreshingToken"] = "refresh_token";
|
|
10
|
+
InngestSubscriptionState["Connecting"] = "connecting";
|
|
11
|
+
InngestSubscriptionState["Active"] = "active";
|
|
12
|
+
InngestSubscriptionState["Closing"] = "closing";
|
|
13
|
+
return InngestSubscriptionState;
|
|
14
|
+
}({});
|
|
15
|
+
/**
|
|
16
|
+
* React hook for subscribing to Inngest realtime channels
|
|
17
|
+
*/
|
|
18
|
+
function useInngestSubscription({ token: tokenInput, refreshToken, key, enabled = true, bufferInterval = 0 }) {
|
|
19
|
+
const [token, setToken] = useState(tokenInput);
|
|
20
|
+
const [data, setData] = useState([]);
|
|
21
|
+
const [freshData, setFreshData] = useState([]);
|
|
22
|
+
const [error, setError] = useState(null);
|
|
23
|
+
const [state, setState] = useState(InngestSubscriptionState.Closed);
|
|
24
|
+
const subscriptionRef = useRef(null);
|
|
25
|
+
const readerRef = useRef(null);
|
|
26
|
+
const messageBuffer = useRef([]);
|
|
27
|
+
const bufferIntervalRef = useRef(bufferInterval);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (tokenInput) setToken(tokenInput);
|
|
30
|
+
}, [tokenInput]);
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!token && enabled) if (refreshToken) {
|
|
33
|
+
setState(InngestSubscriptionState.RefreshingToken);
|
|
34
|
+
refreshToken().then((newToken) => setToken(newToken)).catch((err) => {
|
|
35
|
+
setError(err);
|
|
36
|
+
setState(InngestSubscriptionState.Error);
|
|
37
|
+
});
|
|
38
|
+
} else {
|
|
39
|
+
setError(/* @__PURE__ */ new Error("No token provided and no refreshToken handler."));
|
|
40
|
+
setState(InngestSubscriptionState.Error);
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
setError(null);
|
|
45
|
+
if (!enabled || !token) return;
|
|
46
|
+
let cancelled = false;
|
|
47
|
+
const start = async () => {
|
|
48
|
+
try {
|
|
49
|
+
setState(InngestSubscriptionState.Connecting);
|
|
50
|
+
const stream = await subscribe({ ...token });
|
|
51
|
+
if (cancelled) return;
|
|
52
|
+
subscriptionRef.current = stream;
|
|
53
|
+
setState(InngestSubscriptionState.Active);
|
|
54
|
+
const reader = stream.getReader();
|
|
55
|
+
readerRef.current = reader;
|
|
56
|
+
try {
|
|
57
|
+
while (!cancelled) {
|
|
58
|
+
const { done, value } = await reader.read();
|
|
59
|
+
if (done || cancelled) break;
|
|
60
|
+
if (bufferIntervalRef.current === 0) {
|
|
61
|
+
setFreshData([value]);
|
|
62
|
+
setData((prev) => [...prev, value]);
|
|
63
|
+
} else messageBuffer.current.push(value);
|
|
64
|
+
}
|
|
65
|
+
} finally {
|
|
66
|
+
try {
|
|
67
|
+
reader.releaseLock();
|
|
68
|
+
} catch {}
|
|
69
|
+
readerRef.current = null;
|
|
70
|
+
}
|
|
71
|
+
if (!cancelled) {
|
|
72
|
+
setState(InngestSubscriptionState.Closed);
|
|
73
|
+
if (enabled) start();
|
|
74
|
+
}
|
|
75
|
+
} catch (err) {
|
|
76
|
+
if (cancelled) return;
|
|
77
|
+
if (refreshToken) {
|
|
78
|
+
setState(InngestSubscriptionState.RefreshingToken);
|
|
79
|
+
refreshToken().then((newToken) => setToken(newToken)).catch((e) => {
|
|
80
|
+
setError(e);
|
|
81
|
+
setState(InngestSubscriptionState.Error);
|
|
82
|
+
});
|
|
83
|
+
} else {
|
|
84
|
+
setError(err);
|
|
85
|
+
setState(InngestSubscriptionState.Error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
start();
|
|
90
|
+
return () => {
|
|
91
|
+
cancelled = true;
|
|
92
|
+
const cleanup = async () => {
|
|
93
|
+
const readerToRemove = readerRef.current;
|
|
94
|
+
const subToRemove = subscriptionRef.current;
|
|
95
|
+
readerRef.current = null;
|
|
96
|
+
subscriptionRef.current = null;
|
|
97
|
+
try {
|
|
98
|
+
await readerToRemove?.cancel();
|
|
99
|
+
} catch {}
|
|
100
|
+
try {
|
|
101
|
+
readerToRemove?.releaseLock();
|
|
102
|
+
} catch {}
|
|
103
|
+
try {
|
|
104
|
+
await subToRemove?.cancel();
|
|
105
|
+
} catch {}
|
|
106
|
+
};
|
|
107
|
+
cleanup().catch((err) => {
|
|
108
|
+
console.error("Error cleaning up Inngest subscription", err);
|
|
109
|
+
}).finally(() => {
|
|
110
|
+
setState(InngestSubscriptionState.Closed);
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
}, [
|
|
114
|
+
token,
|
|
115
|
+
enabled,
|
|
116
|
+
key
|
|
117
|
+
]);
|
|
118
|
+
useEffect(() => {
|
|
119
|
+
bufferIntervalRef.current = bufferInterval;
|
|
120
|
+
let interval = null;
|
|
121
|
+
if (bufferInterval > 0) interval = setInterval(() => {
|
|
122
|
+
if (messageBuffer.current.length > 0) {
|
|
123
|
+
const buffered = [...messageBuffer.current];
|
|
124
|
+
messageBuffer.current = [];
|
|
125
|
+
setFreshData(buffered);
|
|
126
|
+
setData((prev) => [...prev, ...buffered]);
|
|
127
|
+
}
|
|
128
|
+
}, bufferInterval);
|
|
129
|
+
return () => {
|
|
130
|
+
if (interval) clearInterval(interval);
|
|
131
|
+
};
|
|
132
|
+
}, [bufferInterval]);
|
|
133
|
+
return {
|
|
134
|
+
data,
|
|
135
|
+
latestData: data[data.length - 1] ?? null,
|
|
136
|
+
freshData,
|
|
137
|
+
error,
|
|
138
|
+
state
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
export { InngestSubscriptionState, useInngestSubscription };
|
|
144
|
+
//# sourceMappingURL=hooks.mjs.map
|
package/hooks.mjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.mjs","names":[],"sources":["../src/hooks.ts"],"sourcesContent":["import { useEffect, useRef, useState } from \"react\";\r\nimport { subscribe } from \"./subscribe\";\r\nimport { type Realtime } from \"./types\";\r\n\r\nexport enum InngestSubscriptionState {\r\n Closed = \"closed\",\r\n Error = \"error\",\r\n RefreshingToken = \"refresh_token\",\r\n Connecting = \"connecting\",\r\n Active = \"active\",\r\n Closing = \"closing\",\r\n}\r\n\r\n/**\r\n * Inngest subscription state and data\r\n */\r\nexport interface InngestSubscription<TToken extends Realtime.Subscribe.Token> {\r\n /**\r\n * All received messages\r\n */\r\n data: Realtime.Subscribe.Token.InferMessage<TToken>[];\r\n\r\n /**\r\n * Most recent message\r\n */\r\n latestData: Realtime.Subscribe.Token.InferMessage<TToken> | null;\r\n\r\n /**\r\n * Messages received since last render\r\n */\r\n freshData: Realtime.Subscribe.Token.InferMessage<TToken>[];\r\n\r\n /**\r\n * Connection or subscription error\r\n */\r\n error: Error | null;\r\n\r\n /**\r\n * Current subscription state\r\n */\r\n state: InngestSubscriptionState;\r\n}\r\n\r\n/**\r\n * React hook for subscribing to Inngest realtime channels\r\n */\r\nexport function useInngestSubscription<\r\n const TToken extends Realtime.Subscribe.Token | null | undefined,\r\n>({\r\n token: tokenInput,\r\n refreshToken,\r\n key,\r\n enabled = true,\r\n bufferInterval = 0,\r\n}: {\r\n /**\r\n * Subscription token\r\n */\r\n token?: TToken;\r\n\r\n /**\r\n * Function to refresh token when needed\r\n */\r\n refreshToken?: () => Promise<TToken>;\r\n\r\n /**\r\n * Unique key to force reconnection\r\n */\r\n key?: string;\r\n\r\n /**\r\n * Whether subscription is enabled\r\n */\r\n enabled?: boolean;\r\n\r\n /**\r\n * Interval in ms to buffer messages (0 = no buffering)\r\n */\r\n bufferInterval?: number;\r\n}): InngestSubscription<NonNullable<TToken>> {\r\n const [token, setToken] = useState<TToken | null | undefined>(tokenInput);\r\n const [data, setData] = useState<Realtime.Message[]>([]);\r\n const [freshData, setFreshData] = useState<Realtime.Message[]>([]);\r\n const [error, setError] = useState<Error | null>(null);\r\n const [state, setState] = useState<InngestSubscriptionState>(\r\n InngestSubscriptionState.Closed,\r\n );\r\n\r\n const subscriptionRef = useRef<Realtime.Subscribe.StreamSubscription | null>(\r\n null,\r\n );\r\n const readerRef =\r\n useRef<ReadableStreamDefaultReader<Realtime.Message> | null>(null);\r\n const messageBuffer = useRef<Realtime.Message[]>([]);\r\n const bufferIntervalRef = useRef<number>(bufferInterval);\r\n\r\n // Sync token if tokenInput prop changes\r\n useEffect(() => {\r\n if (tokenInput) setToken(tokenInput);\r\n }, [tokenInput]);\r\n\r\n // Token fetch fallback on mount\r\n useEffect(() => {\r\n if (!token && enabled) {\r\n if (refreshToken) {\r\n setState(InngestSubscriptionState.RefreshingToken);\r\n refreshToken()\r\n .then((newToken) => setToken(newToken))\r\n .catch((err) => {\r\n setError(err);\r\n setState(InngestSubscriptionState.Error);\r\n });\r\n } else {\r\n setError(new Error(\"No token provided and no refreshToken handler.\"));\r\n setState(InngestSubscriptionState.Error);\r\n }\r\n }\r\n }, []);\r\n\r\n // Subscription management\r\n useEffect(() => {\r\n setError(null);\r\n if (!enabled || !token) return;\r\n let cancelled = false;\r\n\r\n const start = async () => {\r\n try {\r\n setState(InngestSubscriptionState.Connecting);\r\n const stream = await subscribe({ ...token });\r\n if (cancelled) return;\r\n\r\n subscriptionRef.current = stream;\r\n setState(InngestSubscriptionState.Active);\r\n\r\n // Explicitly get and manage the reader so that we can manually release\r\n // the lock if anything goes wrong or we're done with it.\r\n //\r\n // Especially when this is unmounted.\r\n const reader = stream.getReader();\r\n readerRef.current = reader;\r\n try {\r\n while (!cancelled) {\r\n const { done, value } = await reader.read();\r\n if (done || cancelled) break;\r\n\r\n if (bufferIntervalRef.current === 0) {\r\n setFreshData([value]);\r\n setData((prev) => [...prev, value]);\r\n } else {\r\n messageBuffer.current.push(value);\r\n }\r\n }\r\n } finally {\r\n try {\r\n reader.releaseLock();\r\n } catch {\r\n // Reader might already be released\r\n }\r\n\r\n readerRef.current = null;\r\n }\r\n\r\n // Stream has closed cleanly\r\n if (!cancelled) {\r\n setState(InngestSubscriptionState.Closed);\r\n if (enabled) start();\r\n }\r\n } catch (err) {\r\n if (cancelled) return;\r\n if (refreshToken) {\r\n setState(InngestSubscriptionState.RefreshingToken);\r\n refreshToken()\r\n .then((newToken) => setToken(newToken))\r\n .catch((e) => {\r\n setError(e);\r\n setState(InngestSubscriptionState.Error);\r\n });\r\n } else {\r\n setError(err as Error);\r\n setState(InngestSubscriptionState.Error);\r\n }\r\n }\r\n };\r\n\r\n start();\r\n\r\n return () => {\r\n cancelled = true;\r\n\r\n const cleanup = async () => {\r\n const readerToRemove = readerRef.current;\r\n const subToRemove = subscriptionRef.current;\r\n\r\n readerRef.current = null;\r\n subscriptionRef.current = null;\r\n\r\n try {\r\n await readerToRemove?.cancel();\r\n } catch {\r\n // Reader might already be cancelled\r\n }\r\n\r\n try {\r\n readerToRemove?.releaseLock();\r\n } catch {\r\n // Reader might already be released\r\n }\r\n\r\n try {\r\n await subToRemove?.cancel();\r\n } catch {\r\n // Subscription might already be cancelled\r\n }\r\n };\r\n\r\n cleanup()\r\n .catch((err) => {\r\n console.error(\"Error cleaning up Inngest subscription\", err);\r\n })\r\n .finally(() => {\r\n // Ensure state is always updated even if cleanup fails\r\n setState(InngestSubscriptionState.Closed);\r\n });\r\n };\r\n }, [token, enabled, key]);\r\n\r\n // Buffer flushing\r\n useEffect(() => {\r\n bufferIntervalRef.current = bufferInterval;\r\n let interval: NodeJS.Timeout | null = null;\r\n\r\n if (bufferInterval > 0) {\r\n interval = setInterval(() => {\r\n if (messageBuffer.current.length > 0) {\r\n const buffered = [...messageBuffer.current];\r\n messageBuffer.current = [];\r\n\r\n setFreshData(buffered);\r\n setData((prev) => [...prev, ...buffered]);\r\n }\r\n }, bufferInterval);\r\n }\r\n\r\n return () => {\r\n if (interval) clearInterval(interval);\r\n };\r\n }, [bufferInterval]);\r\n\r\n return {\r\n data,\r\n latestData: data[data.length - 1] ?? null,\r\n freshData,\r\n error,\r\n state,\r\n } as unknown as InngestSubscription<NonNullable<TToken>>;\r\n}\r\n"],"mappings":"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"}
|
package/index.cjs
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_topic = require('./topic.cjs');
|
|
3
|
+
const require_channel = require('./channel.cjs');
|
|
4
|
+
const require_types = require('./types.cjs');
|
|
5
|
+
const require_helpers = require('./subscribe/helpers.cjs');
|
|
6
|
+
require('./subscribe/index.cjs');
|
|
7
|
+
|
|
8
|
+
Object.defineProperty(exports, 'Realtime', {
|
|
9
|
+
enumerable: true,
|
|
10
|
+
get: function () {
|
|
11
|
+
return require_types.Realtime;
|
|
12
|
+
}
|
|
13
|
+
});
|
|
14
|
+
exports.TopicDefinitionImpl = require_topic.TopicDefinitionImpl;
|
|
15
|
+
exports.channel = require_channel.channel;
|
|
16
|
+
exports.getSubscriptionToken = require_helpers.getSubscriptionToken;
|
|
17
|
+
exports.subscribe = require_helpers.subscribe;
|
|
18
|
+
exports.topic = require_topic.topic;
|
|
19
|
+
exports.typeOnlyChannel = require_channel.typeOnlyChannel;
|
package/index.d.cts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Expect, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify } from "./types.cjs";
|
|
2
|
+
import { channel, typeOnlyChannel } from "./channel.cjs";
|
|
3
|
+
import { InngestApp, getSubscriptionToken, subscribe } from "./subscribe/helpers.cjs";
|
|
4
|
+
import { TopicDefinitionImpl, topic } from "./topic.cjs";
|
|
5
|
+
export { Expect, InngestApp, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify, TopicDefinitionImpl, channel, getSubscriptionToken, subscribe, topic, typeOnlyChannel };
|
package/index.d.mts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Expect, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify } from "./types.mjs";
|
|
2
|
+
import { channel, typeOnlyChannel } from "./channel.mjs";
|
|
3
|
+
import { InngestApp, getSubscriptionToken, subscribe } from "./subscribe/helpers.mjs";
|
|
4
|
+
import "./subscribe/index.mjs";
|
|
5
|
+
import { TopicDefinitionImpl, topic } from "./topic.mjs";
|
|
6
|
+
export { Expect, InngestApp, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify, TopicDefinitionImpl, channel, getSubscriptionToken, subscribe, topic, typeOnlyChannel };
|
package/index.mjs
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { TopicDefinitionImpl, topic } from "./topic.mjs";
|
|
2
|
+
import { channel, typeOnlyChannel } from "./channel.mjs";
|
|
3
|
+
import { Realtime } from "./types.mjs";
|
|
4
|
+
import { getSubscriptionToken, subscribe } from "./subscribe/helpers.mjs";
|
|
5
|
+
import "./subscribe/index.mjs";
|
|
6
|
+
|
|
7
|
+
export { Realtime, TopicDefinitionImpl, channel, getSubscriptionToken, subscribe, topic, typeOnlyChannel };
|
package/middleware.cjs
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_types = require('./types.cjs');
|
|
3
|
+
|
|
4
|
+
//#region src/middleware.ts
|
|
5
|
+
const realtimeMiddleware = () => {
|
|
6
|
+
return {
|
|
7
|
+
name: "publish",
|
|
8
|
+
init({ client }) {
|
|
9
|
+
return { onFunctionRun({ ctx: { runId } }) {
|
|
10
|
+
return { transformInput({ ctx: { step } }) {
|
|
11
|
+
const publish = async (input) => {
|
|
12
|
+
const { topic, channel, data } = await input;
|
|
13
|
+
const publishOpts = {
|
|
14
|
+
topics: [topic],
|
|
15
|
+
channel,
|
|
16
|
+
runId
|
|
17
|
+
};
|
|
18
|
+
const action = async () => {
|
|
19
|
+
const result = await client["api"].publish(publishOpts, data);
|
|
20
|
+
if (!result.ok) throw new Error(`Failed to publish event: ${result.error?.error}`);
|
|
21
|
+
};
|
|
22
|
+
return step.run(`publish:${publishOpts.channel}`, action).then(() => {
|
|
23
|
+
return data;
|
|
24
|
+
});
|
|
25
|
+
};
|
|
26
|
+
return { ctx: { publish } };
|
|
27
|
+
} };
|
|
28
|
+
} };
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
//#endregion
|
|
34
|
+
Object.defineProperty(exports, 'Realtime', {
|
|
35
|
+
enumerable: true,
|
|
36
|
+
get: function () {
|
|
37
|
+
return require_types.Realtime;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
exports.realtimeMiddleware = realtimeMiddleware;
|
|
41
|
+
//# sourceMappingURL=middleware.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.cjs","names":[],"sources":["../src/middleware.ts"],"sourcesContent":["import type { Realtime } from \"./types\";\r\n\r\nexport interface InngestMiddleware {\r\n name: string;\r\n init: (config: { client: any }) => {\r\n onFunctionRun: (config: { ctx: { runId: string } }) => {\r\n transformInput: (config: { ctx: { step: any } }) => {\r\n ctx: {\r\n publish: Realtime.PublishFn;\r\n };\r\n };\r\n };\r\n };\r\n}\r\n\r\nexport const realtimeMiddleware = () => {\r\n return {\r\n name: \"publish\",\r\n init({ client }: { client: any }) {\r\n return {\r\n onFunctionRun({ ctx: { runId } }: { ctx: { runId: string } }) {\r\n return {\r\n transformInput({ ctx: { step } }: { ctx: { step: any } }) {\r\n const publish: Realtime.PublishFn = async (input) => {\r\n const { topic, channel, data } = await input;\r\n\r\n const publishOpts = {\r\n topics: [topic],\r\n channel,\r\n runId,\r\n };\r\n\r\n const action = async () => {\r\n const result = await client[\"api\"].publish(publishOpts, data);\r\n\r\n if (!result.ok) {\r\n throw new Error(\r\n `Failed to publish event: ${result.error?.error}`,\r\n );\r\n }\r\n };\r\n\r\n return step\r\n .run(`publish:${publishOpts.channel}`, action)\r\n .then(() => {\r\n return data;\r\n });\r\n };\r\n\r\n return {\r\n ctx: {\r\n /**\r\n * Function to publish messages to realtime channel\r\n */\r\n publish,\r\n },\r\n };\r\n },\r\n };\r\n },\r\n };\r\n },\r\n } as InngestMiddleware;\r\n};\r\n\r\n// Re-export types from here, as this is used as a separate entrypoint now\r\nexport * from \"./types\";\r\n"],"mappings":";;;;AAeA,MAAa,2BAA2B;AACtC,QAAO;EACL,MAAM;EACN,KAAK,EAAE,UAA2B;AAChC,UAAO,EACL,cAAc,EAAE,KAAK,EAAE,WAAuC;AAC5D,WAAO,EACL,eAAe,EAAE,KAAK,EAAE,UAAkC;KACxD,MAAM,UAA8B,OAAO,UAAU;MACnD,MAAM,EAAE,OAAO,SAAS,SAAS,MAAM;MAEvC,MAAM,cAAc;OAClB,QAAQ,CAAC,MAAM;OACf;OACA;OACD;MAED,MAAM,SAAS,YAAY;OACzB,MAAM,SAAS,MAAM,OAAO,OAAO,QAAQ,aAAa,KAAK;AAE7D,WAAI,CAAC,OAAO,GACV,OAAM,IAAI,MACR,4BAA4B,OAAO,OAAO,QAC3C;;AAIL,aAAO,KACJ,IAAI,WAAW,YAAY,WAAW,OAAO,CAC7C,WAAW;AACV,cAAO;QACP;;AAGN,YAAO,EACL,KAAK,EAIH,SACD,EACF;OAEJ;MAEJ;;EAEJ"}
|
package/middleware.d.cts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Expect, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify } from "./types.cjs";
|
|
2
|
+
|
|
3
|
+
//#region src/middleware.d.ts
|
|
4
|
+
interface InngestMiddleware {
|
|
5
|
+
name: string;
|
|
6
|
+
init: (config: {
|
|
7
|
+
client: any;
|
|
8
|
+
}) => {
|
|
9
|
+
onFunctionRun: (config: {
|
|
10
|
+
ctx: {
|
|
11
|
+
runId: string;
|
|
12
|
+
};
|
|
13
|
+
}) => {
|
|
14
|
+
transformInput: (config: {
|
|
15
|
+
ctx: {
|
|
16
|
+
step: any;
|
|
17
|
+
};
|
|
18
|
+
}) => {
|
|
19
|
+
ctx: {
|
|
20
|
+
publish: Realtime.PublishFn;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
declare const realtimeMiddleware: () => InngestMiddleware;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Expect, InngestMiddleware, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify, realtimeMiddleware };
|
|
29
|
+
//# sourceMappingURL=middleware.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.cts","names":[],"sources":["../src/middleware.ts"],"mappings":";;;UAEiB,iBAAA;EACf,IAAA;EACA,IAAA,GAAO,MAAA;IAAU,MAAA;EAAA;IACf,aAAA,GAAgB,MAAA;MAAU,GAAA;QAAO,KAAA;MAAA;IAAA;MAC/B,cAAA,GAAiB,MAAA;QAAU,GAAA;UAAO,IAAA;QAAA;MAAA;QAChC,GAAA;UACE,OAAA,EAAS,QAAA,CAAS,SAAA;QAAA;MAAA;IAAA;EAAA;AAAA;AAAA,cAOf,kBAAA,QA+CN,iBAAA"}
|
package/middleware.d.mts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Expect, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify } from "./types.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/middleware.d.ts
|
|
4
|
+
interface InngestMiddleware {
|
|
5
|
+
name: string;
|
|
6
|
+
init: (config: {
|
|
7
|
+
client: any;
|
|
8
|
+
}) => {
|
|
9
|
+
onFunctionRun: (config: {
|
|
10
|
+
ctx: {
|
|
11
|
+
runId: string;
|
|
12
|
+
};
|
|
13
|
+
}) => {
|
|
14
|
+
transformInput: (config: {
|
|
15
|
+
ctx: {
|
|
16
|
+
step: any;
|
|
17
|
+
};
|
|
18
|
+
}) => {
|
|
19
|
+
ctx: {
|
|
20
|
+
publish: Realtime.PublishFn;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
declare const realtimeMiddleware: () => InngestMiddleware;
|
|
27
|
+
//#endregion
|
|
28
|
+
export { Expect, InngestMiddleware, IsEqual, IsLiteral, IsStringLiteral, MaybePromise, Realtime, Simplify, realtimeMiddleware };
|
|
29
|
+
//# sourceMappingURL=middleware.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"middleware.d.mts","names":[],"sources":["../src/middleware.ts"],"mappings":";;;UAEiB,iBAAA;EACf,IAAA;EACA,IAAA,GAAO,MAAA;IAAU,MAAA;EAAA;IACf,aAAA,GAAgB,MAAA;MAAU,GAAA;QAAO,KAAA;MAAA;IAAA;MAC/B,cAAA,GAAiB,MAAA;QAAU,GAAA;UAAO,IAAA;QAAA;MAAA;QAChC,GAAA;UACE,OAAA,EAAS,QAAA,CAAS,SAAA;QAAA;MAAA;IAAA;EAAA;AAAA;AAAA,cAOf,kBAAA,QA+CN,iBAAA"}
|