whale-code 6.5.8 → 6.5.9
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/dist/cli/services/agent-loop.js +26 -2
- package/dist/cli/services/agent-loop.js.map +1 -1
- package/dist/cli/services/hooks.js +2 -1
- package/dist/cli/services/hooks.js.map +1 -1
- package/dist/cli/services/telemetry-spans.js +1 -0
- package/dist/cli/services/telemetry-spans.js.map +1 -1
- package/dist/cli/services/telemetry.d.ts +23 -0
- package/dist/cli/services/telemetry.js +45 -1
- package/dist/cli/services/telemetry.js.map +1 -1
- package/dist/server/handlers/__test-utils__/test-db.d.ts +17 -3
- package/dist/server/handlers/__test-utils__/test-db.js +113 -14
- package/dist/server/handlers/__test-utils__/test-db.js.map +1 -1
- package/dist/server/handlers/affiliates.d.ts +9 -0
- package/dist/server/handlers/affiliates.js +197 -0
- package/dist/server/handlers/affiliates.js.map +1 -0
- package/dist/server/handlers/api-docs.d.ts +4 -2
- package/dist/server/handlers/api-docs.js +204 -1681
- package/dist/server/handlers/api-docs.js.map +1 -1
- package/dist/server/handlers/campaigns.d.ts +9 -0
- package/dist/server/handlers/campaigns.js +237 -0
- package/dist/server/handlers/campaigns.js.map +1 -0
- package/dist/server/handlers/catalog-schemas.js +9 -9
- package/dist/server/handlers/catalog-schemas.js.map +1 -1
- package/dist/server/handlers/catalog.js +1 -1
- package/dist/server/handlers/catalog.js.map +1 -1
- package/dist/server/handlers/comms-documents.js +28 -2
- package/dist/server/handlers/comms-documents.js.map +1 -1
- package/dist/server/handlers/comms-pdf-generation.js +25 -3
- package/dist/server/handlers/comms-pdf-generation.js.map +1 -1
- package/dist/server/handlers/comms-pdf-helpers.js +4 -4
- package/dist/server/handlers/comms-pdf-helpers.js.map +1 -1
- package/dist/server/handlers/comms.d.ts +100 -0
- package/dist/server/handlers/comms.js +146 -12
- package/dist/server/handlers/comms.js.map +1 -1
- package/dist/server/handlers/coupons.d.ts +9 -0
- package/dist/server/handlers/coupons.js +220 -0
- package/dist/server/handlers/coupons.js.map +1 -0
- package/dist/server/handlers/embeddings.js +1 -1
- package/dist/server/handlers/embeddings.js.map +1 -1
- package/dist/server/handlers/enrichment.js +2 -622
- package/dist/server/handlers/enrichment.js.map +1 -1
- package/dist/server/handlers/fulfillment.d.ts +9 -0
- package/dist/server/handlers/fulfillment.js +209 -0
- package/dist/server/handlers/fulfillment.js.map +1 -0
- package/dist/server/handlers/google-ads.d.ts +24 -0
- package/dist/server/handlers/google-ads.js +2199 -0
- package/dist/server/handlers/google-ads.js.map +1 -0
- package/dist/server/handlers/invoices.d.ts +9 -0
- package/dist/server/handlers/invoices.js +252 -0
- package/dist/server/handlers/invoices.js.map +1 -0
- package/dist/server/handlers/loyalty.d.ts +9 -0
- package/dist/server/handlers/loyalty.js +197 -0
- package/dist/server/handlers/loyalty.js.map +1 -0
- package/dist/server/handlers/meta-ads-graph-api.js +18 -3
- package/dist/server/handlers/meta-ads-graph-api.js.map +1 -1
- package/dist/server/handlers/phone.d.ts +9 -0
- package/dist/server/handlers/phone.js +197 -0
- package/dist/server/handlers/phone.js.map +1 -0
- package/dist/server/handlers/pipeline.d.ts +9 -0
- package/dist/server/handlers/pipeline.js +277 -0
- package/dist/server/handlers/pipeline.js.map +1 -0
- package/dist/server/handlers/qr-codes.d.ts +9 -0
- package/dist/server/handlers/qr-codes.js +198 -0
- package/dist/server/handlers/qr-codes.js.map +1 -0
- package/dist/server/handlers/reviews.d.ts +9 -0
- package/dist/server/handlers/reviews.js +171 -0
- package/dist/server/handlers/reviews.js.map +1 -0
- package/dist/server/handlers/segments.d.ts +9 -0
- package/dist/server/handlers/segments.js +229 -0
- package/dist/server/handlers/segments.js.map +1 -0
- package/dist/server/handlers/social.d.ts +9 -0
- package/dist/server/handlers/social.js +81 -0
- package/dist/server/handlers/social.js.map +1 -0
- package/dist/server/handlers/tax.d.ts +9 -0
- package/dist/server/handlers/tax.js +182 -0
- package/dist/server/handlers/tax.js.map +1 -0
- package/dist/server/handlers/wallet.d.ts +9 -0
- package/dist/server/handlers/wallet.js +203 -0
- package/dist/server/handlers/wallet.js.map +1 -0
- package/dist/server/handlers/webhooks-mgmt.d.ts +9 -0
- package/dist/server/handlers/webhooks-mgmt.js +181 -0
- package/dist/server/handlers/webhooks-mgmt.js.map +1 -0
- package/dist/server/handlers/wholesale.d.ts +9 -0
- package/dist/server/handlers/wholesale.js +219 -0
- package/dist/server/handlers/wholesale.js.map +1 -0
- package/dist/server/index.js +20 -9
- package/dist/server/index.js.map +1 -1
- package/dist/server/lib/clickhouse-buffer.js +1 -0
- package/dist/server/lib/clickhouse-buffer.js.map +1 -1
- package/dist/server/lib/coa-renderer.d.ts +1 -1
- package/dist/server/lib/coa-renderer.js +32 -10
- package/dist/server/lib/coa-renderer.js.map +1 -1
- package/dist/server/server-worker.d.ts +1 -0
- package/dist/server/server-worker.js +464 -3
- package/dist/server/server-worker.js.map +1 -1
- package/dist/server/tool-router.js +118 -4
- package/dist/server/tool-router.js.map +1 -1
- package/package.json +26 -3
- package/vendor/ink/package.json +0 -2
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// server/handlers/coupons.ts — Coupon code management (create, list, validate)
|
|
2
|
+
|
|
3
|
+
import { sanitizeFilterValue } from "../lib/utils.js";
|
|
4
|
+
export async function handleCoupons(sb, args, storeId) {
|
|
5
|
+
const sid = storeId;
|
|
6
|
+
switch (args.action) {
|
|
7
|
+
// ---- LIST: list coupons with optional filters ----
|
|
8
|
+
case "list":
|
|
9
|
+
{
|
|
10
|
+
let q = sb.from("coupons").select("*").eq("store_id", sid).order("created_at", {
|
|
11
|
+
ascending: false
|
|
12
|
+
});
|
|
13
|
+
if (args.is_active !== undefined) q = q.eq("is_active", args.is_active);
|
|
14
|
+
if (args.discount_type) q = q.eq("discount_type", args.discount_type);
|
|
15
|
+
if (args.query) {
|
|
16
|
+
const sq = sanitizeFilterValue(String(args.query));
|
|
17
|
+
q = q.or(`code.ilike.%${sq}%,description.ilike.%${sq}%`);
|
|
18
|
+
}
|
|
19
|
+
q = q.limit(args.limit || 50);
|
|
20
|
+
const {
|
|
21
|
+
data,
|
|
22
|
+
error
|
|
23
|
+
} = await q;
|
|
24
|
+
return error ? {
|
|
25
|
+
success: false,
|
|
26
|
+
error: error.message
|
|
27
|
+
} : {
|
|
28
|
+
success: true,
|
|
29
|
+
count: data?.length,
|
|
30
|
+
data
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ---- GET: get a single coupon by ID ----
|
|
35
|
+
case "get":
|
|
36
|
+
{
|
|
37
|
+
const couponId = args.coupon_id;
|
|
38
|
+
if (!couponId) return {
|
|
39
|
+
success: false,
|
|
40
|
+
error: "coupon_id is required"
|
|
41
|
+
};
|
|
42
|
+
const {
|
|
43
|
+
data,
|
|
44
|
+
error
|
|
45
|
+
} = await sb.from("coupons").select("*").eq("id", couponId).eq("store_id", sid).single();
|
|
46
|
+
if (error) return {
|
|
47
|
+
success: false,
|
|
48
|
+
error: error.message
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Include usage stats
|
|
52
|
+
const {
|
|
53
|
+
data: usage
|
|
54
|
+
} = await sb.from("coupon_usage").select("id, customer_id, order_id, discount_amount, created_at").eq("coupon_id", couponId).order("created_at", {
|
|
55
|
+
ascending: false
|
|
56
|
+
}).limit(20);
|
|
57
|
+
return {
|
|
58
|
+
success: true,
|
|
59
|
+
data: {
|
|
60
|
+
...data,
|
|
61
|
+
recent_usage: usage || []
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---- CREATE: create a new coupon ----
|
|
67
|
+
case "create":
|
|
68
|
+
{
|
|
69
|
+
const code = args.code;
|
|
70
|
+
const discountType = args.discount_type;
|
|
71
|
+
const discountAmount = args.discount_amount;
|
|
72
|
+
if (!code || !discountType || discountAmount === undefined) {
|
|
73
|
+
return {
|
|
74
|
+
success: false,
|
|
75
|
+
error: "code, discount_type, and discount_amount are required"
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
const record = {
|
|
79
|
+
store_id: sid,
|
|
80
|
+
code: code.toUpperCase(),
|
|
81
|
+
discount_type: discountType,
|
|
82
|
+
discount_amount: discountAmount
|
|
83
|
+
};
|
|
84
|
+
if (args.description !== undefined) record.description = args.description;
|
|
85
|
+
if (args.free_shipping !== undefined) record.free_shipping = args.free_shipping;
|
|
86
|
+
if (args.minimum_amount !== undefined) record.minimum_amount = args.minimum_amount;
|
|
87
|
+
if (args.maximum_amount !== undefined) record.maximum_amount = args.maximum_amount;
|
|
88
|
+
if (args.usage_limit !== undefined) record.usage_limit = args.usage_limit;
|
|
89
|
+
if (args.usage_limit_per_user !== undefined) record.usage_limit_per_user = args.usage_limit_per_user;
|
|
90
|
+
if (args.individual_use !== undefined) record.individual_use = args.individual_use;
|
|
91
|
+
if (args.exclude_sale_items !== undefined) record.exclude_sale_items = args.exclude_sale_items;
|
|
92
|
+
if (args.start_date !== undefined) record.start_date = args.start_date;
|
|
93
|
+
if (args.end_date !== undefined) record.end_date = args.end_date;
|
|
94
|
+
if (args.allowed_products !== undefined) record.allowed_products = args.allowed_products;
|
|
95
|
+
if (args.excluded_products !== undefined) record.excluded_products = args.excluded_products;
|
|
96
|
+
if (args.allowed_categories !== undefined) record.allowed_categories = args.allowed_categories;
|
|
97
|
+
if (args.excluded_categories !== undefined) record.excluded_categories = args.excluded_categories;
|
|
98
|
+
if (args.allowed_emails !== undefined) record.allowed_emails = args.allowed_emails;
|
|
99
|
+
const {
|
|
100
|
+
data,
|
|
101
|
+
error
|
|
102
|
+
} = await sb.from("coupons").insert(record).select().single();
|
|
103
|
+
return error ? {
|
|
104
|
+
success: false,
|
|
105
|
+
error: error.message
|
|
106
|
+
} : {
|
|
107
|
+
success: true,
|
|
108
|
+
data
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// ---- UPDATE: modify a coupon ----
|
|
113
|
+
case "update":
|
|
114
|
+
{
|
|
115
|
+
const couponId = args.coupon_id;
|
|
116
|
+
if (!couponId) return {
|
|
117
|
+
success: false,
|
|
118
|
+
error: "coupon_id is required"
|
|
119
|
+
};
|
|
120
|
+
const updates = {
|
|
121
|
+
updated_at: new Date().toISOString()
|
|
122
|
+
};
|
|
123
|
+
if (args.code !== undefined) updates.code = args.code.toUpperCase();
|
|
124
|
+
if (args.description !== undefined) updates.description = args.description;
|
|
125
|
+
if (args.discount_type !== undefined) updates.discount_type = args.discount_type;
|
|
126
|
+
if (args.discount_amount !== undefined) updates.discount_amount = args.discount_amount;
|
|
127
|
+
if (args.is_active !== undefined) updates.is_active = args.is_active;
|
|
128
|
+
if (args.free_shipping !== undefined) updates.free_shipping = args.free_shipping;
|
|
129
|
+
if (args.minimum_amount !== undefined) updates.minimum_amount = args.minimum_amount;
|
|
130
|
+
if (args.maximum_amount !== undefined) updates.maximum_amount = args.maximum_amount;
|
|
131
|
+
if (args.usage_limit !== undefined) updates.usage_limit = args.usage_limit;
|
|
132
|
+
if (args.start_date !== undefined) updates.start_date = args.start_date;
|
|
133
|
+
if (args.end_date !== undefined) updates.end_date = args.end_date;
|
|
134
|
+
const {
|
|
135
|
+
data,
|
|
136
|
+
error
|
|
137
|
+
} = await sb.from("coupons").update(updates).eq("id", couponId).eq("store_id", sid).select().single();
|
|
138
|
+
return error ? {
|
|
139
|
+
success: false,
|
|
140
|
+
error: error.message
|
|
141
|
+
} : {
|
|
142
|
+
success: true,
|
|
143
|
+
data
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// ---- VALIDATE: check if a coupon code is valid for use ----
|
|
148
|
+
case "validate":
|
|
149
|
+
{
|
|
150
|
+
const code = args.code;
|
|
151
|
+
if (!code) return {
|
|
152
|
+
success: false,
|
|
153
|
+
error: "code is required"
|
|
154
|
+
};
|
|
155
|
+
const {
|
|
156
|
+
data: coupon,
|
|
157
|
+
error
|
|
158
|
+
} = await sb.from("coupons").select("*").eq("code", code.toUpperCase()).eq("store_id", sid).maybeSingle();
|
|
159
|
+
if (error) return {
|
|
160
|
+
success: false,
|
|
161
|
+
error: error.message
|
|
162
|
+
};
|
|
163
|
+
if (!coupon) return {
|
|
164
|
+
success: false,
|
|
165
|
+
error: "Coupon code not found"
|
|
166
|
+
};
|
|
167
|
+
const now = new Date();
|
|
168
|
+
const issues = [];
|
|
169
|
+
if (!coupon.is_active) issues.push("Coupon is inactive");
|
|
170
|
+
if (coupon.start_date && new Date(coupon.start_date) > now) issues.push("Coupon has not started yet");
|
|
171
|
+
if (coupon.end_date && new Date(coupon.end_date) < now) issues.push("Coupon has expired");
|
|
172
|
+
if (coupon.usage_limit && coupon.usage_count >= coupon.usage_limit) issues.push("Usage limit reached");
|
|
173
|
+
return {
|
|
174
|
+
success: true,
|
|
175
|
+
data: {
|
|
176
|
+
valid: issues.length === 0,
|
|
177
|
+
coupon: {
|
|
178
|
+
id: coupon.id,
|
|
179
|
+
code: coupon.code,
|
|
180
|
+
discount_type: coupon.discount_type,
|
|
181
|
+
discount_amount: coupon.discount_amount,
|
|
182
|
+
free_shipping: coupon.free_shipping
|
|
183
|
+
},
|
|
184
|
+
issues: issues.length > 0 ? issues : undefined
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
// ---- DELETE: deactivate a coupon ----
|
|
190
|
+
case "delete":
|
|
191
|
+
{
|
|
192
|
+
const couponId = args.coupon_id;
|
|
193
|
+
if (!couponId) return {
|
|
194
|
+
success: false,
|
|
195
|
+
error: "coupon_id is required"
|
|
196
|
+
};
|
|
197
|
+
const {
|
|
198
|
+
error
|
|
199
|
+
} = await sb.from("coupons").update({
|
|
200
|
+
is_active: false,
|
|
201
|
+
updated_at: new Date().toISOString()
|
|
202
|
+
}).eq("id", couponId).eq("store_id", sid);
|
|
203
|
+
return error ? {
|
|
204
|
+
success: false,
|
|
205
|
+
error: error.message
|
|
206
|
+
} : {
|
|
207
|
+
success: true,
|
|
208
|
+
data: {
|
|
209
|
+
deactivated: true
|
|
210
|
+
}
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
default:
|
|
214
|
+
return {
|
|
215
|
+
success: false,
|
|
216
|
+
error: `Unknown coupons action: ${args.action}. Valid: list, get, create, update, validate, delete`
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=coupons.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coupons.js","names":["sanitizeFilterValue","handleCoupons","sb","args","storeId","sid","action","q","from","select","eq","order","ascending","is_active","undefined","discount_type","query","sq","String","or","limit","data","error","success","message","count","length","couponId","coupon_id","single","usage","recent_usage","code","discountType","discountAmount","discount_amount","record","store_id","toUpperCase","description","free_shipping","minimum_amount","maximum_amount","usage_limit","usage_limit_per_user","individual_use","exclude_sale_items","start_date","end_date","allowed_products","excluded_products","allowed_categories","excluded_categories","allowed_emails","insert","updates","updated_at","Date","toISOString","update","coupon","maybeSingle","now","issues","push","usage_count","valid","id","deactivated"],"sources":["../../../src/server/handlers/coupons.ts"],"sourcesContent":["// server/handlers/coupons.ts — Coupon code management (create, list, validate)\n\nimport type { SupabaseClient } from \"@supabase/supabase-js\";\nimport { sanitizeFilterValue } from \"../lib/utils.js\";\n\ntype Result = { success: boolean; data?: unknown; error?: string; [key: string]: unknown };\n\nexport async function handleCoupons(\n sb: SupabaseClient,\n args: Record<string, unknown>,\n storeId?: string,\n): Promise<Result> {\n const sid = storeId as string;\n\n switch (args.action) {\n // ---- LIST: list coupons with optional filters ----\n case \"list\": {\n let q = sb.from(\"coupons\")\n .select(\"*\")\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false });\n if (args.is_active !== undefined) q = q.eq(\"is_active\", args.is_active as boolean);\n if (args.discount_type) q = q.eq(\"discount_type\", args.discount_type as string);\n if (args.query) {\n const sq = sanitizeFilterValue(String(args.query));\n q = q.or(`code.ilike.%${sq}%,description.ilike.%${sq}%`);\n }\n q = q.limit(args.limit as number || 50);\n const { data, error } = await q;\n return error ? { success: false, error: error.message } : { success: true, count: data?.length, data };\n }\n\n // ---- GET: get a single coupon by ID ----\n case \"get\": {\n const couponId = args.coupon_id as string;\n if (!couponId) return { success: false, error: \"coupon_id is required\" };\n const { data, error } = await sb.from(\"coupons\")\n .select(\"*\")\n .eq(\"id\", couponId)\n .eq(\"store_id\", sid)\n .single();\n if (error) return { success: false, error: error.message };\n\n // Include usage stats\n const { data: usage } = await sb.from(\"coupon_usage\")\n .select(\"id, customer_id, order_id, discount_amount, created_at\")\n .eq(\"coupon_id\", couponId)\n .order(\"created_at\", { ascending: false })\n .limit(20);\n return { success: true, data: { ...data, recent_usage: usage || [] } };\n }\n\n // ---- CREATE: create a new coupon ----\n case \"create\": {\n const code = args.code as string;\n const discountType = args.discount_type as string;\n const discountAmount = args.discount_amount as number;\n if (!code || !discountType || discountAmount === undefined) {\n return { success: false, error: \"code, discount_type, and discount_amount are required\" };\n }\n const record: Record<string, unknown> = {\n store_id: sid,\n code: code.toUpperCase(),\n discount_type: discountType,\n discount_amount: discountAmount,\n };\n if (args.description !== undefined) record.description = args.description;\n if (args.free_shipping !== undefined) record.free_shipping = args.free_shipping;\n if (args.minimum_amount !== undefined) record.minimum_amount = args.minimum_amount;\n if (args.maximum_amount !== undefined) record.maximum_amount = args.maximum_amount;\n if (args.usage_limit !== undefined) record.usage_limit = args.usage_limit;\n if (args.usage_limit_per_user !== undefined) record.usage_limit_per_user = args.usage_limit_per_user;\n if (args.individual_use !== undefined) record.individual_use = args.individual_use;\n if (args.exclude_sale_items !== undefined) record.exclude_sale_items = args.exclude_sale_items;\n if (args.start_date !== undefined) record.start_date = args.start_date;\n if (args.end_date !== undefined) record.end_date = args.end_date;\n if (args.allowed_products !== undefined) record.allowed_products = args.allowed_products;\n if (args.excluded_products !== undefined) record.excluded_products = args.excluded_products;\n if (args.allowed_categories !== undefined) record.allowed_categories = args.allowed_categories;\n if (args.excluded_categories !== undefined) record.excluded_categories = args.excluded_categories;\n if (args.allowed_emails !== undefined) record.allowed_emails = args.allowed_emails;\n\n const { data, error } = await sb.from(\"coupons\")\n .insert(record)\n .select()\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- UPDATE: modify a coupon ----\n case \"update\": {\n const couponId = args.coupon_id as string;\n if (!couponId) return { success: false, error: \"coupon_id is required\" };\n const updates: Record<string, unknown> = { updated_at: new Date().toISOString() };\n if (args.code !== undefined) updates.code = (args.code as string).toUpperCase();\n if (args.description !== undefined) updates.description = args.description;\n if (args.discount_type !== undefined) updates.discount_type = args.discount_type;\n if (args.discount_amount !== undefined) updates.discount_amount = args.discount_amount;\n if (args.is_active !== undefined) updates.is_active = args.is_active;\n if (args.free_shipping !== undefined) updates.free_shipping = args.free_shipping;\n if (args.minimum_amount !== undefined) updates.minimum_amount = args.minimum_amount;\n if (args.maximum_amount !== undefined) updates.maximum_amount = args.maximum_amount;\n if (args.usage_limit !== undefined) updates.usage_limit = args.usage_limit;\n if (args.start_date !== undefined) updates.start_date = args.start_date;\n if (args.end_date !== undefined) updates.end_date = args.end_date;\n\n const { data, error } = await sb.from(\"coupons\")\n .update(updates)\n .eq(\"id\", couponId)\n .eq(\"store_id\", sid)\n .select()\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- VALIDATE: check if a coupon code is valid for use ----\n case \"validate\": {\n const code = args.code as string;\n if (!code) return { success: false, error: \"code is required\" };\n const { data: coupon, error } = await sb.from(\"coupons\")\n .select(\"*\")\n .eq(\"code\", code.toUpperCase())\n .eq(\"store_id\", sid)\n .maybeSingle();\n if (error) return { success: false, error: error.message };\n if (!coupon) return { success: false, error: \"Coupon code not found\" };\n\n const now = new Date();\n const issues: string[] = [];\n if (!coupon.is_active) issues.push(\"Coupon is inactive\");\n if (coupon.start_date && new Date(coupon.start_date) > now) issues.push(\"Coupon has not started yet\");\n if (coupon.end_date && new Date(coupon.end_date) < now) issues.push(\"Coupon has expired\");\n if (coupon.usage_limit && coupon.usage_count >= coupon.usage_limit) issues.push(\"Usage limit reached\");\n\n return {\n success: true,\n data: {\n valid: issues.length === 0,\n coupon: { id: coupon.id, code: coupon.code, discount_type: coupon.discount_type, discount_amount: coupon.discount_amount, free_shipping: coupon.free_shipping },\n issues: issues.length > 0 ? issues : undefined,\n },\n };\n }\n\n // ---- DELETE: deactivate a coupon ----\n case \"delete\": {\n const couponId = args.coupon_id as string;\n if (!couponId) return { success: false, error: \"coupon_id is required\" };\n const { error } = await sb.from(\"coupons\")\n .update({ is_active: false, updated_at: new Date().toISOString() })\n .eq(\"id\", couponId)\n .eq(\"store_id\", sid);\n return error ? { success: false, error: error.message } : { success: true, data: { deactivated: true } };\n }\n\n default:\n return { success: false, error: `Unknown coupons action: ${args.action}. Valid: list, get, create, update, validate, delete` };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -151,7 +151,7 @@ export async function handleEmbeddings(sb, args, storeId) {
|
|
|
151
151
|
const {
|
|
152
152
|
data: products,
|
|
153
153
|
error: fetchErr
|
|
154
|
-
} = await sb.from("products").select("id, name, description, short_description, custom_fields").eq("store_id", sid);
|
|
154
|
+
} = await sb.from("products").select("id, name, description, short_description, custom_fields").eq("store_id", sid).neq("status", "archived");
|
|
155
155
|
if (fetchErr) return {
|
|
156
156
|
success: false,
|
|
157
157
|
error: fetchErr.message
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embeddings.js","names":["OpenAI","EMBEDDING_MODEL","MAX_CHARS","BATCH_SIZE","truncateText","text","length","slice","getOpenAIClient","sb","storeId","data","apiKey","error","rpc","p_name","p_store_id","Error","generateEmbeddings","openai","texts","results","i","batch","map","response","embeddings","create","model","input","item","push","embedding","productToText","product","parts","name","String","short_description","description","custom_fields","fv","key","val","Object","entries","undefined","join","handleEmbeddings","args","sid","action","content","success","sourceType","source_type","sourceId","source_id","metadata","from","delete","eq","insert","store_id","JSON","stringify","select","single","message","query","matchCount","match_count","threshold","similarity_threshold","queryEmbedding","p_query_embedding","p_match_count","p_source_type","p_similarity_threshold","count","products","fetchErr","indexed","inserted","rows","p","id","insErr","total_products","q","deleted","counts","total","row","st","by_source_type"],"sources":["../../../src/server/handlers/embeddings.ts"],"sourcesContent":["import type { SupabaseClient } from \"@supabase/supabase-js\";\nimport OpenAI from \"openai\";\n\n// ============================================================================\n// EMBEDDINGS — Semantic vector search via OpenAI + pgvector\n// Actions: embed, search, index_products, delete, stats\n// ============================================================================\n\nconst EMBEDDING_MODEL = \"text-embedding-3-small\";\nconst MAX_CHARS = 32_000; // ~8000 tokens\nconst BATCH_SIZE = 100; // OpenAI max per request\n\n/** Truncate text to stay within token limits */\nfunction truncateText(text: string): string {\n return text.length > MAX_CHARS ? text.slice(0, MAX_CHARS) : text;\n}\n\n/** Get an OpenAI client by decrypting the stored API key */\nasync function getOpenAIClient(sb: SupabaseClient, storeId: string): Promise<OpenAI> {\n const { data: apiKey, error } = await sb.rpc(\"decrypt_secret\", {\n p_name: \"OPENAI_API_KEY\",\n p_store_id: storeId,\n });\n if (error || !apiKey) {\n throw new Error(\"OpenAI API key not found. Add OPENAI_API_KEY to your tool secrets.\");\n }\n return new OpenAI({ apiKey });\n}\n\n/** Generate embeddings for one or more texts (batched) */\nasync function generateEmbeddings(\n openai: OpenAI,\n texts: string[]\n): Promise<number[][]> {\n const results: number[][] = [];\n for (let i = 0; i < texts.length; i += BATCH_SIZE) {\n const batch = texts.slice(i, i + BATCH_SIZE).map(truncateText);\n const response = await openai.embeddings.create({\n model: EMBEDDING_MODEL,\n input: batch,\n });\n for (const item of response.data) {\n results.push(item.embedding);\n }\n }\n return results;\n}\n\n/** Build a text representation of a product for embedding */\nfunction productToText(product: Record<string, unknown>): string {\n const parts: string[] = [];\n if (product.name) parts.push(String(product.name));\n if (product.short_description) parts.push(String(product.short_description));\n if (product.description) parts.push(String(product.description));\n if (product.custom_fields && typeof product.custom_fields === \"object\") {\n const fv = product.custom_fields as Record<string, unknown>;\n for (const [key, val] of Object.entries(fv)) {\n if (val !== null && val !== undefined && val !== \"\") {\n parts.push(`${key}: ${String(val)}`);\n }\n }\n }\n return parts.join(\". \");\n}\n\n// ============================================================================\n// MAIN HANDLER\n// ============================================================================\n\nexport async function handleEmbeddings(\n sb: SupabaseClient,\n args: Record<string, unknown>,\n storeId?: string\n) {\n const sid = storeId as string;\n const action = args.action as string;\n\n switch (action) {\n\n // ======================== EMBED ========================\n case \"embed\": {\n const content = args.content as string;\n if (!content) return { success: false, error: \"content is required for embed action\" };\n\n const openai = await getOpenAIClient(sb, sid);\n const [embedding] = await generateEmbeddings(openai, [content]);\n\n const sourceType = (args.source_type as string) || \"text\";\n const sourceId = args.source_id as string | undefined;\n const metadata = (args.metadata as Record<string, unknown>) || {};\n\n // Upsert: delete existing embedding for same source_id if provided\n if (sourceId) {\n await sb.from(\"embeddings\")\n .delete()\n .eq(\"store_id\", sid)\n .eq(\"source_type\", sourceType)\n .eq(\"source_id\", sourceId);\n }\n\n const { data, error } = await sb.from(\"embeddings\").insert({\n store_id: sid,\n content: truncateText(content),\n embedding: JSON.stringify(embedding),\n metadata,\n source_type: sourceType,\n source_id: sourceId || null,\n }).select(\"id, source_type, source_id, created_at\").single();\n\n if (error) return { success: false, error: error.message };\n return { success: true, data };\n }\n\n // ======================== SEARCH ========================\n case \"search\": {\n const query = args.query as string;\n if (!query) return { success: false, error: \"query is required for search action\" };\n\n const matchCount = (args.match_count as number) || 10;\n const sourceType = (args.source_type as string) || null;\n const threshold = (args.similarity_threshold as number) || 0.5;\n\n const openai = await getOpenAIClient(sb, sid);\n const [queryEmbedding] = await generateEmbeddings(openai, [query]);\n\n const { data, error } = await sb.rpc(\"search_embeddings\", {\n p_store_id: sid,\n p_query_embedding: JSON.stringify(queryEmbedding),\n p_match_count: matchCount,\n p_source_type: sourceType,\n p_similarity_threshold: threshold,\n });\n\n if (error) return { success: false, error: error.message };\n return { success: true, count: data?.length ?? 0, data };\n }\n\n // ======================== INDEX PRODUCTS ========================\n case \"index_products\": {\n const { data: products, error: fetchErr } = await sb\n .from(\"products\")\n .select(\"id, name, description, short_description, custom_fields\")\n .eq(\"store_id\", sid);\n\n if (fetchErr) return { success: false, error: fetchErr.message };\n if (!products || products.length === 0) {\n return { success: true, data: { indexed: 0, message: \"No products found to index.\" } };\n }\n\n const openai = await getOpenAIClient(sb, sid);\n const texts = products.map(productToText);\n const embeddings = await generateEmbeddings(openai, texts);\n\n // Delete all existing product embeddings for this store\n await sb.from(\"embeddings\")\n .delete()\n .eq(\"store_id\", sid)\n .eq(\"source_type\", \"product\");\n\n // Insert in batches\n let inserted = 0;\n const rows = products.map((p, i) => ({\n store_id: sid,\n content: truncateText(texts[i]),\n embedding: JSON.stringify(embeddings[i]),\n metadata: { name: p.name },\n source_type: \"product\" as const,\n source_id: p.id,\n }));\n\n for (let i = 0; i < rows.length; i += 500) {\n const batch = rows.slice(i, i + 500);\n const { error: insErr } = await sb.from(\"embeddings\").insert(batch);\n if (insErr) return { success: false, error: insErr.message, data: { indexed: inserted } };\n inserted += batch.length;\n }\n\n return {\n success: true,\n data: { indexed: inserted, total_products: products.length },\n };\n }\n\n // ======================== DELETE ========================\n case \"delete\": {\n const sourceType = args.source_type as string | undefined;\n const sourceId = args.source_id as string | undefined;\n\n if (!sourceType && !sourceId) {\n return { success: false, error: \"Provide source_type and/or source_id to delete embeddings.\" };\n }\n\n let q = sb.from(\"embeddings\").delete().eq(\"store_id\", sid);\n if (sourceType) q = q.eq(\"source_type\", sourceType);\n if (sourceId) q = q.eq(\"source_id\", sourceId);\n\n const { error } = await q;\n if (error) return { success: false, error: error.message };\n return { success: true, data: { deleted: true, source_type: sourceType, source_id: sourceId } };\n }\n\n // ======================== STATS ========================\n case \"stats\": {\n const { data, error } = await sb\n .from(\"embeddings\")\n .select(\"source_type\")\n .eq(\"store_id\", sid);\n\n if (error) return { success: false, error: error.message };\n\n const counts: Record<string, number> = {};\n let total = 0;\n for (const row of data || []) {\n const st = (row.source_type as string) || \"unknown\";\n counts[st] = (counts[st] || 0) + 1;\n total++;\n }\n\n return { success: true, data: { total, by_source_type: counts } };\n }\n\n default:\n return {\n success: false,\n error: `Unknown embeddings action: ${action}. Valid actions: embed, search, index_products, delete, stats`,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"embeddings.js","names":["OpenAI","EMBEDDING_MODEL","MAX_CHARS","BATCH_SIZE","truncateText","text","length","slice","getOpenAIClient","sb","storeId","data","apiKey","error","rpc","p_name","p_store_id","Error","generateEmbeddings","openai","texts","results","i","batch","map","response","embeddings","create","model","input","item","push","embedding","productToText","product","parts","name","String","short_description","description","custom_fields","fv","key","val","Object","entries","undefined","join","handleEmbeddings","args","sid","action","content","success","sourceType","source_type","sourceId","source_id","metadata","from","delete","eq","insert","store_id","JSON","stringify","select","single","message","query","matchCount","match_count","threshold","similarity_threshold","queryEmbedding","p_query_embedding","p_match_count","p_source_type","p_similarity_threshold","count","products","fetchErr","neq","indexed","inserted","rows","p","id","insErr","total_products","q","deleted","counts","total","row","st","by_source_type"],"sources":["../../../src/server/handlers/embeddings.ts"],"sourcesContent":["import type { SupabaseClient } from \"@supabase/supabase-js\";\nimport OpenAI from \"openai\";\n\n// ============================================================================\n// EMBEDDINGS — Semantic vector search via OpenAI + pgvector\n// Actions: embed, search, index_products, delete, stats\n// ============================================================================\n\nconst EMBEDDING_MODEL = \"text-embedding-3-small\";\nconst MAX_CHARS = 32_000; // ~8000 tokens\nconst BATCH_SIZE = 100; // OpenAI max per request\n\n/** Truncate text to stay within token limits */\nfunction truncateText(text: string): string {\n return text.length > MAX_CHARS ? text.slice(0, MAX_CHARS) : text;\n}\n\n/** Get an OpenAI client by decrypting the stored API key */\nasync function getOpenAIClient(sb: SupabaseClient, storeId: string): Promise<OpenAI> {\n const { data: apiKey, error } = await sb.rpc(\"decrypt_secret\", {\n p_name: \"OPENAI_API_KEY\",\n p_store_id: storeId,\n });\n if (error || !apiKey) {\n throw new Error(\"OpenAI API key not found. Add OPENAI_API_KEY to your tool secrets.\");\n }\n return new OpenAI({ apiKey });\n}\n\n/** Generate embeddings for one or more texts (batched) */\nasync function generateEmbeddings(\n openai: OpenAI,\n texts: string[]\n): Promise<number[][]> {\n const results: number[][] = [];\n for (let i = 0; i < texts.length; i += BATCH_SIZE) {\n const batch = texts.slice(i, i + BATCH_SIZE).map(truncateText);\n const response = await openai.embeddings.create({\n model: EMBEDDING_MODEL,\n input: batch,\n });\n for (const item of response.data) {\n results.push(item.embedding);\n }\n }\n return results;\n}\n\n/** Build a text representation of a product for embedding */\nfunction productToText(product: Record<string, unknown>): string {\n const parts: string[] = [];\n if (product.name) parts.push(String(product.name));\n if (product.short_description) parts.push(String(product.short_description));\n if (product.description) parts.push(String(product.description));\n if (product.custom_fields && typeof product.custom_fields === \"object\") {\n const fv = product.custom_fields as Record<string, unknown>;\n for (const [key, val] of Object.entries(fv)) {\n if (val !== null && val !== undefined && val !== \"\") {\n parts.push(`${key}: ${String(val)}`);\n }\n }\n }\n return parts.join(\". \");\n}\n\n// ============================================================================\n// MAIN HANDLER\n// ============================================================================\n\nexport async function handleEmbeddings(\n sb: SupabaseClient,\n args: Record<string, unknown>,\n storeId?: string\n) {\n const sid = storeId as string;\n const action = args.action as string;\n\n switch (action) {\n\n // ======================== EMBED ========================\n case \"embed\": {\n const content = args.content as string;\n if (!content) return { success: false, error: \"content is required for embed action\" };\n\n const openai = await getOpenAIClient(sb, sid);\n const [embedding] = await generateEmbeddings(openai, [content]);\n\n const sourceType = (args.source_type as string) || \"text\";\n const sourceId = args.source_id as string | undefined;\n const metadata = (args.metadata as Record<string, unknown>) || {};\n\n // Upsert: delete existing embedding for same source_id if provided\n if (sourceId) {\n await sb.from(\"embeddings\")\n .delete()\n .eq(\"store_id\", sid)\n .eq(\"source_type\", sourceType)\n .eq(\"source_id\", sourceId);\n }\n\n const { data, error } = await sb.from(\"embeddings\").insert({\n store_id: sid,\n content: truncateText(content),\n embedding: JSON.stringify(embedding),\n metadata,\n source_type: sourceType,\n source_id: sourceId || null,\n }).select(\"id, source_type, source_id, created_at\").single();\n\n if (error) return { success: false, error: error.message };\n return { success: true, data };\n }\n\n // ======================== SEARCH ========================\n case \"search\": {\n const query = args.query as string;\n if (!query) return { success: false, error: \"query is required for search action\" };\n\n const matchCount = (args.match_count as number) || 10;\n const sourceType = (args.source_type as string) || null;\n const threshold = (args.similarity_threshold as number) || 0.5;\n\n const openai = await getOpenAIClient(sb, sid);\n const [queryEmbedding] = await generateEmbeddings(openai, [query]);\n\n const { data, error } = await sb.rpc(\"search_embeddings\", {\n p_store_id: sid,\n p_query_embedding: JSON.stringify(queryEmbedding),\n p_match_count: matchCount,\n p_source_type: sourceType,\n p_similarity_threshold: threshold,\n });\n\n if (error) return { success: false, error: error.message };\n return { success: true, count: data?.length ?? 0, data };\n }\n\n // ======================== INDEX PRODUCTS ========================\n case \"index_products\": {\n const { data: products, error: fetchErr } = await sb\n .from(\"products\")\n .select(\"id, name, description, short_description, custom_fields\")\n .eq(\"store_id\", sid)\n .neq(\"status\", \"archived\");\n\n if (fetchErr) return { success: false, error: fetchErr.message };\n if (!products || products.length === 0) {\n return { success: true, data: { indexed: 0, message: \"No products found to index.\" } };\n }\n\n const openai = await getOpenAIClient(sb, sid);\n const texts = products.map(productToText);\n const embeddings = await generateEmbeddings(openai, texts);\n\n // Delete all existing product embeddings for this store\n await sb.from(\"embeddings\")\n .delete()\n .eq(\"store_id\", sid)\n .eq(\"source_type\", \"product\");\n\n // Insert in batches\n let inserted = 0;\n const rows = products.map((p, i) => ({\n store_id: sid,\n content: truncateText(texts[i]),\n embedding: JSON.stringify(embeddings[i]),\n metadata: { name: p.name },\n source_type: \"product\" as const,\n source_id: p.id,\n }));\n\n for (let i = 0; i < rows.length; i += 500) {\n const batch = rows.slice(i, i + 500);\n const { error: insErr } = await sb.from(\"embeddings\").insert(batch);\n if (insErr) return { success: false, error: insErr.message, data: { indexed: inserted } };\n inserted += batch.length;\n }\n\n return {\n success: true,\n data: { indexed: inserted, total_products: products.length },\n };\n }\n\n // ======================== DELETE ========================\n case \"delete\": {\n const sourceType = args.source_type as string | undefined;\n const sourceId = args.source_id as string | undefined;\n\n if (!sourceType && !sourceId) {\n return { success: false, error: \"Provide source_type and/or source_id to delete embeddings.\" };\n }\n\n let q = sb.from(\"embeddings\").delete().eq(\"store_id\", sid);\n if (sourceType) q = q.eq(\"source_type\", sourceType);\n if (sourceId) q = q.eq(\"source_id\", sourceId);\n\n const { error } = await q;\n if (error) return { success: false, error: error.message };\n return { success: true, data: { deleted: true, source_type: sourceType, source_id: sourceId } };\n }\n\n // ======================== STATS ========================\n case \"stats\": {\n const { data, error } = await sb\n .from(\"embeddings\")\n .select(\"source_type\")\n .eq(\"store_id\", sid);\n\n if (error) return { success: false, error: error.message };\n\n const counts: Record<string, number> = {};\n let total = 0;\n for (const row of data || []) {\n const st = (row.source_type as string) || \"unknown\";\n counts[st] = (counts[st] || 0) + 1;\n total++;\n }\n\n return { success: true, data: { total, by_source_type: counts } };\n }\n\n default:\n return {\n success: false,\n error: `Unknown embeddings action: ${action}. Valid actions: embed, search, index_products, delete, stats`,\n };\n }\n}\n"],"mappings":"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","ignoreList":[]}
|