@datalyr/wizard 1.0.3 → 1.0.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/dist/bin/wizard.js +173 -88
- package/dist/bin/wizard.js.map +1 -1
- package/dist/index.js +173 -88
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +173 -88
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -11
package/dist/bin/wizard.js
CHANGED
|
@@ -132,9 +132,11 @@ Install and configure the Datalyr analytics SDK in the user's project. Complete
|
|
|
132
132
|
| SDK | Use Case | Install Command |
|
|
133
133
|
|-----|----------|-----------------|
|
|
134
134
|
| @datalyr/web | Browser apps (React, Vue, Svelte, Next.js client) | npm install @datalyr/web |
|
|
135
|
-
| @datalyr/api | Server-side (
|
|
135
|
+
| @datalyr/api | Server-side only (API routes, Express, Node) | npm install @datalyr/api |
|
|
136
136
|
| @datalyr/react-native | React Native & Expo mobile apps | npm install @datalyr/react-native |
|
|
137
137
|
| DatalyrSDK | Native iOS Swift apps | Swift Package Manager |
|
|
138
|
+
|
|
139
|
+
IMPORTANT: For most projects, you only need @datalyr/web. Do NOT install @datalyr/api unless the user explicitly needs server-side tracking.
|
|
138
140
|
</sdks>
|
|
139
141
|
|
|
140
142
|
<rules>
|
|
@@ -143,40 +145,140 @@ Install and configure the Datalyr analytics SDK in the user's project. Complete
|
|
|
143
145
|
3. MATCH the project's code style (indentation, quotes, semicolons)
|
|
144
146
|
4. USE TypeScript if the project uses TypeScript
|
|
145
147
|
5. PLACE initialization in the correct entry point for the framework
|
|
146
|
-
6. UPDATE .env or .env.local with
|
|
148
|
+
6. UPDATE .env or .env.local with the correct env var name for the framework
|
|
147
149
|
7. CHECK for existing Datalyr setup first - don't duplicate
|
|
150
|
+
8. ONLY install @datalyr/web unless server-side tracking is explicitly needed
|
|
148
151
|
</rules>
|
|
149
152
|
|
|
153
|
+
<critical-nextjs-rules>
|
|
154
|
+
NEXT.JS APP ROUTER REQUIRES SPECIAL HANDLING:
|
|
155
|
+
|
|
156
|
+
1. SERVER VS CLIENT COMPONENTS:
|
|
157
|
+
- By default, all components in app/ directory are Server Components
|
|
158
|
+
- Server Components CANNOT use: useEffect, useState, useRef, onClick, or any browser APIs
|
|
159
|
+
- To use these, you MUST add 'use client' directive at the TOP of the file
|
|
160
|
+
|
|
161
|
+
2. THE 'use client' DIRECTIVE:
|
|
162
|
+
- MUST be the FIRST line of the file (before any imports)
|
|
163
|
+
- Makes the component and all its children Client Components
|
|
164
|
+
- Required for any component that uses React hooks or browser APIs
|
|
165
|
+
|
|
166
|
+
3. THE PROVIDER PATTERN (REQUIRED FOR NEXT.JS APP ROUTER):
|
|
167
|
+
- Create a separate Client Component file for the provider
|
|
168
|
+
- The layout.tsx stays as a Server Component
|
|
169
|
+
- Import and use the provider inside layout.tsx
|
|
170
|
+
|
|
171
|
+
4. WINDOW/DOCUMENT CHECKS:
|
|
172
|
+
- Even in Client Components, code runs on server first for SSR
|
|
173
|
+
- useEffect is the safest way to run client-only code
|
|
174
|
+
- Never access window/document outside of useEffect
|
|
175
|
+
|
|
176
|
+
CORRECT PATTERN:
|
|
177
|
+
- app/providers.tsx -> 'use client' + DatalyrProvider with useEffect
|
|
178
|
+
- app/layout.tsx -> Server Component that imports and wraps with DatalyrProvider
|
|
179
|
+
</critical-nextjs-rules>
|
|
180
|
+
|
|
150
181
|
<workflow>
|
|
151
182
|
Step 1: Read package.json to detect framework and package manager
|
|
152
183
|
Step 2: List files to find entry points (app/layout.tsx, src/main.tsx, App.tsx, etc.)
|
|
153
184
|
Step 3: Read entry point files to understand current structure
|
|
154
|
-
Step 4: Install SDK
|
|
155
|
-
Step 5: Create initialization file (
|
|
185
|
+
Step 4: Install SDK package using detected package manager (usually just @datalyr/web)
|
|
186
|
+
Step 5: Create initialization file (for Next.js App Router: app/providers.tsx with 'use client')
|
|
156
187
|
Step 6: Update entry point to import and initialize Datalyr
|
|
157
|
-
Step 7: Update or create .env.local with workspace ID
|
|
188
|
+
Step 7: Update or create .env.local with workspace ID
|
|
158
189
|
Step 8: Call task_complete with summary of changes
|
|
159
190
|
</workflow>
|
|
160
191
|
|
|
161
192
|
<examples>
|
|
162
193
|
<example name="nextjs-app-router">
|
|
163
|
-
For Next.js 13+ with App Router
|
|
194
|
+
For Next.js 13+ with App Router:
|
|
195
|
+
|
|
196
|
+
STEP 1: Create app/providers.tsx (Client Component):
|
|
164
197
|
\`\`\`tsx
|
|
165
198
|
'use client';
|
|
199
|
+
|
|
166
200
|
import datalyr from '@datalyr/web';
|
|
167
201
|
import { useEffect } from 'react';
|
|
168
202
|
|
|
169
203
|
export function DatalyrProvider({ children }: { children: React.ReactNode }) {
|
|
170
204
|
useEffect(() => {
|
|
205
|
+
// useEffect only runs on client, safe to initialize here
|
|
171
206
|
datalyr.init({
|
|
172
207
|
workspaceId: process.env.NEXT_PUBLIC_DATALYR_WORKSPACE_ID!,
|
|
173
208
|
debug: process.env.NODE_ENV === 'development',
|
|
174
209
|
});
|
|
175
210
|
}, []);
|
|
211
|
+
|
|
176
212
|
return <>{children}</>;
|
|
177
213
|
}
|
|
178
214
|
\`\`\`
|
|
179
|
-
|
|
215
|
+
|
|
216
|
+
STEP 2: Update app/layout.tsx (stays as Server Component):
|
|
217
|
+
\`\`\`tsx
|
|
218
|
+
import { DatalyrProvider } from './providers';
|
|
219
|
+
|
|
220
|
+
export default function RootLayout({
|
|
221
|
+
children,
|
|
222
|
+
}: {
|
|
223
|
+
children: React.ReactNode;
|
|
224
|
+
}) {
|
|
225
|
+
return (
|
|
226
|
+
<html lang="en">
|
|
227
|
+
<body>
|
|
228
|
+
<DatalyrProvider>
|
|
229
|
+
{children}
|
|
230
|
+
</DatalyrProvider>
|
|
231
|
+
</body>
|
|
232
|
+
</html>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
\`\`\`
|
|
236
|
+
|
|
237
|
+
STEP 3: Add to .env.local:
|
|
238
|
+
\`\`\`
|
|
239
|
+
NEXT_PUBLIC_DATALYR_WORKSPACE_ID=your_workspace_id
|
|
240
|
+
\`\`\`
|
|
241
|
+
|
|
242
|
+
CRITICAL: The 'use client' directive MUST be at the very top of providers.tsx, before any imports!
|
|
243
|
+
</example>
|
|
244
|
+
|
|
245
|
+
<example name="nextjs-pages-router">
|
|
246
|
+
For Next.js Pages Router (pages/ directory):
|
|
247
|
+
|
|
248
|
+
STEP 1: Create lib/datalyr.ts:
|
|
249
|
+
\`\`\`ts
|
|
250
|
+
import datalyr from '@datalyr/web';
|
|
251
|
+
|
|
252
|
+
let initialized = false;
|
|
253
|
+
|
|
254
|
+
export function initDatalyr() {
|
|
255
|
+
if (initialized || typeof window === 'undefined') return;
|
|
256
|
+
|
|
257
|
+
datalyr.init({
|
|
258
|
+
workspaceId: process.env.NEXT_PUBLIC_DATALYR_WORKSPACE_ID!,
|
|
259
|
+
debug: process.env.NODE_ENV === 'development',
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
initialized = true;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
export { datalyr };
|
|
266
|
+
\`\`\`
|
|
267
|
+
|
|
268
|
+
STEP 2: Update pages/_app.tsx:
|
|
269
|
+
\`\`\`tsx
|
|
270
|
+
import { useEffect } from 'react';
|
|
271
|
+
import { initDatalyr } from '../lib/datalyr';
|
|
272
|
+
import type { AppProps } from 'next/app';
|
|
273
|
+
|
|
274
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
275
|
+
useEffect(() => {
|
|
276
|
+
initDatalyr();
|
|
277
|
+
}, []);
|
|
278
|
+
|
|
279
|
+
return <Component {...pageProps} />;
|
|
280
|
+
}
|
|
281
|
+
\`\`\`
|
|
180
282
|
</example>
|
|
181
283
|
|
|
182
284
|
<example name="react-vite">
|
|
@@ -197,7 +299,16 @@ export function initDatalyr() {
|
|
|
197
299
|
|
|
198
300
|
export { datalyr };
|
|
199
301
|
\`\`\`
|
|
200
|
-
|
|
302
|
+
|
|
303
|
+
Update src/main.tsx:
|
|
304
|
+
\`\`\`tsx
|
|
305
|
+
import { initDatalyr } from './lib/datalyr';
|
|
306
|
+
|
|
307
|
+
// Initialize before rendering
|
|
308
|
+
initDatalyr();
|
|
309
|
+
|
|
310
|
+
// ... rest of app
|
|
311
|
+
\`\`\`
|
|
201
312
|
</example>
|
|
202
313
|
|
|
203
314
|
<example name="react-native">
|
|
@@ -205,20 +316,66 @@ For React Native, create src/utils/datalyr.ts:
|
|
|
205
316
|
\`\`\`ts
|
|
206
317
|
import { Datalyr } from '@datalyr/react-native';
|
|
207
318
|
|
|
208
|
-
|
|
319
|
+
let initialized = false;
|
|
320
|
+
|
|
321
|
+
export async function initDatalyr() {
|
|
322
|
+
if (initialized) return;
|
|
323
|
+
|
|
324
|
+
const apiKey = process.env.DATALYR_API_KEY;
|
|
325
|
+
if (!apiKey) {
|
|
326
|
+
console.warn('[Datalyr] API key not configured');
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
|
|
209
330
|
await Datalyr.initialize({
|
|
210
331
|
apiKey,
|
|
211
332
|
enableAutoEvents: true,
|
|
212
333
|
debug: __DEV__,
|
|
213
334
|
});
|
|
335
|
+
|
|
336
|
+
initialized = true;
|
|
214
337
|
}
|
|
215
338
|
|
|
216
339
|
export { Datalyr };
|
|
217
340
|
\`\`\`
|
|
218
|
-
|
|
341
|
+
|
|
342
|
+
Update App.tsx:
|
|
343
|
+
\`\`\`tsx
|
|
344
|
+
import { useEffect } from 'react';
|
|
345
|
+
import { initDatalyr } from './src/utils/datalyr';
|
|
346
|
+
|
|
347
|
+
export default function App() {
|
|
348
|
+
useEffect(() => {
|
|
349
|
+
initDatalyr();
|
|
350
|
+
}, []);
|
|
351
|
+
|
|
352
|
+
return (/* your app */);
|
|
353
|
+
}
|
|
354
|
+
\`\`\`
|
|
219
355
|
</example>
|
|
220
356
|
</examples>
|
|
221
357
|
|
|
358
|
+
<common-mistakes>
|
|
359
|
+
AVOID THESE MISTAKES:
|
|
360
|
+
|
|
361
|
+
1. WRONG: Adding useEffect directly in layout.tsx without 'use client'
|
|
362
|
+
This will error: "useEffect only works in Client Components"
|
|
363
|
+
|
|
364
|
+
2. WRONG: Putting 'use client' after imports
|
|
365
|
+
The directive MUST be the first line of the file
|
|
366
|
+
|
|
367
|
+
3. WRONG: Trying to access window/document at module level
|
|
368
|
+
Always use useEffect or check typeof window !== 'undefined'
|
|
369
|
+
|
|
370
|
+
4. WRONG: Installing @datalyr/api when only client tracking is needed
|
|
371
|
+
Most apps only need @datalyr/web
|
|
372
|
+
|
|
373
|
+
5. WRONG: Using the wrong env var prefix
|
|
374
|
+
- Next.js: NEXT_PUBLIC_
|
|
375
|
+
- Vite: VITE_
|
|
376
|
+
- SvelteKit: PUBLIC_
|
|
377
|
+
</common-mistakes>
|
|
378
|
+
|
|
222
379
|
<signals>
|
|
223
380
|
When complete: ${AGENT_SIGNALS.SUCCESS}
|
|
224
381
|
On error: ${AGENT_SIGNALS.ERROR_FAILED}
|
|
@@ -370,7 +527,6 @@ function getSDKsForFramework(framework) {
|
|
|
370
527
|
case "sveltekit":
|
|
371
528
|
case "nuxt":
|
|
372
529
|
case "astro":
|
|
373
|
-
return ["@datalyr/web", "@datalyr/api"];
|
|
374
530
|
case "react":
|
|
375
531
|
case "react-vite":
|
|
376
532
|
case "svelte":
|
|
@@ -1008,17 +1164,12 @@ var NEXTJS_CONFIG = {
|
|
|
1008
1164
|
id: "nextjs",
|
|
1009
1165
|
name: "Next.js",
|
|
1010
1166
|
detectPackage: "next",
|
|
1011
|
-
sdks: ["@datalyr/web"
|
|
1167
|
+
sdks: ["@datalyr/web"],
|
|
1012
1168
|
envVars: [
|
|
1013
1169
|
{
|
|
1014
1170
|
key: "NEXT_PUBLIC_DATALYR_WORKSPACE_ID",
|
|
1015
1171
|
description: "Your Datalyr workspace ID",
|
|
1016
1172
|
isPublic: true
|
|
1017
|
-
},
|
|
1018
|
-
{
|
|
1019
|
-
key: "DATALYR_API_KEY",
|
|
1020
|
-
description: "Server-side API key",
|
|
1021
|
-
isPublic: false
|
|
1022
1173
|
}
|
|
1023
1174
|
],
|
|
1024
1175
|
estimatedTime: 3,
|
|
@@ -1083,17 +1234,12 @@ var SVELTEKIT_CONFIG = {
|
|
|
1083
1234
|
id: "sveltekit",
|
|
1084
1235
|
name: "SvelteKit",
|
|
1085
1236
|
detectPackage: "@sveltejs/kit",
|
|
1086
|
-
sdks: ["@datalyr/web"
|
|
1237
|
+
sdks: ["@datalyr/web"],
|
|
1087
1238
|
envVars: [
|
|
1088
1239
|
{
|
|
1089
1240
|
key: "PUBLIC_DATALYR_WORKSPACE_ID",
|
|
1090
1241
|
description: "Your Datalyr workspace ID",
|
|
1091
1242
|
isPublic: true
|
|
1092
|
-
},
|
|
1093
|
-
{
|
|
1094
|
-
key: "DATALYR_API_KEY",
|
|
1095
|
-
description: "Server-side API key",
|
|
1096
|
-
isPublic: false
|
|
1097
1243
|
}
|
|
1098
1244
|
],
|
|
1099
1245
|
estimatedTime: 3,
|
|
@@ -1146,17 +1292,12 @@ var NUXT_CONFIG = {
|
|
|
1146
1292
|
id: "nuxt",
|
|
1147
1293
|
name: "Nuxt",
|
|
1148
1294
|
detectPackage: "nuxt",
|
|
1149
|
-
sdks: ["@datalyr/web"
|
|
1295
|
+
sdks: ["@datalyr/web"],
|
|
1150
1296
|
envVars: [
|
|
1151
1297
|
{
|
|
1152
1298
|
key: "NUXT_PUBLIC_DATALYR_WORKSPACE_ID",
|
|
1153
1299
|
description: "Your Datalyr workspace ID",
|
|
1154
1300
|
isPublic: true
|
|
1155
|
-
},
|
|
1156
|
-
{
|
|
1157
|
-
key: "DATALYR_API_KEY",
|
|
1158
|
-
description: "Server-side API key",
|
|
1159
|
-
isPublic: false
|
|
1160
1301
|
}
|
|
1161
1302
|
],
|
|
1162
1303
|
estimatedTime: 3,
|
|
@@ -1171,17 +1312,12 @@ var REMIX_CONFIG = {
|
|
|
1171
1312
|
id: "remix",
|
|
1172
1313
|
name: "Remix",
|
|
1173
1314
|
detectPackage: "@remix-run/react",
|
|
1174
|
-
sdks: ["@datalyr/web"
|
|
1315
|
+
sdks: ["@datalyr/web"],
|
|
1175
1316
|
envVars: [
|
|
1176
1317
|
{
|
|
1177
1318
|
key: "DATALYR_WORKSPACE_ID",
|
|
1178
1319
|
description: "Your Datalyr workspace ID",
|
|
1179
1320
|
isPublic: true
|
|
1180
|
-
},
|
|
1181
|
-
{
|
|
1182
|
-
key: "DATALYR_API_KEY",
|
|
1183
|
-
description: "Server-side API key",
|
|
1184
|
-
isPublic: false
|
|
1185
1321
|
}
|
|
1186
1322
|
],
|
|
1187
1323
|
estimatedTime: 3,
|
|
@@ -1888,38 +2024,8 @@ async function runAgentWizard(_config, options = {}) {
|
|
|
1888
2024
|
const validateSpinner = p.spinner();
|
|
1889
2025
|
validateSpinner.start("Validating API key...");
|
|
1890
2026
|
try {
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
if (allWorkspaces.length > 1) {
|
|
1894
|
-
validateSpinner.stop("API key validated");
|
|
1895
|
-
const workspaceChoice = await p.select({
|
|
1896
|
-
message: "Select a workspace to configure:",
|
|
1897
|
-
options: allWorkspaces.map((w) => ({
|
|
1898
|
-
value: w.id,
|
|
1899
|
-
label: w.name,
|
|
1900
|
-
hint: w.domain || void 0
|
|
1901
|
-
}))
|
|
1902
|
-
});
|
|
1903
|
-
if (p.isCancel(workspaceChoice)) {
|
|
1904
|
-
p.cancel("Wizard cancelled");
|
|
1905
|
-
return { success: false, error: "User cancelled" };
|
|
1906
|
-
}
|
|
1907
|
-
const selectedWorkspace = allWorkspaces.find((w) => w.id === workspaceChoice);
|
|
1908
|
-
if (selectedWorkspace) {
|
|
1909
|
-
workspace = {
|
|
1910
|
-
id: selectedWorkspace.id,
|
|
1911
|
-
name: selectedWorkspace.name,
|
|
1912
|
-
timezone: null,
|
|
1913
|
-
domain: selectedWorkspace.domain
|
|
1914
|
-
};
|
|
1915
|
-
} else {
|
|
1916
|
-
workspace = defaultWorkspace;
|
|
1917
|
-
}
|
|
1918
|
-
p.log.info(`Selected workspace: ${import_chalk.default.cyan(workspace.name)}`);
|
|
1919
|
-
} else {
|
|
1920
|
-
workspace = defaultWorkspace;
|
|
1921
|
-
validateSpinner.stop(`Workspace: ${import_chalk.default.cyan(workspace.name)}`);
|
|
1922
|
-
}
|
|
2027
|
+
workspace = await validateApiKey(apiKey);
|
|
2028
|
+
validateSpinner.stop(`Workspace: ${import_chalk.default.cyan(workspace.name)}`);
|
|
1923
2029
|
} catch (error) {
|
|
1924
2030
|
validateSpinner.stop(import_chalk.default.red("Invalid API key"));
|
|
1925
2031
|
const errorMessage = error instanceof Error ? error.message : "Failed to validate API key";
|
|
@@ -2494,27 +2600,6 @@ async function validateApiKey(apiKey) {
|
|
|
2494
2600
|
}
|
|
2495
2601
|
return result.workspace;
|
|
2496
2602
|
}
|
|
2497
|
-
async function fetchWorkspaces(apiKey) {
|
|
2498
|
-
const controller = new AbortController();
|
|
2499
|
-
const timeoutId = setTimeout(() => controller.abort(), 3e4);
|
|
2500
|
-
const response = await fetch(`${LLM_GATEWAY_URL}/workspaces`, {
|
|
2501
|
-
method: "POST",
|
|
2502
|
-
headers: {
|
|
2503
|
-
"Content-Type": "application/json"
|
|
2504
|
-
},
|
|
2505
|
-
body: JSON.stringify({ apiKey }),
|
|
2506
|
-
signal: controller.signal
|
|
2507
|
-
});
|
|
2508
|
-
clearTimeout(timeoutId);
|
|
2509
|
-
if (!response.ok) {
|
|
2510
|
-
return [];
|
|
2511
|
-
}
|
|
2512
|
-
const result = await response.json();
|
|
2513
|
-
if (!result.success || !result.workspaces) {
|
|
2514
|
-
return [];
|
|
2515
|
-
}
|
|
2516
|
-
return result.workspaces;
|
|
2517
|
-
}
|
|
2518
2603
|
async function verifyInstallation(apiKey, workspaceId) {
|
|
2519
2604
|
try {
|
|
2520
2605
|
const controller = new AbortController();
|