@reactll/connect 1.0.1 → 1.1.1

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # Reactll Connect for Next.js
1
+ # Reactll Connect for Next.js and Node.js
2
2
 
3
- Connects a Next.js site to its [Reactll](https://reactll.com) dashboard: visits, calls, WhatsApp and
3
+ Connects a Next.js or any Node.js site (Express, Fastify, Nuxt, Remix, Astro, Hono…) to its [Reactll](https://reactll.com) dashboard: visits, calls, WhatsApp and
4
4
  directions clicks, form leads and site health. Built and maintained by **Reactor Technology**.
5
5
 
6
6
  ## Install
@@ -54,6 +54,32 @@ export async function contact(formData: FormData) {
54
54
  The script stamps every form with the page view id (`_rc_pv`), so Reactll ties the lead to the visit it
55
55
  came from. `submitLead(form, fields, options)` takes a plain object instead. Neither ever throws.
56
56
 
57
+ ## Any other Node.js app
58
+
59
+ The same package, without Next.js or React (they are optional):
60
+
61
+ ```js
62
+ import { scriptTag, expressHealth, submitLead } from '@reactll/connect/server';
63
+
64
+ app.use(expressHealth()); // Express / Connect / node:http — GET /api/reactll/health
65
+ res.render('page', { reactll: scriptTag() }); // put {{{ reactll }}} / <%- reactll %> in the <head>
66
+
67
+ // after your form was validated and saved:
68
+ await submitLead('Contact', { name, email, message }, { pv: req.body._rc_pv, page: req.get('referer') });
69
+ ```
70
+
71
+ Frameworks on web `Request`/`Response` (Hono, Remix, Nuxt/h3, Astro, Bun, Deno): route
72
+ `GET /api/reactll/health` to `health(request)` from `@reactll/connect/server`.
73
+
74
+ ## Content-Security-Policy
75
+
76
+ If the site sends a CSP header, allow Reactll Connect in it, or the browser blocks the script and its beacons:
77
+
78
+ ```
79
+ script-src … https://connect.reactll.com
80
+ connect-src … https://connect.reactll.com
81
+ ```
82
+
57
83
  ## License
58
84
 
59
85
  MIT © Reactor Technology
package/dist/server.d.ts CHANGED
@@ -21,6 +21,11 @@ export declare function submitLead(form: string, fields: Fields, options?: LeadO
21
21
  export declare function submitLeadFromFormData(form: string, data: FormData, options?: LeadOptions & {
22
22
  headers?: Headers;
23
23
  }): Promise<boolean>;
24
+ /**
25
+ * The script tag for any server-rendered page (Express/EJS, Handlebars, Fastify, Nuxt, Astro…), or ''
26
+ * when not connected. Put it in the <head>. Only the public site key is in it.
27
+ */
28
+ export declare function scriptTag(token?: string): string;
24
29
  /**
25
30
  * Hourly health check, asked by Reactll (Next.js has no scheduler of its own). Export it from
26
31
  * app/api/reactll/health/route.ts:
@@ -31,3 +36,23 @@ export declare function submitLeadFromFormData(form: string, data: FormData, opt
31
36
  */
32
37
  export declare function GET(request: Request): Promise<Response>;
33
38
  export declare const dynamic = "force-dynamic";
39
+ /** The health answer as plain data (platform and versions), for any framework. */
40
+ export declare function health(request: Request): Promise<Response>;
41
+ type NodeReq = {
42
+ method?: string;
43
+ url?: string;
44
+ originalUrl?: string;
45
+ headers: Record<string, string | string[] | undefined>;
46
+ };
47
+ type NodeRes = {
48
+ statusCode: number;
49
+ setHeader(name: string, value: string): unknown;
50
+ end(body?: string): unknown;
51
+ };
52
+ /**
53
+ * Express / Connect (and plain node:http) middleware answering Reactll's hourly health question at
54
+ * GET /api/reactll/health. Other requests pass through.
55
+ *
56
+ * app.use(expressHealth());
57
+ */
58
+ export declare function expressHealth(path?: string): (req: NodeReq, res: NodeRes, next?: () => void) => Promise<void>;
package/dist/server.js CHANGED
@@ -65,6 +65,14 @@ export async function submitLeadFromFormData(form, data, options = {}) {
65
65
  const pv = typeof data.get('_rc_pv') === 'string' ? data.get('_rc_pv') : null;
66
66
  return submitLead(form, fields, { ...options, pv: options.pv ?? pv, page: options.page ?? options.headers?.get('referer') ?? null });
67
67
  }
68
+ /**
69
+ * The script tag for any server-rendered page (Express/EJS, Handlebars, Fastify, Nuxt, Astro…), or ''
70
+ * when not connected. Put it in the <head>. Only the public site key is in it.
71
+ */
72
+ export function scriptTag(token) {
73
+ const c = credentials(token);
74
+ return c ? `<script src="${c.u}/c.js" data-site="${c.k}" defer></script>` : '';
75
+ }
68
76
  function versionOf(pkg) {
69
77
  try {
70
78
  return createRequire(import.meta.url)(`${pkg}/package.json`).version;
@@ -93,7 +101,7 @@ export async function GET(request) {
93
101
  return Response.json({ error: 'unauthorized' }, { status: 401 });
94
102
  }
95
103
  return Response.json({
96
- platform: 'next',
104
+ platform: versionOf('next') ? 'next' : 'node',
97
105
  version: VERSION,
98
106
  runtime: Object.fromEntries(Object.entries({
99
107
  next: versionOf('next'),
@@ -101,7 +109,39 @@ export async function GET(request) {
101
109
  node: process.version.replace(/^v/, ''),
102
110
  environment: process.env.VERCEL_ENV ?? process.env.NODE_ENV,
103
111
  host: process.env.VERCEL ? 'Vercel' : undefined,
112
+ // Reactll counts the site's days on this clock (UTC is read as "not set"; set TZ to override).
113
+ timezone: process.env.TZ || Intl.DateTimeFormat().resolvedOptions().timeZone,
104
114
  }).filter(([, v]) => v)),
105
115
  }, { headers: { 'Cache-Control': 'no-store' } });
106
116
  }
107
117
  export const dynamic = 'force-dynamic';
118
+ /** The health answer as plain data (platform and versions), for any framework. */
119
+ export async function health(request) {
120
+ return GET(request);
121
+ }
122
+ /**
123
+ * Express / Connect (and plain node:http) middleware answering Reactll's hourly health question at
124
+ * GET /api/reactll/health. Other requests pass through.
125
+ *
126
+ * app.use(expressHealth());
127
+ */
128
+ export function expressHealth(path = '/api/reactll/health') {
129
+ return async (req, res, next) => {
130
+ const url = (req.originalUrl ?? req.url ?? '').split('?')[0];
131
+ if ((req.method ?? 'GET') !== 'GET' || url !== path) {
132
+ next?.();
133
+ return;
134
+ }
135
+ const header = (name) => {
136
+ const v = req.headers[name];
137
+ return Array.isArray(v) ? v[0] : (v ?? '');
138
+ };
139
+ const answer = await GET(new Request('http://localhost' + path, {
140
+ headers: { 'x-reactll-timestamp': header('x-reactll-timestamp'), 'x-reactll-signature': header('x-reactll-signature') },
141
+ }));
142
+ res.statusCode = answer.status;
143
+ res.setHeader('Content-Type', 'application/json');
144
+ res.setHeader('Cache-Control', 'no-store');
145
+ res.end(await answer.text());
146
+ };
147
+ }
package/dist/token.d.ts CHANGED
@@ -5,7 +5,7 @@
5
5
  * endpoint, the site's public key and its secret. Only the endpoint and the public key ever reach
6
6
  * the browser.
7
7
  */
8
- export declare const VERSION = "1.0.0";
8
+ export declare const VERSION = "1.1.1";
9
9
  export interface Credentials {
10
10
  /** Endpoint, e.g. https://connect.reactll.com/v1 */
11
11
  u: string;
package/dist/token.js CHANGED
@@ -5,7 +5,7 @@
5
5
  * endpoint, the site's public key and its secret. Only the endpoint and the public key ever reach
6
6
  * the browser.
7
7
  */
8
- export const VERSION = '1.0.0';
8
+ export const VERSION = '1.1.1';
9
9
  export function credentials(token = process.env.REACTLL_CONNECT_TOKEN) {
10
10
  const raw = (token ?? '').trim();
11
11
  if (!raw.startsWith('rcc_'))
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@reactll/connect",
3
- "version": "1.0.1",
4
- "description": "Reactll Connect for Next.js: visits, clicks, form leads and site health for your Reactll dashboard.",
3
+ "version": "1.1.1",
4
+ "description": "Reactll Connect for Next.js and any Node.js app: visits, clicks, form leads and site health for your Reactll dashboard.",
5
5
  "keywords": [
6
6
  "nextjs",
7
+ "nodejs",
8
+ "express",
7
9
  "analytics",
8
10
  "leads",
9
11
  "reactll"
@@ -47,5 +49,16 @@
47
49
  "next": "^15.5.0",
48
50
  "react": "^19.0.0",
49
51
  "typescript": "^5.6.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "next": {
55
+ "optional": true
56
+ },
57
+ "react": {
58
+ "optional": true
59
+ }
60
+ },
61
+ "engines": {
62
+ "node": ">=18"
50
63
  }
51
64
  }