@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.
- package/README.md +262 -0
- package/dist/FeedbackPanel.d.ts +117 -0
- package/dist/FeedbackPanel.d.ts.map +1 -0
- package/dist/FeedbackPanel.js +622 -0
- package/dist/FeedbackPanel.js.map +1 -0
- package/dist/FeedbackToolbar.d.ts +54 -0
- package/dist/FeedbackToolbar.d.ts.map +1 -0
- package/dist/FeedbackToolbar.js +1149 -0
- package/dist/FeedbackToolbar.js.map +1 -0
- package/dist/InboxSheet.d.ts +80 -0
- package/dist/InboxSheet.d.ts.map +1 -0
- package/dist/InboxSheet.js +580 -0
- package/dist/InboxSheet.js.map +1 -0
- package/dist/PinLayer.d.ts +59 -0
- package/dist/PinLayer.d.ts.map +1 -0
- package/dist/PinLayer.js +1702 -0
- package/dist/PinLayer.js.map +1 -0
- package/dist/RichText.d.ts +6 -0
- package/dist/RichText.d.ts.map +1 -0
- package/dist/RichText.js +135 -0
- package/dist/RichText.js.map +1 -0
- package/dist/browser/entry.d.ts +3 -0
- package/dist/browser/entry.d.ts.map +1 -0
- package/dist/browser/entry.js +13 -0
- package/dist/browser/entry.js.map +1 -0
- package/dist/browser/mount.d.ts +37 -0
- package/dist/browser/mount.d.ts.map +1 -0
- package/dist/browser/mount.js +150 -0
- package/dist/browser/mount.js.map +1 -0
- package/dist/browser/toolbar.js +38 -0
- package/dist/browser/toolbar.js.map +7 -0
- package/dist/capture.d.ts +58 -0
- package/dist/capture.d.ts.map +1 -0
- package/dist/capture.js +98 -0
- package/dist/capture.js.map +1 -0
- package/dist/cli/bin.d.ts +3 -0
- package/dist/cli/bin.d.ts.map +1 -0
- package/dist/cli/bin.js +196 -0
- package/dist/cli/bin.js.map +1 -0
- package/dist/cli/fs.d.ts +16 -0
- package/dist/cli/fs.d.ts.map +1 -0
- package/dist/cli/fs.js +33 -0
- package/dist/cli/fs.js.map +1 -0
- package/dist/cli/init.d.ts +75 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +266 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/register.d.ts +55 -0
- package/dist/cli/register.d.ts.map +1 -0
- package/dist/cli/register.js +84 -0
- package/dist/cli/register.js.map +1 -0
- package/dist/cli/registration.d.ts +37 -0
- package/dist/cli/registration.d.ts.map +1 -0
- package/dist/cli/registration.js +69 -0
- package/dist/cli/registration.js.map +1 -0
- package/dist/cli/strategies.d.ts +98 -0
- package/dist/cli/strategies.d.ts.map +1 -0
- package/dist/cli/strategies.js +198 -0
- package/dist/cli/strategies.js.map +1 -0
- package/dist/client.d.ts +59 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +113 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +92 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +65 -0
- package/dist/config.js.map +1 -0
- package/dist/deepLink.d.ts +86 -0
- package/dist/deepLink.d.ts.map +1 -0
- package/dist/deepLink.js +185 -0
- package/dist/deepLink.js.map +1 -0
- package/dist/fontFace.d.ts +70 -0
- package/dist/fontFace.d.ts.map +1 -0
- package/dist/fontFace.js +88 -0
- package/dist/fontFace.js.map +1 -0
- package/dist/gating.d.ts +32 -0
- package/dist/gating.d.ts.map +1 -0
- package/dist/gating.js +49 -0
- package/dist/gating.js.map +1 -0
- package/dist/icons.d.ts +68 -0
- package/dist/icons.d.ts.map +1 -0
- package/dist/icons.js +97 -0
- package/dist/icons.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/markdown.d.ts +59 -0
- package/dist/markdown.d.ts.map +1 -0
- package/dist/markdown.js +130 -0
- package/dist/markdown.js.map +1 -0
- package/dist/motion.d.ts +178 -0
- package/dist/motion.d.ts.map +1 -0
- package/dist/motion.js +302 -0
- package/dist/motion.js.map +1 -0
- package/dist/preferences.d.ts +71 -0
- package/dist/preferences.d.ts.map +1 -0
- package/dist/preferences.js +195 -0
- package/dist/preferences.js.map +1 -0
- package/dist/rasterise.d.ts +45 -0
- package/dist/rasterise.d.ts.map +1 -0
- package/dist/rasterise.js +490 -0
- package/dist/rasterise.js.map +1 -0
- package/dist/selector.d.ts +19 -0
- package/dist/selector.d.ts.map +1 -0
- package/dist/selector.js +142 -0
- package/dist/selector.js.map +1 -0
- package/dist/session.d.ts +19 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +36 -0
- package/dist/session.js.map +1 -0
- package/dist/shortcut.d.ts +88 -0
- package/dist/shortcut.d.ts.map +1 -0
- package/dist/shortcut.js +149 -0
- package/dist/shortcut.js.map +1 -0
- package/dist/tokens.d.ts +709 -0
- package/dist/tokens.d.ts.map +1 -0
- package/dist/tokens.js +749 -0
- package/dist/tokens.js.map +1 -0
- package/dist/vendor/types/account.d.ts +26 -0
- package/dist/vendor/types/account.d.ts.map +1 -0
- package/dist/vendor/types/account.js +23 -0
- package/dist/vendor/types/account.js.map +1 -0
- package/dist/vendor/types/apiKey.d.ts +38 -0
- package/dist/vendor/types/apiKey.d.ts.map +1 -0
- package/dist/vendor/types/apiKey.js +36 -0
- package/dist/vendor/types/apiKey.js.map +1 -0
- package/dist/vendor/types/auth.d.ts +10 -0
- package/dist/vendor/types/auth.d.ts.map +1 -0
- package/dist/vendor/types/auth.js +15 -0
- package/dist/vendor/types/auth.js.map +1 -0
- package/dist/vendor/types/entity.d.ts +74 -0
- package/dist/vendor/types/entity.d.ts.map +1 -0
- package/dist/vendor/types/entity.js +111 -0
- package/dist/vendor/types/entity.js.map +1 -0
- package/dist/vendor/types/feedback.d.ts +285 -0
- package/dist/vendor/types/feedback.d.ts.map +1 -0
- package/dist/vendor/types/feedback.js +286 -0
- package/dist/vendor/types/feedback.js.map +1 -0
- package/dist/vendor/types/index.d.ts +14 -0
- package/dist/vendor/types/index.d.ts.map +1 -0
- package/dist/vendor/types/index.js +16 -0
- package/dist/vendor/types/index.js.map +1 -0
- package/dist/vendor/types/invite.d.ts +42 -0
- package/dist/vendor/types/invite.d.ts.map +1 -0
- package/dist/vendor/types/invite.js +30 -0
- package/dist/vendor/types/invite.js.map +1 -0
- package/dist/vendor/types/mcpToken.d.ts +38 -0
- package/dist/vendor/types/mcpToken.d.ts.map +1 -0
- package/dist/vendor/types/mcpToken.js +39 -0
- package/dist/vendor/types/mcpToken.js.map +1 -0
- package/dist/vendor/types/organization.d.ts +13 -0
- package/dist/vendor/types/organization.d.ts.map +1 -0
- package/dist/vendor/types/organization.js +12 -0
- package/dist/vendor/types/organization.js.map +1 -0
- package/dist/vendor/types/project.d.ts +73 -0
- package/dist/vendor/types/project.d.ts.map +1 -0
- package/dist/vendor/types/project.js +117 -0
- package/dist/vendor/types/project.js.map +1 -0
- package/dist/vendor/types/role.d.ts +12 -0
- package/dist/vendor/types/role.d.ts.map +1 -0
- package/dist/vendor/types/role.js +20 -0
- package/dist/vendor/types/role.js.map +1 -0
- package/dist/vendor/types/roleDefinition.d.ts +61 -0
- package/dist/vendor/types/roleDefinition.d.ts.map +1 -0
- package/dist/vendor/types/roleDefinition.js +45 -0
- package/dist/vendor/types/roleDefinition.js.map +1 -0
- package/dist/vendor/types/team.d.ts +25 -0
- package/dist/vendor/types/team.d.ts.map +1 -0
- package/dist/vendor/types/team.js +17 -0
- package/dist/vendor/types/team.js.map +1 -0
- package/dist/vendor/types/util.d.ts +39 -0
- package/dist/vendor/types/util.d.ts.map +1 -0
- package/dist/vendor/types/util.js +31 -0
- package/dist/vendor/types/util.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type FeedbackMetadata } from "./vendor/types/index.js";
|
|
2
|
+
import type { ToolbarConfig } from "./config.js";
|
|
3
|
+
/** Read the page context. Pure apart from reading `window`/`document`. */
|
|
4
|
+
export declare function captureMetadata(config: ToolbarConfig): FeedbackMetadata;
|
|
5
|
+
/**
|
|
6
|
+
* Capture the current viewport as a PNG data URL, or null when the browser
|
|
7
|
+
* will not produce one.
|
|
8
|
+
*
|
|
9
|
+
* ## Why this is a DOM rasteriser and not `getDisplayMedia`
|
|
10
|
+
*
|
|
11
|
+
* The product promises an *automatic* screenshot on every submission. The
|
|
12
|
+
* platform's own capture API cannot deliver that: `getDisplayMedia` raises the
|
|
13
|
+
* browser's screen/window/tab picker on every call, the grant cannot be kept,
|
|
14
|
+
* and `preferCurrentTab` - the hint that makes the picker a single click - is
|
|
15
|
+
* Chromium-only, so a reviewer on Firefox or Safari picks a surface by hand for
|
|
16
|
+
* every piece of feedback they file. A modal dialog in the middle of the core
|
|
17
|
+
* flow is not automatic; it is the thing most likely to make a reviewer stop
|
|
18
|
+
* filing feedback at all.
|
|
19
|
+
*
|
|
20
|
+
* Rejected alternatives:
|
|
21
|
+
*
|
|
22
|
+
* - *Keep `getDisplayMedia`, retain the stream and reuse it across
|
|
23
|
+
* submissions.* A retained stream keeps the browser's "sharing this tab"
|
|
24
|
+
* indicator lit for the whole session. Leaving a reviewer to believe they
|
|
25
|
+
* are still being recorded, on somebody else's staging site, to save one
|
|
26
|
+
* dialog, is not a trade worth making.
|
|
27
|
+
* - *A rasteriser dependency (html2canvas and friends).* This bundle ships
|
|
28
|
+
* into every reviewed preview deployment, and CLAUDE.md section 8 puts a
|
|
29
|
+
* 100 KB gzipped ceiling on a package without written justification.
|
|
30
|
+
* html2canvas is tens of kilobytes gzipped, so the ceiling is not really
|
|
31
|
+
* the objection - the objection is that both it and html-to-image
|
|
32
|
+
* reimplement the same `foreignObject` trick that `rasterise.ts` does in
|
|
33
|
+
* about 300 lines and 2.5 KB minified and gzipped (measured with esbuild),
|
|
34
|
+
* and a toolbar with no dependencies is one less thing to keep in step
|
|
35
|
+
* with whatever build the host app happens to have.
|
|
36
|
+
* - *A hybrid: rasterise by default, offer `getDisplayMedia` as an opt-in
|
|
37
|
+
* "capture the real pixels" button.* Tempting, but there are two submit
|
|
38
|
+
* surfaces (the panel and the pin composer) and the affordance would have
|
|
39
|
+
* to appear identically on both to avoid teaching the reviewer that a pin
|
|
40
|
+
* and a general report capture different things. That is worth revisiting
|
|
41
|
+
* when somebody actually asks for pixel-exact capture; it is not worth
|
|
42
|
+
* shipping speculatively.
|
|
43
|
+
*
|
|
44
|
+
* Null stays a supported outcome all the way through. `screenshot` is optional
|
|
45
|
+
* on the wire and nullable in the database, because losing a reviewer's words
|
|
46
|
+
* to a failed screenshot would be the worse trade.
|
|
47
|
+
*/
|
|
48
|
+
export declare function captureScreenshot(): Promise<string | null>;
|
|
49
|
+
/**
|
|
50
|
+
* Capture ONE element the reviewer pointed at, or null.
|
|
51
|
+
*
|
|
52
|
+
* Same posture as `captureScreenshot`: a capture that cannot be produced is a
|
|
53
|
+
* missing picture, never a failed reply. Everything about a report is more
|
|
54
|
+
* useful than nothing about it, so every failure here returns null and the
|
|
55
|
+
* words still get sent.
|
|
56
|
+
*/
|
|
57
|
+
export declare function captureElement(element: Element): Promise<string | null>;
|
|
58
|
+
//# sourceMappingURL=capture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AAOA,OAAO,EAA2B,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAI9C,0EAA0E;AAC1E,wBAAgB,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,gBAAgB,CAgBvE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQhE;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAM7E"}
|
package/dist/capture.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
// What the toolbar records about the page without asking the reviewer.
|
|
2
|
+
//
|
|
3
|
+
// The whole point of this product is that a report arrives attached to the
|
|
4
|
+
// page, branch and commit it is about, so none of this is typed by a human and
|
|
5
|
+
// none of it is optional to collect. It is collected at submission time rather
|
|
6
|
+
// than on open, because a reviewer may have scrolled, resized or navigated in
|
|
7
|
+
// between.
|
|
8
|
+
import { SCREENSHOT_DATA_URL_MAX } from "./vendor/types/index.js";
|
|
9
|
+
import { currentPageUrl } from "./deepLink.js";
|
|
10
|
+
import { rasteriseElement, rasteriseViewport } from "./rasterise.js";
|
|
11
|
+
/** Read the page context. Pure apart from reading `window`/`document`. */
|
|
12
|
+
export function captureMetadata(config) {
|
|
13
|
+
return {
|
|
14
|
+
// The page's identity, which has no fragment at all - see the note at the
|
|
15
|
+
// top of deepLink.ts. Two failures this avoids: a reviewer who followed a
|
|
16
|
+
// deep link and then filed a report of their own would file it against a
|
|
17
|
+
// URL carrying our fragment, which no other surface ever queries; and a
|
|
18
|
+
// report filed at `/pricing#plans` would be invisible to a deep link,
|
|
19
|
+
// which cannot carry the host's fragment and ours at once.
|
|
20
|
+
pageUrl: currentPageUrl(),
|
|
21
|
+
viewportWidth: window.innerWidth,
|
|
22
|
+
viewportHeight: window.innerHeight,
|
|
23
|
+
userAgent: navigator.userAgent,
|
|
24
|
+
gitBranch: config.gitBranch,
|
|
25
|
+
gitCommit: config.gitCommit,
|
|
26
|
+
environment: config.environment,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Capture the current viewport as a PNG data URL, or null when the browser
|
|
31
|
+
* will not produce one.
|
|
32
|
+
*
|
|
33
|
+
* ## Why this is a DOM rasteriser and not `getDisplayMedia`
|
|
34
|
+
*
|
|
35
|
+
* The product promises an *automatic* screenshot on every submission. The
|
|
36
|
+
* platform's own capture API cannot deliver that: `getDisplayMedia` raises the
|
|
37
|
+
* browser's screen/window/tab picker on every call, the grant cannot be kept,
|
|
38
|
+
* and `preferCurrentTab` - the hint that makes the picker a single click - is
|
|
39
|
+
* Chromium-only, so a reviewer on Firefox or Safari picks a surface by hand for
|
|
40
|
+
* every piece of feedback they file. A modal dialog in the middle of the core
|
|
41
|
+
* flow is not automatic; it is the thing most likely to make a reviewer stop
|
|
42
|
+
* filing feedback at all.
|
|
43
|
+
*
|
|
44
|
+
* Rejected alternatives:
|
|
45
|
+
*
|
|
46
|
+
* - *Keep `getDisplayMedia`, retain the stream and reuse it across
|
|
47
|
+
* submissions.* A retained stream keeps the browser's "sharing this tab"
|
|
48
|
+
* indicator lit for the whole session. Leaving a reviewer to believe they
|
|
49
|
+
* are still being recorded, on somebody else's staging site, to save one
|
|
50
|
+
* dialog, is not a trade worth making.
|
|
51
|
+
* - *A rasteriser dependency (html2canvas and friends).* This bundle ships
|
|
52
|
+
* into every reviewed preview deployment, and CLAUDE.md section 8 puts a
|
|
53
|
+
* 100 KB gzipped ceiling on a package without written justification.
|
|
54
|
+
* html2canvas is tens of kilobytes gzipped, so the ceiling is not really
|
|
55
|
+
* the objection - the objection is that both it and html-to-image
|
|
56
|
+
* reimplement the same `foreignObject` trick that `rasterise.ts` does in
|
|
57
|
+
* about 300 lines and 2.5 KB minified and gzipped (measured with esbuild),
|
|
58
|
+
* and a toolbar with no dependencies is one less thing to keep in step
|
|
59
|
+
* with whatever build the host app happens to have.
|
|
60
|
+
* - *A hybrid: rasterise by default, offer `getDisplayMedia` as an opt-in
|
|
61
|
+
* "capture the real pixels" button.* Tempting, but there are two submit
|
|
62
|
+
* surfaces (the panel and the pin composer) and the affordance would have
|
|
63
|
+
* to appear identically on both to avoid teaching the reviewer that a pin
|
|
64
|
+
* and a general report capture different things. That is worth revisiting
|
|
65
|
+
* when somebody actually asks for pixel-exact capture; it is not worth
|
|
66
|
+
* shipping speculatively.
|
|
67
|
+
*
|
|
68
|
+
* Null stays a supported outcome all the way through. `screenshot` is optional
|
|
69
|
+
* on the wire and nullable in the database, because losing a reviewer's words
|
|
70
|
+
* to a failed screenshot would be the worse trade.
|
|
71
|
+
*/
|
|
72
|
+
export async function captureScreenshot() {
|
|
73
|
+
try {
|
|
74
|
+
return await rasteriseViewport(SCREENSHOT_DATA_URL_MAX);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Unsupported, blocked, or a DOM that would not serialise. All the same to
|
|
78
|
+
// the caller.
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Capture ONE element the reviewer pointed at, or null.
|
|
84
|
+
*
|
|
85
|
+
* Same posture as `captureScreenshot`: a capture that cannot be produced is a
|
|
86
|
+
* missing picture, never a failed reply. Everything about a report is more
|
|
87
|
+
* useful than nothing about it, so every failure here returns null and the
|
|
88
|
+
* words still get sent.
|
|
89
|
+
*/
|
|
90
|
+
export async function captureElement(element) {
|
|
91
|
+
try {
|
|
92
|
+
return await rasteriseElement(element, SCREENSHOT_DATA_URL_MAX);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../src/capture.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,2EAA2E;AAC3E,+EAA+E;AAC/E,+EAA+E;AAC/E,8EAA8E;AAC9E,WAAW;AACX,OAAO,EAAE,uBAAuB,EAAyB,MAAM,eAAe,CAAC;AAG/E,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAElE,0EAA0E;AAC1E,MAAM,UAAU,eAAe,CAAC,MAAqB;IACnD,OAAO;QACL,0EAA0E;QAC1E,0EAA0E;QAC1E,yEAAyE;QACzE,wEAAwE;QACxE,sEAAsE;QACtE,2DAA2D;QAC3D,OAAO,EAAE,cAAc,EAAE;QACzB,aAAa,EAAE,MAAM,CAAC,UAAU;QAChC,cAAc,EAAE,MAAM,CAAC,WAAW;QAClC,SAAS,EAAE,SAAS,CAAC,SAAS;QAC9B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,WAAW,EAAE,MAAM,CAAC,WAAW;KAChC,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,IAAI,CAAC;QACH,OAAO,MAAM,iBAAiB,CAAC,uBAAuB,CAAC,CAAC;IAC1D,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;QAC3E,cAAc;QACd,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAgB;IACnD,IAAI,CAAC;QACH,OAAO,MAAM,gBAAgB,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC;IAClE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.d.ts","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":""}
|
package/dist/cli/bin.js
ADDED
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// The `npx @bitvea/feedback-toolbar` entry point: two commands, `init` and
|
|
3
|
+
// `register`.
|
|
4
|
+
//
|
|
5
|
+
// Everything of substance is in init.ts and register.ts, behind an InstallerFs
|
|
6
|
+
// and an injected transport respectively. This file is the part that touches
|
|
7
|
+
// the real disk and the real network, parses argv, and turns a thrown error
|
|
8
|
+
// into an exit code and a message a developer can act on.
|
|
9
|
+
//
|
|
10
|
+
// `register` is a first-class command rather than a step inside `init`,
|
|
11
|
+
// because two install paths need it on its own: a Next.js app runs it from its
|
|
12
|
+
// build (init wires it in as `prebuild`), and a script-tag install has no
|
|
13
|
+
// build at all and runs it once from a terminal or CI.
|
|
14
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { dirname, join } from "node:path";
|
|
16
|
+
import { mkdirSync } from "node:fs";
|
|
17
|
+
import { init } from "./init.js";
|
|
18
|
+
import { API_KEY_ENV_VAR, register } from "./register.js";
|
|
19
|
+
import { RegistrationError, postRegistration } from "./registration.js";
|
|
20
|
+
import { InjectionError, PACKAGE_NAME } from "./strategies.js";
|
|
21
|
+
const realFs = {
|
|
22
|
+
exists: (path) => existsSync(path),
|
|
23
|
+
readFile: (path) => readFileSync(path, "utf8"),
|
|
24
|
+
writeFile: (path, contents) => {
|
|
25
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
26
|
+
writeFileSync(path, contents, "utf8");
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
const USAGE = `feedback-toolbar - BitVea Feedback installer
|
|
30
|
+
|
|
31
|
+
init Install the toolbar into the app in the current directory.
|
|
32
|
+
register Make sure this deployment exists as a project in the dashboard.
|
|
33
|
+
|
|
34
|
+
npx @bitvea/feedback-toolbar init --api-url https://feedback.bitvea.com
|
|
35
|
+
npx @bitvea/feedback-toolbar register --project-url https://myapp.com --api-key <key>
|
|
36
|
+
|
|
37
|
+
Flags
|
|
38
|
+
|
|
39
|
+
--api-url <url> BitVea Feedback backend. Falls back to
|
|
40
|
+
NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL.
|
|
41
|
+
--project-url <url> The deployment to register. Falls back to
|
|
42
|
+
VERCEL_PROJECT_PRODUCTION_URL, then VERCEL_URL.
|
|
43
|
+
--api-key <key> Deploy key. Falls back to FEEDBACK_TOOLBAR_API_KEY,
|
|
44
|
+
which is the better place for it: a key on a command
|
|
45
|
+
line ends up in shell history and CI logs. It is never
|
|
46
|
+
written to disk and never reaches a browser.
|
|
47
|
+
--root <path> The app to work on. Defaults to the current directory.
|
|
48
|
+
--environment <name> init, script-tag installs only: what the injected tag
|
|
49
|
+
declares. Defaults to "preview", which is what makes it
|
|
50
|
+
mount; pass "production" to leave the tag inert.
|
|
51
|
+
--optional register only: report a failure and exit 0. This is
|
|
52
|
+
what the prebuild hook uses, so a registration problem
|
|
53
|
+
can never fail somebody's deploy.`;
|
|
54
|
+
function readFlag(argv, name) {
|
|
55
|
+
const withEquals = argv.find((a) => a.startsWith(`--${name}=`));
|
|
56
|
+
if (withEquals)
|
|
57
|
+
return withEquals.slice(name.length + 3);
|
|
58
|
+
const index = argv.indexOf(`--${name}`);
|
|
59
|
+
return index >= 0 ? argv[index + 1] : undefined;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The host app's package name, which the backend uses to name a project when
|
|
63
|
+
* there is no git metadata to name it from. Best effort by design: a missing
|
|
64
|
+
* or unreadable package.json is not a reason to refuse to register.
|
|
65
|
+
*/
|
|
66
|
+
function readPackageName(root) {
|
|
67
|
+
const path = join(root, "package.json");
|
|
68
|
+
if (!existsSync(path))
|
|
69
|
+
return undefined;
|
|
70
|
+
try {
|
|
71
|
+
const pkg = JSON.parse(readFileSync(path, "utf8"));
|
|
72
|
+
return pkg.name;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function runInit(argv) {
|
|
79
|
+
const apiUrl = readFlag(argv, "api-url") ??
|
|
80
|
+
process.env.NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL;
|
|
81
|
+
if (!apiUrl) {
|
|
82
|
+
console.error("missing --api-url. Pass the BitVea Feedback backend URL, e.g.\n" +
|
|
83
|
+
" npx @bitvea/feedback-toolbar init --api-url https://feedback.bitvea.com");
|
|
84
|
+
return 1;
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
const result = await init(realFs, {
|
|
88
|
+
root: readFlag(argv, "root") ?? process.cwd(),
|
|
89
|
+
apiUrl,
|
|
90
|
+
// Only needed to register the project from this machine, which is the
|
|
91
|
+
// exception rather than the rule: the prebuild step init writes is what
|
|
92
|
+
// normally registers, from a build environment that has the key.
|
|
93
|
+
apiKey: readFlag(argv, "api-key") ?? process.env.FEEDBACK_TOOLBAR_API_KEY,
|
|
94
|
+
projectUrl: readFlag(argv, "project-url"),
|
|
95
|
+
environment: readFlag(argv, "environment"),
|
|
96
|
+
registerProject: postRegistration,
|
|
97
|
+
env: process.env,
|
|
98
|
+
});
|
|
99
|
+
if (result.alreadyInstalled) {
|
|
100
|
+
console.log("Already installed. Nothing to change.");
|
|
101
|
+
for (const note of result.notes)
|
|
102
|
+
console.log(` - ${note}`);
|
|
103
|
+
return 0;
|
|
104
|
+
}
|
|
105
|
+
console.log(`Installed via the ${result.strategy} strategy:`);
|
|
106
|
+
for (const change of result.changes)
|
|
107
|
+
console.log(` - ${change}`);
|
|
108
|
+
for (const note of result.notes)
|
|
109
|
+
console.log(` - ${note}`);
|
|
110
|
+
// What is actually left to do, named. The previous sign-off ("redeploy to
|
|
111
|
+
// finish") was wrong in a way that cost a whole issue: the deploy key is
|
|
112
|
+
// a secret, it can only come from the build environment, and without it
|
|
113
|
+
// the prebuild step registers nothing and no project ever appears.
|
|
114
|
+
console.log(result.delivery === "script-tag"
|
|
115
|
+
? "\nDeploy the page to finish. Nothing to install.\n" +
|
|
116
|
+
"Then register the project once, from a terminal or CI:\n" +
|
|
117
|
+
` npx ${PACKAGE_NAME} register --api-url ${apiUrl} --project-url <url> --api-key <key>`
|
|
118
|
+
: "\nTwo things left:\n" +
|
|
119
|
+
" 1. install dependencies, commit, and deploy\n" +
|
|
120
|
+
` 2. set ${API_KEY_ENV_VAR} in your hosting provider's environment variables\n` +
|
|
121
|
+
" (dashboard > Settings > Deploy keys)\n" +
|
|
122
|
+
"The build then registers this deployment itself, on every deploy. The backend\n" +
|
|
123
|
+
"URL needs no configuration: it is committed in package.json and in your layout.");
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
catch (err) {
|
|
127
|
+
if (err instanceof InjectionError) {
|
|
128
|
+
console.error(`Could not install: ${err.message}`);
|
|
129
|
+
return 1;
|
|
130
|
+
}
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function runRegister(argv) {
|
|
135
|
+
const root = readFlag(argv, "root") ?? process.cwd();
|
|
136
|
+
// See USAGE: the prebuild hook passes this so that a registration problem
|
|
137
|
+
// is reported and forgiven rather than failing the host app's deploy.
|
|
138
|
+
const optional = argv.includes("--optional");
|
|
139
|
+
try {
|
|
140
|
+
const outcome = await register({
|
|
141
|
+
apiUrl: readFlag(argv, "api-url"),
|
|
142
|
+
apiKey: readFlag(argv, "api-key"),
|
|
143
|
+
projectUrl: readFlag(argv, "project-url"),
|
|
144
|
+
packageName: readPackageName(root),
|
|
145
|
+
env: process.env,
|
|
146
|
+
registerProject: postRegistration,
|
|
147
|
+
});
|
|
148
|
+
// Two distinct successes, said differently on purpose: somebody running
|
|
149
|
+
// this by hand needs to know whether they just created a project, and
|
|
150
|
+
// somebody reading a build log needs to see that a repeat run is normal.
|
|
151
|
+
if (outcome.created) {
|
|
152
|
+
const named = outcome.projectName ? ` as "${outcome.projectName}"` : "";
|
|
153
|
+
console.log(`Registered ${outcome.projectUrl}${named}. It is in the dashboard now.`);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
console.log(`${outcome.projectUrl} is already registered. Nothing to do.`);
|
|
157
|
+
}
|
|
158
|
+
return 0;
|
|
159
|
+
}
|
|
160
|
+
catch (err) {
|
|
161
|
+
if (err instanceof RegistrationError) {
|
|
162
|
+
if (optional) {
|
|
163
|
+
console.warn(`feedback-toolbar register: skipped - ${err.message}`);
|
|
164
|
+
return 0;
|
|
165
|
+
}
|
|
166
|
+
console.error(`Could not register: ${err.message}`);
|
|
167
|
+
return 1;
|
|
168
|
+
}
|
|
169
|
+
throw err;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
async function main() {
|
|
173
|
+
const argv = process.argv.slice(2);
|
|
174
|
+
const command = argv[0];
|
|
175
|
+
switch (command) {
|
|
176
|
+
case "init":
|
|
177
|
+
return runInit(argv);
|
|
178
|
+
case "register":
|
|
179
|
+
return runRegister(argv);
|
|
180
|
+
case "help":
|
|
181
|
+
case "--help":
|
|
182
|
+
case "-h":
|
|
183
|
+
console.log(USAGE);
|
|
184
|
+
return 0;
|
|
185
|
+
default:
|
|
186
|
+
console.error(USAGE);
|
|
187
|
+
return 1;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
main().then((code) => {
|
|
191
|
+
process.exitCode = code;
|
|
192
|
+
}, (err) => {
|
|
193
|
+
console.error(err);
|
|
194
|
+
process.exitCode = 1;
|
|
195
|
+
});
|
|
196
|
+
//# sourceMappingURL=bin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bin.js","sourceRoot":"","sources":["../../src/cli/bin.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/fs.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface InstallerFs {
|
|
2
|
+
exists(path: string): boolean;
|
|
3
|
+
readFile(path: string): string;
|
|
4
|
+
writeFile(path: string, contents: string): void;
|
|
5
|
+
}
|
|
6
|
+
/** An in-memory tree, which is what the installer's tests run against. */
|
|
7
|
+
export declare class MemoryFs implements InstallerFs {
|
|
8
|
+
readonly files: Map<string, string>;
|
|
9
|
+
constructor(files?: Record<string, string>);
|
|
10
|
+
exists(path: string): boolean;
|
|
11
|
+
readFile(path: string): string;
|
|
12
|
+
writeFile(path: string, contents: string): void;
|
|
13
|
+
}
|
|
14
|
+
/** Join path segments with `/`, collapsing the duplicates that creep in. */
|
|
15
|
+
export declare function joinPath(...parts: string[]): string;
|
|
16
|
+
//# sourceMappingURL=fs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/cli/fs.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACjD;AAED,0EAA0E;AAC1E,qBAAa,QAAS,YAAW,WAAW;IAC1C,QAAQ,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;gBAExB,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM;IAI9C,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAI7B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAM9B,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI;CAGhD;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CAAC,GAAG,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,CAKnD"}
|
package/dist/cli/fs.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// The filesystem the installer talks to, as an interface.
|
|
2
|
+
//
|
|
3
|
+
// The installer edits somebody else's repository, so its tests need to run
|
|
4
|
+
// against a tree they fully control, and a codemod tested only against the
|
|
5
|
+
// real disk is a codemod nobody dares change. Everything below the interface
|
|
6
|
+
// is a handful of thin wrappers; everything above it is pure logic.
|
|
7
|
+
/** An in-memory tree, which is what the installer's tests run against. */
|
|
8
|
+
export class MemoryFs {
|
|
9
|
+
files;
|
|
10
|
+
constructor(files = {}) {
|
|
11
|
+
this.files = new Map(Object.entries(files));
|
|
12
|
+
}
|
|
13
|
+
exists(path) {
|
|
14
|
+
return this.files.has(path);
|
|
15
|
+
}
|
|
16
|
+
readFile(path) {
|
|
17
|
+
const contents = this.files.get(path);
|
|
18
|
+
if (contents === undefined)
|
|
19
|
+
throw new Error(`no such file: ${path}`);
|
|
20
|
+
return contents;
|
|
21
|
+
}
|
|
22
|
+
writeFile(path, contents) {
|
|
23
|
+
this.files.set(path, contents);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** Join path segments with `/`, collapsing the duplicates that creep in. */
|
|
27
|
+
export function joinPath(...parts) {
|
|
28
|
+
return parts
|
|
29
|
+
.filter((p) => p.length > 0)
|
|
30
|
+
.join("/")
|
|
31
|
+
.replace(/\/{2,}/g, "/");
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=fs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fs.js","sourceRoot":"","sources":["../../src/cli/fs.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAC1D,EAAE;AACF,2EAA2E;AAC3E,2EAA2E;AAC3E,6EAA6E;AAC7E,oEAAoE;AAQpE,0EAA0E;AAC1E,MAAM,OAAO,QAAQ;IACV,KAAK,CAAsB;IAEpC,YAAY,QAAgC,EAAE;QAC5C,IAAI,CAAC,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IAC9C,CAAC;IAED,MAAM,CAAC,IAAY;QACjB,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;IAED,QAAQ,CAAC,IAAY;QACnB,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACtC,IAAI,QAAQ,KAAK,SAAS;YAAE,MAAM,IAAI,KAAK,CAAC,iBAAiB,IAAI,EAAE,CAAC,CAAC;QACrE,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,SAAS,CAAC,IAAY,EAAE,QAAgB;QACtC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IACjC,CAAC;CACF;AAED,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CAAC,GAAG,KAAe;IACzC,OAAO,KAAK;SACT,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;SAC3B,IAAI,CAAC,GAAG,CAAC;SACT,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { API_URL_ENV_VAR } from "../config.js";
|
|
2
|
+
import { type InstallerFs } from "./fs.js";
|
|
3
|
+
export { API_URL_ENV_VAR };
|
|
4
|
+
import { type Delivery, type InjectionStrategy } from "./strategies.js";
|
|
5
|
+
import type { RegisterProjectTransport } from "./registration.js";
|
|
6
|
+
/**
|
|
7
|
+
* The command wired into the host app's build, and the marker that says it is
|
|
8
|
+
* already wired. `--optional` is load-bearing: see the comment on
|
|
9
|
+
* `wirePrebuild` for why a registration failure must not fail a deploy.
|
|
10
|
+
*/
|
|
11
|
+
export declare const REGISTER_COMMAND = "feedback-toolbar register";
|
|
12
|
+
/**
|
|
13
|
+
* The prebuild line, with the backend URL BAKED IN.
|
|
14
|
+
*
|
|
15
|
+
* This is the second half of #11's reopened acceptance criterion, and the
|
|
16
|
+
* reason it reopened twice. `register` resolves its backend URL from a flag or
|
|
17
|
+
* from the build environment - and the build environment does not have it:
|
|
18
|
+
* `init` writes `NEXT_PUBLIC_FEEDBACK_TOOLBAR_API_URL` into `.env.local`,
|
|
19
|
+
* which create-next-app gitignores, and a plain node process does not load a
|
|
20
|
+
* dotenv file anyway. So on a real Vercel build the prebuild step threw "no
|
|
21
|
+
* backend URL", `--optional` turned that into one warning line, and no project
|
|
22
|
+
* ever appeared.
|
|
23
|
+
*
|
|
24
|
+
* package.json IS committed, so putting the URL here is what carries it into
|
|
25
|
+
* the build. It is a public URL, not a credential; the key still comes from
|
|
26
|
+
* `FEEDBACK_TOOLBAR_API_KEY` in the build environment and from nowhere else.
|
|
27
|
+
*/
|
|
28
|
+
export declare function prebuildScript(apiUrl: string): string;
|
|
29
|
+
export interface InitOptions {
|
|
30
|
+
/** Repository root to install into. */
|
|
31
|
+
root: string;
|
|
32
|
+
/** Base URL of the BitVea Feedback backend. */
|
|
33
|
+
apiUrl: string;
|
|
34
|
+
/**
|
|
35
|
+
* Deploy key authorising registration. Optional: without it the install
|
|
36
|
+
* still happens, and only the registration step is skipped - the prebuild
|
|
37
|
+
* step this writes registers on the next build instead.
|
|
38
|
+
*/
|
|
39
|
+
apiKey?: string;
|
|
40
|
+
/** Deployment URL to register the project under, when it is known. */
|
|
41
|
+
projectUrl?: string;
|
|
42
|
+
/**
|
|
43
|
+
* What a script-tag install declares as its environment. Ignored by the npm
|
|
44
|
+
* route, which reads the host build's own `NEXT_PUBLIC_VERCEL_ENV`.
|
|
45
|
+
*/
|
|
46
|
+
environment?: string;
|
|
47
|
+
/** Injected so tests do not have to reach the network. */
|
|
48
|
+
registerProject?: RegisterProjectTransport;
|
|
49
|
+
/** Env bag, for the Vercel git metadata. Defaults to `process.env`. */
|
|
50
|
+
env?: Record<string, string | undefined>;
|
|
51
|
+
strategies?: readonly InjectionStrategy[];
|
|
52
|
+
}
|
|
53
|
+
export interface InitResult {
|
|
54
|
+
/** What actually changed, one line each, for the CLI to print. */
|
|
55
|
+
changes: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Things worth saying that are not changes: a project that was already
|
|
58
|
+
* registered, or a registration that did not go through. These are kept
|
|
59
|
+
* apart from `changes` because `alreadyInstalled` is derived from that
|
|
60
|
+
* list, and a re-run must still report itself as a no-op even though it
|
|
61
|
+
* talks to the backend every time.
|
|
62
|
+
*/
|
|
63
|
+
notes: string[];
|
|
64
|
+
/** True when nothing needed doing, which is what a re-run should report. */
|
|
65
|
+
alreadyInstalled: boolean;
|
|
66
|
+
strategy: string;
|
|
67
|
+
/**
|
|
68
|
+
* How the host app gets the code. Reported because it decides what the
|
|
69
|
+
* developer has to do next: an npm install needs a dependency install and a
|
|
70
|
+
* redeploy, a script tag needs neither.
|
|
71
|
+
*/
|
|
72
|
+
delivery: Delivery;
|
|
73
|
+
}
|
|
74
|
+
export declare function init(fs: InstallerFs, options: InitOptions): Promise<InitResult>;
|
|
75
|
+
//# sourceMappingURL=init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AAkCA,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAC5C,OAAO,EAAY,KAAK,WAAW,EAAE,MAAM,MAAM,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,CAAC;AAE3B,OAAO,EAML,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACvB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAK/D;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,8BAA8B,CAAC;AAE5D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,MAAM,WAAW,WAAW;IAC1B,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,eAAe,CAAC,EAAE,wBAAwB,CAAC;IAC3C,uEAAuE;IACvE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,UAAU,CAAC,EAAE,SAAS,iBAAiB,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,UAAU;IACzB,kEAAkE;IAClE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB;;;;;;OAMG;IACH,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4EAA4E;IAC5E,gBAAgB,EAAE,OAAO,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,wBAAsB,IAAI,CACxB,EAAE,EAAE,WAAW,EACf,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,UAAU,CAAC,CA4KrB"}
|