jat-feedback 3.2.0 → 3.3.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": "jat-feedback",
3
- "version": "3.2.0",
3
+ "version": "3.3.0",
4
4
  "description": "Embeddable feedback widget for bug reports and feature requests. Captures screenshots, console logs, and user context as a web component.",
5
5
  "type": "module",
6
6
  "main": "dist/jat-feedback.js",
@@ -82,10 +82,8 @@ interface WebhookPayload {
82
82
 
83
83
  // Tables and the columns that JAT is allowed to update.
84
84
  // Add more tables here if you want JAT to sync status for other record types.
85
- // "feedback_reports" kept as alias for backward compat with pre-v3 callers.
86
85
  const TABLE_CONFIG: Record<string, { statusCol: string; taskIdCol: string }> = {
87
86
  project_tasks: { statusCol: "status", taskIdCol: "jat_task_id" },
88
- feedback_reports: { statusCol: "status", taskIdCol: "jat_task_id" },
89
87
  }
90
88
 
91
89
  Deno.serve(async (req) => {
@@ -139,8 +137,14 @@ Deno.serve(async (req) => {
139
137
  )
140
138
  }
141
139
 
142
- // Resolve actual table name"feedback_reports" alias maps to "project_tasks"
143
- const actualTable = reference_table === "feedback_reports" ? "project_tasks" : reference_table
140
+ // Skip transient voice-pipeline statusesonly sync when the task
141
+ // has a meaningful status that JAT should know about.
142
+ if (data.status === "transcribing" || data.status === "failed") {
143
+ return new Response(
144
+ JSON.stringify({ skipped: true, reason: `Status "${data.status}" is transient, not synced to JAT` }),
145
+ { status: 200, headers: { "Content-Type": "application/json" } },
146
+ )
147
+ }
144
148
 
145
149
  // Build the update object from the payload fields
146
150
  const update: Record<string, unknown> = {}
@@ -162,14 +166,14 @@ Deno.serve(async (req) => {
162
166
 
163
167
  const supabase = createClient(supabaseUrl, supabaseServiceKey)
164
168
  const result = await supabase
165
- .from(actualTable)
169
+ .from(reference_table)
166
170
  .update(update)
167
171
  .eq("id", reference_id)
168
172
  .select("id")
169
173
 
170
174
  if (result.error) {
171
175
  console.error(`JAT webhook failed: ${result.error.message}`, {
172
- table: actualTable,
176
+ table: reference_table,
173
177
  reference_id,
174
178
  update,
175
179
  })
@@ -180,11 +184,11 @@ Deno.serve(async (req) => {
180
184
  }
181
185
 
182
186
  if (!result.data || result.data.length === 0) {
183
- console.warn(`JAT webhook: no rows matched ${actualTable}[${reference_id}]`, update)
187
+ console.warn(`JAT webhook: no rows matched ${reference_table}[${reference_id}]`, update)
184
188
  return new Response(
185
189
  JSON.stringify({
186
- error: `No rows matched: ${actualTable} id=${reference_id}`,
187
- table: actualTable,
190
+ error: `No rows matched: ${reference_table} id=${reference_id}`,
191
+ table: reference_table,
188
192
  id: reference_id,
189
193
  rowsAffected: 0,
190
194
  }),
@@ -192,12 +196,12 @@ Deno.serve(async (req) => {
192
196
  )
193
197
  }
194
198
 
195
- console.log(`JAT webhook: ${event} → ${actualTable}[${reference_id}] (${result.data.length} row(s))`, update)
199
+ console.log(`JAT webhook: ${event} → ${reference_table}[${reference_id}] (${result.data.length} row(s))`, update)
196
200
 
197
201
  return new Response(
198
202
  JSON.stringify({
199
203
  success: true,
200
- table: actualTable,
204
+ table: reference_table,
201
205
  id: reference_id,
202
206
  updated: update,
203
207
  rowsAffected: result.data.length,
@@ -0,0 +1,78 @@
1
+ -- jat-feedback v3.3.0 — Realtime + voice transcription support
2
+ --
3
+ -- Enables Supabase Realtime on project_tasks (REPLICA IDENTITY FULL),
4
+ -- adds audio_url column for voice recordings, expands status CHECK
5
+ -- to include 'transcribing' and 'failed', and creates a private
6
+ -- Storage bucket for voice audio files.
7
+ --
8
+ -- This is an additive migration (nullable column + new bucket + constraint update).
9
+ -- Safe for 3.x — no existing data affected.
10
+ --
11
+ -- Apply to each consuming project:
12
+ -- cp node_modules/jat-feedback/supabase/migrations/3.3.0_realtime_voice.sql \
13
+ -- supabase/migrations/$(date +%Y%m%d%H%M%S)_feedback_3_3_0.sql
14
+ -- supabase db push
15
+ --
16
+ -- After applying, enable Realtime for project_tasks in the Supabase dashboard:
17
+ -- Database → Replication → supabase_realtime → toggle project_tasks ON
18
+
19
+ -- ============================================================================
20
+ -- 1. Enable Realtime (REPLICA IDENTITY FULL for complete row data in changes)
21
+ -- ============================================================================
22
+
23
+ ALTER TABLE project_tasks REPLICA IDENTITY FULL;
24
+
25
+ -- ============================================================================
26
+ -- 2. Add audio_url column (voice recording path in Storage)
27
+ -- ============================================================================
28
+
29
+ -- Separate from recording_url (which stores rrweb session replay data).
30
+ -- audio_url points to raw voice audio files in the voice-recordings bucket.
31
+ ALTER TABLE project_tasks ADD COLUMN IF NOT EXISTS audio_url TEXT;
32
+
33
+ -- ============================================================================
34
+ -- 3. Expand status CHECK constraint to include voice transcription states
35
+ -- ============================================================================
36
+
37
+ -- Drop the existing constraint (inherited from 1.0.0_feedback_reports.sql)
38
+ -- and recreate with additional values for the voice pipeline.
39
+ ALTER TABLE project_tasks DROP CONSTRAINT IF EXISTS feedback_reports_status_check;
40
+
41
+ ALTER TABLE project_tasks ADD CONSTRAINT project_tasks_status_check
42
+ CHECK (status IN (
43
+ 'submitted',
44
+ 'in_progress',
45
+ 'completed',
46
+ 'accepted',
47
+ 'rejected',
48
+ 'wontfix',
49
+ 'closed',
50
+ 'transcribing',
51
+ 'failed'
52
+ ));
53
+
54
+ -- ============================================================================
55
+ -- 4. Storage bucket for voice recordings (private)
56
+ -- ============================================================================
57
+
58
+ INSERT INTO storage.buckets (id, name, public)
59
+ VALUES ('voice-recordings', 'voice-recordings', false)
60
+ ON CONFLICT (id) DO NOTHING;
61
+
62
+ -- RLS: service role can read and write
63
+ CREATE POLICY "Service role can manage voice recordings"
64
+ ON storage.objects FOR ALL TO service_role
65
+ USING (bucket_id = 'voice-recordings')
66
+ WITH CHECK (bucket_id = 'voice-recordings');
67
+
68
+ -- RLS: authenticated users can upload voice recordings
69
+ CREATE POLICY "Authenticated users can upload voice recordings"
70
+ ON storage.objects FOR INSERT TO authenticated
71
+ WITH CHECK (bucket_id = 'voice-recordings');
72
+
73
+ -- RLS: authenticated users can read own voice recordings
74
+ CREATE POLICY "Authenticated users can read voice recordings"
75
+ ON storage.objects FOR SELECT TO authenticated
76
+ USING (bucket_id = 'voice-recordings');
77
+
78
+ -- Anon role is blocked by default (no policy = no access)