@bitvea/feedback-toolbar 0.0.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.
Files changed (176) hide show
  1. package/README.md +262 -0
  2. package/dist/FeedbackPanel.d.ts +117 -0
  3. package/dist/FeedbackPanel.d.ts.map +1 -0
  4. package/dist/FeedbackPanel.js +622 -0
  5. package/dist/FeedbackPanel.js.map +1 -0
  6. package/dist/FeedbackToolbar.d.ts +54 -0
  7. package/dist/FeedbackToolbar.d.ts.map +1 -0
  8. package/dist/FeedbackToolbar.js +1149 -0
  9. package/dist/FeedbackToolbar.js.map +1 -0
  10. package/dist/InboxSheet.d.ts +80 -0
  11. package/dist/InboxSheet.d.ts.map +1 -0
  12. package/dist/InboxSheet.js +580 -0
  13. package/dist/InboxSheet.js.map +1 -0
  14. package/dist/PinLayer.d.ts +59 -0
  15. package/dist/PinLayer.d.ts.map +1 -0
  16. package/dist/PinLayer.js +1702 -0
  17. package/dist/PinLayer.js.map +1 -0
  18. package/dist/RichText.d.ts +6 -0
  19. package/dist/RichText.d.ts.map +1 -0
  20. package/dist/RichText.js +135 -0
  21. package/dist/RichText.js.map +1 -0
  22. package/dist/browser/entry.d.ts +3 -0
  23. package/dist/browser/entry.d.ts.map +1 -0
  24. package/dist/browser/entry.js +13 -0
  25. package/dist/browser/entry.js.map +1 -0
  26. package/dist/browser/mount.d.ts +37 -0
  27. package/dist/browser/mount.d.ts.map +1 -0
  28. package/dist/browser/mount.js +150 -0
  29. package/dist/browser/mount.js.map +1 -0
  30. package/dist/browser/toolbar.js +38 -0
  31. package/dist/browser/toolbar.js.map +7 -0
  32. package/dist/capture.d.ts +58 -0
  33. package/dist/capture.d.ts.map +1 -0
  34. package/dist/capture.js +98 -0
  35. package/dist/capture.js.map +1 -0
  36. package/dist/cli/bin.d.ts +3 -0
  37. package/dist/cli/bin.d.ts.map +1 -0
  38. package/dist/cli/bin.js +196 -0
  39. package/dist/cli/bin.js.map +1 -0
  40. package/dist/cli/fs.d.ts +16 -0
  41. package/dist/cli/fs.d.ts.map +1 -0
  42. package/dist/cli/fs.js +33 -0
  43. package/dist/cli/fs.js.map +1 -0
  44. package/dist/cli/init.d.ts +75 -0
  45. package/dist/cli/init.d.ts.map +1 -0
  46. package/dist/cli/init.js +266 -0
  47. package/dist/cli/init.js.map +1 -0
  48. package/dist/cli/register.d.ts +55 -0
  49. package/dist/cli/register.d.ts.map +1 -0
  50. package/dist/cli/register.js +84 -0
  51. package/dist/cli/register.js.map +1 -0
  52. package/dist/cli/registration.d.ts +37 -0
  53. package/dist/cli/registration.d.ts.map +1 -0
  54. package/dist/cli/registration.js +69 -0
  55. package/dist/cli/registration.js.map +1 -0
  56. package/dist/cli/strategies.d.ts +98 -0
  57. package/dist/cli/strategies.d.ts.map +1 -0
  58. package/dist/cli/strategies.js +198 -0
  59. package/dist/cli/strategies.js.map +1 -0
  60. package/dist/client.d.ts +59 -0
  61. package/dist/client.d.ts.map +1 -0
  62. package/dist/client.js +113 -0
  63. package/dist/client.js.map +1 -0
  64. package/dist/config.d.ts +92 -0
  65. package/dist/config.d.ts.map +1 -0
  66. package/dist/config.js +65 -0
  67. package/dist/config.js.map +1 -0
  68. package/dist/deepLink.d.ts +86 -0
  69. package/dist/deepLink.d.ts.map +1 -0
  70. package/dist/deepLink.js +185 -0
  71. package/dist/deepLink.js.map +1 -0
  72. package/dist/fontFace.d.ts +70 -0
  73. package/dist/fontFace.d.ts.map +1 -0
  74. package/dist/fontFace.js +88 -0
  75. package/dist/fontFace.js.map +1 -0
  76. package/dist/gating.d.ts +32 -0
  77. package/dist/gating.d.ts.map +1 -0
  78. package/dist/gating.js +49 -0
  79. package/dist/gating.js.map +1 -0
  80. package/dist/icons.d.ts +68 -0
  81. package/dist/icons.d.ts.map +1 -0
  82. package/dist/icons.js +97 -0
  83. package/dist/icons.js.map +1 -0
  84. package/dist/index.d.ts +14 -0
  85. package/dist/index.d.ts.map +1 -0
  86. package/dist/index.js +68 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/markdown.d.ts +59 -0
  89. package/dist/markdown.d.ts.map +1 -0
  90. package/dist/markdown.js +130 -0
  91. package/dist/markdown.js.map +1 -0
  92. package/dist/motion.d.ts +178 -0
  93. package/dist/motion.d.ts.map +1 -0
  94. package/dist/motion.js +302 -0
  95. package/dist/motion.js.map +1 -0
  96. package/dist/preferences.d.ts +71 -0
  97. package/dist/preferences.d.ts.map +1 -0
  98. package/dist/preferences.js +195 -0
  99. package/dist/preferences.js.map +1 -0
  100. package/dist/rasterise.d.ts +45 -0
  101. package/dist/rasterise.d.ts.map +1 -0
  102. package/dist/rasterise.js +490 -0
  103. package/dist/rasterise.js.map +1 -0
  104. package/dist/selector.d.ts +19 -0
  105. package/dist/selector.d.ts.map +1 -0
  106. package/dist/selector.js +142 -0
  107. package/dist/selector.js.map +1 -0
  108. package/dist/session.d.ts +19 -0
  109. package/dist/session.d.ts.map +1 -0
  110. package/dist/session.js +36 -0
  111. package/dist/session.js.map +1 -0
  112. package/dist/shortcut.d.ts +88 -0
  113. package/dist/shortcut.d.ts.map +1 -0
  114. package/dist/shortcut.js +149 -0
  115. package/dist/shortcut.js.map +1 -0
  116. package/dist/tokens.d.ts +709 -0
  117. package/dist/tokens.d.ts.map +1 -0
  118. package/dist/tokens.js +749 -0
  119. package/dist/tokens.js.map +1 -0
  120. package/dist/vendor/types/account.d.ts +26 -0
  121. package/dist/vendor/types/account.d.ts.map +1 -0
  122. package/dist/vendor/types/account.js +23 -0
  123. package/dist/vendor/types/account.js.map +1 -0
  124. package/dist/vendor/types/apiKey.d.ts +38 -0
  125. package/dist/vendor/types/apiKey.d.ts.map +1 -0
  126. package/dist/vendor/types/apiKey.js +36 -0
  127. package/dist/vendor/types/apiKey.js.map +1 -0
  128. package/dist/vendor/types/auth.d.ts +10 -0
  129. package/dist/vendor/types/auth.d.ts.map +1 -0
  130. package/dist/vendor/types/auth.js +15 -0
  131. package/dist/vendor/types/auth.js.map +1 -0
  132. package/dist/vendor/types/entity.d.ts +74 -0
  133. package/dist/vendor/types/entity.d.ts.map +1 -0
  134. package/dist/vendor/types/entity.js +111 -0
  135. package/dist/vendor/types/entity.js.map +1 -0
  136. package/dist/vendor/types/feedback.d.ts +285 -0
  137. package/dist/vendor/types/feedback.d.ts.map +1 -0
  138. package/dist/vendor/types/feedback.js +286 -0
  139. package/dist/vendor/types/feedback.js.map +1 -0
  140. package/dist/vendor/types/index.d.ts +14 -0
  141. package/dist/vendor/types/index.d.ts.map +1 -0
  142. package/dist/vendor/types/index.js +16 -0
  143. package/dist/vendor/types/index.js.map +1 -0
  144. package/dist/vendor/types/invite.d.ts +42 -0
  145. package/dist/vendor/types/invite.d.ts.map +1 -0
  146. package/dist/vendor/types/invite.js +30 -0
  147. package/dist/vendor/types/invite.js.map +1 -0
  148. package/dist/vendor/types/mcpToken.d.ts +38 -0
  149. package/dist/vendor/types/mcpToken.d.ts.map +1 -0
  150. package/dist/vendor/types/mcpToken.js +39 -0
  151. package/dist/vendor/types/mcpToken.js.map +1 -0
  152. package/dist/vendor/types/organization.d.ts +13 -0
  153. package/dist/vendor/types/organization.d.ts.map +1 -0
  154. package/dist/vendor/types/organization.js +12 -0
  155. package/dist/vendor/types/organization.js.map +1 -0
  156. package/dist/vendor/types/project.d.ts +73 -0
  157. package/dist/vendor/types/project.d.ts.map +1 -0
  158. package/dist/vendor/types/project.js +117 -0
  159. package/dist/vendor/types/project.js.map +1 -0
  160. package/dist/vendor/types/role.d.ts +12 -0
  161. package/dist/vendor/types/role.d.ts.map +1 -0
  162. package/dist/vendor/types/role.js +20 -0
  163. package/dist/vendor/types/role.js.map +1 -0
  164. package/dist/vendor/types/roleDefinition.d.ts +61 -0
  165. package/dist/vendor/types/roleDefinition.d.ts.map +1 -0
  166. package/dist/vendor/types/roleDefinition.js +45 -0
  167. package/dist/vendor/types/roleDefinition.js.map +1 -0
  168. package/dist/vendor/types/team.d.ts +25 -0
  169. package/dist/vendor/types/team.d.ts.map +1 -0
  170. package/dist/vendor/types/team.js +17 -0
  171. package/dist/vendor/types/team.js.map +1 -0
  172. package/dist/vendor/types/util.d.ts +39 -0
  173. package/dist/vendor/types/util.d.ts.map +1 -0
  174. package/dist/vendor/types/util.js +31 -0
  175. package/dist/vendor/types/util.js.map +1 -0
  176. package/package.json +59 -0
@@ -0,0 +1,266 @@
1
+ // `npx @bitvea/feedback-toolbar init` - the whole codemod, as one pure
2
+ // function over an InstallerFs so it can be driven by tests against a tree
3
+ // they control.
4
+ //
5
+ // Two properties matter more than anything else here, because this edits
6
+ // somebody else's repository:
7
+ //
8
+ // 1. Re-running changes nothing. Every step detects its own prior work
9
+ // rather than blindly appending, so the second run reports "already
10
+ // installed" and writes no file.
11
+ // 2. It never leaves a half-install. Everything that can fail is computed
12
+ // before anything is written, so a missing root layout ends the run with
13
+ // an untouched repository and a message saying what to do by hand.
14
+ //
15
+ // What changed after #11 was reopened: this command no longer pretends to be
16
+ // where registration happens. `init` runs on a laptop, where VERCEL_URL does
17
+ // not exist, so it had nothing to register and said nothing about it - a real
18
+ // install registered silently nothing. It now writes a `prebuild` script that
19
+ // runs `feedback-toolbar register`, so registration happens in the build,
20
+ // where those variables do exist. Registering here too is a convenience for
21
+ // the developer who passed both flags, not the mechanism.
22
+ //
23
+ // And what changed after it reopened a second time: NOTHING THIS COMMAND
24
+ // WRITES MAY DEPEND ON A GITIGNORED FILE. The install used to put the backend
25
+ // URL in `.env.local` alone, which create-next-app gitignores - so the
26
+ // prebuild step reached the build with no URL and skipped itself (one warning
27
+ // line, exit 0, because `--optional` must never fail a deploy), and the
28
+ // deployed toolbar had no URL either and rendered nothing. Two silent
29
+ // failures from one gitignored file. The URL is a public value, so it now
30
+ // goes where it survives a commit: into the injected component, and into the
31
+ // prebuild command in package.json. The only thing left for a developer to
32
+ // set by hand is the deploy KEY, which is a secret and belongs in the build
33
+ // environment - and the notes below say so explicitly.
34
+ import { API_URL_ENV_VAR } from "../config.js";
35
+ import { joinPath } from "./fs.js";
36
+ export { API_URL_ENV_VAR };
37
+ import { DEFAULT_SCRIPT_ENVIRONMENT, InjectionError, PACKAGE_NAME, SCRIPT_BUNDLE_PATH, STRATEGIES, } from "./strategies.js";
38
+ import { API_KEY_ENV_VAR, inferProjectUrl, register } from "./register.js";
39
+ const TOOLBAR_VERSION = "^0.0.1";
40
+ const ENV_FILES = [".env.local", ".env"];
41
+ /**
42
+ * The command wired into the host app's build, and the marker that says it is
43
+ * already wired. `--optional` is load-bearing: see the comment on
44
+ * `wirePrebuild` for why a registration failure must not fail a deploy.
45
+ */
46
+ export const REGISTER_COMMAND = "feedback-toolbar register";
47
+ /**
48
+ * The prebuild line, with the backend URL BAKED IN.
49
+ *
50
+ * This is the second half of #11's reopened acceptance criterion, and the
51
+ * reason it reopened twice. `register` resolves its backend URL from a flag or
52
+ * from the build environment - and the build environment does not have it:
53
+ * `init` writes `NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL` into `.env.local`,
54
+ * which create-next-app gitignores, and a plain node process does not load a
55
+ * dotenv file anyway. So on a real Vercel build the prebuild step threw "no
56
+ * backend URL", `--optional` turned that into one warning line, and no project
57
+ * ever appeared.
58
+ *
59
+ * package.json IS committed, so putting the URL here is what carries it into
60
+ * the build. It is a public URL, not a credential; the key still comes from
61
+ * `FEEDBACK_TOOLBAR_API_KEY` in the build environment and from nowhere else.
62
+ */
63
+ export function prebuildScript(apiUrl) {
64
+ return `${REGISTER_COMMAND} --optional --api-url ${apiUrl}`;
65
+ }
66
+ export async function init(fs, options) {
67
+ const { root, apiUrl } = options;
68
+ const env = options.env ?? {};
69
+ const strategies = options.strategies ?? STRATEGIES;
70
+ const packageJsonPath = joinPath(root, "package.json");
71
+ if (!fs.exists(packageJsonPath)) {
72
+ throw new InjectionError(`no package.json at ${root}. Run this from the root of the app you want to install into.`);
73
+ }
74
+ const pkg = JSON.parse(fs.readFile(packageJsonPath));
75
+ // Resolved before the injection, because the script-tag strategy writes it
76
+ // into the markup and refuses to install without it.
77
+ const projectUrl = options.projectUrl ?? inferProjectUrl(env);
78
+ // Find the injection site FIRST. It is the only step that can fail for a
79
+ // reason the developer has to fix, and finding out after writing package.json
80
+ // and .env.local is exactly the half-install this must not produce.
81
+ const strategy = strategies.find((s) => s.detect(fs, root) !== null);
82
+ if (!strategy) {
83
+ throw new InjectionError([
84
+ "could not find a root layout to inject the toolbar into.",
85
+ `Tried: ${strategies.map((s) => s.label).join(", ")}.`,
86
+ "Nothing was changed. Add the provider by hand instead:",
87
+ "",
88
+ ` import { FeedbackToolbar } from "${PACKAGE_NAME}";`,
89
+ " // ...then render <FeedbackToolbar /> inside <body> in your root layout.",
90
+ ].join("\n"));
91
+ }
92
+ const site = strategy.detect(fs, root);
93
+ const layoutSource = fs.readFile(site.path);
94
+ const alreadyInjected = strategy.isInstalled(layoutSource);
95
+ // Computed before any write: `inject` throws rather than returning a
96
+ // half-edited file, and this is where that throw has to happen.
97
+ const injected = alreadyInjected
98
+ ? layoutSource
99
+ : strategy.inject(layoutSource, {
100
+ apiUrl,
101
+ projectUrl,
102
+ environment: options.environment,
103
+ });
104
+ // A script-tag install has no dependency to add, no env file to read at
105
+ // runtime, and no build to hook - the toolbar is a file served from the
106
+ // backend and configured from the tag's own attributes. Skipping those
107
+ // steps is the whole difference between the two delivery routes.
108
+ const scriptTag = strategy.delivery === "script-tag";
109
+ const changes = [];
110
+ const notes = [];
111
+ let packageJsonChanged = false;
112
+ // 1. Dependency.
113
+ if (!scriptTag) {
114
+ const deps = pkg.dependencies ?? {};
115
+ if (!deps[PACKAGE_NAME]) {
116
+ pkg.dependencies = { ...deps, [PACKAGE_NAME]: TOOLBAR_VERSION };
117
+ packageJsonChanged = true;
118
+ changes.push(`added ${PACKAGE_NAME} to dependencies`);
119
+ }
120
+ }
121
+ // 2. Env var, in whichever env file already exists, else .env.local.
122
+ //
123
+ // A local-development convenience, and only that. It is deliberately NOT
124
+ // how the deployed toolbar finds the backend: `.env.local` is gitignored
125
+ // by create-next-app, so it never reaches a build, which is why the URL
126
+ // is written into the injected component and into the prebuild command
127
+ // as well. Kept because a developer running `next dev` before their
128
+ // first deploy expects the variable to be there.
129
+ if (!scriptTag) {
130
+ const envPath = ENV_FILES.map((f) => joinPath(root, f)).find((p) => fs.exists(p));
131
+ const targetEnvPath = envPath ?? joinPath(root, ENV_FILES[0]);
132
+ const existingEnv = envPath ? fs.readFile(envPath) : "";
133
+ if (!existingEnv.includes(`${API_URL_ENV_VAR}=`)) {
134
+ const separator = existingEnv.length === 0 || existingEnv.endsWith("\n") ? "" : "\n";
135
+ fs.writeFile(targetEnvPath, `${existingEnv}${separator}${API_URL_ENV_VAR}=${apiUrl}\n`);
136
+ changes.push(`wrote ${API_URL_ENV_VAR} to ${targetEnvPath} (local development; the ` +
137
+ "deployed toolbar reads the URL from the component it injected)");
138
+ }
139
+ }
140
+ // 3. The provider.
141
+ if (!alreadyInjected) {
142
+ fs.writeFile(site.path, injected);
143
+ const what = scriptTag
144
+ ? `injected the ${SCRIPT_BUNDLE_PATH} script tag into the ${site.description}`
145
+ : `injected <FeedbackToolbar /> into the ${site.description}`;
146
+ changes.push(what);
147
+ if (scriptTag) {
148
+ // The gate cannot be resolved from a static file - absent means off - so
149
+ // the tag has to declare one or it mounts nothing. Written rather than
150
+ // left to the developer, and said out loud here because it is the one
151
+ // value in the install that decides whether their own customers see a
152
+ // feedback widget.
153
+ const environment = options.environment ?? DEFAULT_SCRIPT_ENVIRONMENT;
154
+ notes.push(`the tag declares data-environment="${environment}", which is what makes it mount. ` +
155
+ "Change it to production on any page real customers reach, or re-run with " +
156
+ "--environment production.");
157
+ }
158
+ }
159
+ // 4. The build hook. This is the step #11 was reopened for: without it,
160
+ // nothing ever registers the project except a developer who happened to
161
+ // pass two flags by hand.
162
+ if (!scriptTag) {
163
+ const wired = wirePrebuild(pkg, apiUrl);
164
+ if (wired) {
165
+ packageJsonChanged = true;
166
+ changes.push(wired);
167
+ }
168
+ }
169
+ if (packageJsonChanged) {
170
+ fs.writeFile(packageJsonPath, `${JSON.stringify(pkg, null, 2)}\n`);
171
+ }
172
+ // 5. Register the project, if this machine happens to know enough to. On a
173
+ // developer's laptop it usually does not, and that is fine now: step 4
174
+ // put the command in the build, where the URL and the key live.
175
+ const apiKey = options.apiKey ?? env[API_KEY_ENV_VAR];
176
+ if (!projectUrl || !apiKey) {
177
+ notes.push(registrationDeferredNote({ projectUrl, apiKey, scriptTag }));
178
+ }
179
+ else {
180
+ try {
181
+ const outcome = await register({
182
+ apiUrl,
183
+ apiKey,
184
+ projectUrl,
185
+ packageName: pkg.name,
186
+ env,
187
+ registerProject: options.registerProject,
188
+ });
189
+ if (outcome.created) {
190
+ changes.push(`registered ${projectUrl} as a project`);
191
+ }
192
+ else {
193
+ notes.push(`${projectUrl} was already registered`);
194
+ }
195
+ }
196
+ catch (err) {
197
+ // A registration that did not go through is not a failed install: the
198
+ // code is in place and the next build registers. Say so and carry on.
199
+ notes.push(`could not register ${projectUrl} (${String(err)}); it will register on the next build`);
200
+ }
201
+ }
202
+ return {
203
+ changes,
204
+ notes,
205
+ alreadyInstalled: changes.length === 0,
206
+ strategy: strategy.id,
207
+ delivery: scriptTag ? "script-tag" : "npm-package",
208
+ };
209
+ }
210
+ /**
211
+ * Add `feedback-toolbar register` to the host app's `prebuild`, once.
212
+ *
213
+ * npm, pnpm and yarn all run `prebuild` before `build`, which is why the hook
214
+ * goes there rather than into `build` itself: appending to somebody's build
215
+ * command changes the command they debug, and prepending to it changes what
216
+ * their CI logs look like at the top. `prebuild` is the slot the package
217
+ * managers reserve for exactly this.
218
+ *
219
+ * `--optional` is not decoration. This runs on every deploy of somebody
220
+ * else's product forever, and a feedback tool that fails a deploy because our
221
+ * backend was slow, or because a key was rotated, has done far more damage
222
+ * than the missing project row it was trying to create.
223
+ *
224
+ * Refusing to add it twice is the same idempotency rule as everything else
225
+ * here, and it has to survive the developer having edited the line: the check
226
+ * is for the command, not for the exact string this wrote. Like every other
227
+ * step, a second run says nothing rather than reporting a non-change.
228
+ *
229
+ * Returns the line to report, or undefined when there was nothing to do.
230
+ */
231
+ function wirePrebuild(pkg, apiUrl) {
232
+ const scripts = pkg.scripts ?? {};
233
+ const existing = scripts.prebuild;
234
+ const line = prebuildScript(apiUrl);
235
+ if (existing === undefined) {
236
+ pkg.scripts = { ...scripts, prebuild: line };
237
+ return `added a "prebuild" script that registers this deployment on every build: ${line}`;
238
+ }
239
+ if (existing.includes(REGISTER_COMMAND))
240
+ return undefined;
241
+ // Their command first, ours after it, joined rather than replaced: the
242
+ // existing prebuild is somebody's build prerequisite and must still run,
243
+ // and must still be the thing that fails the build if it fails.
244
+ pkg.scripts = { ...scripts, prebuild: `${existing} && ${line}` };
245
+ return (`edited the existing "prebuild" script to also register this deployment: ` +
246
+ `${existing} && ${line}`);
247
+ }
248
+ /** Why registration did not happen here, and what will do it instead. */
249
+ function registrationDeferredNote(input) {
250
+ if (input.scriptTag) {
251
+ // No build to hook, so nothing will do this on its own. #17's answer: run
252
+ // the command once, from a terminal or CI, where a key belongs.
253
+ return ("a script-tag install has no build step, so nothing registers the project for you. " +
254
+ `Run it once: npx ${PACKAGE_NAME} register --project-url <url> --api-key <key>`);
255
+ }
256
+ if (!input.apiKey) {
257
+ return ("no deploy key here, so nothing was registered yet. The prebuild step will do it " +
258
+ `on the next build, and it needs one thing from you: set ${API_KEY_ENV_VAR} in your ` +
259
+ "hosting provider's environment variables (dashboard > Settings > Deploy keys). " +
260
+ "The backend URL is already baked into the prebuild command and into the injected " +
261
+ "component, so there is nothing else to configure.");
262
+ }
263
+ return ("this machine does not report a deployment URL, so nothing was registered yet - " +
264
+ "the prebuild step will do it on the next build, where VERCEL_URL is set");
265
+ }
266
+ //# sourceMappingURL=init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ import { type RegisterProjectTransport } from "./registration.js";
2
+ /** Where `--api-url` falls back to. The public name is what a build already has. */
3
+ export declare const API_URL_ENV_VARS: readonly ["NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL", "FEEDBACK_TOOLBAR_API_URL"];
4
+ /**
5
+ * Where `--api-key` falls back to, and the only place a key is ever read from.
6
+ * Never a file, never a `data-` attribute, never an argument this CLI writes
7
+ * down: a deploy key's whole power is creating projects, so it lives in the
8
+ * build environment and nowhere else.
9
+ */
10
+ export declare const API_KEY_ENV_VAR = "FEEDBACK_TOOLBAR_API_KEY";
11
+ export interface RegisterOptions {
12
+ /** `--api-url`, else one of API_URL_ENV_VARS. */
13
+ apiUrl?: string | undefined;
14
+ /** `--api-key`, else API_KEY_ENV_VAR. */
15
+ apiKey?: string | undefined;
16
+ /** `--project-url`, else whatever the build environment reports. */
17
+ projectUrl?: string | undefined;
18
+ /** The host app's package name, as a fallback for naming the project. */
19
+ packageName?: string | undefined;
20
+ /** The build environment. Explicit so this stays a pure function. */
21
+ env: Record<string, string | undefined>;
22
+ /** Injected so tests do not reach the network. */
23
+ registerProject?: RegisterProjectTransport;
24
+ }
25
+ export interface RegisterOutcome {
26
+ projectUrl: string;
27
+ /** False when the URL was already registered, which is a success. */
28
+ created: boolean;
29
+ /** What the backend called it, when it said. */
30
+ projectName?: string | undefined;
31
+ }
32
+ /**
33
+ * Register the deployment, or confirm it is already registered.
34
+ *
35
+ * Idempotent because the backend is: the second call with the same normalised
36
+ * URL is a no-op that answers `created: false`. That is what makes this safe
37
+ * as a prebuild step, which runs on every deploy forever.
38
+ *
39
+ * Throws `RegistrationError` with a message naming the missing piece when it
40
+ * cannot proceed. It never guesses: a registration under a URL nobody meant
41
+ * creates a project that collects feedback from a page that does not exist.
42
+ */
43
+ export declare function register(options: RegisterOptions): Promise<RegisterOutcome>;
44
+ /**
45
+ * The URL to register the project under, from the build environment.
46
+ *
47
+ * Same precedence as the toolbar's own `resolveConfig`, and that agreement is
48
+ * the point: the toolbar reports this value back at runtime, so if the two
49
+ * picked differently the lookup would miss on every preview deployment. The
50
+ * production hostname wins because a preview URL changes on every commit, and
51
+ * keying on it would create a project per deploy instead of collecting a
52
+ * project's feedback in one place.
53
+ */
54
+ export declare function inferProjectUrl(env: Record<string, string | undefined>): string | undefined;
55
+ //# sourceMappingURL=register.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../src/cli/register.ts"],"names":[],"mappings":"AAeA,OAAO,EAGL,KAAK,wBAAwB,EAC9B,MAAM,gBAAgB,CAAC;AAExB,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,+EAGnB,CAAC;AAEX;;;;;GAKG;AACH,eAAO,MAAM,eAAe,6BAA6B,CAAC;AAE1D,MAAM,WAAW,eAAe;IAC9B,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,yCAAyC;IACzC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,yEAAyE;IACzE,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,qEAAqE;IACrE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACxC,kDAAkD;IAClD,eAAe,CAAC,EAAE,wBAAwB,CAAC;CAC5C;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,qEAAqE;IACrE,OAAO,EAAE,OAAO,CAAC;IACjB,gDAAgD;IAChD,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,eAAe,CAAC,CAyC1B;AAED;;;;;;;;;GASG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GACtC,MAAM,GAAG,SAAS,CAGpB"}
@@ -0,0 +1,84 @@
1
+ // `feedback-toolbar register` - make sure this deployment exists as a project.
2
+ //
3
+ // A first-class command, not a step inside `init`, and issues #11 and #17 both
4
+ // needed it to be:
5
+ //
6
+ // - #11 reopened because `init` runs on a laptop, where VERCEL_URL does not
7
+ // exist, so the project URL was undefined and registration was silently
8
+ // skipped. The fix is to register where those variables DO exist: in the
9
+ // build. `init` wires this command in as a prebuild step.
10
+ // - #17 added the script-tag install, which has no build to hook at all. Its
11
+ // project is registered by running this command once from a terminal or CI.
12
+ //
13
+ // Both callers want the same three things: resolve where the values come from,
14
+ // call the one registration service, and say clearly what happened. None of
15
+ // them wants a second opinion on what a valid URL is - see ./registration.ts.
16
+ import { RegistrationError, postRegistration, } from "./registration.js";
17
+ /** Where `--api-url` falls back to. The public name is what a build already has. */
18
+ export const API_URL_ENV_VARS = [
19
+ "NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL",
20
+ "FEEDBACK_TOOLBAR_API_URL",
21
+ ];
22
+ /**
23
+ * Where `--api-key` falls back to, and the only place a key is ever read from.
24
+ * Never a file, never a `data-` attribute, never an argument this CLI writes
25
+ * down: a deploy key's whole power is creating projects, so it lives in the
26
+ * build environment and nowhere else.
27
+ */
28
+ export const API_KEY_ENV_VAR = "FEEDBACK_TOOLBAR_API_KEY";
29
+ /**
30
+ * Register the deployment, or confirm it is already registered.
31
+ *
32
+ * Idempotent because the backend is: the second call with the same normalised
33
+ * URL is a no-op that answers `created: false`. That is what makes this safe
34
+ * as a prebuild step, which runs on every deploy forever.
35
+ *
36
+ * Throws `RegistrationError` with a message naming the missing piece when it
37
+ * cannot proceed. It never guesses: a registration under a URL nobody meant
38
+ * creates a project that collects feedback from a page that does not exist.
39
+ */
40
+ export async function register(options) {
41
+ const { env } = options;
42
+ const transport = options.registerProject ?? postRegistration;
43
+ const apiUrl = options.apiUrl ?? API_URL_ENV_VARS.map((name) => env[name]).find(Boolean);
44
+ if (!apiUrl) {
45
+ throw new RegistrationError("no backend URL. Pass --api-url https://feedback.bitvea.com, or set " +
46
+ `${API_URL_ENV_VARS[0]} in the build environment (\`init\` writes it into .env.local).`);
47
+ }
48
+ const apiKey = options.apiKey ?? env[API_KEY_ENV_VAR];
49
+ if (!apiKey) {
50
+ throw new RegistrationError(`no deploy key. Pass --api-key, or set ${API_KEY_ENV_VAR} in the build ` +
51
+ "environment. Create one in the dashboard under Settings > Deploy keys. " +
52
+ "It must never be committed or put in a page.");
53
+ }
54
+ const projectUrl = options.projectUrl ?? inferProjectUrl(env);
55
+ if (!projectUrl) {
56
+ throw new RegistrationError("could not tell which URL to register. Pass --project-url https://myapp.com. " +
57
+ "Inside a Vercel build this is read from VERCEL_PROJECT_PRODUCTION_URL or " +
58
+ "VERCEL_URL, neither of which is set here.");
59
+ }
60
+ const { created, project } = await transport({
61
+ apiUrl,
62
+ apiKey,
63
+ projectUrl,
64
+ repoOwner: env.VERCEL_GIT_REPO_OWNER,
65
+ repoSlug: env.VERCEL_GIT_REPO_SLUG,
66
+ packageName: options.packageName,
67
+ });
68
+ return { projectUrl, created, projectName: project?.name };
69
+ }
70
+ /**
71
+ * The URL to register the project under, from the build environment.
72
+ *
73
+ * Same precedence as the toolbar's own `resolveConfig`, and that agreement is
74
+ * the point: the toolbar reports this value back at runtime, so if the two
75
+ * picked differently the lookup would miss on every preview deployment. The
76
+ * production hostname wins because a preview URL changes on every commit, and
77
+ * keying on it would create a project per deploy instead of collecting a
78
+ * project's feedback in one place.
79
+ */
80
+ export function inferProjectUrl(env) {
81
+ const host = env.VERCEL_PROJECT_PRODUCTION_URL ?? env.VERCEL_URL;
82
+ return host ? `https://${host}` : undefined;
83
+ }
84
+ //# sourceMappingURL=register.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"register.js","sourceRoot":"","sources":["../../src/cli/register.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAC/E,EAAE;AACF,+EAA+E;AAC/E,mBAAmB;AACnB,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,6EAA6E;AAC7E,8DAA8D;AAC9D,+EAA+E;AAC/E,gFAAgF;AAChF,EAAE;AACF,+EAA+E;AAC/E,4EAA4E;AAC5E,8EAA8E;AAC9E,OAAO,EACL,iBAAiB,EACjB,gBAAgB,GAEjB,MAAM,gBAAgB,CAAC;AAExB,oFAAoF;AACpF,MAAM,CAAC,MAAM,gBAAgB,GAAG;IAC9B,sCAAsC;IACtC,0BAA0B;CAClB,CAAC;AAEX;;;;;GAKG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,0BAA0B,CAAC;AAyB1D;;;;;;;;;;GAUG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,OAAwB;IAExB,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IACxB,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,IAAI,gBAAgB,CAAC;IAE9D,MAAM,MAAM,GACV,OAAO,CAAC,MAAM,IAAI,gBAAgB,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,iBAAiB,CACzB,qEAAqE;YACnE,GAAG,gBAAgB,CAAC,CAAC,CAAC,iEAAiE,CAC1F,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC;IACtD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,iBAAiB,CACzB,yCAAyC,eAAe,gBAAgB;YACtE,yEAAyE;YACzE,8CAA8C,CACjD,CAAC;IACJ,CAAC;IAED,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,IAAI,eAAe,CAAC,GAAG,CAAC,CAAC;IAC9D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,iBAAiB,CACzB,8EAA8E;YAC5E,2EAA2E;YAC3E,2CAA2C,CAC9C,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,SAAS,CAAC;QAC3C,MAAM;QACN,MAAM;QACN,UAAU;QACV,SAAS,EAAE,GAAG,CAAC,qBAAqB;QACpC,QAAQ,EAAE,GAAG,CAAC,oBAAoB;QAClC,WAAW,EAAE,OAAO,CAAC,WAAW;KACjC,CAAC,CAAC;IAEH,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;AAC7D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe,CAC7B,GAAuC;IAEvC,MAAM,IAAI,GAAG,GAAG,CAAC,6BAA6B,IAAI,GAAG,CAAC,UAAU,CAAC;IACjE,OAAO,IAAI,CAAC,CAAC,CAAC,WAAW,IAAI,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,37 @@
1
+ /** What a build environment can tell the backend about the deployment. */
2
+ export interface RegisterProjectRequest {
3
+ /** Base URL of the BitVea Feedback backend. */
4
+ apiUrl: string;
5
+ /** Deploy key. Read from a flag or the environment, never from a file. */
6
+ apiKey: string;
7
+ /** The deployment URL to register, exactly as the caller resolved it. */
8
+ projectUrl: string;
9
+ repoOwner?: string | undefined;
10
+ repoSlug?: string | undefined;
11
+ packageName?: string | undefined;
12
+ }
13
+ /**
14
+ * `created` is the whole answer: false means the URL was already registered,
15
+ * which is a success, not a conflict. `project` is what the backend named it,
16
+ * so the CLI can echo something recognisable back.
17
+ */
18
+ export interface RegisterProjectResponse {
19
+ created: boolean;
20
+ project?: {
21
+ id: string;
22
+ name: string;
23
+ slug: string;
24
+ url: string | null;
25
+ };
26
+ }
27
+ export type RegisterProjectTransport = (input: RegisterProjectRequest) => Promise<RegisterProjectResponse>;
28
+ /**
29
+ * A failure the person running the command can act on, as opposed to a bug.
30
+ * The CLI prints `message` and exits; nothing else is expected to catch it.
31
+ */
32
+ export declare class RegistrationError extends Error {
33
+ constructor(message: string);
34
+ }
35
+ /** The real transport. Injected everywhere else so tests stay off the network. */
36
+ export declare const postRegistration: RegisterProjectTransport;
37
+ //# sourceMappingURL=registration.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.d.ts","sourceRoot":"","sources":["../../src/cli/registration.ts"],"names":[],"mappings":"AAcA,0EAA0E;AAC1E,MAAM,WAAW,sBAAsB;IACrC,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CAC1E;AAED,MAAM,MAAM,wBAAwB,GAAG,CACrC,KAAK,EAAE,sBAAsB,KAC1B,OAAO,CAAC,uBAAuB,CAAC,CAAC;AAEtC;;;GAGG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,EAAE,wBAqD9B,CAAC"}
@@ -0,0 +1,69 @@
1
+ // Talking to the registration endpoint, and nothing else.
2
+ //
3
+ // The rule this file exists to keep is that the CLI does not know what a valid
4
+ // project URL is. Registration's contract - normalise the URL, key on it, be
5
+ // idempotent, lose the race gracefully - lives once in
6
+ // `packages/api/src/services/projects.ts`, behind
7
+ // `POST /api/v1/projects/register`. A second opinion here would be a second
8
+ // implementation, and the failure mode of two implementations disagreeing is a
9
+ // duplicate project: two rows for one deployment, each holding half its
10
+ // feedback, with no way to merge them afterwards.
11
+ //
12
+ // So this module posts what it was given and translates the answer. It trims
13
+ // no slashes, lowercases no hosts, and rejects nothing.
14
+ /**
15
+ * A failure the person running the command can act on, as opposed to a bug.
16
+ * The CLI prints `message` and exits; nothing else is expected to catch it.
17
+ */
18
+ export class RegistrationError extends Error {
19
+ constructor(message) {
20
+ super(message);
21
+ this.name = "RegistrationError";
22
+ }
23
+ }
24
+ /** The real transport. Injected everywhere else so tests stay off the network. */
25
+ export const postRegistration = async (input) => {
26
+ let res;
27
+ try {
28
+ res = await fetch(`${input.apiUrl}/api/v1/projects/register`, {
29
+ method: "POST",
30
+ headers: {
31
+ "content-type": "application/json",
32
+ authorization: `Bearer ${input.apiKey}`,
33
+ },
34
+ body: JSON.stringify({
35
+ url: input.projectUrl,
36
+ repoOwner: input.repoOwner,
37
+ repoSlug: input.repoSlug,
38
+ packageName: input.packageName,
39
+ }),
40
+ });
41
+ }
42
+ catch (err) {
43
+ // DNS, TLS, a wrong host, no network in the build container. Worth naming
44
+ // the URL: the commonest cause is an --api-url pointing at nothing.
45
+ throw new RegistrationError(`could not reach ${input.apiUrl} (${String(err)}). Check --api-url.`);
46
+ }
47
+ if (res.status === 401) {
48
+ // The commonest real failure, and the one a bare status code explains
49
+ // worst. Absent, mistyped and revoked all answer 401 on purpose - the
50
+ // route refuses to be an oracle for which - so say all three.
51
+ throw new RegistrationError("the deploy key was refused (401): it is missing, wrong or revoked. " +
52
+ "Create one in the dashboard under Settings > Deploy keys, then pass it as " +
53
+ "--api-key or set FEEDBACK_TOOLBAR_API_KEY.");
54
+ }
55
+ if (!res.ok) {
56
+ // The backend's own error code, verbatim. `invalid_project_url` is the one
57
+ // that matters, and paraphrasing it here would be the beginning of a
58
+ // second set of validation rules.
59
+ const code = await res
60
+ .json()
61
+ .then((body) => body !== null && typeof body === "object" && "error" in body
62
+ ? String(body.error)
63
+ : undefined)
64
+ .catch(() => undefined);
65
+ throw new RegistrationError(`registration failed with ${res.status}${code ? ` (${code})` : ""}.`);
66
+ }
67
+ return (await res.json());
68
+ };
69
+ //# sourceMappingURL=registration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registration.js","sourceRoot":"","sources":["../../src/cli/registration.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,uDAAuD;AACvD,kDAAkD;AAClD,4EAA4E;AAC5E,+EAA+E;AAC/E,wEAAwE;AACxE,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,wDAAwD;AA6BxD;;;GAGG;AACH,MAAM,OAAO,iBAAkB,SAAQ,KAAK;IAC1C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AAED,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAA6B,KAAK,EAAE,KAAK,EAAE,EAAE;IACxE,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,KAAK,CAAC,MAAM,2BAA2B,EAAE;YAC5D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,aAAa,EAAE,UAAU,KAAK,CAAC,MAAM,EAAE;aACxC;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;gBACnB,GAAG,EAAE,KAAK,CAAC,UAAU;gBACrB,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC;SACH,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,0EAA0E;QAC1E,oEAAoE;QACpE,MAAM,IAAI,iBAAiB,CACzB,mBAAmB,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,GAAG,CAAC,qBAAqB,CACrE,CAAC;IACJ,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,sEAAsE;QACtE,sEAAsE;QACtE,8DAA8D;QAC9D,MAAM,IAAI,iBAAiB,CACzB,qEAAqE;YACnE,4EAA4E;YAC5E,4CAA4C,CAC/C,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,2EAA2E;QAC3E,qEAAqE;QACrE,kCAAkC;QAClC,MAAM,IAAI,GAAG,MAAM,GAAG;aACnB,IAAI,EAAE;aACN,IAAI,CAAC,CAAC,IAAa,EAAE,EAAE,CACtB,IAAI,KAAK,IAAI,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,OAAO,IAAI,IAAI;YAC1D,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC;YACpB,CAAC,CAAC,SAAS,CACd;aACA,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC1B,MAAM,IAAI,iBAAiB,CACzB,4BAA4B,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,CACrE,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAA4B,CAAC;AACvD,CAAC,CAAC"}
@@ -0,0 +1,98 @@
1
+ import { type InstallerFs } from "./fs.js";
2
+ export declare const PACKAGE_NAME = "@bitvea/feedback-toolbar";
3
+ export declare const COMPONENT_NAME = "FeedbackToolbar";
4
+ /** The file the script-tag route is served as, relative to the backend URL. */
5
+ export declare const SCRIPT_BUNDLE_PATH = "/toolbar.js";
6
+ export interface InjectionSite {
7
+ /** The file to edit. */
8
+ path: string;
9
+ /** Human-readable description of what was found, for the CLI's output. */
10
+ description: string;
11
+ }
12
+ /**
13
+ * How a strategy's host app gets the toolbar's *code*, which decides which of
14
+ * the installer's other steps apply at all.
15
+ *
16
+ * `npm-package`: the toolbar is a dependency the host's bundler compiles, so
17
+ * the install needs a package.json entry, an env var for the API URL, and a
18
+ * prebuild hook to register the project from the build.
19
+ *
20
+ * `script-tag`: the toolbar is a file served from the backend and configured
21
+ * from `data-` attributes on the tag. There is no dependency to add, no env
22
+ * file to read at runtime, and - the reason #17 called this out - no build to
23
+ * hook, so its project is registered by running `register` by hand.
24
+ */
25
+ export type Delivery = "npm-package" | "script-tag";
26
+ /**
27
+ * The environment a script-tag install declares, when the developer has not
28
+ * said otherwise.
29
+ *
30
+ * The gate treats "absent" as off, and a static HTML file cannot know which
31
+ * deployment served it - so a tag with no `data-environment` mounts nothing,
32
+ * ever. That is the correct default for a value we could only guess, and the
33
+ * wrong default for an installer whose whole job is to leave a working
34
+ * install behind: #17 asks for a toolbar that mounts on a plain page with no
35
+ * build step, and a tag the developer has to hand-edit is not that.
36
+ *
37
+ * So `init` writes an environment, and the value is a review environment
38
+ * because installing a review tool on a page is what the command means. The
39
+ * result is printed as a change and called out as a note, and `--environment`
40
+ * overrides it - including with `production`, which is how somebody turns the
41
+ * tag off without removing it.
42
+ */
43
+ export declare const DEFAULT_SCRIPT_ENVIRONMENT = "preview";
44
+ /** What `inject` needs to know about the install to write the markup. */
45
+ export interface InjectionContext {
46
+ /** Base URL of the BitVea Feedback backend, no trailing slash. */
47
+ apiUrl: string;
48
+ /** The deployment URL the project is registered under, when known. */
49
+ projectUrl?: string | undefined;
50
+ /**
51
+ * What the injected markup declares as its environment. Only the script-tag
52
+ * route uses it: an npm install reads `NEXT_PUBLIC_VERCEL_ENV`, which the
53
+ * host's own build populates per deployment.
54
+ */
55
+ environment?: string | undefined;
56
+ }
57
+ export interface InjectionStrategy {
58
+ /** Stable id, reported in the CLI's output. */
59
+ readonly id: string;
60
+ readonly label: string;
61
+ /** Which of the installer's steps apply. Defaults to `npm-package`. */
62
+ readonly delivery?: Delivery;
63
+ /** Find the file to inject into, or null when this strategy does not apply. */
64
+ detect(fs: InstallerFs, root: string): InjectionSite | null;
65
+ /** True when the provider is already there, which makes a re-run a no-op. */
66
+ isInstalled(source: string): boolean;
67
+ /**
68
+ * Return the edited source. Only called when `isInstalled` is false, and
69
+ * throws when it cannot do the edit, so a partial write is not possible:
70
+ * the caller writes only what this returns.
71
+ */
72
+ inject(source: string, context: InjectionContext): string;
73
+ }
74
+ export declare const nextAppRouterStrategy: InjectionStrategy;
75
+ /**
76
+ * The `<script src>` install, for an app with no build step to add a
77
+ * dependency to. Second in the list on purpose: an App Router app usually
78
+ * also has an index.html somewhere, and the npm install is the better one.
79
+ *
80
+ * Note what the injected tag does NOT carry: a deploy key. There is no
81
+ * supported way to put one in a page, and the bundle refuses to mount when it
82
+ * finds anything that looks like one - a key in a page is a key in every
83
+ * visitor's hands, and its whole power is creating projects.
84
+ *
85
+ * It does carry an environment, and it has to. A static file cannot know which
86
+ * deployment served it and the gate treats absent as off, so a tag without one
87
+ * mounts nothing at all - which is what #17's "mounts a working toolbar on a
88
+ * plain HTML page" cannot mean. The value is written, printed as a change, and
89
+ * called out as a note; `--environment production` is how a developer turns
90
+ * the tag off without deleting it. See DEFAULT_SCRIPT_ENVIRONMENT.
91
+ */
92
+ export declare const htmlScriptTagStrategy: InjectionStrategy;
93
+ /** A failure the developer can act on, as opposed to an internal error. */
94
+ export declare class InjectionError extends Error {
95
+ constructor(message: string);
96
+ }
97
+ export declare const STRATEGIES: readonly InjectionStrategy[];
98
+ //# sourceMappingURL=strategies.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategies.d.ts","sourceRoot":"","sources":["../../src/cli/strategies.ts"],"names":[],"mappings":"AAOA,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,MAAM,CAAC;AAElD,eAAO,MAAM,YAAY,6BAA6B,CAAC;AACvD,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAEhD,+EAA+E;AAC/E,eAAO,MAAM,kBAAkB,gBAAgB,CAAC;AAEhD,MAAM,WAAW,aAAa;IAC5B,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,QAAQ,GAAG,aAAa,GAAG,YAAY,CAAC;AAEpD;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,0BAA0B,YAAY,CAAC;AAEpD,yEAAyE;AACzE,MAAM,WAAW,gBAAgB;IAC/B,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;IACf,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAClC;AAED,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,uEAAuE;IACvE,QAAQ,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAC7B,+EAA+E;IAC/E,MAAM,CAAC,EAAE,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAAC;IAC5D,6EAA6E;IAC7E,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACrC;;;;OAIG;IACH,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,CAAC;CAC3D;AAUD,eAAO,MAAM,qBAAqB,EAAE,iBAwDnC,CAAC;AAKF;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,qBAAqB,EAAE,iBAuCnC,CAAC;AAgEF,2EAA2E;AAC3E,qBAAa,cAAe,SAAQ,KAAK;gBAC3B,OAAO,EAAE,MAAM;CAI5B;AAED,eAAO,MAAM,UAAU,EAAE,SAAS,iBAAiB,EAGlD,CAAC"}