{"_id":"@ktds-axbd/harness-kit","_rev":"27-883206d45758bfcb5c3a7a02dafd2ad1","name":"@ktds-axbd/harness-kit","dist-tags":{"latest":"0.25.0"},"versions":{"0.1.0":{"name":"@ktds-axbd/harness-kit","version":"0.1.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.1.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"ffb6a11465823fa5d968ebf1f255760849be234f","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.1.0.tgz","fileCount":116,"integrity":"sha512-pzHJa3snL6ytt44YkXH7uwWT/iUunvpLJcCo2pdwpYz1tPMSZsLXvpIPTNApQz9X1as3u1HB+iVLjEZ+CnRx6Q==","signatures":[{"sig":"MEUCIQDsWuo1PSLqNH4yEQWxpcUBqkzPBNOMsUadv+hxd1+gMwIgJFvcM7UQfOPjq/ICUpinuiZ8YbCMZTKJm/oVKciUuOQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":225405},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"b5d49a6886ec3930d8ff8a5bae0832d9f9d596f5","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"11.12.1","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus)","directories":{},"_nodeVersion":"24.14.1","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.1.0_1779202171205_0.46165295099282777","host":"s3://npm-registry-packages-npm-production"}},"0.2.0":{"name":"@ktds-axbd/harness-kit","version":"0.2.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.2.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"9affe72925ad85ceb9fda84454a85838c242aa2a","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.2.0.tgz","fileCount":124,"integrity":"sha512-Rki7sKm+yLTcoCQXeYZ+B+uQuYsuNtkr5zoA79QY00haLUdKM3MG4JXTHCNAkqyEULF7AtXMM0UFWPThNsHX/Q==","signatures":[{"sig":"MEUCIQDDhMFcmz/tzrquzsKWA4PrqmFjfuq7dleHsegkm5m1DgIgGrtk4qBbSBt5vXnUayJeE/YBGRDKqRUr/5LxSc2GjR0=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":234153},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"6596f288dec0e047e4f54c543406616b6a7e02e4","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"11.12.1","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"24.14.1","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.2.0_1779204162850_0.08730338215920375","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"@ktds-axbd/harness-kit","version":"0.3.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.3.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"d2c0327699ab924cd6cf820211d990bb3dfda44c","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.3.0.tgz","fileCount":128,"integrity":"sha512-rhaMAzHvHIYfWox/ebCV0esXlm6FA6Y2uUMGYg+UcfkRYOskLCyaSzkA5c5+NN0ZI2skdPKm0UYzh+HOJbVRcQ==","signatures":[{"sig":"MEYCIQDSYkR1JD71BmwrbdNgTLa4+FOg/3LvrGgcIyh7ETpJmQIhAIxLfsUdmWidjx0Zg1Li4oLm5Y3gxQr2CBk3g9OY98YG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":241483},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.3.0_1779513305132_0.08642675464779503","host":"s3://npm-registry-packages-npm-production"}},"0.4.1":{"name":"@ktds-axbd/harness-kit","version":"0.4.1","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.4.1","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"ad1346e43406a3e5a6ea4ec5043deee99def6eea","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.4.1.tgz","fileCount":128,"integrity":"sha512-mA4nBmiW+05NUOELDQOBE35ROg7HCo7Ig2gfsp1pJ5rlK8J8/bilTja1mSD/W3zTFaK2idc7uXD6TmwR+23RIA==","signatures":[{"sig":"MEUCIDiSnKA/+2XVl9i20a9brGTBNrcMWvZ/f/C9AVb42qwjAiEAs+daf5YwOEKq6JJwIXPB+nIvujjXyZnAjnlVFRbe9Wg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":243250},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"f672212308009e584243d444808ce97ea770de84","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.4.1_1779523482870_0.49947756893304374","host":"s3://npm-registry-packages-npm-production"}},"0.5.0":{"name":"@ktds-axbd/harness-kit","version":"0.5.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.5.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"791a2db4e63f532cd55e768a562b07681833181c","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.5.0.tgz","fileCount":128,"integrity":"sha512-u+AI4HNZjB84EhjXiWQtMsevCzoDRbKDdT03EVDeQEUfMCdZDcTUY972MioaK/Qqjaa4XGIBHDCWj7pWgvS8jA==","signatures":[{"sig":"MEYCIQCfD09GpKl0YnzY89aoq9+5XcLTSQdQKaHly4r+3HMlkwIhAOSUGETmZukhy7kywACjHmZYT5ANjKJTvxqyOf2DojPz","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":246080},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"903ac15ab4cd780a190f3e95de9fa04228bafb53","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.5.0_1779538362179_0.9823726468598164","host":"s3://npm-registry-packages-npm-production"}},"0.6.0":{"name":"@ktds-axbd/harness-kit","version":"0.6.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.6.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"695524d5745b9a5e7fa8025b88ce7cc021458432","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.6.0.tgz","fileCount":128,"integrity":"sha512-zJIabGxTJq/78icxRE0Ut5oMmLataeyJJdsoeB2EHnLw5AUrysXVN7sdmcq/DK+Sa/QZ1zC/MONcp+n9Hz6U4g==","signatures":[{"sig":"MEUCIQC62/uv+iAKYkXBY+sJlFVQhaERNXlvFrhd2SksG5HPEQIgO2JOeyZGMy1Vx8GduG6GUNGRrxMq93y8opjqnCLpVAE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":249514},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"686658046f86a2bc367c65365e4deb2dda36e858","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.6.0_1779549149522_0.8412194849652428","host":"s3://npm-registry-packages-npm-production"}},"0.7.0":{"name":"@ktds-axbd/harness-kit","version":"0.7.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.7.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"e125ba496f2f75dc2e05b3e3fe120d566df23d00","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.7.0.tgz","fileCount":128,"integrity":"sha512-zvoCpDU9iJqf8pprDu5hcu+9VkTUG36c74zPsPX07AD4Zq1n8fntyeVADxvwb0wY5oWBxPtCpJDpx9hLBXIS8Q==","signatures":[{"sig":"MEYCIQCOv3tSXDBGlpsB39VvLwSFuQBv26QUaWCGrtiEK16pqQIhANtvYe9eB7cMgpKaJDABTRVws0PWPFLeqL9jFUzDwgBn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":253422},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"121e14f98602873f1cd33018523cf99739ae0e76","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.7.0_1779573559295_0.7071414549674959","host":"s3://npm-registry-packages-npm-production"}},"0.8.0":{"name":"@ktds-axbd/harness-kit","version":"0.8.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.8.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"d862b31d12593664a749e59c6292e7c985e13d2e","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.8.0.tgz","fileCount":140,"integrity":"sha512-TkDrqL3utFIzgdVZnVkSlg9HRTXMocPZrZkUf03AdP4Gq/hkx3uXnD2EZ08IFmyWAD9xSWaOsifkM+FWUPzqKA==","signatures":[{"sig":"MEYCIQCJqgJFVHQ3HNyRi/rFtSHlVT2HC2PAZIVW4p3roh3MXwIhAMEoZG0Dsw7B3r8cmNhVpGQJKdwQ2rUY8Lt9uOV8mWm6","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":279801},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"eb32544e8920bb1f7bb24540a0f499866bb20362","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.8.0_1779578422604_0.6865968870213526","host":"s3://npm-registry-packages-npm-production"}},"0.9.0":{"name":"@ktds-axbd/harness-kit","version":"0.9.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.9.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"eb5d787820cb9a71edb0f42de4355d97adea1523","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.9.0.tgz","fileCount":150,"integrity":"sha512-nj1Yze7jkVcINpxo59ybTvx4GQ3lgm8CF/nHNnIQvaUZZB9wViM+0jDd9kVKxtLuCjq7FWlrG6pyfyndGZSeSg==","signatures":[{"sig":"MEUCIHqjErPrOZomodwjndrgEv8WgVyF8J0xg2CJ3ifu0A7eAiEA74Az7E6Gc/kJSsEocj4iEzr7v/P2H9Z/gm+5e2uoYlM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":290020},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./otel":"./dist/otel/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"af5476f7788b91d4533c66668b10b82b5c3e5445","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.9.0_1779580856728_0.3091083923716189","host":"s3://npm-registry-packages-npm-production"}},"0.10.0":{"name":"@ktds-axbd/harness-kit","version":"0.10.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.10.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"a05afd965a09b87e619736572324b2a71feb893c","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.10.0.tgz","fileCount":152,"integrity":"sha512-lhY8soZkr8VBbJQseYPWR1vd+F8dwiusPFOtUQOu2ohpIzpPd7tOPd2/x3K8naUZkA4HmPO0gSsskFOijc2QNg==","signatures":[{"sig":"MEYCIQCLRwJSHpNmdDNmo9SKgvNKYpCaKOPxOFNGBr2QBg2m+gIhAOXC+PGv5ssgA0S0VT4H6Hp6MJ4rZLXfdsfROQPBgg8x","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":293839},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./otel":"./dist/otel/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"a0c44dfb7f0d661ec612380c9a9285ffc1e63b62","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.10.0_1779598550931_0.8644842769147021","host":"s3://npm-registry-packages-npm-production"}},"0.11.0":{"name":"@ktds-axbd/harness-kit","version":"0.11.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.11.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"e6ebfe1b691dc157bb3224964d5df7efcead159f","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.11.0.tgz","fileCount":152,"integrity":"sha512-MpiEbkdwEFbTTK5YBjsS8wloUnCPVubmQ3s57iujiMAY01eXdtFytxxP6ucBeXqokifRO8sszQVIJrtj4hXsvg==","signatures":[{"sig":"MEUCIFObTKg3YN1DOZm72PtCeMCVkMg5sHuXGEHiWQykm0KRAiEA0N2jS8gf+MrbrgY4zGcEN/fji8tYl6ZJFIlSs8VpkpU=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":297408},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./otel":"./dist/otel/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"9dbd8cc0a35f7ed28cd8f53dbbca76126bca5ce0","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.11.0_1779601577679_0.9349821734279857","host":"s3://npm-registry-packages-npm-production"}},"0.12.0":{"name":"@ktds-axbd/harness-kit","version":"0.12.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.12.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"b49277fd5628568aba09a5b8d4fdecffa2557159","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.12.0.tgz","fileCount":164,"integrity":"sha512-oPvggJEP5k+yK1QBXbhsklLzh1pEVXLh74no/BU0ZU3pBMQDTilsVFz8v9hAFiFl7wOfnxC6AuRSgZ5+Aszk0Q==","signatures":[{"sig":"MEUCIQDZllyLBEoHtiP9uvSbLlniKGHPd2mNf+a8yKl+VFPhEQIgBU8fpDl2dExuAqAUt2yjWtg1X4L6ZI+zS9lCrfjLYOo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":311337},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"b24b22602dcf38a42285a0183255677068445031","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.12.0_1779612632033_0.34847071696547216","host":"s3://npm-registry-packages-npm-production"}},"0.13.0":{"name":"@ktds-axbd/harness-kit","version":"0.13.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.13.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"f02040b2bed3c28c6a1ef6f970c83668351e9fe6","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.13.0.tgz","fileCount":175,"integrity":"sha512-6KWojKgwW5M9Dy4cJiWHHTNkxHmRg/hfYfqf0oLisHn919PQNkoMynhn08DnZifATSi6+iAPHiAqHBHQfXBPlQ==","signatures":[{"sig":"MEUCIQDqlL74Q8RYl9QiLdeepQIxmWmEwut84i5DXCttbpBlgwIgRzgdUFm6azCkHop4eZVrRplN98Hm0mpOPk0YFmr9eaY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":335945},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js"},"gitHead":"06d5deff1795dc347fc587fae8b89c95d3678992","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.13.0_1779625624089_0.7583851954714129","host":"s3://npm-registry-packages-npm-production"}},"0.14.0":{"name":"@ktds-axbd/harness-kit","version":"0.14.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.14.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"06e73c8223f1a702d932c8b5691fbcd76f29929c","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.14.0.tgz","fileCount":183,"integrity":"sha512-1XtFePJtM+tNyaM905LFkcnikIPf7vKsGsi11UzKvZPzyMfb5r8oGjXxwhhux9AKMOU2onIy3jseWJq9ti3nZQ==","signatures":[{"sig":"MEYCIQD5OZz6ZAWqwgY4uPpjIKi7zYMfEq0Y8BcVfMRiEjO9wAIhAOh7uKTGnBeItiV6zHpztYSwgEEJGG2bNT/1Fk5QzOxt","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":347531},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js","./middleware":"./dist/middleware/index.js"},"gitHead":"2530a7d357c2553ab7050487cf6bb660cc7ee8dc","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.14.0_1779705315524_0.5996486441725466","host":"s3://npm-registry-packages-npm-production"}},"0.15.0":{"name":"@ktds-axbd/harness-kit","version":"0.15.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.15.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"e5aefd3e4c1fe489513f68945d03bc77e09f4dcf","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.15.0.tgz","fileCount":195,"integrity":"sha512-rdcqkI/69WJSPnwzpggAy5SQuDOVV8PtpGFwBAci0Q6llooqNF44ijxCSZ0pi2FEVZmnN81ohiLukIOW5mjWhQ==","signatures":[{"sig":"MEUCIQDK9tAHzjayROIAyTFZ7hrlF6RIgpm9wzgete7psMH+9AIga9kRPPojIhfRJY99k9HmP8PasZguM8+sr8xQXRt+DyE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":365782},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js","./middleware":"./dist/middleware/index.js","./resilience":"./dist/resilience/index.js"},"gitHead":"e4f31c9bae1232ed6793ba3cce567d4b810c1eb6","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.15.0_1779712715685_0.8246137983812705","host":"s3://npm-registry-packages-npm-production"}},"0.16.0":{"name":"@ktds-axbd/harness-kit","version":"0.16.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.16.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"48036f199515a9239feb1f6b5c3727ca3692eae7","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.16.0.tgz","fileCount":205,"integrity":"sha512-uqwc0SlL7/9X34H8gyazRE8Uiky1EK1WsMbwpzXn4dxPUvF0hS6UubqUQqL38XfHJBm8GvNLEveF56OtPf2seg==","signatures":[{"sig":"MEQCICggwHSp65QEigiyv/Fqu0A8kfYkArgJDkTW3vOQEHQ7AiBnGW6uJFqPHT6saykmTBfpWLH9eg2RSIWN3bc9NJ/FZQ==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":387254},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./scaffold":"./dist/scaffold/generator.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js"},"gitHead":"ae4532f45913b16b1017a94ba17d72b54f16600d","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.16.0_1779756790396_0.21795222992209484","host":"s3://npm-registry-packages-npm-production"}},"0.17.0":{"name":"@ktds-axbd/harness-kit","version":"0.17.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.17.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"77565127906b3dbffe361fdc269e76e9976ba2e5","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.17.0.tgz","fileCount":213,"integrity":"sha512-kKTvSfgDX3GcsYZWkUnyVsbVS8qYq8R9zOIpYYrMy1EbBeN6pz4RGLgNxLOvRGKR8ZXYbMVCIjY/wKCA3bKpWg==","signatures":[{"sig":"MEQCIAoKinSnf1YowfK4g5KsPL+KXTuNWTyIUZjsS6G4CEKaAiAJsZULMTZdJ8/0JxhyQlM8MCW/7+ubB31rQZ2sw6Mfug==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":408190},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js"},"gitHead":"7d34457b03b87c720e3c5af702c65833fc149728","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.17.0_1779768197552_0.06763203317003796","host":"s3://npm-registry-packages-npm-production"}},"0.18.0":{"name":"@ktds-axbd/harness-kit","version":"0.18.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.18.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"94fff725a8c56e1e7b15be7974bd9fef11b062bb","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.18.0.tgz","fileCount":219,"integrity":"sha512-7XNwnQr+lTacXBfIDHE3+zq5nYdPpqed1r2kqFgeRH8P/qwjOQ/ugUDSF15VJOgNfnKJtBDz+H5L4ePXKwMKkA==","signatures":[{"sig":"MEUCIGyEH0JvHF14+jLHLcsX89rik3ejweDiaFXIuhN05+KvAiEAkMfrpb5qbmxrOEJNhUCUFXaFquui+U6ZaegL0dbcPzY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":420424},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./service-binding":"./dist/service-binding/index.js"},"gitHead":"44ca0aa17141511abf4c1c0f4e96e85c43f11967","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.18.0_1779776617856_0.2534714496186161","host":"s3://npm-registry-packages-npm-production"}},"0.19.0":{"name":"@ktds-axbd/harness-kit","version":"0.19.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.19.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"58d874e56128bd005ce190c56c9249136544fe3d","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.19.0.tgz","fileCount":219,"integrity":"sha512-WBpgDuhnxOLu/FenrIQt8M7cC5dcLJaRkdkfeKhjCEx2RVlJ6N0bP2njz3mhS5hbY03baT6HRshLx6S18C/HKQ==","signatures":[{"sig":"MEQCIFqvQcQ59WYpIeMa/tFzrLjmJRYS8yU/VOSTxoFik8QPAiAZh6+BW8+3+AcPkKkwHF1J4LIp821cNIch10WibhwV9w==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":422735},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./service-binding":"./dist/service-binding/index.js"},"gitHead":"688c9c96552d2cd401afadd1f72520f95efd4bb7","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.19.0_1779836183762_0.334870620532701","host":"s3://npm-registry-packages-npm-production"}},"0.20.0":{"name":"@ktds-axbd/harness-kit","version":"0.20.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.20.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"2d35ed850877587dc5d79ec5d732cca57a67a43e","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.20.0.tgz","fileCount":219,"integrity":"sha512-/BUFM1G845pHffPilTPbzBpJRQ9hwEkzk0PjCw84xZq0R33eWOHLQErRupzPrVv1WkWwCjSQRoZ9HMAEdiFEtw==","signatures":[{"sig":"MEUCIQDly9Misy8oY5dRM+0mk4gmUbDSrNswtsVy1d/yhd7K8QIgFSig37UGT5MZuIqSE4/4V6gTjYPpPcQ7J7+pEsJo7pM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":424701},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./service-binding":"./dist/service-binding/index.js"},"gitHead":"43320e8b78f0845ca9fdb162e805fd4612f5b6b3","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.20.0_1779837640373_0.9655514020387115","host":"s3://npm-registry-packages-npm-production"}},"0.21.0":{"name":"@ktds-axbd/harness-kit","version":"0.21.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.21.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"b61536a6debe92483bd22083306d9e39bebbfd7f","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.21.0.tgz","fileCount":219,"integrity":"sha512-BtdNkdEaRdZefnRA6WyTjFnkyObRcIo2HacGcdy9NaIQsEN/BdX93GQTI5jFi7+jgZJEXj/YPSUqgNnD9a+M8A==","signatures":[{"sig":"MEUCIQDiv85XD5La5QwVWfH1jsDH9K24OBwQc7sK3h1dhzYDXAIgS0QegxtuXZdHrBkwuJPo8HTntkZ9tcGEe3A8mjBhzDE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":426109},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./service-binding":"./dist/service-binding/index.js"},"gitHead":"8053f4d670fdd737e3866ab64127906a4ead8b16","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.21.0_1779840130687_0.48467966381448324","host":"s3://npm-registry-packages-npm-production"}},"0.22.0":{"name":"@ktds-axbd/harness-kit","version":"0.22.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.22.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"09a0380b29d6353b4debb5b1e74afbc59a9be041","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.22.0.tgz","fileCount":219,"integrity":"sha512-gF1KuN/ExXERNqtyj7Zd0y4RY1/ER27aiBXVjZ/SPCkZk9PDROM++gvXhpcUlpMqigALPxnxfvYNhmcu4dx2Rg==","signatures":[{"sig":"MEUCIHRy+g2/BoBojw/kHUGrCm3Vaz9PpvInoXWD2Da0jSZbAiEAwg88zAkw/gK88Ldo4Ldg6s5ykcoc2ZG1RoXuOp9rqsY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":428455},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./service-binding":"./dist/service-binding/index.js"},"gitHead":"87ef35d3de16e219d114488fea742b1bd6662740","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.22.0_1780207104797_0.5538112786621145","host":"s3://npm-registry-packages-npm-production"}},"0.23.0":{"name":"@ktds-axbd/harness-kit","version":"0.23.0","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.23.0","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"818642cf2192f20829e1610261acf2fac728f560","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.23.0.tgz","fileCount":219,"integrity":"sha512-mZwfMOjpvJW9VrhVI9RInEwAs2TM6WxUJTgVdDzShL3jCWYCySp4xeyL4lFizPRtpUpoYmZmqBZjz6RtDUvKqw==","signatures":[{"sig":"MEYCIQCX635b0VxVOzDCoyZXZthin8WiwFM/FLISVjOvdGbnMAIhAOMAN+WPNhXvJYOlFXCjecyG98irppeb59wVN30kF2S7","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":429453},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./middleware/*":{"types":"./dist/middleware/*.d.ts","default":"./dist/middleware/*.js"},"./service-binding":"./dist/service-binding/index.js"},"gitHead":"72c67d76c5ef979d2d5a0716204986f447e7eb49","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.23.0_1783040810619_0.5929516480992665","host":"s3://npm-registry-packages-npm-production"}},"0.24.1":{"name":"@ktds-axbd/harness-kit","version":"0.24.1","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.24.1","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"12248e262d393534e72bace71f173ee7744b634d","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.24.1.tgz","fileCount":219,"integrity":"sha512-eU9ZGvrsyJxEjS449Y8h9lwvp+JJG11SLKa+IgS6ltPyjrMOx8tZ1eSebpnAUZRnQJqkC6h6KHlw1HdBZ+O0uQ==","signatures":[{"sig":"MEQCIC7N/8kmYmn/dV337pRulxVZOO6WfiXf4wJjrCW28DGyAiBujEZPidSCeN55z240T0UoGTjjONF2unfynPFJBLGQqg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":432891},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./middleware/*":{"types":"./dist/middleware/*.d.ts","default":"./dist/middleware/*.js"},"./service-binding":"./dist/service-binding/index.js"},"gitHead":"f19d29f7738688ae1aada0cea9a42054fc81c6f9","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"deprecated":"stale dist build - use 0.24.2","repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.24.1_1783171685960_0.624156206333573","host":"s3://npm-registry-packages-npm-production"}},"0.24.2":{"name":"@ktds-axbd/harness-kit","version":"0.24.2","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","_id":"@ktds-axbd/harness-kit@0.24.2","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"bin":{"harness":"dist/cli/index.js"},"dist":{"shasum":"76fd7e6a8e1b220b80abc37b0134a2b1e5b09a1e","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.24.2.tgz","fileCount":242,"integrity":"sha512-r7YVRJN1tPnTdtO41cO9aKdSHw3eR3h7OWUO41OOiH8BClA8WqGk2KrRsh0Wst2201glBvlEU8kCj3VWdSyFZQ==","signatures":[{"sig":"MEUCIQCSsjdQTZ3R7GiZ5BdmOUEoIV1lWiqWiFTiTEV/Rv1oKAIgTVr0keW3DBlanDAg63JvdJ1NBTdyQsDcoi365v1QIxY=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":540030},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=22"},"exports":{".":"./dist/index.js","./d1":"./dist/d1/index.js","./kv":"./dist/kv/index.js","./r2":"./dist/r2/index.js","./cron":"./dist/cron/index.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./oauth":"./dist/oauth/index.js","./queue":"./dist/queue/index.js","./config":"./dist/config/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./metrics":"./dist/metrics/index.js","./scaffold":"./dist/scaffold/generator.js","./websocket":"./dist/websocket/index.js","./middleware":"./dist/middleware/index.js","./pagination":"./dist/pagination/index.js","./resilience":"./dist/resilience/index.js","./middleware/*":{"types":"./dist/middleware/*.d.ts","default":"./dist/middleware/*.js"},"./service-binding":"./dist/service-binding/index.js"},"gitHead":"8318690b708d39401d06ad34888780b2ca60442a","scripts":{"test":"vitest run","build":"tsc && cp -r src/scaffold/templates dist/scaffold/","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit","prepublishOnly":"npm run build"},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"repository":{"url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","type":"git","directory":"packages/harness-kit"},"_npmVersion":"10.9.4","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","directories":{},"_nodeVersion":"22.22.0","dependencies":{"hono":"^4.0.0","commander":"^12.0.0","handlebars":"^4.7.8"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^3.0.0","wrangler":"^4.0.0","typescript":"^5.9.3","@types/node":"^22.0.0","@openfeature/server-sdk":"1.21.0","@cloudflare/workers-types":"^4.0.0"},"peerDependencies":{"hono":"^4.0.0","@openfeature/server-sdk":"^1.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/harness-kit_0.24.2_1783172205198_0.9483247692475927","host":"s3://npm-registry-packages-npm-production"}},"0.25.0":{"name":"@ktds-axbd/harness-kit","version":"0.25.0","description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","type":"module","main":"dist/index.js","types":"dist/index.d.ts","bin":{"harness":"dist/cli/index.js"},"exports":{".":"./dist/index.js","./eslint":"./dist/eslint/index.js","./events":"./dist/events/index.js","./d1":"./dist/d1/index.js","./scaffold":"./dist/scaffold/generator.js","./otel":"./dist/otel/index.js","./flags":"./dist/flags/index.js","./middleware":"./dist/middleware/index.js","./middleware/*":{"types":"./dist/middleware/*.d.ts","default":"./dist/middleware/*.js"},"./oauth":"./dist/oauth/index.js","./resilience":"./dist/resilience/index.js","./kv":"./dist/kv/index.js","./pagination":"./dist/pagination/index.js","./r2":"./dist/r2/index.js","./queue":"./dist/queue/index.js","./cron":"./dist/cron/index.js","./metrics":"./dist/metrics/index.js","./websocket":"./dist/websocket/index.js","./config":"./dist/config/index.js","./service-binding":"./dist/service-binding/index.js"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","directory":"packages/harness-kit"},"bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"engines":{"node":">=22"},"scripts":{"build":"tsc && cp -r src/scaffold/templates dist/scaffold/","prepublishOnly":"npm run build","test":"vitest run","test:e2e":"vitest run --config vitest.e2e.config.ts","typecheck":"tsc --noEmit"},"dependencies":{"commander":"^12.0.0","handlebars":"^4.7.8","hono":"^4.0.0"},"devDependencies":{"@cloudflare/workers-types":"^4.0.0","@openfeature/server-sdk":"1.21.0","@types/node":"^22.0.0","typescript":"^5.9.3","vitest":"^3.0.0","wrangler":"^4.0.0"},"peerDependencies":{"@openfeature/server-sdk":"^1.0.0","hono":"^4.0.0"},"peerDependenciesMeta":{"@openfeature/server-sdk":{"optional":true}},"_id":"@ktds-axbd/harness-kit@0.25.0","gitHead":"4621292ca19e028171f4548a5e6e76ae8ebc8340","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-rzVkGOeK/0svNt9iRkPrwAQRZ0ILhi45RzWwz/ZkbtggS8TpGZLgnxBLkkKilxgZKRmxAp9XCxPrme92ddEZuA==","shasum":"4bd8d63fb4482ab6690d25c91faa7ec0628c0423","tarball":"https://registry.npmjs.org/@ktds-axbd/harness-kit/-/harness-kit-0.25.0.tgz","fileCount":242,"unpackedSize":547224,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCICTjNvgF4p+VqCpOW/OSMFsWmLS9yavqv3685WFjCrqcAiEAhzHQQwaziHjDeGmq/b/QfR23W/9qYDrRXyxu6V+Trvg="}]},"_npmUser":{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"},"directories":{},"maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/harness-kit_0.25.0_1785681522553_0.8327327452660918"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-19T14:49:31.122Z","modified":"2026-08-02T14:38:42.869Z","0.1.0":"2026-05-19T14:49:31.402Z","0.2.0":"2026-05-19T15:22:43.014Z","0.3.0":"2026-05-23T05:15:05.337Z","0.4.1":"2026-05-23T08:04:43.064Z","0.5.0":"2026-05-23T12:12:42.367Z","0.6.0":"2026-05-23T15:12:29.658Z","0.7.0":"2026-05-23T21:59:19.450Z","0.8.0":"2026-05-23T23:20:22.748Z","0.9.0":"2026-05-24T00:00:56.897Z","0.10.0":"2026-05-24T04:55:51.080Z","0.11.0":"2026-05-24T05:46:17.845Z","0.12.0":"2026-05-24T08:50:32.180Z","0.13.0":"2026-05-24T12:27:04.311Z","0.14.0":"2026-05-25T10:35:15.672Z","0.15.0":"2026-05-25T12:38:35.844Z","0.16.0":"2026-05-26T00:53:10.575Z","0.17.0":"2026-05-26T04:03:17.720Z","0.18.0":"2026-05-26T06:23:38.008Z","0.19.0":"2026-05-26T22:56:23.986Z","0.20.0":"2026-05-26T23:20:40.539Z","0.21.0":"2026-05-27T00:02:10.851Z","0.22.0":"2026-05-31T05:58:24.994Z","0.23.0":"2026-07-03T01:06:50.801Z","0.24.1":"2026-07-04T13:28:06.116Z","0.24.2":"2026-07-04T13:36:45.351Z","0.25.0":"2026-08-02T14:38:42.738Z"},"bugs":{"url":"https://github.com/KTDS-AXBD/Foundry-X/issues"},"author":{"name":"KTDS AX BD","email":"ktds.axbd@gmail.com"},"license":"MIT","homepage":"https://github.com/KTDS-AXBD/Foundry-X/tree/master/packages/harness-kit","keywords":["cloudflare-workers","scaffold","monorepo","hono","ax-bd","harness","d1","msa"],"repository":{"type":"git","url":"git+https://github.com/KTDS-AXBD/Foundry-X.git","directory":"packages/harness-kit"},"description":"AX BD Cloudflare Workers MSA scaffold + harness CLI (auth/CORS/D1/event-bus/middleware)","maintainers":[{"name":"ktds-axbd","email":"ktds.axbd@gmail.com"}],"readme":"# @ktds-axbd/harness-kit\n\nAX BD MSA 서비스 그룹의 공통 기반 패키지. 새 Cloudflare Workers 서비스를 **1분 내** 생성하고, 인증/CORS/이벤트/ESLint를 일관되게 설정해요.\n\n## Quick Start\n\n### 1. 새 서비스 생성\n\n```bash\nnpx harness create gate-x --service-id gate-x --account-id <CF_ACCOUNT_ID>\ncd gate-x\npnpm install\n```\n\n### 2. app.ts에 harness-kit 통합\n\n```typescript\nimport { Hono } from \"hono\";\nimport {\n  createAuthMiddleware,\n  createCorsMiddleware,\n  errorHandler,\n  rbac,\n} from \"@ktds-axbd/harness-kit\";\n\nconst config = {\n  serviceName: \"gate-x\",\n  serviceId: \"gate-x\" as const,\n  corsOrigins: [\"https://fx.minu.best\"],\n  publicPaths: [\"/api/auth/\", \"/api/openapi.json\"],\n};\n\nconst app = new Hono<{ Bindings: Env }>();\n\napp.use(\"*\", createCorsMiddleware(config));\napp.use(\"*\", createAuthMiddleware(config));\napp.use(\"*\", errorHandler());\n\n// 관리자 전용 라우트\napp.get(\"/api/admin\", rbac(\"admin\"), (c) => c.json({ ok: true }));\n\nexport default app;\n```\n\n### 3. 실행\n\n```bash\npnpm dev    # wrangler dev (로컬)\npnpm test   # vitest\n```\n\n---\n\n## CLI\n\n### `harness create <name>`\n\n새 서비스 scaffold를 생성해요.\n\n```bash\nharness create <name> [options]\n\nOptions:\n  --service-id <id>    서비스 ID (기본: foundry-x)\n  --account-id <id>    Cloudflare Account ID\n  --db-name <name>     D1 Database 이름 (기본: <name>-db)\n  -o, --output <dir>   출력 디렉토리 (기본: ./<name>)\n```\n\n**유효한 service-id:**\n| ID | 서비스 |\n|----|--------|\n| `foundry-x` | Foundry-X (발굴+형상화) |\n| `gate-x` | Gate-X (검증) |\n| `launch-x` | Launch-X (제품화+GTM) |\n| `eval-x` | Eval-X (평가) |\n| `discovery-x` | Discovery-X (수집) |\n| `ai-foundry` | AI Foundry (AI 에이전트) |\n\n**생성 파일:**\n```\n<name>/\n├── package.json\n├── tsconfig.json\n├── vitest.config.ts\n├── wrangler.toml\n└── src/\n    ├── index.ts      # Workers entry\n    ├── app.ts        # Hono app + harness-kit 설정\n    └── env.ts        # 환경 타입 정의\n```\n\n---\n\n## API Reference\n\n### Middleware\n\n#### `createAuthMiddleware(config)`\n\nJWT 검증 미들웨어. `publicPaths`에 등록된 경로는 건너뛰어요.\n\n```typescript\nimport { createAuthMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createAuthMiddleware({\n  serviceName: \"gate-x\",\n  serviceId: \"gate-x\",\n  corsOrigins: [\"https://fx.minu.best\"],\n  publicPaths: [\"/api/auth/\", \"/api/openapi.json\"],\n  jwtAlgorithm: \"HS256\",  // 기본값\n}));\n```\n\nJWT Payload 타입:\n```typescript\ninterface JwtPayload {\n  sub: string;\n  email: string;\n  role: \"admin\" | \"member\" | \"viewer\";\n  orgId?: string;\n  orgRole?: \"owner\" | \"admin\" | \"member\" | \"viewer\";\n  services?: Array<{ id: string; role: string }>;\n  iat: number;\n  exp: number;\n}\n```\n\n#### `createCorsMiddleware(config)`\n\nCORS 미들웨어. `corsOrigins` 기반으로 허용 Origin을 설정해요.\n\n```typescript\napp.use(\"*\", createCorsMiddleware(config));\n```\n\n허용 메서드: `GET, POST, PUT, PATCH, DELETE, OPTIONS`\n\n#### `rbac(minRole)`\n\n역할 기반 접근 제어. JWT payload의 `role`을 확인해요.\n\n```typescript\ntype Role = \"admin\" | \"member\" | \"viewer\";\n\n// admin만 접근 가능\napp.delete(\"/api/resource/:id\", rbac(\"admin\"), handler);\n\n// member 이상 접근 가능\napp.post(\"/api/resource\", rbac(\"member\"), handler);\n```\n\n역할 레벨: `admin(3) > member(2) > viewer(1)`\n\n#### `errorHandler()`\n\n표준 에러 응답 포맷을 처리해요.\n\n```typescript\napp.use(\"*\", errorHandler());\n\n// HarnessError 직접 사용\nimport { HarnessError } from \"@ktds-axbd/harness-kit\";\nthrow new HarnessError(\"NOT_FOUND\", \"Resource not found\", 404);\n```\n\n#### `createStranglerMiddleware(config)`\n\nStrangler Fig 패턴 라우터. 서비스 이관 전/후 트래픽을 분기해요.\n\n```typescript\nimport { createStranglerMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createStranglerMiddleware({\n  routes: [\n    {\n      pathPrefix: \"/api/gate\",\n      serviceId: \"gate-x\",\n      mode: \"local\",  // 이관 전: 모놀리스 처리\n    },\n    {\n      pathPrefix: \"/api/launch\",\n      serviceId: \"launch-x\",\n      mode: \"proxy\",  // 이관 후: 외부 서비스 포워딩\n      targetUrl: \"https://launch-x.ktds-axbd.workers.dev\",\n    },\n  ],\n}));\n```\n\n---\n\n#### `createMultiTenantMiddleware(config?)` — v0.2.0\n\nJWT payload의 `orgId`를 검증하고 컨텍스트에 테넌트 정보를 주입해요.\n\n```typescript\nimport { createMultiTenantMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createMultiTenantMiddleware({\n  bypassPaths: [\"/health\", \"/api/public\"],\n  onMissingOrgId: (c) => c.json({ error: \"Org required\" }, 403),\n}));\n\n// 이후 핸들러에서\napp.get(\"/api/data\", (c) => {\n  const orgId = c.get(\"orgId\");     // string\n  const orgRole = c.get(\"orgRole\"); // string (default: \"member\")\n  const userId = c.get(\"userId\");   // string (= payload.sub)\n  return c.json({ orgId, orgRole, userId });\n});\n```\n\n**Config options (v0.2.0)**: `bypassPaths?`, `onMissingOrgId?`\n\n**Config options (v0.3.0 추가)**: `d1Binding?`, `d1Table?` (default: `\"org_members\"`), `d1Mode?` (`\"optional\"` | `\"forced\"`, default: `\"optional\"`), `onMembershipDenied?`\n\n```typescript\n// v0.3.0: D1 membership 강제 검증\napp.use(\"*\", createMultiTenantMiddleware({\n  d1Binding: \"DB\",          // Cloudflare D1 binding key\n  d1Mode: \"forced\",         // D1 없으면 503, row 없으면 403\n  d1Table: \"org_members\",   // 기본값, 커스터마이즈 가능\n  onMembershipDenied: (c, reason) => c.json({ error: \"Access denied\", reason }, 403),\n}));\n```\n\n**D1 권장 schema**:\n```sql\nCREATE TABLE IF NOT EXISTS org_members (\n  user_id TEXT NOT NULL,\n  org_id  TEXT NOT NULL,\n  role    TEXT NOT NULL DEFAULT 'member',\n  PRIMARY KEY (user_id, org_id)\n);\n```\n\n**d1Mode 선택 가이드**:\n- `\"optional\"` (기본): D1 없으면 JWT-only fallback (v0.2.0 동작과 동일)\n- `\"forced\"`: D1 binding 필수, 없으면 503. 운영 보안 강화 시 사용\n\n---\n\n#### `createCacheMiddleware(config)` — v0.3.0\n\nCache-Control 헤더를 응답에 설정해요. CDN/브라우저 캐싱 정책을 미들웨어로 중앙 관리할 수 있어요.\n\n```typescript\nimport { createCacheMiddleware } from \"@ktds-axbd/harness-kit\";\n\n// GET/HEAD 요청 5분 캐시\napp.use(\"/api/static/*\", createCacheMiddleware({\n  maxAge: 300,               // browser cache 5분\n  sMaxAge: 3600,             // CDN cache 1시간\n  staleWhileRevalidate: 60,  // 재검증 중 stale 60초 허용\n  scope: \"public\",           // \"public\" | \"private\" (default: \"public\")\n  methods: [\"GET\", \"HEAD\"],  // (default)\n}));\n\n// 개인화 데이터: private 캐시\napp.use(\"/api/profile\", createCacheMiddleware({\n  maxAge: 60,\n  scope: \"private\",\n}));\n```\n\n**Config options**: `maxAge?`, `sMaxAge?`, `staleWhileRevalidate?`, `scope?`, `pathMatch?`, `methods?`\n\n**동작 규칙**:\n- 4xx/5xx 응답 → Cache-Control 헤더 미설정 (안전)\n- `pathMatch` 미일치 또는 `methods` 미일치 → 건너뜀\n\n---\n\n#### `createCompressionMiddleware(config?)` — v0.3.0\n\ngzip/deflate 압축을 적용해요. `hono/compress`를 래핑하며 `skipPaths`로 스트리밍 경로를 제외할 수 있어요.\n\n```typescript\nimport { createCompressionMiddleware } from \"@ktds-axbd/harness-kit\";\n\n// 전역 gzip 압축 (streaming 경로 제외)\napp.use(\"*\", createCompressionMiddleware({\n  skipPaths: [\"/api/stream\", \"/api/sse\"],  // 건너뜀\n  threshold: 1024,    // 최소 압축 크기 bytes (기본: 1024)\n  encoding: \"gzip\",   // \"gzip\" | \"deflate\" (기본: Accept-Encoding 자동)\n}));\n```\n\n**Config options**: `threshold?`, `encoding?`, `skipPaths?`\n\n**참고**: Cloudflare Workers는 자동 gzip/brotli를 지원하지만, 이 미들웨어를 사용하면 명시적 제어(skipPaths, threshold)가 가능해요.\n\n---\n\n#### `createRateLimitMiddleware(config)` — v0.2.0\n\n요청 수를 제한해요. KV namespace가 없으면 in-memory fallback을 사용해요.\n\n```typescript\nimport { createRateLimitMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createRateLimitMiddleware({\n  limit: 100,        // 최대 요청 수\n  windowSec: 60,     // 윈도우 (초)\n  keyFn: (c) => c.req.header(\"CF-Connecting-IP\") ?? \"anon\",\n  kvBinding: \"RATE_LIMIT_KV\",  // optional: 분산 카운터\n}));\n```\n\n**Config options**: `limit` (required), `windowSec` (required), `keyFn?`, `kvBinding?`\n\n---\n\n#### `createRequestLoggerMiddleware(config?)` — v0.2.0\n\n요청/응답을 구조화된 JSON 또는 텍스트로 로깅해요. 민감 헤더는 자동 redact돼요.\n\n```typescript\nimport { createRequestLoggerMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createRequestLoggerMiddleware({\n  format: \"json\",   // \"json\" | \"text\"\n  logger: console.log,\n  redactHeaders: [\"authorization\", \"cookie\", \"x-api-key\"],\n}));\n// 출력: {\"method\":\"GET\",\"path\":\"/api\",\"status\":200,\"duration\":12,\"traceId\":\"...\",\"timestamp\":\"...\"}\n```\n\n**Config options**: `format?`, `logger?`, `redactHeaders?`\n\n---\n\n#### `createTraceIdMiddleware(config?)` — v0.2.0\n\n요청 ID를 전파해요. 인입 헤더가 있으면 재사용하고, 없으면 UUID를 생성해요.\n\n```typescript\nimport { createTraceIdMiddleware } from \"@ktds-axbd/harness-kit\";\n\n// 권장 ordering: traceId → logger → auth → tenant\napp.use(\"*\", createTraceIdMiddleware({ header: \"x-trace-id\" }));\n\napp.get(\"/api\", (c) => {\n  const traceId = c.get(\"traceId\"); // string\n  return c.json({ traceId });\n});\n```\n\n**Config options**: `header?` (default: `\"x-trace-id\"`), `generator?`\n\n---\n\n#### `signJwt(payload, secret, opts?)` — v0.6.0\n\nJWT를 발급해요. `createAuthMiddleware`(verify)의 대칭 발급 함수예요.\n\n```typescript\nimport { signJwt, createAuthMiddleware } from \"@ktds-axbd/harness-kit\";\n\n// OAuth callback 후 JWT 발급\nconst token = await signJwt(\n  { sub: profile.id, email: profile.email, role: \"member\" },\n  c.env.JWT_SECRET,\n  { expiresIn: 3600 },  // 기본값\n);\n```\n\n**Parameters**: `payload: Omit<JwtPayload, \"iat\"|\"exp\">`, `secret: string`, `opts.expiresIn?: number` (default: 3600s)\n\n---\n\n#### `createOAuthRoutes(config)` — v0.6.0\n\nOAuth 2.0 소셜 로그인 full flow를 Hono sub-app 한 줄로 붙여요.\n`GET /authorize` (provider redirect) + `GET /callback` (code → JWT) 전체를 캡슐화해요.\n\n```typescript\nimport {\n  createOAuthProvider,\n  createOAuthRoutes,\n  GOOGLE_PRESET,\n} from \"@ktds-axbd/harness-kit\";\n\nconst app = new Hono<{ Bindings: Env }>();\n\nconst googleProvider = createOAuthProvider({\n  provider: GOOGLE_PRESET,\n  clientId: process.env.GOOGLE_CLIENT_ID!,\n  clientSecret: process.env.GOOGLE_CLIENT_SECRET!,\n  redirectUri: \"https://my-service.workers.dev/auth/callback\",\n  usePKCE: true,\n});\n\n// 한 줄로 OAuth 로그인 플로우 완성\napp.route(\"/auth\", createOAuthRoutes({\n  provider: googleProvider,\n  onSuccess: { redirectTo: \"/\" },  // 또는 \"json\"\n}));\n\nexport default app;\n```\n\n**OAuth flow**:\n1. `GET /auth/authorize` → state cookie 저장 + 302 to Google\n2. `GET /auth/callback?code=...&state=...` → code 교환 → JWT 발급 → redirect or JSON\n\n**Config options**:\n| 옵션 | 기본값 | 설명 |\n|------|--------|------|\n| `provider` (required) | — | `OAuthProvider` 인스턴스 |\n| `jwtSecret?` | `c.env.JWT_SECRET` 우선 | JWT 서명 키 |\n| `jwtExpiresIn?` | `3600` | Access token 만료 (초) |\n| `refreshExpiresIn?` | `604800` | Refresh token 만료 (초, 7d) |\n| `refreshStore?` | `InMemoryRefreshTokenStore` | Refresh token 상태 저장소 |\n| `refreshCookieName?` | `\"hk_refresh\"` | Refresh token cookie 이름 |\n| `refreshSecret?` | `jwtSecret` fallback | Refresh token 전용 서명키 (v0.8.0) |\n| `refreshGraceMs?` | `0` | Grace window (ms); 0 = strict revoke (v0.8.0) |\n| `cookieName?` | `\"hk_oauth_state\"` | state cookie 이름 |\n| `usePKCE?` | `false` | PKCE S256 활성화 |\n| `mapProfile?` | `{ sub, email, role: \"member\" }` | profile → JWT payload 매핑 |\n| `onSuccess?` | `\"json\"` | `\"json\"` or `{ redirectTo: string }` |\n\n**응답**:\n- `GET /authorize` → `302` to provider\n- `GET /callback` 성공 → `200 { accessToken, refreshToken, token(deprecated alias), profile }` (json) or `302 redirectTo` + `hk_token`/`hk_refresh` cookies\n- code 누락 → `400`, state 불일치 → `403`, provider 오류 → `502`\n- `POST /refresh` `{ refreshToken }` → `200 { accessToken, refreshToken, token }` (rotation) or `401` (invalid/reuse/revoked)\n\n---\n\n#### Refresh Token Rotation — v0.8.0 (RFC 9700)\n\nRFC 9700 기반 stateful refresh token rotation + reuse detection + grace window 제공해요.\n\n```typescript\nimport {\n  createOAuthProvider,\n  createOAuthRoutes,\n  InMemoryRefreshTokenStore,\n  GOOGLE_PRESET,\n} from \"@ktds-axbd/harness-kit\";\n\n// 프로덕션: D1/KV adapter 주입 권장\n// InMemoryRefreshTokenStore는 Workers single-isolate 환경에서 상태 유실 가능\nconst refreshStore = new InMemoryRefreshTokenStore();\n\napp.route(\"/auth\", createOAuthRoutes({\n  provider: googleProvider,\n  refreshStore,  // 생략 시 자동 InMemory 생성\n  refreshExpiresIn: 604800,  // 7일 (기본값)\n  refreshSecret: env.REFRESH_SECRET,  // v0.8.0: refresh 전용 서명키\n  refreshGraceMs: 5000,              // v0.8.0: 5초 grace window (모바일/SPA 동시 요청 대응)\n}));\n```\n\n**Refresh flow**:\n1. `/callback` → `{ accessToken, refreshToken }` 발급 (각각 별도 서명키 사용 — v0.8.0)\n2. `POST /auth/refresh` `{ refreshToken }` → 새 `{ accessToken, refreshToken }` (1회용 회전)\n3. 동일 refresh token 재사용 → grace window 내라면 `200` 이전 신규 토큰 재반환 (idempotent) — v0.8.0\n4. grace 만료 후 재사용 → `401 { error: \"refresh_token_reuse_detected\" }` + family 전체 revoke\n\n**Grace Window (v0.8.0)**:\n- 모바일/SPA 환경에서 동시 refresh 요청 시 정상 클라이언트가 false-positive revocation 당하는 문제 해소\n- `refreshGraceMs=5000` (5초): 첫 회전 후 5초 내 동일 refresh token 재사용 → 이전에 발급된 새 token 재반환\n- `refreshGraceMs=0` (기본): 즉시 revoke (v0.7.0 strict 동작과 동일)\n\n**refreshSecret 격리 (v0.8.0)**:\n- `refreshSecret` 설정 시 refresh token을 별도 키로 서명 → access token 서명키 노출이 refresh token을 위협하지 않음\n- 미설정 시 `jwtSecret` fallback (v0.7.0 backward compat)\n\n**⚠️ 프로덕션 주의사항**: `InMemoryRefreshTokenStore`는 Workers isolate 재시작 시 상태 초기화. 프로덕션에서는 D1/KV adapter를 직접 구현하세요:\n\n```typescript\nclass D1RefreshTokenStore implements RefreshTokenStore {\n  constructor(private db: D1Database) {}\n\n  async isRotatedOrRevoked(jti: string): Promise<boolean> {\n    const row = await this.db.prepare(\n      \"SELECT 1 FROM refresh_tokens WHERE jti = ? AND (rotated = 1 OR family_revoked = 1)\"\n    ).bind(jti).first();\n    return row !== null;\n  }\n  // markRotated, revokeFamily, isFamilyRevoked ...\n}\n```\n\n---\n\n### D1 유틸리티\n\n```typescript\nimport { getDb, runQuery, runExec } from \"@ktds-axbd/harness-kit/d1\";\n\n// Workers fetch handler에서\nexport default {\n  async fetch(req: Request, env: Env) {\n    const db = getDb(env.DB);\n    const rows = await runQuery<User>(db, \"SELECT * FROM users WHERE id = ?\", [id]);\n    return Response.json(rows);\n  },\n};\n```\n\n---\n\n### Event Bus\n\nD1 기반 도메인 이벤트 발행/구독이에요.\n\n```typescript\nimport { D1EventBus, NoopEventBus, createEvent } from \"@ktds-axbd/harness-kit/events\";\n\n// 이벤트 발행\nconst bus = new D1EventBus(env.DB);\nawait bus.publish(\n  createEvent(\"discovery.opportunity.created\", \"gate-x\", {\n    opportunityId: \"opp-123\",\n    title: \"AI 기반 BD 자동화\",\n  })\n);\n\n// 이벤트 구독\nbus.subscribe(\"discovery.opportunity.created\", async (event) => {\n  console.log(\"New opportunity:\", event.payload);\n});\n\n// 폴링으로 미처리 이벤트 처리 (Cron Trigger에서 사용)\nawait bus.poll(\"gate-x\", 100);  // 최대 100건\n```\n\n**D1 마이그레이션 필요:**\n```sql\n-- 0114_domain_events.sql (Foundry-X DB에 이미 적용됨)\nCREATE TABLE IF NOT EXISTS domain_events (\n  id TEXT PRIMARY KEY,\n  type TEXT NOT NULL,\n  source TEXT NOT NULL,\n  tenant_id TEXT NOT NULL DEFAULT 'default',\n  payload TEXT NOT NULL,\n  metadata TEXT,\n  status TEXT NOT NULL DEFAULT 'pending',\n  created_at TEXT NOT NULL\n);\n```\n\n---\n\n### ESLint Plugin\n\n서비스 간 직접 import를 방지해요.\n\n```javascript\n// eslint.config.mjs\nimport { harnessKitPlugin } from \"@ktds-axbd/harness-kit/eslint\";\n\nexport default [\n  {\n    plugins: { \"harness-kit\": harnessKitPlugin },\n    rules: {\n      \"harness-kit/no-cross-service-import\": \"error\",\n    },\n    settings: {\n      \"harness-kit\": {\n        currentService: \"gate-x\",\n        // 직접 import 금지 서비스 목록 (기본: 모든 ServiceId)\n        forbiddenServices: [\"foundry-x\", \"launch-x\"],\n      },\n    },\n  },\n];\n```\n\n### Resilience — 외부 호출 견고성 (`./resilience`, v0.15.0)\n\n외부 API(AI 모델 등) 호출의 재시도·차단·대체 패턴. 무의존(AbortController + setTimeout).\n\n```ts\nimport { withRetry, createCircuitBreaker, withFallback } from \"@ktds-axbd/harness-kit/resilience\";\n\n// 지수 백오프 + jitter + retryable predicate\nconst result = await withRetry(() => fetch(url), {\n  maxAttempts: 3,\n  baseDelayMs: 200,\n  jitter: true,\n  isRetryable: (e) => e instanceof TypeError, // 네트워크 오류만 재시도\n});\n\n// 회로 차단기 — closed→open→half-open\nconst breaker = createCircuitBreaker({ failureThreshold: 5, resetTimeoutMs: 30_000, halfOpenMaxCalls: 2 });\nconst data = await breaker.execute(() => callExternalApi());\n\n// 폴백 체인 — primary 실패 시 fallback\nconst value = await withFallback(() => primaryModel(), () => fallbackModel());\n```\n\n### Webhook 서명 검증 (`./middleware`, v0.15.0)\n\n수신 webhook의 HMAC 서명을 검증하는 미들웨어. GitHub/Slack/Stripe 프리셋 + Web Crypto timing-safe 비교.\n\n```ts\nimport { createWebhookVerifyMiddleware } from \"@ktds-axbd/harness-kit/middleware\";\n\n// GitHub (X-Hub-Signature-256)\napp.use(\"/webhooks/github\", createWebhookVerifyMiddleware({ provider: \"github\", secret: c.env.GH_WEBHOOK_SECRET }));\n// 검증 실패 시 401, 성공 시 다음 핸들러로 (raw body 보존)\n```\n\n### KV Store (`./kv`, v0.15.0)\n\nCloudflare KV 바인딩 helper. namespace prefix + JSON + TTL. `./d1` 대칭.\n\n```ts\nimport { createKVStore } from \"@ktds-axbd/harness-kit/kv\";\n\nconst kv = createKVStore(c.env.CACHE, { prefix: \"session:\" });\nawait kv.setJSON(\"user-123\", { name: \"...\" }, { ttlSeconds: 3600 });\nconst user = await kv.getJSON<User>(\"user-123\"); // prefix 자동 부착, miss 시 null\n```\n\n**`getOrFetch` cache-aside (v0.21.0+)** — 캐시 hit 시 반환, miss 시 fetcher 실행 후 저장하고 결과 반환. raw JSON(envelope 없음) + prefix·TTL 적용.\n\n```ts\nconst profile = await kv.getOrFetch<Profile>(\n  \"user-123\",\n  async () => fetchProfileFromD1(\"user-123\"), // miss 시에만 실행\n  3600, // TTL(초)\n);\n```\n\n### Health/Readiness probe (`./middleware`, v0.16.0)\n\nliveness(즉시 200) + readiness(의존성 체크 콜백). 로드밸런서·Cloudflare 헬스체크·k8s 스타일 probe.\n\n```ts\nimport { createHealthMiddleware } from \"@ktds-axbd/harness-kit/middleware\";\n\nconst health = createHealthMiddleware({\n  checks: [\n    { name: \"d1\", check: async () => { await c.env.DB.prepare(\"SELECT 1\").first(); } },\n    { name: \"kv\", check: async () => { await c.env.CACHE.get(\"ping\"); } },\n  ],\n  // livenessPath: \"/health\" (기본), readinessPath: \"/ready\" (기본)\n});\napp.route(\"/\", health); // GET /health → 200, GET /ready → 200 또는 503 + 실패 항목\n```\n\n### API Key auth (`./middleware`, v0.16.0)\n\nM2M 인증. 발급·검증·rotation. 원본 키는 1회만 반환, KV에는 SHA-256 해시만 저장.\n\n```ts\nimport { createApiKeyMiddleware, issueApiKey, rotateApiKey } from \"@ktds-axbd/harness-kit/middleware\";\n\n// 발급 (원본 키는 이때 1회만 노출)\nconst { apiKey, id } = await issueApiKey({ kv: c.env.KEYS, owner: \"svc-a\", options: { scopes: [\"read\"] } });\n\n// 검증 미들웨어 (X-API-Key 헤더)\napp.use(\"/api/*\", createApiKeyMiddleware({ kvBinding: \"KEYS\", bypassPaths: [\"/api/health\"] }));\n// 통과 시 c.get(\"apiKeyId\")·c.get(\"owner\")·c.get(\"scopes\"), 실패 시 401\n\n// rotation (신규 발급 + 기존 revoke)\nconst rotated = await rotateApiKey({ kv: c.env.KEYS, id, owner: \"svc-a\" });\n```\n\n### Pagination (`./pagination`, v0.16.0)\n\ncursor·offset 양 모드 순수 유틸. `./d1`와는 SQL fragment 반환으로 연동(직접 import 없음).\n\n```ts\nimport { parsePaginationParams, buildPageResponse, buildD1PaginationClause } from \"@ktds-axbd/harness-kit/pagination\";\n\nconst params = parsePaginationParams(c.req.query()); // { limit, cursor?, offset? } (limit max 100 clamp)\nconst { clause, binds } = buildD1PaginationClause({ ...params, orderBy: \"id\" });\nconst rows = await c.env.DB.prepare(`SELECT * FROM items ${clause}`).bind(...binds).all();\nreturn c.json(buildPageResponse(rows.results, { ...params, cursorField: \"id\" })); // { data, pageInfo: { hasNextPage, nextCursor } }\n```\n\n### R2 Store (`./r2`, v0.16.0)\n\nCloudflare R2 바인딩 helper. `./kv` 대칭. presigned URL은 R2 바인딩 미지원(Worker proxy 패턴 별도).\n\n```ts\nimport { createR2Store } from \"@ktds-axbd/harness-kit/r2\";\n\nconst r2 = createR2Store(c.env.BUCKET, { prefix: \"uploads/\" });\nawait r2.put(\"a.json\", JSON.stringify(data), { httpMetadata: { contentType: \"application/json\" } });\nconst obj = await r2.getJSON<Data>(\"a.json\"); // prefix 자동 부착\nconst page = await r2.list({ prefix: \"2026/\", limit: 100 }); // { objects, truncated, cursor }\n```\n\n### Queue (`./queue`, v0.16.0)\n\nCloudflare Queue producer + consumer helper.\n\n```ts\nimport { createQueueProducer, createQueueConsumer } from \"@ktds-axbd/harness-kit/queue\";\n\n// Producer\nconst q = createQueueProducer(c.env.MY_QUEUE);\nawait q.send({ jobId: 1 });\nawait q.sendBatch([{ jobId: 2 }, { jobId: 3 }], { delaySeconds: 30 });\n\n// Consumer (메시지별 ack 성공 / retry throw 격리)\nexport const queue = createQueueConsumer<Job>(async (body) => { await process(body); });\n```\n\n### Structured Logger (`./middleware`, v0.17.0)\n\n요청별 구조화 JSON 로거. traceId 자동 바인딩. 핸들러에서 `getLogger(c)`로 호출.\n\n```ts\nimport { createLoggerMiddleware, getLogger } from \"@ktds-axbd/harness-kit/middleware\";\n\napp.use(\"*\", createTraceIdMiddleware());\napp.use(\"*\", createLoggerMiddleware({ service: \"gate-x\", minLevel: \"info\", redact: [\"password\"] }));\n\napp.get(\"/api/data\", (c) => {\n  getLogger(c).info(\"fetch data\", { userId: c.get(\"userId\") }); // { level, msg, traceId, service, userId, timestamp }\n  return c.json({ ok: true });\n});\n```\n\n### Cron/Scheduled handler (`./cron`, v0.17.0)\n\nWorkers cron 트리거 래퍼. name별 라우팅 + job 에러 격리.\n\n```ts\nimport { createScheduledHandler } from \"@ktds-axbd/harness-kit/cron\";\n\nexport const scheduled = createScheduledHandler({\n  jobs: [\n    { name: \"hourly-sync\", cron: \"0 * * * *\", handler: async (ctx) => { await sync(ctx.env); } },\n    { name: \"daily-report\", cron: \"0 0 * * *\", handler: async (ctx) => { await report(ctx.env); } },\n  ],\n}); // event.cron으로 매칭 job 실행, 1개 throw가 타 job 미차단, ctx.waitUntil\n```\n\n**`schedule` predicate (v0.22.0):** 단일 cron 안에서 시간/조건 제약을 선언적으로 표현. 예를 들어 6시간마다 도는 cron에 백업 job만 UTC 18시에 실행하고 싶을 때, handler 내부 `if (getUTCHours() !== 18) return` 대신:\n\n```ts\ncreateScheduledHandler({\n  jobs: [\n    { name: \"recon\", cron: \"0 */6 * * *\", handler: reconJob }, // 매 실행\n    {\n      name: \"backup\",\n      cron: \"0 */6 * * *\",\n      schedule: () => new Date().getUTCHours() === 18, // UTC 18시에만\n      handler: backupJob,\n    },\n  ],\n}); // schedule false면 cron 매칭이어도 skip + logger.info(\"cron skip\")\n```\n\n**타입 export (v0.22.0):** `ScheduledEventLike`/`ExecutionContextLike`를 export해, 반환값을 `export const scheduled = ...`로 내보낼 때 declaration emit의 private name leak(TS4023/TS4082)을 방지. Cloudflare 표준 타입(`ScheduledController`/`ExecutionContext`)도 구조적으로 호환돼요.\n\n### Metrics scrape endpoint (`./metrics`, v0.17.0)\n\nPrometheus pull(/metrics) 엔드포인트. otel push와 별개.\n\n```ts\nimport { createMetricsRegistry, createMetricsEndpoint } from \"@ktds-axbd/harness-kit/metrics\";\n\nconst metrics = createMetricsRegistry();\nconst reqCount = metrics.counter(\"http_requests_total\", \"총 요청 수\", [\"method\"]);\nreqCount.inc({ method: \"GET\" });\n\napp.route(\"/\", createMetricsEndpoint(metrics)); // GET /metrics → Prometheus text format\n```\n\n### WebSocket/DO helper (`./websocket`, v0.17.0)\n\nWebSocket 업그레이드 + 룸 broadcast(Durable Object 호환).\n\n```ts\nimport { createWebSocketUpgrade, createBroadcastHub } from \"@ktds-axbd/harness-kit/websocket\";\n\nconst hub = createBroadcastHub();\n\napp.get(\"/ws\", (c) =>\n  createWebSocketUpgrade({           // non-WS 요청 시 426\n    onOpen: (ws) => hub.add(ws),\n    onMessage: (ws, msg) => hub.broadcast(msg),  // 전체 broadcast\n    onClose: (ws) => hub.remove(ws),             // 자동 remove\n  })(c.req.raw),                     // 101 Response 반환\n);\n```\n\n### Validation (`./middleware`, v0.18.0)\n\nduck-typed schema(zod·valibot 호환) 요청 검증. **zod 미import = dependency-free**.\n\n```ts\nimport { createValidatorMiddleware, getValidated } from \"@ktds-axbd/harness-kit/middleware\";\nimport { z } from \"zod\"; // 소비자가 제공 (harness-kit는 zod 비의존)\n\nconst schema = z.object({ name: z.string(), age: z.number() });\n\napp.post(\"/users\", createValidatorMiddleware(\"json\", schema), (c) => {\n  const body = getValidated<{ name: string; age: number }>(c, \"json\"); // 검증된 타입 데이터\n  return c.json({ ok: true, body });\n}); // 검증 실패 시 400 + { issues: [...] }\n```\n\n### Config/Secrets (`./config`, v0.18.0)\n\nWorkers env 타입 로더. required fail-fast + transform + secret 마스킹.\n\n```ts\nimport { createConfig } from \"@ktds-axbd/harness-kit/config\";\n\nconst config = createConfig(c.env, {\n  API_URL: { required: true },\n  MAX_RETRIES: { default: \"3\", transform: Number },\n  JWT_SECRET: { required: true, secret: true }, // 로그에서 *** 마스킹\n});\nconfig.get(\"API_URL\");        // string (없으면 startup throw)\nconfig.get(\"MAX_RETRIES\");    // 3 (number)\nconsole.log(config.toJSON()); // { ..., JWT_SECRET: \"***\" }\n```\n\n### Service Binding/RPC (`./service-binding`, v0.18.0)\n\nMSA Worker-to-Worker service binding 호출 클라이언트.\n\n```ts\nimport { createServiceClient } from \"@ktds-axbd/harness-kit/service-binding\";\n\nconst agent = createServiceClient(c.env.AGENT_SVC, {  // Cloudflare service binding (Fetcher)\n  basePath: \"/api/agent\",\n  headers: { authorization: c.req.header(\"authorization\") ?? \"\" }, // auth 전파\n});\nconst result = await agent.post<{ id: string }>(\"/run\", { task: \"x\" }); // 타입 JSON, non-2xx → throw\n```\n\n### Observability — OTel export (v0.9.0)\n\n`createTraceIdMiddleware` + `createRequestLoggerMiddleware`가 만든 데이터를 **OpenTelemetry SDK 의존성 없이** OTLP/JSON wire format으로 collector에 직접 전송해요 (`fetch` 기반, bundle footprint 추가 ≈ 0).\n\n```typescript\nimport {\n  createOtelTraceMiddleware,\n  exportMetrics,\n  buildRequestCountMetric,\n  buildDurationHistogramMetric,\n} from \"@ktds-axbd/harness-kit/otel\";\n\n// 요청당 span을 OTLP collector로 전송 (trace-id + method/path/status/duration → span attributes)\napp.use(\"*\", createOtelTraceMiddleware({\n  endpoint: \"https://collector.example.com\",  // POST {endpoint}/v1/traces\n  serviceName: \"my-service\",\n  headers: { \"x-api-key\": env.OTEL_API_KEY },\n  resourceAttributes: { \"deployment.environment\": \"production\" },\n}));\n```\n\n**Signals**: traces(요청 span) + metrics(request count `sum` + duration `histogram`). `exportTraces`/`exportMetrics`가 OTLP/JSON payload를 `/v1/traces`·`/v1/metrics`로 POST하고, span flush는 `ctx.waitUntil`로 응답 지연 없이 처리해요.\n\n**의존성 0**: `@microlabs/otel-cf-workers`·`@opentelemetry/*` 등 SDK 미사용 — OTLP/JSON(`resourceSpans`/`resourceMetrics`) 구조를 직접 구성. peerDependency 추가 없음.\n\n#### W3C Trace Context — traceparent 전파 (v0.10.0)\n\n인바운드 `traceparent` 헤더를 파싱하여 분산 트레이싱의 parent-child span 연결을 지원해요.\n\n```typescript\nimport {\n  createOtelTraceMiddleware,\n  getTraceparent,\n  parseTraceparent,\n  buildTraceparent,\n} from \"@ktds-axbd/harness-kit/otel\";\n\n// 1. Middleware 등록 — 인바운드 traceparent 자동 파싱\napp.use(\"*\", createOtelTraceMiddleware({ endpoint: \"https://collector.example.com\", serviceName: \"svc-a\" }));\n\n// 2. 아웃바운드 fetch에 traceparent 전파 (downstream service와 trace 연결)\napp.get(\"/api/data\", async (c) => {\n  const res = await fetch(\"https://svc-b.example.com/api/items\", {\n    headers: { traceparent: getTraceparent(c) ?? \"\" },\n  });\n  return c.json(await res.json());\n});\n\n// 3. 직접 파싱 / 생성 유틸\nconst parsed = parseTraceparent(\"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01\");\n// → { traceId: \"4bf92f...\", parentSpanId: \"00f067...\", flags: \"01\" }\n// all-zero traceId/spanId, 잘못된 포맷 → null\n\nconst header = buildTraceparent(\"4bf92f3577b34da6a3ce929d0e0e4736\", \"00f067aa0ba902b7\");\n// → \"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01\"\n```\n\n인바운드 `traceparent`가 유효하면 span이 기존 traceId를 이어받고 `parentSpanId`가 설정되어 parent-child 관계가 형성돼요. 유효하지 않거나 없으면 새 root span이 생성돼요.\n\n#### OTLP Logs export — log-trace correlation (v0.11.0)\n\n세 번째 OTLP signal인 **logs**를 추가해요. `createOtelTraceMiddleware`의 `includeLogs` 옵션(기본 `true`)으로 요청당 LogRecord 1개를 collector로 전송하며, 같은 요청 span의 `traceId`/`spanId`를 담아 **trace와 자동 상관(correlation)**돼요.\n\n```typescript\nimport {\n  createOtelTraceMiddleware,\n  exportLogs,\n  buildLogRecord,\n} from \"@ktds-axbd/harness-kit/otel\";\n\n// includeLogs 기본 true — traces·metrics와 동일 미들웨어에서 요청당 LogRecord 발행\napp.use(\"*\", createOtelTraceMiddleware({\n  endpoint: \"https://collector.example.com\",  // POST {endpoint}/v1/logs\n  serviceName: \"my-service\",\n  includeLogs: true,   // 기본값 (includeMetrics와 동형, false로 끄기 가능)\n}));\n```\n\n**LogRecord**: body `\"METHOD path STATUS\"`, severity는 status 기반 매핑(5xx → `ERROR`(17) · 4xx → `WARN`(13) · 그 외 → `INFO`(9)). `traceId`/`spanId`가 설정되어 collector backend(Tempo/Loki 등)에서 trace ↔ log 연결이 가능해요.\n\n**Signals (3종)**: traces(요청 span) + metrics(count `sum` + duration `histogram`) + logs(요청당 LogRecord). 각각 `exportTraces`/`exportMetrics`/`exportLogs`가 `/v1/traces`·`/v1/metrics`·`/v1/logs`로 OTLP/JSON POST하고, flush는 모두 `ctx.waitUntil`로 응답 지연 없이 처리해요. **의존성 0 유지** (manual OTLP/JSON, `resourceLogs`/`scopeLogs`/`logRecords` 구조 직접 구성).\n\n---\n\n### Per-Request 테넌트 라우팅 (v0.12.0 — 멀티테넌트 관측)\n\n`createMultiTenantMiddleware`와 `createOtelTraceMiddleware`를 연동하면 **요청마다 orgId를 동적으로 추출**해 traces·metrics·logs 3 signal 전부를 별도 테넌트 백엔드로 라우팅해요. Tempo·Mimir·Loki 멀티테넌시를 단일 Collector 인스턴스로 구현할 수 있어요.\n\n#### 통합 예제\n\n```typescript\nimport { createHarness } from \"@ktds-axbd/harness-kit\";\nimport { createMultiTenantMiddleware, createOtelTraceMiddleware } from \"@ktds-axbd/harness-kit\";\n\nconst config = { serviceName: \"my-service\", serviceId: \"my-service\", corsOrigins: [\"*\"] };\nconst app = createHarness(config);\n\n// 1. 테넌트 컨텍스트 추출 (X-Org-ID 헤더 → c.set(\"orgId\", ...))\napp.use(\"*\", createMultiTenantMiddleware({\n  headerName: \"X-Org-ID\",    // 소비자가 보내는 헤더\n  contextKey: \"orgId\",       // c.get(\"orgId\")로 접근\n  defaultOrgId: \"default\",\n}));\n\n// 2. OTel 트레이싱 + 테넌트 라우팅\napp.use(\"*\", createOtelTraceMiddleware(config, {\n  endpoint: \"http://localhost:4318\",  // OTel Collector\n  tenantContextKey: \"orgId\",          // createMultiTenantMiddleware의 contextKey와 일치\n  tenantHeaderName: \"X-Scope-OrgID\", // Grafana 스택 표준 헤더 (Mimir·Loki·Tempo)\n  defaultOrgId: \"default\",           // 테넌트 컨텍스트 없을 때 fallback (헤더 생략)\n}));\n```\n\n#### 동작 원리\n\n| 단계 | 설명 |\n|------|------|\n| 요청 수신 | `createMultiTenantMiddleware`가 `X-Org-ID` 헤더를 읽어 `c.set(\"orgId\", orgId)` |\n| flush 시점 | `ctx.waitUntil`로 비동기 실행 — `c.get(\"orgId\")`로 orgId 획득 |\n| 헤더 주입 | `withTenantHeader(config, orgId, \"X-Scope-OrgID\")`로 각 signal의 HTTP 헤더에 주입 |\n| 테넌트 격리 | Tempo: `X-Scope-OrgID` → Mimir: `X-Scope-OrgID` → Loki: `X-Scope-OrgID` |\n\n#### No-context Fallback\n\n`tenantContextKey`로 조회된 값이 없으면:\n- `defaultOrgId`가 설정된 경우 → `X-Scope-OrgID: defaultOrgId` 주입\n- `defaultOrgId`도 없으면 → 테넌트 헤더 생략 (단일 테넌트 동작)\n\n---\n\n### Grafana 관측 환경 (v0.12.0 — dashboards-as-code)\n\n`examples/observability/` 디렉토리에 **Grafana as-code provisioning** 파일이 포함돼 있어요. `docker compose up` 한 번으로 완전한 관측 스택을 재현할 수 있어요.\n\nnpm 설치 후 `node_modules/@ktds-axbd/harness-kit/examples/observability/`에서 바로 사용할 수 있어요.\n\n#### 포함 서비스\n\n| 서비스 | 이미지 | 포트 | 역할 |\n|--------|--------|------|------|\n| Grafana | `grafana/grafana:11.3.1` | `:3000` | 대시보드 + datasource 관리 |\n| Tempo | `grafana/tempo:2.6.1` | `:3200 / :4317` | 분산 추적 |\n| Loki | `grafana/loki:3.2.0` | `:3100` | 로그 수집 |\n| Prometheus | `prom/prometheus:v2.53.0` | `:9090` | 메트릭 수집 |\n| Mimir | `grafana/mimir:2.14.0` | `:9009` | 멀티테넌트 메트릭 |\n| OTel Collector | `otel/opentelemetry-collector-contrib:latest` | `:4318` | 신호 수집·라우팅 |\n\n#### 로컬 실행\n\n```bash\n# npm 설치 후\ncd node_modules/@ktds-axbd/harness-kit/examples/observability\ndocker compose up -d\n\n# Grafana UI: http://localhost:3000  (anonymous admin)\n# RED 대시보드: http://localhost:3000/d/harness-kit-red\n```\n\n#### 기존 Grafana에 붙이기\n\n`grafana/provisioning/` 파일을 기존 Grafana 인스턴스의 provisioning 디렉토리에 복사하면 Tempo·Loki·Prometheus datasource와 RED 대시보드를 자동으로 로드해요.\n\n```bash\ncp -r examples/observability/grafana/provisioning /etc/grafana/provisioning\ncp -r examples/observability/grafana/dashboards /etc/grafana/dashboards\n# Grafana 재시작\n```\n\n---\n\n## HarnessConfig 타입\n\n```typescript\ninterface HarnessConfig {\n  serviceName: string;          // 서비스 표시명\n  serviceId: ServiceId;         // 서비스 식별자\n  corsOrigins: string[];        // 허용 CORS Origin 목록\n  publicPaths?: string[];       // 인증 불필요 경로 (prefix 매칭)\n  jwtAlgorithm?: string;        // JWT 알고리즘 (기본: \"HS256\")\n}\n```\n\n---\n\n---\n\n## OAuth 2.0 Provider\n\nWorkers 환경(fetch + crypto.subtle)에서 Google/GitHub 소셜 로그인을 처리하는 helper예요.\n\n### 기본 흐름\n\n```typescript\nimport { createOAuthProvider, generatePKCE, generateState, verifyState } from \"@ktds-axbd/harness-kit\";\n\nconst oauth = createOAuthProvider({\n  provider: \"google\",  // \"google\" | \"github\" | GenericProviderSpec\n  clientId: env.GOOGLE_CLIENT_ID,\n  clientSecret: env.GOOGLE_CLIENT_SECRET,\n  redirectUri: \"https://myapp.example.com/callback\",\n  usePKCE: true,\n});\n\n// Step 1: Authorization URL 생성\nconst pkce = await generatePKCE();\nconst state = generateState();\nconst { url } = oauth.getAuthorizationUrl({ state, codeVerifier: pkce.verifier });\n// state/verifier를 KV에 저장 후 redirect\n\n// Step 2: Callback — code 교환\nconst tokens = await oauth.exchangeCode({ code, codeVerifier: storedVerifier });\n\n// Step 3: 사용자 정보\nconst profile = await oauth.getUserInfo(tokens.accessToken);\n// profile: { id, email, name, avatarUrl?, raw }\n```\n\n### JwtPayload 연동\n\n```typescript\nconst payload = { sub: profile.id, email: profile.email ?? \"\", role: \"member\" as const };\n```\n\n---\n\n## Feature Flags — OpenFeature 어댑터 (v0.12.0)\n\n`./flags` 모듈은 의존성 없는 feature-flag 평가(boolean·string·number·JSON + deterministic rollout + targeting)를 제공하며, `HarnessKitProvider`로 [OpenFeature](https://openfeature.dev) SDK에 연결할 수 있어요. Cloudflare Flagship GA 시 `FlagshipProvider`로 교체 가능한 표준 어댑터예요.\n\n### 설치 (OpenFeature 어댑터 사용 시)\n\n`@openfeature/server-sdk`는 **optional peerDependency**예요 — 어댑터를 쓸 때만 설치하면 돼요.\n\n```bash\nnpm install @openfeature/server-sdk\n```\n\n### 사용 예\n\n```typescript\nimport { OpenFeature } from \"@openfeature/server-sdk\";\nimport { HarnessKitProvider, StaticFlagProvider } from \"@ktds-axbd/harness-kit/flags\";\n\n// 1) harness FlagProvider 정의 (Static 또는 KVFlagProvider)\nconst flagProvider = new StaticFlagProvider({\n  \"new-dashboard\": {\n    key: \"new-dashboard\",\n    defaultValue: false,\n    rules: [{ when: { orgId: \"org-123\" }, value: true, variant: \"treatment\" }],\n  },\n});\n\n// 2) OpenFeature SDK에 harness-kit Provider 등록\nawait OpenFeature.setProviderAndWait(new HarnessKitProvider(flagProvider));\n\n// 3) 표준 OpenFeature client API로 평가\nconst client = OpenFeature.getClient();\nconst enabled = await client.getBooleanValue(\"new-dashboard\", false, {\n  targetingKey: userId, // → harness EvaluationContext.userId\n  orgId: \"org-123\",     // → harness EvaluationContext.orgId\n});\n```\n\n`HarnessKitProvider`는 4종 resolve(`Boolean`/`String`/`Number`/`Object`)를 지원하며, harness `ResolutionDetails`(`value`·`reason`·`variant`)를 OpenFeature 표준 형식으로 전파해요. 어댑터를 거치지 않고 `isEnabled(c, key)`·`getFlag(c, key)` 미들웨어 헬퍼를 직접 쓸 수도 있어요.\n\n---\n\n#### `createSecurityHeadersMiddleware(config?)` (F715)\n\n보안 헤더 주입 미들웨어. CSP·HSTS·X-Frame-Options·X-Content-Type-Options·Referrer-Policy를 응답에 추가해요.\n\n```typescript\nimport { createSecurityHeadersMiddleware } from \"@ktds-axbd/harness-kit\";\n// 또는 subpath: import { createSecurityHeadersMiddleware } from \"@ktds-axbd/harness-kit/middleware\";\n\napp.use(\"*\", createSecurityHeadersMiddleware({\n  csp: \"default-src 'self'; script-src 'self' 'nonce-{n}'\", // false로 비활성화\n  hsts: { maxAge: 63072000, includeSubDomains: true },\n  xFrameOptions: \"DENY\",          // \"SAMEORIGIN\" | false\n  xContentTypeOptions: true,       // nosniff (기본값)\n  referrerPolicy: \"strict-origin-when-cross-origin\", // false로 비활성화\n}));\n```\n\n기본값: CSP `default-src 'self'`, X-Frame-Options `DENY`, nosniff `true`, Referrer-Policy `strict-origin-when-cross-origin`. HSTS는 기본 비활성화.\n\n**조건부 CSP (v0.19.0+)** — `csp`에 콜백을 전달하면 응답 컨텍스트 기반으로 CSP를 동적 결정해요. HTML 문서(예: Swagger UI)는 제외하고 JSON API에만 strict CSP를 적용하는 등 HTML+JSON 혼재 표면에 유용해요. 콜백이 `false`/`undefined`를 반환하면 CSP 헤더를 설정하지 않아요.\n\n```typescript\napp.use(\"*\", createSecurityHeadersMiddleware({\n  // JSON 응답만 strict CSP, HTML(Swagger UI 등)은 제외\n  csp: (c) => (c.res.headers.get(\"Content-Type\") ?? \"\").includes(\"text/html\")\n    ? false\n    : \"default-src 'none'\",\n}));\n```\n\n#### `createTimeoutMiddleware(config)` (F715)\n\n요청 타임아웃 미들웨어. 핸들러가 `timeoutMs` 초과 시 504 Gateway Timeout을 반환해요.\n\n```typescript\nimport { createTimeoutMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createTimeoutMiddleware({\n  timeoutMs: 5000,\n  message: \"Request timed out\",   // 기본: \"Gateway Timeout\"\n  skipPaths: [\"/health\", \"/api/stream\"],\n}));\n```\n\n#### `createBodyLimitMiddleware(config)` (F715)\n\n요청 바디 크기 제한 미들웨어. `Content-Length` 초과 시 413 Payload Too Large를 반환해요. (DoS 방어)\n\n```typescript\nimport { createBodyLimitMiddleware } from \"@ktds-axbd/harness-kit\";\n\napp.use(\"*\", createBodyLimitMiddleware({\n  maxBytes: 10_000_000,           // JSON 기본 10MB\n  message: \"File too large\",\n  methods: [\"POST\", \"PUT\", \"PATCH\"], // 기본값\n  skipPaths: [\"/api/health\"],      // v0.19.0+ — 한도 완전 면제 경로\n  pathLimits: [                    // v0.20.0+ — prefix별 차등 한도\n    { prefix: \"/api/files/\", maxBytes: 50_000_000 },        // 업로드 50MB\n    { prefix: \"/api/launch/object-store/\", maxBytes: 50_000_000 },\n  ],\n}));\n```\n\nContent-Length 헤더가 없으면 passthrough. GET/DELETE 등은 기본 제한 제외. **`skipPaths` (v0.19.0+)** — 업로드 등 큰 body가 정상인 경로를 한도 검사에서 완전 면제해요. **`pathLimits` (v0.20.0+)** — 경로 prefix별 차등 한도를 적용해요(예: JSON 10MB, 업로드 50MB). 여러 prefix 매칭 시 가장 긴(구체적인) prefix가 우선(longest-prefix-match), `skipPaths`와 병존(skipPaths 먼저 검사). `skipPaths`로 업로드를 면제하면 ceiling이 사라지는 문제를 `pathLimits`로 해소(업로드도 한도 유지).\n\n#### `createIdempotencyMiddleware(config?)` (F716)\n\nPOST/PATCH 중복 실행 방지 미들웨어. `Idempotency-Key` 헤더 기반으로 첫 요청 응답을 저장하고, 동일 키 재요청 시 replay해요. in-flight 중 동일 키 요청은 409를 반환해요.\n\n```typescript\nimport { createIdempotencyMiddleware } from \"@ktds-axbd/harness-kit\";\n// 또는 subpath: import { createIdempotencyMiddleware } from \"@ktds-axbd/harness-kit/middleware\";\n\napp.use(\"*\", createIdempotencyMiddleware({\n  kvBinding: \"IDEMPOTENCY_KV\",    // Cloudflare KV namespace binding (필수, 없으면 passthrough)\n  ttlSec: 86400,                  // 캐시 TTL (기본: 86400초 = 24시간)\n  keyHeader: \"Idempotency-Key\",   // 헤더 이름 (기본값)\n  methods: [\"POST\", \"PATCH\"],     // 적용 메서드 (기본값)\n}));\n```\n\n**동작 요약:**\n- `kvBinding` 미설정 → passthrough (idempotency 비활성화)\n- `Idempotency-Key` 헤더 없음 → passthrough\n- 첫 요청 → handler 실행, KV에 응답 저장\n- 동일 키 재요청 → 저장된 응답 replay (handler 미실행)\n- in-flight 중 동일 키 → `409 Conflict`\n- TTL 만료 → 키 소멸, 새 요청으로 처리\n\n---\n\n### Subpath Exports\n\n```typescript\n// middleware subpath (F715+)\nimport { createSecurityHeadersMiddleware, createTimeoutMiddleware, createBodyLimitMiddleware, createIdempotencyMiddleware } from \"@ktds-axbd/harness-kit/middleware\";\n\n// oauth subpath (F715+)\nimport { createOAuthProvider, createOAuthRoutes } from \"@ktds-axbd/harness-kit/oauth\";\n```\n\n---\n\n## Resilience — 외부 호출 견고성 (v0.15.0+)\n\n`./resilience` subpath export — 무의존(AbortController + setTimeout), Cloudflare Workers 호환.\n\n```typescript\nimport { withRetry, createCircuitBreaker, withFallback } from '@ktds-axbd/harness-kit/resilience'\n```\n\n### withRetry\n\n지수 백오프 + jitter + AbortSignal 취소 지원.\n\n```typescript\nconst result = await withRetry(\n  () => fetchExternalAPI(),\n  {\n    maxAttempts: 5,       // 최대 시도 횟수\n    baseDelayMs: 100,     // 기본 지연 ms (기본값: 100)\n    maxDelayMs: 10000,    // 최대 지연 ms (기본값: 10000)\n    jitter: true,         // 지터 활성화 (기본값: true, thundering herd 방지)\n    signal: abortSignal,  // 취소 신호 (선택)\n    retryable: (err) => err instanceof NetworkError, // 재시도 가능 여부 (선택)\n  }\n)\n```\n\n### createCircuitBreaker\n\nclosed → open → half-open 상태머신. cascade failure 차단.\n\n```typescript\nconst cb = createCircuitBreaker({\n  failureThreshold: 5,    // open 전환 실패 횟수\n  resetTimeoutMs: 10000,  // open → half-open 대기 ms\n  halfOpenMaxCalls: 1,    // half-open 허용 호출 수 (기본값: 1)\n})\n\ntry {\n  const result = await cb.execute(() => callExternalService())\n} catch (err) {\n  if (err instanceof CircuitOpenError) {\n    // 회로 열림 — 빠른 실패\n  }\n}\n\ncb.getState() // 'closed' | 'open' | 'half-open'\ncb.reset()    // 강제 CLOSED 복귀\n```\n\n### withFallback\n\nprimary 실패 시 fallback chain 순차 실행.\n\n```typescript\nconst result = await withFallback(\n  () => callPrimaryAPI(),       // 1순위\n  () => callSecondaryAPI(),     // 2순위 (primary 실패 시)\n  () => Promise.resolve(cache), // 최종 fallback\n)\n```\n\n### 조합 패턴\n\n```typescript\nconst cb = createCircuitBreaker({ failureThreshold: 3, resetTimeoutMs: 5000 })\n\nconst result = await withFallback(\n  () => withRetry(() => cb.execute(() => callAI()), { maxAttempts: 3 }),\n  () => Promise.resolve(cachedResponse),\n)\n```\n\n---\n\n## 버전 정보\n\n| 버전 | 변경사항 |\n|------|---------|\n| 0.13.0 | production middleware 3종(security-headers·timeout·body-limit) + `./middleware`·`./oauth` subpath exports |\n| 0.12.0 | OpenFeature 어댑터 — `HarnessKitProvider` (`@openfeature/server-sdk` optional peerDep) |\n| 0.5.0 | OAuth 2.0 provider (Google/GitHub preset + PKCE S256 + state CSRF) |\n| 0.1.0 | 초기 릴리스 — Phase 20 AX BD MSA 재조정 |\n","readmeFilename":"README.md"}