@designfever/web-review-kit 0.1.0 → 0.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.
Files changed (45) hide show
  1. package/README.md +77 -179
  2. package/dist/{chunk-U5K2YGGL.js → chunk-I76WEDLA.js} +2248 -2722
  3. package/dist/chunk-I76WEDLA.js.map +1 -0
  4. package/dist/index.cjs +2346 -2603
  5. package/dist/index.cjs.map +1 -1
  6. package/dist/index.d.cts +10 -6
  7. package/dist/index.d.ts +10 -6
  8. package/dist/index.js +220 -7
  9. package/dist/index.js.map +1 -1
  10. package/dist/react-shell.cjs +8953 -6632
  11. package/dist/react-shell.cjs.map +1 -1
  12. package/dist/react-shell.d.cts +8 -3
  13. package/dist/react-shell.d.ts +8 -3
  14. package/dist/react-shell.js +5956 -3636
  15. package/dist/react-shell.js.map +1 -1
  16. package/dist/{types-D_mNjOHx.d.cts → types-Cf2x5ky6.d.cts} +8 -14
  17. package/dist/{types-D_mNjOHx.d.ts → types-Cf2x5ky6.d.ts} +8 -14
  18. package/dist/vite.cjs +186 -0
  19. package/dist/vite.cjs.map +1 -0
  20. package/dist/vite.d.cts +16 -0
  21. package/dist/vite.d.ts +16 -0
  22. package/dist/vite.js +161 -0
  23. package/dist/vite.js.map +1 -0
  24. package/docs/README.md +21 -30
  25. package/docs/adaptor.sample.ts +182 -0
  26. package/docs/architecture.md +125 -0
  27. package/docs/db-setup.md +253 -0
  28. package/docs/figma-overlay.md +52 -0
  29. package/docs/grid-overlay.md +38 -0
  30. package/docs/installation.md +108 -40
  31. package/package.json +22 -6
  32. package/dist/chunk-U5K2YGGL.js.map +0 -1
  33. package/docs/adapter-handoff.md +0 -146
  34. package/docs/concept.md +0 -102
  35. package/docs/df-sheet-adapter.md +0 -336
  36. package/docs/df-sheet-next.md +0 -222
  37. package/docs/initial-plan.md +0 -226
  38. package/docs/package-split-checkpoint.md +0 -79
  39. package/docs/presence-handoff.md +0 -138
  40. package/docs/review-feedback-2026-06-20.md +0 -267
  41. package/docs/smoke-baseline-2026-06-20.md +0 -41
  42. package/docs/stabilize-ui-work-guide.md +0 -243
  43. package/docs/supabase-presence.md +0 -198
  44. package/docs/supabase-review-items.md +0 -365
  45. package/docs/supabase.md +0 -205
package/dist/index.d.cts CHANGED
@@ -1,11 +1,8 @@
1
- import { L as LocalAdapterOptions, W as WebReviewKitAdapter, D as DfSheetAdapterOptions, S as SupabaseReviewAdapterOptions, R as ReviewWorkflowStatus, a as ReviewItemStatus, b as WebReviewKitOptions, c as WebReviewKitController, d as ReviewViewportPreset, V as ViewportSize, e as ReviewItem, N as NumberedReviewItem, f as ReviewItemScope } from './types-D_mNjOHx.cjs';
2
- export { g as DomAnchor, h as DomAnchorCandidate, i as DomAnchorStrategy, j as DomSourceHint, k as RelativeSelection, l as ReviewItemKind, m as ReviewItemQuery, n as ReviewMarker, o as ReviewMode, p as ReviewPoint, q as ReviewRulerConfig, r as ReviewSelection, s as ReviewSource, t as ReviewSubmitStatus, u as ReviewViewportScope, v as SupabaseReviewClient, w as WebReviewKitTarget } from './types-D_mNjOHx.cjs';
1
+ import { L as LocalAdapterOptions, W as WebReviewKitAdapter, S as SupabaseReviewAdapterOptions, R as ReviewWorkflowStatus, a as ReviewItemStatus, b as WebReviewKitOptions, c as WebReviewKitController, d as ReviewViewportPreset, V as ViewportSize, e as ReviewItem, N as NumberedReviewItem, f as ReviewItemScope } from './types-Cf2x5ky6.cjs';
2
+ export { D as DomAnchor, g as DomAnchorCandidate, h as DomAnchorStrategy, i as DomSourceHint, j as RelativeSelection, k as ReviewItemKind, l as ReviewItemQuery, m as ReviewMarker, n as ReviewMode, o as ReviewPoint, p as ReviewRulerConfig, q as ReviewSelection, r as ReviewSource, s as ReviewSubmitStatus, t as ReviewViewportScope, u as SupabaseReviewClient, v as WebReviewKitTarget } from './types-Cf2x5ky6.cjs';
3
3
 
4
4
  declare function localAdapter(options?: LocalAdapterOptions): WebReviewKitAdapter;
5
5
 
6
- declare const DF_SHEET_REVIEW_SOURCE = "df-web-review-kit";
7
- declare function dfSheetAdapter(options: DfSheetAdapterOptions): WebReviewKitAdapter;
8
-
9
6
  declare function supabaseAdapter(options: SupabaseReviewAdapterOptions): WebReviewKitAdapter;
10
7
 
11
8
  declare const REVIEW_WORKFLOW_STATUS_OPTIONS: Array<{
@@ -14,13 +11,20 @@ declare const REVIEW_WORKFLOW_STATUS_OPTIONS: Array<{
14
11
  }>;
15
12
  declare function normalizeReviewItemStatus(status: ReviewItemStatus | undefined): ReviewWorkflowStatus;
16
13
 
14
+ /** Creates the vanilla runtime controller that mounts review overlays on a target page. */
17
15
  declare function createWebReviewKit(options: WebReviewKitOptions): WebReviewKitController;
18
16
 
17
+ /** Default viewport presets used when a host project does not provide its own. */
19
18
  declare const DEFAULT_REVIEW_VIEWPORTS: ReviewViewportPreset[];
19
+ /** Finds the nearest configured preset for a viewport size. */
20
20
  declare function findReviewViewportPreset(viewport: ViewportSize, presets?: ReviewViewportPreset[]): ReviewViewportPreset;
21
+ /** Resolves a viewport size to the review scope used for item grouping. */
21
22
  declare function getReviewViewportScope(viewport: ViewportSize, presets?: ReviewViewportPreset[]): Exclude<ReviewItemScope, 'dom'>;
23
+ /** Resolves an item's persisted scope, falling back to its captured viewport. */
22
24
  declare function getReviewItemScope(item: ReviewItem, presets?: ReviewViewportPreset[]): ReviewItemScope;
25
+ /** Returns the display label for an item's resolved review scope. */
23
26
  declare function getReviewItemScopeLabel(item: ReviewItem, presets?: ReviewViewportPreset[]): string;
27
+ /** Adds scope-aware display labels to review items without mutating them. */
24
28
  declare function getNumberedReviewItems(items: ReviewItem[], presets?: ReviewViewportPreset[]): NumberedReviewItem[];
25
29
 
26
- export { DEFAULT_REVIEW_VIEWPORTS, DF_SHEET_REVIEW_SOURCE, DfSheetAdapterOptions, LocalAdapterOptions, NumberedReviewItem, REVIEW_WORKFLOW_STATUS_OPTIONS, ReviewItem, ReviewItemScope, ReviewItemStatus, ReviewViewportPreset, ReviewWorkflowStatus, SupabaseReviewAdapterOptions, ViewportSize, WebReviewKitAdapter, WebReviewKitController, WebReviewKitOptions, createWebReviewKit, dfSheetAdapter, findReviewViewportPreset, getNumberedReviewItems, getReviewItemScope, getReviewItemScopeLabel, getReviewViewportScope, localAdapter, normalizeReviewItemStatus, supabaseAdapter };
30
+ export { DEFAULT_REVIEW_VIEWPORTS, LocalAdapterOptions, NumberedReviewItem, REVIEW_WORKFLOW_STATUS_OPTIONS, ReviewItem, ReviewItemScope, ReviewItemStatus, ReviewViewportPreset, ReviewWorkflowStatus, SupabaseReviewAdapterOptions, ViewportSize, WebReviewKitAdapter, WebReviewKitController, WebReviewKitOptions, createWebReviewKit, findReviewViewportPreset, getNumberedReviewItems, getReviewItemScope, getReviewItemScopeLabel, getReviewViewportScope, localAdapter, normalizeReviewItemStatus, supabaseAdapter };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,8 @@
1
- import { L as LocalAdapterOptions, W as WebReviewKitAdapter, D as DfSheetAdapterOptions, S as SupabaseReviewAdapterOptions, R as ReviewWorkflowStatus, a as ReviewItemStatus, b as WebReviewKitOptions, c as WebReviewKitController, d as ReviewViewportPreset, V as ViewportSize, e as ReviewItem, N as NumberedReviewItem, f as ReviewItemScope } from './types-D_mNjOHx.js';
2
- export { g as DomAnchor, h as DomAnchorCandidate, i as DomAnchorStrategy, j as DomSourceHint, k as RelativeSelection, l as ReviewItemKind, m as ReviewItemQuery, n as ReviewMarker, o as ReviewMode, p as ReviewPoint, q as ReviewRulerConfig, r as ReviewSelection, s as ReviewSource, t as ReviewSubmitStatus, u as ReviewViewportScope, v as SupabaseReviewClient, w as WebReviewKitTarget } from './types-D_mNjOHx.js';
1
+ import { L as LocalAdapterOptions, W as WebReviewKitAdapter, S as SupabaseReviewAdapterOptions, R as ReviewWorkflowStatus, a as ReviewItemStatus, b as WebReviewKitOptions, c as WebReviewKitController, d as ReviewViewportPreset, V as ViewportSize, e as ReviewItem, N as NumberedReviewItem, f as ReviewItemScope } from './types-Cf2x5ky6.js';
2
+ export { D as DomAnchor, g as DomAnchorCandidate, h as DomAnchorStrategy, i as DomSourceHint, j as RelativeSelection, k as ReviewItemKind, l as ReviewItemQuery, m as ReviewMarker, n as ReviewMode, o as ReviewPoint, p as ReviewRulerConfig, q as ReviewSelection, r as ReviewSource, s as ReviewSubmitStatus, t as ReviewViewportScope, u as SupabaseReviewClient, v as WebReviewKitTarget } from './types-Cf2x5ky6.js';
3
3
 
4
4
  declare function localAdapter(options?: LocalAdapterOptions): WebReviewKitAdapter;
5
5
 
6
- declare const DF_SHEET_REVIEW_SOURCE = "df-web-review-kit";
7
- declare function dfSheetAdapter(options: DfSheetAdapterOptions): WebReviewKitAdapter;
8
-
9
6
  declare function supabaseAdapter(options: SupabaseReviewAdapterOptions): WebReviewKitAdapter;
10
7
 
11
8
  declare const REVIEW_WORKFLOW_STATUS_OPTIONS: Array<{
@@ -14,13 +11,20 @@ declare const REVIEW_WORKFLOW_STATUS_OPTIONS: Array<{
14
11
  }>;
15
12
  declare function normalizeReviewItemStatus(status: ReviewItemStatus | undefined): ReviewWorkflowStatus;
16
13
 
14
+ /** Creates the vanilla runtime controller that mounts review overlays on a target page. */
17
15
  declare function createWebReviewKit(options: WebReviewKitOptions): WebReviewKitController;
18
16
 
17
+ /** Default viewport presets used when a host project does not provide its own. */
19
18
  declare const DEFAULT_REVIEW_VIEWPORTS: ReviewViewportPreset[];
19
+ /** Finds the nearest configured preset for a viewport size. */
20
20
  declare function findReviewViewportPreset(viewport: ViewportSize, presets?: ReviewViewportPreset[]): ReviewViewportPreset;
21
+ /** Resolves a viewport size to the review scope used for item grouping. */
21
22
  declare function getReviewViewportScope(viewport: ViewportSize, presets?: ReviewViewportPreset[]): Exclude<ReviewItemScope, 'dom'>;
23
+ /** Resolves an item's persisted scope, falling back to its captured viewport. */
22
24
  declare function getReviewItemScope(item: ReviewItem, presets?: ReviewViewportPreset[]): ReviewItemScope;
25
+ /** Returns the display label for an item's resolved review scope. */
23
26
  declare function getReviewItemScopeLabel(item: ReviewItem, presets?: ReviewViewportPreset[]): string;
27
+ /** Adds scope-aware display labels to review items without mutating them. */
24
28
  declare function getNumberedReviewItems(items: ReviewItem[], presets?: ReviewViewportPreset[]): NumberedReviewItem[];
25
29
 
26
- export { DEFAULT_REVIEW_VIEWPORTS, DF_SHEET_REVIEW_SOURCE, DfSheetAdapterOptions, LocalAdapterOptions, NumberedReviewItem, REVIEW_WORKFLOW_STATUS_OPTIONS, ReviewItem, ReviewItemScope, ReviewItemStatus, ReviewViewportPreset, ReviewWorkflowStatus, SupabaseReviewAdapterOptions, ViewportSize, WebReviewKitAdapter, WebReviewKitController, WebReviewKitOptions, createWebReviewKit, dfSheetAdapter, findReviewViewportPreset, getNumberedReviewItems, getReviewItemScope, getReviewItemScopeLabel, getReviewViewportScope, localAdapter, normalizeReviewItemStatus, supabaseAdapter };
30
+ export { DEFAULT_REVIEW_VIEWPORTS, LocalAdapterOptions, NumberedReviewItem, REVIEW_WORKFLOW_STATUS_OPTIONS, ReviewItem, ReviewItemScope, ReviewItemStatus, ReviewViewportPreset, ReviewWorkflowStatus, SupabaseReviewAdapterOptions, ViewportSize, WebReviewKitAdapter, WebReviewKitController, WebReviewKitOptions, createWebReviewKit, findReviewViewportPreset, getNumberedReviewItems, getReviewItemScope, getReviewItemScopeLabel, getReviewViewportScope, localAdapter, normalizeReviewItemStatus, supabaseAdapter };
package/dist/index.js CHANGED
@@ -1,24 +1,237 @@
1
1
  import {
2
2
  DEFAULT_REVIEW_VIEWPORTS,
3
- DF_SHEET_REVIEW_SOURCE,
4
3
  REVIEW_WORKFLOW_STATUS_OPTIONS,
5
4
  createWebReviewKit,
6
- dfSheetAdapter,
7
5
  findReviewViewportPreset,
8
6
  getNumberedReviewItems,
9
7
  getReviewItemScope,
10
8
  getReviewItemScopeLabel,
11
9
  getReviewViewportScope,
12
10
  localAdapter,
13
- normalizeReviewItemStatus,
14
- supabaseAdapter
15
- } from "./chunk-U5K2YGGL.js";
11
+ normalizeReviewItemStatus
12
+ } from "./chunk-I76WEDLA.js";
13
+
14
+ // src/adapters/supabase.ts
15
+ var DEFAULT_SUPABASE_REVIEW_TABLE = "review_items";
16
+ var DEFAULT_SUPABASE_REVIEW_SOURCE = "supabase";
17
+ var DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC = "create_review_item";
18
+ function supabaseAdapter(options) {
19
+ const tableName = options.table ?? DEFAULT_SUPABASE_REVIEW_TABLE;
20
+ const source = options.source ?? DEFAULT_SUPABASE_REVIEW_SOURCE;
21
+ const fromTable = () => options.client.from(tableName);
22
+ return {
23
+ async get(id) {
24
+ const row = await unwrapResponse(
25
+ fromTable().select("*").eq("id", id).maybeSingle(),
26
+ "supabase get review item"
27
+ );
28
+ return row ? rowToReviewItem(row, options) : null;
29
+ },
30
+ async list(query) {
31
+ let request = fromTable().select("*").eq("project_id", query.projectId).eq("source", query.source ?? source);
32
+ const routeKey = query.routeKey ?? query.normalizedPath;
33
+ if (routeKey) {
34
+ request = request.eq("route_key", routeKey);
35
+ }
36
+ if (query.status) {
37
+ request = request.eq(
38
+ "status",
39
+ normalizeReviewItemStatus(query.status)
40
+ );
41
+ }
42
+ const rows = await unwrapResponse(
43
+ request.order("created_at", { ascending: false }),
44
+ "supabase list review items"
45
+ );
46
+ return (rows ?? []).flatMap((row) => {
47
+ const item = rowToReviewItem(row, options);
48
+ return item ? [item] : [];
49
+ });
50
+ },
51
+ async create(item) {
52
+ const nextItem = normalizeItemForSupabaseCreate(item, source, options);
53
+ if (options.unsafeClientReviewNumberFallback) {
54
+ throw new Error(
55
+ "supabase create review item: unsafeClientReviewNumberFallback is no longer supported. Use create_review_item RPC with database-backed review_number sequence."
56
+ );
57
+ }
58
+ return createItemWithRpc(nextItem, source, options);
59
+ },
60
+ async update(id, patch) {
61
+ const current = await this.get(id);
62
+ if (!current) throw new Error(`Review item not found: ${id}`);
63
+ const nextStatus = patch.status ? normalizeReviewItemStatus(patch.status) : current.status;
64
+ const nextItem = {
65
+ ...current,
66
+ ...patch,
67
+ id,
68
+ status: nextStatus,
69
+ createdAt: current.createdAt,
70
+ updatedAt: (/* @__PURE__ */ new Date()).toISOString()
71
+ };
72
+ const patchRow = itemToRowPatch(nextItem, source, options);
73
+ const updated = await unwrapResponse(
74
+ fromTable().update(patchRow).eq("id", id).select("*").single(),
75
+ "supabase update review item"
76
+ );
77
+ return rowToReviewItem(updated, options) ?? nextItem;
78
+ },
79
+ async remove(id) {
80
+ await unwrapResponse(
81
+ fromTable().delete().eq("id", id),
82
+ "supabase delete review item"
83
+ );
84
+ }
85
+ };
86
+ }
87
+ function normalizeItemForSupabaseCreate(item, source, options) {
88
+ const now = (/* @__PURE__ */ new Date()).toISOString();
89
+ const id = createSupabaseReviewItemId();
90
+ const normalizedStatus = normalizeReviewItemStatus(item.status);
91
+ const routeKey = item.routeKey || item.normalizedPath || "/";
92
+ const viewport = item.viewport ?? { width: 390, height: 720 };
93
+ const nextItem = {
94
+ ...item,
95
+ id,
96
+ reviewNumber: void 0,
97
+ projectId: options.projectId,
98
+ routeKey,
99
+ normalizedPath: item.normalizedPath || routeKey,
100
+ viewport,
101
+ status: normalizedStatus,
102
+ externalIssueId: id,
103
+ externalIssueUrl: buildSupabaseReviewUrl(
104
+ { routeKey, normalizedPath: item.normalizedPath || routeKey, viewport },
105
+ source,
106
+ options,
107
+ id
108
+ ),
109
+ submittedAt: item.submittedAt ?? now,
110
+ submitStatus: item.submitStatus ?? "submitted",
111
+ createdAt: now,
112
+ updatedAt: now
113
+ };
114
+ return {
115
+ ...nextItem,
116
+ externalIssueUrl: nextItem.externalIssueUrl ?? buildSupabaseReviewUrl(nextItem, source, options)
117
+ };
118
+ }
119
+ async function createItemWithRpc(item, source, options) {
120
+ const rpcName = options.createRpc ?? DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC;
121
+ if (!options.client.rpc) {
122
+ throw new Error(
123
+ `supabase create review item: ${rpcName} rpc is required`
124
+ );
125
+ }
126
+ const row = await unwrapResponse(
127
+ options.client.rpc(rpcName, {
128
+ p_id: item.id,
129
+ p_project_id: options.projectId,
130
+ p_route_key: item.routeKey || item.normalizedPath || "/",
131
+ p_source: source,
132
+ p_status: normalizeReviewItemStatus(item.status),
133
+ p_item: item
134
+ }),
135
+ `supabase create review item rpc ${rpcName}`
136
+ );
137
+ return rowToReviewItem(row, options) ?? item;
138
+ }
139
+ function itemToRow(item, source, options) {
140
+ const now = (/* @__PURE__ */ new Date()).toISOString();
141
+ const updatedAt = item.updatedAt || now;
142
+ return {
143
+ id: item.id,
144
+ project_id: options.projectId,
145
+ route_key: item.routeKey || item.normalizedPath || "/",
146
+ source,
147
+ review_number: item.reviewNumber ?? null,
148
+ status: normalizeReviewItemStatus(item.status),
149
+ item: {
150
+ ...item,
151
+ projectId: options.projectId,
152
+ status: normalizeReviewItemStatus(item.status),
153
+ updatedAt
154
+ },
155
+ created_at: item.createdAt || now,
156
+ updated_at: updatedAt
157
+ };
158
+ }
159
+ function itemToRowPatch(item, source, options) {
160
+ const row = itemToRow(item, source, options);
161
+ return {
162
+ route_key: row.route_key,
163
+ review_number: row.review_number,
164
+ status: row.status,
165
+ item: row.item,
166
+ updated_at: row.updated_at
167
+ };
168
+ }
169
+ function rowToReviewItem(row, options) {
170
+ if (!row.item || typeof row.item !== "object") return null;
171
+ const item = row.item;
172
+ const status = normalizeReviewItemStatus(
173
+ row.status || item.status || "todo"
174
+ );
175
+ const routeKey = row.route_key || item.routeKey || item.normalizedPath || "/";
176
+ const viewport = item.viewport ?? { width: 390, height: 720 };
177
+ const now = (/* @__PURE__ */ new Date()).toISOString();
178
+ return {
179
+ ...item,
180
+ id: row.id,
181
+ reviewNumber: row.review_number ?? item.reviewNumber,
182
+ projectId: row.project_id || item.projectId || options.projectId,
183
+ routeKey,
184
+ pageUrl: item.pageUrl || toAbsoluteReviewUrl(routeKey),
185
+ normalizedPath: item.normalizedPath || routeKey,
186
+ kind: item.kind === "area" ? "area" : "note",
187
+ comment: item.comment || "",
188
+ status,
189
+ viewport,
190
+ externalIssueId: item.externalIssueId ?? row.id,
191
+ externalIssueUrl: item.externalIssueUrl ?? buildSupabaseReviewUrl(
192
+ { routeKey, normalizedPath: routeKey, viewport },
193
+ row.source,
194
+ options,
195
+ row.id
196
+ ),
197
+ submittedAt: item.submittedAt ?? row.created_at,
198
+ submitStatus: item.submitStatus ?? "submitted",
199
+ createdAt: item.createdAt ?? row.created_at ?? now,
200
+ updatedAt: row.updated_at ?? item.updatedAt ?? now
201
+ };
202
+ }
203
+ async function unwrapResponse(request, label) {
204
+ const { data, error } = await request;
205
+ if (error) {
206
+ throw new Error(`${label}: ${error.message ?? error.code ?? "failed"}`);
207
+ }
208
+ return data;
209
+ }
210
+ function buildSupabaseReviewUrl(item, source, options, itemId) {
211
+ if (typeof window === "undefined") return void 0;
212
+ const prefix = options.reviewPathPrefix ?? "/review";
213
+ const url = new URL(prefix, window.location.origin);
214
+ url.searchParams.set("source", source);
215
+ url.searchParams.set("target", item.routeKey || item.normalizedPath || "/");
216
+ url.searchParams.set("w", String(Math.round(item.viewport.width)));
217
+ url.searchParams.set("h", String(Math.round(item.viewport.height)));
218
+ if (itemId) url.searchParams.set("item", itemId);
219
+ return url.toString();
220
+ }
221
+ function toAbsoluteReviewUrl(path) {
222
+ if (typeof window === "undefined") return path;
223
+ return new URL(path, window.location.origin).toString();
224
+ }
225
+ function createSupabaseReviewItemId() {
226
+ if (typeof crypto !== "undefined" && crypto.randomUUID) {
227
+ return crypto.randomUUID();
228
+ }
229
+ return `review-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;
230
+ }
16
231
  export {
17
232
  DEFAULT_REVIEW_VIEWPORTS,
18
- DF_SHEET_REVIEW_SOURCE,
19
233
  REVIEW_WORKFLOW_STATUS_OPTIONS,
20
234
  createWebReviewKit,
21
- dfSheetAdapter,
22
235
  findReviewViewportPreset,
23
236
  getNumberedReviewItems,
24
237
  getReviewItemScope,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
1
+ {"version":3,"sources":["../src/adapters/supabase.ts"],"sourcesContent":["import { normalizeReviewItemStatus } from '../status';\nimport type {\n ReviewItem,\n ReviewItemQuery,\n ReviewItemStatus,\n ReviewSource,\n SupabaseReviewAdapterOptions,\n WebReviewKitAdapter,\n} from '../types';\n\nconst DEFAULT_SUPABASE_REVIEW_TABLE = 'review_items';\nconst DEFAULT_SUPABASE_REVIEW_SOURCE = 'supabase';\nconst DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC = 'create_review_item';\n\ntype SupabaseReviewRow = {\n id: string;\n project_id: string;\n route_key: string;\n source: string;\n review_number?: number | null;\n status: string;\n item: unknown;\n created_at: string;\n updated_at: string;\n};\n\ntype SupabaseResponse<T> = {\n data: T | null;\n error: { message?: string; code?: string } | null;\n};\n\nexport function supabaseAdapter(\n options: SupabaseReviewAdapterOptions\n): WebReviewKitAdapter {\n const tableName = options.table ?? DEFAULT_SUPABASE_REVIEW_TABLE;\n const source = options.source ?? DEFAULT_SUPABASE_REVIEW_SOURCE;\n\n const fromTable = () => options.client.from(tableName);\n\n return {\n async get(id) {\n const row = await unwrapResponse<SupabaseReviewRow | null>(\n fromTable().select('*').eq('id', id).maybeSingle(),\n 'supabase get review item'\n );\n\n return row ? rowToReviewItem(row, options) : null;\n },\n\n async list(query) {\n let request = fromTable()\n .select('*')\n .eq('project_id', query.projectId)\n .eq('source', query.source ?? source);\n const routeKey = query.routeKey ?? query.normalizedPath;\n\n if (routeKey) {\n request = request.eq('route_key', routeKey);\n }\n if (query.status) {\n request = request.eq(\n 'status',\n normalizeReviewItemStatus(query.status)\n );\n }\n\n const rows = await unwrapResponse<SupabaseReviewRow[]>(\n request.order('created_at', { ascending: false }),\n 'supabase list review items'\n );\n\n return (rows ?? []).flatMap((row) => {\n const item = rowToReviewItem(row, options);\n return item ? [item] : [];\n });\n },\n\n async create(item) {\n const nextItem = normalizeItemForSupabaseCreate(item, source, options);\n\n if (options.unsafeClientReviewNumberFallback) {\n throw new Error(\n 'supabase create review item: unsafeClientReviewNumberFallback is no longer supported. Use create_review_item RPC with database-backed review_number sequence.'\n );\n }\n\n return createItemWithRpc(nextItem, source, options);\n },\n\n async update(id, patch) {\n const current = await this.get(id);\n if (!current) throw new Error(`Review item not found: ${id}`);\n\n const nextStatus = patch.status\n ? normalizeReviewItemStatus(patch.status)\n : current.status;\n const nextItem: ReviewItem = {\n ...current,\n ...patch,\n id,\n status: nextStatus,\n createdAt: current.createdAt,\n updatedAt: new Date().toISOString(),\n };\n const patchRow = itemToRowPatch(nextItem, source, options);\n const updated = await unwrapResponse<SupabaseReviewRow>(\n fromTable().update(patchRow).eq('id', id).select('*').single(),\n 'supabase update review item'\n );\n\n return rowToReviewItem(updated, options) ?? nextItem;\n },\n\n async remove(id) {\n await unwrapResponse<null>(\n fromTable().delete().eq('id', id),\n 'supabase delete review item'\n );\n },\n };\n}\n\nfunction normalizeItemForSupabaseCreate(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n): ReviewItem {\n const now = new Date().toISOString();\n const id = createSupabaseReviewItemId();\n const normalizedStatus = normalizeReviewItemStatus(item.status);\n const routeKey = item.routeKey || item.normalizedPath || '/';\n const viewport = item.viewport ?? { width: 390, height: 720 };\n const nextItem: ReviewItem = {\n ...item,\n id,\n reviewNumber: undefined,\n projectId: options.projectId,\n routeKey,\n normalizedPath: item.normalizedPath || routeKey,\n viewport,\n status: normalizedStatus,\n externalIssueId: id,\n externalIssueUrl: buildSupabaseReviewUrl(\n { routeKey, normalizedPath: item.normalizedPath || routeKey, viewport },\n source,\n options,\n id\n ),\n submittedAt: item.submittedAt ?? now,\n submitStatus: item.submitStatus ?? 'submitted',\n createdAt: now,\n updatedAt: now,\n };\n\n return {\n ...nextItem,\n externalIssueUrl:\n nextItem.externalIssueUrl ??\n buildSupabaseReviewUrl(nextItem, source, options),\n };\n}\n\nasync function createItemWithRpc(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n) {\n const rpcName =\n options.createRpc ?? DEFAULT_SUPABASE_CREATE_REVIEW_ITEM_RPC;\n\n if (!options.client.rpc) {\n throw new Error(\n `supabase create review item: ${rpcName} rpc is required`\n );\n }\n\n const row = await unwrapResponse<SupabaseReviewRow>(\n options.client.rpc(rpcName, {\n p_id: item.id,\n p_project_id: options.projectId,\n p_route_key: item.routeKey || item.normalizedPath || '/',\n p_source: source,\n p_status: normalizeReviewItemStatus(item.status),\n p_item: item,\n }),\n `supabase create review item rpc ${rpcName}`\n );\n\n return rowToReviewItem(row, options) ?? item;\n}\n\nfunction itemToRow(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n): SupabaseReviewRow {\n const now = new Date().toISOString();\n const updatedAt = item.updatedAt || now;\n\n return {\n id: item.id,\n project_id: options.projectId,\n route_key: item.routeKey || item.normalizedPath || '/',\n source,\n review_number: item.reviewNumber ?? null,\n status: normalizeReviewItemStatus(item.status),\n item: {\n ...item,\n projectId: options.projectId,\n status: normalizeReviewItemStatus(item.status),\n updatedAt,\n },\n created_at: item.createdAt || now,\n updated_at: updatedAt,\n };\n}\n\nfunction itemToRowPatch(\n item: ReviewItem,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions\n) {\n const row = itemToRow(item, source, options);\n return {\n route_key: row.route_key,\n review_number: row.review_number,\n status: row.status,\n item: row.item,\n updated_at: row.updated_at,\n };\n}\n\nfunction rowToReviewItem(\n row: SupabaseReviewRow,\n options: SupabaseReviewAdapterOptions\n): ReviewItem | null {\n if (!row.item || typeof row.item !== 'object') return null;\n\n const item = row.item as Partial<ReviewItem>;\n const status = normalizeReviewItemStatus(\n (row.status || item.status || 'todo') as ReviewItemStatus\n );\n const routeKey = row.route_key || item.routeKey || item.normalizedPath || '/';\n const viewport = item.viewport ?? { width: 390, height: 720 };\n const now = new Date().toISOString();\n\n return {\n ...(item as ReviewItem),\n id: row.id,\n reviewNumber: row.review_number ?? item.reviewNumber,\n projectId: row.project_id || item.projectId || options.projectId,\n routeKey,\n pageUrl: item.pageUrl || toAbsoluteReviewUrl(routeKey),\n normalizedPath: item.normalizedPath || routeKey,\n kind: item.kind === 'area' ? 'area' : 'note',\n comment: item.comment || '',\n status,\n viewport,\n externalIssueId: item.externalIssueId ?? row.id,\n externalIssueUrl:\n item.externalIssueUrl ??\n buildSupabaseReviewUrl(\n { routeKey, normalizedPath: routeKey, viewport },\n row.source,\n options,\n row.id\n ),\n submittedAt: item.submittedAt ?? row.created_at,\n submitStatus: item.submitStatus ?? 'submitted',\n createdAt: item.createdAt ?? row.created_at ?? now,\n updatedAt: row.updated_at ?? item.updatedAt ?? now,\n };\n}\n\nasync function unwrapResponse<T>(\n request: Promise<SupabaseResponse<T>> | SupabaseResponse<T>,\n label: string\n) {\n const { data, error } = await request;\n if (error) {\n throw new Error(`${label}: ${error.message ?? error.code ?? 'failed'}`);\n }\n return data as T;\n}\n\nfunction buildSupabaseReviewUrl(\n item: Pick<ReviewItem, 'routeKey' | 'normalizedPath' | 'viewport'>,\n source: ReviewSource,\n options: SupabaseReviewAdapterOptions,\n itemId?: string\n) {\n if (typeof window === 'undefined') return undefined;\n\n const prefix = options.reviewPathPrefix ?? '/review';\n const url = new URL(prefix, window.location.origin);\n url.searchParams.set('source', source);\n url.searchParams.set('target', item.routeKey || item.normalizedPath || '/');\n url.searchParams.set('w', String(Math.round(item.viewport.width)));\n url.searchParams.set('h', String(Math.round(item.viewport.height)));\n if (itemId) url.searchParams.set('item', itemId);\n return url.toString();\n}\n\nfunction toAbsoluteReviewUrl(path: string) {\n if (typeof window === 'undefined') return path;\n return new URL(path, window.location.origin).toString();\n}\n\nfunction normalizeReviewNumber(value: unknown) {\n if (typeof value !== 'number') return undefined;\n if (!Number.isInteger(value) || value < 1) return undefined;\n return value;\n}\n\nfunction createSupabaseReviewItemId() {\n if (typeof crypto !== 'undefined' && crypto.randomUUID) {\n return crypto.randomUUID();\n }\n\n return `review-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`;\n}\n"],"mappings":"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","names":[]}