{"_id":"@blendededge/doohickey-hush-sdk","_rev":"13-560f11b136b2486628161e3090c13914","name":"@blendededge/doohickey-hush-sdk","dist-tags":{"latest":"0.0.13"},"versions":{"0.0.1":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.1","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.1","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"d7a68596a2b0840901f893a1b42ab1ce27bf92e4","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.1.tgz","fileCount":27,"integrity":"sha512-FkeSXxMmoC/illZcqtngpv4whFIPjqjbHo+zJ6hb6vL0xUfBNJDfWMluKtsvrCoNDNKcZJPTrcnsP6J+YsVlQQ==","signatures":[{"sig":"MEQCIBIx1d093Zix5c+9NyuvEGqdLHzCOCXbW2C6MzKn/ApWAiBylMJmexK5LoyMYjiAfXtM1c7qArmCPoXmojd4Tbe9VQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":83788},"main":"./dist/cjs/index.js","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"55df320cddf4ce27fa315e8019fa8101e3c79ade","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.json","test:unit":"vitest run tests/secret-client.test.ts","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test","test:integration":"vitest run tests/integration.test.ts","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for DBOS applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","dependencies":{"axios":"^1.6.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.29.0","vitest":"^3.2.4","typescript":"^5.3.0","@types/node":"^20.10.0","typescript-eslint":"^8.35.0","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.1_1752551627549_0.09753862011413439","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.2","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.2","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"6d5a2cd10c644fa9c95372829cf229a195f77648","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.2.tgz","fileCount":27,"integrity":"sha512-GOyyOKbjMxhNGBRZOZPFX92QecNbb6fxA+prGxxRKtd5voilRVSY7yzV15CEyvMB9u4cMOcSE4BYZEyvGZw4AQ==","signatures":[{"sig":"MEYCIQDgWSVSqFSOWRy55ZG8i0lHJ2e7zwZzKk0d3i4D6QAtHwIhAL+hRCskJsLet25jXsXlL/mSa3cONNclC06v2cS064NA","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":79633},"main":"./dist/cjs/index.js","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"55df320cddf4ce27fa315e8019fa8101e3c79ade","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.json","test:unit":"vitest run tests/secret-client.test.ts","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test","test:integration":"vitest run tests/integration.test.ts","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","dependencies":{"axios":"^1.6.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.29.0","vitest":"^3.2.4","typescript":"^5.3.0","@types/node":"^20.10.0","typescript-eslint":"^8.35.0","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.2_1752552536793_0.2709598985544994","host":"s3://npm-registry-packages-npm-production"}},"0.0.3":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.3","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.3","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"ae827fbdc12bd864d32d294e5bf0436206a35b7b","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.3.tgz","fileCount":27,"integrity":"sha512-HrtmhyDaYCD+m4IBVEP2FJiNLWSL2vFP4rTP+g9VnVoVlOHzHwKVZ4AQqvS7QbvMnUbUyCVJgqUiJokurMNRjg==","signatures":[{"sig":"MEQCIFJrP/1TrZgA6KFW0rK8APF3ympCb0yJEGpfxT5pqsdEAiBRw8GdZWfUXIC98MvTpzKhYk4OzIWleUnFaUzC7jpZWA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93338},"main":"./dist/cjs/index.js","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"6c2ec8607bb69c10204768e406fe6d6c1bac2793","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.json","test:unit":"vitest run tests/secret-client.test.ts","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test","test:integration":"vitest run tests/integration.test.ts","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","dependencies":{"axios":"^1.6.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.29.0","vitest":"^3.2.4","typescript":"^5.3.0","@types/node":"^20.10.0","typescript-eslint":"^8.35.0","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.3_1752783513914_0.19923858844453846","host":"s3://npm-registry-packages-npm-production"}},"0.0.4":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.4","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.4","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"378cfb42dd7cf85bd5e7b58887e93e7f10f07d1a","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.4.tgz","fileCount":27,"integrity":"sha512-p6RpyEGmyei6CX+jvJP1haOmVrCE7Gx6FhdCGp/3GSN7U5gPaLslBt+txQwOTYuqwy6EcqHiocqVTiAQrUzDtg==","signatures":[{"sig":"MEUCIBYdyg4IdIfeddloYwXLBePWzCoQIfns/qLw4darLsLkAiEAmib582bQGEwJinRzWqMQ7gz/uffaQ6QqOSLLfyOD12Y=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":93452},"main":"./dist/cjs/index.js","type":"module","types":"./dist/esm/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/esm/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"6becaba4ac10f427f19f2e6ae6b9644809ffc250","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"tsc -p tsconfig.cjs.json","build:esm":"tsc -p tsconfig.json","test:unit":"vitest run tests/secret-client.test.ts","test:watch":"vitest","test:coverage":"vitest run --coverage","prepublishOnly":"npm run build && npm run test","test:integration":"vitest run tests/integration.test.ts","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","dependencies":{"axios":"^1.6.0"},"_hasShrinkwrap":false,"devDependencies":{"eslint":"^9.29.0","vitest":"^3.2.4","typescript":"^5.3.0","@types/node":"^20.10.0","typescript-eslint":"^8.35.0","@vitest/coverage-v8":"^3.2.4"},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.4_1752785085558_0.31908306939679565","host":"s3://npm-registry-packages-npm-production"}},"0.0.5":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.5","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.5","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"d1005686cb3241d452e55d6a41b93e1d5de61dfb","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.5.tgz","fileCount":15,"integrity":"sha512-72Ww9PAiUPXgGGvLiGHZpGGU22htJrc8Rq4vJtLWepjN8Ms9/JY2BjWQ+MGhFYTs8quU572sKf9gfjCYUuorbg==","signatures":[{"sig":"MEUCIQCFxo+SOvb55gZdj3g8USTn81P76+st4rlgbrojWTgtUgIgYWVqlsgRCURO+aThAxe4sF2VmxhmthA1sn9aXruIAxY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1002598},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"a74c49329e3626c139311fbd15c27b4e87fd2ef0","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","openid-client":"^6.6.2","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.5_1753113669410_0.15605943232699038","host":"s3://npm-registry-packages-npm-production"}},"0.0.6":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.6","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.6","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"97888f643699e29281622444bb715e7c1d358f75","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.6.tgz","fileCount":15,"integrity":"sha512-L8KmtyFFuEXM+yFp11o8fUAlZHmV191Rksjr59QVBEl59X2PwGiu0lKW/1+chkUv8J83zlfo8ciyQdN2RD9uMA==","signatures":[{"sig":"MEUCIBRAgP9eV+Cwh5Y0cAaquPZp2ZNmT6++KRW4y75s2JFlAiEA1w4NnXj6s9pSa6+B12cDXe3ijgjvnZTZI4B0SbD/m9A=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1002701},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"b7cdff30385b58038466c359e18c377ba6c00dfb","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.6_1753114050306_0.4119011069810552","host":"s3://npm-registry-packages-npm-production"}},"0.0.7":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.7","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.7","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"1aefe042b93bf97301f814cda763c2d345146b27","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.7.tgz","fileCount":15,"integrity":"sha512-H7pL6OLmxp598Ue2N6/ijaFdmeUlCnGY0tEp06sl+H6hZStRNy2DFA0a7c5doDlbQ+Hsz7eS44pKDzkhnDZbhw==","signatures":[{"sig":"MEQCICNuIJURj3C+5n3anR4q9iZ5A6SCPO1C6NZwEmjc1L6FAiBjEW+1mYICHUkPcAmLTz+HuTcM+g8JOmMvzfRUHNL49g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1069761},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"164a3b2690e0607cc9d160aeb220e9acf7036124","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.7_1753119271709_0.13122587434975097","host":"s3://npm-registry-packages-npm-production"}},"0.0.8":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.8","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.8","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"0353a82dba7efcbda59bd97993ca0480dbf719ce","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.8.tgz","fileCount":15,"integrity":"sha512-OuHoL1ExzqM/4sL4bOdbbC1x4ZLZwQJpWuz85mAhcA4c27iCxT1WnAExT5jBYd0ZL6t4BNJu1GR2sKF6UwPeGA==","signatures":[{"sig":"MEYCIQDdCWtwER21LUadpOdGB1AV/vgSUopg7XuxBHCWRJVDywIhAKogL8gTdiaNbW4VaBrzCe5GrL7TZ74QxhG8zs4q+UjR","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1069385},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"0131553631d504368401a39ee4b411254866ab73","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.8_1753127241573_0.4879881448909793","host":"s3://npm-registry-packages-npm-production"}},"0.0.9":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.9","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.9","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"41f608865846c3ad335fc0d218bc0dba4dbc6909","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.9.tgz","fileCount":15,"integrity":"sha512-v+5RcpeaI93cfAVqf1K+cAL4G+uP9Ji71YTymJo4nNgXB8YpleZynJml0zuwI8Rm7I1EA5sgcvPyrsg4vYNuFQ==","signatures":[{"sig":"MEYCIQDiGvFj+KhrO0Ucnj79FBtjga4oxZyPvUD0STRfRPMiRwIhANbu2xzUCyleN+wxuSgzID+N2y6NGgFXtGUM6wL3/OEj","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1146323},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"b74504da93ee535150bac9a3ca0a36729ee3bb97","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.9_1753155990291_0.6143357368825291","host":"s3://npm-registry-packages-npm-production"}},"0.0.10":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.10","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.10","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"39bd6d2c95900521f2437cc5eb7681d938a515b6","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.10.tgz","fileCount":15,"integrity":"sha512-X5EDsF9VTBHg2GrVFQ1dR+KfnPCi7iMdIKNCrYAATpzuU0XpTsk5PJDbkk+fyFfmDs0o4FfvJh1vPLull3lJaA==","signatures":[{"sig":"MEUCIDsweMjTobeNJeWo9kSvzs9IrsSuO2DMNsr2NfFcvBn4AiEA4jK71EGYAuTFXbPyXGYHOl6TzXxQLFzAkOI73BAOb6g=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1154340},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"4331b39a4a620d45e766e8d30da923f727260a0d","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.10_1753163523534_0.17117766921196975","host":"s3://npm-registry-packages-npm-production"}},"0.0.11":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.11","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.11","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"331ed3f09b2b738691a6cc01d06a2410e75cff6c","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.11.tgz","fileCount":15,"integrity":"sha512-hbykJ6yHwJl0TyqjtDR1MBhDGEVnk9GdwLBlJEoGAf5KwJPBit7ABLk09csao/cXkmJaETVUn2PLr6bTEV66vg==","signatures":[{"sig":"MEUCIDwJulxfVPEdsYRAuDA7PHiji0rsssG1aw13nmtSwkT4AiEAqnQOTWYqrIBZxXiCk5aCnczFIk8wRQbvY91gp+s5AuQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1155617},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"6f270049820353bc7daaa1ff4dc0d227ee056075","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:ulid --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","ulid":"^2.3.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","ulid":"^2.3.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"ulid":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.11_1753171101436_0.25493536706278785","host":"s3://npm-registry-packages-npm-production"}},"0.0.12":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.12","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.12","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"dist":{"shasum":"a5a49880519ba07ef5f49007aea4dfcde4e6a5c7","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.12.tgz","fileCount":15,"integrity":"sha512-MJBbaQED7pOONa7BFujexv8xlBMMHw2JBAPoOH27QSrcTeTxeRloc7Piiuci7xqQcZolhrZsCwvcT+K0rgNH7A==","signatures":[{"sig":"MEUCIDUFuf14KF9RrIMF8CG4tLXj18w9C+5iDJRJaxgphazAAiEAtZYInmxNwvVGHnZ2w2bqA9FsLky3soLQoW6MYfIt+1Q=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":1153761},"main":"./dist/cjs/index.js","type":"module","types":"./dist/types/index.d.ts","module":"./dist/esm/index.js","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"gitHead":"292728f2b8778acfb6f22509a721b3b90a9e527f","imports":{"@packages/secret-types":"../../packages/secret-types"},"private":false,"scripts":{"dev":"tsc --watch","lint":"eslint src --ext .ts","test":"vitest run","build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","clean":"rm -rf dist","lint:fix":"eslint src --ext .ts --fix","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:watch":"vitest","build:types":"tsc","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","prepublishOnly":"npm run build && npm run test","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json"},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"_npmVersion":"10.9.2","description":"SDK for integration applications to interact with the hush service","directories":{},"_nodeVersion":"22.17.0","_hasShrinkwrap":false,"devDependencies":{"knex":"^3.1.0","eslint":"^9.29.0","vitest":"^3.2.4","esbuild":"^0.25.6","typescript":"^5.3.0","@types/node":"^20.10.0","openid-client":"^6.6.2","typescript-eslint":"^8.35.0","libsodium-wrappers":"^0.7.11","@vitest/coverage-v8":"^3.2.4","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@packages/secret-shared-lib":"file:../../packages/secret-shared-lib"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","libsodium-wrappers":"^0.7.11"},"optionalDependencies":{"openid-client":"^6.6.2","@aws-sdk/client-ssm":">=3.0.0 <4.0.0"},"peerDependenciesMeta":{"knex":{"optional":false},"openid-client":{"optional":true},"libsodium-wrappers":{"optional":false},"@aws-sdk/client-ssm":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/doohickey-hush-sdk_0.0.12_1753171969364_0.23111812283876265","host":"s3://npm-registry-packages-npm-production"}},"0.0.13":{"name":"@blendededge/doohickey-hush-sdk","version":"0.0.13","private":false,"type":"module","description":"SDK for integration applications to interact with the hush service","main":"./dist/cjs/index.js","module":"./dist/esm/index.js","types":"./dist/types/index.d.ts","exports":{".":{"types":"./dist/types/index.d.ts","import":"./dist/esm/index.js","require":"./dist/cjs/index.js"}},"imports":{"@packages/secret-types":"../../packages/secret-types"},"engines":{"node":">=20.0.0"},"scripts":{"build":"npm run clean && npm run build:esm && npm run build:cjs && npm run build:types && npm run build:cjs-package","build:esm":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=esm --outfile=dist/esm/index.js --external:knex --external:libsodium-wrappers --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:cjs":"esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/cjs/index.js --external:knex --external:libsodium-wrappers --external:@aws-sdk/client-ssm --external:openid-client --sourcemap","build:types":"tsc","build:cjs-package":"echo '{\"type\":\"commonjs\"}' > dist/cjs/package.json","dev":"tsc --watch","test":"vitest run","test:watch":"vitest","test:coverage":"vitest run --coverage","test:postgres":"vitest run tests/postgres-secret-client.test.ts","test:oauth":"vitest run tests/oauth-manager.test.ts","test:unit":"vitest run tests/postgres-secret-client.test.ts tests/postgres-convenience-functions.test.ts tests/oauth-manager.test.ts","lint":"eslint src --ext .ts","lint:fix":"eslint src --ext .ts --fix","clean":"rm -rf dist","prepublishOnly":"npm run build && npm run test"},"peerDependencies":{"knex":">=3.0.0 <4.0.0","libsodium-wrappers":"^0.7.11"},"peerDependenciesMeta":{"knex":{"optional":false},"libsodium-wrappers":{"optional":false},"openid-client":{"optional":true},"@aws-sdk/client-ssm":{"optional":true}},"optionalDependencies":{"@aws-sdk/client-ssm":">=3.0.0 <4.0.0","openid-client":"^6.6.2"},"devDependencies":{"@packages/secret-shared-lib":"file:../../packages/secret-shared-lib","@packages/secret-types":"file:../../packages/secret-types","@types/libsodium-wrappers":"^0.7.11","@types/node":"^20.10.0","@vitest/coverage-v8":"^3.2.4","esbuild":"^0.25.6","eslint":"^9.29.0","knex":"^3.1.0","libsodium-wrappers":"^0.7.11","openid-client":"^6.6.2","typescript":"^5.3.0","typescript-eslint":"^8.35.0","vitest":"^3.2.4"},"keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","_id":"@blendededge/doohickey-hush-sdk@0.0.13","gitHead":"e459d2379660821353358f26ccdf956dba213eed","_nodeVersion":"22.17.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-kA/+NCVYaTCdYyt94QZz7teBNEWFbi9m0hcEQHR2UdPVefvgMGRZoGacKGCcdz4V1rHUvmsMzzwZOaUXzwFnzQ==","shasum":"5c13bef11ed21715ad63632ca5d0e39b2aaeac5a","tarball":"https://registry.npmjs.org/@blendededge/doohickey-hush-sdk/-/doohickey-hush-sdk-0.0.13.tgz","fileCount":15,"unpackedSize":1164021,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIE5d13K4W/rnf7Tkyj/UdbIO+onukBHCSh0sLQr+fgMIAiEAj1flWshgfzGU5tJYwjD4ihW84tX5A/rk12ntYlGzJA4="}]},"_npmUser":{"name":"blendededge","email":"robb@blendededge.com"},"directories":{},"maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/doohickey-hush-sdk_0.0.13_1753193763511_0.7121524231578427"},"_hasShrinkwrap":false}},"time":{"created":"2025-07-15T03:53:47.418Z","modified":"2025-07-22T14:16:03.857Z","0.0.1":"2025-07-15T03:53:47.738Z","0.0.2":"2025-07-15T04:08:56.974Z","0.0.3":"2025-07-17T20:18:34.093Z","0.0.4":"2025-07-17T20:44:45.769Z","0.0.5":"2025-07-21T16:01:09.693Z","0.0.6":"2025-07-21T16:07:30.562Z","0.0.7":"2025-07-21T17:34:31.979Z","0.0.8":"2025-07-21T19:47:21.824Z","0.0.9":"2025-07-22T03:46:30.473Z","0.0.10":"2025-07-22T05:52:03.737Z","0.0.11":"2025-07-22T07:58:21.613Z","0.0.12":"2025-07-22T08:12:49.576Z","0.0.13":"2025-07-22T14:16:03.689Z"},"author":{"name":"Blended Edge Inc."},"license":"UNLICENSED","keywords":["dbos","hush","workflow","sdk","authentication","secrets"],"description":"SDK for integration applications to interact with the hush service","maintainers":[{"name":"blendededge","email":"robb@blendededge.com"}],"readme":"# Doohickey Secret Management SDK\n\nA PostgreSQL-based secret management library designed specifically for DBOS applications. This SDK provides secure, encrypted storage of secrets directly in your PostgreSQL database using envelope encryption with libsodium.\n\n## Overview\n\nThis library eliminates the need for external secret management services by storing encrypted secrets directly in your DBOS application's PostgreSQL database. It's built to work seamlessly with DBOS transaction decorators and provides enterprise-grade features including multi-tenant isolation, comprehensive audit logging, and automatic encryption.\n\n**Key Architecture:**\n- **Direct PostgreSQL Storage**: No external services required - all data stored in your DBOS database\n- **Envelope Encryption**: Two-layer security with libsodium XSalsa20-Poly1305 AEAD encryption\n- **DBOS Integration**: Native support for `@DBOS.transaction()` and `@DBOS.workflow()` decorators\n- **Multi-tenant**: Row-level security with tenant isolation for SaaS applications\n- **OAuth2/OIDC**: Built-in OAuth2 and OpenID Connect with PKCE support\n- **Audit Trail**: SOC2-compliant logging with 7-year retention for compliance\n\n> **For Developers**: Technical implementation details, architecture specifications, database schemas, and contribution guidelines are available in [DEVELOPER.md](./DEVELOPER.md).\n\n## Features\n\n- **🔒 Secure Encryption**: Envelope encryption using libsodium XSalsa20-Poly1305 with rotating data encryption keys\n- **🏢 Multi-tenant**: Tenant-based isolation for SaaS applications with row-level security\n- **📋 Audit Logging**: SOC2-compliant audit trail with 7-year retention (2555 days)\n- **⚡ DBOS Native**: Seamless integration with DBOS transaction patterns and workflows\n- **🔐 OAuth2/OIDC**: Full OAuth2 and OpenID Connect support including Authorization Code (with PKCE), Client Credentials, Device Authorization, Resource Owner Password Credentials, and Generic Grant flows\n- **🔄 Token Management**: Automatic token refresh, secure state management, and multi-service support\n- **✅ Type Safety**: Full TypeScript support with comprehensive type definitions\n- **🗃️ Database Schema**: Automatic table creation, migration support, and optimized indexing\n- **🔍 Search & Pagination**: Built-in secret discovery, filtering, and management\n- **⚠️ Error Handling**: Comprehensive error handling, validation, and troubleshooting guidance\n\n## Installation\n\n```bash\nnpm install @blendededge/doohickey-hush-sdk\n```\n\n### Peer Dependencies\n\nThis SDK requires the following peer dependencies:\n\n```json\n{\n  \"peerDependencies\": {\n    \"knex\": \">=3.0.0 <4.0.0\",\n    \"libsodium-wrappers\": \"^0.7.11\",\n    \"openid-client\": \"^6.6.2\",\n  }\n}\n```\n\n**Note**: In DBOS applications, knex is provided automatically via `DBOS.knexClient`. The other dependencies are included for encryption (libsodium-wrappers) and OAuth2/OIDC support (openid-client). UUIDs are generated using Node.js built-in crypto.randomUUID().\n\n## Quick Start\n\n### 1. Generate Encryption Key\n\nFirst, generate a root encryption key for your application:\n\n```bash\n# Generate a secure 32-byte base64-encoded key\nnode -e \"console.log('ROOT_KEY_BASE64=' + require('crypto').randomBytes(32).toString('base64'))\"\n```\n\nAdd this to your environment variables:\n\n```bash\nROOT_KEY_BASE64=your-generated-key-here\n```\n\n### 2. Initialize Database Schema\n\nSet up the required database tables (run once during deployment):\n\n```typescript\nimport { DBOS } from '@dbos-inc/dbos-sdk';\nimport { PostgresSecretClient } from '@blendededge/doohickey-hush-sdk';\n\nexport class DatabaseSetup {\n  @DBOS.transaction()\n  static async initializeSecretTables() {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'setup', // Temporary tenant for schema setup\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    await client.initializeSchema();\n    console.log('Secret management tables created successfully');\n  }\n}\n```\n\n### 3. Basic Usage\n\n```typescript\nimport { DBOS } from '@dbos-inc/dbos-sdk';\nimport { PostgresSecretClient } from '@blendededge/doohickey-hush-sdk';\n\nexport class SecretWorkflow {\n  @DBOS.transaction()\n  static async saveApiKey() {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'tenant-123',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    await client.initialize();\n    \n    // Save a secret\n    const result = await client.saveSecret('stripe_api_key', 'sk_test_...', {\n      description: 'Stripe API key for payments',\n      tags: ['production', 'payment']\n    });\n    \n    return result;\n  }\n  \n  @DBOS.transaction()\n  static async getApiKey(): Promise<string> {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'tenant-123',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    await client.initialize();\n    \n    // Retrieve a secret\n    const secret = await client.getSecret('stripe_api_key');\n    return secret.value;\n  }\n}\n```\n\n### 4. Using Helper Functions\n\nFor simpler usage without managing client instances:\n\n```typescript\nimport { DBOS } from '@dbos-inc/dbos-sdk';\nimport { PostgresSecretWorkflowHelper } from '@blendededge/doohickey-hush-sdk';\n\nexport class QuickSecretAccess {\n  @DBOS.transaction()\n  static async saveAndRetrieve() {\n    const tenantId = 'tenant-456';\n    \n    // Save\n    await PostgresSecretWorkflowHelper.saveSecret(\n      DBOS.knexClient,\n      tenantId,\n      'database_url',\n      'postgresql://user:pass@host:5432/db',\n      { description: 'Production database URL' }\n    );\n    \n    // Retrieve\n    const secret = await PostgresSecretWorkflowHelper.getSecret(\n      DBOS.knexClient,\n      tenantId,\n      'database_url'\n    );\n    \n    return secret.value;\n  }\n}\n```\n\n## DBOS Architecture Patterns\n\n**Critical:** This SDK must follow DBOS architectural constraints for proper operation. Understanding these patterns is essential to avoid runtime errors.\n\n### DBOS Component Separation\n\nDBOS enforces strict separation between different types of operations:\n\n- **`@DBOS.transaction()`**: Database operations only (PostgreSQL queries)\n- **`@DBOS.step()`**: External API calls only (HTTP requests, third-party services)\n- **`@DBOS.workflow()`**: Orchestrates between transactions and steps\n\n### ❌ What NOT to Do\n\n**Never call transaction-decorated methods from within steps:**\n\n```typescript\n// ❌ WRONG - This will cause \"Invalid call to a `transaction` function from within a `step`\" error\nexport class BadExample {\n  @DBOS.step()\n  static async fetchDataFromAPI(): Promise<string> {\n    // External API call (correct for step)\n    const response = await fetch('https://api.example.com/data');\n    const data = await response.json();\n    \n    // ❌ WRONG - Cannot call transaction from step\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'tenant',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize(); // This calls @DBOS.transaction() internally!\n    await client.saveSecret('api-data', data); // This will fail!\n    \n    return data;\n  }\n}\n```\n\n### ✅ Correct Patterns\n\n**Pattern 1: Use transactions for secret operations, steps for API calls**\n\n```typescript\nexport class CorrectExample {\n  // ✅ CORRECT - Workflow orchestrates between transaction and step\n  @DBOS.workflow()\n  static async processExternalData(): Promise<string> {\n    // Step 1: Get access token from database (transaction)\n    const accessToken = await CorrectExample.getAccessTokenTransaction();\n    \n    // Step 2: Call external API with token (step)  \n    const apiData = await CorrectExample.fetchDataFromAPIStep(accessToken);\n    \n    // Step 3: Store result in secrets (transaction)\n    await CorrectExample.saveSecretTransaction('api-result', apiData);\n    \n    return apiData;\n  }\n  \n  // ✅ CORRECT - Transaction for database operations\n  @DBOS.transaction() \n  static async getAccessTokenTransaction(): Promise<string> {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'tenant',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    const secret = await client.getSecret('api-access-token');\n    return secret.value;\n  }\n  \n  // ✅ CORRECT - Step for external API calls\n  @DBOS.step()\n  static async fetchDataFromAPIStep(accessToken: string): Promise<string> {\n    const response = await fetch('https://api.example.com/data', {\n      headers: { 'Authorization': `Bearer ${accessToken}` }\n    });\n    return await response.text();\n  }\n  \n  // ✅ CORRECT - Transaction for database operations\n  @DBOS.transaction()\n  static async saveSecretTransaction(name: string, value: string): Promise<void> {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'tenant', \n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    await client.saveSecret(name, value, {\n      description: 'Data from external API',\n      tags: ['external', 'processed']\n    });\n  }\n}\n```\n\n**Pattern 2: Pass data between decorators rather than calling across boundaries**\n\n```typescript\nexport class DataFlowExample {\n  @DBOS.workflow()\n  static async authenticateAndCallService(): Promise<any> {\n    // 1. Get credentials (transaction)\n    const credentials = await DataFlowExample.getCredentialsTransaction();\n    \n    // 2. Authenticate with service (step) \n    const authToken = await DataFlowExample.authenticateStep(credentials);\n    \n    // 3. Store new token (transaction)\n    await DataFlowExample.updateTokenTransaction(authToken);\n    \n    // 4. Call service with token (step)\n    return await DataFlowExample.callServiceStep(authToken);\n  }\n  \n  @DBOS.transaction()\n  static async getCredentialsTransaction(): Promise<{clientId: string, clientSecret: string}> {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'auth',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    const clientId = await client.getSecret('oauth-client-id');\n    const clientSecret = await client.getSecret('oauth-client-secret');\n    \n    return {\n      clientId: clientId.value,\n      clientSecret: clientSecret.value\n    };\n  }\n  \n  @DBOS.step()\n  static async authenticateStep(credentials: {clientId: string, clientSecret: string}): Promise<string> {\n    const response = await fetch('https://auth.service.com/token', {\n      method: 'POST',\n      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },\n      body: new URLSearchParams({\n        grant_type: 'client_credentials',\n        client_id: credentials.clientId,\n        client_secret: credentials.clientSecret\n      })\n    });\n    \n    const tokenData = await response.json();\n    return tokenData.access_token;\n  }\n  \n  @DBOS.transaction()\n  static async updateTokenTransaction(token: string): Promise<void> {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'auth',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    await client.saveSecret('current-access-token', token, {\n      description: 'Current OAuth access token',\n      tags: ['oauth', 'current']\n    });\n  }\n  \n  @DBOS.step()\n  static async callServiceStep(token: string): Promise<any> {\n    const response = await fetch('https://api.service.com/data', {\n      headers: { 'Authorization': `Bearer ${token}` }\n    });\n    return await response.json();\n  }\n}\n```\n\n### Key Principles\n\n1. **Separate Concerns**: Database operations in `@DBOS.transaction()`, external calls in `@DBOS.step()`\n2. **Pass Data**: Don't call across decorator boundaries, pass data through workflow parameters  \n3. **Orchestrate in Workflows**: Use `@DBOS.workflow()` to coordinate between transactions and steps\n4. **Error Handling**: Each decorator type has its own retry and error handling semantics\n\n## Configuration\n\n### Configuration Interfaces\n\n```typescript\ninterface PostgresSecretClientConfig {\n  /** Tenant ID for multi-tenancy support (required) */\n  tenantId: string;\n  \n  /** Base64-encoded root encryption key (required) */\n  rootKeyBase64: string;\n  \n  /** Enable audit logging (default: true) */\n  auditEnabled?: boolean;\n  \n  /** Audit log retention in days (default: 2555 / 7 years) */\n  retentionDays?: number;\n  \n  /** Enable row-level security (default: false) */\n  enableRowLevelSecurity?: boolean;\n  \n  /** Optional logger for debugging and monitoring */\n  logger?: Logger;\n}\n\ninterface OAuthClientConfig {\n  /** OIDC issuer URL (for auto-discovery) OR manual endpoints */\n  issuerUrl?: string;\n  \n  /** Manual OAuth2 endpoints (if not using OIDC discovery) */\n  authorizationEndpoint?: string;\n  tokenEndpoint?: string;\n  revocationEndpoint?: string;\n  userinfoEndpoint?: string;\n  \n  /** OAuth2 client credentials */\n  clientId: string;\n  clientSecret: string;\n  \n  /** Redirect URIs for OAuth callback (required for authorization_code flow) */\n  redirectUris: string[];\n  \n  /** OAuth scopes to request */\n  scopes: string[];\n  \n  /** Additional OAuth2/OIDC configuration */\n  responseTypes?: string[]; // default: ['code']\n  grantTypes?: string[]; // default: ['authorization_code']\n  \n  /** \n   * Supported grant types for this client\n   * - 'authorization_code': Interactive web/mobile authentication with PKCE\n   * - 'client_credentials': Machine-to-machine authentication\n   * - 'device_code': Smart TV/IoT device authentication\n   * - 'password': Legacy username/password authentication (discouraged)\n   * - 'refresh_token': Token refresh capability\n   * - 'generic': Support for custom/future grant types\n   */\n  supportedGrantTypes?: ('authorization_code' | 'client_credentials' | 'device_code' | 'password' | 'refresh_token' | 'generic')[];\n}\n```\n\n### Configuration Examples\n\n#### Google OAuth2/OIDC (Recommended)\n```typescript\nconst googleConfig: OAuthClientConfig = {\n  issuerUrl: 'https://accounts.google.com',\n  clientId: process.env.GOOGLE_CLIENT_ID!,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  redirectUri: `${process.env.BASE_URL}/oauth/callback/google`,\n  scope: ['openid', 'profile', 'email'],\n  usePkce: true,\n  pkceMethod: 'S256'\n};\n```\n\n#### GitHub OAuth2 (Manual Configuration)\n```typescript\nconst githubConfig: OAuthClientConfig = {\n  authorizationEndpoint: 'https://github.com/login/oauth/authorize',\n  tokenEndpoint: 'https://github.com/login/oauth/access_token',\n  userinfoEndpoint: 'https://api.github.com/user',\n  clientId: process.env.GITHUB_CLIENT_ID!,\n  clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n  redirectUri: `${process.env.BASE_URL}/oauth/callback/github`,\n  scope: ['user:email', 'read:user'],\n  usePkce: true,\n  tokenEndpointAuthMethod: 'client_secret_post'\n};\n```\n\n#### Microsoft Azure AD\n```typescript\nconst microsoftConfig: OAuthClientConfig = {\n  issuerUrl: 'https://login.microsoftonline.com/common/v2.0',\n  clientId: process.env.MICROSOFT_CLIENT_ID!,\n  clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,\n  redirectUri: `${process.env.BASE_URL}/oauth/callback/microsoft`,\n  scope: ['openid', 'profile', 'email', 'User.Read'],\n  usePkce: true\n};\n```\n\n### Environment Variables\n\n```bash\n# Required: Root encryption key (32-byte base64-encoded)\nROOT_KEY_BASE64=base64-encoded-32-byte-key\n\n# OAuth2/OIDC Configuration (optional)\nGOOGLE_CLIENT_ID=your-google-client-id\nGOOGLE_CLIENT_SECRET=your-google-client-secret\nGITHUB_CLIENT_ID=your-github-client-id\nGITHUB_CLIENT_SECRET=your-github-client-secret\nMICROSOFT_CLIENT_ID=your-microsoft-client-id\nMICROSOFT_CLIENT_SECRET=your-microsoft-client-secret\nSLACK_CLIENT_ID=your-slack-client-id\nSLACK_CLIENT_SECRET=your-slack-client-secret\n\n# Application configuration\nBASE_URL=https://yourapp.com\n\n# DBOS provides these automatically:\n# DATABASE_URL=postgresql://...\n# Knex client via DBOS.knexClient\n\n# Optional: Logging and debugging\nDEBUG=doohickey:*\nLOG_LEVEL=info\n```\n\n### Key Generation\n\n```typescript\nimport { SecretEncryption } from '@packages/secret-shared-lib';\n\n// Generate a new root key programmatically\nconst rootKey = await SecretEncryption.generateRootKey();\nconsole.log('ROOT_KEY_BASE64=' + rootKey);\n\n// Validate an existing key\nconst isValid = await SecretEncryption.validateRootKey(process.env.ROOT_KEY_BASE64!);\n```\n\n## OAuth2/OIDC Integration\n\nThe SDK includes comprehensive OAuth2 and OpenID Connect support through the `OAuthManager` class:\n\n### OAuth Setup\n\n```typescript\nimport { DBOS } from '@dbos-inc/dbos-sdk';\nimport { OAuthManager } from '@blendededge/doohickey-hush-sdk';\n\nexport class OAuthSetup {\n  @DBOS.transaction()\n  static async initializeOAuth() {\n    // Initialize OAuth manager\n    await OAuthManager.initialize(DBOS.knexClient, {\n      tenantId: 'oauth-tenant',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    // Configure multiple OAuth providers\n    await OAuthManager.initializeAllClients({\n      google: {\n        issuerUrl: 'https://accounts.google.com',\n        clientId: process.env.GOOGLE_CLIENT_ID!,\n        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n        redirectUri: 'https://yourapp.com/oauth/callback'\n      },\n      github: {\n        authorizationEndpoint: 'https://github.com/login/oauth/authorize',\n        tokenEndpoint: 'https://github.com/login/oauth/access_token',\n        clientId: process.env.GITHUB_CLIENT_ID!,\n        clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n        redirectUri: 'https://yourapp.com/oauth/github/callback'\n      }\n    });\n  }\n}\n```\n\n### OAuth Flow Implementation\n\n```typescript\nexport class OAuthFlow {\n  @DBOS.transaction()\n  static async startGoogleAuth() {\n    // Start OAuth flow with PKCE\n    const { authUrl, state } = await OAuthManager.startAuthFlow(\n      'google',\n      ['profile', 'email', 'openid']\n    );\n    \n    return { authUrl, state };\n  }\n  \n  @DBOS.transaction()\n  static async handleOAuthCallback(callbackUrl: string) {\n    // Handle OAuth callback and get tokens\n    const tokenData = await OAuthManager.handleCallback('google', callbackUrl);\n    \n    return {\n      accessToken: tokenData.access_token,\n      refreshToken: tokenData.refresh_token,\n      expiresIn: tokenData.expires_in\n    };\n  }\n  \n  @DBOS.transaction()\n  static async getValidToken(): Promise<string> {\n    // Get valid access token (automatically refreshes if expired)\n    return await OAuthManager.getValidAccessToken('google');\n  }\n}\n```\n\n## OAuth Grant Types\n\nThe SDK supports all major OAuth2 grant types for different authentication scenarios:\n\n### 1. Authorization Code Grant (Interactive Authentication)\n\n**Use Case**: Web applications, mobile apps, and any scenario requiring user interaction.\n\n**Features**:\n- PKCE (Proof Key for Code Exchange) enabled by default for security\n- Supports both OIDC discovery and manual endpoint configuration\n- Automatic token refresh with stored refresh tokens\n\n#### Complete Setup with Express Endpoints\n\n```typescript\nimport { DBOS, WorkflowQueue } from '@dbos-inc/dbos-sdk';\nimport { OAuthManager, PostgresSecretClient } from '@blendededge/doohickey-hush-sdk';\nimport express from 'express';\nimport session from 'express-session';\n\nexport const app = express();\napp.use(express.json());\n\n// Configure session middleware\napp.use(session({\n  secret: process.env.SESSION_SECRET!,\n  resave: false,\n  saveUninitialized: false,\n  cookie: {\n    secure: process.env.NODE_ENV === 'production', // HTTPS in production\n    httpOnly: true,\n    maxAge: 24 * 60 * 60 * 1000 // 24 hours\n  }\n}));\n\nconst oauthQueue = new WorkflowQueue('oauth_queue');\n\nexport class InteractiveAuth {\n  @DBOS.transaction()\n  static async initializeOAuthClient(): Promise<void> {\n    // Initialize OAuth manager\n    await OAuthManager.initialize(DBOS.knexClient, {\n      tenantId: 'oauth-system',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n\n    // Configure client for authorization code flow\n    await OAuthManager.initializeClient('google', {\n      issuerUrl: 'https://accounts.google.com',\n      clientId: process.env.GOOGLE_CLIENT_ID!,\n      clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n      redirectUris: ['https://yourapp.com/oauth/callback/google'],\n      scopes: ['profile', 'email', 'openid'],\n      supportedGrantTypes: ['authorization_code', 'refresh_token']\n    });\n  }\n\n  @DBOS.workflow()\n  static async startAuthWorkflow(userId?: string): Promise<{ authUrl: string; state: string }> {\n    // Start OAuth flow\n    const { authUrl, state } = await InteractiveAuth.startAuthTransaction();\n    \n    // Optionally store user context with state\n    if (userId) {\n      await InteractiveAuth.storeUserContextTransaction(state, userId);\n    }\n    \n    return { authUrl, state };\n  }\n\n  @DBOS.transaction()\n  static async startAuthTransaction(): Promise<{ authUrl: string; state: string }> {\n    return await OAuthManager.startAuthFlow('google', ['profile', 'email', 'openid']);\n  }\n\n  @DBOS.transaction()\n  static async storeUserContextTransaction(state: string, userId: string): Promise<void> {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'oauth-context',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    await client.saveSecret(`oauth-user-${state}`, userId, {\n      description: `User context for OAuth state ${state}`,\n      tags: ['oauth', 'temporary']\n    });\n  }\n\n  @DBOS.workflow()\n  static async handleCallbackWorkflow(callbackUrl: string, state: string): Promise<{\n    userId: string | null;\n    accessToken: string;\n    refreshToken?: string;\n    expiresAt?: number;\n  }> {\n    // Handle OAuth callback and exchange code for tokens\n    const tokens = await InteractiveAuth.handleCallbackTransaction(callbackUrl);\n    \n    // Get user context if stored\n    const userId = await InteractiveAuth.getUserContextTransaction(state);\n    \n    return {\n      userId,\n      accessToken: tokens.access_token!,\n      refreshToken: tokens.refresh_token,\n      expiresAt: tokens.expires_at\n    };\n  }\n\n  @DBOS.transaction()\n  static async handleCallbackTransaction(callbackUrl: string): Promise<any> {\n    return await OAuthManager.handleCallback('google', callbackUrl);\n  }\n\n  @DBOS.transaction()\n  static async getUserContextTransaction(state: string): Promise<string | null> {\n    try {\n      const client = new PostgresSecretClient(DBOS.knexClient, {\n        tenantId: 'oauth-context',\n        rootKeyBase64: process.env.ROOT_KEY_BASE64!\n      });\n      await client.initialize();\n      \n      const userSecret = await client.getSecret(`oauth-user-${state}`);\n      const userId = userSecret.value;\n      \n      // Clean up temporary state\n      await client.deleteSecret(`oauth-user-${state}`);\n      \n      return userId;\n    } catch {\n      return null;\n    }\n  }\n\n  @DBOS.step()\n  static async getUserProfileStep(accessToken: string): Promise<any> {\n    // External API call to get user profile\n    const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {\n      headers: { 'Authorization': `Bearer ${accessToken}` }\n    });\n    \n    if (!response.ok) {\n      throw new Error(`Failed to get user profile: ${response.statusText}`);\n    }\n    \n    return await response.json();\n  }\n\n  @DBOS.workflow()\n  static async getUserProfileWorkflow(accessToken: string): Promise<any> {\n    return await InteractiveAuth.getUserProfileStep(accessToken);\n  }\n}\n\n// Express endpoints for OAuth flow\napp.get('/auth/login', async (req: any, res: any): Promise<void> => {\n  try {\n    // Get user ID from session/request if available\n    const userId = req.session?.userId || req.query.user_id;\n    \n    const handle = await DBOS.startWorkflow(InteractiveAuth, {queueName: oauthQueue.name})\n      .startAuthWorkflow(userId);\n    const result = await handle.getResult();\n    \n    // Store state in session for security\n    req.session.oauthState = result.state;\n    \n    // Redirect to OAuth provider\n    res.redirect(result.authUrl);\n  } catch (error) {\n    DBOS.logger.error(`OAuth login error: ${(error as Error).message}`);\n    res.status(500).json({ error: 'Failed to start OAuth flow' });\n  }\n});\n\napp.get('/oauth/callback/google', async (req: any, res: any): Promise<void> => {\n  try {\n    const callbackUrl = `${req.protocol}://${req.get('host')}${req.originalUrl}`;\n    const state = req.query.state;\n    \n    // Verify state matches what we stored in session\n    if (req.session.oauthState !== state) {\n      throw new Error('Invalid OAuth state');\n    }\n    \n    // Clear state from session\n    delete req.session.oauthState;\n    \n    // Handle the callback\n    const handle = await DBOS.startWorkflow(InteractiveAuth, {queueName: oauthQueue.name})\n      .handleCallbackWorkflow(callbackUrl, state);\n    const result = await handle.getResult();\n    \n    if (result.userId) {\n      // Update user session with tokens\n      req.session.userId = result.userId;\n      req.session.accessToken = result.accessToken;\n    }\n    \n    // Redirect to success page or dashboard\n    res.redirect('/dashboard?login=success');\n  } catch (error) {\n    DBOS.logger.error(`OAuth callback error: ${(error as Error).message}`);\n    res.redirect('/login?error=oauth_failed');\n  }\n});\n\napp.get('/auth/profile', async (req: any, res: any): Promise<void> => {\n  try {\n    if (!req.session.accessToken) {\n      res.status(401).json({ error: 'Not authenticated' });\n      return;\n    }\n    \n    // Get user profile using stored token\n    const handle = await DBOS.startWorkflow(InteractiveAuth, {queueName: oauthQueue.name})\n      .getUserProfileWorkflow(req.session.accessToken);\n    const profileData = await handle.getResult();\n    \n    res.json(profileData);\n  } catch (error) {\n    DBOS.logger.error(`Profile error: ${(error as Error).message}`);\n    res.status(500).json({ error: 'Failed to get profile' });\n  }\n});\n\napp.get('/auth/logout', async (req: any, res: any): Promise<void> => {\n  try {\n    // Optional: Revoke tokens\n    if (req.session.accessToken) {\n      await OAuthManager.revokeTokens('google');\n    }\n    \n    // Clear session\n    req.session.destroy((err: any) => {\n      if (err) {\n        DBOS.logger.error(`Session destroy error: ${err.message}`);\n      }\n    });\n    \n    res.json({ message: 'Logged out successfully' });\n  } catch (error) {\n    DBOS.logger.error(`Logout error: ${(error as Error).message}`);\n    res.status(500).json({ error: 'Logout failed' });\n  }\n});\n\nasync function main(): Promise<void> {\n  DBOS.setConfig({\n    name: 'oauth-app',\n    databaseUrl: process.env.DBOS_DATABASE_URL!\n  });\n  \n  // Initialize OAuth client on startup\n  await InteractiveAuth.initializeOAuthClient();\n  \n  await DBOS.launch({ expressApp: app });\n  const PORT = 3000;\n  app.listen(PORT, () => {\n    console.log(`🚀 Server is running on http://localhost:${PORT}`);\n  });\n}\n\nmain().catch(console.log);\n```\n\n#### Frontend Integration\n\n```typescript\n// Frontend code to initiate OAuth flow\nconst startLogin = async (): Promise<void> => {\n  // Redirect to your login endpoint\n  window.location.href = '/auth/login';\n};\n\n// Check authentication status\nconst checkAuth = async (): Promise<any> => {\n  try {\n    const response = await fetch('/auth/profile');\n    if (response.ok) {\n      const profile = await response.json();\n      console.log('User authenticated:', profile);\n      return profile;\n    } else {\n      console.log('User not authenticated');\n      return null;\n    }\n  } catch (error) {\n    console.error('Auth check failed:', error);\n    return null;\n  }\n};\n\n// Logout\nconst logout = async (): Promise<void> => {\n  try {\n    await fetch('/auth/logout');\n    window.location.href = '/login';\n  } catch (error) {\n    console.error('Logout failed:', error);\n  }\n};\n```\n\n### 2. Client Credentials Grant (Machine-to-Machine Authentication)\n\n**Use Case**: Server-to-server communication, API access, microservices, background jobs.\n\n**Features**:\n- No user interaction required\n- Perfect for automated systems and service accounts\n- Tokens are cached and automatically refreshed\n\n```typescript\nexport class ServiceAuth {\n  @DBOS.transaction()\n  static async setupClientCredentials(): Promise<void> {\n    // Configure client for client credentials flow\n    await OAuthManager.initializeClient('api-service', {\n      tokenEndpoint: 'https://auth.service.com/oauth/token',\n      clientId: process.env.SERVICE_CLIENT_ID!,\n      clientSecret: process.env.SERVICE_CLIENT_SECRET!,\n      redirectUris: [], // Not needed for client credentials\n      scopes: ['api:read', 'api:write'],\n      supportedGrantTypes: ['client_credentials']\n    });\n  }\n\n  @DBOS.transaction()\n  static async getServiceTokenTransaction(): Promise<string> {\n    // Get client credentials token\n    const tokens = await OAuthManager.getClientCredentialsToken('api-service', ['api:read', 'api:write']);\n    return tokens.access_token!;\n  }\n\n  @DBOS.step()\n  static async callProtectedAPIStep(accessToken: string, data: any): Promise<any> {\n    // Use token for API call (external service call in step)\n    const response = await fetch('https://api.service.com/data', {\n      headers: { \n        'Authorization': `Bearer ${accessToken}`,\n        'Content-Type': 'application/json'\n      },\n      method: 'POST',\n      body: JSON.stringify(data)\n    });\n    \n    if (!response.ok) {\n      throw new Error(`API call failed: ${response.statusText}`);\n    }\n    \n    return await response.json();\n  }\n\n  @DBOS.workflow()\n  static async processDataWorkflow(data: any): Promise<any> {\n    // Get valid token (automatically handles refresh)\n    const accessToken = await ServiceAuth.getServiceTokenTransaction();\n    \n    // Call external API with token\n    const result = await ServiceAuth.callProtectedAPIStep(accessToken, data);\n    \n    return result;\n  }\n}\n```\n\n### 3. Device Authorization Grant (Device Flow)\n\n**Use Case**: Smart TVs, CLI tools, IoT devices, gaming consoles, devices without browsers.\n\n**Features**:\n- User enters code on a separate device (phone/computer)\n- No need for embedded browser in the device\n- Secure for devices with limited input capabilities\n\n```typescript\nexport class DeviceAuth {\n  @DBOS.transaction()\n  static async setupDeviceFlow(): Promise<void> {\n    // Configure client for device authorization flow\n    await OAuthManager.initializeClient('smart-tv', {\n      issuerUrl: 'https://accounts.google.com',\n      clientId: process.env.GOOGLE_TV_CLIENT_ID!,\n      clientSecret: process.env.GOOGLE_TV_CLIENT_SECRET!,\n      redirectUris: [], // Not needed for device flow\n      scopes: ['https://www.googleapis.com/auth/youtube.readonly'],\n      supportedGrantTypes: ['device_code']\n    });\n  }\n\n  @DBOS.transaction()\n  static async startDeviceAuthTransaction(): Promise<{\n    userCode: string;\n    verificationUri: string;\n    verificationUriComplete?: string;\n    expiresIn: number;\n    deviceResponse: any;\n  }> {\n    // Initiate device flow\n    const deviceResponse = await OAuthManager.initiateDeviceFlow('smart-tv', ['https://www.googleapis.com/auth/youtube.readonly']);\n    \n    return {\n      userCode: deviceResponse.user_code,\n      verificationUri: deviceResponse.verification_uri,\n      verificationUriComplete: deviceResponse.verification_uri_complete,\n      expiresIn: deviceResponse.expires_in,\n      deviceResponse // Store this for polling\n    };\n  }\n\n  @DBOS.transaction()\n  static async pollForApprovalTransaction(deviceResponse: any): Promise<{\n    success: boolean;\n    pending?: boolean;\n    expired?: boolean;\n    accessToken?: string;\n    error?: string;\n  }> {\n    try {\n      // Poll for authorization (user must approve on another device)\n      const tokens = await OAuthManager.pollDeviceToken('smart-tv', deviceResponse);\n      \n      return {\n        success: true,\n        accessToken: tokens.access_token\n      };\n    } catch (error) {\n      const errorMessage = (error as Error).message;\n      if (errorMessage.includes('authorization_pending')) {\n        return { success: false, pending: true };\n      } else if (errorMessage.includes('expired_token')) {\n        return { success: false, expired: true };\n      } else {\n        return { success: false, error: errorMessage };\n      }\n    }\n  }\n\n  @DBOS.workflow()\n  static async deviceAuthWorkflow(): Promise<any> {\n    // Start device authentication\n    const deviceInfo = await DeviceAuth.startDeviceAuthTransaction();\n    \n    // Display code to user (in real app, show on TV screen)\n    DBOS.logger.info(`Go to ${deviceInfo.verificationUri} and enter code: ${deviceInfo.userCode}`);\n    \n    // Poll for user approval\n    let attempts = 0;\n    const maxAttempts = Math.floor(deviceInfo.expiresIn / 5); // Poll every 5 seconds\n    \n    while (attempts < maxAttempts) {\n      await DBOS.sleep(5000); // Wait 5 seconds between polls\n      \n      const result = await DeviceAuth.pollForApprovalTransaction(deviceInfo.deviceResponse);\n      \n      if (result.success) {\n        return { success: true, accessToken: result.accessToken };\n      } else if (result.expired) {\n        return { success: false, error: 'Device code expired' };\n      } else if (!result.pending) {\n        return { success: false, error: result.error };\n      }\n      \n      attempts++;\n    }\n    \n    return { success: false, error: 'Timeout waiting for user approval' };\n  }\n}\n```\n\n### 4. Resource Owner Password Credentials (Legacy Authentication)\n\n**Use Case**: Legacy system migration, trusted first-party applications, internal tools.\n\n**⚠️ Security Warning**: This grant type should only be used when other flows are not possible, as it requires handling user passwords directly.\n\n```typescript\nexport class LegacyAuth {\n  @DBOS.transaction()\n  static async setupPasswordAuth(): Promise<void> {\n    // Configure client for password credentials flow\n    await OAuthManager.initializeClient('legacy-system', {\n      tokenEndpoint: 'https://legacy.service.com/oauth/token',\n      clientId: process.env.LEGACY_CLIENT_ID!,\n      clientSecret: process.env.LEGACY_CLIENT_SECRET!,\n      redirectUris: [], // Not needed for password flow\n      scopes: ['user:profile', 'user:data'],\n      supportedGrantTypes: ['password']\n    });\n  }\n\n  @DBOS.transaction()\n  static async authenticateUserTransaction(username: string, password: string): Promise<{\n    success: boolean;\n    accessToken?: string;\n    expiresAt?: number;\n    error?: string;\n  }> {\n    try {\n      // Get token using username/password\n      const tokens = await OAuthManager.getPasswordCredentialsToken(\n        'legacy-system', \n        username, \n        password, \n        ['user:profile', 'user:data']\n      );\n      \n      return {\n        success: true,\n        accessToken: tokens.access_token,\n        expiresAt: tokens.expires_at\n      };\n    } catch (error) {\n      return {\n        success: false,\n        error: 'Invalid credentials'\n      };\n    }\n  }\n\n  @DBOS.workflow()\n  static async legacyLoginWorkflow(username: string, password: string): Promise<any> {\n    return await LegacyAuth.authenticateUserTransaction(username, password);\n  }\n}\n```\n\n### 5. Generic Grant Request (Custom/Future Grant Types)\n\n**Use Case**: Custom OAuth implementations, future grant types, proprietary authentication flows.\n\n**Features**:\n- Flexible parameter passing for any grant type\n- Support for non-standard OAuth extensions\n- Future-proof for new OAuth specifications\n\n```typescript\nexport class CustomAuth {\n  @DBOS.transaction()\n  static async setupCustomGrant(): Promise<void> {\n    // Configure client for generic grants\n    await OAuthManager.initializeClient('custom-service', {\n      tokenEndpoint: 'https://custom.service.com/oauth/token',\n      clientId: process.env.CUSTOM_CLIENT_ID!,\n      clientSecret: process.env.CUSTOM_CLIENT_SECRET!,\n      redirectUris: [],\n      scopes: ['custom:scope'],\n      supportedGrantTypes: ['generic']\n    });\n  }\n\n  @DBOS.transaction()\n  static async tokenExchangeTransaction(userToken: string, targetAudience: string): Promise<{\n    accessToken: string;\n    tokenType: string;\n    scope?: string;\n  }> {\n    // Use token exchange grant (RFC 8693)\n    const tokens = await OAuthManager.executeGenericGrant('custom-service', 'urn:ietf:params:oauth:grant-type:token-exchange', {\n      subject_token: userToken,\n      subject_token_type: 'urn:ietf:params:oauth:token-type:access_token',\n      audience: targetAudience,\n      scope: 'custom:scope'\n    });\n    \n    return {\n      accessToken: tokens.access_token!,\n      tokenType: tokens.token_type!,\n      scope: tokens.scope\n    };\n  }\n\n  @DBOS.transaction()\n  static async jwtBearerGrantTransaction(jwtAssertion: string): Promise<any> {\n    // Use JWT bearer grant (RFC 7523)\n    return await OAuthManager.executeGenericGrant('custom-service', 'urn:ietf:params:oauth:grant-type:jwt-bearer', {\n      assertion: jwtAssertion,\n      scope: 'custom:scope'\n    });\n  }\n\n  @DBOS.workflow()\n  static async customAuthWorkflow(userToken: string, targetAudience: string): Promise<any> {\n    return await CustomAuth.tokenExchangeTransaction(userToken, targetAudience);\n  }\n}\n```\n\n### Grant Type Selection Guide\n\nChoose the appropriate grant type based on your use case:\n\n| Use Case | Grant Type | Security | User Interaction | Best For |\n|----------|------------|----------|------------------|----------|\n| **Web Apps** | Authorization Code + PKCE | High | Required | User-facing applications |\n| **Mobile Apps** | Authorization Code + PKCE | High | Required | Native mobile applications |\n| **API Services** | Client Credentials | High | None | Server-to-server communication |\n| **Smart TVs/IoT** | Device Authorization | High | Limited | Devices without browsers |\n| **Legacy Migration** | Password Credentials | Low | Required | Migrating from legacy auth |\n| **Custom Flows** | Generic Grant | Varies | Varies | Non-standard implementations |\n\n### Security Best Practices\n\n1. **Always use Authorization Code + PKCE** for user-facing applications\n2. **Use Client Credentials** for service-to-service communication\n3. **Avoid Password Credentials** except for legacy migration scenarios\n4. **Use Device Flow** for input-constrained devices\n5. **Validate all tokens** and implement proper error handling\n6. **Store tokens securely** using the SDK's encrypted storage\n7. **Implement token refresh** logic for long-running applications\n8. **Use HTTPS** for all OAuth endpoints in production\n9. **Validate OAuth state** parameters to prevent CSRF attacks\n10. **Set appropriate session timeouts** for security\n\n## API Reference\n\n### PostgresSecretClient\n\n#### Constructor\n```typescript\nnew PostgresSecretClient(knexClient: Knex, config: PostgresSecretClientConfig)\n```\n\n#### Methods\n\n**Initialization**\n- `initialize(): Promise<void>` - Initialize encryption and prepare for operations\n- `initializeSchema(): Promise<void>` - Create database tables (run once per database)\n\n**Secret Management**\n- `saveSecret(name: string, value: string, metadata?: SecretCreationMetadata): Promise<SaveSecretResponse>`\n- `getSecret(name: string): Promise<GenericSecret>`\n- `fetchSecret(id: string): Promise<GenericSecret>`\n- `updateSecret(name: string, updates: Partial<GenericSecret>): Promise<SaveSecretResponse>`\n- `updateSecretById(id: string, updates: Partial<GenericSecret>): Promise<SaveSecretResponse>`\n- `deleteSecret(name: string): Promise<void>`\n- `deleteSecretById(id: string): Promise<void>`\n\n**Discovery & Search**\n- `getAvailableSecrets(page?: number, per_page?: number): Promise<PaginatedResponse<SecretMetadata>>`\n- `searchSecretsByName(name: string, page?: number, per_page?: number): Promise<PaginatedResponse<SecretMetadata>>`\n- `secretExists(id: string): Promise<boolean>`\n\n**Maintenance**\n- `cleanupAuditLogs(retentionDays?: number): Promise<number>` - Remove old audit entries\n\n### OAuthManager\n\n#### Static Methods\n\n**Initialization**\n```typescript\n// Initialize OAuth manager with database connection\nstatic initialize(\n  knexClient: Knex, \n  config: PostgresSecretClientConfig, \n  logger?: Logger\n): Promise<void>\n\n// Initialize single OAuth client\nstatic initializeClient(\n  service: string, \n  config: OAuthClientConfig\n): Promise<openidClient.Configuration>\n\n// Initialize multiple OAuth clients\nstatic initializeAllClients(\n  oauthConfigs: Record<string, OAuthClientConfig>\n): Promise<void>\n```\n\n**OAuth Flow Management**\n```typescript\n// Start OAuth authorization flow (Authorization Code Grant)\nstatic startAuthFlow(\n  service: string, \n  scopes: string[], \n  redirectUri?: string\n): Promise<{ authUrl: string; state: string }>\n\n// Handle OAuth callback (Authorization Code Grant)\nstatic handleCallback(\n  service: string, \n  callbackUrl: string\n): Promise<TokenEndpointResponse>\n```\n\n**Client Credentials Grant**\n```typescript\n// Get access token using client credentials grant\nstatic getClientCredentialsToken(\n  service: string, \n  scopes?: string[]\n): Promise<TokenEndpointResponse>\n```\n\n**Device Authorization Grant**\n```typescript\n// Initiate device authorization flow\nstatic initiateDeviceFlow(\n  service: string, \n  scopes?: string[]\n): Promise<DeviceAuthorizationResponse>\n\n// Poll for device authorization completion\nstatic pollDeviceToken(\n  service: string, \n  deviceAuthorizationResponse: DeviceAuthorizationResponse\n): Promise<TokenEndpointResponse>\n```\n\n**Password Credentials Grant**\n```typescript\n// Get access token using username/password (legacy systems only)\nstatic getPasswordCredentialsToken(\n  service: string, \n  username: string, \n  password: string, \n  scopes?: string[]\n): Promise<TokenEndpointResponse>\n```\n\n**Generic Grant Support**\n```typescript\n// Execute custom/future grant types\nstatic executeGenericGrant(\n  service: string, \n  grantType: string, \n  parameters: Record<string, string>\n): Promise<TokenEndpointResponse>\n```\n\n**Token Management**\n```typescript\n// Get valid access token (auto-refresh if needed)\nstatic getValidAccessToken(service: string): Promise<string>\n\n// Manually refresh access token\nstatic refreshAccessToken(\n  service: string, \n  refreshToken: string\n): Promise<TokenEndpointResponse>\n\n// Revoke all tokens for service\nstatic revokeTokens(service: string): Promise<void>\n```\n\n**State Management (Internal)**\n```typescript\n// Store OAuth state with PKCE verifier\nstatic storeOAuthState(\n  stateId: string, \n  service: string, \n  codeVerifier?: string\n): Promise<void>\n\n// Retrieve OAuth state\nstatic retrieveOAuthState(\n  stateId: string\n): Promise<{ service: string; codeVerifier?: string }>\n```\n\n#### OAuth Types\n\nThe SDK exports native openid-client types for OAuth token responses:\n\n```typescript\n// Re-exported from openid-client for convenience\ntype TokenEndpointResponse = {\n  access_token: string;\n  token_type: string;\n  expires_in?: number;\n  refresh_token?: string;\n  scope?: string;\n  id_token?: string;\n  [parameter: string]: unknown;\n};\n\ntype DeviceAuthorizationResponse = {\n  device_code: string;\n  user_code: string;\n  verification_uri: string;\n  verification_uri_complete?: string;\n  expires_in: number;\n  interval?: number;\n  [parameter: string]: unknown;\n};\n\n// SDK configuration types\ninterface OAuthClientConfig {\n  // OIDC Discovery (optional)\n  issuerUrl?: string;\n  \n  // Manual OAuth2 endpoints (required if issuerUrl not provided)\n  authorizationEndpoint?: string;\n  tokenEndpoint?: string;\n  revocationEndpoint?: string;\n  userinfoEndpoint?: string;\n  \n  // Common OAuth2/OIDC fields\n  clientId: string;\n  clientSecret: string;\n  redirectUris: string[];\n  scopes: string[];\n  \n  // Additional OAuth2 configuration\n  responseTypes?: string[];\n  grantTypes?: string[];\n  \n  // Supported grant types\n  supportedGrantTypes?: (\n    'authorization_code' | \n    'client_credentials' | \n    'refresh_token' | \n    'device_code' | \n    'password' | \n    'generic'\n  )[];\n}\n\ninterface Logger {\n  info(message: string): void;\n  warn(message: string): void;\n  error(message: string): void;\n}\n```\n\n### PostgresSecretWorkflowHelper\n\nStatic helper methods for simple usage:\n\n```typescript\nclass PostgresSecretWorkflowHelper {\n  static async getSecret(knexClient: Knex, tenantId: string, name: string): Promise<GenericSecret>\n  static async saveSecret(knexClient: Knex, tenantId: string, name: string, value: string, metadata?: SecretCreationMetadata): Promise<SaveSecretResponse>\n  static async updateSecret(knexClient: Knex, tenantId: string, name: string, updates: Partial<GenericSecret>): Promise<SaveSecretResponse>\n  static async deleteSecret(knexClient: Knex, tenantId: string, name: string): Promise<void>\n  static async getAvailableSecrets(knexClient: Knex, tenantId: string, page?: number, per_page?: number): Promise<PaginatedResponse<SecretMetadata>>\n  static async searchSecretsByName(knexClient: Knex, tenantId: string, name: string, page?: number, per_page?: number): Promise<PaginatedResponse<SecretMetadata>>\n  static async initializeSchema(knexClient: Knex): Promise<void>\n}\n```\n\n### Convenience Functions\n\n```typescript\n// Direct exports for backward compatibility\nexport const getSecretFromPostgres = PostgresSecretWorkflowHelper.getSecret;\nexport const saveSecretToPostgres = PostgresSecretWorkflowHelper.saveSecret;\nexport const updateSecretInPostgres = PostgresSecretWorkflowHelper.updateSecret;\nexport const deleteSecretFromPostgres = PostgresSecretWorkflowHelper.deleteSecret;\nexport const getAvailableSecretsFromPostgres = PostgresSecretWorkflowHelper.getAvailableSecrets;\nexport const searchSecretsByNameFromPostgres = PostgresSecretWorkflowHelper.searchSecretsByName;\n\n// OAuth Manager exports\nexport { OAuthManager } from './oauth-manager';\nexport type { \n  OAuthClientConfig,\n  TokenData,\n  OAuthState \n} from './types';\n\n// PostgreSQL Secret Client exports\nexport { PostgresSecretClient } from './postgres-secret-client';\nexport { PostgresSecretWorkflowHelper } from './postgres-secret-client';\nexport type {\n  PostgresSecretClientConfig,\n  GenericSecret,\n  SecretMetadata,\n  SaveSecretResponse,\n  PaginatedResponse,\n  SecretCreationMetadata\n} from './types';\n```\n\n### Type Definitions\n\n```typescript\ninterface GenericSecret {\n  id: string;\n  tenant_id: string;\n  name: string;\n  value: string;\n  auth_type: string;\n  metadata?: {\n    description?: string;\n    tags?: string[];\n    created_by?: string;\n    [key: string]: any;\n  };\n  created_at: Date;\n  updated_at: Date;\n  is_deleted: boolean;\n  deleted_at?: Date;\n}\n\ninterface SecretMetadata {\n  id: string;\n  name: string;\n  auth_type: string;\n  created_at: Date;\n  updated_at: Date;\n  metadata?: Record<string, any>;\n}\n\ninterface SaveSecretResponse {\n  id: string;\n  tenant_id: string;\n  name: string;\n  created_at: Date;\n  updated_at: Date;\n}\n\ninterface PaginatedResponse<T> {\n  data: T[];\n  pagination: {\n    page: number;\n    per_page: number;\n    total: number;\n    total_pages: number;\n  };\n}\n\ninterface SecretCreationMetadata {\n  description?: string;\n  tags?: string[];\n  created_by?: string;\n  [key: string]: any;\n}\n```\n\n## Usage Patterns\n\n### Multi-tenant SaaS Application\n\n```typescript\nexport class TenantSecrets {\n  @DBOS.transaction()\n  static async setupTenantSecrets(tenantId: string, apiKeys: Record<string, string>) {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    await client.initialize();\n    \n    // Save multiple secrets for a tenant\n    const results = [];\n    for (const [name, value] of Object.entries(apiKeys)) {\n      const result = await client.saveSecret(name, value, {\n        description: `${name} for tenant ${tenantId}`,\n        created_by: 'system',\n        tags: ['api-key', 'tenant-setup']\n      });\n      results.push(result);\n    }\n    \n    return results;\n  }\n  \n  @DBOS.transaction()\n  static async getTenantSecret(tenantId: string, secretName: string): Promise<string> {\n    const secret = await PostgresSecretWorkflowHelper.getSecret(\n      DBOS.knexClient,\n      tenantId,\n      secretName\n    );\n    return secret.value;\n  }\n}\n```\n\n### Secret Rotation\n\n```typescript\nexport class SecretRotation {\n  @DBOS.transaction()\n  static async rotateApiKey(tenantId: string, secretName: string, newValue: string) {\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    await client.initialize();\n    \n    // Update the secret with new value\n    const result = await client.updateSecret(secretName, {\n      value: newValue,\n      metadata: {\n        description: 'Rotated on ' + new Date().toISOString(),\n        tags: ['rotated']\n      }\n    });\n    \n    return result;\n  }\n}\n```\n\n### Batch Operations\n\n```typescript\nexport class BatchSecretOps {\n  @DBOS.transaction()\n  static async migrateSecrets(fromTenant: string, toTenant: string, secretNames: string[]) {\n    const sourceClient = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: fromTenant,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    const targetClient = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: toTenant,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    await Promise.all([sourceClient.initialize(), targetClient.initialize()]);\n    \n    const migrated = [];\n    for (const secretName of secretNames) {\n      try {\n        const secret = await sourceClient.getSecret(secretName);\n        const result = await targetClient.saveSecret(\n          secret.name,\n          secret.value,\n          {\n            ...secret.metadata,\n            description: `Migrated from ${fromTenant}`\n          }\n        );\n        migrated.push(result);\n      } catch (error) {\n        console.warn(`Failed to migrate secret ${secretName}:`, error);\n      }\n    }\n    \n    return migrated;\n  }\n}\n```\n\n## Advanced Usage Patterns\n\n### OAuth Integration Workflows\n\n#### Multi-Provider OAuth Setup\n\n```typescript\nexport class MultiProviderAuth {\n  @DBOS.transaction()\n  static async initializeAllProviders() {\n    // Initialize OAuth manager\n    await OAuthManager.initialize(DBOS.knexClient, {\n      tenantId: 'oauth-system',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    \n    // Configure multiple providers\n    const oauthConfigs = {\n      google: {\n        issuerUrl: 'https://accounts.google.com',\n        clientId: process.env.GOOGLE_CLIENT_ID!,\n        clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n        redirectUri: `${process.env.BASE_URL}/oauth/callback/google`,\n        scope: ['profile', 'email', 'openid']\n      },\n      github: {\n        authorizationEndpoint: 'https://github.com/login/oauth/authorize',\n        tokenEndpoint: 'https://github.com/login/oauth/access_token',\n        userinfoEndpoint: 'https://api.github.com/user',\n        clientId: process.env.GITHUB_CLIENT_ID!,\n        clientSecret: process.env.GITHUB_CLIENT_SECRET!,\n        redirectUri: `${process.env.BASE_URL}/oauth/callback/github`,\n        scope: ['user:email', 'read:user']\n      },\n      microsoft: {\n        issuerUrl: 'https://login.microsoftonline.com/common/v2.0',\n        clientId: process.env.MICROSOFT_CLIENT_ID!,\n        clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,\n        redirectUri: `${process.env.BASE_URL}/oauth/callback/microsoft`,\n        scope: ['openid', 'profile', 'email']\n      },\n      slack: {\n        authorizationEndpoint: 'https://slack.com/oauth/v2/authorize',\n        tokenEndpoint: 'https://slack.com/api/oauth.v2.access',\n        clientId: process.env.SLACK_CLIENT_ID!,\n        clientSecret: process.env.SLACK_CLIENT_SECRET!,\n        redirectUri: `${process.env.BASE_URL}/oauth/callback/slack`,\n        scope: ['users:read', 'channels:read']\n      }\n    };\n    \n    await OAuthManager.initializeAllClients(oauthConfigs);\n    return Object.keys(oauthConfigs);\n  }\n}\n```\n\n#### OAuth Flow with User Context\n\n```typescript\nexport class UserOAuthFlow {\n  @DBOS.transaction()\n  static async initiateLogin(provider: string, userId: string) {\n    // Start OAuth flow\n    const { authUrl, state } = await OAuthManager.startAuthFlow(\n      provider,\n      provider === 'google' ? ['profile', 'email', 'openid'] :\n      provider === 'github' ? ['user:email', 'read:user'] :\n      ['openid', 'profile', 'email']\n    );\n    \n    // Store user context with state (using secrets for secure storage)\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'user-oauth-context',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    await client.saveSecret(`oauth-state-${state}`, JSON.stringify({\n      userId,\n      provider,\n      initiatedAt: new Date().toISOString()\n    }), {\n      description: `OAuth state for user ${userId}`,\n      tags: ['oauth', 'temporary']\n    });\n    \n    return { authUrl, state };\n  }\n  \n  @DBOS.transaction()\n  static async completeLogin(callbackUrl: string) {\n    // Extract state from callback URL\n    const url = new URL(callbackUrl);\n    const state = url.searchParams.get('state')!;\n    \n    // Get user context\n    const client = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: 'user-oauth-context',\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await client.initialize();\n    \n    const contextSecret = await client.getSecret(`oauth-state-${state}`);\n    const { userId, provider } = JSON.parse(contextSecret.value);\n    \n    // Handle OAuth callback\n    const tokenData = await OAuthManager.handleCallback(provider, callbackUrl);\n    \n    // Store tokens for user\n    const userClient = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: `user-${userId}`,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await userClient.initialize();\n    \n    await userClient.saveSecret(`${provider}-tokens`, JSON.stringify(tokenData), {\n      description: `${provider} OAuth tokens`,\n      tags: ['oauth', 'tokens', provider]\n    });\n    \n    // Clean up temporary state\n    await client.deleteSecret(`oauth-state-${state}`);\n    \n    return { userId, provider, tokenData };\n  }\n  \n  @DBOS.transaction()\n  static async getUserAccessToken(userId: string, provider: string): Promise<string> {\n    try {\n      // Try to get valid token directly from OAuth manager\n      return await OAuthManager.getValidAccessToken(provider);\n    } catch {\n      // Fallback to user-stored tokens\n      const userClient = new PostgresSecretClient(DBOS.knexClient, {\n        tenantId: `user-${userId}`,\n        rootKeyBase64: process.env.ROOT_KEY_BASE64!\n      });\n      await userClient.initialize();\n      \n      const tokenSecret = await userClient.getSecret(`${provider}-tokens`);\n      const tokenData = JSON.parse(tokenSecret.value);\n      \n      return tokenData.access_token;\n    }\n  }\n}\n```\n\n#### Service Integration with OAuth\n\n```typescript\nexport class ServiceIntegration {\n  @DBOS.transaction()\n  static async syncWithGoogleDrive(userId: string, folderId: string) {\n    // Get valid access token\n    const accessToken = await UserOAuthFlow.getUserAccessToken(userId, 'google');\n    \n    // Use token with Google Drive API\n    const response = await fetch(\n      `https://www.googleapis.com/drive/v3/files?parents=${folderId}`,\n      {\n        headers: {\n          'Authorization': `Bearer ${accessToken}`,\n          'Content-Type': 'application/json'\n        }\n      }\n    );\n    \n    if (!response.ok) {\n      throw new Error(`Google Drive API error: ${response.statusText}`);\n    }\n    \n    const data = await response.json();\n    \n    // Store results as secrets for caching\n    const userClient = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: `user-${userId}`,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await userClient.initialize();\n    \n    await userClient.saveSecret(\n      `google-drive-cache-${folderId}`,\n      JSON.stringify(data),\n      {\n        description: `Google Drive folder ${folderId} cache`,\n        tags: ['cache', 'google-drive', 'temporary']\n      }\n    );\n    \n    return data;\n  }\n  \n  @DBOS.transaction()\n  static async postToSlack(userId: string, channel: string, message: string) {\n    const accessToken = await UserOAuthFlow.getUserAccessToken(userId, 'slack');\n    \n    const response = await fetch('https://slack.com/api/chat.postMessage', {\n      method: 'POST',\n      headers: {\n        'Authorization': `Bearer ${accessToken}`,\n        'Content-Type': 'application/json'\n      },\n      body: JSON.stringify({\n        channel,\n        text: message\n      })\n    });\n    \n    const result = await response.json();\n    \n    if (!result.ok) {\n      throw new Error(`Slack API error: ${result.error}`);\n    }\n    \n    return result;\n  }\n}\n```\n\n### Token Lifecycle Management\n\n```typescript\nexport class TokenLifecycle {\n  @DBOS.transaction()\n  static async refreshAllExpiredTokens() {\n    const services = ['google', 'github', 'microsoft', 'slack'];\n    const results = [];\n    \n    for (const service of services) {\n      try {\n        // This will automatically refresh if expired\n        const token = await OAuthManager.getValidAccessToken(service);\n        results.push({ service, status: 'valid', token });\n      } catch (error) {\n        results.push({ service, status: 'error', error: error.message });\n      }\n    }\n    \n    return results;\n  }\n  \n  @DBOS.transaction()\n  static async revokeUserTokens(userId: string, provider?: string) {\n    const userClient = new PostgresSecretClient(DBOS.knexClient, {\n      tenantId: `user-${userId}`,\n      rootKeyBase64: process.env.ROOT_KEY_BASE64!\n    });\n    await userClient.initialize();\n    \n    if (provider) {\n      // Revoke specific provider tokens\n      try {\n        await OAuthManager.revokeTokens(provider);\n        await userClient.deleteSecret(`${provider}-tokens`);\n      } catch (error) {\n        console.warn(`Failed to revoke ${provider} tokens:`, error);\n      }\n    } else {\n      // Revoke all tokens for user\n      const providers = ['google', 'github', 'microsoft', 'slack'];\n      \n      for (const prov of providers) {\n        try {\n          await OAuthManager.revokeTokens(prov);\n          await userClient.deleteSecret(`${prov}-tokens`);\n        } catch (error) {\n          console.warn(`Failed to revoke ${prov} tokens:`, error);\n        }\n      }\n    }\n  }\n}\n```\n\n## Security Features\n\nThe SDK provides enterprise-grade security with the following features:\n\n### Encryption\n- **Envelope Encryption**: Two-layer encryption using libsodium XSalsa20-Poly1305\n- **Individual Secret Keys**: Each secret encrypted with its own unique key\n- **Root Key Management**: Secure master key rotation without re-encrypting secrets\n- **AEAD Security**: Authenticated encryption prevents tampering\n\n### Multi-tenant Security\n- **Tenant Isolation**: All operations scoped to specific tenant ID\n- **Cross-tenant Protection**: No data access across tenant boundaries\n- **Audit Separation**: Complete audit trail isolation per tenant\n- **Row-level Security**: Optional database-level tenant isolation\n\n### OAuth Security\n- **PKCE Implementation**: Proof Key for Code Exchange prevents authorization code interception\n- **Secure Token Storage**: OAuth tokens encrypted using same security as secrets\n- **Automatic Refresh**: Transparent token renewal with fallback handling\n- **State Management**: CSRF protection with secure random state generation\n\nFor detailed security architecture and implementation details, see [DEVELOPER.md](./DEVELOPER.md).\n\n## Database Integration\n\nThe SDK automatically creates and manages PostgreSQL tables:\n\n- **`secrets`**: Encrypted secret storage with tenant isolation and metadata support\n- **`secret_audit_log`**: Comprehensive audit trail with 7-year retention\n- **Automatic Schema Setup**: Call `initializeSchema()` once during deployment\n- **Optimized Performance**: Built-in indexing for fast tenant-scoped queries\n- **Row-level Security**: Optional database-level tenant isolation\n\nFor complete database schema and optimization details, see [DEVELOPER.md](./DEVELOPER.md).\n\n## Testing\n\nRun the comprehensive test suite:\n\n```bash\n# Run all tests\nnpm test\n\n# Run with coverage report\nnpm run test:coverage\n\n# Run PostgreSQL-specific tests\nnpm run test:postgres\n\n# Run OAuth-specific tests\nnpm run test:oauth\n\n# Run in watch mode during development\nnpm run test:watch\n\n# Integration tests (requires running PostgreSQL)\nnpm run test:integration\n```\n\n### Test Coverage\n\nThe SDK includes comprehensive testing with 81%+ code coverage:\n\n- **Unit Tests**: All secret management and OAuth functionality\n- **Integration Tests**: End-to-end workflows with PostgreSQL\n- **Security Tests**: Encryption, tenant isolation, and OAuth flows\n- **Error Handling**: Edge cases and recovery scenarios\n- **Performance Tests**: Large dataset handling and optimization\n\nFor detailed testing infrastructure and development setup, see [DEVELOPER.md](./DEVELOPER.md).\n\n## Error Handling\n\nThe SDK provides comprehensive error handling:\n\n```typescript\ntry {\n  const secret = await client.getSecret('nonexistent');\n} catch (error) {\n  if (error.message.includes('not found')) {\n    // Handle missing secret\n  } else if (error.message.includes('not initialized')) {\n    // Handle uninitialized client\n  } else {\n    // Handle other errors\n  }\n}\n```\n\nCommon error scenarios:\n- **Uninitialized Client**: Call `initialize()` before operations\n- **Missing Secrets**: Check existence with `secretExists()`\n- **Tenant Isolation**: Ensure correct tenant ID\n- **Encryption Errors**: Validate root key format\n- **Database Errors**: Check connection and permissions\n\n## Troubleshooting\n\n### Common Issues\n\n**1. \"PostgresSecretClient not initialized\"**\n```typescript\n// Always call initialize() first\nawait client.initialize();\n```\n\n**2. \"ROOT_KEY_BASE64 environment variable is required\"**\n```bash\n# Generate and set the root key\nROOT_KEY_BASE64=$(node -e \"console.log(require('crypto').randomBytes(32).toString('base64'))\")\nexport ROOT_KEY_BASE64\n```\n\n**3. \"Secret not found\"**\n```typescript\n// Check if secret exists first\nconst exists = await client.secretExists(secretId);\nif (!exists) {\n  // Handle missing secret\n}\n```\n\n**4. Database connection issues**\n- Ensure DBOS is properly configured with PostgreSQL\n- Check that `DBOS.knexClient` is available\n- Verify database permissions for table creation\n\n**5. OAuth configuration errors**\n```typescript\n// Validate OAuth configuration\nconst config = {\n  issuerUrl: 'https://accounts.google.com', // For OIDC\n  clientId: process.env.GOOGLE_CLIENT_ID!,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  redirectUri: 'https://yourapp.com/oauth/callback'\n};\n```\n\n**6. Token management issues**\n```typescript\n// Handle token refresh errors\ntry {\n  const token = await OAuthManager.getValidAccessToken('google');\n} catch (error) {\n  if (error.message.includes('refresh_token')) {\n    // User needs to re-authenticate\n    await OAuthManager.revokeTokens('google');\n    // Redirect to login\n  }\n}\n```\n\n### Performance Tips\n\n1. **Client Management**: Create one client instance per tenant per workflow\n2. **Batch Operations**: Use DBOS transactions for multiple secret operations\n3. **Pagination**: Use appropriate page sizes (10-100) for large result sets\n4. **Audit Cleanup**: Regularly clean old audit logs with `cleanupAuditLogs()`\n5. **Token Caching**: OAuth tokens are automatically cached and refreshed\n\nFor detailed performance optimization strategies, see [DEVELOPER.md](./DEVELOPER.md).\n\n## License\n\n","readmeFilename":"README.md"}