Add full application: receipt scanning, auth, billing, and account deletion
Brings the working codebase (Next.js app, auth system, Stripe billing, Docker/deploy config, tests, docs) into version control on top of the placeholder initial commit, and adds account self-deletion (Danger Zone in Settings, password + typed-email confirmation, cascading DB cleanup, Stripe cancellation) per GDPR right-to-erasure. Excludes local build caches, node_modules, and internal agent scratch files; .gitignore hardened to keep those out going forward. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
98
tests/integration/tsconfig-paths-hooks.mjs
Normal file
98
tests/integration/tsconfig-paths-hooks.mjs
Normal file
@@ -0,0 +1,98 @@
|
||||
/**
|
||||
* ESM hooks for running integration tests without `tsx`.
|
||||
*
|
||||
* The sandbox blocks esbuild's pipe-based service-worker spawn (EPERM), so
|
||||
* this loader performs the TypeScript transform in-process with the locally
|
||||
* installed `typescript` package (ts.transpileModule — pure JS, no child
|
||||
* process), resolves the tsconfig `@/*` path alias, probes extensions for
|
||||
* extensionless relative imports, and falls back to raw package-subpath
|
||||
* files (`next/headers` etc.) that lack an exports map.
|
||||
*
|
||||
* Usage: node --import ./tests/integration/tsconfig-paths-loader.mjs tests/integration/<suite>.test.ts
|
||||
*/
|
||||
import { pathToFileURL, fileURLToPath } from "node:url";
|
||||
import { existsSync, readFileSync, statSync } from "node:fs";
|
||||
import { dirname, join, resolve as pathResolve } from "node:path";
|
||||
import ts from "typescript";
|
||||
|
||||
const ROOT = pathResolve(process.cwd());
|
||||
|
||||
const EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs"];
|
||||
|
||||
function isFile(p) {
|
||||
try {
|
||||
return statSync(p).isFile();
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** Resolve a specifier target, probing extensions and index files. */
|
||||
function probe(base) {
|
||||
if (isFile(base)) return base;
|
||||
for (const ext of EXTENSIONS) {
|
||||
if (isFile(base + ext)) return base + ext;
|
||||
}
|
||||
for (const ext of EXTENSIONS) {
|
||||
if (isFile(join(base, "index" + ext))) return join(base, "index" + ext);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Fallback for bare package subpaths (e.g. `next/headers`) that lack an exports map. */
|
||||
function probePackage(spec) {
|
||||
const base = pathResolve(ROOT, "node_modules", spec);
|
||||
for (const ext of [".js", ".mjs", ".cjs", ".json"]) {
|
||||
if (isFile(base + ext)) return base + ext;
|
||||
}
|
||||
if (isFile(base)) return base;
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function resolve(specifier, context, nextResolve) {
|
||||
if (specifier.startsWith("@/")) {
|
||||
const target = probe(pathResolve(ROOT, "src", specifier.slice(2)));
|
||||
if (target) return { url: pathToFileURL(target).href, shortCircuit: true };
|
||||
} else if (
|
||||
(specifier.startsWith("./") || specifier.startsWith("../")) &&
|
||||
context.parentURL
|
||||
) {
|
||||
const parent = fileURLToPath(context.parentURL);
|
||||
const target = probe(pathResolve(dirname(parent), specifier));
|
||||
if (target) return { url: pathToFileURL(target).href, shortCircuit: true };
|
||||
} else if (!specifier.startsWith("node:")) {
|
||||
try {
|
||||
return await nextResolve(specifier, context);
|
||||
} catch (error) {
|
||||
const target = probePackage(specifier);
|
||||
if (target) return { url: pathToFileURL(target).href, shortCircuit: true };
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
return nextResolve(specifier, context);
|
||||
}
|
||||
|
||||
export async function load(url, context, nextLoad) {
|
||||
if (url.startsWith("file:")) {
|
||||
const filePath = fileURLToPath(url);
|
||||
if (/\.(ts|tsx|mts|cts)$/.test(filePath)) {
|
||||
const source = readFileSync(filePath, "utf8");
|
||||
const out = ts.transpileModule(source, {
|
||||
fileName: filePath,
|
||||
reportDiagnostics: false,
|
||||
compilerOptions: {
|
||||
module: ts.ModuleKind.ESNext,
|
||||
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
||||
target: ts.ScriptTarget.ES2022,
|
||||
isolatedModules: true,
|
||||
esModuleInterop: true,
|
||||
allowJs: true,
|
||||
jsx: ts.JsxEmit.ReactJSX,
|
||||
verbatimModuleSyntax: false,
|
||||
},
|
||||
});
|
||||
return { format: "module", source: out.outputText, shortCircuit: true };
|
||||
}
|
||||
}
|
||||
return nextLoad(url, context);
|
||||
}
|
||||
Reference in New Issue
Block a user