@regressionproof/snapshotter 0.0.1

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/.nvmrc ADDED
@@ -0,0 +1 @@
1
+ lts/*
@@ -0,0 +1,58 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "type": "node",
6
+ "request": "attach",
7
+ "name": "attach.tests",
8
+ "port": 5200,
9
+ "restart": true,
10
+ "timeout": 10000
11
+ },
12
+ {
13
+ "type": "node",
14
+ "request": "launch",
15
+ "name": "test.file",
16
+ "runtimeExecutable": "node",
17
+ "runtimeArgs": [
18
+ "--inspect-brk",
19
+ "--trace-warnings",
20
+ "${workspaceFolder}/node_modules/.bin/jest",
21
+ "${fileBasenameNoExtension}",
22
+ "--detectOpenHandles"
23
+ ],
24
+ "cwd": "${workspaceFolder}",
25
+ "console": "integratedTerminal",
26
+ "internalConsoleOptions": "neverOpen"
27
+ },
28
+ {
29
+ "type": "node",
30
+ "request": "launch",
31
+ "name": "test.all",
32
+ "runtimeExecutable": "node",
33
+ "runtimeArgs": [
34
+ "--inspect-brk",
35
+ "--trace-warnings",
36
+ "${workspaceFolder}/node_modules/.bin/jest"
37
+ ],
38
+ "cwd": "${workspaceFolder}",
39
+ "console": "integratedTerminal",
40
+ "internalConsoleOptions": "neverOpen"
41
+ },
42
+ {
43
+ "type": "node",
44
+ "request": "launch",
45
+ "name": "boot",
46
+ "runtimeExecutable": "yarn",
47
+ "runtimeArgs": [
48
+ "run",
49
+ "--inspect-brk",
50
+ "--trace-warnings",
51
+ "boot"
52
+ ],
53
+ "cwd": "${workspaceFolder}",
54
+ "console": "integratedTerminal",
55
+ "internalConsoleOptions": "neverOpen"
56
+ }
57
+ ]
58
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "debug.node.autoAttach": "on",
3
+ "git.ignoreLimitWarning": true,
4
+ "javascript.validate.enable": false,
5
+ "files.watcherExclude": {
6
+ "**/.git/objects/**": true,
7
+ "**/.git/subtree-cache/**": true,
8
+ "**/build/**": true,
9
+ "**/node_modules/**": true,
10
+ },
11
+ "search.exclude": {
12
+ "**/build/**": true,
13
+ "**/node_modules/**": true,
14
+ "**/.next/**": true
15
+ },
16
+ "editor.maxTokenizationLineLength": 20000000,
17
+ "[javascript]": {
18
+ "editor.formatOnSave": false
19
+ },
20
+ "[javascriptreact]": {
21
+ "editor.formatOnSave": false
22
+ },
23
+ "[typescript]": {
24
+ "editor.formatOnSave": false
25
+ },
26
+ "[typescriptreact]": {
27
+ "editor.formatOnSave": false
28
+ },
29
+ "[handlebars]": {
30
+ "editor.formatOnSave": false
31
+ },
32
+ "typescript.tsdk": "node_modules/typescript/lib",
33
+ "cSpell.ignorePaths": [
34
+ "**/package-lock.json",
35
+ "**/node_modules/**",
36
+ "**/build/**",
37
+ "**/vscode-extension/**",
38
+ "**/.git/objects/**",
39
+ ".vscode",
40
+ ".spruce"
41
+ ],
42
+ "cSpell.words": [
43
+ "arkit",
44
+ "autogenerated",
45
+ "scrollable",
46
+ "serializable"
47
+ ],
48
+ "debug.javascript.unmapMissingSources": true,
49
+ "javascript.preferences.importModuleSpecifier": "relative",
50
+ "typescript.preferences.importModuleSpecifier": "relative",
51
+ "eslint.useFlatConfig": true,
52
+ "eslint.enable": true,
53
+ "eslint.validate": [
54
+ "javascript",
55
+ "javascriptreact",
56
+ "typescript",
57
+ "typescriptreact"
58
+ ],
59
+ "eslint.workingDirectories": [
60
+ "./"
61
+ ],
62
+ "typescript.validate.enable": true,
63
+ "editor.formatOnSave": false,
64
+ "editor.codeActionsOnSave": {
65
+ "source.fixAll.eslint": "always"
66
+ }
67
+ }
@@ -0,0 +1,112 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "type": "npm",
6
+ "script": "watch.build.dev",
7
+ "group": "build",
8
+ "label": "watch.build.dev & problem.watcher",
9
+ "isBackground": true,
10
+ "runOptions": {
11
+ "runOn": "folderOpen"
12
+ },
13
+ "promptOnClose": false,
14
+ "presentation": {
15
+ "focus": false,
16
+ "reveal": "never"
17
+ },
18
+ "problemMatcher": {
19
+ "base": "$tsc-watch",
20
+ "applyTo": "allDocuments"
21
+ }
22
+ },
23
+ {
24
+ "label": "test.file",
25
+ "command": "spruce",
26
+ "args": [
27
+ "test",
28
+ "--inspect",
29
+ "5200",
30
+ "--pattern",
31
+ "${fileBasenameNoExtension}",
32
+ "--watchMode",
33
+ "standard"
34
+ ],
35
+ "promptOnClose": false,
36
+ "group": {
37
+ "kind": "test",
38
+ "isDefault": true
39
+ },
40
+ "presentation": {
41
+ "reveal": "always",
42
+ "panel": "dedicated",
43
+ },
44
+ "problemMatcher": []
45
+ },
46
+ {
47
+ "label": "test.reporter",
48
+ "command": "spruce",
49
+ "args": [
50
+ "test",
51
+ "--shouldHoldAtStart",
52
+ "true",
53
+ "--watchMode",
54
+ "smart"
55
+ ],
56
+ "promptOnClose": false,
57
+ "group": "test",
58
+ "runOptions": {
59
+ "runOn": "folderOpen"
60
+ },
61
+ "presentation": {
62
+ "panel": "shared",
63
+ "focus": true,
64
+ "reveal": "always"
65
+ }
66
+ },
67
+ {
68
+ "label": "spruce",
69
+ "type": "shell",
70
+ "command": "spruce ${input:spruceCommand}",
71
+ "problemMatcher": [],
72
+ "presentation": {
73
+ "reveal": "always",
74
+ "focus": true,
75
+ "panel": "new",
76
+ "clear": false
77
+ }
78
+ },
79
+ {
80
+ "label": "shell",
81
+ "type": "shell",
82
+ "command": "${input:command} ${input:optionsCommand}",
83
+ "problemMatcher": [],
84
+ "presentation": {
85
+ "reveal": "always",
86
+ "focus": true,
87
+ "panel": "new",
88
+ "clear": false
89
+ }
90
+ }
91
+ ],
92
+ "inputs": [
93
+ {
94
+ "id": "spruceCommand",
95
+ "description": "spruce command",
96
+ "default": "create.test",
97
+ "type": "promptString"
98
+ },
99
+ {
100
+ "id": "command",
101
+ "description": "command",
102
+ "default": "yarn",
103
+ "type": "promptString"
104
+ },
105
+ {
106
+ "id": "optionsCommand",
107
+ "description": "optionsCommand",
108
+ "default": "add",
109
+ "type": "promptString"
110
+ }
111
+ ]
112
+ }
@@ -0,0 +1,3 @@
1
+ import eslintConfigSpruce from 'eslint-config-spruce'
2
+
3
+ export default eslintConfigSpruce
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@regressionproof/snapshotter",
3
+ "version": "0.0.1",
4
+ "publishConfig": {
5
+ "access": "public"
6
+ },
7
+ "main": "./build/index.js",
8
+ "types": "./build/index.d.ts",
9
+ "module": "./build/esm/index.js",
10
+ "sideEffects": false,
11
+ "exports": {
12
+ ".": {
13
+ "import": {
14
+ "types": "./build/esm/index.d.ts",
15
+ "default": "./build/esm/index.js"
16
+ },
17
+ "require": {
18
+ "types": "./build/index.d.ts",
19
+ "default": "./build/index.js"
20
+ }
21
+ }
22
+ },
23
+ "scripts": {
24
+ "build.ci": "yarn run build.tsc && yarn run build.resolve-paths && yarn run lint",
25
+ "build.dev": "yarn run build.tsc --sourceMap ; yarn run resolve-paths.lint",
26
+ "build.copy-files": "mkdir -p build && rsync -avzq --exclude='*.ts' ./src/ ./build/",
27
+ "build.resolve-paths": "resolve-path-aliases --target build --patterns '**/*.js,**/*.d.ts'",
28
+ "build.tsc": "yarn run build.copy-files && tsc",
29
+ "build.dist": "tsc --project tsconfig.dist.json && yarn build.resolve-paths && mv build esm && yarn build.esm-postbuild && yarn build.tsc && yarn build.resolve-paths && mv esm build/",
30
+ "build.esm-postbuild": "esm-postbuild --target esm --patterns '**/*.js'",
31
+ "clean": "yarn run clean.build",
32
+ "clean.all": "yarn run clean.dependencies && yarn run clean.build",
33
+ "clean.build": "rm -rf build/",
34
+ "clean.dependencies": "rm -rf node_modules/ package-lock.json yarn.lock",
35
+ "fix.lint": "eslint --fix --cache '**/*.ts'",
36
+ "lint": "eslint --cache '**/*.ts'",
37
+ "lint.tsc": "tsc -p . --noEmit",
38
+ "post.watch.build": "yarn run build.copy-files && yarn run build.resolve-paths",
39
+ "rebuild": "yarn run clean.all && yarn install && yarn run build.dev",
40
+ "update.dependencies": "yarn run clean.dependencies && yarn",
41
+ "resolve-paths.lint": "yarn run build.resolve-paths ; yarn run lint",
42
+ "test": "jest --passWithNoTests",
43
+ "test.manual": "node --env-file=.env build/scripts/testManual.js",
44
+ "watch.build.dev": "tsc-watch --sourceMap --onCompilationComplete 'yarn run post.watch.build'",
45
+ "watch.rebuild": "yarn run clean.all && yarn install && yarn run watch.build.dev",
46
+ "watch.tsc": "tsc -w"
47
+ },
48
+ "devDependencies": {
49
+ "@sprucelabs/esm-postbuild": "^9.0.13",
50
+ "@sprucelabs/jest-json-reporter": "^10.0.19",
51
+ "@sprucelabs/resolve-path-aliases": "^4.0.13",
52
+ "@sprucelabs/test": "^11.1.0",
53
+ "@sprucelabs/test-utils": "^7.2.3",
54
+ "@types/node": "^25.0.6",
55
+ "chokidar-cli": "^3.0.0",
56
+ "eslint": "^9.39.2",
57
+ "eslint-config-spruce": "^11.2.26",
58
+ "jest": "^30.2.0",
59
+ "jest-circus": "^30.2.0",
60
+ "prettier": "^3.7.4",
61
+ "ts-node": "^10.9.2",
62
+ "tsc-watch": "^7.2.0",
63
+ "typescript": "^5.9.3"
64
+ },
65
+ "description": "Take snapshots of your TDD project to be used for LLM training data",
66
+ "skill": {
67
+ "namespace": "regressionproof-snapshotter"
68
+ },
69
+ "jest": {
70
+ "testRunner": "jest-circus/runner",
71
+ "maxWorkers": 4,
72
+ "testTimeout": 120000,
73
+ "testEnvironment": "node",
74
+ "testPathIgnorePatterns": [
75
+ "<rootDir>/tmp/",
76
+ "<rootDir>/src/",
77
+ "<rootDir>/node_modules/",
78
+ "<rootDir>/build/__tests__/testDirsAndFiles/"
79
+ ],
80
+ "testMatch": [
81
+ "**/__tests__/**/*.test.js?(x)"
82
+ ],
83
+ "moduleNameMapper": {
84
+ "^#spruce/(.*)$": "<rootDir>/build/.spruce/$1"
85
+ }
86
+ },
87
+ "dependencies": {}
88
+ }
@@ -0,0 +1,16 @@
1
+ {
2
+ "scriptUpdater": {
3
+ "skipped": []
4
+ },
5
+ "skipped": [
6
+ "skill"
7
+ ],
8
+ "installed": [
9
+ "test"
10
+ ],
11
+ "writer": {
12
+ "skipped": [
13
+ "tsconfig.json"
14
+ ]
15
+ }
16
+ }
package/src/git.ts ADDED
@@ -0,0 +1,48 @@
1
+ import { exec } from 'child_process'
2
+ import { existsSync } from 'fs'
3
+ import path from 'path'
4
+ import { promisify } from 'util'
5
+ import { RemoteOptions } from './snapshotter.types.js'
6
+
7
+ const execAsync = promisify(exec)
8
+
9
+ export async function gitCommit(mirrorPath: string): Promise<boolean> {
10
+ const gitDir = path.join(mirrorPath, '.git')
11
+
12
+ if (!existsSync(gitDir)) {
13
+ await execAsync(`git -C "${mirrorPath}" init`)
14
+ }
15
+
16
+ await execAsync(`git -C "${mirrorPath}" add -A`)
17
+
18
+ const { stdout } = await execAsync(
19
+ `git -C "${mirrorPath}" status --porcelain`
20
+ )
21
+ if (!stdout.trim()) {
22
+ return false
23
+ }
24
+
25
+ const message = `Snapshot ${new Date().toISOString()}`
26
+ await execAsync(`git -C "${mirrorPath}" commit -m "${message}"`)
27
+ return true
28
+ }
29
+
30
+ export async function gitPush(
31
+ mirrorPath: string,
32
+ remote: RemoteOptions
33
+ ): Promise<void> {
34
+ const authedUrl = remote.url.replace('://', `://${remote.token}@`)
35
+
36
+ try {
37
+ await execAsync(`git -C "${mirrorPath}" remote get-url origin`)
38
+ await execAsync(
39
+ `git -C "${mirrorPath}" remote set-url origin "${authedUrl}"`
40
+ )
41
+ } catch {
42
+ await execAsync(
43
+ `git -C "${mirrorPath}" remote add origin "${authedUrl}"`
44
+ )
45
+ }
46
+
47
+ await execAsync(`git -C "${mirrorPath}" push -u origin HEAD`)
48
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { snapshot } from './snapshot.js'
2
+ export * from './snapshotter.types.js'
@@ -0,0 +1,55 @@
1
+ import { snapshot } from '../index'
2
+
3
+ async function main() {
4
+ const sourcePath = process.env.SOURCE_PATH
5
+ const mirrorPath = process.env.MIRROR_PATH
6
+ const remoteUrl = process.env.REMOTE_URL
7
+ const remoteToken = process.env.REMOTE_TOKEN
8
+
9
+ if (!sourcePath || !mirrorPath || !remoteUrl || !remoteToken) {
10
+ console.error('Missing required env vars. See .env.example')
11
+ process.exit(1)
12
+ }
13
+
14
+ console.log('Running snapshot...')
15
+ console.log(' Source:', sourcePath)
16
+ console.log(' Mirror:', mirrorPath)
17
+ console.log(' Remote:', remoteUrl)
18
+
19
+ const result = await snapshot({
20
+ sourcePath,
21
+ mirrorPath,
22
+ testResults: {
23
+ timestamp: new Date().toISOString(),
24
+ summary: {
25
+ totalSuites: 1,
26
+ passedSuites: 1,
27
+ failedSuites: 0,
28
+ totalTests: 2,
29
+ passedTests: 2,
30
+ failedTests: 0,
31
+ },
32
+ suites: [
33
+ {
34
+ path: 'src/__tests__/Example.test.ts',
35
+ passed: true,
36
+ tests: [
37
+ { name: 'should work', passed: true },
38
+ { name: 'should also work', passed: true },
39
+ ],
40
+ },
41
+ ],
42
+ },
43
+ remote: {
44
+ url: remoteUrl,
45
+ token: remoteToken,
46
+ },
47
+ })
48
+
49
+ console.log('Snapshot created:', result)
50
+ }
51
+
52
+ main().catch((err) => {
53
+ console.error('Error:', err.message)
54
+ process.exit(1)
55
+ })
@@ -0,0 +1,27 @@
1
+ import { mkdirSync, writeFileSync } from 'fs'
2
+ import path from 'path'
3
+ import { gitCommit, gitPush } from './git.js'
4
+ import { SnapshotOptions } from './snapshotter.types.js'
5
+ import { syncFiles } from './sync.js'
6
+
7
+ export async function snapshot(options: SnapshotOptions): Promise<boolean> {
8
+ const sourcePath = options.sourcePath ?? process.cwd()
9
+ const { mirrorPath, testResults, remote } = options
10
+
11
+ await syncFiles(sourcePath, mirrorPath)
12
+
13
+ const snapshotterDir = path.join(mirrorPath, '.snapshotter')
14
+ mkdirSync(snapshotterDir, { recursive: true })
15
+ writeFileSync(
16
+ path.join(snapshotterDir, 'testResults.json'),
17
+ JSON.stringify(testResults, null, 2)
18
+ )
19
+
20
+ const committed = await gitCommit(mirrorPath)
21
+
22
+ if (committed) {
23
+ await gitPush(mirrorPath, remote)
24
+ }
25
+
26
+ return committed
27
+ }
@@ -0,0 +1,59 @@
1
+ export interface TestResult {
2
+ /** Name of the individual test */
3
+ name: string
4
+ /** Whether this test passed */
5
+ passed: boolean
6
+ /** Full error message + callstack (only present if failed) */
7
+ error?: string
8
+ }
9
+
10
+ export interface SuiteResult {
11
+ /** Path to test file, relative to project root */
12
+ path: string
13
+ /** Whether all tests in suite passed */
14
+ passed: boolean
15
+ /** Array of test results */
16
+ tests: TestResult[]
17
+ }
18
+
19
+ export interface TestResultsSummary {
20
+ /** Total number of test suites (files) */
21
+ totalSuites: number
22
+ /** Number of suites with all tests passing */
23
+ passedSuites: number
24
+ /** Number of suites with at least one failure */
25
+ failedSuites: number
26
+ /** Total number of individual tests */
27
+ totalTests: number
28
+ /** Number of passing tests */
29
+ passedTests: number
30
+ /** Number of failing tests */
31
+ failedTests: number
32
+ }
33
+
34
+ export interface TestResults {
35
+ /** ISO 8601 timestamp of when test run completed */
36
+ timestamp: string
37
+ /** Summary counts */
38
+ summary: TestResultsSummary
39
+ /** Array of suite results */
40
+ suites: SuiteResult[]
41
+ }
42
+
43
+ export interface RemoteOptions {
44
+ /** Gitea repo URL to push to */
45
+ url: string
46
+ /** Gitea access token for authentication */
47
+ token: string
48
+ }
49
+
50
+ export interface SnapshotOptions {
51
+ /** Source project path to sync from. Defaults to process.cwd() */
52
+ sourcePath?: string
53
+ /** Mirror directory path where the isolated git repo lives */
54
+ mirrorPath: string
55
+ /** Test results from the completed test run */
56
+ testResults: TestResults
57
+ /** Remote Gitea repo to push to */
58
+ remote: RemoteOptions
59
+ }
package/src/sync.ts ADDED
@@ -0,0 +1,35 @@
1
+ import { exec } from 'child_process'
2
+ import { existsSync } from 'fs'
3
+ import path from 'path'
4
+ import { promisify } from 'util'
5
+
6
+ const execAsync = promisify(exec)
7
+
8
+ const DEFAULT_EXCLUDES = [
9
+ 'node_modules',
10
+ 'build',
11
+ '*.env*',
12
+ '*.pem',
13
+ '*.key',
14
+ '*.p12',
15
+ '*.pfx',
16
+ '*credentials*',
17
+ '*secret*',
18
+ '*.local',
19
+ ]
20
+
21
+ export async function syncFiles(
22
+ sourcePath: string,
23
+ mirrorPath: string
24
+ ): Promise<void> {
25
+ const hasGitIgnore = existsSync(path.join(sourcePath, '.gitignore'))
26
+ const excludes = DEFAULT_EXCLUDES.map((e) => `--exclude='${e}'`).join(' ')
27
+
28
+ if (hasGitIgnore) {
29
+ const cmd = `cd "${sourcePath}" && git ls-files --cached --others --exclude-standard -z | rsync -av --files-from=- --from0 ${excludes} . "${mirrorPath}/"`
30
+ await execAsync(cmd)
31
+ } else {
32
+ const cmd = `rsync -av --delete ${excludes} "${sourcePath}/" "${mirrorPath}/"`
33
+ await execAsync(cmd)
34
+ }
35
+ }
@@ -0,0 +1,26 @@
1
+ {
2
+ "compilerOptions": {
3
+ "module": "ES2020",
4
+ "target": "ES6",
5
+ "lib": ["ES2020", "DOM"],
6
+ "skipLibCheck": true,
7
+ "esModuleInterop": true,
8
+ "moduleResolution": "node",
9
+ "declaration": true,
10
+ "noImplicitAny": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "noImplicitReturns": true,
13
+ "strict": true,
14
+ "noUnusedLocals": true,
15
+ "resolveJsonModule": true,
16
+ "sourceMap": false,
17
+ "outDir": "./build",
18
+ "baseUrl": "src",
19
+ "experimentalDecorators": true,
20
+ "paths": {
21
+ "#spruce/*": [".spruce/*"]
22
+ }
23
+ },
24
+ "include": ["./src/*.ts", "./src/**/*.ts"],
25
+ "exclude": ["**/node_modules/*", "build/*", "esm/*"]
26
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "compilerOptions": {
3
+ "skipLibCheck": true,
4
+ "module": "CommonJS",
5
+ "esModuleInterop": true,
6
+ "target": "ES2020",
7
+ "lib": ["DOM", "ES2022"],
8
+ "declaration": true,
9
+ "noImplicitAny": true,
10
+ "allowJs": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "noImplicitReturns": true,
13
+ "strict": true,
14
+ "noUnusedLocals": true,
15
+ "resolveJsonModule": true,
16
+ "moduleResolution": "node",
17
+ "sourceMap": false,
18
+ "outDir": "build",
19
+ "baseUrl": "src",
20
+ "experimentalDecorators": true,
21
+ "paths": {
22
+ "#spruce/*": [".spruce/*"]
23
+ }
24
+ },
25
+ "include": ["./src/*.ts", "./src/**/*.ts", "./src/.spruce/**/*"],
26
+ "exclude": ["build", "esm"]
27
+ }