@mvtlab/nextjs-orchestrator 0.1.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.
package/LICENSE ADDED
@@ -0,0 +1,10 @@
1
+ MVTLab.io License
2
+
3
+ Copyright (c) MVTLab.io. All rights reserved.
4
+
5
+ This software is proprietary and confidential to MVTLab.io.
6
+ Unauthorized copying, modification, distribution, or use of this software,
7
+ in whole or in part, is strictly prohibited without the prior written
8
+ consent of MVTLab.io.
9
+
10
+
package/README.md ADDED
@@ -0,0 +1,204 @@
1
+ ## `@mvtlab/nextjs-orchestrator`
2
+
3
+ **Next.js / React SDK** to integrate the MVTLab.io CDN engine with optional anti‑flicker support.
4
+
5
+ It wraps the plain HTML snippet:
6
+
7
+ ```html
8
+ <script>
9
+ var timeout = 3000;
10
+ !(function (h, i, d, e) {
11
+ var t,
12
+ n = h.createElement('style');
13
+ (n.id = e),
14
+ (n.innerHTML = 'body{opacity:0}'),
15
+ h.head.appendChild(n),
16
+ (t = d),
17
+ (i.rmfk = function () {
18
+ var t = h.getElementById(e);
19
+ t && t.parentNode.removeChild(t);
20
+ }),
21
+ setTimeout(i.rmfk, t);
22
+ })(document, window, timeout, 'abhide');
23
+ </script>
24
+ <script
25
+ src="https://staging-svc.mvtlab.io/scripts/engine.js"
26
+ data-project-key="Kjnqjkf277nYGYUSoA0IXQ"
27
+ data-mvt="engine"
28
+ ></script>
29
+ ```
30
+
31
+ into a simple React component:
32
+
33
+ ```tsx
34
+ <MVTOrchestrator orchestratorKey="yourProjectKey" antiFlickerEnabled>
35
+ {children}
36
+ </MVTOrchestrator>
37
+ ```
38
+
39
+ ---
40
+
41
+ ### Installation
42
+
43
+ ```bash
44
+ npm install @mvtlab/nextjs-orchestrator
45
+ # or
46
+ yarn add @mvtlab/nextjs-orchestrator
47
+ ```
48
+
49
+ `MVTOrchestrator` is a **client component**, so it must be used in a file marked with `'use client';`.
50
+
51
+ > **Why client-only?**
52
+ > The orchestrator injects `<script>` and `<style>` tags, accesses `window` and `document`, and defines `window.rmfk`.
53
+ > These operations only exist in the browser and are not allowed in Next.js server components, so the component must run on the client.
54
+
55
+ ---
56
+
57
+ ### Quick start (Next.js App Router)
58
+
59
+ Recommended usage is to wrap your **entire app at the root** so there is exactly **one project key per website**.
60
+
61
+ Example in `app/layout.tsx`:
62
+
63
+ ```tsx
64
+ 'use client';
65
+
66
+ import React from 'react';
67
+ import { MVTOrchestrator } from '@mvtlab/nextjs-orchestrator';
68
+
69
+ export default function RootLayout({
70
+ children,
71
+ }: {
72
+ children: React.ReactNode;
73
+ }) {
74
+ return (
75
+ <html lang="en">
76
+ <body>
77
+ <MVTOrchestrator
78
+ orchestratorKey="Kjnqjkf277nYGYUSoA0IXQ"
79
+ antiFlickerEnabled={true}
80
+ >
81
+ {children}
82
+ </MVTOrchestrator>
83
+ </body>
84
+ </html>
85
+ );
86
+ }
87
+ ```
88
+
89
+ This:
90
+
91
+ - **Injects the MVTLab.io engine script** into `<head>` once for the whole site with your project key.
92
+ - **Optionally hides the body** until the engine is ready (anti‑flicker).
93
+ In this setup your app has **a single `orchestratorKey` at the root**, shared across all pages.
94
+
95
+ ---
96
+
97
+ ### Usage in Next.js Pages Router
98
+
99
+ In a classic `pages/_app.tsx`:
100
+
101
+ ```tsx
102
+ // pages/_app.tsx
103
+ 'use client';
104
+
105
+ import type { AppProps } from 'next/app';
106
+ import { MVTOrchestrator } from '@mvtlab/nextjs-orchestrator';
107
+
108
+ export default function MyApp({ Component, pageProps }: AppProps) {
109
+ return (
110
+ <MVTOrchestrator
111
+ orchestratorKey="Kjnqjkf277nYGYUSoA0IXQ"
112
+ antiFlickerEnabled={true}
113
+ >
114
+ <Component {...pageProps} />
115
+ </MVTOrchestrator>
116
+ );
117
+ }
118
+ ```
119
+
120
+ In this setup your app has **a single `orchestratorKey` at the root**, shared across all pages.
121
+
122
+ ---
123
+
124
+ ### Props
125
+
126
+ - **`orchestratorKey`** (`string`, required)
127
+ - MVTLab.io project key.
128
+ - Passed to the script as `data-project-key="<orchestratorKey>"`.
129
+
130
+ - **`antiFlickerEnabled`** (`boolean`, default `true`)
131
+ - When `true`, a temporary `<style id="abhide">body{opacity:0}</style>` is injected into `<head>`.
132
+ - This mimics the original anti‑flicker snippet and prevents users from briefly seeing the “un‑optimized” variant.
133
+
134
+ - **`antiFlickerTimeoutMs`** (`number`, default `3000`)
135
+ - Timeout (in ms) after which the anti‑flicker style is removed automatically.
136
+ - Safety net in case the engine never calls `window.rmfk()`.
137
+
138
+ - **`engineUrl`** (`string`, default `https://staging-svc.mvtlab.io/scripts/engine.js`)
139
+ - Script URL used to load the MVTLab.io engine.
140
+ - You can override this if you have a different environment (e.g. production).
141
+
142
+ - **`children`** (`ReactNode`, required)
143
+ - The part of your app that should be orchestrated by the engine.
144
+ - Usually your entire app tree.
145
+
146
+ ---
147
+
148
+ ### How anti‑flicker works
149
+
150
+ When `antiFlickerEnabled` is `true`:
151
+
152
+ - A `<style>` tag with `id="abhide"` and `body{opacity:0}` is injected into `<head>`.
153
+ - `window.rmfk` is defined as a function that removes that style element.
154
+ - A timeout (`antiFlickerTimeoutMs`, default 3000ms) calls `window.rmfk()` as a fallback.
155
+
156
+ This is a **direct React translation** of the plain HTML snippet you would otherwise manually embed.
157
+
158
+ If you do **not** want anti‑flicker:
159
+
160
+ ```tsx
161
+ <MVTOrchestrator orchestratorKey="yourProjectKey" antiFlickerEnabled={false}>
162
+ {children}
163
+ </MVTOrchestrator>
164
+ ``>
165
+
166
+ ---
167
+
168
+ ### How script loading works
169
+
170
+ On the client, the component:
171
+
172
+ 1. Checks for an existing `<script>` with `id="mvt-engine-script"`.
173
+ 2. If not present, it creates a new `<script>` element with:
174
+ - `src` = `engineUrl` (default staging URL),
175
+ - `data-project-key` = `orchestratorKey`,
176
+ - `data-mvt` = `"engine"`,
177
+ - `async = true`.
178
+ 3. Appends the script to `<head>`.
179
+
180
+ This ensures **only one script is injected per page**, even if `MVTOrchestrator` is rendered multiple times.
181
+
182
+ ---
183
+
184
+ ### FAQ
185
+
186
+ - **Is this safe with SSR?**
187
+ Yes. All DOM access runs inside `useEffect`, so it only executes in the browser, not during server‑side rendering.
188
+
189
+ - **Can I use multiple project keys on the same page or site?**
190
+ **No.** The orchestrator is designed to be used **once at the root of your app** with a single `orchestratorKey` for the whole website.
191
+ Rendering with different keys on the same page is not supported and will log an error in the console as a safety check.
192
+
193
+ - **Can I disable anti‑flicker in development?**
194
+ Yes, just pass `antiFlickerEnabled={false}` when you don’t need it.
195
+
196
+ ---
197
+
198
+ ### License
199
+
200
+ This package is **licensed under MVTLab.io**.
201
+
202
+ Copyright © MVTLab.io. All rights reserved.
203
+
204
+
@@ -0,0 +1,29 @@
1
+ import React, { ReactNode } from 'react';
2
+ export type MVTOrchestratorProps = {
3
+ /**
4
+ * Project key used by the MVT engine.
5
+ * This will be passed as data-project-key to the engine script.
6
+ */
7
+ orchestratorKey: string;
8
+ /**
9
+ * Enable or disable the anti-flicker style injection.
10
+ * When enabled, a temporary style is applied to hide the body until
11
+ * the engine (or timeout) removes it, similar to the plain HTML snippet.
12
+ */
13
+ antiFlickerEnabled?: boolean;
14
+ /**
15
+ * Timeout in milliseconds to automatically remove the anti-flicker style.
16
+ * Defaults to 3000ms.
17
+ */
18
+ antiFlickerTimeoutMs?: number;
19
+ /**
20
+ * Custom engine script URL.
21
+ * Defaults to the staging endpoint used in the original snippet.
22
+ */
23
+ engineUrl?: string;
24
+ /**
25
+ * Children that will be rendered inside the orchestrator.
26
+ */
27
+ children: ReactNode;
28
+ };
29
+ export declare const MVTOrchestrator: React.FC<MVTOrchestratorProps>;
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+ import { Fragment as _Fragment, jsx as _jsx } from "react/jsx-runtime";
3
+ import { useEffect } from 'react';
4
+ const DEFAULT_ENGINE_URL = 'https://staging-svc.mvtlab.io/scripts/engine.js';
5
+ const ANTI_FLICKER_STYLE_ID = 'abhide';
6
+ export const MVTOrchestrator = ({ orchestratorKey, antiFlickerEnabled = true, antiFlickerTimeoutMs = 3000, engineUrl = DEFAULT_ENGINE_URL, children, }) => {
7
+ useEffect(() => {
8
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
9
+ return;
10
+ }
11
+ // Anti-flicker: inject style to hide body and set up removal function
12
+ if (antiFlickerEnabled) {
13
+ const existing = document.getElementById(ANTI_FLICKER_STYLE_ID);
14
+ if (!existing) {
15
+ const style = document.createElement('style');
16
+ style.id = ANTI_FLICKER_STYLE_ID;
17
+ style.innerHTML = 'body{opacity:0}';
18
+ document.head.appendChild(style);
19
+ }
20
+ // Expose rmfk on window to allow the engine to remove the flicker style
21
+ window.rmfk = function rmfk() {
22
+ const el = document.getElementById(ANTI_FLICKER_STYLE_ID);
23
+ if (el && el.parentNode) {
24
+ el.parentNode.removeChild(el);
25
+ }
26
+ };
27
+ // Safety timeout to remove if engine never calls rmfk
28
+ window.setTimeout(() => {
29
+ if (typeof window.rmfk === 'function') {
30
+ window.rmfk();
31
+ }
32
+ }, antiFlickerTimeoutMs);
33
+ }
34
+ // Enforce a single project key per page
35
+ const globalKeyProp = '__mvtOrchestratorKey';
36
+ const w = window;
37
+ if (w[globalKeyProp] && w[globalKeyProp] !== orchestratorKey) {
38
+ // eslint-disable-next-line no-console
39
+ console.error('[MVTOrchestrator] Multiple different project keys on the same page are not supported. ' +
40
+ `Existing key: "${w[globalKeyProp]}", new key: "${orchestratorKey}".`);
41
+ return;
42
+ }
43
+ w[globalKeyProp] = orchestratorKey;
44
+ // Inject the engine script only once per page load / key
45
+ const scriptId = 'mvt-engine-script';
46
+ if (!document.getElementById(scriptId)) {
47
+ const script = document.createElement('script');
48
+ script.id = scriptId;
49
+ script.src = engineUrl;
50
+ script.async = true;
51
+ script.setAttribute('data-project-key', orchestratorKey);
52
+ script.setAttribute('data-mvt', 'engine');
53
+ document.head.appendChild(script);
54
+ }
55
+ }, [orchestratorKey, antiFlickerEnabled, antiFlickerTimeoutMs, engineUrl]);
56
+ return _jsx(_Fragment, { children: children });
57
+ };
@@ -0,0 +1 @@
1
+ export * from './MVTOrchestrator';
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from './MVTOrchestrator';
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@mvtlab/nextjs-orchestrator",
3
+ "version": "0.1.0",
4
+ "description": "Next.js SDK to integrate the MVT Lab CDN engine with optional anti-flicker support.",
5
+ "main": "dist/index.cjs",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "sideEffects": false,
9
+ "scripts": {
10
+ "build": "tsc -p tsconfig.build.json"
11
+ },
12
+ "peerDependencies": {
13
+ "next": ">=13.0.0",
14
+ "react": ">=18.0.0",
15
+ "react-dom": ">=18.0.0"
16
+ },
17
+ "devDependencies": {
18
+ "@types/react": "^18.0.0",
19
+ "@types/react-dom": "^18.0.0",
20
+ "typescript": "^5.6.0"
21
+ },
22
+ "keywords": [
23
+ "nextjs",
24
+ "mvt",
25
+ "ab-testing",
26
+ "cdn-engine"
27
+ ],
28
+ "author": "MVT Lab",
29
+ "license": "MVTLab.io"
30
+ }