whale-code 6.5.8 → 6.5.10

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 (101) hide show
  1. package/dist/cli/services/agent-loop.js +26 -2
  2. package/dist/cli/services/agent-loop.js.map +1 -1
  3. package/dist/cli/services/config-store.js +2 -3
  4. package/dist/cli/services/config-store.js.map +1 -1
  5. package/dist/cli/services/hooks.js +2 -1
  6. package/dist/cli/services/hooks.js.map +1 -1
  7. package/dist/cli/services/telemetry-spans.js +1 -0
  8. package/dist/cli/services/telemetry-spans.js.map +1 -1
  9. package/dist/cli/services/telemetry.d.ts +23 -0
  10. package/dist/cli/services/telemetry.js +45 -1
  11. package/dist/cli/services/telemetry.js.map +1 -1
  12. package/dist/server/handlers/__test-utils__/test-db.d.ts +17 -3
  13. package/dist/server/handlers/__test-utils__/test-db.js +113 -14
  14. package/dist/server/handlers/__test-utils__/test-db.js.map +1 -1
  15. package/dist/server/handlers/affiliates.d.ts +9 -0
  16. package/dist/server/handlers/affiliates.js +197 -0
  17. package/dist/server/handlers/affiliates.js.map +1 -0
  18. package/dist/server/handlers/api-docs.d.ts +4 -2
  19. package/dist/server/handlers/api-docs.js +204 -1681
  20. package/dist/server/handlers/api-docs.js.map +1 -1
  21. package/dist/server/handlers/campaigns.d.ts +9 -0
  22. package/dist/server/handlers/campaigns.js +237 -0
  23. package/dist/server/handlers/campaigns.js.map +1 -0
  24. package/dist/server/handlers/catalog-schemas.js +9 -9
  25. package/dist/server/handlers/catalog-schemas.js.map +1 -1
  26. package/dist/server/handlers/catalog.js +1 -1
  27. package/dist/server/handlers/catalog.js.map +1 -1
  28. package/dist/server/handlers/comms-documents.js +28 -2
  29. package/dist/server/handlers/comms-documents.js.map +1 -1
  30. package/dist/server/handlers/comms-pdf-generation.js +25 -3
  31. package/dist/server/handlers/comms-pdf-generation.js.map +1 -1
  32. package/dist/server/handlers/comms-pdf-helpers.js +4 -4
  33. package/dist/server/handlers/comms-pdf-helpers.js.map +1 -1
  34. package/dist/server/handlers/comms.d.ts +100 -0
  35. package/dist/server/handlers/comms.js +146 -12
  36. package/dist/server/handlers/comms.js.map +1 -1
  37. package/dist/server/handlers/coupons.d.ts +9 -0
  38. package/dist/server/handlers/coupons.js +220 -0
  39. package/dist/server/handlers/coupons.js.map +1 -0
  40. package/dist/server/handlers/embeddings.js +1 -1
  41. package/dist/server/handlers/embeddings.js.map +1 -1
  42. package/dist/server/handlers/enrichment.js +2 -622
  43. package/dist/server/handlers/enrichment.js.map +1 -1
  44. package/dist/server/handlers/fulfillment.d.ts +9 -0
  45. package/dist/server/handlers/fulfillment.js +209 -0
  46. package/dist/server/handlers/fulfillment.js.map +1 -0
  47. package/dist/server/handlers/google-ads.d.ts +24 -0
  48. package/dist/server/handlers/google-ads.js +2199 -0
  49. package/dist/server/handlers/google-ads.js.map +1 -0
  50. package/dist/server/handlers/invoices.d.ts +9 -0
  51. package/dist/server/handlers/invoices.js +252 -0
  52. package/dist/server/handlers/invoices.js.map +1 -0
  53. package/dist/server/handlers/loyalty.d.ts +9 -0
  54. package/dist/server/handlers/loyalty.js +197 -0
  55. package/dist/server/handlers/loyalty.js.map +1 -0
  56. package/dist/server/handlers/meta-ads-graph-api.js +18 -3
  57. package/dist/server/handlers/meta-ads-graph-api.js.map +1 -1
  58. package/dist/server/handlers/phone.d.ts +9 -0
  59. package/dist/server/handlers/phone.js +197 -0
  60. package/dist/server/handlers/phone.js.map +1 -0
  61. package/dist/server/handlers/pipeline.d.ts +9 -0
  62. package/dist/server/handlers/pipeline.js +277 -0
  63. package/dist/server/handlers/pipeline.js.map +1 -0
  64. package/dist/server/handlers/qr-codes.d.ts +9 -0
  65. package/dist/server/handlers/qr-codes.js +198 -0
  66. package/dist/server/handlers/qr-codes.js.map +1 -0
  67. package/dist/server/handlers/reviews.d.ts +9 -0
  68. package/dist/server/handlers/reviews.js +171 -0
  69. package/dist/server/handlers/reviews.js.map +1 -0
  70. package/dist/server/handlers/segments.d.ts +9 -0
  71. package/dist/server/handlers/segments.js +229 -0
  72. package/dist/server/handlers/segments.js.map +1 -0
  73. package/dist/server/handlers/social.d.ts +9 -0
  74. package/dist/server/handlers/social.js +81 -0
  75. package/dist/server/handlers/social.js.map +1 -0
  76. package/dist/server/handlers/tax.d.ts +9 -0
  77. package/dist/server/handlers/tax.js +182 -0
  78. package/dist/server/handlers/tax.js.map +1 -0
  79. package/dist/server/handlers/wallet.d.ts +9 -0
  80. package/dist/server/handlers/wallet.js +203 -0
  81. package/dist/server/handlers/wallet.js.map +1 -0
  82. package/dist/server/handlers/webhooks-mgmt.d.ts +9 -0
  83. package/dist/server/handlers/webhooks-mgmt.js +181 -0
  84. package/dist/server/handlers/webhooks-mgmt.js.map +1 -0
  85. package/dist/server/handlers/wholesale.d.ts +9 -0
  86. package/dist/server/handlers/wholesale.js +219 -0
  87. package/dist/server/handlers/wholesale.js.map +1 -0
  88. package/dist/server/index.js +20 -9
  89. package/dist/server/index.js.map +1 -1
  90. package/dist/server/lib/clickhouse-buffer.js +1 -0
  91. package/dist/server/lib/clickhouse-buffer.js.map +1 -1
  92. package/dist/server/lib/coa-renderer.d.ts +1 -1
  93. package/dist/server/lib/coa-renderer.js +32 -10
  94. package/dist/server/lib/coa-renderer.js.map +1 -1
  95. package/dist/server/server-worker.d.ts +1 -0
  96. package/dist/server/server-worker.js +464 -3
  97. package/dist/server/server-worker.js.map +1 -1
  98. package/dist/server/tool-router.js +118 -4
  99. package/dist/server/tool-router.js.map +1 -1
  100. package/package.json +26 -3
  101. package/vendor/ink/package.json +0 -2
@@ -1,18 +1,27 @@
1
1
  /**
2
2
  * Real Supabase test helper for handler tests.
3
- * Replaces mock-supabase.ts — all operations hit the real database.
3
+ * All operations hit the real LOCAL database (never production).
4
4
  *
5
5
  * When SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY / TEST_STORE_ID are missing,
6
6
  * exports are stubbed so test files can skip gracefully via:
7
7
  * describe.skipIf(!HAS_TEST_DB)("...", () => { ... })
8
+ *
9
+ * Cleanup uses a direct Postgres connection for speed — one SQL round-trip
10
+ * instead of 44+ individual HTTP DELETE calls through PostgREST.
8
11
  */
9
12
 
10
13
  import { createClient } from "@supabase/supabase-js";
14
+ import pg from "pg";
11
15
  import { randomUUID } from "node:crypto";
12
16
  const url = process.env.SUPABASE_URL;
13
17
  const key = process.env.SUPABASE_SERVICE_ROLE_KEY;
14
18
  const storeId = process.env.TEST_STORE_ID;
15
19
 
20
+ // Guard: refuse to run against production
21
+ if (url && !url.includes("127.0.0.1") && !url.includes("localhost")) {
22
+ throw new Error(`FATAL: SUPABASE_URL points to "${url}" which is NOT local. ` + `Tests must run against local Supabase (127.0.0.1:54321). ` + `Fix your .env.test file.`);
23
+ }
24
+
16
25
  /** true when all required env vars are present — use with describe.skipIf(!HAS_TEST_DB) */
17
26
  export const HAS_TEST_DB = !!(url && key && storeId);
18
27
  const sb = HAS_TEST_DB ? createClient(url, key) : null;
@@ -26,13 +35,36 @@ export function getTestClient() {
26
35
  return sb;
27
36
  }
28
37
 
38
+ // ---------------------------------------------------------------------------
39
+ // Direct Postgres pool for fast cleanup (bypasses PostgREST HTTP overhead)
40
+ // ---------------------------------------------------------------------------
41
+
42
+ const pgPool = HAS_TEST_DB ? new pg.Pool({
43
+ host: "127.0.0.1",
44
+ port: 54322,
45
+ database: "postgres",
46
+ user: "postgres",
47
+ password: "postgres",
48
+ max: 2
49
+ }) : null;
50
+
51
+ /** Timeout wrapper — prevents tests from hanging on unresponsive DB */
52
+ function withTimeout(promise, ms, label) {
53
+ return Promise.race([promise, new Promise((_, reject) => setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms))]);
54
+ }
55
+ const DB_TIMEOUT = 5_000;
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // Seed helpers (still go through Supabase client for type consistency)
59
+ // ---------------------------------------------------------------------------
60
+
29
61
  /** Insert rows and return them with DB-generated fields */
30
62
  export async function seed(table, rows) {
31
63
  if (!rows.length) return [];
32
64
  const {
33
65
  data,
34
66
  error
35
- } = await sb.from(table).insert(rows).select();
67
+ } = await withTimeout(Promise.resolve(sb.from(table).insert(rows).select()), DB_TIMEOUT, `seed(${table})`);
36
68
  if (error) throw new Error(`seed(${table}): ${error.message}`);
37
69
  return data;
38
70
  }
@@ -42,7 +74,7 @@ export async function deleteByIds(table, ids) {
42
74
  if (!ids.length) return;
43
75
  const {
44
76
  error
45
- } = await sb.from(table).delete().in("id", ids);
77
+ } = await withTimeout(Promise.resolve(sb.from(table).delete().in("id", ids)), DB_TIMEOUT, `deleteByIds(${table})`);
46
78
  if (error) throw new Error(`deleteByIds(${table}): ${error.message}`);
47
79
  }
48
80
 
@@ -50,23 +82,36 @@ export async function deleteByIds(table, ids) {
50
82
  export async function deleteWhere(table, column, value) {
51
83
  const {
52
84
  error
53
- } = await sb.from(table).delete().eq(column, value);
85
+ } = await withTimeout(Promise.resolve(sb.from(table).delete().eq(column, value)), DB_TIMEOUT, `deleteWhere(${table})`);
54
86
  if (error) throw new Error(`deleteWhere(${table}): ${error.message}`);
55
87
  }
56
88
 
57
- /** Upsert rows (for tables with unique constraints where insert would conflict) */
89
+ /** Upsert rows and track for cleanup */
58
90
  export async function upsertTracked(table, rows, onConflict) {
59
91
  if (!rows.length) return [];
60
92
  const {
61
93
  data,
62
94
  error
63
- } = await sb.from(table).upsert(rows, {
95
+ } = await withTimeout(Promise.resolve(sb.from(table).upsert(rows, {
64
96
  onConflict
65
- }).select();
97
+ }).select()), DB_TIMEOUT, `upsert(${table})`);
66
98
  if (error) throw new Error(`upsert(${table}): ${error.message}`);
99
+ const ids = data.map(r => r.id);
100
+ tracked.set(table, [...(tracked.get(table) || []), ...ids]);
67
101
  return data;
68
102
  }
69
103
 
104
+ // ---------------------------------------------------------------------------
105
+ // Tracking and cleanup
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /**
109
+ * Infrastructure tables that tests must NEVER delete from.
110
+ * Tests can INSERT into these (tracked), but cleanup skips them.
111
+ * This prevents accidental deletion of real API keys, store plans, stores, etc.
112
+ */
113
+ const PROTECTED_TABLES = new Set(["stores", "store_plans", "store_config", "store_prefetch_data"]);
114
+
70
115
  /** Track seeded data for automatic cleanup */
71
116
  const tracked = new Map();
72
117
  export async function seedTracked(table, rows) {
@@ -76,16 +121,70 @@ export async function seedTracked(table, rows) {
76
121
  return data;
77
122
  }
78
123
 
79
- /** Delete all tracked rows in FK-safe order */
124
+ /**
125
+ * Delete all tracked rows via a single Postgres transaction.
126
+ *
127
+ * Instead of 44+ individual HTTP DELETE calls through PostgREST,
128
+ * this sends one SQL statement directly to Postgres. ~10x faster,
129
+ * more reliable, and handles FK ordering via CASCADE-aware deletion.
130
+ */
80
131
  export async function cleanup() {
81
- const fkOrder = ["ai_messages", "ai_conversation_checkpoints", "ai_conversations", "ai_agent_config", "platform_secrets", "business_audit", "workflow_step_runs", "workflow_dlq", "workflow_runs", "workflow_versions", "workflow_steps", "workflows", "error_events", "payment_intents", "order_items", "cart_items", "webhook_events", "inventory_levels", "inventory", "email_sends", "orders", "carts", "products", "categories", "catalogs", "creation_collections", "customers", "store_customer_profiles", "user_creation_relationships", "creations", "locations", "webhook_configs", "webhook_endpoints", "api_keys", "platform_users", "store_media", "purchase_orders", "suppliers", "stock_transfers", "v_daily_sales", "meta_ads", "meta_ad_sets", "meta_campaigns", "channel_messages", "channels", "node_events", "nodes", "store_members", "store_plans", "store_usage", "email_templates", "email_threads", "customer_risk_scores", "customer_exposures", "customer_breach_records", "customer_enrichment_profiles", "stores"];
82
- const orderedTables = [...fkOrder.filter(t => tracked.has(t)), ...[...tracked.keys()].filter(t => !fkOrder.includes(t))];
83
- for (const table of orderedTables) {
84
- const ids = tracked.get(table);
85
- if (ids?.length) {
86
- await deleteByIds(table, ids).catch(() => {});
132
+ if (!tracked.size) return;
133
+
134
+ // Filter out protected tables
135
+ const tablesToClean = [...tracked.entries()].filter(([table]) => !PROTECTED_TABLES.has(table));
136
+ if (!tablesToClean.length) {
137
+ tracked.clear();
138
+ return;
139
+ }
140
+
141
+ // Fast path: single Postgres transaction with all DELETEs
142
+ if (pgPool) {
143
+ const client = await pgPool.connect();
144
+ try {
145
+ // FK-safe order: children before parents
146
+ const fkOrder = ["ai_messages", "ai_conversation_checkpoints", "ai_conversations", "ai_agent_config", "platform_secrets", "business_audit", "workflow_step_runs", "workflow_dlq", "workflow_runs", "workflow_versions", "workflow_steps", "workflows", "error_events", "payment_intents", "order_items", "cart_items", "webhook_events", "inventory_levels", "inventory", "email_sends", "orders", "carts", "products", "categories", "catalogs", "creation_collections", "customers", "store_customer_profiles", "user_creation_relationships", "creations", "locations", "webhook_configs", "webhook_endpoints", "api_keys", "platform_users", "store_media", "purchase_orders", "suppliers", "stock_transfers", "v_daily_sales", "meta_ads", "meta_ad_sets", "meta_campaigns", "channel_messages", "channels", "node_events", "nodes", "store_members", "store_usage", "email_templates", "email_threads", "customer_risk_scores", "customer_exposures", "customer_breach_records", "customer_enrichment_profiles"];
147
+ const ordered = [...fkOrder.filter(t => tablesToClean.some(([table]) => table === t)), ...tablesToClean.filter(([table]) => !fkOrder.includes(table)).map(([t]) => t)];
148
+
149
+ // Build a single transaction with all DELETEs
150
+ const statements = ["BEGIN;"];
151
+ let paramIdx = 1;
152
+ const allParams = [];
153
+ for (const table of ordered) {
154
+ const ids = tracked.get(table);
155
+ if (!ids?.length) continue;
156
+ const placeholders = ids.map(() => `$${paramIdx++}`);
157
+ statements.push(`DELETE FROM "${table}" WHERE id IN (${placeholders.join(",")});`);
158
+ allParams.push(...ids);
159
+ }
160
+ statements.push("COMMIT;");
161
+ await withTimeout(client.query(statements.join("\n"), allParams), DB_TIMEOUT, "cleanup(batch)");
162
+ } catch {
163
+ // Swallow cleanup errors — same as before, tests shouldn't fail on cleanup
164
+ try {
165
+ await client.query("ROLLBACK;");
166
+ } catch {/* ignore */}
167
+ } finally {
168
+ client.release();
169
+ }
170
+ } else {
171
+ // Fallback: individual Supabase calls (slower, for environments without pg)
172
+ for (const [table, ids] of tablesToClean) {
173
+ if (ids?.length) {
174
+ await withTimeout(deleteByIds(table, ids), DB_TIMEOUT, `cleanup(${table})`).catch(() => {});
175
+ }
87
176
  }
88
177
  }
89
178
  tracked.clear();
90
179
  }
180
+
181
+ /**
182
+ * Shut down the Postgres pool. Call in globalTeardown or afterAll
183
+ * of the last test file to prevent open handles.
184
+ */
185
+ export async function closePool() {
186
+ if (pgPool) {
187
+ await pgPool.end();
188
+ }
189
+ }
91
190
  //# sourceMappingURL=test-db.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"test-db.js","names":["createClient","randomUUID","url","process","env","SUPABASE_URL","key","SUPABASE_SERVICE_ROLE_KEY","storeId","TEST_STORE_ID","HAS_TEST_DB","sb","TEST_STORE","RUN_PREFIX","slice","getTestClient","seed","table","rows","length","data","error","from","insert","select","Error","message","deleteByIds","ids","delete","in","deleteWhere","column","value","eq","upsertTracked","onConflict","upsert","tracked","Map","seedTracked","map","r","id","set","get","cleanup","fkOrder","orderedTables","filter","t","has","keys","includes","catch","clear"],"sources":["../../../../src/server/handlers/__test-utils__/test-db.ts"],"sourcesContent":["/**\n * Real Supabase test helper for handler tests.\n * Replaces mock-supabase.ts — all operations hit the real database.\n *\n * When SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY / TEST_STORE_ID are missing,\n * exports are stubbed so test files can skip gracefully via:\n * describe.skipIf(!HAS_TEST_DB)(\"...\", () => { ... })\n */\n\nimport { createClient, SupabaseClient } from \"@supabase/supabase-js\";\nimport { randomUUID } from \"node:crypto\";\n\nconst url = process.env.SUPABASE_URL;\nconst key = process.env.SUPABASE_SERVICE_ROLE_KEY;\nconst storeId = process.env.TEST_STORE_ID;\n\n/** true when all required env vars are present — use with describe.skipIf(!HAS_TEST_DB) */\nexport const HAS_TEST_DB = !!(url && key && storeId);\n\nconst sb = HAS_TEST_DB ? createClient(url!, key!) : (null as unknown as SupabaseClient);\n\nexport const TEST_STORE = storeId ?? \"missing-test-store\";\n\n/** Unique prefix per test run */\nexport const RUN_PREFIX = `T${randomUUID().slice(0, 6)}`;\n\n/** Get the real Supabase client (pass to handlers as `sb`) */\nexport function getTestClient(): SupabaseClient {\n return sb;\n}\n\n/** Insert rows and return them with DB-generated fields */\nexport async function seed(table: string, rows: Record<string, unknown>[]): Promise<any[]> {\n if (!rows.length) return [];\n const { data, error } = await sb.from(table).insert(rows).select();\n if (error) throw new Error(`seed(${table}): ${error.message}`);\n return data!;\n}\n\n/** Delete rows by IDs */\nexport async function deleteByIds(table: string, ids: string[]): Promise<void> {\n if (!ids.length) return;\n const { error } = await sb.from(table).delete().in(\"id\", ids);\n if (error) throw new Error(`deleteByIds(${table}): ${error.message}`);\n}\n\n/** Delete rows matching column=value */\nexport async function deleteWhere(table: string, column: string, value: unknown): Promise<void> {\n const { error } = await sb.from(table).delete().eq(column, value);\n if (error) throw new Error(`deleteWhere(${table}): ${error.message}`);\n}\n\n/** Upsert rows (for tables with unique constraints where insert would conflict) */\nexport async function upsertTracked(table: string, rows: Record<string, unknown>[], onConflict: string): Promise<any[]> {\n if (!rows.length) return [];\n const { data, error } = await sb.from(table).upsert(rows, { onConflict }).select();\n if (error) throw new Error(`upsert(${table}): ${error.message}`);\n return data!;\n}\n\n/** Track seeded data for automatic cleanup */\nconst tracked = new Map<string, string[]>();\n\nexport async function seedTracked(table: string, rows: Record<string, unknown>[]): Promise<any[]> {\n const data = await seed(table, rows);\n const ids = data.map((r: any) => r.id);\n tracked.set(table, [...(tracked.get(table) || []), ...ids]);\n return data;\n}\n\n/** Delete all tracked rows in FK-safe order */\nexport async function cleanup(): Promise<void> {\n const fkOrder = [\n \"ai_messages\",\n \"ai_conversation_checkpoints\",\n \"ai_conversations\",\n \"ai_agent_config\",\n \"platform_secrets\",\n \"business_audit\",\n \"workflow_step_runs\",\n \"workflow_dlq\",\n \"workflow_runs\",\n \"workflow_versions\",\n \"workflow_steps\",\n \"workflows\",\n \"error_events\",\n \"payment_intents\",\n \"order_items\",\n \"cart_items\",\n \"webhook_events\",\n \"inventory_levels\",\n \"inventory\",\n \"email_sends\",\n \"orders\",\n \"carts\",\n \"products\",\n \"categories\",\n \"catalogs\",\n \"creation_collections\",\n \"customers\",\n \"store_customer_profiles\",\n \"user_creation_relationships\",\n \"creations\",\n \"locations\",\n \"webhook_configs\",\n \"webhook_endpoints\",\n \"api_keys\",\n \"platform_users\",\n \"store_media\",\n \"purchase_orders\",\n \"suppliers\",\n \"stock_transfers\",\n \"v_daily_sales\",\n \"meta_ads\",\n \"meta_ad_sets\",\n \"meta_campaigns\",\n \"channel_messages\",\n \"channels\",\n \"node_events\",\n \"nodes\",\n \"store_members\",\n \"store_plans\",\n \"store_usage\",\n \"email_templates\",\n \"email_threads\",\n \"customer_risk_scores\",\n \"customer_exposures\",\n \"customer_breach_records\",\n \"customer_enrichment_profiles\",\n \"stores\",\n ];\n\n const orderedTables = [\n ...fkOrder.filter((t) => tracked.has(t)),\n ...[...tracked.keys()].filter((t) => !fkOrder.includes(t)),\n ];\n\n for (const table of orderedTables) {\n const ids = tracked.get(table);\n if (ids?.length) {\n await deleteByIds(table, ids).catch(() => {});\n }\n }\n tracked.clear();\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"test-db.js","names":["createClient","pg","randomUUID","url","process","env","SUPABASE_URL","key","SUPABASE_SERVICE_ROLE_KEY","storeId","TEST_STORE_ID","includes","Error","HAS_TEST_DB","sb","TEST_STORE","RUN_PREFIX","slice","getTestClient","pgPool","Pool","host","port","database","user","password","max","withTimeout","promise","ms","label","Promise","race","_","reject","setTimeout","DB_TIMEOUT","seed","table","rows","length","data","error","resolve","from","insert","select","message","deleteByIds","ids","delete","in","deleteWhere","column","value","eq","upsertTracked","onConflict","upsert","map","r","id","tracked","set","get","PROTECTED_TABLES","Set","Map","seedTracked","cleanup","size","tablesToClean","entries","filter","has","clear","client","connect","fkOrder","ordered","t","some","statements","paramIdx","allParams","placeholders","push","join","query","release","catch","closePool","end"],"sources":["../../../../src/server/handlers/__test-utils__/test-db.ts"],"sourcesContent":["/**\n * Real Supabase test helper for handler tests.\n * All operations hit the real LOCAL database (never production).\n *\n * When SUPABASE_URL / SUPABASE_SERVICE_ROLE_KEY / TEST_STORE_ID are missing,\n * exports are stubbed so test files can skip gracefully via:\n * describe.skipIf(!HAS_TEST_DB)(\"...\", () => { ... })\n *\n * Cleanup uses a direct Postgres connection for speed — one SQL round-trip\n * instead of 44+ individual HTTP DELETE calls through PostgREST.\n */\n\nimport { createClient, SupabaseClient } from \"@supabase/supabase-js\";\nimport pg from \"pg\";\nimport { randomUUID } from \"node:crypto\";\n\nconst url = process.env.SUPABASE_URL;\nconst key = process.env.SUPABASE_SERVICE_ROLE_KEY;\nconst storeId = process.env.TEST_STORE_ID;\n\n// Guard: refuse to run against production\nif (url && !url.includes(\"127.0.0.1\") && !url.includes(\"localhost\")) {\n throw new Error(\n `FATAL: SUPABASE_URL points to \"${url}\" which is NOT local. ` +\n `Tests must run against local Supabase (127.0.0.1:54321). ` +\n `Fix your .env.test file.`\n );\n}\n\n/** true when all required env vars are present — use with describe.skipIf(!HAS_TEST_DB) */\nexport const HAS_TEST_DB = !!(url && key && storeId);\n\nconst sb = HAS_TEST_DB ? createClient(url!, key!) : (null as unknown as SupabaseClient);\n\nexport const TEST_STORE = storeId ?? \"missing-test-store\";\n\n/** Unique prefix per test run */\nexport const RUN_PREFIX = `T${randomUUID().slice(0, 6)}`;\n\n/** Get the real Supabase client (pass to handlers as `sb`) */\nexport function getTestClient(): SupabaseClient {\n return sb;\n}\n\n// ---------------------------------------------------------------------------\n// Direct Postgres pool for fast cleanup (bypasses PostgREST HTTP overhead)\n// ---------------------------------------------------------------------------\n\nconst pgPool = HAS_TEST_DB\n ? new pg.Pool({\n host: \"127.0.0.1\",\n port: 54322,\n database: \"postgres\",\n user: \"postgres\",\n password: \"postgres\",\n max: 2,\n })\n : null;\n\n/** Timeout wrapper — prevents tests from hanging on unresponsive DB */\nfunction withTimeout<T>(promise: Promise<T>, ms: number, label: string): Promise<T> {\n return Promise.race([\n promise,\n new Promise<never>((_, reject) =>\n setTimeout(() => reject(new Error(`${label} timed out after ${ms}ms`)), ms),\n ),\n ]);\n}\n\nconst DB_TIMEOUT = 5_000;\n\n// ---------------------------------------------------------------------------\n// Seed helpers (still go through Supabase client for type consistency)\n// ---------------------------------------------------------------------------\n\n/** Insert rows and return them with DB-generated fields */\nexport async function seed(table: string, rows: Record<string, unknown>[]): Promise<any[]> {\n if (!rows.length) return [];\n const { data, error } = await withTimeout(\n Promise.resolve(sb.from(table).insert(rows).select()),\n DB_TIMEOUT,\n `seed(${table})`,\n ) as { data: any; error: any };\n if (error) throw new Error(`seed(${table}): ${error.message}`);\n return data!;\n}\n\n/** Delete rows by IDs */\nexport async function deleteByIds(table: string, ids: string[]): Promise<void> {\n if (!ids.length) return;\n const { error } = await withTimeout(\n Promise.resolve(sb.from(table).delete().in(\"id\", ids)),\n DB_TIMEOUT,\n `deleteByIds(${table})`,\n ) as { error: any };\n if (error) throw new Error(`deleteByIds(${table}): ${error.message}`);\n}\n\n/** Delete rows matching column=value */\nexport async function deleteWhere(table: string, column: string, value: unknown): Promise<void> {\n const { error } = await withTimeout(\n Promise.resolve(sb.from(table).delete().eq(column, value)),\n DB_TIMEOUT,\n `deleteWhere(${table})`,\n ) as { error: any };\n if (error) throw new Error(`deleteWhere(${table}): ${error.message}`);\n}\n\n/** Upsert rows and track for cleanup */\nexport async function upsertTracked(table: string, rows: Record<string, unknown>[], onConflict: string): Promise<any[]> {\n if (!rows.length) return [];\n const { data, error } = await withTimeout(\n Promise.resolve(sb.from(table).upsert(rows, { onConflict }).select()),\n DB_TIMEOUT,\n `upsert(${table})`,\n ) as { data: any; error: any };\n if (error) throw new Error(`upsert(${table}): ${error.message}`);\n const ids = data!.map((r: any) => r.id);\n tracked.set(table, [...(tracked.get(table) || []), ...ids]);\n return data!;\n}\n\n// ---------------------------------------------------------------------------\n// Tracking and cleanup\n// ---------------------------------------------------------------------------\n\n/**\n * Infrastructure tables that tests must NEVER delete from.\n * Tests can INSERT into these (tracked), but cleanup skips them.\n * This prevents accidental deletion of real API keys, store plans, stores, etc.\n */\nconst PROTECTED_TABLES = new Set([\n \"stores\",\n \"store_plans\",\n \"store_config\",\n \"store_prefetch_data\",\n]);\n\n/** Track seeded data for automatic cleanup */\nconst tracked = new Map<string, string[]>();\n\nexport async function seedTracked(table: string, rows: Record<string, unknown>[]): Promise<any[]> {\n const data = await seed(table, rows);\n const ids = data.map((r: any) => r.id);\n tracked.set(table, [...(tracked.get(table) || []), ...ids]);\n return data;\n}\n\n/**\n * Delete all tracked rows via a single Postgres transaction.\n *\n * Instead of 44+ individual HTTP DELETE calls through PostgREST,\n * this sends one SQL statement directly to Postgres. ~10x faster,\n * more reliable, and handles FK ordering via CASCADE-aware deletion.\n */\nexport async function cleanup(): Promise<void> {\n if (!tracked.size) return;\n\n // Filter out protected tables\n const tablesToClean = [...tracked.entries()].filter(\n ([table]) => !PROTECTED_TABLES.has(table),\n );\n\n if (!tablesToClean.length) {\n tracked.clear();\n return;\n }\n\n // Fast path: single Postgres transaction with all DELETEs\n if (pgPool) {\n const client = await pgPool.connect();\n try {\n // FK-safe order: children before parents\n const fkOrder = [\n \"ai_messages\", \"ai_conversation_checkpoints\", \"ai_conversations\",\n \"ai_agent_config\", \"platform_secrets\", \"business_audit\",\n \"workflow_step_runs\", \"workflow_dlq\", \"workflow_runs\",\n \"workflow_versions\", \"workflow_steps\", \"workflows\",\n \"error_events\", \"payment_intents\", \"order_items\", \"cart_items\",\n \"webhook_events\", \"inventory_levels\", \"inventory\", \"email_sends\",\n \"orders\", \"carts\", \"products\", \"categories\", \"catalogs\",\n \"creation_collections\", \"customers\", \"store_customer_profiles\",\n \"user_creation_relationships\", \"creations\", \"locations\",\n \"webhook_configs\", \"webhook_endpoints\", \"api_keys\", \"platform_users\",\n \"store_media\", \"purchase_orders\", \"suppliers\", \"stock_transfers\",\n \"v_daily_sales\", \"meta_ads\", \"meta_ad_sets\", \"meta_campaigns\",\n \"channel_messages\", \"channels\", \"node_events\", \"nodes\",\n \"store_members\", \"store_usage\", \"email_templates\", \"email_threads\",\n \"customer_risk_scores\", \"customer_exposures\",\n \"customer_breach_records\", \"customer_enrichment_profiles\",\n ];\n\n const ordered = [\n ...fkOrder.filter((t) => tablesToClean.some(([table]) => table === t)),\n ...tablesToClean.filter(([table]) => !fkOrder.includes(table)).map(([t]) => t),\n ];\n\n // Build a single transaction with all DELETEs\n const statements: string[] = [\"BEGIN;\"];\n let paramIdx = 1;\n const allParams: string[] = [];\n\n for (const table of ordered) {\n const ids = tracked.get(table);\n if (!ids?.length) continue;\n const placeholders = ids.map(() => `$${paramIdx++}`);\n statements.push(\n `DELETE FROM \"${table}\" WHERE id IN (${placeholders.join(\",\")});`,\n );\n allParams.push(...ids);\n }\n statements.push(\"COMMIT;\");\n\n await withTimeout(\n client.query(statements.join(\"\\n\"), allParams),\n DB_TIMEOUT,\n \"cleanup(batch)\",\n );\n } catch {\n // Swallow cleanup errors — same as before, tests shouldn't fail on cleanup\n try { await client.query(\"ROLLBACK;\"); } catch { /* ignore */ }\n } finally {\n client.release();\n }\n } else {\n // Fallback: individual Supabase calls (slower, for environments without pg)\n for (const [table, ids] of tablesToClean) {\n if (ids?.length) {\n await withTimeout(\n deleteByIds(table, ids),\n DB_TIMEOUT,\n `cleanup(${table})`,\n ).catch(() => {});\n }\n }\n }\n\n tracked.clear();\n}\n\n/**\n * Shut down the Postgres pool. Call in globalTeardown or afterAll\n * of the last test file to prevent open handles.\n */\nexport async function closePool(): Promise<void> {\n if (pgPool) {\n await pgPool.end();\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -0,0 +1,9 @@
1
+ import type { SupabaseClient } from "@supabase/supabase-js";
2
+ type Result = {
3
+ success: boolean;
4
+ data?: unknown;
5
+ error?: string;
6
+ [key: string]: unknown;
7
+ };
8
+ export declare function handleAffiliates(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<Result>;
9
+ export {};
@@ -0,0 +1,197 @@
1
+ // server/handlers/affiliates.ts — Affiliate program management and commission tracking
2
+
3
+ import { sanitizeFilterValue } from "../lib/utils.js";
4
+ export async function handleAffiliates(sb, args, storeId) {
5
+ const sid = storeId;
6
+ switch (args.action) {
7
+ // ---- LIST: list affiliates ----
8
+ case "list":
9
+ {
10
+ let q = sb.from("affiliates").select("id, email, first_name, last_name, company_name, referral_code, commission_rate, status, total_clicks, total_orders, total_revenue, total_commission_earned, total_commission_paid, pending_commission, created_at").eq("store_id", sid).order("total_revenue", {
11
+ ascending: false
12
+ });
13
+ if (args.status) q = q.eq("status", args.status);
14
+ if (args.query) {
15
+ const sq = sanitizeFilterValue(String(args.query));
16
+ q = q.or(`email.ilike.%${sq}%,first_name.ilike.%${sq}%,last_name.ilike.%${sq}%,company_name.ilike.%${sq}%,referral_code.ilike.%${sq}%`);
17
+ }
18
+ q = q.limit(args.limit || 50);
19
+ const {
20
+ data,
21
+ error
22
+ } = await q;
23
+ return error ? {
24
+ success: false,
25
+ error: error.message
26
+ } : {
27
+ success: true,
28
+ count: data?.length,
29
+ data
30
+ };
31
+ }
32
+
33
+ // ---- GET: get affiliate detail with recent conversions ----
34
+ case "get":
35
+ {
36
+ const affiliateId = args.affiliate_id;
37
+ if (!affiliateId) return {
38
+ success: false,
39
+ error: "affiliate_id is required"
40
+ };
41
+ const {
42
+ data,
43
+ error
44
+ } = await sb.from("affiliates").select("*").eq("id", affiliateId).eq("store_id", sid).single();
45
+ if (error) return {
46
+ success: false,
47
+ error: error.message
48
+ };
49
+ // Strip password_hash
50
+ const {
51
+ password_hash,
52
+ ...safe
53
+ } = data;
54
+ const {
55
+ data: conversions
56
+ } = await sb.from("affiliate_conversions").select("id, order_id, order_total, commission_rate, commission_amount, status, created_at").eq("affiliate_id", affiliateId).eq("store_id", sid).order("created_at", {
57
+ ascending: false
58
+ }).limit(20);
59
+ return {
60
+ success: true,
61
+ data: {
62
+ ...safe,
63
+ recent_conversions: conversions || []
64
+ }
65
+ };
66
+ }
67
+
68
+ // ---- CREATE: register a new affiliate ----
69
+ case "create":
70
+ {
71
+ const email = args.email;
72
+ const firstName = args.first_name;
73
+ const lastName = args.last_name;
74
+ if (!email || !firstName || !lastName) {
75
+ return {
76
+ success: false,
77
+ error: "email, first_name, and last_name are required"
78
+ };
79
+ }
80
+ const referralCode = args.referral_code || `${firstName.toLowerCase().slice(0, 4)}${Math.random().toString(36).slice(2, 8)}`;
81
+ const record = {
82
+ store_id: sid,
83
+ email,
84
+ first_name: firstName,
85
+ last_name: lastName,
86
+ referral_code: referralCode,
87
+ status: "pending"
88
+ };
89
+ if (args.phone !== undefined) record.phone = args.phone;
90
+ if (args.company_name !== undefined) record.company_name = args.company_name;
91
+ if (args.website_url !== undefined) record.website_url = args.website_url;
92
+ if (args.commission_rate !== undefined) record.commission_rate = args.commission_rate;
93
+ if (args.customer_discount_rate !== undefined) record.customer_discount_rate = args.customer_discount_rate;
94
+ if (args.minimum_payout !== undefined) record.minimum_payout = args.minimum_payout;
95
+ if (args.notes !== undefined) record.notes = args.notes;
96
+ const {
97
+ data,
98
+ error
99
+ } = await sb.from("affiliates").insert(record).select("id, email, first_name, last_name, referral_code, commission_rate, status, created_at").single();
100
+ return error ? {
101
+ success: false,
102
+ error: error.message
103
+ } : {
104
+ success: true,
105
+ data
106
+ };
107
+ }
108
+
109
+ // ---- UPDATE: modify affiliate settings ----
110
+ case "update":
111
+ {
112
+ const affiliateId = args.affiliate_id;
113
+ if (!affiliateId) return {
114
+ success: false,
115
+ error: "affiliate_id is required"
116
+ };
117
+ const updates = {
118
+ updated_at: new Date().toISOString()
119
+ };
120
+ if (args.email !== undefined) updates.email = args.email;
121
+ if (args.first_name !== undefined) updates.first_name = args.first_name;
122
+ if (args.last_name !== undefined) updates.last_name = args.last_name;
123
+ if (args.commission_rate !== undefined) updates.commission_rate = args.commission_rate;
124
+ if (args.status !== undefined) {
125
+ updates.status = args.status;
126
+ if (args.status === "active") updates.approved_at = new Date().toISOString();
127
+ }
128
+ if (args.notes !== undefined) updates.notes = args.notes;
129
+ if (args.minimum_payout !== undefined) updates.minimum_payout = args.minimum_payout;
130
+ if (args.payment_method !== undefined) updates.payment_method = args.payment_method;
131
+ if (args.payment_details !== undefined) updates.payment_details = args.payment_details;
132
+ const {
133
+ data,
134
+ error
135
+ } = await sb.from("affiliates").update(updates).eq("id", affiliateId).eq("store_id", sid).select("id, email, first_name, last_name, commission_rate, status, updated_at").single();
136
+ return error ? {
137
+ success: false,
138
+ error: error.message
139
+ } : {
140
+ success: true,
141
+ data
142
+ };
143
+ }
144
+
145
+ // ---- CONVERSIONS: list conversions for an affiliate ----
146
+ case "conversions":
147
+ {
148
+ let q = sb.from("affiliate_conversions").select("*").eq("store_id", sid).order("created_at", {
149
+ ascending: false
150
+ });
151
+ if (args.affiliate_id) q = q.eq("affiliate_id", args.affiliate_id);
152
+ if (args.status) q = q.eq("status", args.status);
153
+ q = q.limit(args.limit || 50);
154
+ const {
155
+ data,
156
+ error
157
+ } = await q;
158
+ return error ? {
159
+ success: false,
160
+ error: error.message
161
+ } : {
162
+ success: true,
163
+ count: data?.length,
164
+ data
165
+ };
166
+ }
167
+
168
+ // ---- PAYOUTS: list affiliate payouts ----
169
+ case "payouts":
170
+ {
171
+ let q = sb.from("affiliate_payouts").select("*").eq("store_id", sid).order("created_at", {
172
+ ascending: false
173
+ });
174
+ if (args.affiliate_id) q = q.eq("affiliate_id", args.affiliate_id);
175
+ if (args.status) q = q.eq("status", args.status);
176
+ q = q.limit(args.limit || 50);
177
+ const {
178
+ data,
179
+ error
180
+ } = await q;
181
+ return error ? {
182
+ success: false,
183
+ error: error.message
184
+ } : {
185
+ success: true,
186
+ count: data?.length,
187
+ data
188
+ };
189
+ }
190
+ default:
191
+ return {
192
+ success: false,
193
+ error: `Unknown affiliates action: ${args.action}. Valid: list, get, create, update, conversions, payouts`
194
+ };
195
+ }
196
+ }
197
+ //# sourceMappingURL=affiliates.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"affiliates.js","names":["sanitizeFilterValue","handleAffiliates","sb","args","storeId","sid","action","q","from","select","eq","order","ascending","status","query","sq","String","or","limit","data","error","success","message","count","length","affiliateId","affiliate_id","single","password_hash","safe","conversions","recent_conversions","email","firstName","first_name","lastName","last_name","referralCode","referral_code","toLowerCase","slice","Math","random","toString","record","store_id","phone","undefined","company_name","website_url","commission_rate","customer_discount_rate","minimum_payout","notes","insert","updates","updated_at","Date","toISOString","approved_at","payment_method","payment_details","update"],"sources":["../../../src/server/handlers/affiliates.ts"],"sourcesContent":["// server/handlers/affiliates.ts — Affiliate program management and commission tracking\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 handleAffiliates(\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 affiliates ----\n case \"list\": {\n let q = sb.from(\"affiliates\")\n .select(\"id, email, first_name, last_name, company_name, referral_code, commission_rate, status, total_clicks, total_orders, total_revenue, total_commission_earned, total_commission_paid, pending_commission, created_at\")\n .eq(\"store_id\", sid)\n .order(\"total_revenue\", { ascending: false });\n if (args.status) q = q.eq(\"status\", args.status as string);\n if (args.query) {\n const sq = sanitizeFilterValue(String(args.query));\n q = q.or(`email.ilike.%${sq}%,first_name.ilike.%${sq}%,last_name.ilike.%${sq}%,company_name.ilike.%${sq}%,referral_code.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 affiliate detail with recent conversions ----\n case \"get\": {\n const affiliateId = args.affiliate_id as string;\n if (!affiliateId) return { success: false, error: \"affiliate_id is required\" };\n const { data, error } = await sb.from(\"affiliates\")\n .select(\"*\")\n .eq(\"id\", affiliateId)\n .eq(\"store_id\", sid)\n .single();\n if (error) return { success: false, error: error.message };\n // Strip password_hash\n const { password_hash, ...safe } = data as Record<string, unknown>;\n\n const { data: conversions } = await sb.from(\"affiliate_conversions\")\n .select(\"id, order_id, order_total, commission_rate, commission_amount, status, created_at\")\n .eq(\"affiliate_id\", affiliateId)\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false })\n .limit(20);\n\n return { success: true, data: { ...safe, recent_conversions: conversions || [] } };\n }\n\n // ---- CREATE: register a new affiliate ----\n case \"create\": {\n const email = args.email as string;\n const firstName = args.first_name as string;\n const lastName = args.last_name as string;\n if (!email || !firstName || !lastName) {\n return { success: false, error: \"email, first_name, and last_name are required\" };\n }\n const referralCode = args.referral_code as string || `${firstName.toLowerCase().slice(0, 4)}${Math.random().toString(36).slice(2, 8)}`;\n const record: Record<string, unknown> = {\n store_id: sid,\n email,\n first_name: firstName,\n last_name: lastName,\n referral_code: referralCode,\n status: \"pending\",\n };\n if (args.phone !== undefined) record.phone = args.phone;\n if (args.company_name !== undefined) record.company_name = args.company_name;\n if (args.website_url !== undefined) record.website_url = args.website_url;\n if (args.commission_rate !== undefined) record.commission_rate = args.commission_rate;\n if (args.customer_discount_rate !== undefined) record.customer_discount_rate = args.customer_discount_rate;\n if (args.minimum_payout !== undefined) record.minimum_payout = args.minimum_payout;\n if (args.notes !== undefined) record.notes = args.notes;\n\n const { data, error } = await sb.from(\"affiliates\")\n .insert(record)\n .select(\"id, email, first_name, last_name, referral_code, commission_rate, status, created_at\")\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- UPDATE: modify affiliate settings ----\n case \"update\": {\n const affiliateId = args.affiliate_id as string;\n if (!affiliateId) return { success: false, error: \"affiliate_id is required\" };\n const updates: Record<string, unknown> = { updated_at: new Date().toISOString() };\n if (args.email !== undefined) updates.email = args.email;\n if (args.first_name !== undefined) updates.first_name = args.first_name;\n if (args.last_name !== undefined) updates.last_name = args.last_name;\n if (args.commission_rate !== undefined) updates.commission_rate = args.commission_rate;\n if (args.status !== undefined) {\n updates.status = args.status;\n if (args.status === \"active\") updates.approved_at = new Date().toISOString();\n }\n if (args.notes !== undefined) updates.notes = args.notes;\n if (args.minimum_payout !== undefined) updates.minimum_payout = args.minimum_payout;\n if (args.payment_method !== undefined) updates.payment_method = args.payment_method;\n if (args.payment_details !== undefined) updates.payment_details = args.payment_details;\n\n const { data, error } = await sb.from(\"affiliates\")\n .update(updates)\n .eq(\"id\", affiliateId)\n .eq(\"store_id\", sid)\n .select(\"id, email, first_name, last_name, commission_rate, status, updated_at\")\n .single();\n return error ? { success: false, error: error.message } : { success: true, data };\n }\n\n // ---- CONVERSIONS: list conversions for an affiliate ----\n case \"conversions\": {\n let q = sb.from(\"affiliate_conversions\")\n .select(\"*\")\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false });\n if (args.affiliate_id) q = q.eq(\"affiliate_id\", args.affiliate_id as string);\n if (args.status) q = q.eq(\"status\", args.status as string);\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 // ---- PAYOUTS: list affiliate payouts ----\n case \"payouts\": {\n let q = sb.from(\"affiliate_payouts\")\n .select(\"*\")\n .eq(\"store_id\", sid)\n .order(\"created_at\", { ascending: false });\n if (args.affiliate_id) q = q.eq(\"affiliate_id\", args.affiliate_id as string);\n if (args.status) q = q.eq(\"status\", args.status as string);\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 default:\n return { success: false, error: `Unknown affiliates action: ${args.action}. Valid: list, get, create, update, conversions, payouts` };\n }\n}\n"],"mappings":"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","ignoreList":[]}
@@ -4,5 +4,7 @@ export declare function handleApiDocs(_sb: SupabaseClient, args: Record<string,
4
4
  data?: unknown;
5
5
  error?: string;
6
6
  }>;
7
- /** Generate the full OpenAPI 3.0 spec from SECTIONS */
8
- export declare function generateOpenApiSpec(): object;
7
+ /** Generate (fetch) the OpenAPI spec async version for the handler */
8
+ export declare function generateOpenApiSpec(): Promise<object>;
9
+ /** Sync fallback: return cached spec or a redirect hint */
10
+ export declare function generateOpenApiSpecSync(): object;