intellitester 0.1.12
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/README.md +200 -0
- package/dist/chunk-35WJGNDA.cjs +136 -0
- package/dist/chunk-35WJGNDA.cjs.map +1 -0
- package/dist/chunk-4B54JUOP.js +234 -0
- package/dist/chunk-4B54JUOP.js.map +1 -0
- package/dist/chunk-5LFSLMQ7.js +2517 -0
- package/dist/chunk-5LFSLMQ7.js.map +1 -0
- package/dist/chunk-6PYKWWH5.js +63 -0
- package/dist/chunk-6PYKWWH5.js.map +1 -0
- package/dist/chunk-ARJYJVRM.cjs +302 -0
- package/dist/chunk-ARJYJVRM.cjs.map +1 -0
- package/dist/chunk-CN6HSJJX.js +133 -0
- package/dist/chunk-CN6HSJJX.js.map +1 -0
- package/dist/chunk-DE5UFTTG.js +31 -0
- package/dist/chunk-DE5UFTTG.js.map +1 -0
- package/dist/chunk-ECBA4GJ3.js +287 -0
- package/dist/chunk-ECBA4GJ3.js.map +1 -0
- package/dist/chunk-OFXNJXMV.cjs +237 -0
- package/dist/chunk-OFXNJXMV.cjs.map +1 -0
- package/dist/chunk-PAKODOH4.cjs +66 -0
- package/dist/chunk-PAKODOH4.cjs.map +1 -0
- package/dist/chunk-QMYM2TCH.cjs +36 -0
- package/dist/chunk-QMYM2TCH.cjs.map +1 -0
- package/dist/chunk-SAVY6D3X.js +125 -0
- package/dist/chunk-SAVY6D3X.js.map +1 -0
- package/dist/chunk-UUJXCHVT.cjs +128 -0
- package/dist/chunk-UUJXCHVT.cjs.map +1 -0
- package/dist/chunk-XWGUA67E.cjs +2552 -0
- package/dist/chunk-XWGUA67E.cjs.map +1 -0
- package/dist/cli/index.cjs +1985 -0
- package/dist/cli/index.cjs.map +1 -0
- package/dist/cli/index.d.cts +1 -0
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +1957 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/cleanup/index.cjs +45 -0
- package/dist/core/cleanup/index.cjs.map +1 -0
- package/dist/core/cleanup/index.d.cts +117 -0
- package/dist/core/cleanup/index.d.ts +117 -0
- package/dist/core/cleanup/index.js +8 -0
- package/dist/core/cleanup/index.js.map +1 -0
- package/dist/index.cjs +110 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +852 -0
- package/dist/index.d.ts +852 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/integration/index.cjs +22 -0
- package/dist/integration/index.cjs.map +1 -0
- package/dist/integration/index.d.cts +42 -0
- package/dist/integration/index.d.ts +42 -0
- package/dist/integration/index.js +20 -0
- package/dist/integration/index.js.map +1 -0
- package/dist/providers/appwrite/index.cjs +16 -0
- package/dist/providers/appwrite/index.cjs.map +1 -0
- package/dist/providers/appwrite/index.d.cts +12 -0
- package/dist/providers/appwrite/index.d.ts +12 -0
- package/dist/providers/appwrite/index.js +3 -0
- package/dist/providers/appwrite/index.js.map +1 -0
- package/dist/providers/index.cjs +60 -0
- package/dist/providers/index.cjs.map +1 -0
- package/dist/providers/index.d.cts +13 -0
- package/dist/providers/index.d.ts +13 -0
- package/dist/providers/index.js +7 -0
- package/dist/providers/index.js.map +1 -0
- package/dist/providers/mysql/index.cjs +16 -0
- package/dist/providers/mysql/index.cjs.map +1 -0
- package/dist/providers/mysql/index.d.cts +14 -0
- package/dist/providers/mysql/index.d.ts +14 -0
- package/dist/providers/mysql/index.js +3 -0
- package/dist/providers/mysql/index.js.map +1 -0
- package/dist/providers/postgres/index.cjs +16 -0
- package/dist/providers/postgres/index.cjs.map +1 -0
- package/dist/providers/postgres/index.d.cts +10 -0
- package/dist/providers/postgres/index.d.ts +10 -0
- package/dist/providers/postgres/index.js +3 -0
- package/dist/providers/postgres/index.js.map +1 -0
- package/dist/providers/sqlite/index.cjs +16 -0
- package/dist/providers/sqlite/index.cjs.map +1 -0
- package/dist/providers/sqlite/index.d.cts +11 -0
- package/dist/providers/sqlite/index.d.ts +11 -0
- package/dist/providers/sqlite/index.js +3 -0
- package/dist/providers/sqlite/index.js.map +1 -0
- package/dist/types-LONNVTIF.d.cts +56 -0
- package/dist/types-l-ZaFKC-.d.ts +56 -0
- package/package.json +114 -0
- package/schemas/intellitester.config.schema.json +384 -0
- package/schemas/test.schema.json +517 -0
- package/schemas/workflow.schema.json +227 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkQMYM2TCH_cjs = require('./chunk-QMYM2TCH.cjs');
|
|
4
|
+
var glob = require('glob');
|
|
5
|
+
var path = require('path');
|
|
6
|
+
var fs = require('fs');
|
|
7
|
+
var fs2 = require('fs/promises');
|
|
8
|
+
|
|
9
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
|
+
|
|
11
|
+
var path__default = /*#__PURE__*/_interopDefault(path);
|
|
12
|
+
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
13
|
+
var fs2__default = /*#__PURE__*/_interopDefault(fs2);
|
|
14
|
+
|
|
15
|
+
async function loadCleanupHandlers(config, cwd = process.cwd()) {
|
|
16
|
+
const handlers = /* @__PURE__ */ new Map();
|
|
17
|
+
let typeMappings = {};
|
|
18
|
+
let provider;
|
|
19
|
+
if (config.provider) {
|
|
20
|
+
const providerConfig = config[config.provider];
|
|
21
|
+
if (!providerConfig) {
|
|
22
|
+
throw new Error(`Provider "${config.provider}" specified but no configuration found`);
|
|
23
|
+
}
|
|
24
|
+
const factory = chunkQMYM2TCH_cjs.providerFactories[config.provider];
|
|
25
|
+
if (!factory) {
|
|
26
|
+
throw new Error(`Unknown provider: ${config.provider}. Available: ${Object.keys(chunkQMYM2TCH_cjs.providerFactories).join(", ")}`);
|
|
27
|
+
}
|
|
28
|
+
provider = factory(providerConfig);
|
|
29
|
+
await provider.configure(providerConfig);
|
|
30
|
+
for (const [methodName, handler] of Object.entries(provider.methods)) {
|
|
31
|
+
handlers.set(`${provider.name}.${methodName}`, handler);
|
|
32
|
+
}
|
|
33
|
+
typeMappings = { ...chunkQMYM2TCH_cjs.getDefaultTypeMappings(config.provider) };
|
|
34
|
+
}
|
|
35
|
+
const rootCleanupPath = path__default.default.join(cwd, "intellitester.cleanup.ts");
|
|
36
|
+
const rootHandlers = await tryLoadHandlerFile(rootCleanupPath);
|
|
37
|
+
if (rootHandlers) {
|
|
38
|
+
mergeHandlers(handlers, rootHandlers);
|
|
39
|
+
}
|
|
40
|
+
if (config.discover?.enabled !== false) {
|
|
41
|
+
const discoveryPaths = config.discover?.paths ?? ["./tests/cleanup"];
|
|
42
|
+
const pattern = config.discover?.pattern ?? "**/*.ts";
|
|
43
|
+
for (const basePath of discoveryPaths) {
|
|
44
|
+
const absoluteBase = path__default.default.isAbsolute(basePath) ? basePath : path__default.default.join(cwd, basePath);
|
|
45
|
+
try {
|
|
46
|
+
const files = await glob.glob(pattern, {
|
|
47
|
+
cwd: absoluteBase,
|
|
48
|
+
absolute: true,
|
|
49
|
+
ignore: ["**/*.d.ts", "**/node_modules/**"]
|
|
50
|
+
});
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
const fileHandlers = await tryLoadHandlerFile(file);
|
|
53
|
+
if (fileHandlers) {
|
|
54
|
+
mergeHandlers(handlers, fileHandlers);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
} catch {
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const handlerPath of config.handlers ?? []) {
|
|
62
|
+
const absolutePath = path__default.default.isAbsolute(handlerPath) ? handlerPath : path__default.default.join(cwd, handlerPath);
|
|
63
|
+
const fileHandlers = await tryLoadHandlerFile(absolutePath);
|
|
64
|
+
if (fileHandlers) {
|
|
65
|
+
mergeHandlers(handlers, fileHandlers);
|
|
66
|
+
} else {
|
|
67
|
+
console.warn(`Warning: Could not load cleanup handler file: ${handlerPath}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (config.types) {
|
|
71
|
+
typeMappings = { ...typeMappings, ...config.types };
|
|
72
|
+
}
|
|
73
|
+
return { handlers, typeMappings, provider };
|
|
74
|
+
}
|
|
75
|
+
async function tryLoadHandlerFile(filePath) {
|
|
76
|
+
try {
|
|
77
|
+
if (!fs__default.default.existsSync(filePath)) {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
let loadPath = filePath;
|
|
81
|
+
if (filePath.endsWith(".ts")) {
|
|
82
|
+
const jsPath = filePath.replace(/\.ts$/, ".js");
|
|
83
|
+
if (fs__default.default.existsSync(jsPath)) {
|
|
84
|
+
loadPath = jsPath;
|
|
85
|
+
} else {
|
|
86
|
+
loadPath = filePath;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
const module = await import(`${loadPath}?t=${Date.now()}`);
|
|
90
|
+
if (module.default && typeof module.default === "object") {
|
|
91
|
+
return module.default;
|
|
92
|
+
}
|
|
93
|
+
const handlers = {};
|
|
94
|
+
for (const [key, value] of Object.entries(module)) {
|
|
95
|
+
if (typeof value === "function" && key !== "default") {
|
|
96
|
+
handlers[key] = value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return Object.keys(handlers).length > 0 ? handlers : null;
|
|
100
|
+
} catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function mergeHandlers(target, source) {
|
|
105
|
+
for (const [key, handler] of Object.entries(source)) {
|
|
106
|
+
target.set(key, handler);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function resolveHandler(handlers, typeMappings, resourceType) {
|
|
110
|
+
const mappedKey = typeMappings[resourceType];
|
|
111
|
+
if (mappedKey && handlers.has(mappedKey)) {
|
|
112
|
+
return handlers.get(mappedKey);
|
|
113
|
+
}
|
|
114
|
+
if (handlers.has(resourceType)) {
|
|
115
|
+
return handlers.get(resourceType);
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
var CLEANUP_DIR = ".intellitester/cleanup/failed";
|
|
120
|
+
async function saveFailedCleanup(cleanup, cwd = process.cwd()) {
|
|
121
|
+
const dir = path__default.default.join(cwd, CLEANUP_DIR);
|
|
122
|
+
await fs2__default.default.mkdir(dir, { recursive: true });
|
|
123
|
+
const filePath = path__default.default.join(dir, `${cleanup.sessionId}.json`);
|
|
124
|
+
await fs2__default.default.writeFile(filePath, JSON.stringify(cleanup, null, 2), "utf8");
|
|
125
|
+
console.log(`Saved failed cleanup to ${filePath}`);
|
|
126
|
+
}
|
|
127
|
+
async function loadFailedCleanups(cwd = process.cwd()) {
|
|
128
|
+
const dir = path__default.default.join(cwd, CLEANUP_DIR);
|
|
129
|
+
try {
|
|
130
|
+
const files = await fs2__default.default.readdir(dir);
|
|
131
|
+
const cleanups = [];
|
|
132
|
+
for (const file of files) {
|
|
133
|
+
if (file.endsWith(".json")) {
|
|
134
|
+
const content = await fs2__default.default.readFile(path__default.default.join(dir, file), "utf8");
|
|
135
|
+
cleanups.push(JSON.parse(content));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return cleanups;
|
|
139
|
+
} catch (error) {
|
|
140
|
+
if (error.code === "ENOENT") {
|
|
141
|
+
return [];
|
|
142
|
+
}
|
|
143
|
+
throw error;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
async function removeFailedCleanup(sessionId, cwd = process.cwd()) {
|
|
147
|
+
const filePath = path__default.default.join(cwd, CLEANUP_DIR, `${sessionId}.json`);
|
|
148
|
+
try {
|
|
149
|
+
await fs2__default.default.unlink(filePath);
|
|
150
|
+
console.log(`Removed failed cleanup file: ${sessionId}.json`);
|
|
151
|
+
} catch (error) {
|
|
152
|
+
if (error.code !== "ENOENT") {
|
|
153
|
+
throw error;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// src/core/cleanup/executor.ts
|
|
159
|
+
var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
160
|
+
async function executeCleanup(resources, handlers, typeMappings, options = {}) {
|
|
161
|
+
const { parallel = false, retries = 3, config, provider } = options;
|
|
162
|
+
const deleted = [];
|
|
163
|
+
const failed = [];
|
|
164
|
+
const sorted = [...resources].sort((a, b) => {
|
|
165
|
+
const aCreatedAt = "createdAt" in a ? a.createdAt : void 0;
|
|
166
|
+
const bCreatedAt = "createdAt" in b ? b.createdAt : void 0;
|
|
167
|
+
const timeA = aCreatedAt ? new Date(aCreatedAt).getTime() : 0;
|
|
168
|
+
const timeB = bCreatedAt ? new Date(bCreatedAt).getTime() : 0;
|
|
169
|
+
return timeB - timeA;
|
|
170
|
+
});
|
|
171
|
+
const deleteResource = async (resource) => {
|
|
172
|
+
const resourceLabel = `${resource.type}:${resource.id}`;
|
|
173
|
+
const isDeleted = "deleted" in resource ? resource.deleted : false;
|
|
174
|
+
if (isDeleted) {
|
|
175
|
+
deleted.push(`${resourceLabel} (already deleted)`);
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
const handler = resolveHandler(handlers, typeMappings, resource.type);
|
|
179
|
+
if (!handler) {
|
|
180
|
+
failed.push(`${resourceLabel} (no handler for type "${resource.type}")`);
|
|
181
|
+
console.warn(`No cleanup handler for resource type: ${resource.type}`);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
for (let attempt = 1; attempt <= retries; attempt++) {
|
|
185
|
+
try {
|
|
186
|
+
await handler(resource);
|
|
187
|
+
deleted.push(resourceLabel);
|
|
188
|
+
return true;
|
|
189
|
+
} catch (error) {
|
|
190
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
191
|
+
if (attempt === retries) {
|
|
192
|
+
failed.push(`${resourceLabel} (${errorMessage})`);
|
|
193
|
+
console.warn(
|
|
194
|
+
`Failed to delete ${resourceLabel} after ${retries} attempts:`,
|
|
195
|
+
errorMessage
|
|
196
|
+
);
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
const delay = 100 * Math.pow(2, attempt - 1);
|
|
200
|
+
console.debug(
|
|
201
|
+
`Cleanup attempt ${attempt}/${retries} failed for ${resourceLabel}, retrying in ${delay}ms...`
|
|
202
|
+
);
|
|
203
|
+
await sleep(delay);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return false;
|
|
207
|
+
};
|
|
208
|
+
if (parallel) {
|
|
209
|
+
const results = await Promise.allSettled(sorted.map(deleteResource));
|
|
210
|
+
results.forEach((result2, index) => {
|
|
211
|
+
if (result2.status === "rejected") {
|
|
212
|
+
const resource = sorted[index];
|
|
213
|
+
console.error(
|
|
214
|
+
`Unexpected error cleaning up ${resource.type}:${resource.id}:`,
|
|
215
|
+
result2.reason
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
} else {
|
|
220
|
+
for (const resource of sorted) {
|
|
221
|
+
await deleteResource(resource);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (config?.scanUntracked && provider?.cleanupUntracked) {
|
|
225
|
+
console.log("\n[Cleanup] Scanning for untracked resources...");
|
|
226
|
+
try {
|
|
227
|
+
const untrackedResult = await provider.cleanupUntracked({
|
|
228
|
+
testStartTime: options.testStartTime ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
229
|
+
userId: options.userId,
|
|
230
|
+
sessionId: options.sessionId
|
|
231
|
+
});
|
|
232
|
+
if (untrackedResult.deleted.length > 0) {
|
|
233
|
+
console.log(`[Cleanup] Cleaned up ${untrackedResult.deleted.length} untracked resources`);
|
|
234
|
+
deleted.push(...untrackedResult.deleted);
|
|
235
|
+
}
|
|
236
|
+
if (untrackedResult.failed.length > 0) {
|
|
237
|
+
console.log(`[Cleanup] Failed to clean up ${untrackedResult.failed.length} untracked resources`);
|
|
238
|
+
failed.push(...untrackedResult.failed);
|
|
239
|
+
}
|
|
240
|
+
} catch (error) {
|
|
241
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
242
|
+
console.warn(`[Cleanup] Untracked resource scan failed: ${errorMessage}`);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
const result = {
|
|
246
|
+
success: failed.length === 0,
|
|
247
|
+
deleted,
|
|
248
|
+
failed
|
|
249
|
+
};
|
|
250
|
+
if (failed.length > 0 && options.sessionId && options.providerConfig) {
|
|
251
|
+
try {
|
|
252
|
+
const failedResources = sorted.filter((resource) => {
|
|
253
|
+
const resourceLabel = `${resource.type}:${resource.id}`;
|
|
254
|
+
return failed.some((f) => f.startsWith(resourceLabel));
|
|
255
|
+
});
|
|
256
|
+
await saveFailedCleanup(
|
|
257
|
+
{
|
|
258
|
+
sessionId: options.sessionId,
|
|
259
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
260
|
+
resources: failedResources,
|
|
261
|
+
providerConfig: options.providerConfig,
|
|
262
|
+
errors: failed
|
|
263
|
+
},
|
|
264
|
+
options.cwd
|
|
265
|
+
);
|
|
266
|
+
} catch (error) {
|
|
267
|
+
console.warn("Failed to save cleanup persistence file:", error);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return result;
|
|
271
|
+
}
|
|
272
|
+
function createCleanupExecutor(handlers, typeMappings, options = {}) {
|
|
273
|
+
return {
|
|
274
|
+
/**
|
|
275
|
+
* Execute cleanup for resources
|
|
276
|
+
*/
|
|
277
|
+
cleanup: (resources) => executeCleanup(resources, handlers, typeMappings, options),
|
|
278
|
+
/**
|
|
279
|
+
* Execute cleanup for a single resource
|
|
280
|
+
*/
|
|
281
|
+
cleanupOne: async (resource) => {
|
|
282
|
+
const result = await executeCleanup([resource], handlers, typeMappings, options);
|
|
283
|
+
return result.success;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/core/cleanup/index.ts
|
|
289
|
+
function defineCleanupHandlers(handlers) {
|
|
290
|
+
return handlers;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
exports.createCleanupExecutor = createCleanupExecutor;
|
|
294
|
+
exports.defineCleanupHandlers = defineCleanupHandlers;
|
|
295
|
+
exports.executeCleanup = executeCleanup;
|
|
296
|
+
exports.loadCleanupHandlers = loadCleanupHandlers;
|
|
297
|
+
exports.loadFailedCleanups = loadFailedCleanups;
|
|
298
|
+
exports.removeFailedCleanup = removeFailedCleanup;
|
|
299
|
+
exports.resolveHandler = resolveHandler;
|
|
300
|
+
exports.saveFailedCleanup = saveFailedCleanup;
|
|
301
|
+
//# sourceMappingURL=chunk-ARJYJVRM.cjs.map
|
|
302
|
+
//# sourceMappingURL=chunk-ARJYJVRM.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/cleanup/loader.ts","../src/core/cleanup/persistence.ts","../src/core/cleanup/executor.ts","../src/core/cleanup/index.ts"],"names":["providerFactories","getDefaultTypeMappings","path","glob","fs","result"],"mappings":"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","file":"chunk-ARJYJVRM.cjs","sourcesContent":["import { glob } from 'glob';\nimport path from 'path';\nimport fs from 'fs';\nimport type { CleanupHandler, CleanupConfig, CleanupProvider } from './types.js';\nimport { providerFactories, getDefaultTypeMappings } from '../../providers/index.js';\n\n/**\n * Load all cleanup handlers based on configuration\n *\n * Loading order (later overrides earlier):\n * 1. Built-in provider methods\n * 2. intellitester.cleanup.ts at project root\n * 3. Discovery paths (default: tests/cleanup/**\\/*.ts)\n * 4. Explicit handler files from config\n *\n * @param config - Cleanup configuration object\n * @param cwd - Current working directory (defaults to process.cwd())\n *\n * @example\n * ```typescript\n * // Basic usage with a provider\n * const { handlers, typeMappings } = await loadCleanupHandlers({\n * provider: 'sqlite',\n * sqlite: { database: './test.db' },\n * types: {\n * user: 'sqlite.deleteUser',\n * team: 'sqlite.deleteRow'\n * }\n * });\n *\n * // With custom discovery paths\n * const { handlers, typeMappings } = await loadCleanupHandlers({\n * provider: 'postgres',\n * postgres: { connectionString: 'postgresql://...' },\n * discover: {\n * enabled: true,\n * paths: ['./tests/cleanup', './e2e/cleanup'],\n * pattern: '**\\/*.cleanup.ts'\n * },\n * handlers: ['./custom-cleanup.ts']\n * });\n * ```\n *\n * @returns Object containing loaded handlers map and type mappings\n */\nexport async function loadCleanupHandlers(\n config: CleanupConfig,\n cwd: string = process.cwd()\n): Promise<{\n handlers: Map<string, CleanupHandler>;\n typeMappings: Record<string, string>;\n provider?: CleanupProvider;\n}> {\n const handlers = new Map<string, CleanupHandler>();\n let typeMappings: Record<string, string> = {};\n let provider: CleanupProvider | undefined;\n\n // 1. Load built-in provider methods\n if (config.provider) {\n const providerConfig = config[config.provider] as Record<string, unknown> | undefined;\n if (!providerConfig) {\n throw new Error(`Provider \"${config.provider}\" specified but no configuration found`);\n }\n\n const factory = providerFactories[config.provider];\n if (!factory) {\n throw new Error(`Unknown provider: ${config.provider}. Available: ${Object.keys(providerFactories).join(', ')}`);\n }\n\n provider = factory(providerConfig);\n await provider.configure(providerConfig);\n\n // Register provider methods\n for (const [methodName, handler] of Object.entries(provider.methods)) {\n handlers.set(`${provider.name}.${methodName}`, handler);\n }\n\n // Get default type mappings for this provider\n typeMappings = { ...getDefaultTypeMappings(config.provider) };\n }\n\n // 2. Auto-discover intellitester.cleanup.ts at root\n const rootCleanupPath = path.join(cwd, 'intellitester.cleanup.ts');\n const rootHandlers = await tryLoadHandlerFile(rootCleanupPath);\n if (rootHandlers) {\n mergeHandlers(handlers, rootHandlers);\n }\n\n // 3. Auto-discover from discovery paths\n if (config.discover?.enabled !== false) {\n const discoveryPaths = config.discover?.paths ?? ['./tests/cleanup'];\n const pattern = config.discover?.pattern ?? '**/*.ts';\n\n for (const basePath of discoveryPaths) {\n const absoluteBase = path.isAbsolute(basePath) ? basePath : path.join(cwd, basePath);\n\n try {\n const files = await glob(pattern, {\n cwd: absoluteBase,\n absolute: true,\n ignore: ['**/*.d.ts', '**/node_modules/**']\n });\n\n for (const file of files) {\n const fileHandlers = await tryLoadHandlerFile(file);\n if (fileHandlers) {\n mergeHandlers(handlers, fileHandlers);\n }\n }\n } catch {\n // Directory doesn't exist or no matches - that's fine\n }\n }\n }\n\n // 4. Load explicit handler files\n for (const handlerPath of config.handlers ?? []) {\n const absolutePath = path.isAbsolute(handlerPath)\n ? handlerPath\n : path.join(cwd, handlerPath);\n\n const fileHandlers = await tryLoadHandlerFile(absolutePath);\n if (fileHandlers) {\n mergeHandlers(handlers, fileHandlers);\n } else {\n console.warn(`Warning: Could not load cleanup handler file: ${handlerPath}`);\n }\n }\n\n // Merge config.types over default mappings\n if (config.types) {\n typeMappings = { ...typeMappings, ...config.types };\n }\n\n return { handlers, typeMappings, provider };\n}\n\n/**\n * Try to load a handler file, returning null if it doesn't exist or fails\n */\nasync function tryLoadHandlerFile(filePath: string): Promise<Record<string, CleanupHandler> | null> {\n try {\n // Check if file exists\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n // For TypeScript files, we need to handle them appropriately\n // In a built environment, look for the .js equivalent\n let loadPath = filePath;\n if (filePath.endsWith('.ts')) {\n // In production, TypeScript files will be compiled to .js\n // Try to load the compiled .js version from dist if running compiled\n const jsPath = filePath.replace(/\\.ts$/, '.js');\n if (fs.existsSync(jsPath)) {\n loadPath = jsPath;\n } else {\n // Running in development with tsx/ts-node\n // Try direct import which works with these tools\n loadPath = filePath;\n }\n }\n\n // Use dynamic import with cache busting for fresh load\n const module = await import(`${loadPath}?t=${Date.now()}`);\n\n // Handle default export or named exports\n if (module.default && typeof module.default === 'object') {\n return module.default as Record<string, CleanupHandler>;\n }\n\n // Filter to only include function exports\n const handlers: Record<string, CleanupHandler> = {};\n for (const [key, value] of Object.entries(module)) {\n if (typeof value === 'function' && key !== 'default') {\n handlers[key] = value as CleanupHandler;\n }\n }\n\n return Object.keys(handlers).length > 0 ? handlers : null;\n } catch {\n // File doesn't exist or failed to load\n return null;\n }\n}\n\n/**\n * Merge handlers from a file into the main handlers map\n */\nfunction mergeHandlers(\n target: Map<string, CleanupHandler>,\n source: Record<string, CleanupHandler>\n): void {\n for (const [key, handler] of Object.entries(source)) {\n target.set(key, handler);\n }\n}\n\n/**\n * Get a handler by key, checking both direct keys and provider.method format\n */\nexport function resolveHandler(\n handlers: Map<string, CleanupHandler>,\n typeMappings: Record<string, string>,\n resourceType: string\n): CleanupHandler | null {\n // First check type mappings\n const mappedKey = typeMappings[resourceType];\n if (mappedKey && handlers.has(mappedKey)) {\n return handlers.get(mappedKey)!;\n }\n\n // Then check for direct handler\n if (handlers.has(resourceType)) {\n return handlers.get(resourceType)!;\n }\n\n return null;\n}\n","import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport type { TrackedResource } from '../../integration/index.js';\n\n// Types\nexport interface FailedCleanup {\n sessionId: string;\n timestamp: string;\n resources: TrackedResource[];\n // Generic provider config (no secrets!)\n providerConfig: {\n provider: string; // 'appwrite' | 'postgres' | 'mysql' | 'sqlite'\n [key: string]: unknown; // endpoint, projectId, database, etc. - NO passwords/keys\n };\n errors: string[];\n}\n\nconst CLEANUP_DIR = '.intellitester/cleanup/failed';\n\n// Save failed cleanup to disk\nexport async function saveFailedCleanup(\n cleanup: FailedCleanup,\n cwd: string = process.cwd()\n): Promise<void> {\n const dir = path.join(cwd, CLEANUP_DIR);\n await fs.mkdir(dir, { recursive: true });\n const filePath = path.join(dir, `${cleanup.sessionId}.json`);\n await fs.writeFile(filePath, JSON.stringify(cleanup, null, 2), 'utf8');\n console.log(`Saved failed cleanup to ${filePath}`);\n}\n\n// Load all failed cleanups from disk\nexport async function loadFailedCleanups(\n cwd: string = process.cwd()\n): Promise<FailedCleanup[]> {\n const dir = path.join(cwd, CLEANUP_DIR);\n try {\n const files = await fs.readdir(dir);\n const cleanups: FailedCleanup[] = [];\n for (const file of files) {\n if (file.endsWith('.json')) {\n const content = await fs.readFile(path.join(dir, file), 'utf8');\n cleanups.push(JSON.parse(content));\n }\n }\n return cleanups;\n } catch (error) {\n // Directory doesn't exist = no failed cleanups\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') {\n return [];\n }\n throw error;\n }\n}\n\n// Remove a failed cleanup file after successful retry\nexport async function removeFailedCleanup(\n sessionId: string,\n cwd: string = process.cwd()\n): Promise<void> {\n const filePath = path.join(cwd, CLEANUP_DIR, `${sessionId}.json`);\n try {\n await fs.unlink(filePath);\n console.log(`Removed failed cleanup file: ${sessionId}.json`);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n}\n","import type { TrackedResource } from '../../integration/index.js';\nimport type { CleanupHandler, CleanupResult, ExecutorOptions, CleanupConfig, CleanupProvider } from './types.js';\nimport { resolveHandler } from './loader.js';\nimport { saveFailedCleanup } from './persistence.js';\n\nconst sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));\n\n/**\n * Extended options for cleanup execution including provider and config\n */\nexport interface ExtendedCleanupOptions extends ExecutorOptions {\n config?: CleanupConfig;\n provider?: CleanupProvider;\n}\n\n/**\n * Execute cleanup for all tracked resources\n *\n * @param resources - Array of tracked resources to clean up\n * @param handlers - Map of handler names to cleanup functions\n * @param typeMappings - Map of resource types to handler names\n * @param options - Executor options (parallel, retries, config, provider)\n */\nexport async function executeCleanup(\n resources: TrackedResource[],\n handlers: Map<string, CleanupHandler>,\n typeMappings: Record<string, string>,\n options: ExtendedCleanupOptions = {}\n): Promise<CleanupResult> {\n const { parallel = false, retries = 3, config, provider } = options;\n const deleted: string[] = [];\n const failed: string[] = [];\n\n // Sort by creation time (reverse) - delete newest first (LIFO)\n const sorted = [...resources].sort((a, b) => {\n // Handle optional createdAt field (present at runtime from tracking server)\n const aCreatedAt = 'createdAt' in a ? (a.createdAt as string | undefined) : undefined;\n const bCreatedAt = 'createdAt' in b ? (b.createdAt as string | undefined) : undefined;\n\n const timeA = aCreatedAt ? new Date(aCreatedAt).getTime() : 0;\n const timeB = bCreatedAt ? new Date(bCreatedAt).getTime() : 0;\n\n return timeB - timeA;\n });\n\n const deleteResource = async (resource: TrackedResource): Promise<boolean> => {\n const resourceLabel = `${resource.type}:${resource.id}`;\n\n // Skip already deleted resources (deleted field may be set dynamically)\n const isDeleted = 'deleted' in resource ? (resource.deleted as boolean | undefined) : false;\n if (isDeleted) {\n deleted.push(`${resourceLabel} (already deleted)`);\n return true;\n }\n\n // Resolve the handler\n const handler = resolveHandler(handlers, typeMappings, resource.type);\n\n if (!handler) {\n failed.push(`${resourceLabel} (no handler for type \"${resource.type}\")`);\n console.warn(`No cleanup handler for resource type: ${resource.type}`);\n return false;\n }\n\n // Retry with exponential backoff\n for (let attempt = 1; attempt <= retries; attempt++) {\n try {\n await handler(resource);\n deleted.push(resourceLabel);\n return true;\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n\n if (attempt === retries) {\n failed.push(`${resourceLabel} (${errorMessage})`);\n console.warn(\n `Failed to delete ${resourceLabel} after ${retries} attempts:`,\n errorMessage\n );\n return false;\n }\n\n // Exponential backoff: 100ms, 200ms, 400ms, 800ms...\n const delay = 100 * Math.pow(2, attempt - 1);\n console.debug(\n `Cleanup attempt ${attempt}/${retries} failed for ${resourceLabel}, ` +\n `retrying in ${delay}ms...`\n );\n await sleep(delay);\n }\n }\n\n return false;\n };\n\n if (parallel) {\n // Parallel execution\n const results = await Promise.allSettled(sorted.map(deleteResource));\n\n // Log any unexpected rejections (shouldn't happen since deleteResource handles errors)\n results.forEach((result, index) => {\n if (result.status === 'rejected') {\n const resource = sorted[index];\n console.error(\n `Unexpected error cleaning up ${resource.type}:${resource.id}:`,\n result.reason\n );\n }\n });\n } else {\n // Sequential execution\n for (const resource of sorted) {\n await deleteResource(resource);\n }\n }\n\n // After tracked cleanup, scan for untracked resources if configured\n if (config?.scanUntracked && provider?.cleanupUntracked) {\n console.log('\\n[Cleanup] Scanning for untracked resources...');\n\n try {\n const untrackedResult = await provider.cleanupUntracked({\n testStartTime: options.testStartTime ?? new Date().toISOString(),\n userId: options.userId,\n sessionId: options.sessionId,\n });\n\n if (untrackedResult.deleted.length > 0) {\n console.log(`[Cleanup] Cleaned up ${untrackedResult.deleted.length} untracked resources`);\n deleted.push(...untrackedResult.deleted);\n }\n\n if (untrackedResult.failed.length > 0) {\n console.log(`[Cleanup] Failed to clean up ${untrackedResult.failed.length} untracked resources`);\n failed.push(...untrackedResult.failed);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n console.warn(`[Cleanup] Untracked resource scan failed: ${errorMessage}`);\n }\n }\n\n const result: CleanupResult = {\n success: failed.length === 0,\n deleted,\n failed,\n };\n\n // Save failed cleanups for retry if there were failures\n if (failed.length > 0 && options.sessionId && options.providerConfig) {\n try {\n // Extract the resources that failed\n const failedResources = sorted.filter((resource) => {\n const resourceLabel = `${resource.type}:${resource.id}`;\n return failed.some((f) => f.startsWith(resourceLabel));\n });\n\n await saveFailedCleanup(\n {\n sessionId: options.sessionId,\n timestamp: new Date().toISOString(),\n resources: failedResources,\n providerConfig: options.providerConfig,\n errors: failed,\n },\n options.cwd\n );\n } catch (error) {\n // Don't fail the cleanup if we can't save the failed cleanup file\n console.warn('Failed to save cleanup persistence file:', error);\n }\n }\n\n return result;\n}\n\n/**\n * Create a cleanup executor with pre-configured handlers and options\n */\nexport function createCleanupExecutor(\n handlers: Map<string, CleanupHandler>,\n typeMappings: Record<string, string>,\n options: ExecutorOptions = {}\n) {\n return {\n /**\n * Execute cleanup for resources\n */\n cleanup: (resources: TrackedResource[]) =>\n executeCleanup(resources, handlers, typeMappings, options),\n\n /**\n * Execute cleanup for a single resource\n */\n cleanupOne: async (resource: TrackedResource): Promise<boolean> => {\n const result = await executeCleanup([resource], handlers, typeMappings, options);\n return result.success;\n },\n };\n}\n","export * from './types.js';\nimport type { CleanupHandler } from './types.js';\n\nexport { executeCleanup, createCleanupExecutor, type ExtendedCleanupOptions } from './executor.js';\nexport { loadCleanupHandlers, resolveHandler } from './loader.js';\nexport { saveFailedCleanup, loadFailedCleanups, removeFailedCleanup, type FailedCleanup } from './persistence.js';\n\n/**\n * Helper for defining cleanup handlers in user files.\n * Provides type safety for custom cleanup handler definitions.\n *\n * @example\n * // In cleanup.ts\n * import { defineCleanupHandlers } from 'intellitester/cleanup';\n *\n * export default defineCleanupHandlers({\n * async deleteUser(resource) {\n * // cleanup logic\n * },\n * async deleteTeam(resource) {\n * // cleanup logic\n * }\n * });\n */\nexport function defineCleanupHandlers<T extends Record<string, CleanupHandler>>(handlers: T): T {\n return handlers;\n}\n"]}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// src/providers/mysql/index.ts
|
|
2
|
+
function createMysqlProvider(config) {
|
|
3
|
+
let connection = null;
|
|
4
|
+
const methods = {
|
|
5
|
+
deleteRow: async (resource) => {
|
|
6
|
+
if (!connection) {
|
|
7
|
+
throw new Error("MySQL connection not initialized. Call configure() first.");
|
|
8
|
+
}
|
|
9
|
+
const table = resource.table;
|
|
10
|
+
const database = resource.database || config.database;
|
|
11
|
+
if (!table) {
|
|
12
|
+
throw new Error(`Missing table name for row ${resource.id}`);
|
|
13
|
+
}
|
|
14
|
+
await connection.execute(
|
|
15
|
+
`DELETE FROM \`${database}\`.\`${table}\` WHERE id = ?`,
|
|
16
|
+
[resource.id]
|
|
17
|
+
);
|
|
18
|
+
},
|
|
19
|
+
deleteUser: async (resource) => {
|
|
20
|
+
if (!connection) {
|
|
21
|
+
throw new Error("MySQL connection not initialized. Call configure() first.");
|
|
22
|
+
}
|
|
23
|
+
const table = resource.table || "users";
|
|
24
|
+
const database = resource.database || config.database;
|
|
25
|
+
await connection.execute(
|
|
26
|
+
`DELETE FROM \`${database}\`.\`${table}\` WHERE id = ?`,
|
|
27
|
+
[resource.id]
|
|
28
|
+
);
|
|
29
|
+
},
|
|
30
|
+
customDelete: async (resource) => {
|
|
31
|
+
if (!connection) {
|
|
32
|
+
throw new Error("MySQL connection not initialized. Call configure() first.");
|
|
33
|
+
}
|
|
34
|
+
const query = resource.query;
|
|
35
|
+
const params = resource.params || [resource.id];
|
|
36
|
+
if (!query) {
|
|
37
|
+
throw new Error(`Missing query for custom delete of resource ${resource.id}`);
|
|
38
|
+
}
|
|
39
|
+
await connection.execute(query, params);
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
async function cleanupUntracked(options) {
|
|
43
|
+
if (!connection) {
|
|
44
|
+
throw new Error("MySQL connection not initialized. Call configure() first.");
|
|
45
|
+
}
|
|
46
|
+
const { testStartTime, userId } = options;
|
|
47
|
+
const deleted = [];
|
|
48
|
+
const failed = [];
|
|
49
|
+
let scanned = 0;
|
|
50
|
+
const [tablesRows] = await connection.execute(`
|
|
51
|
+
SELECT TABLE_NAME as table_name
|
|
52
|
+
FROM INFORMATION_SCHEMA.TABLES
|
|
53
|
+
WHERE TABLE_SCHEMA = ?
|
|
54
|
+
AND TABLE_TYPE = 'BASE TABLE'
|
|
55
|
+
AND TABLE_NAME NOT LIKE '_intellitester%'
|
|
56
|
+
`, [config.database]);
|
|
57
|
+
for (const row of tablesRows) {
|
|
58
|
+
const tableName = row.table_name;
|
|
59
|
+
scanned++;
|
|
60
|
+
const [columnsRows] = await connection.execute(`
|
|
61
|
+
SELECT COLUMN_NAME as column_name
|
|
62
|
+
FROM INFORMATION_SCHEMA.COLUMNS
|
|
63
|
+
WHERE TABLE_SCHEMA = ?
|
|
64
|
+
AND TABLE_NAME = ?
|
|
65
|
+
`, [config.database, tableName]);
|
|
66
|
+
const columns = columnsRows.map((r) => r.column_name);
|
|
67
|
+
const hasCreatedAt = columns.some((c) => ["created_at", "createdat", "created"].includes(c.toLowerCase()));
|
|
68
|
+
const userIdColumn = columns.find((c) => ["user_id", "userid", "owner_id", "author_id"].includes(c.toLowerCase()));
|
|
69
|
+
if (!hasCreatedAt) continue;
|
|
70
|
+
const createdAtCol = columns.find((c) => ["created_at", "createdat", "created"].includes(c.toLowerCase()));
|
|
71
|
+
if (!createdAtCol) continue;
|
|
72
|
+
let selectQuery = `SELECT id FROM \`${tableName}\` WHERE `;
|
|
73
|
+
const conditions = [];
|
|
74
|
+
const params = [];
|
|
75
|
+
conditions.push(`\`${createdAtCol}\` >= ?`);
|
|
76
|
+
params.push(testStartTime);
|
|
77
|
+
if (userId && userIdColumn) {
|
|
78
|
+
conditions.push(`\`${userIdColumn}\` = ?`);
|
|
79
|
+
params.push(userId);
|
|
80
|
+
}
|
|
81
|
+
selectQuery += conditions.join(" AND ");
|
|
82
|
+
try {
|
|
83
|
+
const [rowsToDelete] = await connection.execute(selectQuery, params);
|
|
84
|
+
const idsToDelete = rowsToDelete.map((r) => r.id);
|
|
85
|
+
if (idsToDelete.length === 0) continue;
|
|
86
|
+
const deleteQuery = `DELETE FROM \`${tableName}\` WHERE ` + conditions.join(" AND ");
|
|
87
|
+
await connection.execute(deleteQuery, params);
|
|
88
|
+
for (const id of idsToDelete) {
|
|
89
|
+
deleted.push(`${tableName}:${id}`);
|
|
90
|
+
}
|
|
91
|
+
} catch {
|
|
92
|
+
failed.push(`${tableName}:error`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
success: failed.length === 0,
|
|
97
|
+
scanned,
|
|
98
|
+
deleted,
|
|
99
|
+
failed
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
name: "mysql",
|
|
104
|
+
async configure() {
|
|
105
|
+
try {
|
|
106
|
+
const mysql = await import('mysql2/promise');
|
|
107
|
+
const createConnection = mysql.createConnection || mysql.default?.createConnection;
|
|
108
|
+
connection = await createConnection({
|
|
109
|
+
host: config.host,
|
|
110
|
+
port: config.port || 3306,
|
|
111
|
+
user: config.user,
|
|
112
|
+
password: config.password,
|
|
113
|
+
database: config.database
|
|
114
|
+
});
|
|
115
|
+
} catch {
|
|
116
|
+
throw new Error(
|
|
117
|
+
'Failed to initialize MySQL connection. Make sure the "mysql2" package is installed: npm install mysql2'
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
methods,
|
|
122
|
+
cleanupUntracked
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
var mysqlTypeMappings = {
|
|
126
|
+
row: "mysql.deleteRow",
|
|
127
|
+
user: "mysql.deleteUser",
|
|
128
|
+
custom: "mysql.customDelete"
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
export { createMysqlProvider, mysqlTypeMappings };
|
|
132
|
+
//# sourceMappingURL=chunk-CN6HSJJX.js.map
|
|
133
|
+
//# sourceMappingURL=chunk-CN6HSJJX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/mysql/index.ts"],"names":[],"mappings":"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","file":"chunk-CN6HSJJX.js","sourcesContent":["import type { CleanupProvider, CleanupHandler, CleanupUntrackedOptions, CleanupUntrackedResult } from '../../core/cleanup/types.js';\nimport type { TrackedResource } from '../../integration/index.js';\n\ninterface MysqlConfig {\n host: string;\n port?: number;\n user: string;\n password: string;\n database: string;\n}\n\nexport function createMysqlProvider(config: MysqlConfig): CleanupProvider {\n // Connection will be lazily initialized in configure()\n let connection: any = null;\n\n const methods: Record<string, CleanupHandler> = {\n deleteRow: async (resource: TrackedResource) => {\n if (!connection) {\n throw new Error('MySQL connection not initialized. Call configure() first.');\n }\n\n const table = resource.table as string;\n const database = (resource.database as string) || config.database;\n\n if (!table) {\n throw new Error(`Missing table name for row ${resource.id}`);\n }\n\n // Use parameterized query to prevent SQL injection\n await connection.execute(\n `DELETE FROM \\`${database}\\`.\\`${table}\\` WHERE id = ?`,\n [resource.id]\n );\n },\n\n deleteUser: async (resource: TrackedResource) => {\n if (!connection) {\n throw new Error('MySQL connection not initialized. Call configure() first.');\n }\n\n const table = (resource.table as string) || 'users';\n const database = (resource.database as string) || config.database;\n\n await connection.execute(\n `DELETE FROM \\`${database}\\`.\\`${table}\\` WHERE id = ?`,\n [resource.id]\n );\n },\n\n customDelete: async (resource: TrackedResource) => {\n if (!connection) {\n throw new Error('MySQL connection not initialized. Call configure() first.');\n }\n\n // Allow custom SQL queries via the query property\n const query = resource.query as string;\n const params = (resource.params as any[]) || [resource.id];\n\n if (!query) {\n throw new Error(`Missing query for custom delete of resource ${resource.id}`);\n }\n\n await connection.execute(query, params);\n },\n };\n\n async function cleanupUntracked(options: CleanupUntrackedOptions): Promise<CleanupUntrackedResult> {\n if (!connection) {\n throw new Error('MySQL connection not initialized. Call configure() first.');\n }\n\n const { testStartTime, userId } = options;\n const deleted: string[] = [];\n const failed: string[] = [];\n let scanned = 0;\n\n // 1. Get all tables in the database\n const [tablesRows] = await connection.execute(`\n SELECT TABLE_NAME as table_name\n FROM INFORMATION_SCHEMA.TABLES\n WHERE TABLE_SCHEMA = ?\n AND TABLE_TYPE = 'BASE TABLE'\n AND TABLE_NAME NOT LIKE '_intellitester%'\n `, [config.database]);\n\n for (const row of tablesRows as { table_name: string }[]) {\n const tableName = row.table_name;\n scanned++;\n\n // 2. Check if table has created_at and user_id columns\n const [columnsRows] = await connection.execute(`\n SELECT COLUMN_NAME as column_name\n FROM INFORMATION_SCHEMA.COLUMNS\n WHERE TABLE_SCHEMA = ?\n AND TABLE_NAME = ?\n `, [config.database, tableName]);\n\n const columns: string[] = (columnsRows as { column_name: string }[]).map(r => r.column_name);\n const hasCreatedAt = columns.some(c => ['created_at', 'createdat', 'created'].includes(c.toLowerCase()));\n const userIdColumn = columns.find(c => ['user_id', 'userid', 'owner_id', 'author_id'].includes(c.toLowerCase()));\n\n if (!hasCreatedAt) continue;\n\n // 3. Find the created_at column name\n const createdAtCol = columns.find(c => ['created_at', 'createdat', 'created'].includes(c.toLowerCase()));\n if (!createdAtCol) continue;\n\n // 4. First, select rows to be deleted (MySQL doesn't have RETURNING)\n let selectQuery = `SELECT id FROM \\`${tableName}\\` WHERE `;\n const conditions: string[] = [];\n const params: (string | undefined)[] = [];\n\n // Add created_at condition\n conditions.push(`\\`${createdAtCol}\\` >= ?`);\n params.push(testStartTime);\n\n // Add user_id condition if available\n if (userId && userIdColumn) {\n conditions.push(`\\`${userIdColumn}\\` = ?`);\n params.push(userId);\n }\n\n selectQuery += conditions.join(' AND ');\n\n try {\n // Get IDs of rows to be deleted\n const [rowsToDelete] = await connection.execute(selectQuery, params);\n const idsToDelete = (rowsToDelete as { id: string | number }[]).map(r => r.id);\n\n if (idsToDelete.length === 0) continue;\n\n // Delete the rows\n const deleteQuery = `DELETE FROM \\`${tableName}\\` WHERE ` + conditions.join(' AND ');\n await connection.execute(deleteQuery, params);\n\n // Record deleted IDs\n for (const id of idsToDelete) {\n deleted.push(`${tableName}:${id}`);\n }\n } catch {\n failed.push(`${tableName}:error`);\n }\n }\n\n return {\n success: failed.length === 0,\n scanned,\n deleted,\n failed,\n };\n }\n\n return {\n name: 'mysql',\n async configure() {\n try {\n // Dynamic import since mysql2 is an optional dependency\n // @ts-expect-error - mysql2 is an optional peer dependency\n const mysql = await import('mysql2/promise');\n const createConnection = mysql.createConnection || mysql.default?.createConnection;\n connection = await createConnection({\n host: config.host,\n port: config.port || 3306,\n user: config.user,\n password: config.password,\n database: config.database,\n });\n } catch {\n throw new Error(\n 'Failed to initialize MySQL connection. Make sure the \"mysql2\" package is installed: npm install mysql2'\n );\n }\n },\n methods,\n cleanupUntracked,\n };\n}\n\n// Default type mappings for MySQL resources\nexport const mysqlTypeMappings: Record<string, string> = {\n row: 'mysql.deleteRow',\n user: 'mysql.deleteUser',\n custom: 'mysql.customDelete',\n};\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { createSqliteProvider, sqliteTypeMappings } from './chunk-6PYKWWH5.js';
|
|
2
|
+
import { createAppwriteProvider, appwriteTypeMappings } from './chunk-4B54JUOP.js';
|
|
3
|
+
import { createPostgresProvider, postgresTypeMappings } from './chunk-SAVY6D3X.js';
|
|
4
|
+
import { createMysqlProvider, mysqlTypeMappings } from './chunk-CN6HSJJX.js';
|
|
5
|
+
|
|
6
|
+
// src/providers/index.ts
|
|
7
|
+
var providerFactories = {
|
|
8
|
+
appwrite: (config) => createAppwriteProvider(config),
|
|
9
|
+
postgres: (config) => createPostgresProvider(config),
|
|
10
|
+
mysql: (config) => createMysqlProvider(config),
|
|
11
|
+
sqlite: (config) => createSqliteProvider(config)
|
|
12
|
+
};
|
|
13
|
+
var typeMappingsRegistry = {
|
|
14
|
+
appwrite: appwriteTypeMappings,
|
|
15
|
+
postgres: postgresTypeMappings,
|
|
16
|
+
mysql: mysqlTypeMappings,
|
|
17
|
+
sqlite: sqliteTypeMappings
|
|
18
|
+
};
|
|
19
|
+
function getDefaultTypeMappings(provider) {
|
|
20
|
+
return typeMappingsRegistry[provider] ?? {};
|
|
21
|
+
}
|
|
22
|
+
function isProviderAvailable(provider) {
|
|
23
|
+
return provider in providerFactories;
|
|
24
|
+
}
|
|
25
|
+
function listAvailableProviders() {
|
|
26
|
+
return Object.keys(providerFactories);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export { getDefaultTypeMappings, isProviderAvailable, listAvailableProviders, providerFactories };
|
|
30
|
+
//# sourceMappingURL=chunk-DE5UFTTG.js.map
|
|
31
|
+
//# sourceMappingURL=chunk-DE5UFTTG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/providers/index.ts"],"names":[],"mappings":";;;;;;AAaO,IAAM,iBAAA,GAAsE;AAAA,EACjF,QAAA,EAAU,CAAC,MAAA,KAAW,sBAAA,CAAuB,MAAM,CAAA;AAAA,EACnD,QAAA,EAAU,CAAC,MAAA,KAAW,sBAAA,CAAuB,MAAM,CAAA;AAAA,EACnD,KAAA,EAAO,CAAC,MAAA,KAAW,mBAAA,CAAoB,MAAM,CAAA;AAAA,EAC7C,MAAA,EAAQ,CAAC,MAAA,KAAW,oBAAA,CAAqB,MAAM;AACjD;AAGA,IAAM,oBAAA,GAA+D;AAAA,EACnE,QAAA,EAAU,oBAAA;AAAA,EACV,QAAA,EAAU,oBAAA;AAAA,EACV,KAAA,EAAO,iBAAA;AAAA,EACP,MAAA,EAAQ;AACV,CAAA;AAGO,SAAS,uBAAuB,QAAA,EAA0C;AAC/E,EAAA,OAAO,oBAAA,CAAqB,QAAQ,CAAA,IAAK,EAAC;AAC5C;AAGO,SAAS,oBAAoB,QAAA,EAA2B;AAC7D,EAAA,OAAO,QAAA,IAAY,iBAAA;AACrB;AAGO,SAAS,sBAAA,GAAmC;AACjD,EAAA,OAAO,MAAA,CAAO,KAAK,iBAAiB,CAAA;AACtC","file":"chunk-DE5UFTTG.js","sourcesContent":["import { createAppwriteProvider, appwriteTypeMappings } from './appwrite/index.js';\nimport { createPostgresProvider, postgresTypeMappings } from './postgres/index.js';\nimport { createMysqlProvider, mysqlTypeMappings } from './mysql/index.js';\nimport { createSqliteProvider, sqliteTypeMappings } from './sqlite/index.js';\nimport type { CleanupProvider } from '../core/cleanup/types.js';\n\n// Re-export for convenience\nexport { createAppwriteProvider, appwriteTypeMappings } from './appwrite/index.js';\nexport { createPostgresProvider, postgresTypeMappings } from './postgres/index.js';\nexport { createMysqlProvider, mysqlTypeMappings } from './mysql/index.js';\nexport { createSqliteProvider, sqliteTypeMappings } from './sqlite/index.js';\n\n// Provider factory registry using static imports\nexport const providerFactories: Record<string, (config: any) => CleanupProvider> = {\n appwrite: (config) => createAppwriteProvider(config),\n postgres: (config) => createPostgresProvider(config),\n mysql: (config) => createMysqlProvider(config),\n sqlite: (config) => createSqliteProvider(config),\n};\n\n// Default type mappings for each provider\nconst typeMappingsRegistry: Record<string, Record<string, string>> = {\n appwrite: appwriteTypeMappings,\n postgres: postgresTypeMappings,\n mysql: mysqlTypeMappings,\n sqlite: sqliteTypeMappings,\n};\n\n// Get default type mappings for a provider\nexport function getDefaultTypeMappings(provider: string): Record<string, string> {\n return typeMappingsRegistry[provider] ?? {};\n}\n\n// Helper to check if a provider is available\nexport function isProviderAvailable(provider: string): boolean {\n return provider in providerFactories;\n}\n\n// Helper to list all available providers\nexport function listAvailableProviders(): string[] {\n return Object.keys(providerFactories);\n}\n"]}
|