@kitschpatrol/shared-config 8.11.0 → 8.12.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/cli.js +1 -1
- package/package.json +11 -11
- package/readme.md +20 -16
- package/skills/ksc-update/SKILL.md +762 -0
- package/skills/ksc-update/references/verification.md +168 -0
package/bin/cli.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{cosmiconfig as e}from"cosmiconfig";import{TypeScriptLoader as t}from"cosmiconfig-typescript-loader";import{execa as n}from"execa";import r from"fs-extra";import i,{constants as a}from"node:fs";import{availableParallelism as o}from"node:os";import s from"node:path";import{PassThrough as c,Transform as l}from"node:stream";import{packageUp as u,packageUpSync as d}from"package-up";import f from"yargs";import{hideBin as p}from"yargs/helpers";import m from"picocolors";import{stripVTControlCharacters as h}from"node:util";import g from"@pinojs/json-colorizer";import ee from"decircular";import te from"deepmerge";import _ from"json-stringify-pretty-compact";import{findWorkspaces as v,findWorkspacesRoot as ne}from"find-workspaces";import y,{access as re}from"node:fs/promises";import{getDefaultConfigLoader as b,resolveConfigFileImports as ie}from"cspell-lib";import{fileURLToPath as x}from"node:url";import{createReaderWriter as ae}from"cspell-config-lib";import{loadFile as oe,writeFile as se}from"magicast";import{getDefaultExportOptions as ce}from"magicast/helpers";import{lint as le}from"cspell";import"deepmerge-ts";import{loadConfig as ue}from"mdat";import{globby as de}from"globby";import S,{gt as fe,minVersion as pe}from"semver";import{readWantedLockfile as me}from"@pnpm/lockfile.fs";import he from"stylelint";var ge=`8.
|
|
2
|
+
import{cosmiconfig as e}from"cosmiconfig";import{TypeScriptLoader as t}from"cosmiconfig-typescript-loader";import{execa as n}from"execa";import r from"fs-extra";import i,{constants as a}from"node:fs";import{availableParallelism as o}from"node:os";import s from"node:path";import{PassThrough as c,Transform as l}from"node:stream";import{packageUp as u,packageUpSync as d}from"package-up";import f from"yargs";import{hideBin as p}from"yargs/helpers";import m from"picocolors";import{stripVTControlCharacters as h}from"node:util";import g from"@pinojs/json-colorizer";import ee from"decircular";import te from"deepmerge";import _ from"json-stringify-pretty-compact";import{findWorkspaces as v,findWorkspacesRoot as ne}from"find-workspaces";import y,{access as re}from"node:fs/promises";import{getDefaultConfigLoader as b,resolveConfigFileImports as ie}from"cspell-lib";import{fileURLToPath as x}from"node:url";import{createReaderWriter as ae}from"cspell-config-lib";import{loadFile as oe,writeFile as se}from"magicast";import{getDefaultExportOptions as ce}from"magicast/helpers";import{lint as le}from"cspell";import"deepmerge-ts";import{loadConfig as ue}from"mdat";import{globby as de}from"globby";import S,{gt as fe,minVersion as pe}from"semver";import{readWantedLockfile as me}from"@pnpm/lockfile.fs";import he from"stylelint";var ge=`8.12.1`;const _e=[`json`,`machine`,`native`],ve=_e;function ye(e){return typeof e==`string`&&_e.includes(e)}function C(){let e=process.env.KSC_FORMAT;return ye(e)?e:`native`}function be(){let{argv:e}=process;for(let[t,n]of e.entries()){let r;n===`--format`?r=e[t+1]:n.startsWith(`--format=`)&&(r=n.slice(9)),ye(r)&&(process.env.KSC_FORMAT=r)}}function xe(){let{env:e}=process;return e.NO_COLOR!==void 0&&e.NO_COLOR!==``?!1:e.FORCE_COLOR===void 0?e.TERM!==`dumb`&&void 0:e.FORCE_COLOR!==`0`&&e.FORCE_COLOR!==`false`}function w(e,{ciColor:t=!0}={}){return xe()??(e.isTTY===!0||t&&!!process.env.CI)}function Se(){return C()===`json`?process.stderr:process.stdout}function T(){return C()===`machine`?m.createColors(!1):m.createColors(w(Se()))}function Ce(e){if(!e)return{FORCE_COLOR:`0`,NO_COLOR:`1`};let t=process.env.FORCE_COLOR;return t===void 0||t===``?{FORCE_COLOR:`1`}:{}}function E(e,t){let n=s.isAbsolute(e)?e:s.resolve(t,e),r=s.relative(process.cwd(),n);return r===``?`.`:r}const we=/\s*\n\s*/gv,Te=/\r?\n/v;function Ee(e){let t=e.message.replaceAll(we,` `),n=e.rule===void 0?e.tool:`${e.tool}/${e.rule}`;return e.file===void 0?`${e.severity}: ${t} [${n}]`:`${e.file}:${e.line??1}:${e.column??1}: ${e.severity}: ${t} [${n}]`}function De(e,t){let n=(e.file??``).localeCompare(t.file??``);if(n!==0)return n;let r=(e.line??0)-(t.line??0);return r===0?(e.column??0)-(t.column??0):r}function Oe(e,t){let n=t.toSorted(De);return{cwd:process.cwd(),diagnostics:n,success:e.every(e=>e.exitCode===0),summary:{errors:n.filter(e=>e.severity===`error`).length,infos:n.filter(e=>e.severity===`info`).length,warnings:n.filter(e=>e.severity===`warning`).length},tools:e,version:1}}function D(e){return h(e).split(Te).map(e=>e.trimEnd()).filter(e=>e.trim().length>0)}function ke(e){return e instanceof Error&&`exitCode`in e&&typeof e.exitCode==`number`}function O(e){return w(process.stdout,{ciColor:!1})?je(e):Ae(e)}function Ae(e){return _(ee(e),{indent:2,replacer(e,t){return typeof t==`function`?t.name:t}})}function je(e){return g(Ae(e),{colors:{BRACKET:`gray`}})}const Me=(e,t,n)=>{let r=[...e];for(let[i,a]of t.entries())r[i]===void 0?r[i]=n.cloneUnlessOtherwiseSpecified(a,n):n.isMergeableObject(a)?r[i]=k(e[i],a,n):e.includes(a)||r.push(a);return r};function k(e,t,n={arrayMerge:Me}){return te(e,t,n)}function Ne(e){let{tasks:t}=e;return Array.isArray(t)?t:[]}function Pe(e,t){let n=[...Ne(e)];for(let e of Ne(t)){let t=n.findIndex(t=>t.label!==void 0&&t.label===e.label);t===-1?n.push(e):n[t]=e}return{...e,...t,tasks:n}}function Fe(e,t){return e.startsWith(t+s.sep)}function A(){let e=j(),t=/* @__PURE__ */ new Set([e]),n=v();if(n!==null)for(let r of n){let n=s.resolve(r.location);Fe(n,e)&&t.add(n)}return[...t]}function j(){let e=d();if(e===void 0)throw Error(`No package.json found.`);return s.dirname(e)}function Ie(){return ne()!==null}function M(){let e=ne();return e===null?j():s.resolve(e.location)}function Le(e){let t=s.join(M(),e);return r.existsSync(t)?t:void 0}function Re(e){if(e===`workspace-root`)return M();if(e===`package-dir`)return j();if(typeof e==`string`){if(!r.pathExistsSync(e))throw Error(`Custom cwd directory does not exist: ${e}`);return e}return process.cwd()}async function ze(e,t){try{let{default:n}=await import(`prettier`),r=await n.resolveConfig(e),i=await n.format(t,{filepath:e,...r});await y.writeFile(e,i,`utf8`)}catch{console.warn(`Skipped formatting ${e} since Prettier is not installed.`)}}async function N(e){try{await ze(e,await y.readFile(e,`utf8`))}catch{}}const Be=/\r?\n/v;function Ve(e){let t=``,n=t=>t.filter(t=>{let n=h(t);return n.trim()!==``&&!e(n)}).map(e=>`${e}\n`).join(``);return new l({flush(e){e(void 0,n([t]))},transform(e,r,i){let a=(t+e.toString()).split(Be);t=a.pop()??``,i(void 0,n(a))}})}function P(e,t){let n=C()===`native`?e:void 0,r=n===void 0||n===``?``:t===void 0?n:T()[t](n),i=``,a=e=>e.filter(e=>e.trim().length>0).map(e=>r===``?`${e}\n`:`${r} ${e}\n`).join(``);return new l({flush(e){e(void 0,a([i]))},transform(e,t,n){let r=(i+e.toString()).split(Be);i=r.pop()??``,n(void 0,a(r))}})}async function He(e){let t=[];return new Promise((n,r)=>{e.on(`data`,e=>{t.push(e)}),e.on(`error`,e=>{r(e)}),e.on(`end`,()=>{n(Buffer.concat(t).toString(`utf8`))})})}function Ue(e){return e.replaceAll(/[A-Z\u{C0}-\u{D6}\u{D8}-\u{DE}]/gv,e=>`-`+e.toLowerCase())}function F(e,t){return t===1?e:e+`s`}async function I(e){return typeof e==`function`?e():e}async function We(e,t,n,r,i){let a;if(r.logPrefix===void 0)a=e;else{let t=P(r.logPrefix,r.logColor);t.pipe(e),a=t}i&&a.write(T().bold(`Running: "${r.name}() with Positional arguments: ${String(t)} and Option flags: ${String(n)}"`)+`
|
|
3
3
|
`);try{return await r.execute(a,t,n)}catch(e){return console.error(String(e)),1}}function Ge(e,t,n,r,i){return[...e.subcommands??[],...e.receiveOptionFlags?n:[],...r??[],...i&&e.cache!==void 0?[...e.cache.flags,`--cache-location`,s.join(M(),`node_modules`,`.cache`,`ksc`,e.cache.name)]:[],...e.receivePositionalArguments?t:[],...e.positionalArguments??[]]}async function Ke(e,t,r,i,a,o){let s=1,l;if(i.logPrefix===void 0)l=e;else{let t=P(i.logPrefix,i.logColor);t.pipe(e),l=t}let u=Ge(i,t,r,i.optionFlags,a),d=Re(i.cwdOverride);o&&l.write(`Running: "${i.name} ${u.join(` `)}"\n`);let f=i.prettyJsonOutput?new c:void 0,p=f??l,m=Ce(C()===`native`&&i.prettyJsonOutput!==!0&&w(Se()));try{let e=n(i.name,u,{cwd:d,env:m,preferLocal:!0,reject:!1,stdin:`inherit`});if(i.outputFilter){let t=Ve(i.outputFilter),n=Ve(i.outputFilter);e.stdout.pipe(t).pipe(p,{end:!1}),e.stderr.pipe(n).pipe(p,{end:!1})}else e.stdout.pipe(p,{end:!1}),e.stderr.pipe(p,{end:!1});let t=await e;if(f!==void 0){f.end();let e=await He(f),t=O(JSON.parse(e)).split(`
|
|
4
4
|
`);for(let e of t)l.write(`${e}\n`)}s=t.exitCode??1}catch(e){console.error(`${i.name} failed with error:`),console.error(e),ke(e)&&(s=typeof e.exitCode==`number`?e.exitCode:1)}return s}function L(e){return`execute`in e}function R(e){return`kind`in e}async function qe(e,t,r,i,a){let o=Ge(r,e,t,r.collect?.optionFlags??r.optionFlags,i),s=Re(r.cwdOverride);try{let e=n(r.name,o,{cwd:s,env:Ce(!1),preferLocal:!0,reject:!1,stdin:`inherit`});a!==void 0&&e.stderr.pipe(a,{end:!1});let t=await e;return{cwd:s,exitCode:t.exitCode??1,stderr:t.stderr,stdout:t.stdout}}catch(e){return console.error(`${r.name} failed with error:`),console.error(e),{cwd:s,exitCode:ke(e)&&typeof e.exitCode==`number`?e.exitCode:1,stderr:``,stdout:``}}}function Je(e){return typeof e==`object`&&!!e&&`version`in e&&e.version===1&&`diagnostics`in e&&Array.isArray(e.diagnostics)&&`success`in e&&typeof e.success==`boolean`&&`tools`in e&&Array.isArray(e.tools)}function Ye(e,t){return e===0&&t.some(({severity:e})=>e===`error`)?1:e}async function Xe(e,t,n,r,i){let a=Date.now(),o=()=>Date.now()-a;if(L(r)){if(r.collect!==void 0)try{let e=await r.collect(t,n),i=Ye(e.exitCode,e.diagnostics);return{diagnostics:e.diagnostics,exitCode:i,tools:[{durationMs:o(),exitCode:i,name:r.name,unparsed:e.unparsed}]}}catch(e){return{diagnostics:[],exitCode:1,tools:[{durationMs:o(),exitCode:1,name:r.name,unparsed:[String(e)]}]}}let e=``,i=new c;i.on(`data`,t=>{e+=t.toString()});let a;try{a=await r.execute(i,t,n)}catch(t){e+=`\n${String(t)}`,a=1}return{diagnostics:[],exitCode:a,tools:[{durationMs:o(),exitCode:a,name:r.name,unparsed:D(e)}]}}if(R(r))throw Error(`Command groups must be expanded before collection`);if(r.outputFormatAware){let a=await qe(t,n,r,i,e);try{let e=JSON.parse(a.stdout);if(!Je(e))throw Error(`Child output is not a lint report`);return{diagnostics:e.diagnostics,exitCode:a.exitCode===0&&e.success&&e.diagnostics.every(({severity:e})=>e!==`error`)?0:1,tools:e.tools}}catch{return{diagnostics:[],exitCode:a.exitCode,tools:[{durationMs:o(),exitCode:a.exitCode,name:r.name,unparsed:D(a.stdout)}]}}}let s=await qe(t,n,r,i);if(r.collect!==void 0)try{let{diagnostics:e,unparsed:t}=r.collect.parse(s),n=Ye(s.exitCode,e);return{diagnostics:e,exitCode:n,tools:[{durationMs:o(),exitCode:n,name:r.name,unparsed:t}]}}catch(e){return{diagnostics:[],exitCode:s.exitCode,tools:[{durationMs:o(),exitCode:s.exitCode,name:r.name,unparsed:[...D(s.stdout),...D(s.stderr),String(e)]}]}}return{diagnostics:[],exitCode:s.exitCode,tools:[{durationMs:o(),exitCode:s.exitCode,name:r.name,unparsed:[...D(s.stdout),...D(s.stderr)]}]}}const Ze=/^ksc-/v;function z(e){return e.replace(Ze,``)}function B(e){return e===void 0||e.length===0?[]:e.flatMap(e=>e.split(`,`)).map(e=>z(e.trim()))}function V(e){return e.option(`skip`,{array:!0,describe:`Tool names to skip (with or without "ksc-" prefix).`,type:`string`})}function Qe(e){return e.option(`format`,{choices:ve,default:`native`,describe:`Output format: "native" streams each tool's own output, "machine" prints one parseable line per issue for editor problem matchers, "json" prints an aggregate report.`})}function $e(e){return e.option(`cache`,{boolean:!0,default:!0,describe:`Use tool-native caches stored below node_modules/.cache/ksc at the workspace root. Disable with --no-cache.`})}function et(e,t){return t===`native`||t===`machine`&&!L(e)&&!R(e)&&e.outputFormatAware===!0}function tt(e,t){for(let n of t.diagnostics)e.write(`${Ee(n)}\n`);for(let n of t.tools)for(let t of n.unparsed)e.write(`${t}\n`)}function nt(){return Math.min(4,o())}var rt=class{concurrency;active=0;queue=[];constructor(e){this.concurrency=e}async run(e){this.active>=this.concurrency&&await new Promise(e=>{this.queue.push(e)}),this.active+=1;try{return await e()}finally{--this.active,this.queue.shift()?.()}}};async function it(e){let t=e.cache??!1,n=e.concurrency??nt();if(!Number.isInteger(n)||n<1)throw Error(`Concurrency must be a positive integer.`);return t&&await r.ensureDir(s.join(M(),`node_modules`,`.cache`,`ksc`)),{cache:t,scheduler:new rt(n)}}const at=/[*?\[\]\{\}]/v;function H(e,t){return t===void 0?e:e.filter(e=>at.test(e)||i.statSync(e,{throwIfNoEntry:!1})?.isDirectory()===!0||t.includes(s.extname(e).slice(1).toLowerCase()))}function ot(e,t){return e.length===0||H(e,t).length>0}function st(e,t){return e.positionalArgumentMode===`none`?[]:t.length>0?H(t,e.positionalArgumentExtensions):e.positionalArgumentDefault===void 0?[]:[e.positionalArgumentDefault]}function ct(e,t){if(t.logPrefix===void 0)return e;let n=P(t.logPrefix,t.logColor);return n.pipe(e),n}function lt(e){return R(e)?`${e.name} ${e.subcommand}`:!L(e)&&e.subcommands!==void 0?[e.name,...e.subcommands].join(` `):e.name}async function ut(e,t){if(R(e)){let n=ct(t.logStream,e);try{let r=await I(e.commands);if(t.nativeVerbose){let n=[e.subcommand,...t.positionalArguments].join(` `),i=e.showResolvedCommands?` (${r.map(e=>lt(e)).join(`, `)})`:``;t.logStream.write(`Running: "${e.name} ${n}"${i}\n`)}let i=await dt(r,{...t,logStream:n,nativeVerbose:e.verbose,positionalArguments:st(e,t.positionalArguments)},e.parallel===!0);return{diagnostics:i.flatMap(({diagnostics:e})=>e),exitCode:+!i.every(({exitCode:e})=>e===0),machineOutcomes:i.flatMap(({machineOutcomes:e})=>e),name:e.name,tools:i.flatMap(({tools:e})=>e)}}catch(r){let i=String(r),a={durationMs:0,exitCode:1,name:e.name,unparsed:[i]};return t.format===`native`&&n.write(`${i}\n`),{diagnostics:[],exitCode:1,machineOutcomes:[{diagnostics:[],exitCode:1,tools:[a]}],name:e.name,tools:[a]}}}return t.scheduler.run(async()=>{if(et(e,t.format))return{diagnostics:[],exitCode:await(L(e)?We(t.logStream,t.positionalArguments,t.optionFlags,e,t.nativeVerbose):Ke(t.logStream,t.positionalArguments,t.optionFlags,e,t.cache,t.nativeVerbose)),machineOutcomes:[],name:e.name,tools:[]};let n=await Xe(t.logStream,t.positionalArguments,t.optionFlags,e,t.cache);return{diagnostics:n.diagnostics,exitCode:n.exitCode,machineOutcomes:[n],name:e.name,tools:n.tools}})}async function dt(e,t,n){if(!n||t.scheduler.concurrency===1){let n=[];for(let r of e)n.push(await ut(r,t));return n}await Promise.resolve();let r=/* @__PURE__ */ new Map;for(let[t,n]of e.entries()){let e=n.stage??0,i=r.get(e)??[];i.push({command:n,index:t}),r.set(e,i)}let i=Array.from({length:e.length}),a=r.keys().toArray().toSorted((e,t)=>e-t);for(let e of a){let n=r.get(e)??[],a=await Promise.all(n.map(async({command:e,index:n})=>({index:n,outcome:await ut(e,t)})));for(let{index:e,outcome:t}of a)i[e]=t}return i.filter(e=>e!==void 0)}function ft(e,t,n,r,i){let a=[],o=[],s=ot(r,i);for(let e of t){let t=!R(e)||ot(r,e.positionalArgumentExtensions);!s||!t||n.includes(z(e.name))?o.push(e):a.push(e)}if(n.length>0){let r=new Set(o.map(e=>z(e.name))),i=n.filter(e=>!r.has(e));if(i.length>0){let n=t.map(e=>z(e.name)).join(`, `);e.write(`⚠️ ${T().yellow(`Unrecognized --skip ${F(`value`,i.length)}: ${i.join(`, `)}. Available: ${n}`)}\n`)}}return{commandsToRun:a,skippedCommands:o}}async function U(e,t,n,r,i,a,o,s=`native`){let c=typeof s==`string`?{}:s,l=typeof s==`string`?s:c.format??`native`,{commandsToRun:u,skippedCommands:d}=ft(e,r,o??[],t,c.positionalArgumentExtensions),f=l===`native`&&i,{cache:p,scheduler:m}=await it(c),h=await dt(u,{cache:p,format:l,logStream:e,nativeVerbose:f,optionFlags:n,positionalArguments:H(t,c.positionalArgumentExtensions),scheduler:m},c.parallel===!0),g=h.map(({exitCode:e,name:t})=>({exitCode:e,name:t})),ee=h.flatMap(({tools:e})=>e),te=h.flatMap(({diagnostics:e})=>e);if(l===`machine`)for(let t of h)for(let n of t.machineOutcomes)tt(e,n);let _=r.length,v=T();if(l===`native`&&d.length>0){let t=d.map(({name:e})=>e),n=v.bold(`${t.length} / ${_} ${F(`Command`,t.length)} Skipped:`);e.write(`⏭️ ${v.dim(n)} ${v.dim(t.join(`, `))}\n`)}if(l===`native`&&a){let t=g.filter(({exitCode:e})=>e===0).map(({name:e})=>e),n=g.filter(({exitCode:e})=>e!==0).map(({name:e})=>e);if(t.length>0){let n=v.bold(`${t.length} / ${_} ${F(`Command`,t.length)} Succeeded:`);e.write(`✅ ${v.green(n)} ${v.green(t.join(`, `))}\n`)}if(n.length>0){let t=v.bold(`${n.length} / ${_} ${F(`Command`,n.length)} Failed:`);e.write(`❌ ${v.red(t)} ${v.red(n.join(`, `))}\n`)}}return{exitCode:+!g.every(({exitCode:e})=>e===0),report:l===`json`?Oe(ee,te):void 0}}async function pt(e){let t=await u({cwd:e});return t===void 0?void 0:s.join(s.dirname(t),`init`)}async function mt(e,t,n,a){let o=await u();if(o===void 0)throw Error("The `init` command must be used in a directory with a package.json file");let c=await pt(import.meta.dirname);if(c===void 0)return e.write(`Error: Could not locate the package containing this CLI.
|
|
5
5
|
`),1;let l=s.dirname(o),d=(t===`file`||t===`package`)&&n!==void 0&&a!==void 0;try{if(d){let n=Object.keys(a)[0],i=r.readJsonSync(o);if(t===`package`){e.write(`Merging: \nPackage config key "${n}" → "${l}" (Because --location is set to "package")\n`);let t=k(i,a);r.writeJSONSync(o,t,{spaces:` `}),await N(o)}else n!==void 0&&Object.keys(i).includes(n)&&(e.write(`Deleting: \nPackage config key "${n}" in "${l}" (Because --location is set to "file")\n`),delete i[n],r.writeJSONSync(o,i,{spaces:` `}),await N(o))}if(!await r.pathExists(c))return 0;if((await r.readdir(c)).length===0)return e.write(`Source directory "${c}" is empty.\n`),0;e.write(`Adding initial configuration files from:\n"${c}" → "${l}"\n`),await r.copy(c,l,{async filter(a,o){let c=i.statSync(a).isFile(),l=i.existsSync(o);if(c){if(d&&t===`package`&&a.includes(n))return l?(e.write(`Deleting: \n"${a}" → "${o}" (Because --location is set to "package")\n`),r.removeSync(o)):e.write(`Skipping: \n"${a}" → "${o}" (Because --location is set to "package")\n`),!1;if(l&&(o.includes(`.vscode/`)||o.includes(`package.json`))&&s.extname(o)===`.json`){e.write(`Merging: \n"${a}" → "${o}"\n`);let t=r.readJSONSync(a),n=r.readJSONSync(o),i=o.endsWith(`.vscode/tasks.json`)?Pe(n,t):k(n,t);return r.writeJSONSync(o,i,{spaces:` `}),await N(o),!1}return l?(e.write(`Overwriting: \n"${a}" → "${o}"\n`),await N(o),!0):(e.write(`Copying: \n"${a}" → "${o}"\n`),await N(o),!0)}return!0},overwrite:!0})}catch(e){return console.error(String(e)),1}return 0}async function ht(e){let{commands:{fix:t,init:n,lint:r,printConfig:i},description:a,logColor:o,logPrefix:s,name:c,showSummary:l,verbose:u}=e;be();let d=P(s,o);d.pipe(Se());let m=f(p(process.argv)).scriptName(c).usage(`$0 <command>`,a);n!==void 0&&m.command({builder(e){let t=n.locationOptionFlag?e.option(`location`,{choices:[`file`,`package`],default:`file`,describe:`Where to store the configuration.`,type:`string`}):e;return l?V(t):t},command:`init`,describe:n.description??`Initialize by copying starter config files to your project root${n.locationOptionFlag?` or to your package.json file.`:`.`}`,async handler(e){let t=n.locationOptionFlag?e.location:void 0,r=B(e.skip),{exitCode:i}=await U(d,[],t===void 0?[]:[`--location`,t],[{async execute(e,t,r){return mt(e,r.at(1),n.configFile,n.configPackageJson)},name:`copyAndMergeInitFiles`},...n.commands??[]],void 0,void 0,r);process.exitCode=i}}),r!==void 0&&m.command({builder(e){let t=$e(Qe(r.positionalArgumentMode===`none`?e:e.positional(`files`,{array:!0,...r.positionalArgumentDefault!==void 0&&{default:r.positionalArgumentDefault},describe:`Files or glob pattern to lint.`,type:`string`})));return l?V(t):t},command:r.positionalArgumentMode===`none`?`lint`:r.positionalArgumentMode===`optional`?`lint [files..]`:`lint <files..>`,describe:r.description,async handler(e){let t=e.files??[],n=B(e.skip),i=C(),{exitCode:a,report:o}=await U(d,t,[],await I(r.commands),u,l,n,{cache:e.cache,format:i,parallel:r.parallel,positionalArgumentExtensions:r.positionalArgumentExtensions});o!==void 0&&process.stdout.write(`${O(o)}\n`),process.exitCode=a}}),t!==void 0&&m.command({builder(e){let n=$e(Qe(t.positionalArgumentMode===`none`?e:e.positional(`files`,{array:!0,...t.positionalArgumentDefault!==void 0&&{default:t.positionalArgumentDefault},describe:`Files or glob pattern to fix.`,type:`string`})));return l?V(n):n},command:t.positionalArgumentMode===`none`?`fix`:t.positionalArgumentMode===`optional`?`fix [files..]`:`fix <files..>`,describe:t.description,async handler(e){let n=e.files??[],r=B(e.skip),i=C(),{exitCode:a,report:o}=await U(d,n,[],await I(t.commands),void 0,void 0,r,{cache:e.cache,format:i,parallel:t.parallel,positionalArgumentExtensions:t.positionalArgumentExtensions});o!==void 0&&process.stdout.write(`${O(o)}\n`),process.exitCode=a}}),i!==void 0&&m.command({builder(e){let t=i.positionalArgumentMode===`none`?e:e.positional(`file`,{...i.positionalArgumentDefault!==void 0&&{default:i.positionalArgumentDefault},describe:`File or glob pattern to print configuration for.`,type:`string`});return l?V(t):t},command:i.positionalArgumentMode===`none`?`print-config`:i.positionalArgumentMode===`optional`?`print-config [file]`:`print-config <file>`,describe:i.description,async handler(e){let t=e.file??void 0,n=t===void 0?[]:[t],r=B(e.skip),{exitCode:a}=await U(d,n,[],await I(i.commands),u,l,r);process.exitCode=a}}),m.alias(`h`,`help`),m.version(ge),m.alias(`v`,`version`),m.help(),m.wrap(process.stdout.isTTY?Math.min(120,m.terminalWidth()):0),await m.parseAsync()}function W(e){return{async execute(t){let n=await G(e);if(n===void 0)return 1;let{config:r,filepath:i,isEmpty:a}=n;if(t.write(`Found ${e} configuration at "${i}"\n`),a)return t.write(`Configuration is empty.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitschpatrol/shared-config",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.12.1",
|
|
4
4
|
"description": "A collection of shared configurations, linters, and formatting tools for TypeScript projects. All managed as a single dependency. All invoked via a single CLI command.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shared-config",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"skills/*"
|
|
55
55
|
],
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"@kitschpatrol/cspell-config": "8.
|
|
58
|
-
"@kitschpatrol/eslint-config": "8.
|
|
59
|
-
"@kitschpatrol/knip-config": "8.
|
|
60
|
-
"@kitschpatrol/mdat-config": "8.
|
|
61
|
-
"@kitschpatrol/prettier-config": "8.
|
|
62
|
-
"@kitschpatrol/remark-config": "8.
|
|
63
|
-
"@kitschpatrol/repo-config": "8.
|
|
64
|
-
"@kitschpatrol/stylelint-config": "8.
|
|
65
|
-
"@kitschpatrol/typescript-config": "8.
|
|
57
|
+
"@kitschpatrol/cspell-config": "8.12.1",
|
|
58
|
+
"@kitschpatrol/eslint-config": "8.12.1",
|
|
59
|
+
"@kitschpatrol/knip-config": "8.12.1",
|
|
60
|
+
"@kitschpatrol/mdat-config": "8.12.1",
|
|
61
|
+
"@kitschpatrol/prettier-config": "8.12.1",
|
|
62
|
+
"@kitschpatrol/remark-config": "8.12.1",
|
|
63
|
+
"@kitschpatrol/repo-config": "8.12.1",
|
|
64
|
+
"@kitschpatrol/stylelint-config": "8.12.1",
|
|
65
|
+
"@kitschpatrol/typescript-config": "8.12.1",
|
|
66
66
|
"@pinojs/json-colorizer": "^4.0.0",
|
|
67
67
|
"@pnpm/lockfile.fs": "^1100.2.12",
|
|
68
68
|
"cosmiconfig": "^10.0.1",
|
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"globby": "^16.2.4",
|
|
80
80
|
"json-stringify-pretty-compact": "^4.0.0",
|
|
81
81
|
"magicast": "^0.5.5",
|
|
82
|
-
"mdat": "^3.
|
|
82
|
+
"mdat": "^3.6.1",
|
|
83
83
|
"package-up": "^5.0.0",
|
|
84
84
|
"picocolors": "^1.1.1",
|
|
85
85
|
"prettier": "^3.9.9",
|
package/readme.md
CHANGED
|
@@ -190,12 +190,18 @@ pnpm --package=@kitschpatrol/repo-config dlx ksc-repo init && pnpm i && pnpm add
|
|
|
190
190
|
|
|
191
191
|
5. Set up GitHub action credentials (if desired)
|
|
192
192
|
|
|
193
|
-
The GitHub actions included in @kitschpatrol/repo-config require permissions to create releases
|
|
193
|
+
The GitHub actions included in @kitschpatrol/repo-config require permissions to create releases, update your repository metadata, and open dependency update pull requests. You can add these through the GitHub website under the _Settings → Secrets and variables → Actions_ page under the key `PERSONAL_ACCESS_TOKEN`, or with the [GitHub CLI](https://cli.github.com) and a credential manager like [1Password CLI](https://www.1password.dev/cli/get-started):
|
|
194
194
|
|
|
195
195
|
```sh
|
|
196
196
|
gh secret set PERSONAL_ACCESS_TOKEN --app actions --body $(op read 'op://Personal/GitHub Mika/PERSONAL_ACCESS_TOKEN')
|
|
197
197
|
```
|
|
198
198
|
|
|
199
|
+
The weekly dependency update workflow also needs a `CLAUDE_CODE_OAUTH_TOKEN` secret, a Claude subscription token generated with `claude setup-token`:
|
|
200
|
+
|
|
201
|
+
```sh
|
|
202
|
+
gh secret set CLAUDE_CODE_OAUTH_TOKEN --app actions --body $(op read 'op://Personal/Claude Code OAuth Token/credential')
|
|
203
|
+
```
|
|
204
|
+
|
|
199
205
|
See the [@kitschpatrol/repo-config readme](https://github.com/kitschpatrol/shared-config/blob/main/packages/repo-config/readme.md#github-configuration) for more details.
|
|
200
206
|
|
|
201
207
|
## Usage
|
|
@@ -216,7 +222,7 @@ pnpm run fix
|
|
|
216
222
|
|
|
217
223
|
### CLI
|
|
218
224
|
|
|
219
|
-
<!-- cli-help({ command: 'ksc' }) -->
|
|
225
|
+
<!-- cli-help({ command: 'ksc', heading: false, headingLevel: 3 }) -->
|
|
220
226
|
|
|
221
227
|
#### Command: `ksc`
|
|
222
228
|
|
|
@@ -374,31 +380,29 @@ The tasks share a problem matcher owner, so the panel reflects the most recent r
|
|
|
374
380
|
|
|
375
381
|
## Agent skills
|
|
376
382
|
|
|
377
|
-
This project
|
|
378
|
-
|
|
379
|
-
- **[`ksc`](skills/ksc/SKILL.md)**: Code conventions and lint workflow for projects that use @kitschpatrol/shared-config and its ksc CLI, which wraps ESLint, Prettier, TypeScript, Stylelint, CSpell, Knip, remark, and mdat. Use when writing or editing TypeScript, JavaScript, CSS, Markdown, JSON, or YAML in a project that depends on @kitschpatrol/shared-config, when running or fixing `ksc lint` / `ksc fix` failures, when writing eslint-disable comments, or when editing the tool config files it manages.
|
|
383
|
+
This project bundles 2 [Agent Skills](https://agentskills.io) in its published package to help coding agents work with @kitschpatrol/shared-config.
|
|
380
384
|
|
|
381
|
-
|
|
385
|
+
To sync the skills into your project, run Vercel's [skills CLI](https://github.com/vercel-labs/skills) from your project root:
|
|
382
386
|
|
|
383
|
-
|
|
387
|
+
```sh
|
|
388
|
+
npx skills experimental_sync
|
|
389
|
+
```
|
|
384
390
|
|
|
385
|
-
|
|
391
|
+
Or install globally:
|
|
386
392
|
|
|
387
393
|
```sh
|
|
388
|
-
npx skills
|
|
394
|
+
npx skills add kitschpatrol/shared-config --global
|
|
389
395
|
```
|
|
390
396
|
|
|
391
|
-
|
|
397
|
+
Included skills:
|
|
392
398
|
|
|
393
|
-
###
|
|
399
|
+
### Skill: [`ksc`](skills/ksc/SKILL.md)
|
|
394
400
|
|
|
395
|
-
|
|
401
|
+
Code conventions and lint workflow for projects that use @kitschpatrol/shared-config and its ksc CLI, which wraps ESLint, Prettier, TypeScript, Stylelint, CSpell, Knip, remark, and mdat.
|
|
396
402
|
|
|
397
|
-
|
|
398
|
-
npx skills add kitschpatrol/shared-config
|
|
399
|
-
```
|
|
403
|
+
### Skill: [`ksc-update`](skills/ksc-update/SKILL.md)
|
|
400
404
|
|
|
401
|
-
|
|
405
|
+
Update dependencies in kitschpatrol shared config pnpm repositories and monorepos with compatible changes, explicitly approved breaking changes, or breaking changes for zero-versioned projects.
|
|
402
406
|
|
|
403
407
|
<!-- /skills -->
|
|
404
408
|
|
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ksc-update
|
|
3
|
+
description: >-
|
|
4
|
+
Update dependencies in kitschpatrol shared config pnpm repositories and
|
|
5
|
+
monorepos with compatible changes, explicitly approved breaking changes, or
|
|
6
|
+
breaking changes for zero-versioned projects. Use when asked to update,
|
|
7
|
+
upgrade, refresh, or audit repository dependencies or GitHub Actions. Applies
|
|
8
|
+
a 24-hour release maturity policy with exceptions for kitschpatrol's packages,
|
|
9
|
+
reviews fresh-install consumer compatibility, validates known consumers,
|
|
10
|
+
minimizes configuration overrides, and recommends a release level.
|
|
11
|
+
Does not commit or release.
|
|
12
|
+
compatibility: Requires Node.js, pnpm/pnpx, curl, jq, git, Bash, and access to npm, GitHub, and upstream release documentation.
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# Update dependencies
|
|
16
|
+
|
|
17
|
+
Accept dependency and GitHub Actions updates under the version policy below.
|
|
18
|
+
For projects at 1.0.0 or later, preserve existing contracts: public APIs, CLI
|
|
19
|
+
behavior, outputs, defaults, supported consumer environments, and peer
|
|
20
|
+
compatibility, unless the user explicitly approves a reviewed breaking change.
|
|
21
|
+
Make the migrations accepted updates require. Do not commit, push, tag, publish,
|
|
22
|
+
or release.
|
|
23
|
+
|
|
24
|
+
## Execution mode
|
|
25
|
+
|
|
26
|
+
Determine the mode before Setup and record it with the reason. Use unattended
|
|
27
|
+
mode when explicitly requested, when `GITHUB_ACTIONS=true` or `CI=true`, or when
|
|
28
|
+
the host cannot receive user replies. Otherwise use interactive mode; a missing
|
|
29
|
+
TTY alone does not establish that an agent session is unattended.
|
|
30
|
+
|
|
31
|
+
In unattended mode, never ask questions, wait for answers, or invoke a command
|
|
32
|
+
that requires an interactive response. Existing explicit approvals still apply;
|
|
33
|
+
silence and CI execution do not grant new approval. This mode overrides every
|
|
34
|
+
instruction below to ask, confirm, or await a decision:
|
|
35
|
+
|
|
36
|
+
- **Projects at 1.0.0 or later:** apply compatible changes and any reviewed
|
|
37
|
+
breaks already explicitly authorized. Defer other breaking candidates and
|
|
38
|
+
report them for a later decision. Judge consumer impact, not dependency
|
|
39
|
+
version labels; a compatible dependency major remains eligible.
|
|
40
|
+
- **Zero-versioned projects:** retain the existing `0.x` allowance in unattended
|
|
41
|
+
runs. Apply reviewed breaking changes, complete their migrations, validate
|
|
42
|
+
the new contracts, and normally recommend a minor release. Do not defer them
|
|
43
|
+
merely because no person is present to approve the break. Other requirements,
|
|
44
|
+
including explicit approval for runtime-range restrictions and Node-types
|
|
45
|
+
changes, still apply.
|
|
46
|
+
- **Other decisions and missing prerequisites:** without prior authorization,
|
|
47
|
+
leave approval-dependent changes unapplied, including range restrictions.
|
|
48
|
+
Scoped peer-warning suppressions with verified compatibility (step 5) are not
|
|
49
|
+
approval-dependent: apply them and report them. Report missing consumer
|
|
50
|
+
information, credentials, permissions, or validation resources instead of
|
|
51
|
+
requesting them. Continue independent work; if a required prerequisite
|
|
52
|
+
prevents progress, report the affected work as blocked or incomplete without
|
|
53
|
+
waiting or bypassing it.
|
|
54
|
+
|
|
55
|
+
Review and validation remain required in both modes. Defer candidates whose
|
|
56
|
+
impact cannot be established or whose migration cannot be validated. If safe
|
|
57
|
+
updates cannot be isolated without an unapproved change, leave those updates
|
|
58
|
+
unapplied and explain the blocker. Report incompatible releases already allowed
|
|
59
|
+
by published runtime ranges as existing consumer exposure; an older lockfile
|
|
60
|
+
does not resolve that exposure.
|
|
61
|
+
|
|
62
|
+
Produce changes and a report suitable for later PR automation. This skill does
|
|
63
|
+
not create workflows or pull requests, or split work into multiple PRs. If
|
|
64
|
+
nothing can be applied, produce the report without manufacturing a change.
|
|
65
|
+
|
|
66
|
+
## Policy
|
|
67
|
+
|
|
68
|
+
- **Compatible by default at 1.0.0 or later:** assess the effect on this repository
|
|
69
|
+
and its consumers, not just the dependency's version label. Minor and patch
|
|
70
|
+
updates can break compatibility; a dependency major can still be compatible.
|
|
71
|
+
Present useful breaking candidates with their impact, migration, validation,
|
|
72
|
+
and release implications before asking for approval; apply only within the
|
|
73
|
+
approved scope. Existing explicit approval remains valid. Defer unapproved
|
|
74
|
+
breaks and updates whose impact cannot be established. This includes authored
|
|
75
|
+
packages, production transitives, and workflow actions.
|
|
76
|
+
- **Zero-versioned projects:** if the project's current release version has
|
|
77
|
+
major version zero (`0.x.y`), any breaking change within this update workflow
|
|
78
|
+
is permitted in both interactive and unattended modes, including changes to
|
|
79
|
+
public contracts and consumer Node.js requirements. This exception overrides
|
|
80
|
+
compatibility-preservation requirements below. Use the project's version, not
|
|
81
|
+
the dependency's version; in monorepos,
|
|
82
|
+
assess each affected published package or shared release version, not just a
|
|
83
|
+
private workspace root. Review and report breaking changes, make required
|
|
84
|
+
migrations, and validate the resulting behavior. All other policies still
|
|
85
|
+
apply.
|
|
86
|
+
- **Release maturity:** third-party releases must be at least 24 hours old
|
|
87
|
+
(`--maturity-period 1` for Taze, `--min-age 1` for Actions Up, pnpm
|
|
88
|
+
`minimumReleaseAge` of 1440 minutes). The period governs which releases a run
|
|
89
|
+
may introduce; it never requires downgrading a version that was locked before
|
|
90
|
+
the run. Never disable installation safeguards to make an update succeed.
|
|
91
|
+
- **Authored packages:** packages owned by `kitschpatrol` (discovered at runtime
|
|
92
|
+
in Setup) are exempt from maturity and target their latest stable release,
|
|
93
|
+
including majors. pnpm enforces `minimumReleaseAge` silently by selecting the
|
|
94
|
+
newest mature version, so the exemption is only real while a
|
|
95
|
+
`minimumReleaseAgeExclude` entry for the young release exists: add it when
|
|
96
|
+
needed (step 5) and keep it through cleanup (step 6). The maturity period
|
|
97
|
+
must never hold an authored package back or downgrade it. The exemption does
|
|
98
|
+
not bypass security or compatibility review.
|
|
99
|
+
- **Consumer ranges:** review the newest published version allowed by each
|
|
100
|
+
runtime dependency's declared range, as well as the lockfile. A fresh consumer
|
|
101
|
+
install can resolve that version even when CI locks an older one. Holding
|
|
102
|
+
back an in-range release requires a genuinely restrictive published range
|
|
103
|
+
(for example, `~1.8.0` instead of `^1.8.0` to exclude `1.9.0`), not just an
|
|
104
|
+
older lockfile entry. Obtain explicit approval before narrowing a runtime
|
|
105
|
+
range to hold a release back, even for a zero-versioned project; explain the
|
|
106
|
+
consumer effect. Do not use local overrides as a substitute for that decision.
|
|
107
|
+
- **TypeScript:** select a compiler within the peer range of the accepted
|
|
108
|
+
`@kitschpatrol/typescript-config` version, intersected with other required
|
|
109
|
+
peers. Do not hard-code a compiler version. Offer the migration when an
|
|
110
|
+
authored major requires a newer compiler; see step 3. If that config is not
|
|
111
|
+
used, follow the project's compiler policy and peer requirements.
|
|
112
|
+
- **Node types:** preserve `@types/node` declarations, including catalogs and
|
|
113
|
+
overrides, unless a reviewed migration has explicit authorization to change
|
|
114
|
+
them. New compiler libraries do not establish runtime API availability.
|
|
115
|
+
- **Package manager:** only Setup manages `packageManager`. Exclude `pnpm`
|
|
116
|
+
from Taze discovery and writes.
|
|
117
|
+
- **No weakening:** do not relax tests, lint rules, type checking, or security
|
|
118
|
+
settings to get a pass; do not blindly accept changed snapshots or generated
|
|
119
|
+
output. Compatibility-preserving lint disables described in step 5 are an
|
|
120
|
+
exception.
|
|
121
|
+
- **No new noise:** every check is compared against the Setup baseline. A new
|
|
122
|
+
warning, deprecation, or peer issue is a finding to trace and report, not
|
|
123
|
+
background noise. Report compatibility-preserving lint disables and scoped
|
|
124
|
+
peer-warning entries from step 5; ask before silencing other findings.
|
|
125
|
+
|
|
126
|
+
## Environment
|
|
127
|
+
|
|
128
|
+
Shell variables do not persist between commands. Save anything later steps need
|
|
129
|
+
under `node_modules/.cache/ksc-update/` and pass values literally.
|
|
130
|
+
|
|
131
|
+
Plan network access for `registry.npmjs.org`, `raw.githubusercontent.com`,
|
|
132
|
+
`api.github.com`, and `nodejs.org`; identify any additional upstream hosts as
|
|
133
|
+
needed. Prefer registry metadata and raw changelog files over GitHub API calls
|
|
134
|
+
for package reviews. Cache responses and share them between reviewers.
|
|
135
|
+
|
|
136
|
+
Under Claude Code's sandbox:
|
|
137
|
+
|
|
138
|
+
- **pnpm store:** pnpm 12 cannot take its store lock inside the sandbox
|
|
139
|
+
(`ERR_PNPM_STORE_DIR_OPEN_OPERATION_LOCK`), even with writable store, cache,
|
|
140
|
+
and temp directories. `pnpm install`, `pnpm update`, `pnpm dedupe`,
|
|
141
|
+
`pnpx taze`, and `pnpx actions-up` must be listed in the user's
|
|
142
|
+
`sandbox.excludedCommands` and `permissions.allow`. If the Setup install fails
|
|
143
|
+
this way, stop the affected work and ask the user to add them in interactive
|
|
144
|
+
mode; in unattended mode, report the missing prerequisite. Do not route around
|
|
145
|
+
it. Run these commands bare (no pipes, redirects, or command substitution) so the
|
|
146
|
+
exclusions match. Run each as its own invocation, without `&&` or `||`.
|
|
147
|
+
Save captured tool output to log files in a separate operation.
|
|
148
|
+
- **Proxy:** Node-based tools ignore the proxy variables. If one fails with
|
|
149
|
+
`ENOTFOUND`, rerun it with `NODE_USE_ENV_PROXY=1`.
|
|
150
|
+
- **Sockets:** the `tsx` CLI cannot open its IPC socket. Run the same script
|
|
151
|
+
with `node --import tsx <script>` and report the substitution.
|
|
152
|
+
- **GitHub API:** without `GITHUB_TOKEN`, GitHub allows 60 requests per hour.
|
|
153
|
+
One Actions Up run plus a few release-note lookups can exhaust it. The sandbox
|
|
154
|
+
cannot read `gh` credentials, so budget lookups or ask for a token in
|
|
155
|
+
interactive mode. In unattended mode, report any review blocked by missing
|
|
156
|
+
credentials or exhausted API limits.
|
|
157
|
+
- **JSON output:** pnpm can emit `[WARN]` lines on stdout. Sync `packageManager`
|
|
158
|
+
before parsing pnpm JSON; if warnings remain, save them separately, remove
|
|
159
|
+
only lines beginning with `[WARN]`, and parse the rest strictly. Do not hide
|
|
160
|
+
malformed output or other diagnostics.
|
|
161
|
+
- **Supply-chain settings:** an unreadable global config or `~/.npmrc` can make
|
|
162
|
+
a sandboxed config query return `undefined`; this does not mean the policy is
|
|
163
|
+
disabled. Read accessible workspace settings and record pnpm's successful
|
|
164
|
+
`Lockfile passes supply-chain policies` message from the actual install.
|
|
165
|
+
That message confirms the active checks passed, not the numeric release-age
|
|
166
|
+
threshold. Verify the required 1440 minutes from accessible configuration or
|
|
167
|
+
release timestamps when the effective setting cannot be read. Do not disable
|
|
168
|
+
safeguards or access private config through a workaround.
|
|
169
|
+
|
|
170
|
+
## Setup
|
|
171
|
+
|
|
172
|
+
1. Read repository instructions. Note uncommitted changes in the working tree
|
|
173
|
+
and preserve them. If files change during the run that you did not touch,
|
|
174
|
+
leave them alone and report them.
|
|
175
|
+
Read the `Known consumers` section in the project's `CLAUDE.md` or `AGENTS.md`.
|
|
176
|
+
Maintain a short section in the existing instruction file using confirmed
|
|
177
|
+
consumer names, local paths, consumed packages, test commands, and CLI
|
|
178
|
+
fixtures. If none are documented, ask for known consumers in interactive
|
|
179
|
+
mode; in unattended mode, report the missing information and use available
|
|
180
|
+
repository evidence. Continue independent review; do not invent consumers or
|
|
181
|
+
treat example paths as real. Report unavailable consumer validation as a gap.
|
|
182
|
+
2. Map the workspace: root, every maintained package root, catalogs, overrides,
|
|
183
|
+
`pnpm-workspace.yaml`, Taze config. Exclude fixtures, generated files, and
|
|
184
|
+
dependency directories. Run each step once per shared-lockfile workspace and
|
|
185
|
+
separately in any independent package root. Check Taze config for anything
|
|
186
|
+
that overrides mode, adds maturity exemptions, or auto-writes/installs.
|
|
187
|
+
Record current project and published package versions to determine where the
|
|
188
|
+
zero-versioned exception applies. Record every existing
|
|
189
|
+
`minimumReleaseAgeExclude` entry with the publish time of that exact version
|
|
190
|
+
(`time` in the registry metadata), so step 6 can tell which entries still
|
|
191
|
+
matter.
|
|
192
|
+
Create the baseline folder and preserve the starting manifests and lockfile
|
|
193
|
+
before the package-manager sync or any other edit.
|
|
194
|
+
3. Sync `packageManager` before installs and parsed pnpm output. Choose the
|
|
195
|
+
newest of three versions, so the pin only ever moves up:
|
|
196
|
+
|
|
197
|
+
- the current `packageManager` pin;
|
|
198
|
+
- the pnpm installed on this host, read from a new empty directory under
|
|
199
|
+
`${TMPDIR:-/tmp}` so the project's stale pin cannot select or download
|
|
200
|
+
another version:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
pnpm --version
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
- the newest stable pnpm release at least 24 hours old, from `versions` and
|
|
207
|
+
`time` in `https://registry.npmjs.org/pnpm` (`dist-tags.latest` when it
|
|
208
|
+
qualifies).
|
|
209
|
+
|
|
210
|
+
A CI runner usually installs the pinned version itself, so the installed
|
|
211
|
+
pnpm alone never moves the pin; the registry lookup is what keeps it
|
|
212
|
+
current. Then edit only the workspace root's `packageManager` directly to
|
|
213
|
+
`pnpm@<chosen-version>`, preserving all other manifest fields. Use the
|
|
214
|
+
editor or a structured JSON edit, not `pnpm pkg set`; use direct edits for
|
|
215
|
+
later specifiers and engines too. pnpm downloads and runs the pinned
|
|
216
|
+
version on demand after the pin changes. If this crosses a pnpm major,
|
|
217
|
+
report it, review the pnpm release notes for lockfile and settings changes,
|
|
218
|
+
and verify the CI installer supports the selected version.
|
|
219
|
+
|
|
220
|
+
4. Install the starting dependency declarations and record a baseline before
|
|
221
|
+
dependency updates. Keep the original files saved in Setup 2 even if
|
|
222
|
+
installation fails.
|
|
223
|
+
|
|
224
|
+
```bash
|
|
225
|
+
pnpm install --frozen-lockfile
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
On `ERR_PNPM_OUTDATED_LOCKFILE` only, run this as a separate bare command:
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
pnpm install --no-frozen-lockfile
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
Save the lockfile diff against the starting copy in the baseline folder and
|
|
235
|
+
report the drift. Treat checks after repair as the repaired baseline, not as
|
|
236
|
+
proof that the original locked tree passed. Other installation failures need
|
|
237
|
+
their own diagnosis; do not generalize this fallback or relax safeguards.
|
|
238
|
+
|
|
239
|
+
Save the output of each install attempt and of `pnpm build`, `pnpm test`, and
|
|
240
|
+
`pnpm lint` to `node_modules/.cache/ksc-update/baseline/`. If the repository
|
|
241
|
+
produces build output (`dist/` or similar), copy it there as well. Use the
|
|
242
|
+
repository's switches to disable nondeterministic build steps (encryption,
|
|
243
|
+
timestamps) and note the exact command and environment; later comparison
|
|
244
|
+
builds must match them. Baseline failures are pre-existing; record them.
|
|
245
|
+
|
|
246
|
+
Record each published package's declared `engines.node`, effective consumer
|
|
247
|
+
Node.js minimum and supported range, and separate development requirement.
|
|
248
|
+
Include published dependency trees and built artifacts, not just the root
|
|
249
|
+
manifest or current CI runtime. If no Node.js support policy is declared,
|
|
250
|
+
establish the existing requirement from the published code and dependencies;
|
|
251
|
+
do not assume the developer's Node.js version is the consumer minimum.
|
|
252
|
+
Save production-tree engine inventories and baseline consumer results using
|
|
253
|
+
[the verification recipes](references/verification.md). Distinguish the
|
|
254
|
+
starting locked tree, the newest versions a fresh consumer install can
|
|
255
|
+
resolve, and the published release with its old dependencies when available.
|
|
256
|
+
|
|
257
|
+
5. Discover authored packages:
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
set -euo pipefail
|
|
261
|
+
mkdir -p node_modules/.cache/ksc-update
|
|
262
|
+
curl -fsS https://registry.npmjs.org/-/user/kitschpatrol/package \
|
|
263
|
+
| jq -er 'keys | join(",") | select(length > 0)' \
|
|
264
|
+
> node_modules/.cache/ksc-update/authored-packages.txt
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
If registry discovery fails or returns empty, stop and report. Intersect the
|
|
268
|
+
result with direct dependency names across the maintained manifests in this
|
|
269
|
+
workspace, including dev, optional, and peer dependencies and dependencies
|
|
270
|
+
using catalogs or npm aliases. Save the intersection separately; an empty
|
|
271
|
+
intersection is valid. `<authored-packages>` below means only those exact
|
|
272
|
+
matching names, comma-separated and passed literally, with no wildcards.
|
|
273
|
+
Omit `--maturity-period-exclude` if the intersection is empty. Keep the full
|
|
274
|
+
ownership list only for evaluating other packages encountered later.
|
|
275
|
+
|
|
276
|
+
## 1. Workflow actions
|
|
277
|
+
|
|
278
|
+
```bash
|
|
279
|
+
pnpx actions-up --yes --min-age 1 --style sha
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Read the whole output. Actions Up can print "All actions are up to date!" after
|
|
283
|
+
checks failed; any `Skipped`, `Failed`, or `Rate Limit` line means the run is
|
|
284
|
+
incomplete. Fix the cause and rerun.
|
|
285
|
+
|
|
286
|
+
Review the diff: major action bumps, runner, input, output, and permission
|
|
287
|
+
changes. Every external GitHub Action and reusable workflow must be pinned to
|
|
288
|
+
its full commit SHA, including `kitschpatrol/*`; retain version comments for
|
|
289
|
+
readability. Do not exclude first-party actions from pinning or allow their
|
|
290
|
+
floating refs in zizmor's `unpinned-uses` policy. Resolve skipped branch/tag
|
|
291
|
+
refs and unchanged versions to verified commits too; if an update is deferred,
|
|
292
|
+
pin the retained version. Check existing zizmor configuration for exceptions
|
|
293
|
+
that contradict this policy. Pin container actions by digest; local `./` actions
|
|
294
|
+
use the checked-out tree. Keep only reviewed changes without disturbing user
|
|
295
|
+
edits. Local tests say nothing about remote workflows.
|
|
296
|
+
|
|
297
|
+
## 2. Discover all candidates (read-only)
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
pnpx taze major -r --force --include-locked \
|
|
301
|
+
--no-github-actions --no-node-version \
|
|
302
|
+
--exclude '@types/node,pnpm' \
|
|
303
|
+
--maturity-period 1 \
|
|
304
|
+
--maturity-period-exclude '<authored-packages>'
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Major mode lists every available update, minor and major. Confirm authored
|
|
308
|
+
packages are offered their latest stable release, not the latest within their
|
|
309
|
+
current major. Do not opt into new prerelease channels. Taze's newest candidate
|
|
310
|
+
does not replace the separate newest-in-range runtime review in step 3.
|
|
311
|
+
|
|
312
|
+
## 3. Evaluate candidates
|
|
313
|
+
|
|
314
|
+
### Runtime ranges and review scope
|
|
315
|
+
|
|
316
|
+
For each runtime dependency of a published package, inspect registry metadata
|
|
317
|
+
for the newest stable version satisfying its current declared range, accounting
|
|
318
|
+
for catalogs and aliases. Record declared range, locked version, newest allowed
|
|
319
|
+
version, and proposed range/version. Review that newest allowed release even
|
|
320
|
+
if it is already in range or too young for this run to install; consumer
|
|
321
|
+
package managers may not enforce this repository's maturity policy. Keep the
|
|
322
|
+
installation safeguards and report any untested consumer exposure.
|
|
323
|
+
|
|
324
|
+
Review direct candidates and production-tree changes fully. For development-only
|
|
325
|
+
transitives, check advisories, deprecations, and engines; investigate release
|
|
326
|
+
notes only when those checks, validation failures, or output differences give
|
|
327
|
+
cause. A package bundled or shipped with a CLI/config package belongs to the
|
|
328
|
+
consumer review even if this repository also uses it as development tooling.
|
|
329
|
+
|
|
330
|
+
An in-range behavior change may already reach the published release's consumers.
|
|
331
|
+
Do not describe an older lockfile as protecting them or infer a new breaking
|
|
332
|
+
release solely from the lockfile movement. Test the published release and
|
|
333
|
+
updated build against actual consumer resolution, and separately preserve the
|
|
334
|
+
old locked baseline. To hold back an incompatible allowed release, present a
|
|
335
|
+
specific narrower range and obtain the policy's required approval.
|
|
336
|
+
|
|
337
|
+
### Breaking candidates
|
|
338
|
+
|
|
339
|
+
Track breaking-candidate decisions throughout the run; report applied breaks
|
|
340
|
+
and deferred opportunities separately.
|
|
341
|
+
For each candidate, identify affected APIs, behavior, Node.js support, known
|
|
342
|
+
consumers, migration work, validation results or gaps, and release implications.
|
|
343
|
+
Prepare a concrete proposal and, where useful, validate a trial migration in an
|
|
344
|
+
isolated copy before seeking a decision. For stable projects in interactive
|
|
345
|
+
mode, ask explicitly whether to accept each reviewed break unless that exact
|
|
346
|
+
scope was already authorized; continue independent updates while awaiting the
|
|
347
|
+
answer. In unattended mode, defer unapproved stable-project breaks and include
|
|
348
|
+
them under `Breaking changes available` in the report without asking. General
|
|
349
|
+
permission to update dependencies is not approval for an unspecified break.
|
|
350
|
+
For `0.x` projects, both modes authorize reviewed breaking migrations without
|
|
351
|
+
another question, but runtime-range narrowing still needs explicit approval.
|
|
352
|
+
Record accepted, declined, pending, and deferred decisions separately.
|
|
353
|
+
|
|
354
|
+
### Node.js compatibility
|
|
355
|
+
|
|
356
|
+
For every candidate, minor and patch included, distinguish consumer requirements
|
|
357
|
+
from development requirements:
|
|
358
|
+
|
|
359
|
+
- **Consumers:** for projects at 1.0.0 or later, keep the user-facing Node.js
|
|
360
|
+
minimum and supported range unchanged unless explicitly approved under
|
|
361
|
+
`Breaking candidates`. A higher effective consumer minimum or loss of any
|
|
362
|
+
supported Node.js version is a break even if `engines.node` is left unchanged.
|
|
363
|
+
Check direct, transitive, optional, peer, and workspace dependencies used by
|
|
364
|
+
published packages, plus bundled code, emitted syntax, and runtime APIs.
|
|
365
|
+
A tool used only for development here is still a consumer dependency if it
|
|
366
|
+
ships in a published CLI or configuration package. Compare
|
|
367
|
+
full engine ranges, not just the lowest version in each range.
|
|
368
|
+
For authorized breaks or zero-versioned projects, update published
|
|
369
|
+
`engines.node` to reflect the actual supported range and validate against it.
|
|
370
|
+
- **Development:** a higher Node.js requirement for development-only tools,
|
|
371
|
+
builds, or CI is allowed when published artifacts remain compatible. If it
|
|
372
|
+
exceeds the consumer requirement, add or update the Node.js entry in the
|
|
373
|
+
relevant `package.json`'s `devEngines.runtime` with `name: "node"` and a
|
|
374
|
+
`version` range covering the actual development requirement. Use the workspace
|
|
375
|
+
root for shared tooling and package manifests for package-specific tooling;
|
|
376
|
+
preserve other runtime entries and settings. This is the development Node.js
|
|
377
|
+
dependency notice; do not raise published `engines.node` to satisfy dev tools.
|
|
378
|
+
Align development version pins and build CI as needed while retaining consumer
|
|
379
|
+
compatibility checks at the applicable supported minimums (the original ones
|
|
380
|
+
unless an approved break or the zero-versioned exception changes them).
|
|
381
|
+
|
|
382
|
+
### TypeScript migrations
|
|
383
|
+
|
|
384
|
+
Read the accepted `@kitschpatrol/typescript-config` release's TypeScript peer
|
|
385
|
+
range and base configuration. If it requires a new compiler major, offer that
|
|
386
|
+
migration with its concrete effects rather than automatically deferring the
|
|
387
|
+
authored update. Apply it within existing authorization; ask only for additional
|
|
388
|
+
breaking or Node-types changes that the policy actually requires approval for.
|
|
389
|
+
|
|
390
|
+
- Add `"node"` to `compilerOptions.types` where Node globals are needed,
|
|
391
|
+
preserving other required type entries and the separation from browser code.
|
|
392
|
+
- Fix errors exposed by newly enabled strict options; do not disable those
|
|
393
|
+
options just to pass.
|
|
394
|
+
- Check the new base `lib`, emitted syntax, declarations, and APIs used by
|
|
395
|
+
shipped code against the consumer Node.js floor and browser targets. A newer
|
|
396
|
+
`lib` can make unavailable APIs type-check; it does not polyfill them.
|
|
397
|
+
|
|
398
|
+
Run type checks, build, and consumer validation after the migration, retaining
|
|
399
|
+
the distinction between development and consumer Node.js requirements.
|
|
400
|
+
|
|
401
|
+
### Compatibility review
|
|
402
|
+
|
|
403
|
+
For direct candidates and production-tree changes, read relevant upstream
|
|
404
|
+
release notes, migration guides, and advisories, and assess:
|
|
405
|
+
|
|
406
|
+
- **Security:** vulnerabilities fixed or introduced, ownership or publishing
|
|
407
|
+
anomalies, new install scripts or permissions.
|
|
408
|
+
- **Breaking:** APIs, types, defaults, configuration, module format, peer
|
|
409
|
+
ranges, runtime requirements, measured against this repository's actual use.
|
|
410
|
+
- **Coupling:** peer and engine requirements against the versions this update
|
|
411
|
+
will produce, not the current ones. A major can require a newer minor of a
|
|
412
|
+
sibling package (`@astrojs/mdx` 8 requires `astro` 7.2.10 or later).
|
|
413
|
+
- **Worth it:** fixes and performance adoptable under the version policy.
|
|
414
|
+
Note features exposed by the update and optional feature opportunities for the
|
|
415
|
+
report; do not implement new features as extra work.
|
|
416
|
+
|
|
417
|
+
When delegation is available, run independent release-note reviews in parallel,
|
|
418
|
+
split into authored packages, remaining runtime packages, and remaining direct
|
|
419
|
+
development packages. Give each reviewer its disjoint package list, a short
|
|
420
|
+
survey of actual usage and relevant source paths, consumer requirements, and
|
|
421
|
+
the version policy. Reviewers return decisions and sources without editing
|
|
422
|
+
dependencies; the primary agent resolves coupling and combines the results.
|
|
423
|
+
Share cached metadata and the GitHub API budget. Review sequentially when
|
|
424
|
+
delegation is unavailable.
|
|
425
|
+
|
|
426
|
+
Record current → proposed version, decision, and sources. Accept only when
|
|
427
|
+
compatibility is preserved, an explicit approval covers the break, or the
|
|
428
|
+
zero-versioned exception permits it; defer the rest with a reason. Do not
|
|
429
|
+
defer a migration merely because it needs a major release recommendation.
|
|
430
|
+
|
|
431
|
+
## 4. Apply
|
|
432
|
+
|
|
433
|
+
Apply only accepted candidates, including minors and patches, by exact package
|
|
434
|
+
name. Major mode can apply all accepted update sizes:
|
|
435
|
+
|
|
436
|
+
```bash
|
|
437
|
+
pnpx taze major -rw --force --include-locked \
|
|
438
|
+
--no-github-actions --no-node-version \
|
|
439
|
+
--include 'pkg-a,pkg-b' \
|
|
440
|
+
--exclude '@types/node,pnpm' \
|
|
441
|
+
--maturity-period 1 \
|
|
442
|
+
--maturity-period-exclude '<authored-packages>'
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
Replace the example names with the accepted set; skip the command if it is
|
|
446
|
+
empty. If only an older compatible target was accepted, set that reviewed
|
|
447
|
+
version manually instead of letting Taze choose the latest. Then check the
|
|
448
|
+
diff: every manifest and catalog covered, only reviewed versions applied, no
|
|
449
|
+
excluded or deferred update slipped in, range style and `workspace:` references
|
|
450
|
+
intact. A package accepted in one workspace package is not automatically
|
|
451
|
+
accepted in every other one. Hand-edit anything Taze missed (catalogs,
|
|
452
|
+
overrides), and add any required development Node.js notice. Use direct edits
|
|
453
|
+
for reviewed compiler versions inside the required peer range and for
|
|
454
|
+
authorized Node-types changes; do not let Taze choose an incompatible compiler.
|
|
455
|
+
Verify `packageManager` remains at Setup's selected version.
|
|
456
|
+
|
|
457
|
+
## 5. Resolve and validate
|
|
458
|
+
|
|
459
|
+
Run each command as a separate invocation:
|
|
460
|
+
|
|
461
|
+
```bash
|
|
462
|
+
pnpm update -r
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
```bash
|
|
466
|
+
pnpm dedupe --check
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Inspect the result. If it reports available deduplication, run the following
|
|
470
|
+
separately; other failures need diagnosis, not an unconditional retry:
|
|
471
|
+
|
|
472
|
+
```bash
|
|
473
|
+
pnpm dedupe
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Then run the checks, each separately:
|
|
477
|
+
|
|
478
|
+
```bash
|
|
479
|
+
pnpm build
|
|
480
|
+
pnpm fix
|
|
481
|
+
pnpm build # only if fix changed source files
|
|
482
|
+
pnpm test
|
|
483
|
+
pnpm lint
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
Always use bare `pnpm update -r` for the full refresh, without package selectors.
|
|
487
|
+
Named-package updates do not provide the same transitive refresh; do not use
|
|
488
|
+
`!pkg` selectors as a workaround for deferred candidates. It resolves within
|
|
489
|
+
the written ranges and rechecks release age without discarding `node_modules`.
|
|
490
|
+
In pnpm 12 it can also raise range floors in `package.json` and catalogs
|
|
491
|
+
(for example, `^1.8.0` to `^1.9.0`), so review the manifest and catalog diffs
|
|
492
|
+
after every update, not just the lockfile. Preserve excluded declarations and
|
|
493
|
+
undo unreviewed range changes by direct edits, then synchronize the lockfile.
|
|
494
|
+
|
|
495
|
+
Deduplication consolidates compatible references onto newer versions already
|
|
496
|
+
in the tree; it is not a way to hold back or downgrade a dependency. Review
|
|
497
|
+
production changes fully and development-only transitives under step 3's
|
|
498
|
+
narrower checks. If a deferred version is still allowed by a published runtime
|
|
499
|
+
range, use the explicit range/consumer decision path rather than fighting the
|
|
500
|
+
resolver with an older lockfile or an unapproved override.
|
|
501
|
+
After every `pnpm update -r`, confirm each accepted authored package resolved
|
|
502
|
+
to its accepted version. pnpm does not report a maturity holdback, so a lower
|
|
503
|
+
resolved version means the release is blocked by `minimumReleaseAge`: add only
|
|
504
|
+
that package/version to `minimumReleaseAgeExclude`, never its dependencies,
|
|
505
|
+
and update again. If the release still cannot resolve because its own
|
|
506
|
+
third-party dependencies are too young, defer that authored update, keep the
|
|
507
|
+
previously locked version, and report the date it becomes installable.
|
|
508
|
+
|
|
509
|
+
Compare every result with the baseline:
|
|
510
|
+
|
|
511
|
+
- **Consumer compatibility:** recheck the resolved published dependency trees
|
|
512
|
+
and build artifacts against the Node.js baseline, including transitive
|
|
513
|
+
changes from resolution or deduplication. Validate consumer installation and
|
|
514
|
+
representative runtime behavior at the applicable supported minimums separately
|
|
515
|
+
from checks needing the newer development runtime. Review `pnpm fix` changes
|
|
516
|
+
to `engines.node` and `devEngines.runtime`; a passing fixer or build on newer
|
|
517
|
+
Node.js does not establish consumer compatibility. Diff the production-engine
|
|
518
|
+
inventories, test the packed build in known consumers, and run fixtures at the
|
|
519
|
+
exact supported Node.js floor using [the verification recipes](references/verification.md).
|
|
520
|
+
For stable projects, do not retain a newly introduced break outside explicit
|
|
521
|
+
approval; present the candidate or defer it. Do not mask it with an unapproved
|
|
522
|
+
`engines.node` increase. For authorized breaks and zero-versioned projects,
|
|
523
|
+
validate the new contracts and Node.js range and report the baseline changes.
|
|
524
|
+
- **Logs:** for each new warning, deprecation, or peer issue, find the package
|
|
525
|
+
that introduced it (`pnpm why`) and search upstream for a known issue. Report
|
|
526
|
+
it with the link. For a peer declaration lagging a verified compatible
|
|
527
|
+
version, add a scoped `peerDependencyRules.allowedVersions` entry such as
|
|
528
|
+
`"parent@range>peer": "tested-range"` to `pnpm-workspace.yaml` in both modes,
|
|
529
|
+
and report the entry, the evidence, and that it suppresses a warning rather
|
|
530
|
+
than fixing incompatibility. Compatibility is verified when the checks pass
|
|
531
|
+
with the installed combination and no upstream report contradicts it. Apply
|
|
532
|
+
the entry even when the lagging declaration arrives through an authored
|
|
533
|
+
package: the consumer's `pnpm-workspace.yaml` is where the warning is
|
|
534
|
+
silenced, and the report flags the upstream fix separately. Do not use broad
|
|
535
|
+
`allowAny` or disable strict peer checks. Revisit such workarounds during
|
|
536
|
+
step 9.
|
|
537
|
+
- **Build output:** if the baseline includes build output, rebuild with the
|
|
538
|
+
same command and environment and diff the trees with content-hashed filenames
|
|
539
|
+
normalized (`name.[hash].js`). Explain every remaining difference with an
|
|
540
|
+
upstream changelog entry, issue, or reviewed migration/configuration change;
|
|
541
|
+
anything unexplained needs investigation. Unless explicitly configured,
|
|
542
|
+
tsdown derives its build target from `engines.node`, so an engine change can
|
|
543
|
+
legitimately alter emitted syntax. Record the effective targets before and
|
|
544
|
+
after, distinguish expected output changes from regressions, and check runtime
|
|
545
|
+
APIs separately because syntax lowering does not provide polyfills.
|
|
546
|
+
- **After `pnpm fix`:** review the diff, then rebuild and diff against the
|
|
547
|
+
pre-fix build. Formatting changes must produce identical output. A lint
|
|
548
|
+
rule's suggested fix can change behavior (default parameters do not replace
|
|
549
|
+
`null`); in that case keep the code and add an inline disable that says why.
|
|
550
|
+
If the project can or is likely to run in browsers, including alongside
|
|
551
|
+
Node.js, disable the offending rule for the affected shipped code when its
|
|
552
|
+
fix would introduce regex flags or DOM APIs unsupported by the project's
|
|
553
|
+
browser targets. Explain the compatibility reason and keep the compatible
|
|
554
|
+
code instead of applying the fix; revert the fix if already applied. This
|
|
555
|
+
also applies to zero-versioned projects, but does not apply to Node.js-only
|
|
556
|
+
projects.
|
|
557
|
+
Resolve remaining lint errors by hand.
|
|
558
|
+
|
|
559
|
+
On failure, make the smallest change consistent with the version policy and
|
|
560
|
+
rerun from the failing step:
|
|
561
|
+
|
|
562
|
+
- To tell a new failure from a pre-existing one, check the baseline logs.
|
|
563
|
+
`git stash` does not revert `node_modules`, so rerunning a check after
|
|
564
|
+
stashing still uses the new dependencies.
|
|
565
|
+
- To isolate a breaking update: set half the accepted updates back to their
|
|
566
|
+
previous specifiers, `pnpm update -r`, rerun the failing check, repeat.
|
|
567
|
+
For an out-of-range update, defer the culprit unless an approval or the
|
|
568
|
+
zero-versioned exception permits it and the migration resolves the failure.
|
|
569
|
+
For an already allowed runtime release, follow the range decision path in
|
|
570
|
+
step 3; resetting a specifier or lockfile may not exclude it.
|
|
571
|
+
|
|
572
|
+
Add focused tests only where a compatibility change touches behavior not
|
|
573
|
+
already covered. Review `readme.md` accuracy and automation opportunities in step 8.
|
|
574
|
+
|
|
575
|
+
## 6. Exclusion cleanup
|
|
576
|
+
|
|
577
|
+
Only if `minimumReleaseAgeExclude` is non-empty in any `pnpm-workspace.yaml`
|
|
578
|
+
(at the start, or after step 5). Cleanup removes entries that no longer do
|
|
579
|
+
anything; it never changes which version is installed and never downgrades a
|
|
580
|
+
package.
|
|
581
|
+
|
|
582
|
+
Decide each entry from the publish time recorded in Setup, or look it up now:
|
|
583
|
+
|
|
584
|
+
- **Mature** (published at least 24 hours before the run): remove it; the
|
|
585
|
+
version resolves without it.
|
|
586
|
+
- **Young, authored package:** keep it while that version is the release this
|
|
587
|
+
run installs. Removing it would hold the package back to an older release.
|
|
588
|
+
- **Young, third-party package:** keep it while it holds a version that is
|
|
589
|
+
already locked; removing it would downgrade an installed package rather than
|
|
590
|
+
block a new one. Report it as pending with the date it can be removed. Do not
|
|
591
|
+
add new third-party entries.
|
|
592
|
+
- **Unused** (the version is neither locked nor targeted): remove it.
|
|
593
|
+
|
|
594
|
+
Edit the list directly, preserving neighboring keys and comments; use `[]` or
|
|
595
|
+
remove the key when nothing remains. Then repeat step 5's separate bare
|
|
596
|
+
`pnpm update -r` and `pnpm dedupe --check` invocations, followed by a separate
|
|
597
|
+
`pnpm dedupe` only when the check reports available changes, and run each check
|
|
598
|
+
separately:
|
|
599
|
+
|
|
600
|
+
```bash
|
|
601
|
+
pnpm build
|
|
602
|
+
pnpm test
|
|
603
|
+
pnpm lint
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
Compare the resolved versions with the lockfile saved before cleanup. pnpm
|
|
607
|
+
does not report a maturity holdback; it silently selects the newest mature
|
|
608
|
+
version. An authored package that resolved below its latest stable release, or
|
|
609
|
+
any package that resolved below its previous locked version, means a removed
|
|
610
|
+
entry was still needed: re-add that narrowest package/version entry and resolve
|
|
611
|
+
again. Never restore the whole previous list.
|
|
612
|
+
|
|
613
|
+
If `pnpm-lock.yaml` comes out byte-identical and no ranges or build settings
|
|
614
|
+
changed, the installed tree did not change; rerun only `pnpm lint`.
|
|
615
|
+
|
|
616
|
+
If the list was empty throughout, skip this section and continue to step 7.
|
|
617
|
+
If cleanup changes resolution, repeat step 5's consumer compatibility review
|
|
618
|
+
before reviewing documentation.
|
|
619
|
+
|
|
620
|
+
## 7. Side effects declaration
|
|
621
|
+
|
|
622
|
+
For each published package, check that `sideEffects` in `package.json` tells
|
|
623
|
+
the truth about the shipped code. Bundlers drop any import they consider
|
|
624
|
+
side-effect-free when the declaration says `false`, and keep every module
|
|
625
|
+
otherwise, so a wrong `false` silently removes code consumers rely on and a
|
|
626
|
+
missing `false` on a pure package forfeits tree shaking.
|
|
627
|
+
|
|
628
|
+
1. Read the declaration: `false`, an array of file patterns, `true`, or absent,
|
|
629
|
+
which means `true`.
|
|
630
|
+
2. Inspect the published entry points and every module they reach, in the
|
|
631
|
+
build output that actually ships (`dist/` and the `files` list), including
|
|
632
|
+
bundled dependencies. Look for work that runs on import rather than on
|
|
633
|
+
call: assignments to globals or prototypes, polyfills, registrations such
|
|
634
|
+
as `customElements.define`, event listeners, process or environment setup,
|
|
635
|
+
CSS and asset imports, and bare imports kept only for their effects.
|
|
636
|
+
3. Set the declaration to match: `false` when nothing runs on import, an array
|
|
637
|
+
naming the exact shipped files that do when only some do, and `true` when
|
|
638
|
+
most modules have effects. A package with no importable entry point, such
|
|
639
|
+
as a `bin`-only CLI, is unaffected either way; leave its declaration as is.
|
|
640
|
+
4. Report every evaluation: the package, which modules were inspected, what
|
|
641
|
+
runs on import if anything, the declaration before and after, and why it
|
|
642
|
+
changed or stayed. A wrong `false` is a bug; a newly added `false` is a
|
|
643
|
+
performance improvement. Count either under the release recommendation.
|
|
644
|
+
|
|
645
|
+
## 8. Documentation review
|
|
646
|
+
|
|
647
|
+
Review `readme.md` against the updated project. Correct stale installation
|
|
648
|
+
instructions, requirements, commands, examples, configuration guidance, and
|
|
649
|
+
feature descriptions. If `readme.md` is generated or copied from another file,
|
|
650
|
+
update its maintained source and regenerate it through the existing workflow.
|
|
651
|
+
|
|
652
|
+
Review each section for content that would be more reliably maintained through
|
|
653
|
+
mdat, especially content duplicated from package metadata, CLI help, source
|
|
654
|
+
files, or other documentation. Prefer a rule bundled with mdat when it fits,
|
|
655
|
+
then a suitable mdat plugin, or a custom rule in `mdat.config.ts` for
|
|
656
|
+
project-specific needs. Check the installed rules and relevant documentation
|
|
657
|
+
before choosing an implementation. Replace manually maintained sections where
|
|
658
|
+
generation reduces duplication or prevents drift while preserving their useful
|
|
659
|
+
content and readability; keep explanatory prose manual when automation adds
|
|
660
|
+
no benefit. Any new plugin dependency must satisfy the dependency review,
|
|
661
|
+
release maturity, and compatibility policies above.
|
|
662
|
+
|
|
663
|
+
Run the project's mdat generation and documentation checks, inspect the
|
|
664
|
+
rendered content for accuracy, and confirm that a second generation produces
|
|
665
|
+
no further changes. Validate any new dependency under steps 5 and 6 before
|
|
666
|
+
continuing. Include documentation corrections and mdat conversions in the
|
|
667
|
+
final report.
|
|
668
|
+
|
|
669
|
+
## 9. @kitschpatrol/shared-config override minimization
|
|
670
|
+
|
|
671
|
+
Run this as the final cleanup after all accepted updates, migrations,
|
|
672
|
+
validation, release-age exclusion cleanup, and documentation review, before
|
|
673
|
+
the release recommendation and report.
|
|
674
|
+
|
|
675
|
+
Review every shared-config-related configuration file in the project root,
|
|
676
|
+
including dotfiles, companion ignore files, and configuration stored in
|
|
677
|
+
`package.json`. Include the individual `@kitschpatrol/*-config` packages used by
|
|
678
|
+
the project. Inventory every local override beyond the upstream default export
|
|
679
|
+
or equivalent base configuration: rule settings, options, ignores, file globs,
|
|
680
|
+
and other customizations.
|
|
681
|
+
Include scoped peer-warning workarounds added during the update; remove them
|
|
682
|
+
when upstream peer declarations now cover the tested versions.
|
|
683
|
+
|
|
684
|
+
For each override, compare it with the defaults and effective configuration of
|
|
685
|
+
the exact versions installed after the updates. Check the current code it
|
|
686
|
+
targets and, for upstream workarounds, the relevant upstream changes or fixes.
|
|
687
|
+
Remove overrides that duplicate current defaults, target code or files that no
|
|
688
|
+
longer exist, or compensate for issues now fixed upstream. Do not infer that an
|
|
689
|
+
override is redundant merely because checks pass while it is still enabled.
|
|
690
|
+
|
|
691
|
+
Validate removals in small groups using the affected tools' checks without
|
|
692
|
+
caches, covering the affected files and any files newly included by removing
|
|
693
|
+
ignores. Recheck effective configuration where merge order or file matching
|
|
694
|
+
matters. Keep overrides that still serve a concrete project requirement,
|
|
695
|
+
including the behavior and browser-compatibility protections from step 5, and
|
|
696
|
+
record why each is needed. If necessity cannot be established, retain the
|
|
697
|
+
override and report the unresolved evidence instead of guessing.
|
|
698
|
+
|
|
699
|
+
After changing configuration, rerun the affected build, test, type, and lint
|
|
700
|
+
checks and compare results and build output as in step 5. The final report must
|
|
701
|
+
account for every override: removed with evidence, retained with a current
|
|
702
|
+
justification, or unresolved with the missing evidence identified.
|
|
703
|
+
|
|
704
|
+
## Release recommendation
|
|
705
|
+
|
|
706
|
+
Recommend `patch`, `minor`, or `major` from the final consumer-visible changes,
|
|
707
|
+
following the repository's release conventions. Do not infer the recommendation
|
|
708
|
+
from the largest dependency version bump:
|
|
709
|
+
|
|
710
|
+
- **Patch:** compatible bug or security fixes, performance improvements, or
|
|
711
|
+
dependency/tooling maintenance with no new consumer-facing capability. A
|
|
712
|
+
development-only Node.js increase with the required notice can remain a
|
|
713
|
+
patch, and so does a corrected or newly added `sideEffects` declaration.
|
|
714
|
+
- **Minor:** backward-compatible functionality or capabilities actually exposed
|
|
715
|
+
to consumers by the accepted updates. Explain what consumers gain; an upstream
|
|
716
|
+
minor or major alone is not a reason for a minor release. For zero-versioned
|
|
717
|
+
projects, also recommend a minor release for accepted consumer-facing breaking
|
|
718
|
+
changes unless the repository's pre-1.0 release conventions specify otherwise.
|
|
719
|
+
- **Major:** explicitly approved consumer-facing breaks in projects at 1.0.0
|
|
720
|
+
or later, including a raised consumer Node.js floor. A dependency major or
|
|
721
|
+
development-only requirement by itself does not justify a project major.
|
|
722
|
+
|
|
723
|
+
Give a short rationale per affected published package, or one recommendation
|
|
724
|
+
for a shared release version using the highest applicable level. If nothing
|
|
725
|
+
releasable changed, say no release is needed. An approved major release is
|
|
726
|
+
within scope; an unapproved breaking candidate stays pending or deferred.
|
|
727
|
+
For `0.x`, accepted consumer breaks normally require a minor, not a patch;
|
|
728
|
+
follow an explicit repository convention if it differs. Assess changes already
|
|
729
|
+
reachable through the old published ranges separately from newly introduced
|
|
730
|
+
consumer breaks. Recommend only; do not bump repository versions.
|
|
731
|
+
|
|
732
|
+
## Report
|
|
733
|
+
|
|
734
|
+
State the execution mode and why it was selected. Include a `Breaking changes
|
|
735
|
+
available` section for deferred breaking candidates: current and proposed
|
|
736
|
+
versions, affected consumers/contracts, required migration, validation results
|
|
737
|
+
or gaps, recommended release level, and the decision still needed. Distinguish
|
|
738
|
+
these from applied breaks authorized explicitly or by the `0.x` policy. In
|
|
739
|
+
unattended mode, record decisions needed as follow-up items, not questions or
|
|
740
|
+
pending waits. Report unavailable checks and blocked work even when independent
|
|
741
|
+
updates succeeded.
|
|
742
|
+
|
|
743
|
+
Review the full diff for unrelated changes, excluded-package edits, and stray
|
|
744
|
+
exemptions first. Then summarize: applied updates; major decisions with
|
|
745
|
+
sources; consumer impact, approvals, declined/deferred decisions, and breaks
|
|
746
|
+
accepted under the zero-versioned policy;
|
|
747
|
+
output differences and their upstream or migration explanations;
|
|
748
|
+
consumer Node.js requirements before and after, development Node.js changes and
|
|
749
|
+
their `devEngines.runtime` notices; the release recommendation and its rationale;
|
|
750
|
+
baseline lockfile drift; the `packageManager` version chosen and its source;
|
|
751
|
+
`sideEffects` evaluations with their evidence and outcome; runtime ranges,
|
|
752
|
+
fresh-install exposure, and approved
|
|
753
|
+
range restrictions; TypeScript migrations; new warnings and scoped peer
|
|
754
|
+
workarounds; what was validated, at which exact Node.js versions, and in which
|
|
755
|
+
known consumers, including CLI/fixture comparisons and unavailable baselines;
|
|
756
|
+
remaining `minimumReleaseAgeExclude` entries with reasons and the dates they can
|
|
757
|
+
be removed; deferred upgrades,
|
|
758
|
+
documentation corrections and mdat conversions;
|
|
759
|
+
shared-config overrides removed or retained with their justifications and any
|
|
760
|
+
unresolved override reviews; unresolved failures, checks that could not run,
|
|
761
|
+
and steps run differently because of the environment. Passing checks do not
|
|
762
|
+
prove the absence of breaking changes. Leave everything uncommitted.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# Consumer verification recipes
|
|
2
|
+
|
|
3
|
+
Use these recipes during Setup and step 5. Save commands, versions, raw logs,
|
|
4
|
+
and results under `node_modules/.cache/ksc-update/`. Run pnpm commands as separate
|
|
5
|
+
bare invocations; save their captured output in a later operation so sandbox
|
|
6
|
+
command exclusions still match.
|
|
7
|
+
|
|
8
|
+
## Production-tree engines
|
|
9
|
+
|
|
10
|
+
For each published package, run in its directory before and after updates:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pnpm list --prod --depth Infinity --json
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Save stdout as `production-tree.json` in the corresponding baseline or updated
|
|
17
|
+
results folder. If stdout contains lines beginning with `[WARN]`, preserve those
|
|
18
|
+
warnings separately and remove only those lines before parsing. Require a
|
|
19
|
+
successful command and valid JSON; do not extract an arbitrary JSON-looking
|
|
20
|
+
substring from failed output. In a monorepo, include every published package,
|
|
21
|
+
not only a private root with no production dependencies.
|
|
22
|
+
|
|
23
|
+
The list supplies installed paths; read each installed `package.json` for its
|
|
24
|
+
actual engine declaration. Save this helper in the run's cache as
|
|
25
|
+
`production-engines.mjs` and run it with the saved JSON path as its argument:
|
|
26
|
+
|
|
27
|
+
```js
|
|
28
|
+
import { readFileSync } from 'node:fs'
|
|
29
|
+
import { join } from 'node:path'
|
|
30
|
+
|
|
31
|
+
const roots = JSON.parse(readFileSync(process.argv[2], 'utf8'))
|
|
32
|
+
if (!Array.isArray(roots)) {
|
|
33
|
+
throw new TypeError('Expected pnpm list JSON array')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const manifests = new Map()
|
|
37
|
+
const rows = new Set()
|
|
38
|
+
|
|
39
|
+
function visit(pkg) {
|
|
40
|
+
if (!pkg.path) {
|
|
41
|
+
throw new Error(`Missing installed path: ${pkg.name ?? pkg.version}`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (!manifests.has(pkg.path)) {
|
|
45
|
+
manifests.set(pkg.path, JSON.parse(readFileSync(join(pkg.path, 'package.json'), 'utf8')))
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const manifest = manifests.get(pkg.path)
|
|
49
|
+
rows.add(
|
|
50
|
+
JSON.stringify({
|
|
51
|
+
name: manifest.name,
|
|
52
|
+
node: manifest.engines?.node,
|
|
53
|
+
version: manifest.version,
|
|
54
|
+
}),
|
|
55
|
+
)
|
|
56
|
+
for (const group of ['dependencies', 'optionalDependencies']) {
|
|
57
|
+
const children = Object.values(pkg[group] ?? {})
|
|
58
|
+
for (const child of children) {
|
|
59
|
+
visit(child)
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
for (const root of roots) {
|
|
65
|
+
visit(root)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
process.stdout.write(`${[...rows].toSorted().join('\n')}\n`)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Save the resulting inventory and diff it after resolution and deduplication.
|
|
72
|
+
Investigate missing installed paths instead of omitting them. An omitted `node`
|
|
73
|
+
field means undeclared support, not proof of compatibility. Check full semver ranges
|
|
74
|
+
against every supported consumer Node.js line; check platform-specific optional
|
|
75
|
+
dependencies and shipped/bundled code separately because one host's installed
|
|
76
|
+
tree cannot prove their compatibility. This inventory does not replace the
|
|
77
|
+
newest-in-range registry review.
|
|
78
|
+
|
|
79
|
+
## Known consumers and packed builds
|
|
80
|
+
|
|
81
|
+
Read confirmed consumers from the repository's existing `CLAUDE.md` or
|
|
82
|
+
`AGENTS.md`. A useful `Known consumers` entry contains the consumer's name and
|
|
83
|
+
path, which package/imports/CLI it uses, its test command, and representative
|
|
84
|
+
CLI commands and fixture inputs. Preserve existing instructions; add only facts
|
|
85
|
+
confirmed by the user or repository evidence.
|
|
86
|
+
|
|
87
|
+
1. Create a temporary directory under `${TMPDIR:-/tmp}`. Clone each local
|
|
88
|
+
consumer into it with `git clone --no-hardlinks <consumer-path> <temp-copy>`.
|
|
89
|
+
Record the commit. A clone excludes uncommitted changes; if those matter,
|
|
90
|
+
explicitly copy the needed working-tree files and record that baseline.
|
|
91
|
+
Do not modify the consumer's original checkout.
|
|
92
|
+
2. Install and test the copied consumer with its starting lockfile. Save
|
|
93
|
+
its tests' results and representative CLI stdout, stderr, and exit codes.
|
|
94
|
+
Preserve this copy for baseline comparison.
|
|
95
|
+
3. Build the updated package with its development Node.js version and run
|
|
96
|
+
`pnpm pack` as a bare command in that package. Inspect the archive's contents
|
|
97
|
+
and manifest, including exported entrypoints, engines, and dependency ranges.
|
|
98
|
+
4. In a second consumer copy, replace the consumed dependency with a `file:`
|
|
99
|
+
specifier pointing to that tarball, using a direct manifest edit. Install
|
|
100
|
+
and rerun the same tests and CLI fixtures. If a workspace-local dependency
|
|
101
|
+
or alias is involved, replace the actual consuming edge and verify the
|
|
102
|
+
resolved path/version; do not assume a root-only edit reaches it. A linked
|
|
103
|
+
built package is useful for iteration, but the tarball verifies what ships
|
|
104
|
+
and avoids a link silently borrowing the producer's `node_modules`.
|
|
105
|
+
5. Diff CLI output and fixture results. Inspect semantic differences, including
|
|
106
|
+
behavior JSON may hide, such as object prototypes, missing versus undefined
|
|
107
|
+
values, ordering, and error handling. Explain each change against accepted
|
|
108
|
+
migrations and consumer expectations.
|
|
109
|
+
|
|
110
|
+
Also test a fresh isolated install of the published release with the newest
|
|
111
|
+
dependencies its ranges allow. Keep this distinct from the published release
|
|
112
|
+
running on its old dependencies: reproduce the latter from a known historical
|
|
113
|
+
lockfile or preserved installation where possible. A registry tarball alone
|
|
114
|
+
does not recover the old transitive tree. If it cannot be reconstructed, report
|
|
115
|
+
that gap instead of labeling a fresh install the historical baseline.
|
|
116
|
+
|
|
117
|
+
## Exact Node.js floor
|
|
118
|
+
|
|
119
|
+
Use the exact minimum supported release, not just the same major or the active
|
|
120
|
+
developer runtime. For disjoint supported Node.js lines, check each relevant
|
|
121
|
+
minimum. For an approved engine bump, compare the old release at its old floor
|
|
122
|
+
with the updated package at its new floor and on an overlapping supported
|
|
123
|
+
runtime where available.
|
|
124
|
+
|
|
125
|
+
If the version manager has no suitable installed binary, download the exact
|
|
126
|
+
official archive from `https://nodejs.org/dist/v<version>/` for the host's OS and
|
|
127
|
+
architecture into a temporary folder, alongside `SHASUMS256.txt`. Verify the
|
|
128
|
+
archive's SHA-256 against that file, then extract it there. Use the resulting
|
|
129
|
+
Node binary by absolute path; do not install globally or change the user's
|
|
130
|
+
default runtime. Record `<absolute-node-path> --version` with the results.
|
|
131
|
+
|
|
132
|
+
Install/build with the development runtime if the tooling requires it, then
|
|
133
|
+
invoke the packed package's built JavaScript entrypoint and runtime fixtures
|
|
134
|
+
with the exact-floor binary. For subprocesses that use `env node`, put that
|
|
135
|
+
binary's directory first in the test process's `PATH` only. Confirm which
|
|
136
|
+
binary the child process uses. If the test runner itself requires newer Node,
|
|
137
|
+
run a small fixture harness or the built CLI directly at the floor; running
|
|
138
|
+
only the newer test runner is not floor validation. Report when installation
|
|
139
|
+
or execution on the exact runtime could not be verified.
|
|
140
|
+
|
|
141
|
+
Record stdout, stderr, exit codes, and relevant API results for identical
|
|
142
|
+
fixtures across runtimes and dependency baselines. Normalize only known
|
|
143
|
+
nondeterministic fields using the same documented transformation for all runs.
|
|
144
|
+
Save SHA-256 digests of normalized fixture output for quick comparison, retain
|
|
145
|
+
the actual output for reviewing differences, and add targeted assertions for
|
|
146
|
+
behavior that serialization omits.
|
|
147
|
+
|
|
148
|
+
## Tool references
|
|
149
|
+
|
|
150
|
+
Check these primary sources or the installed tool's help when behavior differs:
|
|
151
|
+
|
|
152
|
+
- [GitHub Actions variables](https://docs.github.com/en/actions/reference/workflows-and-actions/variables):
|
|
153
|
+
`GITHUB_ACTIONS` and `CI` identify unattended execution in Actions.
|
|
154
|
+
- [pnpm update](https://pnpm.io/cli/update): manifest/catalog writes and selectors.
|
|
155
|
+
This workflow uses a bare update for its full dependency refresh; do not
|
|
156
|
+
assume exclusion syntax works identically in every pnpm implementation.
|
|
157
|
+
- [pnpm dedupe](https://pnpm.io/cli/dedupe) and
|
|
158
|
+
[pnpm list](https://pnpm.io/cli/list): deduplication checks and tree inventories.
|
|
159
|
+
- [pnpm settings](https://pnpm.io/settings) and
|
|
160
|
+
[peer rules](https://pnpm.io/settings/peer-dependencies#peerdependencyrulesallowedversions):
|
|
161
|
+
current configuration locations and scoped warning allowances. Current pnpm
|
|
162
|
+
uses `.npmrc` for registry/auth settings, not all policy settings.
|
|
163
|
+
- [Taze](https://github.com/antfu-collective/taze): candidate filters and maturity.
|
|
164
|
+
- [Actions Up](https://github.com/azat-io/actions-up#update-style): SHA pinning.
|
|
165
|
+
- [tsdown target](https://tsdown.dev/options/target): implicit engine-derived
|
|
166
|
+
targets affect emitted syntax and do not provide API polyfills.
|
|
167
|
+
- [Node.js release archives](https://nodejs.org/dist/): exact runtime downloads
|
|
168
|
+
and checksum manifests.
|