mirror of
https://github.com/actualbudget/actual.git
synced 2026-11-09 10:45:04 -06:00
* [AI] Add getScheduleDates to the API Expand a schedule date (single date or RecurConfig) into the dates it falls on within a window, honouring frequency, interval, monthly patterns, end mode and the weekend move. Also type the return value of recurConfigToRSchedule, and fix the API vitest setup, where inlined UMD builds of @rschedule registered the date adapter and recurrence rules on a second @rschedule/core instance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01CfQCMhYExnAqVNC4gJjF8E * [AI] Build getScheduleDates on getNextDateAfter Walk the schedule with getNextDateAfter, the step the app uses to advance a schedule, instead of expanding the rschedule occurrences. A date that several occurrences move onto (a daily schedule's Saturday and Sunday both landing on Monday with skipWeekend) is now listed once, and count counts distinct dates. Add an API test that pins the first listed date to the next_date the app stores, so the two cannot drift. Drop the recurConfigToRSchedule typing change, which is no longer needed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HRp7PBTgAChF39Dv9iFLyc --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
108 lines
3.3 KiB
TypeScript
108 lines
3.3 KiB
TypeScript
import fs from 'fs';
|
|
import path from 'path';
|
|
|
|
import {
|
|
defaultDbPath,
|
|
migrationsDir,
|
|
} from '@actual-app/core/default-filesystem';
|
|
import { peggyLoader } from '@actual-app/vite-plugin-peggy';
|
|
import { visualizer } from 'rollup-plugin-visualizer';
|
|
import { defineConfig } from 'vite';
|
|
import { configDefaults } from 'vitest/config';
|
|
|
|
const distDir = path.resolve(__dirname, 'dist');
|
|
const typesDir = path.resolve(__dirname, '@types');
|
|
|
|
function cleanOutputDirs() {
|
|
return {
|
|
name: 'clean-output-dirs',
|
|
buildStart() {
|
|
if (fs.existsSync(distDir)) fs.rmSync(distDir, { recursive: true });
|
|
if (fs.existsSync(typesDir)) fs.rmSync(typesDir, { recursive: true });
|
|
},
|
|
};
|
|
}
|
|
|
|
// The Node build reads migrations + the default DB from disk at runtime (see
|
|
// fs.migrationsPath / bundledDatabasePath in loot-core), so copy them next to
|
|
// the bundle. The browser build embeds its own copies, so nothing else needs to
|
|
// be on disk.
|
|
function copyNodeRuntimeAssets() {
|
|
return {
|
|
name: 'copy-node-runtime-assets',
|
|
closeBundle() {
|
|
fs.cpSync(migrationsDir, path.join(distDir, 'migrations'), {
|
|
recursive: true,
|
|
});
|
|
fs.copyFileSync(defaultDbPath, path.join(distDir, 'default-db.sqlite'));
|
|
},
|
|
};
|
|
}
|
|
|
|
export default defineConfig({
|
|
ssr: {
|
|
noExternal: true,
|
|
external: ['better-sqlite3'],
|
|
resolve: {
|
|
conditions: ['api'],
|
|
// Under vitest, `noExternal` inlines the UMD builds of @rschedule, but
|
|
// the `require()` calls inside them still load native copies, so
|
|
// `@rschedule/standard-date-adapter/setup` registers the date adapter
|
|
// and recurrence rules on a different `@rschedule/core` than the one
|
|
// loot-core uses, and every schedule then advances one millisecond at
|
|
// a time. Resolving the ES builds keeps a single instance. The
|
|
// production bundle is unaffected, as Rollup merges the copies.
|
|
...(process.env.VITEST ? { mainFields: ['module', 'main'] } : {}),
|
|
},
|
|
},
|
|
build: {
|
|
ssr: true,
|
|
target: 'node20',
|
|
outDir: distDir,
|
|
emptyOutDir: true,
|
|
sourcemap: true,
|
|
lib: {
|
|
entry: {
|
|
index: path.resolve(__dirname, 'index.ts'),
|
|
models: path.resolve(__dirname, 'models.ts'),
|
|
},
|
|
formats: ['cjs'],
|
|
fileName: (_format, entryName) => `${entryName}.js`,
|
|
},
|
|
},
|
|
plugins: [
|
|
cleanOutputDirs(),
|
|
peggyLoader(),
|
|
copyNodeRuntimeAssets(),
|
|
visualizer({ template: 'raw-data', filename: 'app/stats.json' }),
|
|
],
|
|
resolve: {
|
|
conditions: ['api'],
|
|
},
|
|
test: {
|
|
globals: true,
|
|
// e2e/ holds Playwright tests (yarn e2e), not vitest ones.
|
|
exclude: [...configDefaults.exclude, 'e2e/**'],
|
|
// Each test loads a budget file and runs all DB migrations, which can be
|
|
// slow on busy CI runners; the default 5s timeout is too tight and causes
|
|
// flaky timeouts (and a cascade of unhandled rejections from in-flight work
|
|
// continuing after teardown).
|
|
testTimeout: 20_000,
|
|
hookTimeout: 20_000,
|
|
onConsoleLog(log: string, type: 'stdout' | 'stderr'): boolean | void {
|
|
// print only console.error
|
|
return type === 'stderr';
|
|
},
|
|
maxWorkers: 2,
|
|
reporters: process.env.CI
|
|
? [
|
|
'default',
|
|
[
|
|
'junit',
|
|
{ outputFile: './test-results/junit.xml', suiteName: 'api' },
|
|
],
|
|
]
|
|
: ['default'],
|
|
},
|
|
});
|