relion 0.4.0 → 0.5.0
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/dist/cli.js +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +6 -6
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{resolve as e}from"node:path";import{pathToFileURL as t}from"node:url";import n from"./index.js";import{cli as r}from"cleye";
|
|
1
|
+
import{resolve as e}from"node:path";import{pathToFileURL as t}from"node:url";import n from"./index.js";import{cli as r}from"cleye";import.meta.main&&await i();async function i(i,a){if(!i)try{let n=t(e(process.cwd(),`relion.config.ts`)).href;i=(await import(n)).default}catch(e){throw Error(`Error loading config: ${e.message}`)}let o=r({name:`relion`,flags:{bump:{alias:`b`,type:Boolean,description:`Bump the version`,default:!1},changelog:{alias:`l`,type:Boolean,description:`Generate a changelog`,default:!1},commit:{alias:`c`,type:Boolean,description:`Create a commit`,default:!1},tag:{alias:`t`,type:Boolean,description:`Create a tag`,default:!1},profile:{alias:`p`,type:String,description:`Use config profile`},dryRun:{alias:`d`,type:Boolean,description:`Run without making any changes`,default:!1},latest:{alias:`L`,type:Boolean,description:`Use the latest-release commit range in changelog`,default:!1}}},void 0,a);o&&(o.flags.bump||(i.bump=!1),o.flags.changelog||(i.changelog=!1),o.flags.commit||(i.commit=!1),o.flags.tag||(i.tag=!1),o.flags.profile&&(i.profile=o.flags.profile),o.flags.dryRun&&(i.dryRun=o.flags.dryRun),o.flags.latest&&i.changelog&&(i.changelog===!0&&(i.changelog={}),i.changelog.commitRange=`latest-release`),await n(i))}export{i as runCli};
|
package/dist/index.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ interface UserConfig {
|
|
|
17
17
|
prevReleaseTagPattern?: RegExp;
|
|
18
18
|
dryRun?: boolean;
|
|
19
19
|
profile?: string;
|
|
20
|
+
silent?: boolean;
|
|
20
21
|
[profile: `_${string}`]: UserConfig | undefined;
|
|
21
22
|
}
|
|
22
23
|
type OptionalKeys = 'releaseVersion' | 'releaseType' | 'context' | 'profile';
|
|
@@ -37,8 +38,7 @@ interface ContextualConfig extends TransformedConfig {
|
|
|
37
38
|
type ResolvedConfig = ContextualConfig;
|
|
38
39
|
type BumpFiles = (string | VersionedFile)[];
|
|
39
40
|
interface ChangelogOptions {
|
|
40
|
-
|
|
41
|
-
outputFile?: string;
|
|
41
|
+
output?: 'stdout' | (string & {});
|
|
42
42
|
commitRange?: CommitRange;
|
|
43
43
|
sections?: ChangelogSectionDefinition[];
|
|
44
44
|
header?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import r from"handlebars";import i from"semver";import{execSync as a}from"node:child_process";const o=
|
|
1
|
+
import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import r from"handlebars";import i from"semver";import{execSync as a}from"node:child_process";const o={bump:!1,changelog:!1,commit:!1,tag:!1,versionSourceFile:`./package.json`,newTagFormat:`v{{newVersion}}`,prevReleaseTagPattern:/^v(?<version>\d+\.\d+\.\d+)/,zeroMajorBreakingIsMinor:!0,dryRun:!1,silent:!1,context:{commitHyperlink:!0,refHyperlink:!0},commitsParser:{headerPattern:/^(?<type>\w+)(?:\((?<scope>.+)\))?(?<bang>!)?: (?<subject>.+)/s,breakingChangesPattern:/BREAKING CHANGES?:\s*(?<content>.+)/s,breakingChangeListPattern:/- (.+)/g,tagPattern:/tag: (?<tag>.*?)[,)]/g,coAuthorPattern:/Co-authored-by: (?<name>.+?) <(?<email>.+)>/g,signerPattern:/Signed-off-by: (?<name>.+?) <(?<email>.+)>/g,ghEmailPattern:/^(?:\d+\+)?(?<username>.+)@users\.noreply\.github\.com$/,remoteUrlPattern:/^(https:\/\/|git@)(?<host>[^/:]+)[/:](?<owner>.+?)\/(?<name>.+?)(?:\..*)?$/,refPattern:/^(?<action>.+?) (?<labels>.+)$/gm,refLabelPattern:/(?:(?<owner>\S+?)\/(?<repo>\S+?))?#(?<number>\d+)/g,refActionPattern:/Fixes|Closes|Refs/i,dateSource:`authorDate`,dateFormat:`YYYY-MM-DD`}},s={breaking:{title:`⚠️ BREAKING CHANGES`,commitType:`breaking`},feat:{title:`✨ Features`,commitType:`feat`},fix:{title:`🩹 Fixes`,commitType:`fix`},perf:{title:`⚡ Performance`,commitType:`perf`},refactor:{title:`🚜 Refactoring`,commitType:`refactor`},docs:{title:`📚 Documentation`,commitType:`docs`},style:{title:`🎨 Formatting`,commitType:`style`},build:{title:`📦 Build`,commitType:`build`},ci:{title:`🚀 CI`,commitType:`ci`},revert:{title:`♻️ Reverts`,commitType:`revert`},deps:{title:`🧩 Dependencies`,commitType:`chore`,filter:e=>!!e.scope?.includes(`deps`)},chore:{title:`🛠️ Chores`,commitType:`chore`},test:{title:`🧪 Tests`,commitType:`test`},misc:{title:`⚙️ Miscellaneous`,commitType:`*`,filter:e=>e.type!==`release`},[Symbol.iterator](){return Object.values(this)[Symbol.iterator]()}},c={output:`CHANGELOG.md`,commitRange:`unreleased`,sections:[...s],header:`# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
`,prevReleaseHeaderPattern:/^##.*?\d+\.\d+\.\d+/m,helpers:{repeat:(e,t)=>e.repeat(t)},partials:{}},
|
|
4
|
+
`,prevReleaseHeaderPattern:/^##.*?\d+\.\d+\.\d+/m,helpers:{repeat:(e,t)=>e.repeat(t)},partials:{}},l={message:`release({{repo.name}}): {{newTag}}`,signOff:!1,gpgSign:!1,stageAll:!0,extraArgs:``},u={name:`{{newTag}}`,message:`release({{repo.name}}): {{newTag}}`,gpgSign:!1,force:!1,extraArgs:``},d=[{filePathRegex:/package\.json$/,versionPattern:/(^.*?"version".*?")(.*?)(")/s},{filePathRegex:/package-lock\.json$/,versionPattern:/(^.*?"version".*?"|"packages".*?"".*"version".*?")(.*?)(")/gs}],f=async e=>{let t=p(e),n=m(t),r=h(n),i=await g(r),a=b(i);return a},p=e=>{let t=e.profile;if(!t)return e;let n=e[`_${t}`];if(!n)throw Error(`Profile "${t}" not found in configuration.`);let r=(t,...r)=>{let i=e=>Object.prototype.toString.call(e)===`[object Object]`,a=(e,t)=>{let n=i(e),r=i(t);if(n&&r)return{...e,...t};if(!n&&r)return t;if(n&&!r)return e},o=e[t],s=n[t],c=a(o,s);if(c!==void 0)return r.forEach(e=>{c[e]=a(o?.[e],s?.[e])}),c};return{...e,...n,commitsParser:r(`commitsParser`),changelog:r(`changelog`,`partials`,`helpers`),commit:r(`commit`),tag:r(`tag`),context:r(`context`)}},m=e=>{let t=(e,t,n,...r)=>{if(t==null||t===!1)return!1;if(t===!0)return n;if(typeof t!=`object`)throw Error(`Invalid value for ${e}. It should be a boolean or an object.`);let i={...n,...t};return r.forEach(e=>i[e]={...n[e],...t[e]}),i};return{...o,...e,commitsParser:{...o.commitsParser,...e.commitsParser},changelog:t(`changelog`,e.changelog,c,`partials`,`helpers`),commit:t(`commit`,e.commit,l),tag:t(`tag`,e.tag,u)}},h=e=>{let t=e=>{let t=d.find(t=>t.filePathRegex.test(e));if(t)return{filePath:e,versionPattern:t.versionPattern};throw Error(`File ${e} doesn't match any default versioned files. Please provide a custom version pattern for this file.`)},n=e=>{if(e===!1)return!1;if(e===!0)return[i];if(Array.isArray(e))return[i,...e.map(e=>typeof e==`string`?t(e):e)];throw Error(`Invalid value for bump. It should be a boolean or an array.`)},i=typeof e.versionSourceFile==`string`?t(e.versionSourceFile):e.versionSourceFile;return{...e,versionSourceFile:i,bump:n(e.bump),changelog:e.changelog===!1?!1:{...e.changelog,compiledPartials:Object.fromEntries(Object.entries(e.changelog.partials).map(([e,t])=>[e,r.compile(t)]))}}},g=async e=>{let t=e.context??{},n=P(e.commitsParser.remoteUrlPattern);t.repo={...n,...t.repo};let r=e.changelog?e.changelog.commitRange:`unreleased`;t.commits=e.context?.commits?await Promise.all(e.context.commits.map(async t=>typeof t==`object`&&`message`in t||typeof t==`string`?(await D([t],e.commitsParser,e.prevReleaseTagPattern))[0]:t)):await D(r,e.commitsParser,e.prevReleaseTagPattern),t.currentVersion??=S(e.versionSourceFile),t.currentTag??=L(e.prevReleaseTagPattern)[0],t.newVersion??=await C(e,t.currentVersion),t.newTag??=x(e.newTagFormat,t);let i={...e,context:t};return t.releases=e.changelog?_(t.commits,e.changelog.sections,i):null,i},_=(e,t,n)=>{let r={};return e.forEach(e=>{let t=e.tags?.find(e=>n.prevReleaseTagPattern.test(e));if(t)r[t]??={tag:t,version:n.prevReleaseTagPattern.exec(t)?.groups?.version,date:e.date,commits:[e]};else{let t=Object.keys(r).at(-1);t?r[t].commits.push(e):r[n.context.newTag]={tag:n.context.newTag,version:n.context.newVersion,date:e.date,commits:[e]}}}),Object.values(r).map(e=>v(e,t))},v=(e,t)=>{let{commits:n,...r}=e;return{...r,commitGroups:y(n,t)}},y=(e,t)=>{let n=Object.fromEntries(t.map(e=>[e.title,[]]));return e.forEach(e=>{let r=!!e.breakingChanges,i=!1,a=!1;for(let o of t){if(o.filter&&!o.filter(e))continue;let t=[o.commitType].flat();if(r&&!a&&t.includes(`breaking`)){n[o.title].push(e),a=!0;continue}if(!i&&(t.includes(e.type)||t.includes(`*`))&&(n[o.title].push(e),i=!0),i&&(!r||a))return}}),Object.keys(n).forEach(e=>{n[e].length||delete n[e]}),Object.entries(n).map(([e,t])=>({title:e,commits:t}))},b=e=>({...e,commit:e.commit?{...e.commit,message:x(e.commit.message,e.context)}:e.commit,tag:e.tag?{...e.tag,name:x(e.tag.name,e.context),message:x(e.tag.message,e.context)}:e.tag}),x=(e,t)=>{let n=r.compile(e);return n(t)},S=e=>{let n=t(e.filePath,`utf8`),r=e.versionPattern.exec(n)?.[2];if(!r)throw Error(`Version not found in '${e.filePath}' with pattern '${e.versionPattern}'`);if(!i.valid(r))throw Error(`Invalid version format in '${e.filePath}': '${r}'`);return B(`Current version from '${e.filePath}': '${r}'`),r},C=async(e,t)=>{if(e.releaseVersion){if(!i.valid(e.releaseVersion))throw Error(`Invalid release version format: '${e.releaseVersion}'`);return e.releaseVersion}let n;if(e.releaseType)n=e.releaseType;else{let r=await D(`unreleased`,e.commitsParser,e.prevReleaseTagPattern);n=w(r),e.zeroMajorBreakingIsMinor&&i.major(t)===0&&n===`major`&&(n=`minor`)}let r=T(t,n);return B(`Determined new version: '${r}' (release type: '${n}')`),r},w=e=>{let t=e.some(e=>e.breakingChanges);if(t)return`major`;let n=e.some(e=>e.type===`feat`);return n?`minor`:`patch`},T=(e,t)=>i.inc(e,t)??(()=>{throw Error(`Failed to calculate new version from '${e}' with release type '${t}'`)})();let E=function(e){return e.G=`valid`,e.B=`bad`,e.U=`valid, unknown validity`,e.X=`valid, expired`,e.Y=`valid, made by expired key`,e.R=`valid, made by revoked key`,e.E=`cannot check (missing key)`,e.N=`no signature`,e}({});const D=async(e,t,n)=>{let r=Array.isArray(e)?e:F(e,n),i=t;return(await Promise.all(r.map(async e=>{typeof e==`string`&&(e={message:e});let{hash:t,tagRefs:n}=e,r=e.message.trim();if(!r)throw Error(`Message is missing for commit: ${JSON.stringify(e)}`);let a;try{a=O(r,i)}catch(e){console.warn(`Error parsing commit '${t??`<no hash>`}':`,e.message);return}let{type:o,scope:s,subject:c,body:l,breakingChanges:u,footer:d}=a,f=n?[...n.matchAll(i.tagPattern)].map(e=>e.groups?.tag??``):[],p=d?[...d.matchAll(i.signerPattern)].map(e=>e.groups):[],m=[],h=e=>{m.some(t=>t.email===e.email)||m.push(e)},g=e.authorName&&e.authorEmail?A({name:e.authorName,email:e.authorEmail},p):void 0;g&&h(g);let _=e.committerName&&e.committerEmail?A({name:e.committerName,email:e.committerEmail},p):void 0;_&&h(_);let v=d?[...d.matchAll(i.coAuthorPattern)].map(e=>e.groups).map(e=>A(e,p)):[];v.forEach(e=>h(e));let y=await j(d??``,i),b=e.gpgSigCode?{code:e.gpgSigCode,label:E[e.gpgSigCode],keyId:e.gpgSigKeyId}:void 0,x=e[i.dateSource===`committerDate`?`committerTs`:`authorTs`];typeof x==`string`&&(x=M(new Date(x*1e3),i.dateFormat));let S={hash:t,type:o,scope:s,subject:c,body:l,breakingChanges:u,footer:d,committer:_,gpgSig:b,date:x,tags:f.length?f:void 0,authors:m.length?m:void 0,refs:y.length?y:void 0};return S}))).filter(e=>e!==void 0)},O=(e,t)=>{let[n,...r]=e.split(`
|
|
5
5
|
|
|
6
|
-
`),i=t.headerPattern.exec(n);if(!i?.groups)throw Error(`Commit header '${n}' doesn't match expected format`);let{type:a,scope:o,bang:s,subject:c}=i.groups,l,u=r.find(e=>t.breakingChangesPattern.test(e));u?(l=
|
|
6
|
+
`),i=t.headerPattern.exec(n);if(!i?.groups)throw Error(`Commit header '${n}' doesn't match expected format`);let{type:a,scope:o,bang:s,subject:c}=i.groups,l,u=r.find(e=>t.breakingChangesPattern.test(e));u?(l=k(u,t),r.splice(r.indexOf(u),1)):s&&(l=c);let d=r.findIndex(e=>e.match(t.refActionPattern)??e.match(t.coAuthorPattern)??e.match(t.signerPattern)),[f,p]=d===-1?[r.join(`
|
|
7
7
|
|
|
8
8
|
`),``]:[r.slice(0,d).join(`
|
|
9
9
|
|
|
10
10
|
`),r.slice(d).join(`
|
|
11
11
|
|
|
12
|
-
`)];return{type:a,scope:o||void 0,subject:c,body:f||void 0,breakingChanges:l,footer:p||void 0}},
|
|
13
|
-
`).filter(t=>e.test(t))};var
|
|
12
|
+
`)];return{type:a,scope:o||void 0,subject:c,body:f||void 0,breakingChanges:l,footer:p||void 0}},k=(e,t)=>{let n=t.breakingChangesPattern.exec(e)?.groups?.content;if(!n)throw Error(`Failed to extract breaking changes content from '${e}' using pattern "${t.breakingChangesPattern}"`);let r=[...n.matchAll(t.breakingChangeListPattern)];return r.length?r.map(e=>e[1]):n},A=(e,t)=>{let n=t.some(t=>t.email===e.email&&t.name===e.name);return{...e,hasSignedOff:n,ghLogin:e.name,ghUrl:`https://github.com/${e.name}`}},j=async(e,t)=>await Promise.all([...e.matchAll(t.refPattern)].map(e=>e.groups).filter(e=>t.refActionPattern.test(e.action)).flatMap(e=>[...e.labels.matchAll(t.refLabelPattern)].map(e=>e.groups).filter(e=>!!e.number).map(t=>({action:e.action,owner:t.owner,repo:t.repo,number:t.number})))),M=(e,t)=>{let n=e=>e.toString().padStart(2,`0`),r={YYYY:e.getUTCFullYear().toString(),MM:n(e.getUTCMonth()+1),DD:n(e.getUTCDate()),HH:n(e.getUTCHours()),mm:n(e.getUTCMinutes()),ss:n(e.getUTCSeconds())};return t.replace(/YYYY|MM|DD|HH|mm|ss/g,e=>r[e])},N=/##COMMIT##\n#HASH# (?<hash>.+)?\n#MSG# (?<message>[\s\S]*?)\n#REFS#\s+(?<tagRefs>.+)?\n#AUTHOR-NAME# (?<authorName>.+)?\n#AUTHOR-EMAIL# (?<authorEmail>.+)?\n#AUTHOR-DATE# (?<authorTs>.+)?\n#COMMITTER-NAME# (?<committerName>.+)?\n#COMMITTER-EMAIL# (?<committerEmail>.+)?\n#COMMITTER-DATE# (?<committerTs>.+)?\n#GPGSIG-CODE# (?<gpgSigCode>.+)?\n#GPGSIG-KEYID# (?<gpgSigKeyId>.+)?/g,P=e=>{let t=a(`git remote get-url origin`,{encoding:`utf8`}).trim(),n=e.exec(t);if(!n?.groups)throw Error(`Couldn't parse remote URL: `+t);let{host:r,owner:i,name:o}=n.groups,s=`https://${r}/${i}/${o}`;return{host:r,owner:i,name:o,homepage:s}},F=(e,t)=>{let n=I(),r=L(t),i,o;if(e===`all`)i=n,o=`HEAD`;else if(e===`unreleased`)i=r[0]??n,o=`HEAD`;else if(e===`latest-release`)i=r[1]??n,o=r[0]??`HEAD`;else if(`from`in e||`to`in e){let t=`from`in e?e.from:`firstCommit`;i=t===`firstCommit`?n:t,o=`to`in e?e.to:`HEAD`}else if(`versionTag`in e){let t=r.indexOf(e.versionTag);if(t===-1)throw Error(`Version tag '${e.versionTag}' not found`);i=r[t],o=r[t+1]??`HEAD`}else throw Error(`Invalid commit range provided`);let s=a(`git log "${i}..${o}" --pretty="##COMMIT##%n#HASH# %h%n#MSG# %B%n#REFS# %d%n#AUTHOR-NAME# %an%n#AUTHOR-EMAIL# %ae%n#AUTHOR-DATE# %at%n#COMMITTER-NAME# %cn%n#COMMITTER-EMAIL# %ce%n#COMMITTER-DATE# %ct%n#GPGSIG-CODE# %G?%n#GPGSIG-KEYID# %GK%n"`,{encoding:`utf8`});return[...s.matchAll(N)].map(e=>e.groups)},I=()=>a(`git rev-list --max-parents=0 HEAD`,{encoding:`utf8`}).trim(),L=e=>{let t=a(`git tag --sort=-creatordate`,{encoding:`utf8`});return t.split(`
|
|
13
|
+
`).filter(t=>e.test(t))};let R=!1;const z=e=>R=e,B=(...e)=>{R||console.log(...e)},V=e=>{if(!e.bump)return;let r=e.bump,i=e.context.newVersion;r.forEach(r=>{let a=t(r.filePath,`utf8`),o=a.replace(r.versionPattern,`$1${i}$3`);e.dryRun||n(r.filePath,o,`utf8`),B(`Updated version in '${r.filePath}' to '${i}'`)})};var H=`{{#>header}}
|
|
14
14
|
##   [\` 📦 {{tag}} \`]({{repo.homepage}}/{{#if prevTag}}compare/{{prevTag}}...{{tag}}{{else}}commits/{{tag}}{{/if}})
|
|
15
15
|
|
|
16
16
|
{{/header}}
|
|
@@ -24,4 +24,4 @@ import{existsSync as e,readFileSync as t,writeFileSync as n}from"node:fs";import
|
|
|
24
24
|
#####    🔗 [Full Commit History: {{#if prevTag}}\`{{prevTag}}\` → \`{{tag}}\`]({{repo.homepage}}/compare/{{prevTag}}...{{tag}}){{else}}\`{{tag}}\`]({{repo.homepage}}/commits/{{tag}}){{/if}}  /  _{{date}}_
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
`;const
|
|
27
|
+
`;const U=e=>{if(!e.changelog)return;let t=e.changelog,n=e.context.releases;if(!n)return;let i=L(e.prevReleaseTagPattern);r.registerPartial(t.compiledPartials),r.registerHelper(t.helpers);let a=t.header;n.forEach((t,r)=>{let o=n[r+1],s,c;if(o)s=o.tag,c=K(s,e.prevReleaseTagPattern);else{let n=i.indexOf(t.tag);n===-1?(s=e.context.currentTag,c=K(s,e.prevReleaseTagPattern)):(s=i[n+1],c=s&&K(s,e.prevReleaseTagPattern))}let l={...t,...e.context,prevTag:s,prevVersion:c},u=G(H,l);a+=u}),t.output===`stdout`?(B(`Generated changelog:`),console.log(a)):(B(`Writing changelog to file '${t.output}'`),e.dryRun||W(t.output,a,t.prevReleaseHeaderPattern))},W=(r,i,a)=>{let o=e(r)?t(r,{encoding:`utf8`}):``,s=o.search(a),c=o.slice(s),l=i+c;n(r,l,{encoding:`utf8`})},G=(e,t)=>{let n=r.compile(e);return n(t)},K=(e,t)=>t.exec(e)?.groups?.version,q=e=>{if(!e.commit)return;let t=e.commit;B(`Committing with options:`,t),t.stageAll&&!e.dryRun&&a(`git add -A`,{stdio:`inherit`}),e.dryRun||a(`git commit -m "${t.message}" ${t.signOff?`-s`:``} ${t.gpgSign?`-S`:``} ${t.extraArgs}`,{stdio:`inherit`})},J=e=>{if(!e.tag)return;let t=e.tag;B(`Tagging with options:`,t),e.dryRun||a(`git tag -a ${t.name} -m "${t.message}" ${t.gpgSign?`-s`:``} ${t.force?`-f`:``} ${t.extraArgs}`,{stdio:`inherit`})};async function Y(e){z(!!e.silent||!!e.profile&&!!e[`_${e.profile}`]?.silent);let t=await f(e);V(t),U(t),q(t),J(t)}const X=e=>e;export{Y as default,X as defineConfig};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "relion",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Release workflow helper for Node.js projects.",
|
|
5
5
|
"author": "Kh4f <kh4f.dev@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"lint-staged": "^16.1.6",
|
|
43
43
|
"simple-git-hooks": "^2.13.1",
|
|
44
44
|
"tsdown": "^0.14.2",
|
|
45
|
+
"tsx": "^4.20.5",
|
|
45
46
|
"typescript": "^5.9.2",
|
|
46
47
|
"typescript-eslint": "^8.42.0",
|
|
47
48
|
"vitest": "^3.2.4"
|