@mindbowser_inc/ui-feedback-plugin 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindbowser_inc/ui-feedback-plugin",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "A floating widget for collecting annotated UI feedback on web prototypes — Figma-style comments, screenshots, reply threads, Firebase/Supabase/AWS support",
5
5
  "main": "dist/ui-feedback-plugin.umd.js",
6
6
  "module": "dist/ui-feedback-plugin.es.js",
@@ -43,7 +43,6 @@
43
43
  "homepage": "https://github.com/vishmindbowser/ui-feedback-plugin#readme",
44
44
  "devDependencies": {
45
45
  "@aws-sdk/client-s3": "^3.1075.0",
46
- "@aws-sdk/credential-provider-cognito-identity": "^3.972.48",
47
46
  "@supabase/supabase-js": "^2.108.2",
48
47
  "firebase": "^10.12.0",
49
48
  "html2canvas-pro": "^2.2.1",
@@ -52,15 +51,11 @@
52
51
  "vite-plugin-dts": "^3.9.1"
53
52
  },
54
53
  "peerDependencies": {
55
- "@aws-sdk/client-s3": "^3.0.0",
56
- "@aws-sdk/credential-provider-cognito-identity": "^3.0.0"
54
+ "@aws-sdk/client-s3": "^3.0.0"
57
55
  },
58
56
  "peerDependenciesMeta": {
59
57
  "@aws-sdk/client-s3": {
60
58
  "optional": true
61
- },
62
- "@aws-sdk/credential-provider-cognito-identity": {
63
- "optional": true
64
59
  }
65
60
  }
66
61
  }
@@ -1,134 +0,0 @@
1
- var h = Object.defineProperty;
2
- var u = (c, t, s) => t in c ? h(c, t, { enumerable: !0, configurable: !0, writable: !0, value: s }) : c[t] = s;
3
- var l = (c, t, s) => u(c, typeof t != "symbol" ? t + "" : t, s);
4
- class g {
5
- constructor(t) {
6
- l(this, "cfg");
7
- l(this, "_s3", null);
8
- this.cfg = t;
9
- }
10
- async s3() {
11
- if (this._s3) return this._s3;
12
- const [{ S3Client: t }, { fromCognitoIdentityPool: s }] = await Promise.all([
13
- import("@aws-sdk/client-s3"),
14
- import("@aws-sdk/credential-provider-cognito-identity")
15
- ]);
16
- return this._s3 = new t({
17
- region: this.cfg.region,
18
- credentials: s({
19
- identityPoolId: this.cfg.identityPoolId,
20
- clientConfig: { region: this.cfg.region }
21
- })
22
- }), this._s3;
23
- }
24
- async pageHash(t) {
25
- const s = new TextEncoder().encode(t), e = await crypto.subtle.digest("SHA-256", s);
26
- return Array.from(new Uint8Array(e)).map((n) => n.toString(16).padStart(2, "0")).join("").slice(0, 16);
27
- }
28
- async put(t, s, e) {
29
- const { PutObjectCommand: n } = await import("@aws-sdk/client-s3");
30
- await (await this.s3()).send(new n({
31
- Bucket: this.cfg.bucket,
32
- Key: t,
33
- Body: s,
34
- ContentType: e
35
- }));
36
- }
37
- async getJson(t) {
38
- const { GetObjectCommand: s } = await import("@aws-sdk/client-s3"), i = await (await (await this.s3()).send(new s({ Bucket: this.cfg.bucket, Key: t }))).Body.transformToString();
39
- return JSON.parse(i);
40
- }
41
- async listKeys(t) {
42
- const { ListObjectsV2Command: s } = await import("@aws-sdk/client-s3"), e = await this.s3(), n = [];
43
- let i;
44
- do {
45
- const o = await e.send(new s({
46
- Bucket: this.cfg.bucket,
47
- Prefix: t,
48
- ContinuationToken: i
49
- }));
50
- for (const a of o.Contents ?? [])
51
- a.Key && n.push(a.Key);
52
- i = o.NextContinuationToken;
53
- } while (i);
54
- return n;
55
- }
56
- indexKey(t) {
57
- return `ufp/_index/${t}.json`;
58
- }
59
- async lookupComment(t) {
60
- const { p: s, h: e } = await this.getJson(this.indexKey(t));
61
- return { key: `ufp/${s}/${e}/comments/${t}.json` };
62
- }
63
- async addComment(t) {
64
- const s = await this.pageHash(t.pageUrl), e = crypto.randomUUID();
65
- return await Promise.all([
66
- this.put(
67
- `ufp/${t.projectKey}/${s}/comments/${e}.json`,
68
- JSON.stringify({ id: e, ...t }),
69
- "application/json"
70
- ),
71
- this.put(
72
- this.indexKey(e),
73
- JSON.stringify({ p: t.projectKey, h: s }),
74
- "application/json"
75
- )
76
- ]), e;
77
- }
78
- async getComments(t, s) {
79
- const e = await this.pageHash(t), n = await this.listKeys(`ufp/${s}/${e}/comments/`);
80
- return (await Promise.all(
81
- n.map(
82
- (o) => this.getJson(o).catch(() => null)
83
- )
84
- )).filter((o) => o !== null && !o.deleted).sort((o, a) => a.createdAt - o.createdAt);
85
- }
86
- async updateComment(t, s) {
87
- const { key: e } = await this.lookupComment(t), n = await this.getJson(e);
88
- await this.put(e, JSON.stringify({ ...n, ...s }), "application/json");
89
- }
90
- async deleteComment(t) {
91
- const { key: s } = await this.lookupComment(t), e = await this.getJson(s);
92
- await this.put(s, JSON.stringify({ ...e, deleted: !0 }), "application/json");
93
- }
94
- async addReply(t) {
95
- const s = crypto.randomUUID();
96
- return await this.put(
97
- `ufp/replies/${t.commentId}/${s}.json`,
98
- JSON.stringify({ id: s, ...t }),
99
- "application/json"
100
- ), s;
101
- }
102
- async getReplies(t) {
103
- const s = await this.listKeys(`ufp/replies/${t}/`);
104
- return (await Promise.all(
105
- s.map((n) => this.getJson(n).catch(() => null))
106
- )).filter((n) => n !== null).sort((n, i) => n.createdAt - i.createdAt);
107
- }
108
- subscribeToComments(t, s, e) {
109
- let n = !0;
110
- const i = this.cfg.pollInterval ?? 5e3, o = async () => {
111
- if (n) {
112
- try {
113
- const a = await this.getComments(t, s);
114
- n && e(a);
115
- } catch (a) {
116
- console.error("[ui-feedback-plugin] AWS poll error:", a);
117
- }
118
- n && setTimeout(o, i);
119
- }
120
- };
121
- return o(), () => {
122
- n = !1;
123
- };
124
- }
125
- async uploadScreenshot(t, s) {
126
- const { p: e, h: n } = await this.getJson(this.indexKey(t)), i = `ufp/${e}/${n}/screenshots/${t}.png`, o = s.split(",")[1], a = atob(o), p = new Uint8Array(a.length);
127
- for (let r = 0; r < a.length; r++) p[r] = a.charCodeAt(r);
128
- return await this.put(i, p, "image/png"), `https://${this.cfg.bucket}.s3.${this.cfg.region}.amazonaws.com/${i}`;
129
- }
130
- }
131
- export {
132
- g as AWSAdapter
133
- };
134
- //# sourceMappingURL=aws-DieAIoX4.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"aws-DieAIoX4.mjs","sources":["../src/adapters/aws.ts"],"sourcesContent":["// AWS Cognito Identity Pool adapter — browser talks directly to S3, no backend required.\n// Requires @aws-sdk/client-s3 and @aws-sdk/credential-providers (both in devDependencies).\n//\n// ── AWS SETUP ────────────────────────────────────────────────────────────────────\n//\n// 1. CREATE S3 BUCKET\n// - Keep \"Block all public access\" ON (we'll add a narrow public policy for screenshots).\n// - Enable versioning (optional, helpful for recovery).\n//\n// 2. CONFIGURE CORS (required for browser access)\n// Go to Bucket → Permissions → CORS and paste:\n// [\n// {\n// \"AllowedOrigins\": [\"https://your-domain.com\"],\n// \"AllowedMethods\": [\"GET\", \"PUT\", \"HEAD\"],\n// \"AllowedHeaders\": [\"*\"],\n// \"ExposeHeaders\": [\"ETag\"],\n// \"MaxAgeSeconds\": 3000\n// }\n// ]\n// Use \"*\" for AllowedOrigins during local dev, restrict to real domains in production.\n//\n// 3. ADD BUCKET POLICY (public read for screenshots only)\n// Go to Bucket → Permissions → Bucket policy and paste (replace YOUR_BUCKET):\n// {\n// \"Version\": \"2012-10-17\",\n// \"Statement\": [{\n// \"Sid\": \"PublicReadScreenshots\",\n// \"Effect\": \"Allow\",\n// \"Principal\": \"*\",\n// \"Action\": \"s3:GetObject\",\n// \"Resource\": \"arn:aws:s3:::YOUR_BUCKET/ufp/*/screenshots/*\"\n// }]\n// }\n// Comments and replies remain private (only accessible via Cognito credentials).\n//\n// 4. CREATE IAM POLICY (least privilege — see README for full JSON)\n// Allows:\n// - s3:PutObject on ufp/*/comments/*.json (max 50 KB)\n// - s3:PutObject on ufp/replies/*/*.json (max 50 KB)\n// - s3:PutObject on ufp/_index/*.json (max 50 KB)\n// - s3:PutObject on ufp/*/screenshots/*.png (max 3 MB)\n// - s3:GetObject on ufp/*\n// - s3:ListBucket on the bucket with prefix ufp/*\n// NO s3:DeleteObject — soft delete is used instead.\n//\n// 5. CREATE IAM ROLE\n// - Trusted entity: Web Identity\n// - Identity provider: Amazon Cognito\n// - Attach the policy from step 4\n//\n// 6. CREATE COGNITO IDENTITY POOL\n// - Enable unauthenticated identities\n// - Unauthenticated role: the IAM role from step 5\n// - Authenticated role: none (or a different role)\n// - Copy the Identity Pool ID (format: us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx)\n//\n// 7. CONFIGURE THE PLUGIN\n// initFeedbackPlugin({\n// backend: {\n// provider: 'aws',\n// region: 'us-east-1',\n// bucket: 'your-bucket-name',\n// identityPoolId: 'us-east-1:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',\n// },\n// projectKey: 'my-project',\n// })\n//\n// ── DATA LAYOUT IN S3 ────────────────────────────────────────────────────────────\n//\n// ufp/{projectKey}/{pageHash}/comments/{commentId}.json\n// ufp/{projectKey}/{pageHash}/screenshots/{commentId}.png ← publicly readable\n// ufp/replies/{commentId}/{replyId}.json\n// ufp/_index/{commentId}.json ← { \"p\": projectKey, \"h\": pageHash }\n//\n// ── SOFT DELETE ──────────────────────────────────────────────────────────────────\n// deleteComment overwrites the comment JSON with { ...comment, deleted: true }.\n// The file stays in S3 but is filtered out on read. No s3:DeleteObject needed.\n//\n// ── REAL-TIME ────────────────────────────────────────────────────────────────────\n// S3 has no push. subscribeToComments polls on a configurable interval (default 5s).\n\nimport type { FeedbackComment, Reply, DatabaseAdapter, ScreenshotAdapter, AWSProviderConfig } from '../core/types'\nimport type { S3Client as S3ClientType } from '@aws-sdk/client-s3'\n\nexport class AWSAdapter implements DatabaseAdapter, ScreenshotAdapter {\n private cfg: AWSProviderConfig\n private _s3: S3ClientType | null = null\n\n constructor(config: AWSProviderConfig) {\n this.cfg = config\n }\n\n private async s3(): Promise<S3ClientType> {\n if (this._s3) return this._s3\n const [{ S3Client }, { fromCognitoIdentityPool }] = await Promise.all([\n import('@aws-sdk/client-s3'),\n import('@aws-sdk/credential-provider-cognito-identity'),\n ])\n this._s3 = new S3Client({\n region: this.cfg.region,\n credentials: fromCognitoIdentityPool({\n identityPoolId: this.cfg.identityPoolId,\n clientConfig: { region: this.cfg.region },\n }),\n })\n return this._s3\n }\n\n private async pageHash(url: string): Promise<string> {\n const data = new TextEncoder().encode(url)\n const buf = await crypto.subtle.digest('SHA-256', data)\n return Array.from(new Uint8Array(buf))\n .map(b => b.toString(16).padStart(2, '0'))\n .join('')\n .slice(0, 16)\n }\n\n private async put(key: string, body: string | Uint8Array, contentType: string): Promise<void> {\n const { PutObjectCommand } = await import('@aws-sdk/client-s3')\n const client = await this.s3()\n await client.send(new PutObjectCommand({\n Bucket: this.cfg.bucket,\n Key: key,\n Body: body,\n ContentType: contentType,\n }))\n }\n\n private async getJson<T>(key: string): Promise<T> {\n const { GetObjectCommand } = await import('@aws-sdk/client-s3')\n const client = await this.s3()\n const res = await client.send(new GetObjectCommand({ Bucket: this.cfg.bucket, Key: key }))\n const text = await res.Body!.transformToString()\n return JSON.parse(text) as T\n }\n\n private async listKeys(prefix: string): Promise<string[]> {\n const { ListObjectsV2Command } = await import('@aws-sdk/client-s3')\n const client = await this.s3()\n const keys: string[] = []\n let token: string | undefined\n do {\n const res = await client.send(new ListObjectsV2Command({\n Bucket: this.cfg.bucket,\n Prefix: prefix,\n ContinuationToken: token,\n }))\n for (const obj of res.Contents ?? []) {\n if (obj.Key) keys.push(obj.Key)\n }\n token = res.NextContinuationToken\n } while (token)\n return keys\n }\n\n private indexKey(id: string) {\n return `ufp/_index/${id}.json`\n }\n\n private async lookupComment(id: string): Promise<{ key: string }> {\n const { p, h } = await this.getJson<{ p: string; h: string }>(this.indexKey(id))\n return { key: `ufp/${p}/${h}/comments/${id}.json` }\n }\n\n async addComment(comment: Omit<FeedbackComment, 'id'>): Promise<string> {\n const hash = await this.pageHash(comment.pageUrl)\n const id = crypto.randomUUID()\n await Promise.all([\n this.put(\n `ufp/${comment.projectKey}/${hash}/comments/${id}.json`,\n JSON.stringify({ id, ...comment }),\n 'application/json'\n ),\n this.put(\n this.indexKey(id),\n JSON.stringify({ p: comment.projectKey, h: hash }),\n 'application/json'\n ),\n ])\n return id\n }\n\n async getComments(pageUrl: string, projectKey: string): Promise<FeedbackComment[]> {\n const hash = await this.pageHash(pageUrl)\n const keys = await this.listKeys(`ufp/${projectKey}/${hash}/comments/`)\n const rows = await Promise.all(\n keys.map(k =>\n this.getJson<FeedbackComment & { deleted?: boolean }>(k).catch(() => null)\n )\n )\n return rows\n .filter((r): r is FeedbackComment => r !== null && !r.deleted)\n .sort((a, b) => b.createdAt - a.createdAt)\n }\n\n async updateComment(id: string, data: Partial<FeedbackComment>): Promise<void> {\n const { key } = await this.lookupComment(id)\n const existing = await this.getJson<FeedbackComment>(key)\n await this.put(key, JSON.stringify({ ...existing, ...data }), 'application/json')\n }\n\n async deleteComment(id: string): Promise<void> {\n const { key } = await this.lookupComment(id)\n const existing = await this.getJson<FeedbackComment>(key)\n // Soft delete — overwrite with deleted flag. No s3:DeleteObject permission needed.\n await this.put(key, JSON.stringify({ ...existing, deleted: true }), 'application/json')\n }\n\n async addReply(reply: Omit<Reply, 'id'>): Promise<string> {\n const id = crypto.randomUUID()\n await this.put(\n `ufp/replies/${reply.commentId}/${id}.json`,\n JSON.stringify({ id, ...reply }),\n 'application/json'\n )\n return id\n }\n\n async getReplies(commentId: string): Promise<Reply[]> {\n const keys = await this.listKeys(`ufp/replies/${commentId}/`)\n const rows = await Promise.all(\n keys.map(k => this.getJson<Reply>(k).catch(() => null))\n )\n return rows\n .filter((r): r is Reply => r !== null)\n .sort((a, b) => a.createdAt - b.createdAt)\n }\n\n subscribeToComments(\n pageUrl: string,\n projectKey: string,\n callback: (comments: FeedbackComment[]) => void\n ): () => void {\n let active = true\n const interval = this.cfg.pollInterval ?? 5000\n\n const poll = async () => {\n if (!active) return\n try {\n const comments = await this.getComments(pageUrl, projectKey)\n if (active) callback(comments)\n } catch (err) {\n console.error('[ui-feedback-plugin] AWS poll error:', err)\n }\n if (active) setTimeout(poll, interval)\n }\n\n poll()\n return () => { active = false }\n }\n\n async uploadScreenshot(commentId: string, dataUrl: string): Promise<string> {\n const { p, h } = await this.getJson<{ p: string; h: string }>(this.indexKey(commentId))\n const key = `ufp/${p}/${h}/screenshots/${commentId}.png`\n\n const base64 = dataUrl.split(',')[1]\n const binary = atob(base64)\n const bytes = new Uint8Array(binary.length)\n for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i)\n\n await this.put(key, bytes, 'image/png')\n return `https://${this.cfg.bucket}.s3.${this.cfg.region}.amazonaws.com/${key}`\n }\n}\n"],"names":["AWSAdapter","config","__publicField","S3Client","fromCognitoIdentityPool","url","data","buf","b","key","body","contentType","PutObjectCommand","GetObjectCommand","text","prefix","ListObjectsV2Command","client","keys","token","res","obj","id","p","h","comment","hash","pageUrl","projectKey","k","r","a","existing","reply","commentId","callback","active","interval","poll","comments","err","dataUrl","base64","binary","bytes","i"],"mappings":"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"}