{"_id":"@axeptio/provisioning","_rev":"13-cc99a972896ff42742a93fafb173714d","name":"@axeptio/provisioning","dist-tags":{"latest":"4.0.0"},"versions":{"1.0.0":{"name":"@axeptio/provisioning","version":"1.0.0","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@1.0.0","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"achalhii","email":"ilyesachalhi@gmail.com"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"0755d228f39e902612a24e115d8eeee8d1139b38","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-1.0.0.tgz","fileCount":2,"integrity":"sha512-QHco1vR1krLMvnBNIjfBBqXiTv1lRmUkL5p6hsBgFAbWNIh/1DcaX9dsMSWDSH3IqBOG1F4rdNZ9FFehL+pGHA==","signatures":[{"sig":"MEQCIENeGUSsF9B1dChvWYA+NDMa+jgmMS7iI3CfCQAlVi65AiB0lEgZ9KeCS0HN2oHZMKtR1qPNm9cVtDkEJgVmJlD/FQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":19627},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"3ead7fdf3320e16778d04791dee6b6e9ba7a5c1c","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"romainbessuges","email":"romainbessuges@gmail.com"},"_npmVersion":"10.9.3","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ora":"^5.4.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","fs-extra":"^11.2.0","cli-table3":"^0.6.3"},"_hasShrinkwrap":false,"devDependencies":{"ts-node":"^10.9.1","typescript":"^5.3.2","@types/node":"^20.10.0","@types/yargs":"^17.0.32","@types/fs-extra":"^11.0.4"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_1.0.0_1755869809993_0.9011124614506278","host":"s3://npm-registry-packages-npm-production"}},"1.1.0":{"name":"@axeptio/provisioning","version":"1.1.0","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@1.1.0","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"achalhii","email":"ilyesachalhi@gmail.com"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"1eb7f096d49bced9d3f64398b7940c1e8ea7c60f","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-1.1.0.tgz","fileCount":130,"integrity":"sha512-w3qizMCrfFAbvyg6/NHIki12G5l+5HTNPLy9xpFdqyeLuJ47V35L/lD0img6DWu4Hzkb/g0QQc7yGWgFkVtYOw==","signatures":[{"sig":"MEQCIBx3d6ML73lAml4VfB8Ei6N5+7/s4Fq8Vz9PAgaiJLWVAiBmcyLz5cKdKj/c+BaKdXVr5Mja5XlY6bkFuJ0cS734ew==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":462996},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"c970288fbd0cd5c78a07595f352b81158366633a","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","build":"tsc"},"_npmUser":{"name":"romainbessuges","email":"romainbessuges@gmail.com"},"_npmVersion":"10.9.3","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ora":"^5.4.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","fs-extra":"^11.2.0","cli-table3":"^0.6.3"},"_hasShrinkwrap":false,"devDependencies":{"ts-node":"^10.9.1","typescript":"^5.3.2","@types/node":"^20.10.0","@types/yargs":"^17.0.32","@types/fs-extra":"^11.0.4"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_1.1.0_1756979711114_0.4363212715293556","host":"s3://npm-registry-packages-npm-production"}},"2.0.0":{"name":"@axeptio/provisioning","version":"2.0.0","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@2.0.0","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"c3bd872801464074f93a1f543e8acfc7bd9fa604","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-2.0.0.tgz","fileCount":1183,"integrity":"sha512-wqRIKazHSIGH4YYfSsbM0t37SLUQqYML7KRs7FLHVKEUkeq5zTJn6WPaLZdfVbB8XzVBgCmDT8YaJpGKDRaQXQ==","signatures":[{"sig":"MEYCIQDV1zPLDEV9rcg+QrBEe85cHfUyliPlhibQWZRrG4ZUbAIhAKXnxSHs+TBgzR9jE2KdTwOhaxPBIduyUmyvfEzYPyXV","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6351822},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"dbfdc921131b65f27d5bbe6ae286cef04b60a683","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"romainbessuges","email":"romainbessuges@gmail.com"},"_npmVersion":"10.9.3","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","semantic-release":"^24.0.0","@jest/test-sequencer":"^30.2.0","@semantic-release/git":"^10.0.0","@semantic-release/github":"^10.0.0","@semantic-release/changelog":"^6.0.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_2.0.0_1776980157996_0.28585547180589255","host":"s3://npm-registry-packages-npm-production"}},"2.0.1":{"name":"@axeptio/provisioning","version":"2.0.1","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@2.0.1","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"e3bbfc3e8378677c129c7c79f4ba782b835f67cb","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-2.0.1.tgz","fileCount":1183,"integrity":"sha512-f84xvWtDLxb/ODNhNqTxypS8vgFuZeRW5nW0mQWJ+iIQZycJ6wn2ijQGdu2AbXmh51703tZ4HD8VcdopPBiwnQ==","signatures":[{"sig":"MEYCIQDseyjyHV0Ldhe0IeJunzGnBoGtrYPBBRucAKo2Nac7ZgIhAMFyZOgIVFTbHSH/PeZgS9IDdeiZ7Stfry11Vwu9jrSy","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6376771},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"0a4efdb955a0efd46aa32dd5e5a1303318eccd35","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"romainbessuges","email":"romainbessuges@gmail.com"},"_npmVersion":"10.9.3","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.18.0","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","semantic-release":"^24.0.0","@jest/test-sequencer":"^30.2.0","@semantic-release/git":"^10.0.0","@semantic-release/github":"^10.0.0","@semantic-release/changelog":"^6.0.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_2.0.1_1777039456639_0.6368914764168849","host":"s3://npm-registry-packages-npm-production"}},"3.0.0":{"name":"@axeptio/provisioning","version":"3.0.0","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@3.0.0","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"b50b6db1d76824222776aea83831618be65d7958","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-3.0.0.tgz","fileCount":1179,"integrity":"sha512-3tvXrwBT55tcmM//fFwbR36CIvRyDt/7RayVB6wN6QEr8fKOPT3BwfimPGLp1Nq78ApnbT8rEwBLyg35sGh3OA==","signatures":[{"sig":"MEUCIQCuM0sGxRYuqikGQefLSwC69Y+oWfSSOCOahbWAPnBElgIgIvFqF5jnH31Tk54Lzmt+f2EWUG3dSE7baQu5RUOg8nI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6379106},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"ee8a586d7c8039c3a91cae064af7ed796f040111","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"axeptiotech","email":"tech@axeptio.eu"},"_npmVersion":"10.9.8","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","semantic-release":"^24.0.0","@jest/test-sequencer":"^30.2.0","@semantic-release/git":"^10.0.0","@semantic-release/github":"^10.0.0","@semantic-release/changelog":"^6.0.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_3.0.0_1784012947687_0.44326299188410334","host":"s3://npm-registry-packages-npm-production"}},"3.0.2":{"name":"@axeptio/provisioning","version":"3.0.2","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@3.0.2","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"5bc02ce4374ef76d5150601cb6b5e79b2c3f2032","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-3.0.2.tgz","fileCount":1183,"integrity":"sha512-okkVcx+4DPDP1HzxMqQzUOSK55IA7q/PVbRMhbTHHqi6f8oolH9RV1M35LLu5sAhI0aU8nBPt2WI4qWvyD5pvA==","signatures":[{"sig":"MEUCIQCZi24jTQTExfvDXe+uZMWulNzPYA7GilFZXlUYXknsJgIgfM/wBk1Ve8DtBtvICFv82U+JIzFJBixb6kYod6eJ6pA=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6394877},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"11be3cc2c708e8cda4d59b89ec85a253aa585c21","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"axeptiotech","email":"tech@axeptio.eu"},"_npmVersion":"10.9.8","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","semantic-release":"^24.0.0","@jest/test-sequencer":"^30.2.0","@semantic-release/git":"^10.0.0","@semantic-release/github":"^10.0.0","@semantic-release/changelog":"^6.0.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_3.0.2_1784725554530_0.4562616283508252","host":"s3://npm-registry-packages-npm-production"}},"3.0.3":{"name":"@axeptio/provisioning","version":"3.0.3","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@3.0.3","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"379e0d75e7fe39d076ba920b01d755f3ee99e422","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-3.0.3.tgz","fileCount":1183,"integrity":"sha512-5UfvopJW/YbWvaqgD3FAm0YGOEDDG2zs4QLlOcOJUfRZqMJczyn2ynD3ipj6BO0XZzdhpmqZ2JrsX44BbSHdgA==","signatures":[{"sig":"MEUCIHNlX03KcYZ9mVLDK7vw2NkbliXFNfQxodETD28Bzhg0AiEA2JrNOdFWdXpZNq9jF/NvdNcU7KzVmeRffuSkypvxHP0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6394714},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"f60f2d0e25cd654a1e2c20e0caa49477757890fb","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"axeptiotech","email":"tech@axeptio.eu"},"_npmVersion":"10.9.8","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","@jest/test-sequencer":"^30.2.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_3.0.3_1785839440940_0.9023120463971863","host":"s3://npm-registry-packages-npm-production"}},"3.1.0":{"name":"@axeptio/provisioning","version":"3.1.0","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@3.1.0","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"a18173cf818a2bbc413bd59865502981e253c2b8","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-3.1.0.tgz","fileCount":1195,"integrity":"sha512-FUNsP0oqM4aObi//ygqGBhjBsENeq6vlmTTDtbfRb5weSs0hhea/irX5dt9Se3UpWZGUGCxQPVqYoBo+jfKKyg==","signatures":[{"sig":"MEYCIQC9AAdqHkeGSEExL5crVIMywn9U06OE/Mc3JcQWPQiV5gIhAMPo3o9mRyGtVdhlrrvzui+IQ1kBHDTrgtHW4ArmV51x","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6490177},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"732529882e09cc41e88e3635d19813fc4aa8e36a","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"axeptiotech","email":"tech@axeptio.eu"},"_npmVersion":"10.9.8","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.23.1","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","@jest/test-sequencer":"^30.2.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_3.1.0_1785952133943_0.853995429746329","host":"s3://npm-registry-packages-npm-production"}},"3.1.1":{"name":"@axeptio/provisioning","version":"3.1.1","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@3.1.1","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"66adb2c48f89bc00d9f74b3db59716db72aea1fa","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-3.1.1.tgz","fileCount":1195,"integrity":"sha512-0DNhvLzHfQxSR4foleM4zf0FgIGJ/6O0rlf4KGLsdnfJnIeHPIwp1yQc6LSXEGQjkfXBHqRlVGwngtFXKCXrbw==","signatures":[{"sig":"MEQCIHhn3jqFY7zro8hb4TblNFoqGQJLWiV12wKni5/9CO/VAiB0DxUHRSpDQdjYSkkojgDvdaCVXEeg/FGG98Nr6mSGPg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6490177},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"a9c3db2406f7b66af49d3dd63a566a3200a2c274","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"axeptiotech","email":"tech@axeptio.eu"},"_npmVersion":"10.9.8","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.23.2","dependencies":{"ora":"^5.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^4.1.2","yargs":"^17.7.2","stripe":"^14.0.0","fs-extra":"^11.2.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.2.0","husky":"^9.0.0","ts-jest":"^29.4.5","ts-node":"^10.9.1","commitlint":"^20.5.0","typescript":"^5.3.2","@types/jest":"^30.0.0","@types/node":"^20.10.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.32","@commitlint/cli":"^19.0.0","@types/fs-extra":"^11.0.4","@jest/test-sequencer":"^30.2.0","@commitlint/config-conventional":"^19.8.1"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_3.1.1_1787831210211_0.1165807545916997","host":"s3://npm-registry-packages-npm-production"}},"4.0.0":{"name":"@axeptio/provisioning","version":"4.0.0","keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"_id":"@axeptio/provisioning@4.0.0","maintainers":[{"name":"romainbessuges","email":"romainbessuges@gmail.com"},{"name":"axeptiotech","email":"tech@axeptio.eu"},{"name":"a_ng_d_axeptio","email":"aurelien.grimaud@axeptio.eu"},{"name":"rombat","email":"roro.devweb@gmail.com"},{"name":"pleberre","email":"philippe.leberre@axeptio.eu"},{"name":"luisfl-axeptio","email":"luis.flores.externe@axeptio.eu"},{"name":"javier.costa-axeptio","email":"javier.costa@axeptio.eu"},{"name":"matheuscavinax","email":"matheus.cavinato@axeptio.eu"}],"bin":{"axeptio-provision":"dist/cli.js"},"dist":{"shasum":"2240520f4d15d7ba6238799bee872736aa72bcab","tarball":"https://registry.npmjs.org/@axeptio/provisioning/-/provisioning-4.0.0.tgz","fileCount":1195,"integrity":"sha512-0Dy8OI2zzjAbVq8Z8dPS5nTw9YogZzxo5nteOlU5TFKbhr/nG2oa+sNznvzI2VhBobSKGt00sTJK8td1UJi7GQ==","signatures":[{"sig":"MEUCIGtWPbpI+2XizM8HIjGqeR95iOPtWZ19PmNlEXRiKtePAiEApyzBawWFltldGY+iwSF0ebDWQdjgorPctPkSOldh64Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":6533853},"main":"dist/index.js","types":"dist/index.d.ts","engines":{"node":">=22.12"},"gitHead":"19d038be4cb98fbe87379886cbdff6078b16ee1d","scripts":{"cli":"ts-node src/cli.ts","dev":"tsc --watch","test":"jest --selectProjects unit","build":"tsc","prepare":"husky","test:live":"jest --selectProjects live","test:watch":"jest --selectProjects unit --watch","test:coverage":"jest --selectProjects unit --coverage"},"_npmUser":{"name":"axeptiotech","email":"tech@axeptio.eu"},"_npmVersion":"10.9.8","description":"Axeptio provisioning client with state management and batch operations","directories":{},"_nodeVersion":"22.23.2","dependencies":{"ora":"^9.4.1","pino":"^10.3.1","axios":"^1.6.2","chalk":"^6.0.0","yargs":"^18.1.0","stripe":"^22.4.0","fs-extra":"^11.4.0","cli-table3":"^0.6.3","pino-datadog-transport":"^3.0.6"},"_hasShrinkwrap":false,"devDependencies":{"jest":"^30.4.2","husky":"^9.0.0","ts-jest":"^29.4.12","ts-node":"^10.9.1","commitlint":"^21.2.1","typescript":"^6.0.3","@types/jest":"^30.0.0","@types/node":"^26.2.0","pino-pretty":"^13.1.3","@types/yargs":"^17.0.35","@commitlint/cli":"^21.2.1","@types/fs-extra":"^11.0.4","@jest/test-sequencer":"^30.4.1","@commitlint/config-conventional":"^21.2.0"},"_npmOperationalInternal":{"tmp":"tmp/provisioning_4.0.0_1788888020396_0.5498495077681913","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2025-08-22T13:36:49.992Z","modified":"2026-09-14T10:07:23.988Z","1.0.0":"2025-08-22T13:36:50.175Z","1.1.0":"2025-09-04T09:55:11.289Z","2.0.0":"2026-04-23T21:35:58.173Z","2.0.1":"2026-04-24T14:04:16.822Z","3.0.0":"2026-07-14T07:09:07.871Z","3.0.2":"2026-07-22T13:05:54.722Z","3.0.3":"2026-08-04T10:30:41.116Z","3.1.0":"2026-08-05T17:48:54.231Z","3.1.1":"2026-08-27T11:46:50.398Z","4.0.0":"2026-09-08T17:20:20.684Z"},"keywords":["axeptio","provisioning","compliance","cookies","gdpr"],"description":"Axeptio provisioning client with state management and batch operations","maintainers":[{"email":"romainbessuges@gmail.com","name":"romainbessuges"},{"email":"tech@axeptio.eu","name":"axeptiotech"},{"email":"aurelien.grimaud@axeptio.eu","name":"a_ng_d_axeptio"},{"email":"philippe.leberre@axeptio.eu","name":"pleberre"},{"email":"luis.flores.externe@axeptio.eu","name":"luisfl-axeptio"},{"email":"javier.costa@axeptio.eu","name":"javier.costa-axeptio"},{"email":"matheus.cavinato@axeptio.eu","name":"matheuscavinax"}],"readme":"# `@axeptio/provisioning` Axeptio provisioning library\n\nTypeScript toolkit for automated provisioning of Axeptio entities. It provides a typed HTTP client, composable provisioners, checkpointed execution, and a CLI — optimized for both humans and AI agents.\n\n## Why this package\n\n- ✅ Type-safe client with retries and rate limiting\n- ✅ Composable provisioners for org → projects → configurations → users\n- ✅ Checkpointed execution with automatic resume and rollback\n- ✅ Smart merge: fresh data + existing state on every run\n- ✅ Parallel batches with pause/resume\n- ✅ Built-in callbacks (publish, scan, logs)\n- ✅ CLI and programmatic APIs\n- ✅ AI-friendly: small, copy-pasteable recipes and clear method names\n\n## Table of Contents\n\n- Authentication (Frontegg M2M client-credentials)\n- Reading & Querying\n  - Quick Start (Fetcher API)\n  - Chain Navigation\n  - Filters & Pagination\n  - Public vs Authenticated Endpoints\n  - `.fetch()` vs `.stream()`\n  - Analytics\n- Provisioning\n  - Quick Start (Provisioner API)\n  - Post-Provisioning Callbacks\n- Entity Recipes (copy-paste)\n  - Organizations & Projects\n  - Projects Groups\n  - Cookies Configuration\n  - Project Design & Stylesheet\n  - Terms (ContractsV2) Configuration\n  - Vendors (Company & Solution)\n  - Media & Assets\n  - Billing (Customer, VAT, Subscription)\n  - Users, Invitations, Access\n- User Management & Access Control\n- CLI Usage\n- Configuration\n- Supported Entity Types\n- Error Handling & Recovery\n- State Management\n- Rate Limiting\n- Environment Variables\n- API Reference (Configs)\n- Examples\n\n## Installation\n\n```bash\nnpm install @axeptio/provisioning\n```\n\n## Authentication\n\n**Two auth modes: Frontegg M2M client-credentials, or a caller-supplied bearer.** The pre-Frontegg auth system (username/password `/auth/local/signin`, browser-driven provider login, pre-minted user tokens) has been removed server-side and from this client; passing `username`, `password`, or `token` throws at construction with a migration hint.\n\n### M2M (Frontegg client-credentials)\n\nPass `clientId` + `secret` (the machine user's M2M pair, e.g. for `automator@axeptio.eu`) and the client authenticates against **Frontegg SSO** via the client-credentials exchange: it POSTs the pair to `https://login.axept.io/identity/resources/auth/v1/api-token`, receives a short-lived access JWT (`expiresIn` seconds, 24h in practice), and attaches it as the bearer. Protected services (e.g. the automator / shake-api routes behind `requireAuth()`) no longer accept the legacy `?access_key=` shared secret.\n\n```typescript\nimport { createClient } from '@axeptio/provisioning';\n\nconst client = createClient({\n  clientId: process.env.FRONTEGG_M2M_CLIENT_ID!,   // secret — from your secret store\n  secret: process.env.FRONTEGG_M2M_SECRET!,         // secret — from your secret store\n  baseURL: process.env.AXEPTIO_API_URL!,\n  // Optional knobs (defaults shown):\n  // fronteggBaseUrl: 'https://login.axept.io',\n  // fronteggTokenPath: '/identity/resources/auth/v1/api-token',\n});\n\n// client.getAuthMode() returns 'm2m'. The first request mints the token lazily;\n// call client.authenticate() to mint eagerly at startup instead.\n```\n\n**Behavior in M2M mode:**\n\n- Tokens are minted lazily, cached in-memory, and reused across calls; concurrent mints collapse into a single in-flight request (single-flight).\n- The cache expires ~60s **before** the token's real expiry, so long-running processes re-mint transparently across the 24h TTL.\n- A `401` from the API invalidates the cached token and re-mints **once** before the error is surfaced (handles rotation / early expiry). If the retry also 401s, an `AuthenticationError` is thrown.\n- Construction **fails fast** when `clientId` or `secret` is missing or empty — don't defer the failure to the first call.\n- The `secret`, the `accessToken`, and the `refreshToken` are never logged.\n\n**Pitfalls (all observed in practice):**\n\n- The mint host is your Frontegg **environment host** (`https://login.axept.io` for prod, the default) — **not** `api.frontegg.com`.\n- The mint path is `/identity/resources/auth/v1/api-token` (v1, unauthenticated, camelCase response). The `…/v1/user/api-token` variant requires an existing bearer; v2 returns snake_case.\n- Tokens are **environment-scoped**: a production-minted token is rejected (401) by a staging service and vice-versa. Match `fronteggBaseUrl` to the environment `baseURL` points at.\n- Store `clientId`/`secret` in a secret store (e.g. SSM `SecureString`), never plaintext env/IaC vars — and beware trailing newlines when writing them (a newline-terminated secret 401s the mint).\n\n### Caller bearer (pass-through)\n\nFor services that act **on behalf of a caller** who already holds a Frontegg access token (e.g. the MCP server's HTTP transport), pass the token itself and the client attaches it verbatim — the service holds no credentials of its own:\n\n```typescript\nconst client = createClient({\n  bearer: callerAccessToken,  // the `accessToken` a caller minted themselves\n  baseURL: process.env.AXEPTIO_API_URL!,\n});\n\n// client.getAuthMode() returns 'bearer'. The client is authenticated from\n// construction; authenticate() is a no-op.\n```\n\n**Behavior in bearer mode:** no mint, no cache, no refresh. The token is used as-is for the lifetime of the client instance; when it expires or is revoked, requests surface `AuthenticationError` (the internal 401 retry re-sends the same token) and the caller must supply a fresh token via a new client. `bearer` is exclusive with `clientId`/`secret` — passing both throws. The bearer is a secret and is never logged.\n\nThe underlying token provider is also exported standalone, if you need a Frontegg bearer for something other than this client:\n\n```typescript\nimport { createFronteggTokenProvider } from '@axeptio/provisioning';\n\nconst provider = createFronteggTokenProvider({\n  clientId: process.env.FRONTEGG_M2M_CLIENT_ID!,\n  secret: process.env.FRONTEGG_M2M_SECRET!,\n});\nconst jwt = await provider.getToken(); // mint + cache + single-flight\n// On a 401 from the protected service: provider.invalidate(); retry once.\n```\n\n**Refusing legacy or missing shapes.** Passing any removed pre-Frontegg field (`username`, `password`, `token`) or only `{ baseURL }` throws at construction. TypeScript rejects the removed fields at compile time (`never`-typed); the constructor also validates at runtime.\n\n## Reading & Querying\n\n`@axeptio/provisioning` ships a typed, chainable fetcher API for reading Axeptio entities. Start with `createFetcher(client)` and navigate through the entity hierarchy. Every chain ends in one of two terminals: `.fetch()` (eager, auto-paginated) or `.stream()` (lazy async iterator).\n\n### Quick Start (Fetcher API)\n\n```typescript\nimport { createClient, createFetcher } from '@axeptio/provisioning';\n\nconst client = createClient({\n  clientId: process.env.FRONTEGG_M2M_CLIENT_ID!,\n  secret: process.env.FRONTEGG_M2M_SECRET!,\n  baseURL: process.env.AXEPTIO_API_URL!,\n  environment: 'staging',\n});\n\nconst fetcher = createFetcher(client);\n\n// List organizations (first page auto-paginated, default perPage 100)\nconst { data, __meta } = await fetcher.organizations().fetch();\n\nconsole.log(`${data.length} organizations, total ${__meta.totalCount}`);\n```\n\n### Chain Navigation\n\nOne worked example per major entity family:\n\n**Organizations and Projects** — authenticated, parent → child:\n\n```typescript\n// All projects the caller can see, across every organization (flat /vault/projects).\n// No org id required — use this when you don't know or don't care about the owning org.\nconst all = await fetcher.projects().fetch();\n\n// Narrow to a single org without going through the chain.\nconst orgProjects = await fetcher.projects({ organizationId: 'org_xxx' }).fetch();\n\n// Or use the parent chain — identical payload, different ergonomics.\nconst projects = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .projects()\n  .fetch();\n```\n\n**Configurations** — `CookieConfigurationFetcher`, `TCFConfigurationFetcher`, `DPOConfigurationFetcher`, `SubsConfigurationFetcher`, `CustomVendorFetcher` all hang off a project via `data.projectId`:\n\n```typescript\nconst cookies = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .projects({ id: 'proj_yyy' })\n  .cookieConfigurations()\n  .fetch();\n\n// tcfConfigurations() / dpoConfigurations() / subsConfigurations() / customVendors()\n// follow the exact same chain shape.\n```\n\n**Terms (VersionedDocs)** — a `TermsConfiguration` contract has both revisions (`TermsRevisionFetcher`) and versions (`TermsVersionFetcher`) as direct children:\n\n```typescript\n// A contract's revisions\nconst revisions = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .projects({ id: 'proj_yyy' })\n  .termsConfigurations({ id: 'terms_zzz' })\n  .revisions()\n  .fetch();\n\n// A contract's versions\nconst versions = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .projects({ id: 'proj_yyy' })\n  .termsConfigurations({ id: 'terms_zzz' })\n  .versions()\n  .fetch();\n```\n\n**Vendors** — public, unauthenticated endpoints:\n\n```typescript\nconst companies = await fetcher.vendorCompanies().fetch();\n\nconst solutions = await fetcher\n  .vendorCompanies({ id: 'company_xxx' })\n  .vendorSolutions()\n  .fetch();\n\n// Top-level solution search\nconst matches = await fetcher\n  .vendorSolutions({ searchTerm: 'analytics', language: 'fr' })\n  .fetch();\n```\n\n**Users** — parent-scoped with dynamic endpoint (org-scoped OR project-scoped):\n\n```typescript\n// Org-scoped users (→ /vault/organizations/:id/users)\nconst orgUsers = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .users()\n  .fetch();\n\n// Project-scoped users (→ /vault/projects/:id/users, same UserFetcher class)\nconst projectUsers = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .projects({ id: 'proj_yyy' })\n  .users()\n  .fetch();\n```\n\nThere is no top-level `.users()` — users are always parent-scoped.\n\n### Filters & Pagination\n\nEvery fetcher accepts a filter. The base shape is `{ id?: string, page?: number, perPage?: number }`. Specialized filters add fields (`ProjectFilter.organizationId`, `VendorSolutionFilter.companyId` / `searchTerm` / `language` / `sort`). Scoping usually comes from the parent chain, not the filter.\n\n```typescript\n// Cap the aggregate record count across auto-pagination\nconst page = await fetcher\n  .organizations()\n  .fetch({ limit: 250 });\n```\n\nAdjust the default page size at factory time:\n\n```typescript\nconst fetcher = createFetcher(client, { defaultPerPage: 50 });\n```\n\n### Public vs Authenticated Endpoints\n\nMost fetchers require authentication via `createClient({ username, password })` and route through `requestWithMeta()`. The exceptions are vendor data:\n\n- `VendorCompanyFetcher` hits `/vendors/companies` (public)\n- `VendorSolutionFetcher` hits `/vendors/solutions/search` (public)\n\nThese call `publicRequestWithMeta()` internally, so they work without valid credentials. Every fetcher exposes its auth mode via `isPublicEndpoint()`.\n\n### `.fetch()` vs `.stream()`\n\n`.fetch()` returns everything eagerly as a single `FetchResult<T>`. `.stream()` returns an async iterator yielding pages lazily — use it for large datasets like vendors (hundreds of records):\n\n```typescript\nfor await (const page of fetcher.vendorCompanies().stream()) {\n  for (const company of page.data) {\n    // process each record as it arrives\n  }\n}\n```\n\n`.stream()` works on every fetcher — root and child. Use it the same way on a parent-scoped chain:\n\n```typescript\nfor await (const page of fetcher.vendorCompanies({ id: 'company_xxx' }).vendorSolutions().stream()) {\n  for (const solution of page.data) {\n    // process each solution as it arrives\n  }\n}\n```\n\n### Fan-out over parents\n\nWhen you call `.stream()` on a child fetcher without a resolved parent id, the library fetches all parents first, then streams each parent's children sequentially. Each yielded page carries `__meta.parentId` so you can group results:\n\n```typescript\nconst groupByOrg = new Map<string, User[]>();\n\nfor await (const page of fetcher.organizations().users().stream({ limit: 500 })) {\n  const parentId = page.__meta.parentId!;\n  const bucket = groupByOrg.get(parentId) ?? [];\n  bucket.push(...page.data);\n  groupByOrg.set(parentId, bucket);\n}\n```\n\nThe opening parent fetch is O(parents) — narrow with a parent filter (or resolve the parent id yourself) when the parent population is large.\n\n### Analytics\n\n`projects({ id }).analytics(filter)` is a project-scoped query builder (not a list endpoint — it never appears in `getChildren()`). It fronts **two different backends**, and which one you get depends on the method you call.\n\nThe chain must resolve a project id (`projects({ id: '...' })`) — every stats endpoint is project-scoped, and the fetcher throws if it is missing. Every method except `configMetadata()` also needs a date range: either `{ startDate, endDate }` on the filter, or `.period(...)` / `{ period }` (`allTime`, `lastYear`, `lastQuarter`, `lastMonth`, `lastWeek`). Neither present throws.\n\n**caas-api stats** — the back-office charts, under `baseURL`:\n\n```typescript\nconst kpis = await fetcher\n  .organizations({ id: 'org_xxx' })\n  .projects({ id: '657c621b5e1a3b05ccf432d2' })\n  .analytics({ period: 'lastMonth' })\n  .globalStats();\n\n// .line(['pageview', 'consentRate']) — one row per date\n// .byDimension('device')             — one row per dimension value\n// .bounceRateByDuration()\n// .configMetadata(['cfg_1'])         — needs no date range\n```\n\nThe full `AnalyticsFilter` accepted by these methods:\n\n| Field | Meaning |\n|---|---|\n| `startDate` / `endDate` | `YYYY-MM-DD`, exclusive with `period`. Validated as parseable and correctly ordered, not for the exact shape — `consentStats()` is the one terminal that enforces `YYYY-MM-DD` strictly, because the headless endpoint 400s on anything else |\n| `period` | rolling window, resolved to a range client-side |\n| `filters` | `[{ dimension, values }]` where dimension is `device`, `os`, `browser`, `config` or `host` |\n| `source` | event source, server defaults to `sdk-web` |\n| `aggregateBy` | e.g. `'day'`; the server enforces a plan allow-list |\n\nKPI fields on `globalStats().results[0]` are all optional — the server strips KPIs for callers whose plan does not unlock them. Null-check before reading.\n\n**Headless CMP consent stats** — the BI-shaped read behind Looker Studio / Power BI / Tableau, on the **headless host**:\n\n```typescript\nconst client = createClient({\n  clientId: process.env.FRONTEGG_M2M_CLIENT_ID!,\n  secret: process.env.FRONTEGG_M2M_SECRET!,\n  baseURL: process.env.AXEPTIO_API_URL!,\n  headlessBaseURL: process.env.AXEPTIO_HEADLESS_API_URL!,  // https://headless-api.axeptio.tech\n});\n\nconst stats = await createFetcher(client)\n  .organizations({ id: 'org_xxx' })\n  .projects({ id: '657c621b5e1a3b05ccf432d2' })\n  .analytics({ startDate: '2026-08-01', endDate: '2026-08-30' })\n  .consentStats({ groupBy: ['date', 'country'] });\n\nstats.meta;     // { filters: { from, to }, groupBy, rows, timeZone }\nstats.summary;  // totals over the whole period\nstats.data;     // one row per groupBy combination\n```\n\n`consentStats()` issues `GET {headlessBaseURL}/stats`. It authorizes per request off the Frontegg bearer the client already holds, so it needs no headless project token — but it does need `headlessBaseURL`, which has no default (the host differs per environment and a production bearer is rejected by a staging service). Calling it without one throws naming `AXEPTIO_HEADLESS_API_URL`.\n\n| | caas-api methods | `consentStats()` |\n|---|---|---|\n| Host | `baseURL` | `headlessBaseURL` |\n| Call | `POST /stats/{projectId}` | `GET /stats?projectId&from&to&groupBy` |\n| Shape | `{ dimensions, results[] }` | `{ meta, summary, data[] }` |\n| Dimensions | `device`, `os`, `browser`, `config`, `host` | `date`, `country`, `device`, `configVersion` |\n\n`groupBy` defaults to `['date']`. Pass any combination of the four dimensions; each `data` row then carries the dimension keys it was grouped on alongside the metrics.\n\n`summary` and every `data` row share one metric shape (`ConsentStatsMetrics`):\n\n| Counts (integers) | Ratios (**0..1, not percentages**) |\n|---|---|\n| `pageview`, `visitor`, `couldInteract`, `widgetDisplay`, `interaction`, `consent`, `reject`, `partial`, `bounce`, `quickBounce`, `suspectedBots` | `interactionRate`, `consentRate`, `optInRate`, `bounceRate`, `quickBounceRate` |\n\nEvery field is optional, same reasoning as the caas-api KPIs. Both `summary` and each row also carry a `responses` array — the `consent` / `partial` / `reject` counts pivoted into exactly three labelled rows (`Consent`, `Partial`, `Reject`, always in that order, zeros when empty) so a BI tool has a dimension to chart a pie against. It is derived from those fields and adds no new data; the labels are a stable contract that surfaces in customer-facing reports.\n\nTwo limits are enforced client-side, before the request goes out, so they fail with a message naming the problem rather than as an opaque `400`: `groupBy` must contain only the four allowed dimensions, and the window must be 365 days or narrower.\n\n⚠️ That second limit rules out **two** of the rolling periods, not one. `allTime` is obviously too wide, but so is `lastYear` — it resolves to 366 days inclusive, one over the cap. `lastQuarter` (122 days) is the widest period that fits; for a true one-year window pass an explicit `{ startDate, endDate }` 365 days apart. The caas-api terminals have no such cap and accept every period.\n\nOne API behavior is deliberately not exposed: `GET /stats` also accepts *no* date bounds and defaults to the last 30 days. `consentStats()` still requires a range or a period, so it behaves like every other method on this fetcher rather than being the one that silently picks a window.\n\nOn errors, `403` covers both \"not allowed to read this project\" and \"no such project\" — the endpoint does not distinguish them, deliberately, so it cannot be used to probe which project ids exist. The endpoint also has its own `429` and surfaces upstream stats-backend failures as `502` / `503` / `504`; all of them arrive as the client's usual typed errors.\n\n---\n\n## Provisioning\n\n`@axeptio/provisioning` also provides a write-capable provisioner tree for creating Axeptio entities in bulk, with checkpointed execution and automatic resume. The sections below cover the provisioner API and entity recipes.\n\n### Quick Start (Provisioner API)\n\n```typescript\nimport {\n  createClient,\n  createExecutor,\n  OrganizationProvisioner,\n  ProjectProvisioner,\n  CookieConfigurationProvisioner,\n  TermsConfigurationProvisioner,\n  ProvisioningCallbacks,\n} from '@axeptio/provisioning';\n\nconst client = createClient({\n  clientId: process.env.FRONTEGG_M2M_CLIENT_ID!,\n  secret: process.env.FRONTEGG_M2M_SECRET!,\n  baseURL: process.env.AXEPTIO_API_URL!,\n  environment: 'staging',\n});\nconst executor = createExecutor(client, 'my-migration-2024', './provisioning-state', true); // true = dry run\n\nconst org = new OrganizationProvisioner('org', {\n  companyName: 'Example Corp',\n  email: 'contact@example.com',\n  country: 'FR', line1: '123 Rue de la Paix', city: 'Paris', postalCode: '75001',\n  isProfessional: 'YES',\n});\n\nconst project = new ProjectProvisioner('project', {\n  name: 'Example Website', websiteURL: 'https://example.com', locales: ['en', 'fr'],\n});\n\nconst cookies = new CookieConfigurationProvisioner('cookies', {\n  projectId: '', language: 'en', country: 'FR',\n  steps: [{ layout: 'welcome', title: 'We use cookies' }],\n});\n\nconst terms = new TermsConfigurationProvisioner('terms', {\n  projectId: '',\n  config: { language: 'en', title: 'Terms of Service', name: 'tos', mandatory_download: false },\n  content: {\n    sections: [\n      { uid: 'intro', name: 'introduction', title: 'Introduction', blocks: [\n        { type: 'title', content: 'Introduction' },\n        { type: 'richText', content: 'Welcome to our service.' },\n      ] }]\n  },\n});\n\nproject\n  .addConfiguration(cookies)\n  .addConfiguration(terms)\n  .onSuccess(ProvisioningCallbacks.publishProject(['cookies']))\n  .onSuccess(ProvisioningCallbacks.startScan({ maxPages: 10 }));\n\norg.addProject(project);\nexecutor.addOrganization(org);\n\n// environment is 'staging' or 'production' - dry run is controlled by executor creation\nawait executor.execute({ environment: 'staging', organizationBatchSize: 1, projectBatchSize: 2 });\n```\n\n### Post-Provisioning Callbacks\n\nAdd callbacks to execute actions after successful or failed provisioning:\n\n```typescript\nimport { ProvisioningCallbacks } from '@axeptio/provisioning';\n\n// Single callback\nproject.onSuccess(ProvisioningCallbacks.publishProject(['cookies', 'tcf']));\n\n// Multiple callbacks\nproject\n  .onSuccess(ProvisioningCallbacks.publishProject())\n  .onSuccess(ProvisioningCallbacks.startScan({\n    maxTabs: 3,\n    maxPages: 10,\n    testCMP: true,\n    languages: ['en', 'fr']\n  }))\n  .onFailure(ProvisioningCallbacks.logFailure('Project creation failed'));\n\n// Custom callbacks\nproject.onSuccess(async (result: Project, context) => {\n  console.log(`Project created: ${result.name} (${result._id})`);\n  await sendNotificationEmail(result.name);\n  await updateExternalSystem(result._id);\n});\n\n// Chain multiple callbacks\nproject.onSuccess(ProvisioningCallbacks.chain(\n  ProvisioningCallbacks.publishProject(['cookies']),\n  ProvisioningCallbacks.logSuccess('Project published'),\n  customNotificationCallback\n));\n```\n\n#### Built-in Callbacks\n\n| Callback | Description | Parameters |\n|----------|-------------|------------|\n| `publishProject(services?)` | Publish project configurations | `services: string[]` - Default: `['cookies', 'tcf']` |\n| `startScan(config)` | Launch automator scan job | `maxTabs`, `maxPages`, `testCMP`, `languages` |\n| `logSuccess(message?)` | Log successful completion | `message: string` - Optional custom message |\n| `logFailure(message?)` | Log failure details | `message: string` - Optional custom message |\n| `chain(...callbacks)` | Execute multiple callbacks in sequence | `callbacks: ProvisioningCallback[]` |\n\n**Note**: Callbacks are preserved during state recovery. When resuming from a saved state, callbacks will still execute for newly completed nodes, but not for previously completed ones.\n\n### Projects Groups\n\nProjects groups are organizational folders used in the backoffice to organize projects. They execute after projects are created so their `projectIds` array can be populated.\n\n```typescript\n// Create projects first\nconst project1 = new ProjectProvisioner('proj-1', projectData1);\nconst project2 = new ProjectProvisioner('proj-2', projectData2);\n\n// Create a projects group and add projects to it\nconst projectsGroup = new ProjectsGroupProvisioner('group-1', {\n  name: 'Website Projects',\n  organizationId: 'org-123' // Optional - will be set from context\n})\n.addProject(project1) // Projects will be referenced by ID\n.addProject(project2)\n.onSuccess(ProvisioningCallbacks.logSuccess('Projects group created'));\n\n// Add to executor\norg.addProject(project1);\norg.addProject(project2);\nexecutor.addOrganization(org);\nexecutor.addProjectsGroup(projectsGroup); // Executed after projects\n```\n\n**Execution Order**: Projects Groups are executed after Projects to ensure project IDs are available for the `projectIds` array.\n\n## Entity Recipes (copy-paste)\n\nSmall, focused snippets to reduce context load. Replace IDs/emails as needed.\n\n### Organizations & Projects\n```typescript\nconst org = new OrganizationProvisioner('org', { companyName: 'ACME', email: 'billing@acme.com', country: 'FR', line1: '1 Rue', city: 'Paris', postalCode: '75001', isProfessional: 'YES' });\nconst project = new ProjectProvisioner('proj', { name: 'Website', websiteURL: 'https://acme.com' });\norg.addProject(project);\nexecutor.addOrganization(org);\n```\n\n### Projects Groups\n```typescript\nconst group = new ProjectsGroupProvisioner('group', { name: 'Web Properties' }).addProject(project);\nexecutor.addProjectsGroup(group);\n```\n\n### Cookies Configuration\n```typescript\nconst cookies = new CookieConfigurationProvisioner('cookies', {\n  projectId: '', language: 'en', country: 'FR',\n  steps: [{ layout: 'welcome', title: 'We use cookies' }],\n});\nproject.addConfiguration(cookies);\n```\n\n#### Auto-categorization mode (bulk-friendly)\n\nOptionally let the provisioner build steps from a list of vendor solution IDs. Vendors are assigned to their most frequent category, cookie-step templates are used to shape steps, and an optional ConsentWall can be added as a special step.\n\n```typescript\nconst autoCookies = new CookieConfigurationProvisioner('cookies-auto', {\n  projectId: '',\n  language: 'en',\n  autoCategorize: {\n    vendorIds: ['64a...','64b...'],\n    language: 'en',\n    withConsentWall: true,\n  },\n});\nproject.addConfiguration(autoCookies);\n```\n\nNotes:\n- Reorders 'info' to index 1 when present, 'other' to last.\n- Supports `specialSteps` for the ConsentWall.\n- Uses `/vendors/categories`, `/vendors/solutions/{id}/{language}`, and `/templates/cookie-step` endpoints.\n\n#### Extending Cookie Configuration Validation\n\nCookie configurations support two extensibility hooks for migration-specific validation logic:\n\n**1. Language Resolver Hook**\n\nOverride how the configuration language is determined for validation:\n\n```typescript\ncookieConfig.setConfigLanguageResolver((provisioner) => {\n  // Example: Use a custom property for original language\n  return (provisioner as any).originalLanguage || provisioner.state.data.language;\n});\n```\n\n**Use case**: When you need to validate against a different language value than the normalized API language (e.g., distinguishing between 'nl' and 'nlinf' in Dutch migrations).\n\n**2. Vendor Validator Hook**\n\nInject custom vendor validation logic:\n\n```typescript\ncookieConfig.setVendorValidator(\n  (vendorProvisioner, configLanguage, context) => {\n    // Custom validation logic\n    const vendorLang = vendorProvisioner.someProperty;\n    if (vendorLang !== configLanguage) {\n      throw new Error(`Validation failed in ${context}: vendor=${vendorLang}, config=${configLanguage}`);\n    }\n  },\n  (vendorId: string) => {\n    // Vendor lookup function - returns provisioner object for vendor ID\n    return project.getChildren().find(child => child.getId() === vendorId);\n  }\n);\n```\n\n**Use case**: When vendor IDs need domain-specific validation beyond MongoDB ObjectID format checks (e.g., language matching, category restrictions).\n\n**Integration**:\n\nBoth hooks run during the validation phase, before any API calls are made. They work together:\n\n```typescript\nconst cookiesConfig = new CookieConfigurationProvisioner('cookies-en', {\n  projectId: 'proj-123',\n  language: 'en',\n  autoCategorize: { vendorIds: [...], language: 'en' }\n});\n\n// Set language resolver first\ncookiesConfig.setConfigLanguageResolver((provisioner) => {\n  // Return migration-specific language\n  return (provisioner as any).originalLanguage || provisioner.state.data.language;\n});\n\n// Then set vendor validator\ncookiesConfig.setVendorValidator(\n  (vendorProvisioner, configLanguage, context) => {\n    // Validator receives language from resolver\n    validateLanguageMatch(vendorProvisioner, configLanguage, context);\n  },\n  (vendorId) => project.getChildren().find(child => child.getId() === vendorId)\n);\n\nproject.addConfiguration(cookiesConfig);\n```\n\nBoth methods return `this` for method chaining.\n\n### Project Design & Stylesheet\n\nProvision project theming with a simple style guide. We expose types and a helper to mirror `WidgetGenerator` behavior without pulling its internals.\n\nTypes:\n- `StyleGuideInput` — { lightColor, darkColor, themeColor, isDarkMode?, isMonochrome?, font? } (hex colors without '#')\n- `ProjectStylesheet` — shape compatible with `ProjectInput` (`colors`, `fonts`, `widgetStyle`, `overlayStyle`, `isCustomStyle`)\n\nHelper:\n- `integrateStyleGuide(style: StyleGuideInput): ProjectStylesheet`\n\nExample:\n```typescript\nimport { ProjectProvisioner, integrateStyleGuide, type StyleGuideInput } from '@axeptio/provisioning';\n\nconst sg: StyleGuideInput = {\n  lightColor: 'F5BD55',\n  darkColor: '000000',\n  themeColor: 'f6c434',\n  isDarkMode: false,\n  isMonochrome: false,\n  font: 'Lato',\n};\n\nconst stylesheet = integrateStyleGuide(sg);\n\nconst project = new ProjectProvisioner('project-1', {\n  name: 'Website',\n  websiteURL: 'https://example.com',\n  ...stylesheet,\n});\n```\n\nNotes:\n- `ProjectInput.colors`, `widgetStyle`, and `overlayStyle` support extended fields (e.g., `toggle_on`, `consent_button_*`, `borderRadius`, `position.side`).\n- `isCustomStyle: true` is set by the helper to indicate custom design.\n\n### Terms (ContractsV2) Configuration\n```typescript\nconst terms = new TermsConfigurationProvisioner('terms', {\n  projectId: '',\n  config: { language: 'en', title: 'Terms of Service', name: 'tos' },\n  content: { sections: [{ uid: 'intro', name: 'introduction', blocks: [ { type: 'title', content: 'Introduction' }, { type: 'richText', content: 'Welcome.' } ] }] }\n});\nproject.addConfiguration(terms);\n```\n\n### Vendors (Company & Solution)\n```typescript\nconst company = new VendorCompanyProvisioner('vendor-co', { name: 'Acme Analytics', domain: 'acme-analytics.example' });\nconst solution = new VendorSolutionProvisioner('vendor-sol', {\n  name: 'acme-analytics',\n  title: { __lang: { en: 'Acme Analytics' } },\n  website: { __lang: { en: 'https://acme.example' } },\n  shortDescription: { __lang: { en: 'Analytics' } },\n  categoryIds: [],\n} as any);\nexecutor.addVendorCompany(company);\nexecutor.addVendorSolution(solution);\n```\n\n### Media & Assets\n```typescript\nimport { readFileSync } from 'fs';\n\n// One step: upload a file to the asset store and register it as project media\nconst media = await client.uploadMedia(projectId, {\n  content: readFileSync('./logo.png'),\n  filename: 'logo.png',\n  contentType: 'image/png',\n});\nconsole.log(media.data?.url); // public Imgix URL\n\n// Or drive the two API calls yourself (POST /assets → POST /vault/media)\nconst asset = await client.uploadAsset({\n  content: readFileSync('./logo.png'),\n  filename: 'logo.png',\n  contentType: 'image/png',\n});\nawait client.createMedia({ projectId, ...asset });\n\n// Remove a media entry (the uploaded asset URL is unaffected)\nawait client.deleteMedia(media._id);\n```\n\n### Billing (Customer, VAT, Subscription)\n```typescript\n// 1) Customer\nconst customer = await client.createCustomer({\n  email: 'billing@example.com', name: 'Example Corp',\n  tax_exempt: 'none', preferred_locales: ['fr-FR','en-US'],\n  address: { line1: '123 Rue', city: 'Paris', country: 'FR', postal_code: '75001' },\n  metadata: { isProfessional: 'YES', vatNumber: 'FR123...', contactName: 'Jane' },\n  expand: ['tax_ids','invoice_settings.default_payment_method','sources'],\n});\n// 2) VAT\nawait client.createCustomerTaxId(customer.id, { type: 'eu_vat', value: 'FR123...' });\n// 3) Subscription\nproject.setSubscription(new SubscriptionProvisioner('sub', {\n  customer: customer.id,\n  items: [{ price: 'price_agency_monthly', quantity: 1 }],\n  payment_behavior: 'default_incomplete', currency: 'eur',\n  expand: ['latest_invoice','discounts','items.price'],\n  metadata: { organizationId: '...', userId: '...' },\n}));\n```\n\n### Users, Invitations, Access\n```typescript\nconst user = new UserProvisioner('user', { email: 'new@acme.com', password: 'Secret123!', displayName: 'New User', data: { preferredLanguage: 'en', acceptTerms: true }});\nuser.addInvitation(new InvitationProvisioner('inv', { email: 'new@acme.com', data: { collection: 'organizations', id: 'orgId' }}));\nexecutor.addUser(user);\nexecutor.addGroupAssignment(new GroupManagerProvisioner('assign', { userId: '', projectId: 'projectId', action: 'add' }));\n```\n\n## User Management & Access Control\n\nProvision users and manage their access to organizations and projects:\n\n```typescript\nimport { \n  UserProvisioner, \n  InvitationProvisioner, \n  GroupManagerProvisioner \n} from '@axeptio/provisioning';\n\n// Create user with invitations\nconst user = new UserProvisioner('user-1', {\n  email: 'newuser@example.com',\n  password: 'SecurePassword123',\n  displayName: 'New User',\n  data: {\n    preferredLanguage: 'en',\n    acceptTerms: true\n  }\n})\n.onSuccess(ProvisioningCallbacks.logSuccess('User created successfully'))\n.onSuccess(ProvisioningCallbacks.addUserToOrganization('org-id'));\n\n// Add invitation to organization\nconst invitation = new InvitationProvisioner('invite-1', {\n  email: 'newuser@example.com',\n  data: {\n    collection: 'organizations',\n    id: 'org-id-123',\n    templateVars: {\n      organization: { companyName: 'Example Corp' }\n    }\n  }\n});\n\nuser.addInvitation(invitation);\n\n// Manage group assignments\nconst groupAssignment = new GroupManagerProvisioner('group-1', {\n  userId: 'user-id-from-context',\n  projectId: 'project-id-123',\n  action: 'add'\n});\n\nexecutor.addUser(user);\nexecutor.addGroupAssignment(groupAssignment);\n```\n\n#### User Management Callbacks\n\n| Callback | Description | Usage |\n|----------|-------------|-------|\n| `addUserToOrganization(orgId)` | Add user to organization | After user creation |\n| `addUserToProject(projectId)` | Add user to project | After user creation |\n| `logSuccess(message?)` | Log successful completion | After successful user creation |\n\n### CLI Usage\n\n```bash\n# Set Frontegg M2M credentials (from your secret store)\nexport FRONTEGG_M2M_CLIENT_ID=\"...\"\nexport FRONTEGG_M2M_SECRET=\"...\"\n\n# Run provisioning\naxeptio-provision run my-migration config.json\n\n# Check status\naxeptio-provision status\n\n# Resume after interruption (automatic - just re-run with config)\naxeptio-provision run my-migration ./config.json\n# Automatically merges fresh build with existing state\n\n# View checkpoints\naxeptio-provision checkpoints my-migration\n\n# Rollback created entities\naxeptio-provision rollback my-migration\n\n# Clean up state\naxeptio-provision clean my-migration\n```\n\n## Configuration\n\nCreate a `config.json` file for execution settings:\n\n```json\n{\n  \"environment\": \"staging\",\n  \"organizationBatchSize\": 3,\n  \"projectBatchSize\": 5,\n  \"configurationBatchSize\": 10,\n  \"continueOnError\": false,\n  \"maxRetries\": 3,\n  \"retryDelay\": 1000\n}\n```\n\n**Note:** The `environment` field specifies the target environment (`\"staging\"` or `\"production\"`). Dry-run behavior is controlled separately when creating the executor:\n\n```typescript\n// Dry run mode\nconst executor = createExecutor(client, 'state-id', './provisioning-state', true);\n\n// Live mode\nconst executor = createExecutor(client, 'state-id', './provisioning-state', false);\n```\n\n## Supported Entity Types\n\n- **Organizations** - Company entities that own projects\n- **Projects** - Individual compliance projects\n- **Projects Groups** - Organizational folders for projects (backoffice convenience)\n- **Users** - User accounts with authentication and access control\n- **Invitations** - User invitations to organizations/projects\n- **Group Assignments** - User access management for organizations/projects\n- **Cookie Configurations** - Cookie consent widgets\n- **TCF Configurations** - IAB TCF compliance setups\n- **DPO Configurations** - Data Protection Officer contacts\n- **Terms Configurations** - Legal terms and conditions (ContractsV2)\n- **Subs Configurations** - Data processing subscriptions (formerly Processing)\n- **Subscriptions** - Billing subscriptions (Stripe)\n- **Media & Assets** - Project media uploaded to the Imgix-backed asset store\n\n## Error Handling & Recovery\n\nThe package automatically handles:\n\n- **Network failures** with exponential backoff\n- **Rate limiting** with intelligent queuing\n- **Partial failures** with detailed error reporting\n- **State corruption** with checkpoint recovery\n\n### Authentication errors\n\nEvery 401/403 surfaces as `AuthenticationError` (non-retryable). Key off `err.code` to branch:\n\n| Condition                                    | `err.code`                         |\n| -------------------------------------------- | ---------------------------------- |\n| Frontegg mint failed (bad clientId/secret)   | `ErrorCode.AUTHENTICATION_FAILED`  |\n| Request 401'd even after a one-shot re-mint  | `ErrorCode.AUTHENTICATION_FAILED`  |\n\n```typescript\nimport { AuthenticationError, ErrorCode } from '@axeptio/provisioning';\n\ntry {\n  await fetcher.organizations().fetch();\n} catch (err) {\n  if (err instanceof AuthenticationError && err.code === ErrorCode.TOKEN_EXPIRED) {\n    // Prompt the user to mint a fresh token; reconstruct the client.\n  }\n  throw err;\n}\n```\n\n### Resuming After Interruption (Automatic)\n\n```typescript\n// Resume is automatic! Just build fresh and use setFreshTree()\n// Orchestrator automatically merges with existing state\nconst built = await buildProvisioners(jobConfig);\nconst executor = new ResumableProvisioningExecutor(client, stateManager);\nawait executor.setFreshTree(built); // Merges fresh with state!\nawait executor.execute(config);\n\n// OLD WAY (deprecated - only for rollback/status):\n// const executor = await ResumableProvisioningExecutor.fromState(\n//   'my-migration-2024',\n  './provisioning-state',\n  client,\n  false  // isDryRun: false for live mode, true for dry run\n);\n\nawait executor.execute(config);\n```\n\n### Rollback Created Entities\n\n```typescript\n// Rollback all created entities in reverse order\nawait executor.rollback();\n```\n\n## State Management\n\nState is automatically persisted to disk:\n\n```\n./provisioning-state/\n├── my-migration-2024.json          # Main state file\n└── checkpoints/\n    └── my-migration-2024/\n        ├── 2024-01-15T10-30-00-execution-start.json\n        ├── 2024-01-15T10-45-00-execution-complete.json\n        └── 2024-01-15T10-46-00-execution-failed.json\n```\n\n### Checkpoint Strategy\n\nCheckpoints are **milestone-based**, not per-entity, to minimize file system overhead while maintaining recovery capability:\n\n**When Checkpoints Are Created:**\n- ✅ `execution-start` - At the beginning of execution (for crash recovery)\n- ✅ `execution-complete` - When execution succeeds (marks completion)\n- ✅ `execution-failed` - When execution fails (preserves partial progress)\n- ✅ `execution-paused` - When execution is manually paused\n- ✅ `rollback-complete` - When rollback finishes\n\n**State Updates:**\n- State is updated after **every provisioner** executes (via `updateStateAfterExecution()`)\n- This ensures progress is always persisted without creating excessive checkpoint files\n- Resume operations use the main state file, which always contains the latest progress\n\n**Why Not Per-Entity Checkpoints?**\n- A migration with 100 entities would create 100+ checkpoint files per run\n- State updates already happen after each entity, so progress is never lost\n- Milestone checkpoints provide sufficient recovery points for debugging\n- Reduces filesystem I/O and improves performance\n\n**Recovery Scenarios:**\n- **Crash during execution**: Use main state file (most recent) or `execution-start` checkpoint\n- **Failed execution**: Use `execution-failed` checkpoint to inspect partial progress\n- **Manual pause**: Use `execution-paused` checkpoint to resume later\n- **Debugging**: Compare checkpoints to see state at different execution phases\n\n## Rate Limiting\n\nThe client automatically handles Axeptio's rate limits:\n\n- **Authentication**: 60 requests per 5 minutes\n- **Scan endpoints**: 15 requests per minute  \n- **General API**: 50 requests per minute (configurable)\n\n## Environment Variables\n\nEnv vars apply to **M2M mode** only (bearer mode takes a caller-provided token directly in code — no env vars), plus the API URL and environment.\n\n```bash\n# Frontegg M2M (machine user)\n# Both are SECRETS — inject from a secret store (e.g. SSM SecureString at\n# /<service>/<env>/frontegg-m2m-client-id and /<service>/<env>/frontegg-m2m-secret),\n# never plaintext env/IaC vars. Beware trailing newlines when writing them.\nFRONTEGG_M2M_CLIENT_ID=...\nFRONTEGG_M2M_SECRET=...\n# Non-secret knobs (defaults shown; must match the environment you call —\n# tokens are environment-scoped)\nFRONTEGG_M2M_BASE_URL=https://login.axept.io\nFRONTEGG_M2M_TOKEN_PATH=/identity/resources/auth/v1/api-token\n\n# Common\nAXEPTIO_API_URL=https://api-staging.axept.io/v1\nAXEPTIO_ENVIRONMENT=staging  # or production\n\n# Headless CMP API — a different host from AXEPTIO_API_URL, needed only by the\n# headless-backed reads (analytics().consentStats()). Must point at the same\n# environment as AXEPTIO_API_URL: Frontegg tokens are environment-scoped.\nAXEPTIO_HEADLESS_API_URL=https://headless-api.axeptio.tech\n```\n\nThe CLI requires `FRONTEGG_M2M_CLIENT_ID` + `FRONTEGG_M2M_SECRET` and authenticates as the machine user; the legacy `--admin-username`/`--admin-password` options have been removed.\n\n## CLI Commands\n\n| Command | Description |\n|---------|-------------|\n| `run <state-id> <config-file>` | Execute provisioning (auto-resumes from existing state) |\n| `status` | Show all provisioning states |\n| `checkpoints <state-id>` | List available checkpoints |\n| `rollback <state-id>` | Delete all created entities |\n\n**Note:** The `resume` command has been removed. The `run` command now automatically resumes from existing state if present, merging fresh data with saved progress.\n| `clean <state-id>` | Remove state files |\n\n## API Reference\n\n### Client Configuration\n\n`ClientConfig` is a discriminated union: pick **one** of the two auth shapes.\n\n```typescript\ninterface ClientConfigBase {\n  baseURL?: string;          // Required via env or explicit value\n  headlessBaseURL?: string;  // Headless CMP host; required only by headless-backed reads\n  environment?: 'staging' | 'production';\n  maxRetries?: number;\n  retryDelay?: number;\n  rateLimitRpm?: number;\n  mockStripe?: boolean;\n  mockShake?: boolean;\n  mockPublish?: boolean;\n}\n\n// Frontegg M2M client credentials — the server mints/refreshes its own token\ninterface M2MClientConfig extends ClientConfigBase {\n  clientId: string;          // Frontegg machine-user clientId (secret)\n  secret: string;            // Frontegg machine-user secret (secret)\n  fronteggBaseUrl?: string;  // default https://login.axept.io\n  fronteggTokenPath?: string; // default /identity/resources/auth/v1/api-token\n}\n\n// Caller-supplied access token — attached verbatim, no mint/refresh\ninterface BearerClientConfig extends ClientConfigBase {\n  bearer: string;            // caller-minted Frontegg accessToken (secret)\n}\n\ntype ClientConfig = M2MClientConfig | BearerClientConfig;\n```\n\nSee **Authentication** above for the full behavior (mint, cache, 401 re-mint).\n\n### Extended Client Methods\n\nNew helper endpoints exposed by the client for bulk cookie configuration generation:\n\n- Templates & Steps\n  - `getCookieStepTemplates()` — list available cookie-step templates\n  - `getCookieStepTemplateByName(name)` — fetch a specific cookie-step template\n  - `getCustomCookieTemplate({ language, country?, subdivision?, steps })` — generate a cookie template from step names\n\n- Vendors & Categories\n  - `getVendorCategories(params?)`\n  - `getVendorSolutionLocalized(id, language)`\n\n- Project Admin Actions\n  - `lockProject(projectId)` / `unlockProject(projectId)`\n  - `unpublishProject(projectId)`\n  - `duplicateProject(projectId, { name?, websiteURL? })`\n\n- Media & Assets\n  - `uploadAsset({ content, filename, contentType? })` — upload a file to the asset store (`POST /assets`)\n  - `createMedia({ projectId, url, ... })` — register an uploaded asset as project media (`POST /vault/media`)\n  - `uploadMedia(projectId, { content, filename, contentType? })` — upload + register in one call\n  - `deleteMedia(id)` — delete a project media entry\n\n### Style and Template Helpers\n\n- Cookies\n  - `buildCookieConfiguration(client, { projectId, language, stepNames?, stepsOverride?, published?, googleConsentMode? })`\n  - `positionStepByNameAndIndex(steps, name, index)` / `concatenateOtherSteps(steps)`\n\n- Design\n  - `integrateStyleGuide(style: StyleGuideInput): ProjectStylesheet`\n\n### Execution Configuration\n\n```typescript\ninterface ExecutionConfig {\n  environment: 'staging' | 'production';\n  organizationBatchSize?: number;\n  projectBatchSize?: number;\n  configurationBatchSize?: number;\n  continueOnError?: boolean;\n  maxRetries?: number;\n  retryDelay?: number;\n}\n```\n\n**Note:** The `environment` field specifies the target environment ('staging' or 'production'). Dry-run behavior is controlled separately via the `--dry` flag or `isDryRun` parameter when creating the executor.\n\n## Examples\n\nSee the `examples/` directory for complete usage:\n\n- `basic-usage.ts` — Org, Project, Cookies, Terms, optional Subscription\n- `organizations-and-projects.ts` — Multiple projects + projects group\n- `cookies-config.ts` — Cookie configuration with steps/vendors\n- `terms-config.ts` — ContractsV2 Terms with config/content model\n- `vendors.ts` — Vendor company and solution creation\n- `billing.ts` — Stripe customer + VAT + subscription\n- `users-and-access.ts` — Users, invitations, and project access\n- `media-upload.ts` — Upload a file and register it as project media\n- `config.json` — Execution configuration\n\n## License\n\n© Axeptio 2025 - Proprietary\n","readmeFilename":"README.md"}