@userreason/wizard 0.1.0 → 0.2.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.
@@ -0,0 +1,899 @@
1
+ import { createRequire as createWizardRequire } from 'node:module'; import { fileURLToPath as wizardFileURLToPath } from 'node:url'; import { dirname as wizardDirname } from 'node:path'; const require = createWizardRequire(import.meta.url); const __filename = wizardFileURLToPath(import.meta.url); const __dirname = wizardDirname(__filename);
2
+ import {
3
+ DEFAULT_API,
4
+ initializationSource,
5
+ integrationSource
6
+ } from "./chunk-YMSGDC4B.js";
7
+ import {
8
+ require_typescript
9
+ } from "./chunk-GGARVSPT.js";
10
+ import {
11
+ __toESM
12
+ } from "./chunk-WL3NOIM6.js";
13
+
14
+ // src/integrate.ts
15
+ var import_typescript = __toESM(require_typescript(), 1);
16
+
17
+ // node_modules/diff/libesm/diff/base.js
18
+ var Diff = class {
19
+ diff(oldStr, newStr, options = {}) {
20
+ let callback;
21
+ if (typeof options === "function") {
22
+ callback = options;
23
+ options = {};
24
+ } else if ("callback" in options) {
25
+ callback = options.callback;
26
+ }
27
+ const oldString = this.castInput(oldStr, options);
28
+ const newString = this.castInput(newStr, options);
29
+ const oldTokens = this.removeEmpty(this.tokenize(oldString, options));
30
+ const newTokens = this.removeEmpty(this.tokenize(newString, options));
31
+ return this.diffWithOptionsObj(oldTokens, newTokens, options, callback);
32
+ }
33
+ diffWithOptionsObj(oldTokens, newTokens, options, callback) {
34
+ var _a;
35
+ const done = (value) => {
36
+ value = this.postProcess(value, options);
37
+ if (callback) {
38
+ setTimeout(function() {
39
+ callback(value);
40
+ }, 0);
41
+ return void 0;
42
+ } else {
43
+ return value;
44
+ }
45
+ };
46
+ const newLen = newTokens.length, oldLen = oldTokens.length;
47
+ let editLength = 1;
48
+ let maxEditLength = newLen + oldLen;
49
+ if (options.maxEditLength != null) {
50
+ maxEditLength = Math.min(maxEditLength, options.maxEditLength);
51
+ }
52
+ const maxExecutionTime = (_a = options.timeout) !== null && _a !== void 0 ? _a : Infinity;
53
+ const abortAfterTimestamp = Date.now() + maxExecutionTime;
54
+ const bestPath = [{ oldPos: -1, lastComponent: void 0 }];
55
+ let newPos = this.extractCommon(bestPath[0], newTokens, oldTokens, 0, options);
56
+ if (bestPath[0].oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
57
+ return done(this.buildValues(bestPath[0].lastComponent, newTokens, oldTokens));
58
+ }
59
+ let minDiagonalToConsider = -Infinity, maxDiagonalToConsider = Infinity;
60
+ const execEditLength = () => {
61
+ for (let diagonalPath = Math.max(minDiagonalToConsider, -editLength); diagonalPath <= Math.min(maxDiagonalToConsider, editLength); diagonalPath += 2) {
62
+ let basePath;
63
+ const removePath = bestPath[diagonalPath - 1], addPath = bestPath[diagonalPath + 1];
64
+ if (removePath) {
65
+ bestPath[diagonalPath - 1] = void 0;
66
+ }
67
+ let canAdd = false;
68
+ if (addPath) {
69
+ const addPathNewPos = addPath.oldPos - diagonalPath;
70
+ canAdd = addPath && 0 <= addPathNewPos && addPathNewPos < newLen;
71
+ }
72
+ const canRemove = removePath && removePath.oldPos + 1 < oldLen;
73
+ if (!canAdd && !canRemove) {
74
+ bestPath[diagonalPath] = void 0;
75
+ continue;
76
+ }
77
+ if (!canRemove || canAdd && removePath.oldPos < addPath.oldPos) {
78
+ basePath = this.addToPath(addPath, true, false, 0, options);
79
+ } else {
80
+ basePath = this.addToPath(removePath, false, true, 1, options);
81
+ }
82
+ newPos = this.extractCommon(basePath, newTokens, oldTokens, diagonalPath, options);
83
+ if (basePath.oldPos + 1 >= oldLen && newPos + 1 >= newLen) {
84
+ return done(this.buildValues(basePath.lastComponent, newTokens, oldTokens)) || true;
85
+ } else {
86
+ bestPath[diagonalPath] = basePath;
87
+ if (basePath.oldPos + 1 >= oldLen) {
88
+ maxDiagonalToConsider = Math.min(maxDiagonalToConsider, diagonalPath - 1);
89
+ }
90
+ if (newPos + 1 >= newLen) {
91
+ minDiagonalToConsider = Math.max(minDiagonalToConsider, diagonalPath + 1);
92
+ }
93
+ }
94
+ }
95
+ editLength++;
96
+ };
97
+ if (callback) {
98
+ (function exec() {
99
+ setTimeout(function() {
100
+ if (editLength > maxEditLength || Date.now() > abortAfterTimestamp) {
101
+ return callback(void 0);
102
+ }
103
+ if (!execEditLength()) {
104
+ exec();
105
+ }
106
+ }, 0);
107
+ })();
108
+ } else {
109
+ while (editLength <= maxEditLength && Date.now() <= abortAfterTimestamp) {
110
+ const ret = execEditLength();
111
+ if (ret) {
112
+ return ret;
113
+ }
114
+ }
115
+ }
116
+ }
117
+ addToPath(path, added, removed, oldPosInc, options) {
118
+ const last = path.lastComponent;
119
+ if (last && !options.oneChangePerToken && last.added === added && last.removed === removed) {
120
+ return {
121
+ oldPos: path.oldPos + oldPosInc,
122
+ lastComponent: { count: last.count + 1, added, removed, previousComponent: last.previousComponent }
123
+ };
124
+ } else {
125
+ return {
126
+ oldPos: path.oldPos + oldPosInc,
127
+ lastComponent: { count: 1, added, removed, previousComponent: last }
128
+ };
129
+ }
130
+ }
131
+ extractCommon(basePath, newTokens, oldTokens, diagonalPath, options) {
132
+ const newLen = newTokens.length, oldLen = oldTokens.length;
133
+ let oldPos = basePath.oldPos, newPos = oldPos - diagonalPath, commonCount = 0;
134
+ while (newPos + 1 < newLen && oldPos + 1 < oldLen && this.equals(oldTokens[oldPos + 1], newTokens[newPos + 1], options)) {
135
+ newPos++;
136
+ oldPos++;
137
+ commonCount++;
138
+ if (options.oneChangePerToken) {
139
+ basePath.lastComponent = { count: 1, previousComponent: basePath.lastComponent, added: false, removed: false };
140
+ }
141
+ }
142
+ if (commonCount && !options.oneChangePerToken) {
143
+ basePath.lastComponent = { count: commonCount, previousComponent: basePath.lastComponent, added: false, removed: false };
144
+ }
145
+ basePath.oldPos = oldPos;
146
+ return newPos;
147
+ }
148
+ equals(left, right, options) {
149
+ if (options.comparator) {
150
+ return options.comparator(left, right);
151
+ } else {
152
+ return left === right || !!options.ignoreCase && left.toLowerCase() === right.toLowerCase();
153
+ }
154
+ }
155
+ removeEmpty(array) {
156
+ const ret = [];
157
+ for (let i = 0; i < array.length; i++) {
158
+ if (array[i]) {
159
+ ret.push(array[i]);
160
+ }
161
+ }
162
+ return ret;
163
+ }
164
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
165
+ castInput(value, options) {
166
+ return value;
167
+ }
168
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
169
+ tokenize(value, options) {
170
+ return Array.from(value);
171
+ }
172
+ join(chars) {
173
+ return chars.join("");
174
+ }
175
+ postProcess(changeObjects, options) {
176
+ return changeObjects;
177
+ }
178
+ get useLongestToken() {
179
+ return false;
180
+ }
181
+ buildValues(lastComponent, newTokens, oldTokens) {
182
+ const components = [];
183
+ let nextComponent;
184
+ while (lastComponent) {
185
+ components.push(lastComponent);
186
+ nextComponent = lastComponent.previousComponent;
187
+ delete lastComponent.previousComponent;
188
+ lastComponent = nextComponent;
189
+ }
190
+ components.reverse();
191
+ const componentLen = components.length;
192
+ let componentPos = 0, newPos = 0, oldPos = 0;
193
+ for (; componentPos < componentLen; componentPos++) {
194
+ const component = components[componentPos];
195
+ if (!component.removed) {
196
+ if (!component.added && this.useLongestToken) {
197
+ let value = newTokens.slice(newPos, newPos + component.count);
198
+ value = value.map(function(value2, i) {
199
+ const oldValue = oldTokens[oldPos + i];
200
+ return oldValue.length > value2.length ? oldValue : value2;
201
+ });
202
+ component.value = this.join(value);
203
+ } else {
204
+ component.value = this.join(newTokens.slice(newPos, newPos + component.count));
205
+ }
206
+ newPos += component.count;
207
+ if (!component.added) {
208
+ oldPos += component.count;
209
+ }
210
+ } else {
211
+ component.value = this.join(oldTokens.slice(oldPos, oldPos + component.count));
212
+ oldPos += component.count;
213
+ }
214
+ }
215
+ return components;
216
+ }
217
+ };
218
+
219
+ // node_modules/diff/libesm/diff/line.js
220
+ var LineDiff = class extends Diff {
221
+ constructor() {
222
+ super(...arguments);
223
+ this.tokenize = tokenize;
224
+ }
225
+ equals(left, right, options) {
226
+ if (options.ignoreWhitespace) {
227
+ if (!options.newlineIsToken || !left.includes("\n")) {
228
+ left = left.trim();
229
+ }
230
+ if (!options.newlineIsToken || !right.includes("\n")) {
231
+ right = right.trim();
232
+ }
233
+ } else if (options.ignoreNewlineAtEof && !options.newlineIsToken) {
234
+ if (left.endsWith("\n")) {
235
+ left = left.slice(0, -1);
236
+ }
237
+ if (right.endsWith("\n")) {
238
+ right = right.slice(0, -1);
239
+ }
240
+ }
241
+ return super.equals(left, right, options);
242
+ }
243
+ };
244
+ var lineDiff = new LineDiff();
245
+ function diffLines(oldStr, newStr, options) {
246
+ return lineDiff.diff(oldStr, newStr, options);
247
+ }
248
+ function tokenize(value, options) {
249
+ if (options.stripTrailingCr) {
250
+ value = value.replace(/\r\n/g, "\n");
251
+ }
252
+ const retLines = [], linesAndNewlines = value.split(/(\n|\r\n)/);
253
+ if (!linesAndNewlines[linesAndNewlines.length - 1]) {
254
+ linesAndNewlines.pop();
255
+ }
256
+ for (let i = 0; i < linesAndNewlines.length; i++) {
257
+ const line = linesAndNewlines[i];
258
+ if (i % 2 && !options.newlineIsToken) {
259
+ retLines[retLines.length - 1] += line;
260
+ } else {
261
+ retLines.push(line);
262
+ }
263
+ }
264
+ return retLines;
265
+ }
266
+
267
+ // node_modules/diff/libesm/patch/create.js
268
+ function needsQuoting(s) {
269
+ for (let i = 0; i < s.length; i++) {
270
+ if (s[i] < " " || s[i] > "~" || s[i] === '"' || s[i] === "\\") {
271
+ return true;
272
+ }
273
+ }
274
+ return false;
275
+ }
276
+ function quoteFileNameIfNeeded(s) {
277
+ if (!needsQuoting(s)) {
278
+ return s;
279
+ }
280
+ let result = '"';
281
+ const bytes = new TextEncoder().encode(s);
282
+ let i = 0;
283
+ while (i < bytes.length) {
284
+ const b = bytes[i];
285
+ if (b === 7) {
286
+ result += "\\a";
287
+ } else if (b === 8) {
288
+ result += "\\b";
289
+ } else if (b === 9) {
290
+ result += "\\t";
291
+ } else if (b === 10) {
292
+ result += "\\n";
293
+ } else if (b === 11) {
294
+ result += "\\v";
295
+ } else if (b === 12) {
296
+ result += "\\f";
297
+ } else if (b === 13) {
298
+ result += "\\r";
299
+ } else if (b === 34) {
300
+ result += '\\"';
301
+ } else if (b === 92) {
302
+ result += "\\\\";
303
+ } else if (b >= 32 && b <= 126) {
304
+ result += String.fromCharCode(b);
305
+ } else {
306
+ result += "\\" + b.toString(8).padStart(3, "0");
307
+ }
308
+ i++;
309
+ }
310
+ result += '"';
311
+ return result;
312
+ }
313
+ var INCLUDE_HEADERS = {
314
+ includeIndex: true,
315
+ includeUnderline: true,
316
+ includeFileHeaders: true
317
+ };
318
+ function structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
319
+ let optionsObj;
320
+ if (!options) {
321
+ optionsObj = {};
322
+ } else if (typeof options === "function") {
323
+ optionsObj = { callback: options };
324
+ } else {
325
+ optionsObj = options;
326
+ }
327
+ if (typeof optionsObj.context === "undefined") {
328
+ optionsObj.context = 4;
329
+ }
330
+ const context = optionsObj.context;
331
+ if (optionsObj.newlineIsToken) {
332
+ throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");
333
+ }
334
+ if (!optionsObj.callback) {
335
+ return diffLinesResultToPatch(diffLines(oldStr, newStr, optionsObj));
336
+ } else {
337
+ const { callback } = optionsObj;
338
+ diffLines(oldStr, newStr, Object.assign(Object.assign({}, optionsObj), { callback: (diff) => {
339
+ const patch = diffLinesResultToPatch(diff);
340
+ callback(patch);
341
+ } }));
342
+ }
343
+ function diffLinesResultToPatch(diff) {
344
+ if (!diff) {
345
+ return;
346
+ }
347
+ diff.push({ value: "", lines: [] });
348
+ function contextLines(lines) {
349
+ return lines.map(function(entry) {
350
+ return " " + entry;
351
+ });
352
+ }
353
+ const hunks = [];
354
+ let oldRangeStart = 0, newRangeStart = 0, curRange = [], oldLine = 1, newLine = 1;
355
+ for (let i = 0; i < diff.length; i++) {
356
+ const current = diff[i], lines = current.lines || splitLines(current.value);
357
+ current.lines = lines;
358
+ if (current.added || current.removed) {
359
+ if (!oldRangeStart) {
360
+ const prev = diff[i - 1];
361
+ oldRangeStart = oldLine;
362
+ newRangeStart = newLine;
363
+ if (prev) {
364
+ curRange = context > 0 ? contextLines(prev.lines.slice(-context)) : [];
365
+ oldRangeStart -= curRange.length;
366
+ newRangeStart -= curRange.length;
367
+ }
368
+ }
369
+ for (const line of lines) {
370
+ curRange.push((current.added ? "+" : "-") + line);
371
+ }
372
+ if (current.added) {
373
+ newLine += lines.length;
374
+ } else {
375
+ oldLine += lines.length;
376
+ }
377
+ } else {
378
+ if (oldRangeStart) {
379
+ if (lines.length <= context * 2 && i < diff.length - 2) {
380
+ for (const line of contextLines(lines)) {
381
+ curRange.push(line);
382
+ }
383
+ } else {
384
+ const contextSize = Math.min(lines.length, context);
385
+ for (const line of contextLines(lines.slice(0, contextSize))) {
386
+ curRange.push(line);
387
+ }
388
+ const hunk = {
389
+ oldStart: oldRangeStart,
390
+ oldLines: oldLine - oldRangeStart + contextSize,
391
+ newStart: newRangeStart,
392
+ newLines: newLine - newRangeStart + contextSize,
393
+ lines: curRange
394
+ };
395
+ hunks.push(hunk);
396
+ oldRangeStart = 0;
397
+ newRangeStart = 0;
398
+ curRange = [];
399
+ }
400
+ }
401
+ oldLine += lines.length;
402
+ newLine += lines.length;
403
+ }
404
+ }
405
+ for (const hunk of hunks) {
406
+ for (let i = 0; i < hunk.lines.length; i++) {
407
+ if (hunk.lines[i].endsWith("\n")) {
408
+ hunk.lines[i] = hunk.lines[i].slice(0, -1);
409
+ } else {
410
+ hunk.lines.splice(i + 1, 0, "\");
411
+ i++;
412
+ }
413
+ }
414
+ }
415
+ return {
416
+ oldFileName,
417
+ newFileName,
418
+ oldHeader,
419
+ newHeader,
420
+ hunks
421
+ };
422
+ }
423
+ }
424
+ function formatPatch(patch, headerOptions) {
425
+ var _a, _b, _c, _d, _e, _f;
426
+ if (!headerOptions) {
427
+ headerOptions = INCLUDE_HEADERS;
428
+ }
429
+ if (Array.isArray(patch)) {
430
+ if (patch.length > 1 && !headerOptions.includeFileHeaders && !patch.every((p) => p.isGit)) {
431
+ throw new Error("Cannot omit file headers on a multi-file patch. (The result would be unparseable; how would a tool trying to apply the patch know which changes are to which file?)");
432
+ }
433
+ return patch.map((p) => formatPatch(p, headerOptions)).join("\n");
434
+ }
435
+ const ret = [];
436
+ if (patch.isGit) {
437
+ headerOptions = INCLUDE_HEADERS;
438
+ if (!patch.oldFileName) {
439
+ throw new Error("oldFileName must be specified for Git patches");
440
+ }
441
+ if (!patch.newFileName) {
442
+ throw new Error("newFileName must be specified for Git patches");
443
+ }
444
+ let gitOldName = patch.oldFileName;
445
+ let gitNewName = patch.newFileName;
446
+ if (patch.isCreate && gitOldName === "/dev/null") {
447
+ gitOldName = gitNewName.replace(/^b\//, "a/");
448
+ } else if (patch.isDelete && gitNewName === "/dev/null") {
449
+ gitNewName = gitOldName.replace(/^a\//, "b/");
450
+ }
451
+ ret.push("diff --git " + quoteFileNameIfNeeded(gitOldName) + " " + quoteFileNameIfNeeded(gitNewName));
452
+ if (patch.isDelete) {
453
+ ret.push("deleted file mode " + ((_a = patch.oldMode) !== null && _a !== void 0 ? _a : "100644"));
454
+ }
455
+ if (patch.isCreate) {
456
+ ret.push("new file mode " + ((_b = patch.newMode) !== null && _b !== void 0 ? _b : "100644"));
457
+ }
458
+ if (patch.oldMode && patch.newMode && !patch.isDelete && !patch.isCreate) {
459
+ ret.push("old mode " + patch.oldMode);
460
+ ret.push("new mode " + patch.newMode);
461
+ }
462
+ if (patch.isRename) {
463
+ ret.push("rename from " + quoteFileNameIfNeeded(((_c = patch.oldFileName) !== null && _c !== void 0 ? _c : "").replace(/^a\//, "")));
464
+ ret.push("rename to " + quoteFileNameIfNeeded(((_d = patch.newFileName) !== null && _d !== void 0 ? _d : "").replace(/^b\//, "")));
465
+ }
466
+ if (patch.isCopy) {
467
+ ret.push("copy from " + quoteFileNameIfNeeded(((_e = patch.oldFileName) !== null && _e !== void 0 ? _e : "").replace(/^a\//, "")));
468
+ ret.push("copy to " + quoteFileNameIfNeeded(((_f = patch.newFileName) !== null && _f !== void 0 ? _f : "").replace(/^b\//, "")));
469
+ }
470
+ } else {
471
+ if (headerOptions.includeIndex && patch.oldFileName == patch.newFileName && patch.oldFileName !== void 0) {
472
+ ret.push("Index: " + patch.oldFileName);
473
+ }
474
+ if (headerOptions.includeUnderline) {
475
+ ret.push("===================================================================");
476
+ }
477
+ }
478
+ const hasHunks = patch.hunks.length > 0;
479
+ if (headerOptions.includeFileHeaders && patch.oldFileName !== void 0 && patch.newFileName !== void 0 && (!patch.isGit || hasHunks)) {
480
+ ret.push("--- " + quoteFileNameIfNeeded(patch.oldFileName) + (patch.oldHeader ? " " + patch.oldHeader : ""));
481
+ ret.push("+++ " + quoteFileNameIfNeeded(patch.newFileName) + (patch.newHeader ? " " + patch.newHeader : ""));
482
+ }
483
+ for (let i = 0; i < patch.hunks.length; i++) {
484
+ const hunk = patch.hunks[i];
485
+ const oldStart = hunk.oldLines === 0 ? hunk.oldStart - 1 : hunk.oldStart;
486
+ const newStart = hunk.newLines === 0 ? hunk.newStart - 1 : hunk.newStart;
487
+ ret.push("@@ -" + oldStart + "," + hunk.oldLines + " +" + newStart + "," + hunk.newLines + " @@");
488
+ for (const line of hunk.lines) {
489
+ ret.push(line);
490
+ }
491
+ }
492
+ return ret.join("\n") + "\n";
493
+ }
494
+ function createTwoFilesPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options) {
495
+ if (typeof options === "function") {
496
+ options = { callback: options };
497
+ }
498
+ if (!(options === null || options === void 0 ? void 0 : options.callback)) {
499
+ const patchObj = structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, options);
500
+ if (!patchObj) {
501
+ return;
502
+ }
503
+ return formatPatch(patchObj, options === null || options === void 0 ? void 0 : options.headerOptions);
504
+ } else {
505
+ const { callback } = options;
506
+ structuredPatch(oldFileName, newFileName, oldStr, newStr, oldHeader, newHeader, Object.assign(Object.assign({}, options), { callback: (patchObj) => {
507
+ if (!patchObj) {
508
+ callback(void 0);
509
+ } else {
510
+ callback(formatPatch(patchObj, options.headerOptions));
511
+ }
512
+ } }));
513
+ }
514
+ }
515
+ function splitLines(text) {
516
+ const hasTrailingNl = text.endsWith("\n");
517
+ const result = text.split("\n").map((line) => line + "\n");
518
+ if (hasTrailingNl) {
519
+ result.pop();
520
+ } else {
521
+ result.push(result.pop().slice(0, -1));
522
+ }
523
+ return result;
524
+ }
525
+
526
+ // src/integrate.ts
527
+ import { readFile, lstat, writeFile } from "node:fs/promises";
528
+ import { join, relative, dirname, resolve } from "node:path";
529
+ var dummy = { workspaceId: "YOUR_WORKSPACE_ID", siteId: "YOUR_SITE_ID", apiBaseUrl: "https://api.userreason.com" };
530
+ async function source(project, path) {
531
+ const rel = relative(project.directory, path);
532
+ if (rel.startsWith("..") || rel === "") throw new Error("Application entry must stay inside the project");
533
+ let current = project.directory;
534
+ for (const part of rel.split("/")) {
535
+ current = join(current, part);
536
+ let entry;
537
+ try {
538
+ entry = await lstat(current);
539
+ } catch (error) {
540
+ if (error.code === "ENOENT") return;
541
+ throw error;
542
+ }
543
+ if (entry.isSymbolicLink()) throw new Error(`Refusing a symlinked application path: ${rel}`);
544
+ }
545
+ const info = await lstat(path);
546
+ if (!info.isFile() || info.size > 2e6)
547
+ throw new Error("Application entry must be a regular file under 2 MB");
548
+ return readFile(path, "utf8");
549
+ }
550
+ function parse(path, text) {
551
+ const ast = import_typescript.default.createSourceFile(
552
+ path,
553
+ text,
554
+ import_typescript.default.ScriptTarget.Latest,
555
+ true,
556
+ /\.(?:jsx?|tsx)$/.test(path) ? import_typescript.default.ScriptKind.TSX : import_typescript.default.ScriptKind.TS
557
+ );
558
+ if (ast.parseDiagnostics?.length)
559
+ return;
560
+ return ast;
561
+ }
562
+ function nodes(ast, test) {
563
+ const result = [];
564
+ const visit = (node) => {
565
+ if (test(node)) result.push(node);
566
+ import_typescript.default.forEachChild(node, visit);
567
+ };
568
+ visit(ast);
569
+ return result;
570
+ }
571
+ function sdkCalls(ast) {
572
+ const bindings = new Set(ast.statements.filter(import_typescript.default.isImportDeclaration).filter((node) => import_typescript.default.isStringLiteral(node.moduleSpecifier) && node.moduleSpecifier.text === "@userreason/js").map((node) => node.importClause?.name?.text).filter((value) => Boolean(value)));
573
+ return nodes(ast, import_typescript.default.isCallExpression).filter((call) => import_typescript.default.isPropertyAccessExpression(call.expression) && call.expression.name.text === "init" && bindings.has(call.expression.expression.getText(ast)));
574
+ }
575
+ function literalInstallation(call) {
576
+ const options = call.arguments[0];
577
+ if (!options || !import_typescript.default.isObjectLiteralExpression(options)) throw new Error("Existing UserReason options are dynamic. Supply --workspace and --site to select this installation explicitly.");
578
+ const values = {};
579
+ for (const property of options.properties) {
580
+ if (import_typescript.default.isSpreadAssignment(property)) throw new Error("Existing UserReason options use a spread. Supply --workspace and --site.");
581
+ if (!import_typescript.default.isPropertyAssignment(property)) continue;
582
+ const name = import_typescript.default.isIdentifier(property.name) || import_typescript.default.isStringLiteral(property.name) ? property.name.text : "";
583
+ if (!["siteId", "workspaceId", "apiBaseUrl"].includes(name)) continue;
584
+ if (!import_typescript.default.isStringLiteral(property.initializer)) throw new Error("Existing UserReason configuration uses variables. Supply --workspace and --site.");
585
+ values[name] = property.initializer.text;
586
+ }
587
+ if (!values.siteId) return;
588
+ return { siteId: values.siteId, apiBaseUrl: values.apiBaseUrl ?? DEFAULT_API, ...values.workspaceId ? { workspaceId: values.workspaceId } : {} };
589
+ }
590
+ async function scanInstallation(project) {
591
+ const candidates = /* @__PURE__ */ new Set();
592
+ for (const folder of ["", "src/"]) {
593
+ for (const name of ["main", "index", "instrumentation-client", "userreason.client", "userreason.generated"]) {
594
+ for (const extension of ["ts", "tsx", "js", "jsx"]) candidates.add(`${folder}${name}.${extension}`);
595
+ }
596
+ }
597
+ if (project.framework === "vanilla") {
598
+ const html = await source(project, join(project.directory, "index.html"));
599
+ for (const match of html?.matchAll(/<script\b([^>]*)>/gi) ?? []) {
600
+ const src = /\bsrc\s*=\s*['"]([^'"]+)['"]/i.exec(match[1])?.[1];
601
+ if (src && /\btype\s*=\s*['"]module['"]/i.test(match[1]) && !/^(?:[a-z]+:|\/\/)|[?#]/i.test(src)) candidates.add(src.replace(/^\//, ""));
602
+ }
603
+ }
604
+ let found;
605
+ for (const candidate of candidates) {
606
+ const path = resolve(project.directory, candidate), text = await source(project, path);
607
+ if (!text) continue;
608
+ const ast = parse(path, text);
609
+ if (!ast) continue;
610
+ for (const call of sdkCalls(ast)) {
611
+ const config = literalInstallation(call);
612
+ if (!config) continue;
613
+ if (found && (found.config.siteId !== config.siteId || found.config.apiBaseUrl !== config.apiBaseUrl)) throw new Error("Multiple UserReason installations found. Supply --workspace and --site.");
614
+ found = { config, path };
615
+ }
616
+ }
617
+ return found;
618
+ }
619
+ async function detectInstallation(project) {
620
+ return (await scanInstallation(project))?.config;
621
+ }
622
+ async function initializationPath(project) {
623
+ return (await scanInstallation(project))?.path;
624
+ }
625
+ function importStatement(project, path, name) {
626
+ const helper = join(
627
+ project.sourceDirectory,
628
+ integrationSource(project.framework, dummy, project.typescript, true).filename
629
+ );
630
+ let specifier = relative(dirname(path), helper).replaceAll("\\", "/");
631
+ if (project.framework !== "vanilla") specifier = specifier.replace(/\.[jt]sx?$/, "");
632
+ if (!specifier.startsWith(".")) specifier = "./" + specifier;
633
+ return `import { ${name} } from '${specifier}';`;
634
+ }
635
+ function insertImport(ast, text, statement) {
636
+ let position = ast.statements[0]?.getStart(ast) ?? 0;
637
+ for (const node of ast.statements) {
638
+ if (import_typescript.default.isImportDeclaration(node) || import_typescript.default.isExpressionStatement(node) && import_typescript.default.isStringLiteral(node.expression))
639
+ position = node.end;
640
+ else break;
641
+ }
642
+ return text.slice(0, position) + (position ? "\n" : "") + statement + "\n" + text.slice(position);
643
+ }
644
+ function wrap(text, node, ast) {
645
+ return text.slice(0, node.getStart(ast)) + `<><UserReasonInit />${node.getText(ast)}</>` + text.slice(node.end);
646
+ }
647
+ function importedBindings(ast) {
648
+ const dom = /* @__PURE__ */ new Set(), factories = /* @__PURE__ */ new Set();
649
+ for (const node of ast.statements) {
650
+ if (!import_typescript.default.isImportDeclaration(node) || !import_typescript.default.isStringLiteral(node.moduleSpecifier) || !["react-dom", "react-dom/client"].includes(node.moduleSpecifier.text))
651
+ continue;
652
+ if (node.importClause?.name) dom.add(node.importClause.name.text);
653
+ const bindings = node.importClause?.namedBindings;
654
+ if (bindings && import_typescript.default.isNamespaceImport(bindings)) dom.add(bindings.name.text);
655
+ if (bindings && import_typescript.default.isNamedImports(bindings)) {
656
+ for (const item of bindings.elements)
657
+ if ((item.propertyName ?? item.name).text === "createRoot") factories.add(item.name.text);
658
+ }
659
+ }
660
+ return { dom, factories };
661
+ }
662
+ function reactRoots(ast) {
663
+ const { dom, factories } = importedBindings(ast);
664
+ const createRoot = (node) => import_typescript.default.isCallExpression(node) && (import_typescript.default.isIdentifier(node.expression) && factories.has(node.expression.text) || import_typescript.default.isPropertyAccessExpression(node.expression) && import_typescript.default.isIdentifier(node.expression.expression) && dom.has(node.expression.expression.text) && node.expression.name.text === "createRoot");
665
+ const creationCount = nodes(ast, import_typescript.default.isCallExpression).filter(createRoot).length;
666
+ const roots = new Set(
667
+ nodes(ast, import_typescript.default.isVariableDeclaration).filter((node) => node.initializer && createRoot(node.initializer) && import_typescript.default.isIdentifier(node.name)).map((node) => node.name.getText(ast))
668
+ );
669
+ const renders = nodes(ast, import_typescript.default.isCallExpression).filter((node) => {
670
+ const member = node.expression;
671
+ return import_typescript.default.isPropertyAccessExpression(member) && member.name.text === "render" && (createRoot(member.expression) || import_typescript.default.isIdentifier(member.expression) && (roots.has(member.expression.text) || !creationCount && dom.has(member.expression.text)));
672
+ });
673
+ if (!creationCount && renders.length > 1) return [];
674
+ return renders.flatMap((node) => node.arguments[0] ? [node.arguments[0]] : []);
675
+ }
676
+ function integrationReferences(ast) {
677
+ const modules = nodes(ast, import_typescript.default.isStringLiteral).filter((literal) => {
678
+ const parent = literal.parent;
679
+ return (import_typescript.default.isImportDeclaration(parent) || import_typescript.default.isExportDeclaration(parent)) && parent.moduleSpecifier === literal || import_typescript.default.isExternalModuleReference(parent) && parent.expression === literal || import_typescript.default.isCallExpression(parent) && parent.arguments[0] === literal && (parent.expression.kind === import_typescript.default.SyntaxKind.ImportKeyword || import_typescript.default.isIdentifier(parent.expression) && parent.expression.text === "require");
680
+ }).map((literal) => literal.text);
681
+ const tags = nodes(ast, (node) => import_typescript.default.isJsxOpeningElement(node) || import_typescript.default.isJsxSelfClosingElement(node)).map((node) => node.tagName.getText(ast));
682
+ const globalAccess = nodes(ast, import_typescript.default.isElementAccessExpression).some((node) => import_typescript.default.isIdentifier(node.expression) && ["window", "globalThis", "self", "global"].includes(node.expression.text) && import_typescript.default.isStringLiteralLike(node.argumentExpression) && node.argumentExpression.text === "UserReason");
683
+ const identifier = nodes(ast, import_typescript.default.isIdentifier).some((node) => {
684
+ if (node.text !== "UserReason") return false;
685
+ const parent = node.parent;
686
+ if ((import_typescript.default.isPropertyAssignment(parent) || import_typescript.default.isPropertyDeclaration(parent) || import_typescript.default.isPropertySignature(parent) || import_typescript.default.isMethodDeclaration(parent) || import_typescript.default.isMethodSignature(parent) || import_typescript.default.isGetAccessorDeclaration(parent) || import_typescript.default.isSetAccessorDeclaration(parent) || import_typescript.default.isJsxAttribute(parent)) && parent.name === node) return false;
687
+ if ((import_typescript.default.isBindingElement(parent) || import_typescript.default.isImportSpecifier(parent)) && parent.propertyName === node) return false;
688
+ if (import_typescript.default.isExportSpecifier(parent) && parent.name === node && parent.propertyName) return false;
689
+ if (import_typescript.default.isLabeledStatement(parent) || import_typescript.default.isBreakStatement(parent) || import_typescript.default.isContinueStatement(parent)) return false;
690
+ return true;
691
+ });
692
+ return { modules, tags, identifier: identifier || globalAccess };
693
+ }
694
+ function existing(text, config = dummy, path = "entry.tsx") {
695
+ const ast = parse(path, text), calls = ast ? sdkCalls(ast) : [];
696
+ if (calls.length) {
697
+ try {
698
+ let first;
699
+ for (const call of calls) {
700
+ const options = literalInstallation(call);
701
+ if (!options) return { status: "manual", reason: "Existing SDK initialization configuration needs review." };
702
+ if (first && (options.siteId !== first.siteId || options.apiBaseUrl !== first.apiBaseUrl) || config !== dummy && (options.workspaceId !== void 0 && config.workspaceId !== void 0 && options.workspaceId !== config.workspaceId || options.siteId !== config.siteId || options.apiBaseUrl !== config.apiBaseUrl))
703
+ return { status: "manual", reason: "Existing SDK initialization targets a different site or API. Review it before replacing its configuration." };
704
+ first = options;
705
+ }
706
+ } catch {
707
+ return { status: "manual", reason: "Existing SDK initialization configuration needs review." };
708
+ }
709
+ return { status: "integrated", reason: "SDK initialization is already present; its configuration is preserved." };
710
+ }
711
+ if (!ast) return;
712
+ const references = integrationReferences(ast);
713
+ if (references.modules.some((name) => /userreason\.generated/.test(name)) && references.tags.includes("UserReasonProvider"))
714
+ return { status: "integrated", reason: "Existing generated initialization is already present." };
715
+ if (references.modules.some((name) => /userreason\.client/.test(name)) && references.tags.includes("UserReasonInit"))
716
+ return { status: "integrated", reason: "Browser initialization is already present." };
717
+ if (references.identifier || references.modules.some((name) => name === "@userreason/js" || /userreason\.(?:generated|client)/.test(name)))
718
+ return { status: "manual", reason: "An existing UserReason integration needs review before editing the entry." };
719
+ }
720
+ async function planIntegration(project, config = dummy) {
721
+ if (project.framework === "next" && project.nextInstrumentation) {
722
+ let directory = project.sourceDirectory;
723
+ for (const folder of ["app", "pages"]) {
724
+ try {
725
+ const entry = await lstat(join(project.directory, folder));
726
+ if (entry.isSymbolicLink()) throw new Error("Refusing a symlinked Next application directory");
727
+ if (entry.isDirectory()) directory = project.directory;
728
+ } catch (error) {
729
+ if (error.code !== "ENOENT") throw error;
730
+ }
731
+ }
732
+ const files = await Promise.all(["ts", "js"].map(async (extension) => {
733
+ const path3 = join(directory, `instrumentation-client.${extension}`);
734
+ return { path: path3, text: await source(project, path3) };
735
+ }));
736
+ const found2 = files.filter((value) => value.text !== void 0);
737
+ if (found2.length > 1) return { status: "manual", reason: "Multiple instrumentation-client files need review." };
738
+ for (const root of ["app/layout.tsx", "app/layout.jsx", "app/layout.js", "pages/_app.tsx", "pages/_app.jsx", "pages/_app.js"]) {
739
+ const rootPath = join(directory, root), rootText = await source(project, rootPath);
740
+ const rootAst = rootText ? parse(rootPath, rootText) : void 0;
741
+ if (rootText && rootAst && integrationReferences(rootAst).modules.some((name2) => /userreason\.(?:generated|client)/.test(name2)))
742
+ return existing(rootText, dummy, rootPath) ?? { status: "manual", reason: "Existing root initialization needs review." };
743
+ }
744
+ const { path: path2, text: text2 } = found2[0] ?? files[project.typescript ? 0 : 1];
745
+ const current2 = text2 ? existing(text2, config, path2) : void 0;
746
+ if (current2) return current2;
747
+ const ast = parse(path2, text2 ?? "");
748
+ if (!ast) return { status: "manual", reason: "Client instrumentation could not be parsed safely." };
749
+ return {
750
+ status: "ready",
751
+ directory: project.directory,
752
+ path: path2,
753
+ before: text2 ?? null,
754
+ after: insertImport(ast, text2 ?? "", `import UserReason from '@userreason/js';
755
+
756
+ ${initializationSource(config)}`),
757
+ description: `Initialize UserReason in ${relative(project.directory, path2)}`
758
+ };
759
+ }
760
+ const candidates = project.framework === "next" ? [
761
+ "app/layout.tsx",
762
+ "app/layout.jsx",
763
+ "app/layout.js",
764
+ "src/app/layout.tsx",
765
+ "src/app/layout.jsx",
766
+ "src/app/layout.js",
767
+ "pages/_app.tsx",
768
+ "pages/_app.jsx",
769
+ "pages/_app.js",
770
+ "src/pages/_app.tsx",
771
+ "src/pages/_app.jsx",
772
+ "src/pages/_app.js"
773
+ ] : project.framework === "vue" ? ["src/main.ts", "src/main.js", "main.ts", "main.js"] : project.framework === "react" ? [
774
+ "src/main.tsx",
775
+ "src/main.jsx",
776
+ "src/index.tsx",
777
+ "src/index.jsx",
778
+ "src/index.js",
779
+ "src/main.js",
780
+ "index.jsx"
781
+ ] : [];
782
+ if (project.framework === "vanilla") {
783
+ const html = await source(project, join(project.directory, "index.html"));
784
+ if (html) {
785
+ const scripts = [...html.matchAll(/<script\b([^>]*)>/gi)].filter((match) => /\btype\s*=\s*['"]module['"]/i.test(match[1])).map((match) => /\bsrc\s*=\s*['"]([^'"]+)['"]/i.exec(match[1])?.[1]).filter((value) => Boolean(value) && !/^(?:[a-z]+:|\/\/)|[?#]/i.test(value));
786
+ if (scripts.length === 1) candidates.push(scripts[0].replace(/^\//, ""));
787
+ }
788
+ }
789
+ const found = [];
790
+ for (const candidate of candidates) {
791
+ const path2 = resolve(project.directory, candidate), text2 = await source(project, path2);
792
+ if (text2 !== void 0) found.push({ path: path2, text: text2 });
793
+ }
794
+ if (found.length !== 1)
795
+ return {
796
+ status: "manual",
797
+ reason: found.length ? "More than one application entry was found. Choose the root and follow the generated integration instructions." : "The application root could not be identified. Follow the generated integration instructions."
798
+ };
799
+ const { path, text } = found[0];
800
+ const name = project.framework === "next" ? "UserReasonInit" : "UserReason";
801
+ const current = existing(text, config, path);
802
+ if (current) return current;
803
+ let after;
804
+ {
805
+ const ast = parse(path, text);
806
+ if (!ast) return { status: "manual", reason: "The application entry could not be parsed safely." };
807
+ if (project.framework !== "next") {
808
+ if (project.framework === "react" && !reactRoots(ast).length)
809
+ return { status: "manual", reason: "The browser entry needs one recognizable React application root." };
810
+ if (project.framework === "vue" && !/\bcreateApp\s*\(/.test(text))
811
+ return { status: "manual", reason: "The Vue entry needs a recognizable createApp call." };
812
+ after = insertImport(ast, text, `import UserReason from '@userreason/js';
813
+
814
+ ${initializationSource(config)}`);
815
+ } else if (project.framework === "next" && /[/\\]app[/\\]layout\./.test(path)) {
816
+ const bodies = nodes(ast, import_typescript.default.isJsxElement).filter(
817
+ (node) => node.openingElement.tagName.getText(ast) === "body"
818
+ );
819
+ if (bodies.length !== 1)
820
+ return { status: "manual", reason: "The Next.js root layout needs one recognizable <body> element." };
821
+ const position = bodies[0].openingElement.end;
822
+ after = text.slice(0, position) + "\n<UserReasonInit />" + text.slice(position);
823
+ after = insertImport(ast, after, importStatement(project, path, name));
824
+ } else {
825
+ let roots;
826
+ {
827
+ const exports = nodes(ast, import_typescript.default.isFunctionDeclaration).filter(
828
+ (node) => node.modifiers?.some((modifier) => modifier.kind === import_typescript.default.SyntaxKind.DefaultKeyword)
829
+ );
830
+ const returns = exports.length === 1 ? nodes(exports[0], import_typescript.default.isReturnStatement).filter((node) => node.expression) : [];
831
+ roots = returns.length === 1 && returns[0]?.expression ? [returns[0].expression] : [];
832
+ }
833
+ roots = roots.map((root) => import_typescript.default.isParenthesizedExpression(root) ? root.expression : root);
834
+ if (!roots.length || roots.some(
835
+ (root) => !(import_typescript.default.isJsxElement(root) || import_typescript.default.isJsxSelfClosingElement(root) || import_typescript.default.isJsxFragment(root))
836
+ ))
837
+ return {
838
+ status: "manual",
839
+ reason: "Automatic integration needs recognizable JSX renders on one application root."
840
+ };
841
+ after = text;
842
+ for (const root of roots.sort((left, right) => right.pos - left.pos)) after = wrap(after, root, ast);
843
+ after = insertImport(ast, after, importStatement(project, path, name));
844
+ }
845
+ }
846
+ return {
847
+ status: "ready",
848
+ directory: project.directory,
849
+ path,
850
+ before: text,
851
+ after,
852
+ description: `Add ${name} initialization to ${relative(project.directory, path)}`,
853
+ legacyNext: project.framework === "next"
854
+ };
855
+ }
856
+ async function applyIntegration(plan) {
857
+ if (plan.status !== "ready") return void 0;
858
+ if (plan.before === null) {
859
+ if (await source({ directory: plan.directory }, plan.path) !== void 0)
860
+ throw new Error("The client instrumentation entry changed after the preview. Rerun to review it again.");
861
+ await writeFile(plan.path, plan.after, { flag: "wx", mode: 420 });
862
+ return void 0;
863
+ }
864
+ const info = await lstat(plan.path);
865
+ if (!info.isFile() || info.isSymbolicLink() || await source({ directory: plan.directory }, plan.path) !== plan.before)
866
+ throw new Error("The application entry changed after the preview. Rerun to review it again.");
867
+ await writeFile(plan.path, plan.after);
868
+ }
869
+ function integrationPreview(plan) {
870
+ if (plan.status !== "ready") return plan.reason;
871
+ const path = relative(plan.directory, plan.path);
872
+ return createTwoFilesPatch(path, path, plan.before ?? "", plan.after, "", "", { context: 1 }).trim();
873
+ }
874
+ function integrationAdditions(plan) {
875
+ if (plan.status !== "ready") return void 0;
876
+ const added = [];
877
+ let line = 0, cursor = 0;
878
+ for (const part of diffLines(plan.before ?? "", plan.after)) {
879
+ if (part.removed) continue;
880
+ if (part.added) {
881
+ const lines = part.value.split("\n");
882
+ if (lines[lines.length - 1] === "") lines.pop();
883
+ const shown = lines.filter((value) => value.trim());
884
+ if (shown.length && !line) line = cursor + 1 + lines.findIndex((value) => value.trim());
885
+ added.push(...shown);
886
+ }
887
+ cursor += part.count ?? 0;
888
+ }
889
+ return { path: relative(plan.directory, plan.path), line, added, created: plan.before === null };
890
+ }
891
+
892
+ export {
893
+ detectInstallation,
894
+ initializationPath,
895
+ planIntegration,
896
+ applyIntegration,
897
+ integrationPreview,
898
+ integrationAdditions
899
+ };