whale-code 6.4.0 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/swagmanager-mcp.js +7 -0
- package/dist/cli/app.js +30 -2
- package/dist/cli/chat/ChatApp.d.ts +4 -4
- package/dist/cli/chat/ChatApp.js +114 -44
- package/dist/cli/chat/ChatInput.d.ts +13 -6
- package/dist/cli/chat/ChatInput.js +433 -89
- package/dist/cli/chat/MemoryManager.d.ts +15 -0
- package/dist/cli/chat/MemoryManager.js +61 -0
- package/dist/cli/chat/MessageList.d.ts +8 -0
- package/dist/cli/chat/MessageList.js +1 -1
- package/dist/cli/chat/NodeManager.d.ts +30 -0
- package/dist/cli/chat/NodeManager.js +89 -0
- package/dist/cli/chat/NodeSelector.d.ts +19 -0
- package/dist/cli/chat/NodeSelector.js +37 -0
- package/dist/cli/chat/PlanApproval.d.ts +17 -0
- package/dist/cli/chat/PlanApproval.js +82 -0
- package/dist/cli/chat/SessionManager.d.ts +16 -0
- package/dist/cli/chat/SessionManager.js +43 -0
- package/dist/cli/chat/SlashMenu.d.ts +38 -0
- package/dist/cli/chat/SlashMenu.js +208 -0
- package/dist/cli/chat/StatusBar.d.ts +16 -0
- package/dist/cli/chat/StatusBar.js +22 -0
- package/dist/cli/chat/ThemeSelector.d.ts +14 -0
- package/dist/cli/chat/ThemeSelector.js +29 -0
- package/dist/cli/chat/ToolIndicator.d.ts +8 -0
- package/dist/cli/chat/ToolIndicator.js +33 -9
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +2 -1
- package/dist/cli/chat/hooks/useAgentLoop.js +22 -17
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +19 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +254 -15
- package/dist/cli/commands/config-cmd.js +4 -25
- package/dist/cli/commands/db.d.ts +13 -0
- package/dist/cli/commands/db.js +243 -0
- package/dist/cli/commands/doctor.js +6 -9
- package/dist/cli/commands/mcp.js +1 -20
- package/dist/cli/services/agent-events.d.ts +22 -1
- package/dist/cli/services/agent-events.js +9 -0
- package/dist/cli/services/agent-loop.js +66 -2
- package/dist/cli/services/agent-worker-base.js +21 -6
- package/dist/cli/services/api-retry.d.ts +25 -0
- package/dist/cli/services/api-retry.js +91 -0
- package/dist/cli/services/auth-service.d.ts +1 -1
- package/dist/cli/services/auth-service.js +40 -19
- package/dist/cli/services/background-processes.js +26 -2
- package/dist/cli/services/config-store.d.ts +13 -1
- package/dist/cli/services/config-store.js +116 -13
- package/dist/cli/services/format-server-response.js +12 -6
- package/dist/cli/services/ink-resize-fix.d.ts +18 -0
- package/dist/cli/services/ink-resize-fix.js +66 -0
- package/dist/cli/services/interactive-tools.d.ts +14 -0
- package/dist/cli/services/interactive-tools.js +47 -2
- package/dist/cli/services/keybinding-manager.js +1 -1
- package/dist/cli/services/local-tools.js +35 -2
- package/dist/cli/services/server-tools.js +175 -3
- package/dist/cli/services/subagent.js +15 -3
- package/dist/cli/services/system-prompt.js +5 -3
- package/dist/cli/services/task-decomposer.d.ts +35 -0
- package/dist/cli/services/task-decomposer.js +199 -0
- package/dist/cli/services/team-lead.d.ts +18 -0
- package/dist/cli/services/team-lead.js +80 -0
- package/dist/cli/services/teammate.js +5 -5
- package/dist/cli/services/telemetry.d.ts +8 -2
- package/dist/cli/services/telemetry.js +116 -92
- package/dist/cli/services/tools/agent-tools.d.ts +1 -0
- package/dist/cli/services/tools/agent-tools.js +50 -4
- package/dist/cli/services/tools/file-ops.d.ts +2 -0
- package/dist/cli/services/tools/file-ops.js +71 -19
- package/dist/cli/services/tools/shell-exec.js +22 -12
- package/dist/cli/shared/Theme.d.ts +1 -2
- package/dist/cli/shared/Theme.js +1 -1
- package/dist/cli/shared/WhaleBanner.d.ts +4 -1
- package/dist/cli/shared/WhaleBanner.js +12 -8
- package/dist/cli/shared/markdown.d.ts +5 -4
- package/dist/cli/shared/markdown.js +376 -334
- package/dist/cli/shared/theme-manager.d.ts +27 -0
- package/dist/cli/shared/theme-manager.js +178 -0
- package/dist/cli/shared/theme-presets.d.ts +16 -0
- package/dist/cli/shared/theme-presets.js +265 -0
- package/dist/index.js +0 -51
- package/dist/node/adapters/imessage.d.ts +10 -0
- package/dist/node/adapters/imessage.js +45 -6
- package/dist/node/cli.js +459 -8
- package/dist/node/config.d.ts +17 -0
- package/dist/node/gateway-client.d.ts +55 -0
- package/dist/node/gateway-client.js +201 -0
- package/dist/node/portal/clipboard.d.ts +28 -0
- package/dist/node/portal/clipboard.js +183 -0
- package/dist/node/portal/discovery.d.ts +29 -0
- package/dist/node/portal/discovery.js +61 -0
- package/dist/node/portal/forward.d.ts +30 -0
- package/dist/node/portal/forward.js +90 -0
- package/dist/node/portal/index.d.ts +47 -0
- package/dist/node/portal/index.js +250 -0
- package/dist/node/portal/multiplexer.d.ts +48 -0
- package/dist/node/portal/multiplexer.js +207 -0
- package/dist/node/portal/permissions.d.ts +36 -0
- package/dist/node/portal/permissions.js +131 -0
- package/dist/node/portal/protocol.d.ts +140 -0
- package/dist/node/portal/protocol.js +193 -0
- package/dist/node/portal/screen.d.ts +18 -0
- package/dist/node/portal/screen.js +93 -0
- package/dist/node/portal/session.d.ts +68 -0
- package/dist/node/portal/session.js +127 -0
- package/dist/node/portal/shell.d.ts +26 -0
- package/dist/node/portal/shell.js +142 -0
- package/dist/node/portal/stream.d.ts +43 -0
- package/dist/node/portal/stream.js +90 -0
- package/dist/node/portal/transfer.d.ts +33 -0
- package/dist/node/portal/transfer.js +231 -0
- package/dist/node/portal/ui.d.ts +16 -0
- package/dist/node/portal/ui.js +148 -0
- package/dist/node/remote-desktop/compile-helper.d.ts +13 -0
- package/dist/node/remote-desktop/compile-helper.js +73 -0
- package/dist/node/remote-desktop/index.d.ts +67 -0
- package/dist/node/remote-desktop/index.js +220 -0
- package/dist/node/remote-desktop/protocol.d.ts +96 -0
- package/dist/node/remote-desktop/protocol.js +67 -0
- package/dist/node/runtime.d.ts +8 -1
- package/dist/node/runtime.js +117 -9
- package/dist/server/handlers/__test-utils__/test-db.d.ts +25 -0
- package/dist/server/handlers/__test-utils__/test-db.js +128 -0
- package/dist/server/handlers/api-keys.js +26 -2
- package/dist/server/handlers/browser.d.ts +0 -4
- package/dist/server/handlers/browser.js +0 -46
- package/dist/server/handlers/catalog.js +37 -14
- package/dist/server/handlers/clickhouse.d.ts +10 -0
- package/dist/server/handlers/clickhouse.js +215 -0
- package/dist/server/handlers/comms.d.ts +308 -4
- package/dist/server/handlers/comms.js +444 -11
- package/dist/server/handlers/creations.js +1 -1
- package/dist/server/handlers/crm.d.ts +54 -8
- package/dist/server/handlers/crm.js +353 -68
- package/dist/server/handlers/embeddings.js +3 -3
- package/dist/server/handlers/enrichment.js +39 -55
- package/dist/server/handlers/inventory.js +1 -1
- package/dist/server/handlers/kali.d.ts +9 -1
- package/dist/server/handlers/kali.js +50 -1
- package/dist/server/handlers/media.d.ts +8 -0
- package/dist/server/handlers/media.js +902 -0
- package/dist/server/handlers/meta-ads.js +6 -3
- package/dist/server/handlers/nodes.d.ts +2 -0
- package/dist/server/handlers/nodes.js +331 -40
- package/dist/server/handlers/operations.d.ts +4 -6
- package/dist/server/handlers/operations.js +99 -38
- package/dist/server/handlers/platform.js +224 -107
- package/dist/server/handlers/remove-bg.d.ts +6 -0
- package/dist/server/handlers/remove-bg.js +96 -0
- package/dist/server/handlers/storefront.d.ts +6 -0
- package/dist/server/handlers/storefront.js +477 -0
- package/dist/server/handlers/supply-chain.js +21 -3
- package/dist/server/handlers/workflow-steps.js +87 -31
- package/dist/server/handlers/workflows.js +4 -1
- package/dist/server/index.js +334 -88
- package/dist/server/lib/clickhouse-buffer.d.ts +48 -0
- package/dist/server/lib/clickhouse-buffer.js +175 -0
- package/dist/server/lib/clickhouse-client.d.ts +112 -0
- package/dist/server/lib/clickhouse-client.js +141 -0
- package/dist/server/lib/coa-renderer.d.ts +91 -0
- package/dist/server/lib/coa-renderer.js +411 -0
- package/dist/server/lib/compaction-service.js +45 -1
- package/dist/server/lib/pdf-renderer.d.ts +143 -0
- package/dist/server/lib/pdf-renderer.js +867 -0
- package/dist/server/lib/react-pdf-layout.d.ts +40 -0
- package/dist/server/lib/react-pdf-layout.js +437 -0
- package/dist/server/lib/server-agent-loop.d.ts +2 -0
- package/dist/server/lib/server-agent-loop.js +61 -15
- package/dist/server/lib/server-subagent.d.ts +3 -0
- package/dist/server/lib/server-subagent.js +7 -4
- package/dist/server/lib/supabase-client.js +51 -3
- package/dist/server/lib/template-resolver.js +14 -4
- package/dist/server/lib/utils.js +15 -0
- package/dist/server/local-agent-gateway.d.ts +44 -0
- package/dist/server/local-agent-gateway.js +389 -49
- package/dist/server/providers/anthropic.js +12 -2
- package/dist/server/providers/gemini.js +17 -2
- package/dist/server/proxy-handlers.js +151 -0
- package/dist/server/tool-router.d.ts +2 -2
- package/dist/server/tool-router.js +25 -35
- package/dist/shared/agent-core.d.ts +5 -2
- package/dist/shared/agent-core.js +30 -4
- package/dist/shared/api-client.js +54 -3
- package/dist/shared/sse-parser.d.ts +1 -1
- package/dist/shared/sse-parser.js +5 -2
- package/dist/shared/tool-dispatch.js +1 -1
- package/package.json +16 -10
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +0 -11
- package/dist/server/handlers/__test-utils__/mock-supabase.js +0 -393
|
@@ -1,393 +0,0 @@
|
|
|
1
|
-
// __test-utils__/mock-supabase.ts — Reusable mock Supabase client for handler tests
|
|
2
|
-
//
|
|
3
|
-
// Provides an in-memory Supabase client mock with chainable query builder methods
|
|
4
|
-
// that mirror the real SupabaseClient interface used by all handlers.
|
|
5
|
-
import { vi } from "vitest";
|
|
6
|
-
// ---------------------------------------------------------------------------
|
|
7
|
-
// In-memory stores
|
|
8
|
-
// ---------------------------------------------------------------------------
|
|
9
|
-
/** Per-table row arrays keyed by table name */
|
|
10
|
-
let tableData = {};
|
|
11
|
-
/** Per-RPC function response stubs keyed by RPC name */
|
|
12
|
-
let rpcResponses = {};
|
|
13
|
-
/** Per-table forced errors (simulates DB failures) */
|
|
14
|
-
let tableErrors = {};
|
|
15
|
-
// ---------------------------------------------------------------------------
|
|
16
|
-
// Public helpers
|
|
17
|
-
// ---------------------------------------------------------------------------
|
|
18
|
-
/** Pre-populate a table with rows for the current test */
|
|
19
|
-
export function setTableData(table, rows) {
|
|
20
|
-
tableData[table] = [...rows];
|
|
21
|
-
}
|
|
22
|
-
/** Pre-configure the return value for an RPC call */
|
|
23
|
-
export function setRpcResponse(name, data, error) {
|
|
24
|
-
rpcResponses[name] = { data: data ?? null, error: error ?? null };
|
|
25
|
-
}
|
|
26
|
-
/** Force a table to return an error on any operation */
|
|
27
|
-
export function setTableError(table, message, code) {
|
|
28
|
-
tableErrors[table] = { message, code };
|
|
29
|
-
}
|
|
30
|
-
/** Reset all in-memory state between tests */
|
|
31
|
-
export function resetMock() {
|
|
32
|
-
tableData = {};
|
|
33
|
-
rpcResponses = {};
|
|
34
|
-
tableErrors = {};
|
|
35
|
-
}
|
|
36
|
-
/** Get current in-memory rows for a table (for post-operation assertions) */
|
|
37
|
-
export function getTableData(table) {
|
|
38
|
-
return tableData[table] || [];
|
|
39
|
-
}
|
|
40
|
-
function defaultState(table) {
|
|
41
|
-
return {
|
|
42
|
-
table,
|
|
43
|
-
selectColumns: "*",
|
|
44
|
-
filters: [],
|
|
45
|
-
orderCol: null,
|
|
46
|
-
orderAsc: true,
|
|
47
|
-
limitCount: 1000,
|
|
48
|
-
rangeFrom: null,
|
|
49
|
-
rangeTo: null,
|
|
50
|
-
isSingle: false,
|
|
51
|
-
isMaybeSingle: false,
|
|
52
|
-
isInsert: false,
|
|
53
|
-
isUpdate: false,
|
|
54
|
-
isDelete: false,
|
|
55
|
-
insertPayload: null,
|
|
56
|
-
updatePayload: null,
|
|
57
|
-
countMode: false,
|
|
58
|
-
headMode: false,
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
function applyFilters(rows, filters) {
|
|
62
|
-
let result = rows;
|
|
63
|
-
for (const fn of filters) {
|
|
64
|
-
result = result.filter((r) => fn(r));
|
|
65
|
-
}
|
|
66
|
-
return result;
|
|
67
|
-
}
|
|
68
|
-
/** Convert SQL LIKE/ILIKE pattern to regex */
|
|
69
|
-
function likeToRegex(pattern, caseInsensitive) {
|
|
70
|
-
// Escape regex special chars except % and _
|
|
71
|
-
const escaped = pattern.replace(/[.*+?^${}()|[\]\\]/g, (ch) => {
|
|
72
|
-
if (ch === "%" || ch === "_")
|
|
73
|
-
return ch;
|
|
74
|
-
return `\\${ch}`;
|
|
75
|
-
});
|
|
76
|
-
const regexStr = "^" + escaped.replace(/%/g, ".*").replace(/_/g, ".") + "$";
|
|
77
|
-
return new RegExp(regexStr, caseInsensitive ? "i" : "");
|
|
78
|
-
}
|
|
79
|
-
function buildQueryBuilder(state) {
|
|
80
|
-
const builder = {};
|
|
81
|
-
// select -----------------------------------------------------------------
|
|
82
|
-
builder.select = vi.fn((columns, opts) => {
|
|
83
|
-
state.selectColumns = columns || "*";
|
|
84
|
-
if (opts?.count)
|
|
85
|
-
state.countMode = true;
|
|
86
|
-
if (opts?.head)
|
|
87
|
-
state.headMode = true;
|
|
88
|
-
return builder;
|
|
89
|
-
});
|
|
90
|
-
// insert -----------------------------------------------------------------
|
|
91
|
-
builder.insert = vi.fn((payload) => {
|
|
92
|
-
state.isInsert = true;
|
|
93
|
-
state.insertPayload = payload;
|
|
94
|
-
return builder;
|
|
95
|
-
});
|
|
96
|
-
// update -----------------------------------------------------------------
|
|
97
|
-
builder.update = vi.fn((payload) => {
|
|
98
|
-
state.isUpdate = true;
|
|
99
|
-
state.updatePayload = payload;
|
|
100
|
-
return builder;
|
|
101
|
-
});
|
|
102
|
-
// delete -----------------------------------------------------------------
|
|
103
|
-
builder.delete = vi.fn(() => {
|
|
104
|
-
state.isDelete = true;
|
|
105
|
-
return builder;
|
|
106
|
-
});
|
|
107
|
-
// upsert -----------------------------------------------------------------
|
|
108
|
-
builder.upsert = vi.fn((payload, _opts) => {
|
|
109
|
-
state.isInsert = true;
|
|
110
|
-
state.insertPayload = payload;
|
|
111
|
-
return builder;
|
|
112
|
-
});
|
|
113
|
-
// filters ----------------------------------------------------------------
|
|
114
|
-
builder.eq = vi.fn((col, val) => {
|
|
115
|
-
state.filters.push((r) => r[col] === val);
|
|
116
|
-
return builder;
|
|
117
|
-
});
|
|
118
|
-
builder.neq = vi.fn((col, val) => {
|
|
119
|
-
state.filters.push((r) => r[col] !== val);
|
|
120
|
-
return builder;
|
|
121
|
-
});
|
|
122
|
-
builder.gt = vi.fn((col, val) => {
|
|
123
|
-
state.filters.push((r) => r[col] > val);
|
|
124
|
-
return builder;
|
|
125
|
-
});
|
|
126
|
-
builder.gte = vi.fn((col, val) => {
|
|
127
|
-
state.filters.push((r) => r[col] >= val);
|
|
128
|
-
return builder;
|
|
129
|
-
});
|
|
130
|
-
builder.lt = vi.fn((col, val) => {
|
|
131
|
-
state.filters.push((r) => r[col] < val);
|
|
132
|
-
return builder;
|
|
133
|
-
});
|
|
134
|
-
builder.lte = vi.fn((col, val) => {
|
|
135
|
-
state.filters.push((r) => r[col] <= val);
|
|
136
|
-
return builder;
|
|
137
|
-
});
|
|
138
|
-
builder.in = vi.fn((col, vals) => {
|
|
139
|
-
state.filters.push((r) => vals.includes(r[col]));
|
|
140
|
-
return builder;
|
|
141
|
-
});
|
|
142
|
-
builder.not = vi.fn((col, op, val) => {
|
|
143
|
-
state.filters.push((r) => {
|
|
144
|
-
switch (op) {
|
|
145
|
-
case "eq": return r[col] !== val;
|
|
146
|
-
case "is": return r[col] !== val;
|
|
147
|
-
case "in": return !(val || []).includes(r[col]);
|
|
148
|
-
case "like": return !likeToRegex(val, false).test(String(r[col] ?? ""));
|
|
149
|
-
case "ilike": return !likeToRegex(val, true).test(String(r[col] ?? ""));
|
|
150
|
-
default: return true;
|
|
151
|
-
}
|
|
152
|
-
});
|
|
153
|
-
return builder;
|
|
154
|
-
});
|
|
155
|
-
builder.ilike = vi.fn((col, pattern) => {
|
|
156
|
-
const re = likeToRegex(pattern, true);
|
|
157
|
-
state.filters.push((r) => re.test(String(r[col] ?? "")));
|
|
158
|
-
return builder;
|
|
159
|
-
});
|
|
160
|
-
builder.like = vi.fn((col, pattern) => {
|
|
161
|
-
const re = likeToRegex(pattern, false);
|
|
162
|
-
state.filters.push((r) => re.test(String(r[col] ?? "")));
|
|
163
|
-
return builder;
|
|
164
|
-
});
|
|
165
|
-
builder.or = vi.fn((expr) => {
|
|
166
|
-
// Parse simple PostgREST or-expressions like "status.eq.active,status.eq.draft"
|
|
167
|
-
const clauses = expr.split(",").map((c) => c.trim());
|
|
168
|
-
const parsedFilters = [];
|
|
169
|
-
for (const clause of clauses) {
|
|
170
|
-
const parts = clause.split(".");
|
|
171
|
-
if (parts.length >= 3) {
|
|
172
|
-
const col = parts[0];
|
|
173
|
-
const op = parts[1];
|
|
174
|
-
const val = parts.slice(2).join(".");
|
|
175
|
-
switch (op) {
|
|
176
|
-
case "eq":
|
|
177
|
-
parsedFilters.push((r) => String(r[col]) === val);
|
|
178
|
-
break;
|
|
179
|
-
case "neq":
|
|
180
|
-
parsedFilters.push((r) => String(r[col]) !== val);
|
|
181
|
-
break;
|
|
182
|
-
case "ilike":
|
|
183
|
-
parsedFilters.push((r) => likeToRegex(val, true).test(String(r[col] ?? "")));
|
|
184
|
-
break;
|
|
185
|
-
case "like":
|
|
186
|
-
parsedFilters.push((r) => likeToRegex(val, false).test(String(r[col] ?? "")));
|
|
187
|
-
break;
|
|
188
|
-
case "is":
|
|
189
|
-
parsedFilters.push((r) => {
|
|
190
|
-
if (val === "null")
|
|
191
|
-
return r[col] === null || r[col] === undefined;
|
|
192
|
-
return false;
|
|
193
|
-
});
|
|
194
|
-
break;
|
|
195
|
-
default:
|
|
196
|
-
// Unknown op — pass through
|
|
197
|
-
parsedFilters.push(() => true);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
if (parsedFilters.length > 0) {
|
|
202
|
-
state.filters.push((r) => parsedFilters.some((fn) => fn(r)));
|
|
203
|
-
}
|
|
204
|
-
return builder;
|
|
205
|
-
});
|
|
206
|
-
builder.is = vi.fn((col, val) => {
|
|
207
|
-
state.filters.push((r) => {
|
|
208
|
-
if (val === null)
|
|
209
|
-
return r[col] === null || r[col] === undefined;
|
|
210
|
-
return r[col] === val;
|
|
211
|
-
});
|
|
212
|
-
return builder;
|
|
213
|
-
});
|
|
214
|
-
builder.contains = vi.fn((col, val) => {
|
|
215
|
-
state.filters.push((r) => {
|
|
216
|
-
const v = r[col];
|
|
217
|
-
if (Array.isArray(v) && Array.isArray(val)) {
|
|
218
|
-
return val.every((item) => v.includes(item));
|
|
219
|
-
}
|
|
220
|
-
return false;
|
|
221
|
-
});
|
|
222
|
-
return builder;
|
|
223
|
-
});
|
|
224
|
-
builder.containedBy = vi.fn((col, val) => {
|
|
225
|
-
state.filters.push((r) => {
|
|
226
|
-
const v = r[col];
|
|
227
|
-
if (Array.isArray(v) && Array.isArray(val)) {
|
|
228
|
-
return v.every((item) => val.includes(item));
|
|
229
|
-
}
|
|
230
|
-
return false;
|
|
231
|
-
});
|
|
232
|
-
return builder;
|
|
233
|
-
});
|
|
234
|
-
builder.textSearch = vi.fn((_col, _query, _opts) => {
|
|
235
|
-
// Simplified: no-op (full-text search not easily mockable)
|
|
236
|
-
return builder;
|
|
237
|
-
});
|
|
238
|
-
// ordering / limits ------------------------------------------------------
|
|
239
|
-
builder.order = vi.fn((_col, _opts) => {
|
|
240
|
-
state.orderCol = _col;
|
|
241
|
-
state.orderAsc = _opts?.ascending ?? true;
|
|
242
|
-
return builder;
|
|
243
|
-
});
|
|
244
|
-
builder.limit = vi.fn((n) => {
|
|
245
|
-
state.limitCount = n;
|
|
246
|
-
return builder;
|
|
247
|
-
});
|
|
248
|
-
builder.range = vi.fn((from, to) => {
|
|
249
|
-
state.rangeFrom = from;
|
|
250
|
-
state.rangeTo = to;
|
|
251
|
-
return builder;
|
|
252
|
-
});
|
|
253
|
-
// terminal modifiers -----------------------------------------------------
|
|
254
|
-
builder.single = vi.fn(() => {
|
|
255
|
-
state.isSingle = true;
|
|
256
|
-
return resolve(state);
|
|
257
|
-
});
|
|
258
|
-
builder.maybeSingle = vi.fn(() => {
|
|
259
|
-
state.isMaybeSingle = true;
|
|
260
|
-
return resolve(state);
|
|
261
|
-
});
|
|
262
|
-
// csv export mock --------------------------------------------------------
|
|
263
|
-
builder.csv = vi.fn(() => {
|
|
264
|
-
const rows = tableData[state.table] || [];
|
|
265
|
-
const filtered = applyFilters(rows, state.filters);
|
|
266
|
-
if (filtered.length === 0)
|
|
267
|
-
return { data: "", error: null };
|
|
268
|
-
const keys = Object.keys(filtered[0]);
|
|
269
|
-
const header = keys.join(",");
|
|
270
|
-
const lines = filtered.map((r) => keys.map((k) => String(r[k] ?? "")).join(","));
|
|
271
|
-
return { data: [header, ...lines].join("\n"), error: null };
|
|
272
|
-
});
|
|
273
|
-
// Make the builder thenable so `await sb.from(...).select(...)...` works
|
|
274
|
-
// When no terminal modifier (.single/.maybeSingle) is called, the query
|
|
275
|
-
// resolves on await.
|
|
276
|
-
builder.then = (onFulfilled, onRejected) => {
|
|
277
|
-
const result = resolve(state);
|
|
278
|
-
return Promise.resolve(result).then(onFulfilled, onRejected);
|
|
279
|
-
};
|
|
280
|
-
return builder;
|
|
281
|
-
}
|
|
282
|
-
/** Resolve the query against the in-memory store */
|
|
283
|
-
function resolve(state) {
|
|
284
|
-
// Check for forced table errors
|
|
285
|
-
const tableError = tableErrors[state.table];
|
|
286
|
-
if (tableError) {
|
|
287
|
-
return { data: null, error: tableError };
|
|
288
|
-
}
|
|
289
|
-
const rows = tableData[state.table] || [];
|
|
290
|
-
// INSERT
|
|
291
|
-
if (state.isInsert) {
|
|
292
|
-
const payload = state.insertPayload;
|
|
293
|
-
const newRows = Array.isArray(payload) ? payload : [payload];
|
|
294
|
-
// Assign ids if missing
|
|
295
|
-
const withIds = newRows.map((r, i) => ({
|
|
296
|
-
id: `mock-id-${Date.now()}-${i}`,
|
|
297
|
-
created_at: new Date().toISOString(),
|
|
298
|
-
...r,
|
|
299
|
-
}));
|
|
300
|
-
if (!tableData[state.table])
|
|
301
|
-
tableData[state.table] = [];
|
|
302
|
-
tableData[state.table].push(...withIds);
|
|
303
|
-
if (state.isSingle) {
|
|
304
|
-
return { data: withIds[0], error: null };
|
|
305
|
-
}
|
|
306
|
-
return { data: withIds, error: null };
|
|
307
|
-
}
|
|
308
|
-
// UPDATE
|
|
309
|
-
if (state.isUpdate) {
|
|
310
|
-
const matching = applyFilters(rows, state.filters);
|
|
311
|
-
const updated = matching.map((r) => ({ ...r, ...state.updatePayload }));
|
|
312
|
-
// Replace in store
|
|
313
|
-
tableData[state.table] = rows.map((r) => {
|
|
314
|
-
const upd = updated.find((u) => u.id === r.id);
|
|
315
|
-
return upd || r;
|
|
316
|
-
});
|
|
317
|
-
if (state.isSingle || state.isMaybeSingle) {
|
|
318
|
-
return { data: updated[0] || null, error: null };
|
|
319
|
-
}
|
|
320
|
-
return { data: updated, error: null, count: updated.length };
|
|
321
|
-
}
|
|
322
|
-
// DELETE
|
|
323
|
-
if (state.isDelete) {
|
|
324
|
-
const before = rows.length;
|
|
325
|
-
const remaining = rows.filter((r) => !state.filters.every((fn) => fn(r)));
|
|
326
|
-
tableData[state.table] = remaining;
|
|
327
|
-
const deletedCount = before - remaining.length;
|
|
328
|
-
return { data: null, error: null, count: deletedCount };
|
|
329
|
-
}
|
|
330
|
-
// SELECT
|
|
331
|
-
let filtered = applyFilters(rows, state.filters);
|
|
332
|
-
if (state.orderCol) {
|
|
333
|
-
const col = state.orderCol;
|
|
334
|
-
const asc = state.orderAsc;
|
|
335
|
-
filtered.sort((a, b) => {
|
|
336
|
-
if (a[col] < b[col])
|
|
337
|
-
return asc ? -1 : 1;
|
|
338
|
-
if (a[col] > b[col])
|
|
339
|
-
return asc ? 1 : -1;
|
|
340
|
-
return 0;
|
|
341
|
-
});
|
|
342
|
-
}
|
|
343
|
-
// Apply range before limit
|
|
344
|
-
if (state.rangeFrom !== null && state.rangeTo !== null) {
|
|
345
|
-
filtered = filtered.slice(state.rangeFrom, state.rangeTo + 1);
|
|
346
|
-
}
|
|
347
|
-
else {
|
|
348
|
-
filtered = filtered.slice(0, state.limitCount);
|
|
349
|
-
}
|
|
350
|
-
if (state.countMode && state.headMode) {
|
|
351
|
-
return { data: null, error: null, count: filtered.length };
|
|
352
|
-
}
|
|
353
|
-
if (state.isSingle) {
|
|
354
|
-
return { data: filtered[0] || null, error: filtered.length === 0 ? { message: "Row not found" } : null };
|
|
355
|
-
}
|
|
356
|
-
if (state.isMaybeSingle) {
|
|
357
|
-
return { data: filtered[0] || null, error: null };
|
|
358
|
-
}
|
|
359
|
-
return { data: filtered, error: null, count: filtered.length };
|
|
360
|
-
}
|
|
361
|
-
// ---------------------------------------------------------------------------
|
|
362
|
-
// Main factory
|
|
363
|
-
// ---------------------------------------------------------------------------
|
|
364
|
-
export function createMockSupabase() {
|
|
365
|
-
const client = {
|
|
366
|
-
from: vi.fn((table) => {
|
|
367
|
-
const state = defaultState(table);
|
|
368
|
-
return buildQueryBuilder(state);
|
|
369
|
-
}),
|
|
370
|
-
rpc: vi.fn((name, params) => {
|
|
371
|
-
const resp = rpcResponses[name];
|
|
372
|
-
if (resp)
|
|
373
|
-
return Promise.resolve(resp);
|
|
374
|
-
// Default: success with null data
|
|
375
|
-
return Promise.resolve({ data: null, error: null });
|
|
376
|
-
}),
|
|
377
|
-
storage: {
|
|
378
|
-
from: vi.fn((_bucket) => ({
|
|
379
|
-
upload: vi.fn((_path, _file, _opts) => Promise.resolve({ data: { path: _path }, error: null })),
|
|
380
|
-
getPublicUrl: vi.fn((path) => ({
|
|
381
|
-
data: { publicUrl: `https://mock-storage.test/${path}` },
|
|
382
|
-
})),
|
|
383
|
-
remove: vi.fn((_paths) => Promise.resolve({ data: _paths.map((p) => ({ name: p })), error: null })),
|
|
384
|
-
list: vi.fn((_prefix) => Promise.resolve({ data: [], error: null })),
|
|
385
|
-
})),
|
|
386
|
-
},
|
|
387
|
-
auth: {
|
|
388
|
-
getUser: vi.fn(() => Promise.resolve({ data: { user: null }, error: null })),
|
|
389
|
-
getSession: vi.fn(() => Promise.resolve({ data: { session: null }, error: null })),
|
|
390
|
-
},
|
|
391
|
-
};
|
|
392
|
-
return client;
|
|
393
|
-
}
|