{"_id":"@akalforge/supaforge","_rev":"23-0b7cab66ab0ea5f05738d159783a1b32","name":"@akalforge/supaforge","dist-tags":{"latest":"0.0.23"},"versions":{"0.0.2":{"name":"@akalforge/supaforge","version":"0.0.2","author":{"name":"Akal Forge"},"license":"MIT","_id":"@akalforge/supaforge@0.0.2","maintainers":[{"name":"jasdeepkhalsa","email":"jasdeepkhalsa1@gmail.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"812f051b26d48dcc77c6b00de3c63f2056b7e171","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.2.tgz","fileCount":63,"integrity":"sha512-GtmvfgOKHrGTFhrGUxAn3CX93W6yDZ5prJ2oztgjvMUnadvtxStgyohIA5CynGyzjWa90F1ENgdo//6K9qJjaA==","signatures":[{"sig":"MEQCIBYYH/sdSxThXXxMmQqbKXtM/T9N0nwhbKn24MLsP2JOAiAimHiXY4mdoMS2IMk01JQU0ACGbha66ryihmlR2eXeGQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.2","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":433460},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"773b5175c249c8470d50028f9451cb4c263e51cb","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"jasdeepkhalsa1@gmail.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","@dbdiff/cli":"^3.0.0-rc.2","@oclif/core":"^4"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.2_1775782392549_0.7936392685428606","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@akalforge/supaforge","version":"0.0.3","author":{"name":"Akal Forge"},"license":"MIT","_id":"@akalforge/supaforge@0.0.3","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"5c6557585138bcdacbc3fb0f1e594bf32652bac9","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.3.tgz","fileCount":68,"integrity":"sha512-mtzhsHP6xQHcIaeUSNPyltQt7Cu2typXWaZyB26/EtH7aaJXRTGCpM4sj98IKD/JQIaLhDfM6/OkFO5Zrz4zFA==","signatures":[{"sig":"MEUCIQDX8+jaP/JzlonV+NgOdSifoFHUB+qddCUVjT5L2KjYZAIgaP/poEN0xV5uvBh080vvbf5duj6ZXw4R9cqyPxoURTs=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.3","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":701325},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"14d1f45a959d5c7405b013b777497b0e92bd932a","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","@dbdiff/cli":"^3.0.0-rc.2","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.3_1776771626673_0.9165894535483925","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@akalforge/supaforge","version":"0.0.4","author":{"name":"Akal Forge"},"license":"MIT","_id":"@akalforge/supaforge@0.0.4","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"1a735ae21fdef2eb9f2c6b4c8739cbc3110b0830","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.4.tgz","fileCount":68,"integrity":"sha512-cU9hJrmnmhuIz9NvBM4U8iMWwCqJrpFyR97Wt8UlQWJiQ81E6o1djQ8oZ9OFnnP6hsx9zW4GV/KuUJLW2BfV4w==","signatures":[{"sig":"MEYCIQD6g6DEhvrsHtEN3wmxUImNQekauS4kpXnfEJUDMM2z8gIhAI5SnEzo5KPqc9xnOLKpX6qnMkheuqMzAxpihvG1HkOR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.4","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":717108},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"424756a2d87e89d7e3d3e3ae1ff0161e156055f0","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","@dbdiff/cli":"^3.0.0-rc.2","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.4_1778516430304_0.5532864814934781","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@akalforge/supaforge","version":"0.0.5","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.5","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"a99cafd50bb98de7483009cb794764a59025f3a3","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.5.tgz","fileCount":77,"integrity":"sha512-upg+3aCo5ftX9YJd22nQaqV7HKxXNz1cT5/RML9rA1DTwl1pR1grnZoRxwG/M5IcLuJgPQnEUqggGilSeNTWmw==","signatures":[{"sig":"MEUCIFMmzPRpfDjAFbPx1oCkUj7Fl1kGpqsjtu68H5IcEgTxAiEArsslzVLpE2DvkFDzYoP6iDhkEcgwLprlRAfYweERhgE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.5","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":779323},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"93655c11d54671f1b9766e9b02772890502c2acc","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.2","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.5_1780991472778_0.1915469328390278","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"@akalforge/supaforge","version":"0.0.6","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.6","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"d554b3e12aa6c686dfed64c3271ede4113f2d1f2","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.6.tgz","fileCount":87,"integrity":"sha512-zg1gUNLXZOhuVARh7ReIFdGwRICYYPLGqjHRoWhLyMycbblLQ9VO/NSKowKUWnGxeQiFbHnwpCYScTSkZv6/rw==","signatures":[{"sig":"MEUCIHg0dVLKUm7lXCUzesFu9p6TjF1o138UxFa9ObM6GgDtAiEArIuLjqLkA3/N+Jwyt0SkMygGWBxO8n/Zg43NXVHHKbA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.6","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":858495},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"74e82a9686a5e0e11e7a8af2c9dd49e34282581b","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.2","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.6_1781048683743_0.2820881834814313","host":"s3://npm-registry-packages-npm-production"}},"0.0.7":{"name":"@akalforge/supaforge","version":"0.0.7","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.7","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"cdf5ff899d0f36833c933d06ba0e25503706ee89","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.7.tgz","fileCount":85,"integrity":"sha512-VVnI9u0Q3IWh/AcVTTkURRikDkXpofYh1wa1ALGt8/HXLf6K2Zry2870ZqoXgdjLwbqVMUf0I1tcqKr4Jbafkw==","signatures":[{"sig":"MEQCIAIt4DDsNIUxQMRx3QP3w2ThRSCKjG+0loPkPm9KtIcxAiBobDSFGNUqiKqmYOozdryhvyzyvD6XqFM/VCd+gY5LSA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.7","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":910790},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"931d4612140b889d5db733564aa76b39f24f5c58","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.3","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.7_1784850239748_0.8293480259589618","host":"s3://npm-registry-packages-npm-production"}},"0.0.8":{"name":"@akalforge/supaforge","version":"0.0.8","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.8","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"fac81536bca0d06e5e80da595ea9de7c88d21070","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.8.tgz","fileCount":85,"integrity":"sha512-w/AHQjNjpig0YW6PfsbfrFIp+NhaTyeEPyM//nQp9N5dqGssBhppAkZqaAGioiCea2dPgoslCHjq10jkC1yg3Q==","signatures":[{"sig":"MEYCIQC3jjQQdIinWntU7T5DDFNXLHiebVYhTECvWmw+F8ex1gIhANHonHJSvzZAa7n/nmNByBZ1S2ERD8/fTV1UsAVHMjNe","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.8","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":910790},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"783464e951517299c5c4e417a4828f3462ceab18","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.4","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.8_1785970819027_0.5373892036616807","host":"s3://npm-registry-packages-npm-production"}},"0.0.9":{"name":"@akalforge/supaforge","version":"0.0.9","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.9","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"a45706384c3bc4d6e5f0f255cc4bd61b6a16926f","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.9.tgz","fileCount":85,"integrity":"sha512-AcKtDNvAu3xKmafaamBpmyTZIE0oKTlyQemWKS6aKRXHcDiexXSNH/IHYNzpcsnCP50QrxGT3y7bWczJQLnlBA==","signatures":[{"sig":"MEUCIQDn+hdY/Rq2LMgQM4xdwDGiCA5HhbuZOv1JgXam/2wxUAIgT46dbPXZp8qlgGYNUwo0ZDD7OR3wEOCI/zXDhwhtgHE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.9","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":920725},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"8dd486f34b175e634a4cee59e57adc3b69871ffa","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.6","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.9_1786487789431_0.11379148895618774","host":"s3://npm-registry-packages-npm-production"}},"0.0.10":{"name":"@akalforge/supaforge","version":"0.0.10","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.10","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"48b666e67eca182a889dcda5df9ede5d9b4edeae","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.10.tgz","fileCount":85,"integrity":"sha512-26S3xRLvDW+LPxCU45/4oSqEBIsasYwIt+I5j5JyDS4lZDEyUiOqdPZRQrQr20t/K1LIrmeLfMAYZK/4KN3ldQ==","signatures":[{"sig":"MEQCIAoD2ZVNSIWmRxBNKM6QD2KIrkaKFzfO1FuCnKwHF1RqAiAvhGb6Wjx/ZG3/23lvl5NYEX1CWsado5/LCRS2V5GsYQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.10","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":962311},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"77c60647ac6b382f7cdec72cecf8261c7369ba2c","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.7","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.10_1786787775277_0.6606644794951877","host":"s3://npm-registry-packages-npm-production"}},"0.0.11":{"name":"@akalforge/supaforge","version":"0.0.11","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.11","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"86097cb31867d92447e2fcaf402b8c0f80530283","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.11.tgz","fileCount":85,"integrity":"sha512-urj2DXikB0lB/POe3iB3ZoxOtzVKlOSGyHWQfU/OLZRS0YqdNBrvr8zgRK4mOQ1zg1durOhMTa5ulyK7JGGeAw==","signatures":[{"sig":"MEQCIDhyY6xHJnorkxs0WY4KTRk0ndl9S3guWYoY4OoWQxj9AiAeG2zqcl36r6GUzmUNp2n3PAwG1p5Jh2oPEyA5tfhJAg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.11","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1036549},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"63ceddc3f19925d2091d672eec4be5452488fd17","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.7","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.11_1787293828595_0.9612468788488466","host":"s3://npm-registry-packages-npm-production"}},"0.0.12":{"name":"@akalforge/supaforge","version":"0.0.12","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.12","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"da3b6ac352c363a450b6f0c664b7eb04525d9c6b","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.12.tgz","fileCount":85,"integrity":"sha512-i23xU2WPdlz7KqCofyGWInq8rWUHQKoyeDH4tJAT3DmWHZopazs9IfC/DvlYLWVvY4l2O7zd9maEFJsnDFGjXg==","signatures":[{"sig":"MEYCIQC51iT/O5aL3MfSKL5fDeYnR3hPZ60cP6W51TpjU4V5ywIhAIlxte37RH/KWYtyhwmCmQVNtu/er2KgwkmPJ1CE3DRf","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.12","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1097552},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"30c4d9a7b7edd0601d8bed78d435eafb6d4aa1a5","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.7","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.12_1787339792563_0.6105187812515473","host":"s3://npm-registry-packages-npm-production"}},"0.0.13":{"name":"@akalforge/supaforge","version":"0.0.13","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.13","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"6ff360c297fd7debb6a0a1cfab5a7a054b8ca661","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.13.tgz","fileCount":85,"integrity":"sha512-O1gZHku4AXT/g8x2OizC7w/fBb99WyH7ONx27Ri5b1Y6guxxRRnAfB0gEIogpGrH1Vrmre9Mo8rB7kqhgWPpyA==","signatures":[{"sig":"MEQCIDwxrn05OpyK1xuLPM2nKVQPYDxMcxJpmLnWVt4P9EUnAiBI2INYFXTSXmJrbLOhMgyEjyKNdQwBWalXo3GmMzei3g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.13","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1098610},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"567c42a81e1c20d3b2f65233c4c84e48d017a4db","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.9","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.13_1787511312778_0.24721175182712418","host":"s3://npm-registry-packages-npm-production"}},"0.0.14":{"name":"@akalforge/supaforge","version":"0.0.14","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.14","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"fbc77fabfa3bd6b5535016055f4ab598ef1867e0","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.14.tgz","fileCount":85,"integrity":"sha512-oPEdimEWzjNC+cTEBs2YVtBrg+9yCuDlzF6+9ijUgoZf7Nk43b8iqxYVhB2j2f9JYCAUKBfPl9XFA9VBz9eFVA==","signatures":[{"sig":"MEUCIQDlBgRP/mdaTQH5ky9kSgJ7tESO2zytQpBVxdcWmRmKsgIgIX39caO4phA1+uFhP5n+bZ0vqbvOmnQ5DWiEW6AV6Dg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.14","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1100331},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"8abda3c4045630fd714074eb89ed668b8efc7a1a","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.9","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.14_1787520268145_0.19576212402523652","host":"s3://npm-registry-packages-npm-production"}},"0.0.15":{"name":"@akalforge/supaforge","version":"0.0.15","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.15","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"8ffb9e401ba46530720bf75ed1f5e944b1d34e82","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.15.tgz","fileCount":85,"integrity":"sha512-zPw1wYYhr1YBsRmzlOB1KjeKwZxRkJHY1dX0Kmu2pdr65KeOi7H9pPs4lovMJfjh6KYmR9gE/BdB7RRaEOE+uw==","signatures":[{"sig":"MEUCIQC3t9ocHb9s7jbK99IsbxdnEz6IYdUbXUcd0PYygJU9agIgPX1fCt+hmbkHRygnzo+B1WX9Nsy3XyExcuNSdZWOWZo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.15","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1159302},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"5c291bd10f2a1a566f32dfe81eb5c6f4d0674126","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.9","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.15_1787601128052_0.7586826116365051","host":"s3://npm-registry-packages-npm-production"}},"0.0.16":{"name":"@akalforge/supaforge","version":"0.0.16","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.16","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"46b041aecb9ab3f3c7824a46c68f2af29950dd8f","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.16.tgz","fileCount":85,"integrity":"sha512-TWFHrjtjYlE8W3xefIwhcTMfgUG7C+6IJBTrVld/05OvgsYiM0EP464ZtD7caah3eQKDY609aCy+YZ4c4a/Xtg==","signatures":[{"sig":"MEQCIAWws22ZCPnN+YSWxB1i6VlmXmWNbCaMitZICYZCXKQ6AiATBudYb9BtyIFA8LT7r7SMNPthXQbaiYF4Skjtio5xEA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCICUNiK6w3Jh1JqiQH72lYk9Tgvq+oG9veRJcv/NcjcL/AiEAvhGbcH7afbZUyyMpEmbMP/YB8y4xkea9AenRzrOw7bE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.16","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1204014},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"5229ab80e3d048770879e33a1c88b098070e318b","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.12","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.4","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.16_1789502693556_0.3178240392560838","host":"s3://npm-registry-packages-npm-production"}},"0.0.17":{"name":"@akalforge/supaforge","version":"0.0.17","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.17","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"2aeea9a9225637d4e27cdb96b8b8fc7c3b28e708","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.17.tgz","fileCount":85,"integrity":"sha512-isAazIcZ+kFGp67zZkYBqJlLoORL76A87QaIwJNmBmQ26NaZ1EdTRHVVlXrYeEwvDvzRakZb9wI5Ixcfhds6YQ==","signatures":[{"sig":"MEUCIDH/FQGB3X/F/pwmCSyUNjsEyOuo/3I/gigL913u13n3AiEAjyFSxYtj4pvYnu7O8PD09F0UIR7Qqac8gqCCx0OD940=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIDWoatucePJQhUrB2SaMK8hDwlburQEk4BCpr1Ev4cLhAiEA74H5Vj+Q2Q6VGsrOBosfIPZnI8AOLDPw04b2ycN5nOI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.17","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1213464},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"34995ce0df82dcb04d2ec03557f90db7f5575586","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.12","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.4","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.17_1789749116754_0.7570831280180093","host":"s3://npm-registry-packages-npm-production"}},"0.0.18":{"name":"@akalforge/supaforge","version":"0.0.18","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.18","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"e9986ffcebe97fa01d4342f96f46a9315a87a823","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.18.tgz","fileCount":85,"integrity":"sha512-iN75j5tNTFsEns5I5HoD+DSV4oFbgpwpQETeqyC/5tVrdaPwXAa8idQcIgaFpgqIBqlB+dYg052PX4mN1dpsyA==","signatures":[{"sig":"MEUCIQDC1X2FpnuFpPAMBUvmjech25kvjX/nWNGikxYUOAVWqAIgLwORZNVSjRFQjIyyi9C6EpyXNfwoWgk0ejrYPnRIzkU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIDrW5sr0WLnmOcmQSyFlZGLlyzP5cJ3NmsDeLCYqv4ugAiEAxzXoiGG1nVrECrCcRnap8dF0GtGhUaSpcUXq8xXbOwk=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.18","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1252696},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"56e1cd4c309c1a21e474136935379a1b17ba3113","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.12","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.4","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.18_1790167487496_0.9750431023991772","host":"s3://npm-registry-packages-npm-production"}},"0.0.19":{"name":"@akalforge/supaforge","version":"0.0.19","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.19","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"f6051c6945bd61cfe523e27dc3625b74f462a037","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.19.tgz","fileCount":85,"integrity":"sha512-h9b2xljgoJiJu153knYU13nmpUU3opG0wyD+koXnQYmgHXlm8+45GVuEPRAuEqLYPQlIz2h8zbbN5as5DO84+g==","signatures":[{"sig":"MEQCIAxBtix9ZBlHjlv2i0gLNEapjSdEAaNwkfNGXT0pDn1gAiBPFCfbOhJyudBiYI5WR3GLF9ocCV/Puy2CnappD3WI+Q==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDlvG43eLZT7ntKM0ES7D7iOuhtCRLftp5tu5arTspssgIhAPg5ifWAja7elg3nervw/2dVhp3mtM2drSoMPiqM34oX","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.19","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1252696},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"4db78ae410f68925908a134074ccea04017d3dc8","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.13","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.6","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.19_1790552645237_0.4475683159765236","host":"s3://npm-registry-packages-npm-production"}},"0.0.20":{"name":"@akalforge/supaforge","version":"0.0.20","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.20","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"71dd55b052125c2b0c01d2540e16958321f3822e","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.20.tgz","fileCount":87,"integrity":"sha512-SpdrHunrNysKqOsEoEvad+exIPHnPygMIyw7yDsDAY269FdRH/X6/cUSd59TPMdGSNI959rCuCVrc++DSIx4cg==","signatures":[{"sig":"MEYCIQC54JwCWeX3oHE14q6MEW7SMB/X/DC/TlnaIFFnRxaJOAIhAL0Jb9RSXi4l6PKRrSZ7u808Yz8XMzItnR7cztCsaRO3","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDUFNB5yHvJ4H423VBAWhNumhGRdXXTxkIw/edU5zEJJgIhAP4ecxIffpv4hBcS/4Wj1/RSmZwWNpsKv/ae2zaZgs/e","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.20","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1287762},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"6109125ec3b2e9e8af118e97d901c37702a3c495","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"deprecated":"Superseded: this version takes whatever @dbdiff/cli is newest, which it was never tested with, and enum, identity, generated-column and ordering fixes need 0.0.21 or later. Upgrade: npm install -g @akalforge/supaforge@latest","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"^3.0.0-rc.14","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.6","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.20_1790686010527_0.2881568184548966","host":"s3://npm-registry-packages-npm-production"}},"0.0.21":{"name":"@akalforge/supaforge","version":"0.0.21","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.21","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"6c4f387c205cd5c64356322d7f76619e06989a28","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.21.tgz","fileCount":94,"integrity":"sha512-Y/DourC6OFg8b7RbBavGsy6+1VI/NjBu1vBG0JYxrMb9cayBvZGf5NVYJbWjnf2acvzsRsgn5LSzs/Gh6Af4kA==","signatures":[{"sig":"MEYCIQCkAeLPYYxwb0VnB19xpPb29lKY1LenwuFBBz4MUDn9LQIhAJ5nN7FHaQhXSh+EG3a1oRvplD9U9N9cNL9//khNtMbZ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQDzyLb/1lb012F64WBPaxiyY71YwHq/9mNtWqmdktfV3AIhALepewEC2vOAMHqw2BH+DF5bYL2J4U2xl9lMk5iesmL4","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.21","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1536544},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","topics":{"migrate":{"description":"Create, list and run versioned SQL migrations"}},"dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"56c8bbbc5267c295fbc8daaebd7c04bb21c96787","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"3.0.0-rc.17","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.6","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.21_1790892419294_0.5093386672041604","host":"s3://npm-registry-packages-npm-production"}},"0.0.22":{"name":"@akalforge/supaforge","version":"0.0.22","author":{"name":"Akal Software Ltd"},"license":"MIT","_id":"@akalforge/supaforge@0.0.22","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"homepage":"https://github.com/akalforge/supaforge#readme","bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"bin":{"supaforge":"bin/run.js"},"dist":{"shasum":"6b3a48396dde22e529c698acb70bfd6ad6365f53","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.22.tgz","fileCount":94,"integrity":"sha512-Umj9SYMUysPQ9hX/QnXeDx9zZd45GGeYWt1B7WyTQzJKQ92ewzhADNM1ajk0UhKHgouJvfvENfDwAsDiZ+Y5dg==","signatures":[{"sig":"MEUCIBDOSAwhWh/if8HknXUnvL4Scmei2OU+1YOXwAaUMObvAiEApXWDd8anweMXlOUkV+oI3rvkuT1VwiOL/aoOSdx/tf4=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEQCIDSXohWp8P6cXRQ/epmSsLWOLob2sft3hhyye3gM93dxAiASK94viNStZXE1J7SKBWXzdzl2uLowRx3mxumBZqnaOw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.22","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1555121},"main":"dist/index.js","type":"module","oclif":{"bin":"supaforge","topics":{"migrate":{"description":"Create, list and run versioned SQL migrations"}},"dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","engines":{"node":">=18.0.0"},"gitHead":"ba8f46bf18dc1de81046b17209f10dd0b0861828","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"3.0.0-rc.18","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.6","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"tmp":"tmp/supaforge_0.0.22_1790923102338_0.42167701806839575","host":"s3://npm-registry-packages-npm-production"}},"0.0.23":{"_id":"@akalforge/supaforge@0.0.23","bin":{"supaforge":"bin/run.js"},"bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"dist":{"shasum":"e5622bdaeb49aa89bb2104f2179d0f6a75b1aa04","tarball":"https://registry.npmjs.org/@akalforge/supaforge/-/supaforge-0.0.23.tgz","fileCount":94,"integrity":"sha512-VFeo8GyrX+DZ2Ju4bmCxV1w6jIyyEbg+gwsr104cOC6GEQCxPltRItx9svZNJmheOE2ayHmKa+cruHftfJkSMQ==","signatures":[{"sig":"MEYCIQDmZpqDJ9JQsnFSETUyefudzSNpkxX51jCGEhTfSdRSRwIhAJp4No+xMaeeNMeg/7E4m597JToEvlN2I8TVOdZ/gDQJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIA64FNecDtKXvLNE2qjYIV8bTXdMZNJbaaNM7hIqC4z8AiEA9X1pggtXu0CzH7Xn1mYw0kjTKVitxOGtP5mNtgB+jRg="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/@akalforge%2fsupaforge@0.0.23","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":1556562},"main":"dist/index.js","name":"@akalforge/supaforge","type":"module","oclif":{"bin":"supaforge","topics":{"migrate":{"description":"Create, list and run versioned SQL migrations"}},"dirname":"supaforge","commands":{"target":"./dist/commands","strategy":"pattern"},"helpClass":"./dist/help","topicSeparator":" "},"types":"dist/index.d.ts","author":{"name":"Akal Software Ltd"},"engines":{"node":">=18.0.0"},"gitHead":"4dcab408cad50670970c9eb8d7cfeed70b81b5fc","license":"MIT","scripts":{"dev":"./bin/dev.js","lint":"tsc --noEmit && tsc --noEmit -p tsconfig.test.json","test":"vitest run","build":"tsup","test:e2e":"vitest run test/e2e/","test:watch":"vitest","test:integration":"./scripts/test-integration.sh","test:e2e:supabase":"./scripts/test-e2e.sh","test:dbdiff-source":"./scripts/test-against-dbdiff-source.sh"},"version":"0.0.23","_npmUser":{"name":"jasdeepkhalsa","email":"oss@akalforge.com"},"homepage":"https://github.com/akalforge/supaforge#readme","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"_npmVersion":"10.8.2","description":"Diff and sync your Supabase environments","directories":{},"maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"_nodeVersion":"20.20.2","dependencies":{"pg":"^8","zod":"^3.25.51","@dbdiff/cli":"3.0.0-rc.18","@oclif/core":"^4","@supabase/supabase-js":"^2.103.0","@akalforge/pg-conformance":"~0.0.6","@modelcontextprotocol/sdk":"^1.12.1"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsx":"^4","tsup":"^8.5.1","vitest":"^3","@types/pg":"^8","typescript":"^5","@types/node":"^22"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/supaforge_0.0.23_1790924116813_0.02197900611774095"}}},"time":{"created":"2026-04-10T00:53:12.475Z","modified":"2026-10-02T06:55:17.308Z","0.0.2":"2026-04-10T00:53:12.718Z","0.0.3":"2026-04-21T11:40:26.832Z","0.0.4":"2026-05-11T16:20:30.463Z","0.0.5":"2026-06-09T07:51:12.994Z","0.0.6":"2026-06-09T23:44:43.884Z","0.0.7":"2026-07-23T23:43:59.982Z","0.0.8":"2026-08-05T23:00:19.237Z","0.0.9":"2026-08-11T22:36:29.599Z","0.0.10":"2026-08-15T09:56:15.426Z","0.0.11":"2026-08-21T06:30:28.753Z","0.0.12":"2026-08-21T19:16:32.750Z","0.0.13":"2026-08-23T18:55:12.923Z","0.0.14":"2026-08-23T21:24:28.347Z","0.0.15":"2026-08-24T19:52:08.201Z","0.0.16":"2026-09-15T20:04:53.684Z","0.0.17":"2026-09-18T16:31:56.849Z","0.0.18":"2026-09-23T12:44:47.588Z","0.0.19":"2026-09-27T23:44:05.339Z","0.0.20":"2026-09-29T12:46:50.641Z","0.0.21":"2026-10-01T22:06:59.419Z","0.0.22":"2026-10-02T06:38:22.451Z","0.0.23":"2026-10-02T06:55:16.960Z"},"bugs":{"url":"https://github.com/akalforge/supaforge/issues"},"author":{"name":"Akal Software Ltd"},"license":"MIT","homepage":"https://github.com/akalforge/supaforge#readme","repository":{"url":"git+https://github.com/akalforge/supaforge.git","type":"git","directory":"packages/cli"},"description":"Diff and sync your Supabase environments","maintainers":[{"name":"jasdeepkhalsa","email":"oss@akalforge.com"}],"readme":"# supaforge\n\n> Diff and sync your Supabase environments.\n\nBuilt by [Akal](https://github.com/akalforge).\n\n## Quick Start\n\n```bash\nnpm install -g @akalforge/supaforge\n\n# Interactive setup — creates supaforge.config.json\nsupaforge init\n\n# Check for drift\nsupaforge diff\n\n# See detailed SQL diffs\nsupaforge diff --detail\n\n# Fix the drift\nsupaforge diff --apply\n```\n\n## Single Database\n\nWorking with one Supabase project? Choose \"single\" mode during `supaforge init` to set up snapshot, clone, and restore workflows without needing a second environment.\n\n```bash\nsupaforge init                                       # Choose \"single\" mode\nsupaforge snapshot --env=prod --apply                # Capture current state\nsupaforge clone --env=prod --apply                   # Clone remote to local\nsupaforge snapshot --env=prod --migration --apply    # Capture + incremental migration\n```\n\n`snapshot` previews by default like everything else here, so leaving `--apply`\noff prints what it *would* capture and writes no snapshot.\n\n## Comprehensive Checks\n\n| Check | Source | Detection | Fix |\n|-------|--------|-----------|-----|\n| Schema | `@dbdiff/cli` | ✅ Tables, columns, indexes, constraints, views, triggers, functions, standalone sequences, enum and composite types, domains, materialized views | SQL (up/down) |\n| Data | `@dbdiff/cli --type=data` | ✅ Row-level diff for all public tables (configurable). A content digest per table (`md5` of every row, order-independent) decides which tables to skip — a row count alone read an edited row as unchanged | SQL (up/down) |\n| RLS Policies | `pg_policies` view | ✅ Compared by what PostgreSQL makes of each expression, not its text — a policy restored from a dump renders differently and is still the same policy. Also applies to storage and Realtime Authorization policies | SQL (up/down) |\n| Edge Functions | Management API (hosted), Studio's `/api/v1/projects/{ref}/functions` (self-hosted), or the functions directory | ✅ Hosted and **self-hosted**, comparing module contents | DELETE extras via API (hosted); otherwise guidance to `supabase functions deploy` |\n| Storage | Storage API + `pg_policies` | ✅ Buckets (`public`, `type`, `file_size_limit`, `allowed_mime_types`, `avif_autodetection`; `owner_id` reported only), analytics and vector buckets, policies. Skipped when either side has no `storage` schema. `--include-files` adds file-level drift detection (checksums for JSON, size/date for binary) — **detection only, files are never transferred**. | Buckets via API (POST/PUT/DELETE); Policies via SQL |\n| Auth Config | Management API, or GoTrue `/auth/v1/settings` when `apiUrl` is set | ✅ Self-hosted covers provider flags and signup settings, not `JWT_EXP` / `MFA_ENABLED` | PATCH via API (hosted only) |\n| Cron Jobs | `cron.job` table | ✅ | SQL (up/down) |\n| Webhooks | `pg_trigger` + `pg_net` | ✅ Every trigger calling `supabase_functions.http_request`, compared on its full definition — so the URL, method, headers, params and timeout count, not just the table and events | SQL (up/down) |\n| Realtime | `pg_publication` + `pg_publication_tables`, and `pg_policies` on `realtime` | ✅ Publications, plus **Realtime Authorization** policies on `realtime.messages` (who may join which channel) | SQL (CREATE/ALTER PUBLICATION; CREATE/DROP POLICY) |\n| Vault Secrets | `vault.secrets` | ✅ | A *modified* secret's name or description: SQL (`vault.update_secret`). A **missing** secret: none — reported with the `vault.create_secret` call to run by hand |\n| Postgres Extensions | `pg_extension` | ✅ | SQL (CREATE/DROP EXTENSION) |\n| Postgres Roles & Grants | `pg_roles` + `information_schema` grants | ✅ Custom role attributes and table grants. Supabase's platform roles (`postgres`, `supabase_admin`, `authenticator`, `supabase_*_admin`, `dashboard_user`, `pgbouncer`, `supavisor`, `pg_*`) are excluded throughout. The Data API roles (`anon`, `authenticated`, `service_role`) are excluded from *attribute* comparison — their attributes are the platform's — but their **grants are compared**, because revoking `anon`'s access to a table is exactly the drift worth catching | SQL (CREATE/ALTER ROLE, GRANT/REVOKE) |\n| RLS Coverage | `pg_class.relrowsecurity` | ✅ Tables with RLS disabled — **reads the target only** | SQL, held back unless `--apply-posture` |\n| Migration History | `supabase_migrations.schema_migrations` | ✅ Local migration files with no tracking row — **reads the target only** | SQL (records the file as applied without running it), held back unless `--apply-posture` |\n\nThe last two compare nothing: they judge the target on its own and fire\nidentically whichever pair you diff, so they are scored apart and their fixes\nare not applied by default. See\n[Drift score vs posture score](#drift-score-vs-posture-score).\n\n### Scoping a diff to specific tables\n\n`--check` / `--skip` select whole layers. `--tables` / `--exclude-tables` scope\n*within* the schema and data layers, which is what makes it possible to promote\na reviewed subset between environments rather than applying everything a layer\nfound:\n\n```bash\nsupaforge diff --tables=orders,order_items          # only these two\nsupaforge diff --tables='billing_*' --exclude-tables='*_audit'\nsupaforge diff --tables=orders --apply              # promote just this table\n```\n\nBoth flags are repeatable and comma-separated, and take the same `*` / `?`\nglobs `@dbdiff/cli` supports. The scope is enforced inside the diff itself —\ndbdiff is told what to compare — rather than by generating everything and\ndiscarding findings afterwards.\n\nThe equivalent config keys make a scope repeatable:\n\n```json\n{\n  \"checks\": {\n    \"tables\": [\"orders\", \"order_items\"],\n    \"excludeTables\": [\"*_audit\", \"*_log\"]\n  }\n}\n```\n\n**Precedence.** `--tables` *overrides* `checks.tables` — asking for one table on\nthe command line must not be widened by a broader list in config.\n`--exclude-tables` is *unioned* with `checks.excludeTables`, the same way\n`--skip` merges with `checks.exclude`: an exclusion is a safety rail, so both\nsources excluding more is never the surprising direction.\n\n**Which layers it reaches.** A table is a concept the schema and data checks\nhave and the others do not, so `diff --tables=orders` still compares every RLS\npolicy, storage bucket and cron job. A scoped run says so before it starts:\n\n```\n  Scoped to only orders — applies to the schema and data checks; other layers are unfiltered.\n```\n\n**Dependants of an excluded table.** dbdiff's `--tables` covers *tables*, so a\nscoped fix set still arrives carrying the views, triggers and indexes hanging\noff the tables it excluded. With `--apply`, those are skipped with a reason\nnaming the table rather than attempted and failed:\n\n```\n○ [schema] schema-create-view-2: Depends on table 'orders', excluded by --tables\n```\n\n**Scoping to something other than a table.** `--tables` has no way to express\n\"these two tables and these three functions\". `--only` does, by taking the\nissue ids `--json` already reports:\n\n```bash\nsupaforge diff --check=schema --json > plan.json\nsupaforge diff --apply --only=schema-create-function-7,schema-create-trigger-6\nsupaforge diff --apply --only='schema-create-*'\n```\n\nCombine with `--check=schema` when you want the run itself narrowed too.\n\n### Drift score vs posture score\n\nTwelve of the fourteen checks compare source against target. Two do not:\n\n- **RLS Coverage** reads only the target, listing tables with RLS disabled.\n- **Migration History** compares local migration *files* against the target's\n  tracking table.\n\nBoth fire identically whichever pair you diff, so they are scored separately.\nCounting them as drift meant a diff of an environment *against itself* could\nnever reach 100, and any project with a long-standing RLS gap scored 0 no\nmatter how well synchronised its environments were:\n\n```\nSupaForge scan complete: no drift detected. ✓\n9 posture findings (RLS coverage / migration history) — present regardless of which pair you diff.\n\n  ✓ Layer 1 (Schema):                   0 issues\n  ● Layer 3 (RLS Coverage):             8 issues[CRITICAL]\n  ● Layer 13 (Migration History):       1 issue[INFO]\n\nDrift score: 100/100\nPosture score: 0/100 (target only — RLS coverage, migration history)\n```\n\nThe findings are not discarded or downgraded — they keep their severity and\nappear in `--detail`, in `--ci`'s `criticalIssues`, and in the GitHub\nannotations. Only the drift score changes, so `no drift detected` becomes a\ntrustworthy synchronisation signal. `--ci` output carries `postureScore`\nalongside `score`.\n\n#### They do not decide the exit code\n\nThe scope that keeps them out of the drift score keeps them out of the exit code\ntoo. Gating on the combined critical count meant a diff of an environment\nagainst *itself* exited 1 forever: these checks fire identically whichever pair\nyou diff, so a long-standing RLS gap failed every sync check while the report\ndirectly above it said `no drift detected`. `--fail-on` could not express the\ndifference, because the difference is one of scope, not severity.\n\n```bash\nsupaforge diff                      # exits 0: environments agree\nsupaforge diff --fail-on-posture    # exits 1: gate on posture findings too\n```\n\n`--fail-on-posture` works in `--ci` as well, and the `--fail-on` threshold\napplies within that scope — `--fail-on=warning --fail-on-posture` fails on a\nposture warning, plain `--fail-on-posture` only on a critical one.\n\nA run that would have exited 1 before and now exits 0 says so, once:\n\n```\n  Posture findings do not affect the exit code — they describe the target,\n  not drift from the source. Add --fail-on-posture to gate on them.\n```\n\n#### Their fixes are held back too\n\n`--apply` skips posture fixes, for the same reason they are scored apart: they\nchange the target without reference to the source, so applying one *creates*\ndrift. Enabling RLS on a target whose source has it disabled means the next\ndiff reports schema drift and the next sync turns it back off — the pair\noscillates and never converges.\n\n```\nSkipped 2 issue(s):\n  ○ [rls-coverage] rls-coverage-public.customers: Posture finding about the target,\n    not drift from the source — applying it would create drift.\n    Use --apply-posture to apply anyway.\n```\n\n```bash\nsupaforge sync --apply-posture              # enable RLS, record migrations, deliberately\nsupaforge diff --check=rls-coverage --apply # naming the check counts as asking\n```\n\nAsking for the check by name is treated as intent, so an RLS rollout does not\nneed the extra flag. What `--apply-posture` prevents is a *general* sync quietly\nreaching for fixes that undo themselves.\n\n### Self-hosted Supabase\n\nSet `apiUrl` on an environment and every API-backed check targets that gateway\ninstead of `api.supabase.com`, authenticating with the service-role key in\n`accessToken`. `projectRef` is not required when `apiUrl` is set — it is only a\npath segment on a hosted URL that will not be called.\n\n```json\n{\n  \"environments\": {\n    \"self-hosted-a\": {\n      \"dbUrl\": \"$DB_URL_A\",\n      \"apiUrl\": \"https://supabase.example.com\",\n      \"accessToken\": \"$SUPABASE_SERVICE_KEY\"\n    }\n  }\n}\n```\n\nAll fourteen checks run against self-hosted. Edge Functions needs one extra\npiece of config: self-hosted Supabase exposes no \"list functions\" *management*\nendpoint, so point `studioUrl` at Studio (which serves the same shape) or\n`functionsPath` at the mounted directory — see\n[Edge Functions on self-hosted](#edge-functions-on-self-hosted). With neither\nset, the check reports\n\n```\n  ○ Layer 4 (Edge Functions):           skipped — Edge Functions comparison needs a source to read from on self-hosted. Set \"studioUrl\" on both environments…\n```\n\nrather than attempting a call that can only return `Unauthorized`. Add it to\n`checks.exclude` if you would rather not see the line at all.\n\nAuth Config reads GoTrue's `/auth/v1/settings` on self-hosted, which exposes\nfewer keys than the hosted Management API's `/config/auth` — provider flags and\nsignup settings, but not `JWT_EXP` or `MFA_ENABLED`. Because the two shapes are\nnot comparable, a self-hosted source and a hosted target are reported as\nskipped rather than diffed against each other. Self-hosted GoTrue also has no\nconfig write endpoint, so its findings carry no `--apply` action: change the\ntarget deployment's environment and restart it.\n\n### Skipped checks\n\nA check that cannot run — no credentials configured, an extension absent, no\ntables listed to compare — is reported as **skipped with the reason**, never as\na clean pass:\n\n```\n  ✓ Layer 1 (Schema):                   0 issues\n  ○ Layer 4 (Edge Functions):           skipped — no projectRef or accessToken configured\n  ○ Layer 6 (Auth Config):              skipped — no projectRef or accessToken configured\n  ✓ Layer 7 (Cron Jobs):                0 issues\n  ○ Layer 8 (Reference Data):           skipped — no tables configured in checks.data.tables\n\nDrift score: 100/100 (2 of 5 checks compared)\n```\n\nThe closing line says `N checks were skipped — coverage is partial`, and the\nscore carries the denominator it was computed over, so a perfect number across\na partial run cannot be read as a full comparison.\n\nA skip is not drift and does not reduce the score or fail CI — penalising it\nwould give every self-hosted project a permanently depressed score for layers\nit deliberately cannot run. `--ci` output carries a `skipped` array and a\n`coverage` object alongside the existing `errors` array, so a pipeline can gate\non coverage explicitly if it wants to:\n\n```json\n{\n  \"score\": 100,\n  \"coverage\": { \"compared\": 2, \"total\": 5 },\n  \"skipped\": [\n    { \"check\": \"auth\", \"reason\": \"no projectRef or accessToken configured\" }\n  ],\n  \"errors\": []\n}\n```\n\n## Commands\n\n```\nsupaforge init                          Create supaforge.config.json interactively\nsupaforge init --force                  Overwrite existing config file\n\nsupaforge diff                          Summary: what's drifted? (score + pass/fail)\nsupaforge diff --detail                 Show detailed SQL diffs\nsupaforge diff --apply                  Apply SQL + API fixes to the target environment\nsupaforge diff --dry-run                Print the fixes in execution order, run nothing\nsupaforge diff --apply --allow-destructive  Also apply fixes that drop tables/columns\nsupaforge diff --apply --no-transaction Apply statement by statement, keeping partial progress\nsupaforge diff --apply --only=<id,...>  Apply only these issue ids (globs allowed)\nsupaforge diff --check=rls              Limit to a specific check\nsupaforge diff --check=rls --apply      Fix only one check\nsupaforge diff --skip=storage           Skip a specific check\nsupaforge diff --skip=auth --skip=vault Skip multiple checks (flag is repeatable)\nsupaforge diff --tables=orders,items    Scope the schema and data checks to these tables\nsupaforge diff --exclude-tables='*_log' Exclude tables from those checks (repeatable)\nsupaforge diff --include-files          Include file-level storage drift detection\nsupaforge diff --apply --prove          Replay on a throwaway clone before applying\nsupaforge diff --apply --apply-posture  Also apply target-only (posture) fixes\nsupaforge diff --ci                     CI mode: annotations + semantic exit codes\nsupaforge diff --ci --fail-on=warning   Fail on WARNING as well as CRITICAL\nsupaforge diff --fail-on-posture        Let target-only findings set the exit code\nsupaforge diff --json                   Output as JSON\nsupaforge sync                          Alias for diff --apply\nsupaforge hukam                         Alias for diff 🙏\n\nsupaforge snapshot                      Preview what a 12-layer snapshot would capture\nsupaforge snapshot --apply              Capture it\nsupaforge snapshot --env=prod --apply   Snapshot a specific environment\nsupaforge snapshot --migration --apply  Capture + generate incremental migration diff\nsupaforge snapshot --list               List all snapshots\nsupaforge snapshot --prune              Preview old snapshot cleanup (keeps last 7)\nsupaforge snapshot --prune --apply      Delete old snapshots\nsupaforge snapshot --prune --keep=5     Keep last 5 instead of 7\nsupaforge snapshot --output=<dir>       Write snapshots somewhere other than .supaforge\n\nsupaforge clone --env=prod              Preflight checks (validates connectivity)\nsupaforge clone --env=prod --apply      Clone remote to local (snapshot + baseline)\nsupaforge clone --env=prod --force      Force re-clone (drop existing DB)\nsupaforge clone --env=prod --start-local  Auto-start a local PostgreSQL container\nsupaforge clone --schema-only --apply   Clone schema only, no data\nsupaforge clone --local-url=<url>       Point at a local server other than :5432\nsupaforge clone --local-db=<name>       Name the local database (default supaforge_local)\nsupaforge clone --list                  List existing clones\nsupaforge clone --delete=<name>         Preview clone deletion\nsupaforge clone --delete=<name> --apply Drop database and remove tracking\n\nsupaforge restore --env=local --from-snapshot=latest          Preview snapshot restore\nsupaforge restore --env=local --from-snapshot=latest --apply  Rebuild the target from a snapshot\nsupaforge restore --env=local --from-migrations --apply       Replay migration history\nsupaforge restore --env=local --from-snapshot=latest --force  Allow a non-empty target\nsupaforge restore --env=local --from-snapshot=latest --no-transaction  Keep whatever succeeds\nsupaforge restore --env=local --from-migrations --from=<ver> --to=<ver>  Replay a version range\n\nsupaforge migrate create --name=add_orders  Generate a migration from schema drift\nsupaforge migrate list                  List local migrations, applied and pending\nsupaforge migrate list --offline        List without querying the target\nsupaforge migrate run --dry-run         Preview which migrations would run\nsupaforge migrate run                   Execute pending migrations\nsupaforge migrate run --up-to=003       Stop after a given migration\nsupaforge migrate run --allow-destructive  Permit migrations that drop, delete or truncate\nsupaforge migrate baseline              Mark local migrations applied without running them\n\nsupaforge report                        Recent command history from the local run log\nsupaforge report --last=20              Show more entries\nsupaforge report --send                 Choose entries to send as anonymous bug reports\n\nsupaforge mcp                           Start the MCP stdio server for AI agents\nsupaforge mcp --allow-config-path       Let a client choose which config to load (off by default)\n\nsupaforge help                          The command list\nsupaforge help <command>                Help for one command, e.g. `help migrate create`\n```\n\nTwo notes on the shape of that list. `migrate run` and `migrate baseline` are\nthe only state-changing commands that do **not** take `--apply`: `run` executes\nunless given `--dry-run`, and `baseline` writes tracking rows only, so it has no\npreview mode. And `report` is local — it reads `~/.supaforge/run-log.jsonl` and\nprints it. Only `report --send` leaves the machine, only for the entries you\nselect, and it shows exactly what would be transmitted before asking. No SQL,\ntable names or schema content is ever included.\n\n### Safe by Default\n\nCommands that modify databases preview what they would do first — the `migrate`\nfamily noted above being the exception. Add `--apply` to execute:\n\n```bash\n# Preview only (default)\nsupaforge diff\nsupaforge clone --env=prod\n\n# Actually execute\nsupaforge diff --apply\nsupaforge clone --env=prod --apply\n```\n\n**Destructive fixes need a second opt-in.** Drift that would destroy data, or\nwiden access, is always *reported*, but `--apply` skips it unless you also pass\n`--allow-destructive`. Six shapes are gated, and each says which it is rather\nthan all printing \"drops data\":\n\n| Statement | Reported as |\n| --- | --- |\n| `DROP SCHEMA` | drops a schema and everything in it |\n| `DROP TABLE` | drops a table and its rows |\n| `TRUNCATE` | deletes every row in a table |\n| `DROP COLUMN` | drops a column and its values |\n| `DELETE FROM` | deletes rows |\n| `DROP POLICY` | removes the policy …, which may widen access |\n\nA dropped policy is the one that is not about data at all: nothing is lost from\na table, and calling it \"drops data\" hid the part that matters — removing a\nRESTRICTIVE policy opens access up.\n\n```\n$ supaforge diff --apply\n\nApplied 1 fix(es):\n  ✓ [schema] schema-alter-1\n\nSkipped 2 issue(s):\n  ○ [schema] 2 issues: Destructive — drops a table and its rows; re-run with --allow-destructive to apply\n      drop-1, drop-2\n```\n\n```bash\n# Also apply those\nsupaforge diff --apply --allow-destructive\n```\n\nThe same gate applies to `migrate run`, which used to execute a `DROP TABLE` in\na migration file without asking:\n\n```bash\nsupaforge migrate run --allow-destructive\n```\n\nDropping a view, trigger, function, index or type is not gated — those lose a\ndefinition the migration can recreate, not data.\n\n**Fixes run in dependency order.** A function is created before the trigger\nthat executes it, a column before the index and view that read it, tables\nbefore their foreign keys, and dependants are dropped before what they depend\non. `@dbdiff/cli` emits statements in the order it walks the catalogue, and\napplying that order directly failed on sets that were perfectly valid. Preview\nthe order without running anything:\n\n```bash\nsupaforge diff --dry-run\n```\n\n```\nWould apply 3 fix(es), in this order:\n  1. [schema] schema-alter-2\n     ALTER TABLE \"orders\" ADD COLUMN \"status\" text DEFAULT 'pending'::text;\n  2. [schema] schema-create-function-7\n     CREATE OR REPLACE FUNCTION public.touch_updated() RETURNS trigger ...\n  3. [schema] schema-create-trigger-6\n     CREATE TRIGGER trg_orders_touch BEFORE UPDATE ON public.orders ...\n\n  Nothing was executed. Drop --dry-run to apply.\n```\n\n**Replacing an object is one fix, not two.** Sending drops to the end would be\nthe wrong call when a drop and a create are two halves of replacing *the same\nobject*. An enum whose values changed arrives from `@dbdiff/cli` as `DROP TYPE`\n+ `CREATE TYPE`, and ordering the create first failed with\n`type … already exists`, rolling the whole transaction back\n([#81](https://github.com/akalforge/supaforge/issues/81)). Such a pair is\nrecognised and merged into a single fix that drops and recreates in that order,\nso it is never split across the ordering — functions, procedures, types, domains\nand sequences alike. It reports as `Type modified: public.order_status` rather\nthan as an unrelated drop and create.\n\n`--dry-run` needs no `--apply`: previewing should not require typing the flag\nthat writes. The flags that shape *what* would be applied — `--only`,\n`--allow-destructive`, `--apply-posture` — take effect under it, so a scoped plan\ncan be reviewed before it is run:\n\n```bash\nsupaforge diff --dry-run --only='schema-create-*'\n```\n\nThe flags that only mean something while executing — `--prove`,\n`--no-transaction` — say so rather than doing nothing quietly:\n\n```\n  --prove has no effect without --apply. Add --apply for it to take effect.\n```\n\nThat warning goes to stderr, so `--json` and `--ci` stdout stay parseable.\n\n**An apply is all-or-nothing.** PostgreSQL supports transactional DDL, so the\nSQL fix set runs in one transaction: if any statement fails, every statement is\nrolled back and the target is left exactly as it was. A partial apply would\nleave a shared environment matching neither the source nor its own previous\nstate.\n\n```\nRolled back 5 fix(es) — the target is unchanged:\n  ↩ [schema] schema-alter-3\n  ...\n1 error(s):\n  ✗ [schema] schema-create-view-6: relation \"active_orders\" already exists\n\n  Nothing was written. Re-run with --no-transaction to apply the fixes that do work.\n```\n\nPass `--no-transaction` (or its alias `--continue-on-error`) to run each fix on\nits own and keep whatever succeeds. API-based fixes — storage buckets, auth\nconfig — are not transactional, so they are not attempted at all when the SQL\nbatch rolls back.\n\n### Snapshot & Clone\n\n```bash\n# Capture a full snapshot of your remote Supabase (12 layers)\nsupaforge snapshot --env=prod --apply\n\n# With incremental migration diff (compares against previous snapshot)\nsupaforge snapshot --env=prod --migration --description=\"before-deploy\" --apply\n\n# Clone remote to local for development\nsupaforge clone --env=prod --apply\n\n# Manage clones\nsupaforge clone --list\nsupaforge clone --delete=my-clone --apply\n\n# Restore into a local database\nsupaforge restore --env=local --from-snapshot=latest --apply\n\n# Replay migration history\nsupaforge restore --env=local --from-migrations --apply\n```\n\n**Snapshots capture 12 layers**: schema, RLS policies, cron jobs, webhooks,\nextensions, storage (buckets + policies), auth config, edge functions,\nreference data, Realtime publications, Vault secret names, and role grants.\n\nThe schema layer is written twice, for two different jobs: `schema.json` is an\nintrospection document for diffing, and `schema.sql` — a `pg_dump\n--schema-only` per schema — is what a restore replays.\n\n#### What `restore --from-snapshot` puts back\n\nThese are replayed as SQL, in dependency order:\n\n| Replayed | From | Notes |\n| --- | --- | --- |\n| Extensions | `extensions.sql` | The schema each one wants is created first — `WITH SCHEMA \"extensions\"` has nothing to land in on plain PostgreSQL |\n| Schema | `schema.sql` | Tables, columns, indexes, constraints, views, functions, triggers, types |\n| RLS policies | `rls.sql` | |\n| Cron jobs | `cron.sql` | |\n| Webhooks | `webhooks.sql` | Triggers the schema dump already created are skipped rather than attempted twice |\n| Storage policies | `storage-policies.sql` | |\n| Realtime publications | `realtime.sql` | Membership is added conditionally, so restoring twice is not an error |\n| Role grants | `roles.sql` | A grantee the target has never heard of is created as `NOLOGIN` first |\n| Reference data | `data/*.sql` | The tables listed in `checks.data.tables` |\n\nFour things cannot be put back, and are **named in the output** with what to do\ninstead — a restore that lists what it did and says nothing about the rest reads\nas complete when it is not:\n\n| Not replayed | What to do |\n| --- | --- |\n| Auth config | Pass `--project-ref` and `--api-key` to restore it via the Management API |\n| Edge Functions | `supabase functions deploy` from your functions directory |\n| Storage **buckets** | The bucket rows are created over the Storage API. The policies *on* them are restored; objects are never transferred |\n| Vault secrets | `vault.sql` lists the names only. A secret's value cannot be read out of Vault, so each is recreated by hand with `vault.create_secret` — deliberately not runnable SQL, so a restore cannot invent a value |\n\nPreview it first, as with everything else — the preview lists each layer and\nthe statements it would run:\n\n```\nRestore preview (dry-run) -- from snapshot\n\n  Layer: extensions (1 statements)\n    CREATE EXTENSION IF NOT EXISTS \"pgcrypto\" WITH SCHEMA \"extensions\";\n\n  Layer: schema (18 statements)\n    SET statement_timeout = 0;\n    ... and 15 more\n\n  Layer: realtime (2 statements)\n    ALTER PUBLICATION \"supabase_realtime\" ADD TABLE \"public\".\"orders\";\n\n  Layer: roles (1 statements)\n    GRANT SELECT ON \"public\".\"orders\" TO \"anon\";\n\n  → Add --apply to execute the restore.\n```\n\n**A restore is one transaction.** A failure rolls the whole thing back and\nleaves the target exactly as it was, rather than half-rebuilt with the errors\nreported at the end. `--no-transaction` keeps whatever succeeded.\n\n**It expects an empty database** and refuses otherwise, because replaying a\nschema over existing tables fails on the first one. `--force` replaces instead:\nit clears the objects in the snapshot's own schemas, never a Supabase schema,\nand keeps the schemas so their grants and default privileges survive. Triggers\nand policies elsewhere that depend on what it clears (the `auth.users` trigger\ncalling `public.handle_new_user()` is the usual one) are recreated afterwards;\nif anything else outside would be lost, it refuses and changes nothing.\n\nObjects the platform owns — `pgbouncer.get_auth`, pg_cron's own policies on\n`cron.job` — are skipped with a reason, since no ordinary role can recreate\nthem. Into plain PostgreSQL, statements that only attach to something and need\na Supabase schema the target lacks — a foreign key to `auth.users`, a Database\nWebhook, a grant on `storage.objects`, a policy calling `auth.uid()` — are\nskipped and listed, as is an extension the server does not ship. A table that\ncannot be created still fails the restore.\n\nGrants come back exactly as captured. The schema is dumped without privileges,\nso a recreated table or view first gets whatever the target's default\nprivileges give (on Supabase, everything to `anon`); those are cleared before\nthe captured grants are replayed, so a view that had been revoked from `anon`\nstays revoked.\n\n**Snapshot pruning**: Use `--prune` to delete old snapshots, keeping the most\nrecent 7 (configurable with `--keep`). Preview mode by default — add `--apply`\nto execute. A snapshot an incremental migration was generated against is\nretained regardless of age: deleting it would leave that migration unable to\nsay what it was a diff *from*.\n\n**Migrations are incremental**: `--migration` diffs against the previous\nsnapshot and writes a migration file to `.supaforge/migrations/`, with both\ndirections filled in. `up` is what changed; `down` is the inverse of each\nstatement — `cron.unschedule` for a scheduled job, `DROP POLICY` for a created\npolicy, `DROP TRIGGER` for a webhook, `DROP EXTENSION` for an extension. A\nstatement with no safe inverse is left out of `down` rather than guessed at: a\nwrong inverse looks like a revert and is not.\n\nThe schema layer contributes comments rather than DDL, naming what moved:\n\n```sql\n-- The schema changed. This migration carries no DDL for it: deriving one\n-- from two introspection documents is what @dbdiff/cli does, and it needs\n-- two live databases. Generate it with `supaforge migrate create`.\n--   tables added:   public.order_items\n--   tables removed: public.legacy_orders\n--   views added:   public.order_summary\n```\n\nIt used to be the single line \"Schema changed. Use @dbdiff/cli to generate\nmigration SQL.\" — true, and useless, because it named nothing that had changed.\n\n**Clone preflight checks**: Before cloning, `supaforge clone` validates that the remote database is reachable, pg_dump is compatible, and the local PostgreSQL server is running. If port 54322 is unreachable, it hints to run `supabase start`.\n\n**What clone writes to your config.** It adds a `local` environment for the\nclone and points `source` at the environment it cloned *from* and `target` at\n`local` — so the obvious next step, comparing the clone against its origin,\nworks without editing anything, and a bare `diff --apply` writes into the clone\nrather than into the environment you just copied. It prints both moves, naming\nwhat each was before:\n\n```\n      ✓ Config updated: /path/to/supaforge.config.json\n      source: dev → prod\n      target: prod → local (the clone)\n      A bare supaforge diff --apply now writes into the clone, not into \"prod\".\n```\n\nOverwriting those silently was the other half of the problem: whatever the\nproject pointed at before was gone with no record of it in the output.\n\n## Configuration\n\nThe fastest way to get started:\n\n```bash\nsupaforge init          # Interactive wizard — creates config + .env\n```\n\nOr copy the annotated example files and fill in your values:\n\n```bash\ncp supaforge.config.example.jsonc supaforge.config.json\ncp .env.example .env\n```\n\n**Key fields**:\n\n| Field | Required | Description |\n|-------|----------|-------------|\n| `dbUrl` | Yes | PostgreSQL connection string. Use `$VAR` references for secrets. |\n| `projectRef` | No | Supabase Project URL (e.g. `https://xyz.supabase.co`) or bare ref. Enables API-based checks (auth, edge functions). |\n| `accessToken` | No | Supabase personal access token. Required when `projectRef` is set for Management API checks (auth config, edge functions). |\n| `apiUrl` | No | Base URL for self-hosted Supabase API gateway. Use instead of `projectRef` for local/self-hosted. |\n| `source` / `target` | Yes | Environment names to compare. Source = truth, target = to be synced. |\n| `checks.data.tables` | No | Tables to include in row-level data drift detection. |\n| `checks.exclude` | No | Checks to always skip (e.g. `[\"storage\",\"auth\",\"vault\"]`). Useful for clone environments where these checks produce noise. Can also be overridden per-run with `--skip`. |\n| `checks.migrations.dir` | No | Directory holding migration files. Defaults to `supabase/migrations`. |\n| `checks.migrations.mode` | No | How to report local migration files with no row in `schema_migrations` — `auto` (default), `warn`, or `ignore`. See [Migration history](#migration-history). |\n\n### Migration history\n\nLayer 13 compares migration files in `supabase/migrations/` against the\n`supabase_migrations.schema_migrations` table on the target.\n\nThat table is a Supabase CLI convention, not a database requirement. A project\nthat applies migrations another way — `psql`, the SQL editor, another migration\ntool — never populates it, so every local file looks unapplied. Reporting each\none individually is noise, not drift.\n\n`checks.migrations.mode` controls this:\n\n| Mode | Behaviour |\n| --- | --- |\n| `auto` *(default)* | If the tracking table is **empty** but local files exist, report a single INFO noting an untracked migration workflow. Otherwise warn per file. |\n| `warn` | Always warn per unrecorded file, even when nothing is tracked. |\n| `ignore` | Report nothing from this check. No migration directory read, no query. |\n\n```json\n{\n  \"checks\": {\n    \"migrations\": { \"mode\": \"ignore\" }\n  }\n}\n```\n\nThe collapse in `auto` only applies when *nothing at all* is tracked. A project\nthat records some migrations and missed others has genuine drift, and still gets\none actionable warning per missing file.\n\nTo adopt the tracking table rather than silence the check, `supaforge migrate\nbaseline` records existing files as applied without executing them.\n\nSensitive values (`dbUrl`, `accessToken`) support `$VAR` and `${VAR}` syntax — expanded from environment variables at runtime. Store actual credentials in `.env` (already in `.gitignore`).\n\n**`.env` auto-detection**: SupaForge automatically loads `.env` files following the Next.js / Vite / CRA convention:\n\n1. `.env.{NODE_ENV}.local`\n2. `.env.local`\n3. `.env.{NODE_ENV}`\n4. `.env`\n\nHigher-priority files win for duplicate keys. Existing `process.env` values are never overwritten.\n\nSee [`supaforge.config.example.jsonc`](supaforge.config.example.jsonc) and [`.env.example`](.env.example) for fully commented examples.\n\n## What Storage Compares\n\nBucket **metadata**, not the objects inside them:\n\n| Property | Reported as | Synced |\n|---|---|---|\n| `public` | critical if private→public, else warning | ✅ |\n| `type` (`STANDARD`/`ANALYTICS`/`VECTOR`) | **critical** — a different storage backend, not a setting | ✅ |\n| `file_size_limit`, `allowed_mime_types` | warning | ✅ |\n| `avif_autodetection` | warning | ✅ |\n| `owner_id` | info | ❌ — the owner is an identity local to its own project |\n\nColumns are resolved per connection, so an older Supabase without `type` or\n`owner_id` is compared on what it does have rather than failing.\n\n**Analytics and vector buckets** live in their own tables — `storage.buckets_analytics`\nand `storage.buckets_vectors` — not in `storage.buckets`, so they are compared\nseparately. Analytics buckets are matched on `name` rather than `id`: that `id`\nis a `gen_random_uuid()` default and differs between any two projects, so\nkeying on it would report every bucket as both missing and extra on every run.\nSoft-deleted analytics buckets (`deleted_at`) are excluded. Both tables are\nprobed first, so a Supabase predating them is unaffected.\n\n`created_at` and `updated_at` are ignored: they differ between any two\nenvironments and mean nothing.\n\n> **Files are never transferred.** `--include-files` lists objects and downloads\n> them to compute checksums so it can *report* drift. There is no upload, copy,\n> move or delete path for storage objects anywhere in SupaForge — syncing a\n> bucket row is cheap and reversible, overwriting user uploads is not.\n\n### Policies inside ignored schemas\n\nSupabase's own schemas are excluded from the RLS layer because their tables are\nproduct-managed — a difference there means the two projects run different\nSupabase versions, not that anyone changed anything.\n\nTwo of them hold policies **you** write, so those are compared anyway, by the\ncheck that owns them:\n\n| Policy location | Compared by | What it controls |\n|---|---|---|\n| `storage.objects` | Storage | who may read or write which files |\n| `realtime.messages` | Realtime | who may join which channel (Realtime Authorization) |\n\nA missing or altered policy is **critical** — for these schemas it is an access\nrule. An extra policy is **info**: it may be deliberate, and removing it is a\njudgement call rather than a fix to apply blindly.\n\n### Ignored schemas\n\nSupabase-managed schemas are excluded by default, so findings are limited to\nthings you can actually change:\n\n`auth`, `storage`, `realtime`, `_realtime`, `vault`, `net`, `graphql_public`,\n`supabase_migrations`, `pgsodium`, `pgtle`, `supabase_functions`, `extensions`\n\nNote `_realtime` (underscore) is a *different* schema from `realtime` — its\ntables are owned by `supabase_admin`, so RLS findings on them are not\nactionable. Override with `ignoreSchemas` in your config.\n\n## Edge Functions on self-hosted\n\nHosted Supabase lists functions over the Management API. Self-hosted Studio\nserves **the same shape** at `/api/v1/projects/{ref}/functions`, so point\n`studioUrl` at it:\n\n```jsonc\n{\n  \"environments\": {\n    \"staging\": { \"dbUrl\": \"$STAGING_DATABASE_URL\", \"studioUrl\": \"http://staging-host:3000\" },\n    \"prod\":    { \"dbUrl\": \"$PROD_DATABASE_URL\",    \"studioUrl\": \"http://prod-host:3000\" }\n  }\n}\n```\n\nNote this is **Studio's** port, not the Kong gateway `apiUrl` points at — Kong\nreturns 401 for that path even with a service-role key. The project ref is\n`default` on self-hosted unless you set `projectRef`.\n\n> **Studio's functions API has no authentication** on the versions tested — a\n> publicly reachable Studio exposes function *source* via the `/body` endpoint\n> to anyone who asks. Worth checking before you expose one.\n\n### Falling back to the directory\n\nWhen Studio is not reachable, point `functionsPath` at the directory the\nfunctions are mounted from instead — one subdirectory per function:\n\n```jsonc\n{ \"environments\": { \"local\": { \"dbUrl\": \"…\", \"functionsPath\": \"./supabase/functions\" } } }\n```\n\nEach environment resolves independently, so the two can be **mixed**: compare a\nlocal checkout against a live instance by giving one `functionsPath` and the\nother `studioUrl`. Both hash modules identically — sorted by filename, hashing\nfilename then contents — so the results are directly comparable.\n\n`main` is excluded: it is edge-runtime's router (`--main-service`), present on\nevery self-hosted instance and deployed by nobody. Studio's API omits it too,\nso including it reported a phantom \"Missing Edge Function: main\". Underscore\ndirectories (`_shared`) are excluded for the same reason, along with\n`.DS_Store`, `Thumbs.db` and `.gitkeep`.\n\nOnly hashes are reported, never source: what matters is that they differ, and\nfunction code can contain secrets that have no business in a drift report.\n\nNothing is applied automatically — deploying needs the Supabase CLI and, on\nself-hosted, an edge-runtime restart. Each issue carries the command to run\ninstead, because reporting a fix that cannot be applied is worse than admitting\nthere is not one.\n\n## Proving a migration before you run it\n\nA migration that executes without error can still leave the database in a state\nthat is not the source. A partitioned table rebuilt as an ordinary one, an enum\ncolumn that lost its type, an index that never reached its partitions — all\napply cleanly and all produce the wrong database. No comparison of the generated\nSQL can catch that, because the SQL is valid.\n\n`--prove` replays the migration on a throwaway clone of the target and compares\nthe result against the source:\n\n```bash\nsupaforge sync --prove          # sync already implies --apply\nsupaforge diff --apply --prove  # same thing, spelled out\n```\n\n```\nProving convergence on a throwaway clone…\nMigration does not reproduce the source. Nothing was applied.\n\n  missing: idx public.sales_2026_d_idx CREATE INDEX sales_2026_d_idx ON public.sales_2026 …\n\nThese objects would still differ after applying.\n```\n\nThe target is never touched when the proof fails — it exits 1 having applied\nnothing. On success it reports `Converged` and proceeds.\n\nWhen the clone and the source differ, the source is copied onto the same\nserver the way the clone was and compared again, so both sides have been\nthrough the same dump and restore. PostgreSQL does not render every expression\nthe same way twice — `status IN ('draft', 'active')` on a `varchar` column\ncomes back as an equivalent but differently written `ARRAY` expression — and\ncompared with the source as written, every correct migration creating such a\nCHECK, partial index or policy was refused.\n\nThe clone also gets the structure of any schema the proved ones lean on — a\ntable referencing `auth.users`, a policy calling `auth.uid()` — so a real\nSupabase project can be proved at all; before, those failed with `schema\n\"auth\" does not exist`. Only the proved schemas are compared.\n\nThe clone is created on the target's own server (no extra credentials), holds\nstructure only (no data is copied), and is dropped even if the proof throws. It\nreceives only the schemas being compared — `public` unless you say otherwise —\nso it costs seconds rather than minutes, and the extensions those schemas\nreference are installed first, since a column default calling\n`extensions.uuid_generate_v4()` cannot be created without them.\n\n**A client newer than the server is fine.** `pg_dump` writes its preamble for\nits own version, so a PostgreSQL 17+ client dumping a 15 or 16 server emits\n`SET transaction_timeout = 0;`, which that server rejects. The preamble is\nfiltered against the destination's own `pg_settings` before replay, which makes\nthe common pairing — a Homebrew or distro client on 17/18, Supabase on 15 —\nwork without installing anything. Nothing else about the replay is relaxed:\n`ON_ERROR_STOP` stays on, so a genuine failure still fails the proof rather than\nbeing skipped past.\n\nIf the proof cannot run at all — no `pg_dump`, or the role lacks `CREATEDB` —\nthat is reported as *not proven* and the apply continues. Not being able to\ncheck is a different thing from checking and failing, and conflating them would\neither block legitimate work or hide real failures. A clone that *can* be made\nbut cannot be built — the structure would not replay — is neither: it blocks the\napply, because what the migration would do is then unknown.\n\n> Order matters more than it looks. `CREATE INDEX … ON ONLY parent` is correct\n> when the index is created *before* partitions attach, because PostgreSQL\n> propagates it to partitions added later — and wrong when they already exist.\n> Nothing in the SQL text distinguishes the two. This is exactly the class of\n> problem that only replaying can decide.\n\n## Exit Codes\n\nEvery command follows the same contract, so a pipeline can branch on the exit\ncode without parsing output:\n\n| Code | Meaning |\n|------|---------|\n| `0` | Did what was asked — including when there was nothing to do |\n| `1` | Ran, but declined to act, found drift above the threshold, or an operation failed |\n| `2` | Could not run: a usage error, or (in `--ci`) a check that could not complete |\n\nThe distinction that matters is between **0** and **1**. A command that refuses\nto act is not a success:\n\n```bash\n# Refused because the target is not empty — exits 1, so this does NOT deploy\nsupaforge restore --env=prod --from-snapshot=latest --apply && ./deploy.sh\n```\n\n`--ci` gives `diff`, `sync` and `hukam` a stricter contract, with the drift\nthreshold under your control:\n\n```bash\nsupaforge diff --ci                      # 1 only on CRITICAL drift (default)\nsupaforge diff --ci --fail-on=warning    # 1 on CRITICAL or WARNING\nsupaforge diff --ci --fail-on=any        # 1 on any issue at all\nsupaforge diff --ci --fail-on-posture    # also gate on the target-only checks\n```\n\n**Drift, not posture.** The threshold applies to the twelve checks that compare\nthe two environments. RLS Coverage and Migration History report on the target\nalone, so they are reported but do not set the exit code unless\n`--fail-on-posture` asks them to — see\n[Drift score vs posture score](#drift-score-vs-posture-score).\n\nIn `--ci` mode a check that **could not complete** exits `2` rather than `0`,\nbecause unmeasured is not the same as clean. Outside `--ci` those commands\nreport the problem in their output but keep their exit code, so existing\nnon-CI callers are unaffected — use `--ci` when a script depends on the result.\n\n## Workflows\n\n### Multi-DB: Compare Two Environments (Remote ↔ Remote)\n\nThe primary use case — detect drift between `dev` and `prod` (or `staging` and `prod`, or any two environments):\n\n```bash\n# 1. Set up config with source + target\nsupaforge init            # Choose \"multi\" mode, enter two environment URLs\n\n# 2. Check for drift (summary)\nsupaforge diff\n# Output:\n#   ✗ DRIFTED (Score: 42/100)\n#   ● Schema: 2 issues [CRITICAL]\n#   ● RLS:    3 issues [CRITICAL]\n#   ● Cron:   1 issue  [WARNING]\n#   → Run with --detail to see SQL · --apply to fix\n\n# 3. See the full SQL\nsupaforge diff --detail\n\n# 4. Apply fixes to the target\nsupaforge diff --apply\n\n# 5. Verify\nsupaforge diff\n#   ✓ SYNCED (Score: 100/100)\n```\n\n**Config** (`supaforge.config.json`):\n```json\n{\n  \"environments\": {\n    \"dev\": {\n      \"dbUrl\": \"$DEV_DATABASE_URL\",\n      \"projectRef\": \"dev-abc123\",\n      \"accessToken\": \"$SUPABASE_ACCESS_TOKEN\"\n    },\n    \"prod\": {\n      \"dbUrl\": \"$PROD_DATABASE_URL\",\n      \"projectRef\": \"prod-xyz789\",\n      \"accessToken\": \"$SUPABASE_ACCESS_TOKEN\"\n    }\n  },\n  \"source\": \"dev\",\n  \"target\": \"prod\",\n  \"checks\": {\n    \"data\": { \"tables\": [\"plans\", \"feature_flags\"] }\n  }\n}\n```\n\n### Diffing a Clone (Suppressing Expected Noise)\n\nAfter `supaforge clone`, the local copy has no Supabase-managed services\n(`storage`, `auth`, `edge-functions`, `vault`, `realtime`) — and, because it is\nvanilla PostgreSQL, none of Supabase's service roles either, so **Postgres Roles\n& Grants** (`roles`) reports every grant referencing one as drift. On a real\nclone → remote diff that was 227 findings, the second-largest source of clone\nnoise after schema; a remote-to-remote diff of the same pair reports zero,\nconfirming all of them are clone artefacts.\n\nRunning `diff` against a clone will always report drift on those six checks. Use\n`--skip` to suppress them:\n\n```bash\nsupaforge diff --skip=storage --skip=auth --skip=edge-functions --skip=vault --skip=realtime --skip=roles\n```\n\nOr lock the exclusions in config so you never have to repeat them:\n\n```json\n{\n  \"checks\": {\n    \"exclude\": [\"storage\", \"auth\", \"edge-functions\", \"vault\", \"realtime\", \"roles\"]\n  }\n}\n```\n\nBoth mechanisms merge — `--skip` on the CLI is unioned with `checks.exclude` from config.\n\n**Which way you are diffing changes what `--apply` means.** From a clone, it\nreshapes the remote to match a vanilla-PostgreSQL copy — dropping the roles,\ngrants and policies the clone never had. So in that direction the closing advice\nis a warning rather than a suggestion:\n\n```\n  → --apply would push this clone's shape onto the target, absences included —\n    preview it first with --apply --dry-run\n```\n\nThis no longer depends on the clone being one SupaForge recorded: the local\nclone manifest is per-directory and keyed by exact database name, so a clone\ndiffed from elsewhere used to read as an ordinary environment. SupaForge now\nalso asks the databases directly — a target carrying Supabase's schemas against\na source carrying none is the situation the warning is about, however that\nsource came to exist.\n\n### Single-DB: Snapshot, Clone, Restore (Local ↔ Remote)\n\nWorking with a single Supabase environment — no source/target pair needed:\n\n```bash\n# 1. Set up config with one environment\nsupaforge init            # Choose \"single\" mode\n\n# 2. Capture a full 12-layer snapshot\nsupaforge snapshot --env=prod --apply\n\n# 3. Track changes over time with incremental migrations\nsupaforge snapshot --env=prod --migration --description=\"before-deploy\" --apply\n\n# 4. Clone remote to local for development (requires supabase start)\nsupaforge clone --env=prod --apply\n\n# 5. Restore from a previous snapshot\nsupaforge restore --env=local --from-snapshot=latest --apply\n```\n\n**Config** (`supaforge.config.json`):\n```json\n{\n  \"environments\": {\n    \"prod\": {\n      \"dbUrl\": \"$PROD_DATABASE_URL\",\n      \"projectRef\": \"prod-xyz789\",\n      \"accessToken\": \"$SUPABASE_ACCESS_TOKEN\"\n    }\n  }\n}\n```\n\n**Available commands by config mode:**\n\n| Command | Multi-DB | Single-DB |\n|---------|----------|-----------|\n| `diff` | ✅ Compares source → target | ✗ Requires two environments |\n| `snapshot` | ✅ Any environment | ✅ |\n| `clone` | ✅ Any environment → local | ✅ |\n| `restore` | ✅ | ✅ |\n| `hukam` | ✅ Alias for diff | ✗ |\n\n### CI/CD Integration\n\n```yaml\n# .github/workflows/drift-check.yml\n- name: Check for drift\n  env:\n    DEV_DATABASE_URL: ${{ secrets.DEV_DATABASE_URL }}\n    PROD_DATABASE_URL: ${{ secrets.PROD_DATABASE_URL }}\n    SUPABASE_ACCESS_TOKEN: ${{ secrets.SUPABASE_ACCESS_TOKEN }}\n  run: npx supaforge diff --ci\n```\n\n`--ci` emits GitHub Actions annotations and exits 1 on CRITICAL drift, failing\nthe pipeline. Raise or lower the bar with `--fail-on=warning` / `--fail-on=any`,\nand see [Exit Codes](#exit-codes) for the full contract — notably that a check\nwhich could not complete exits 2 rather than passing quietly, and that the\ntarget-only checks are reported but do not fail the build unless you add\n`--fail-on-posture`. That last part is what keeps a long-standing RLS gap from\nfailing every drift check you run.\n\nNote `--check` is a different flag: it takes a check *name* (`--check=rls`) and\nlimits the run to that layer.\n\n## Extending with Hooks\n\nSupaForge includes a lightweight hook bus for extensibility:\n\n```typescript\nimport { HookBus, scan, createDefaultRegistry, loadConfig } from '@akalforge/supaforge'\n\nconst bus = new HookBus()\n\nbus.on('supaforge.scan.before', (ctx) => {\n  console.log(`Scanning ${ctx.config.source} → ${ctx.config.target}`)\n})\n\nbus.on('supaforge.check.after', ({ check, result }) => {\n  if (result.status === 'drifted') {\n    console.log(`⚠ Drift detected in ${check}`)\n  }\n})\n\nconst config = await loadConfig()\nconst registry = createDefaultRegistry()\nconst result = await scan(registry, { config }, bus)\n```\n\n## Development\n\n```bash\ncd packages/cli\nnpm install\nnpm test\n\n# Run in dev mode\n./bin/dev.js diff\n```\n\n### Integration Tests (Docker / Podman)\n\nIntegration tests run against real Supabase Postgres containers. The test script auto-detects Docker or Podman:\n\n```bash\n# Full flow: start containers → seed → test → teardown\nnpm run test:integration\n\n# Keep containers running for debugging\n./scripts/test-integration.sh --no-teardown\n\n# Force a specific compose command\nCOMPOSE_CMD=\"podman-compose\" npm run test:integration\n```\n\nYou can also start the containers manually and run the tests separately:\n\n```bash\n# Start containers (works with Docker Compose v2, docker-compose, or podman-compose)\ndocker compose -f tests/docker-compose.test.yml up -d\n\n# Wait for Postgres to be ready\nuntil psql postgresql://postgres:source-test-pass@localhost:15432/postgres -c 'SELECT 1' 2>/dev/null; do sleep 1; done\nuntil psql postgresql://postgres:target-test-pass@localhost:15433/postgres -c 'SELECT 1' 2>/dev/null; do sleep 1; done\n\n# Seed\npsql postgresql://postgres:source-test-pass@localhost:15432/postgres -f tests/fixtures/seed-source.sql\npsql postgresql://postgres:target-test-pass@localhost:15433/postgres -f tests/fixtures/seed-target.sql\n\n# Run integration tests\nSUPAFORGE_TEST_SOURCE_URL=postgresql://postgres:source-test-pass@localhost:15432/postgres \\\nSUPAFORGE_TEST_TARGET_URL=postgresql://postgres:target-test-pass@localhost:15433/postgres \\\nnpx vitest run --config vitest.integration.config.ts\n\n# Teardown\ndocker compose -f tests/docker-compose.test.yml down -v\n```\n\n### CLI e2e Tests\n\n```bash\nnpm run test:e2e\n```\n\n### E2E Tests (Supabase)\n\nFull end-to-end tests against two real Supabase local instances (source = dev, target = prod). Tests the\ncomplete scan → promote → re-scan roundtrip for RLS, Cron, Webhooks, and Storage checks.\n\n**Requirements**: Supabase CLI, Docker (or Podman with docker compat), psql, curl.\n\n```bash\n# Full flow: start instances → seed → test → teardown\nnpm run test:e2e:supabase\n\n# Keep instances running for debugging\n./scripts/test-e2e.sh --no-teardown\n\n# Reuse already-running instances\n./scripts/test-e2e.sh --skip-start\n```\n\nPort allocation:\n- Source: API 54321, DB 54322\n- Target: API 55321, DB 55322\n\n### @dbdiff/cli Integration\n\nThe Schema and Data checks are powered by [`@dbdiff/cli`](https://github.com/DBDiff/DBDiff). It is included as a dependency and installed automatically — no separate install needed. The native binary runs without PHP.\n\n```bash\nsupaforge diff                # schema + data checks active out of the box\n```\n\nThe adapter (`src/dbdiff.ts`) resolves the local `@dbdiff/cli` binary, invokes it directly (no `npx`), and parses the UP/DOWN marker output into `DriftIssue` objects.\n\n**What the schema layer reaches.** `3.0.0-rc.18`, the pinned version, models\ncomposite types, domains, materialized views (and their indexes), standalone\nsequences and RLS policies — five kinds that earlier releases did not read at\nall, and therefore reported as no drift whether they matched or not. A schema\nthat SupaForge has synced can now be diffed again and come back clean, which is\nwhat makes `--prove` meaningful.\n\n**Objects an extension owns are no longer compared.** An extension brings its\nown functions, tables and types — `CREATE EXTENSION pg_trgm` alone installs 31\nfunctions and a type into `public` — and those belong to the extension's\nversion, not to anything you wrote. Read as ordinary user objects, an extension\npresent on one side only produced a `CREATE OR REPLACE FUNCTION` for every\nmember, C-language ones included, which no managed-database role can run.\nSince rc.14 they are excluded via `pg_depend.deptype = 'e'`, the catalogue's own record of\nthat ownership, so what is left in the report is yours.\n\n**Migrations that run (rc.15–rc.18).** A column type change under a view, policy or\ntrigger condition now comes as one bracket — drop what reads the column, retype\nit, put everything back with its options, grants and comments — and SupaForge\nkeeps that bracket as one finding so it applies as a unit. An added enum label\nis `ALTER TYPE ... ADD VALUE` instead of replacing the type, and SupaForge\ncommits it ahead of the rest of an apply so the same apply can use it.\n`UNLOGGED` and storage parameters are compared, and a materialized view's\npopulation state no longer reads as drift between a project and a restored\ncopy. Since rc.16, a partitioned or inherited column is retyped once, through\nits parent; a stored generated column reading a retyped column is recomputed\nrather than blocking the change; and an expression PostgreSQL renders\ndifferently after a round trip — `status IN ('draft', 'active')` on a\n`varchar` column, in a CHECK, partial index, view or trigger condition — is no\nlonger reported as a change. Since rc.17, removing or reordering an enum's labels\nmoves its columns to a new type, keeping the rows and everything reading them;\nidentity changes are made in place, so the sequence carries on; a `serial`\ncolumn can become an identity and back; column storage and compression are\ncompared; and types and functions are created before, and dropped after,\nwhatever uses them, in both directions. Since rc.18, a foreign key onto\nanother schema's table is rendered with its schema and a multi-column key with\nall its columns; both used to produce SQL PostgreSQL rejected. So a key from\nyour schema onto an ignored one — a table referencing `auth.users` — is now\nsynced when the target has the table it references; until now such keys were\nfiltered out, because the SQL for them could not run.\n\n**One finding per change (rc.17).** Some changes are several statements that\nonly work together and in order — an enum whose labels are removed is moved to\na new type, a column type change stands its views aside, a generated column is\ndropped and re-added, a `serial` column becomes an identity. SupaForge asks\ndbdiff to mark each change (`--units`), so such a change is one finding that\napplies whole, and its DOWN is the same change's rather than whatever sat at\nthe same position. A column dropped and re-added in one change is not held\nback as destructive: it is recomputed, not lost. And when a table or column\n*is* held back, so is the drop of a type, domain, function or sequence it\nstill uses, and in turn what that keeps — otherwise that drop failed and took\nthe whole apply with it. Functions the source no longer has are dropped after\nthe defaults, constraints, indexes and tables that call them. An enum change\ndbdiff cannot carry out on its own (a function takes the type, a domain is\nbuilt on it) is reported as a manual step naming what is in the way, rather\nthan run and refused. With a dbdiff that predates\nthe markers, SupaForge reads the SQL statement by statement as before.\n\nOne consequence: a policy that differs is found by both the schema layer and the\nRLS layer. It is reported once, by the RLS layer — the finding that names the\npolicy's risk — and the schema layer's line says how many it left there:\n\n```\n  ● Layer 1 (Schema):                   0 issues  (+2 reported under RLS Policies)\n```\n\nOnly a schema finding that is nothing but policy statements is folded; one that\nalso creates the table the policy sits on is kept whole. Should both still reach\nan apply — with `--check=schema,rls`, say — the second `CREATE POLICY` is\ndropped rather than failing the transaction:\n\n```\n○ [rls] rls-missing-public.invoices.read_own: Already created by the schema fix for the same policy\n```\n\n**Overloaded functions.** Postgres lets several functions share a name with\ndifferent argument types. Each overload is compared and reported separately, and\nis identified by its signature — `Function modified: public.dist(text,text)` —\nso two overloads of one name are distinguishable in the output and the generated\nmigration touches only the one that actually drifted. This needs\n`@dbdiff/cli` 3.0.0-rc.7 or newer; earlier versions saw only one overload per\nname and missed drift in the rest.\n\n**Schema diff performance.** DBDiff compares table schemas in a constant number\nof round-trips rather than one set per table: it first hashes every table's\nschema in a single query per side and skips the ones that match, then loads the\nremainder in a fixed 7 queries per side. On a Supabase project where most tables\nare unchanged, this is the difference between thousands of round-trips and a\ncouple of dozen. Nothing to configure — it is on for Postgres automatically.\n\nrc.14 removed two more sources of latency, which matters most over a remote\nlink: every catalogue query cost three round trips under PDO's default named\nprepares, and the internal `pg_dump` dumped the whole database — a query per\nfunction, in every schema — when only `public` was being read. Measured over a\n100 ms link against 20 tables plus 300 functions in a non-public schema:\n\n| scenario | before | after |\n| --- | --- | --- |\n| two identical databases | 9.55 s, 90 round trips | 3.85 s, 34 |\n| one added table | 48.86 s, 477 round trips | 11.32 s, 107 |\n\nThe generated migration is byte-identical either way, comment headers aside.\n\nIf a diff still struggles on a very large schema, raise the ceilings rather than\nnarrowing the scan:\n\n| Variable | Default | Purpose |\n| --- | --- | --- |\n| `SUPAFORGE_DBDIFF_TIMEOUT` | `600` | Seconds before a diff is abandoned. Overrides `checks.schema.timeout` |\n| `SUPAFORGE_DBDIFF_MEMORY` | dbdiff's own `1G` | Passed to `--memory-limit`; takes `512M`, `2G`, or `-1` for unlimited |\n| `SUPAFORGE_CONNECT_TIMEOUT` | `15` | Seconds before a database connection attempt is abandoned. Applies to every connection, including the preflight reachability check |\n| `SUPAFORGE_CHECK_CONCURRENCY` | `4` | How many checks run at once. `1` restores running them one after another |\n\n```bash\nSUPAFORGE_DBDIFF_TIMEOUT=600 SUPAFORGE_DBDIFF_MEMORY=2G supaforge diff\n```\n\n**Connections and concurrency.** Queries share one connection pool per database\nrather than opening a connection per query, and the checks — which are\nindependent of one another — run four at a time, so a scan spends its latency in\nparallel rather than end to end. The concurrency limit is deliberate rather than\nunbounded: the schema and data checks each spawn `@dbdiff/cli`, and a Supabase\npooler counts every connection against your limit. Lower\n`SUPAFORGE_CHECK_CONCURRENCY` when diffing through a pooler with little\nheadroom; `=1` makes a run strictly sequential, which is occasionally easier to\nfollow when debugging one check.\n\nThe report is assembled by position, not by completion order, so it reads in\ncheck order however the checks happen to finish. Two paths still hold a\nconnection of their own by design: the preflight reachability probes, which have\ntheir own timeout, and an `--apply`, which is one connection for one transaction.\n\n**Per-environment overrides.** `checks.exclude` and `checks.schema.timeout` can\nbe set on an individual environment, applying when it is the diff target and\nunioned with the top-level config. Timeout precedence is\n`SUPAFORGE_DBDIFF_TIMEOUT` → `checks.schema.timeout` → the 600s default.\n\n**Progress.** On a TTY the schema check reports a live table counter while it\nruns, so a long diff reads as working rather than hung. Suppressed under\n`--json`, `--ci`, and when output is piped.\n\n**Destructive changes.** DBDiff refuses by default to generate a migration that\ndrops a table or column. SupaForge passes `--allow-destructive` when invoking it,\nbecause detecting an extra table on the target is the whole point of a drift\ncheck — reporting it must not be a hard failure. The safety gate is applied at\n*apply* time instead, in `promote()`, which skips those statements unless you\npass `--allow-destructive` to SupaForge itself. See\n[Safe by Default](#safe-by-default).\n\n## License\n\nMIT\n","readmeFilename":"README.md"}