@webflow/variables 0.0.0-stage → 2.8.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/CHANGELOG.md ADDED
@@ -0,0 +1,16 @@
1
+ # @webflow/variables
2
+
3
+ ## 2.8.0
4
+
5
+ ### Minor Changes
6
+
7
+ - b84a11f: - Add the new `@webflow/data-types/variables` sub-export with style variable types definitions.
8
+ - Add the new `@webflow/variables` package, which re-exports those types.
9
+ - 70699e7: Add `validateStyleVariableCatalog` and `sanitizeVariableName` to `@webflow/variables`. The validator returns every problem in a library variable catalog with its JSON path, so the CLI and the Webflow server enforce the same rules. It rejects value types that don't match the variable type, unsupported length units, refs to a variable of another type, reference cycles through refs and raw `var()` values, and `var()` fallbacks. `extractVarReferences` is exported for the server.
10
+
11
+ ### Patch Changes
12
+
13
+ - Updated dependencies [ab5c5b8]
14
+ - Updated dependencies [b84a11f]
15
+ - Updated dependencies [348c304]
16
+ - @webflow/data-types@2.8.0
package/LICENCE ADDED
@@ -0,0 +1,23 @@
1
+ MIT license
2
+
3
+ Copyright (c) 2026 Webflow, Inc
4
+
5
+ Permission is hereby granted, free of charge, to any person ob-
6
+ taining a copy of this software and associated documentation
7
+ files (the "Software"), to deal in the Software without restric-
8
+ tion, including without limitation the rights to use, copy, modi-
9
+ fy, merge, publish, distribute, sublicense, and/or sell copies of
10
+ the Software, and to permit persons to whom the Software is fur-
11
+ nished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONIN-
19
+ FRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21
+ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,9 @@
1
- # Temporary Holding Version
1
+ # @webflow/variables
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Tooling for style variables in Webflow code component libraries.
4
+
5
+ ## Entry points
6
+
7
+ - `@webflow/variables`: re-exports the `StyleVariableCatalog` types from `@webflow/data-types/variables`, and adds `validateStyleVariableCatalog`, `sanitizeVariableName` and `extractVarReferences`. The CLI and the Webflow server both validate catalogs with it, and the server uses `extractVarReferences` to rewrite `var(--x)` in raw values.
8
+
9
+ The types live in `@webflow/data-types`, which holds the contracts. This package holds the behavior.
package/dist/index.cjs ADDED
@@ -0,0 +1,2 @@
1
+ 'use strict';var M=/\s/;function m(s){if(s===void 0)return false;let e=s.charCodeAt(0);return (e>=128||e>=97&&e<=122||e>=65&&e<=90||e>=48&&e<=57||s==="_")&&!M.test(s)}function v(s){let e=s.length,t="",a=0,r=e;for(;a<e&&!m(s[a]);a+=1);for(;r>=1&&!m(s[r-1]);r-=1);for(;a<r;a+=1){let i=s[a];if(i==="/"){for(t[t.length-1]==="-"&&(t=t.substring(0,t.length-1));a<r;a+=1)if(i=s[a],m(i)){t+="--"+i;break}}else m(i)?t+=i:t[t.length-1]!=="-"&&(t+="-");}return t.toLowerCase()}var g="base",_="webflow-collection";var R=/var\(\s*(--[^\s,)(]+)/gi,F=/var\(\s*(--[^\s,)(]+)\s*,/gi;function h(s){return [...s.matchAll(R)].map(e=>e[1]).filter(e=>e!==void 0)}function V(s){return [...s.matchAll(F)].map(e=>e[1]).filter(e=>e!==void 0)}var A=/^--[A-Za-z0-9_\-\u0080-\uFFFF]+$/,E=/^(?:--|-?[A-Za-z_\u0080-\uFFFF])[A-Za-z0-9_\-\u0080-\uFFFF]*$/,L={px:true,em:true,rem:true,vh:true,vw:true,dvh:true,dvw:true,lvh:true,lvw:true,svh:true,svw:true,vmax:true,vmin:true,ch:true};function $(s,e,t,a){let r=new Map,i=[];return s.forEach((n,d)=>{let l=e(n),o=r.get(l);o===void 0?r.set(l,d):i.push({path:t(d),message:a(l,o,d)});}),i}function b(s){return s.variables.flatMap((e,t)=>[{path:`variables[${t}].value`,value:e.value,variableIndex:t},...Object.entries(e.modes??{}).map(([a,r])=>({path:`variables[${t}].modes.${a}`,value:r,variableIndex:t}))])}function x(s){let e=new Map;return s.variables.forEach((t,a)=>{e.has(t.id)||e.set(t.id,{variable:t,index:a});}),e}var w=50;function T(s){let e=x(s),t=new Map;for(let{value:l,variableIndex:o}of b(s)){let c=s.variables[o]?.id;if(c===void 0||e.get(c)?.index!==o)continue;let u=l.type==="ref"?l.value.variableId===c?[]:[l.value.variableId]:l.type==="raw"?h(l.value):[],p=t.get(c)??new Set;u.filter(f=>e.has(f)).forEach(f=>p.add(f)),t.set(c,p);}let a=[],r=new Set,i=new Map,n=false,d=l=>{let o=[],c=u=>{i.set(u,"visiting"),o.push({id:u,targets:(t.get(u)??new Set).values()});};for(c(l);o.length>0&&!n;){let u=o[o.length-1],p=u.targets.next();if(p.done){i.set(u.id,"done"),o.pop();continue}let f=p.value,S=i.get(f);if(S==="visiting"){let C=o.slice(o.findIndex(y=>y.id===f)).map(y=>y.id),I=[...C].sort().join(`
2
+ `);if(!r.has(I)){if(r.size===w){n=true,a.push({path:"variables",message:`More than ${w} circular references, the rest are not shown`});continue}r.add(I),a.push({path:`variables[${e.get(f)?.index}]`,message:`Circular reference: ${[...C,f].join(" \u2192 ")}`});}}else S===void 0&&c(f);}};for(let l of e.keys()){if(n)break;i.has(l)||d(l);}return a}var k=[({variables:s})=>s.flatMap((e,t)=>A.test(e.id)?[]:[{path:`variables[${t}].id`,message:`"${e.id}" is not a valid CSS custom property name (it must start with "--")`}]),({variables:s})=>$(s,e=>e.id,e=>`variables[${e}].id`,(e,t,a)=>`Duplicate variable id "${e}" at variables[${t}] and variables[${a}]`),({collections:s})=>s.flatMap((e,t)=>[...e.id===g?[{path:`collections[${t}].id`,message:`Collection id "${g}" is reserved`}]:E.test(e.id)?[]:[{path:`collections[${t}].id`,message:`"${e.id}" is not a valid CSS identifier`}],..."isDefault"in e?[{path:`collections[${t}].isDefault`,message:"Library collections cannot set isDefault"}]:[]]),({collections:s})=>$(s,e=>e.id,e=>`collections[${e}].id`,(e,t,a)=>`Duplicate collection id "${e}" at collections[${t}] and collections[${a}]`),({collections:s})=>s.flatMap((e,t)=>[...e.modes.flatMap((a,r)=>{let i=`collections[${t}].modes[${r}].id`;return a.id===g?[{path:i,message:`Mode id "${g}" is reserved`}]:E.test(a.id)?[]:[{path:i,message:`"${a.id}" is not a valid CSS identifier`}]}),...$(e.modes,a=>a.id,a=>`collections[${t}].modes[${a}].id`,(a,r,i)=>`Duplicate mode id "${a}" at modes[${r}] and modes[${i}] of collection "${e.id}"`)]),({collections:s,variables:e})=>{let t=[],a=(i,n)=>{let d=new Map,l=new Set;for(let{item:o,path:c}of i){if(l.has(o.id))continue;l.add(o.id);let u=o.name??o.id,p=v(u);if(p===""){t.push({path:c,message:`"${u}" has no characters usable in a CSS variable name`});continue}let f=d.get(p);f===void 0?d.set(p,{label:u,path:c}):t.push({path:c,message:`"${f.label}" at ${f.path} and "${u}" at ${c} both become "${p}" ${n}`});}};a(s.map((i,n)=>({item:i,path:`collections[${n}]`})),"in this library"),s.forEach((i,n)=>a(i.modes.map((d,l)=>({item:d,path:`collections[${n}].modes[${l}]`})),`in collection "${i.id}"`));let r=new Map;return e.forEach((i,n)=>{let d=i.collectionId??g,l=r.get(d)??[];l.push({item:i,path:`variables[${n}]`}),r.set(d,l);}),r.forEach((i,n)=>a(i,`in collection "${n}"`)),t},({collections:s,variables:e})=>{let t=new Map(s.map(a=>[a.id,new Set(a.modes.map(r=>r.id))]));return e.flatMap((a,r)=>{let i=a.collectionId===void 0?new Set:t.get(a.collectionId);return i===void 0?[{path:`variables[${r}].collectionId`,message:a.collectionId===g?`Collection id "${g}" is reserved; omit the id for the implicit base collection`:`Collection "${a.collectionId}" is not declared`}]:Object.keys(a.modes??{}).filter(n=>!i.has(n)).map(n=>({path:`variables[${r}].modes.${n}`,message:`Mode "${n}" is not declared in collection "${a.collectionId??g}"`}))})},s=>{let e=new Set(s.variables.map(t=>t.id));return b(s).flatMap(({path:t,value:a,variableIndex:r})=>{if(a.type!=="ref")return [];let i=a.value.variableId;return i===s.variables[r]?.id?[{path:t,message:`Variable "${i}" references itself`}]:e.has(i)?[]:[{path:t,message:`Referenced variable "${i}" does not exist`}]})},s=>{let e=new Set(s.variables.map(t=>t.id));return b(s).flatMap(({path:t,value:a})=>a.type!=="raw"?[]:h(a.value).filter(r=>!e.has(r)).map(r=>({path:t,message:`var(${r}) does not name a variable in this library`})))},s=>b(s).flatMap(({path:e,value:t})=>t.type!=="raw"?[]:V(t.value).map(a=>({path:e,message:`var(${a}, \u2026) has a fallback; library variables do not support var() fallbacks`}))),s=>b(s).flatMap(({path:e,value:t,variableIndex:a})=>{let r=s.variables[a]?.type;return t.type==="ref"||t.type==="raw"?[]:t.type===r?[]:[{path:e,message:`A "${t.type}" value does not match the variable type "${r}"`}]}),s=>b(s).flatMap(({path:e,value:t})=>t.type!=="length"?[]:Object.hasOwn(L,t.value.unit)?[]:[{path:e,message:`"${t.value.unit}" is not a supported length unit`}]),s=>{let e=x(s);return b(s).flatMap(({path:t,value:a,variableIndex:r})=>{if(a.type!=="ref")return [];let i=s.variables[r],n=e.get(a.value.variableId)?.variable;return i===void 0||n===void 0?[]:n.id===i.id?[]:n.type===i.type?[]:[{path:t,message:`"${i.id}" is a ${i.type} variable but references "${n.id}", a ${n.type} variable`}]})},T];function D(s){return k.flatMap(e=>e(s))}exports.RESERVED_BASE_ID=g;exports.WEBFLOW_COLLECTION_AT_RULE=_;exports.extractVarReferences=h;exports.sanitizeVariableName=v;exports.validateStyleVariableCatalog=D;
@@ -0,0 +1,41 @@
1
+ import { StyleVariableCatalog } from '@webflow/data-types/variables';
2
+ export * from '@webflow/data-types/variables';
3
+
4
+ /**
5
+ * Port of `sanitizeVariableName` from the Webflow style-variable utilities.
6
+ * Replaces non-permitted characters with single dashes, turns `/` group
7
+ * separators into `--`, trims, and lowercases. `sanitize.fixture.json` is the
8
+ * parity contract with the server copy.
9
+ */
10
+ declare function sanitizeVariableName(name: string): string;
11
+
12
+ type StyleVariableCatalogIssue = {
13
+ /** JSON path into the catalog, e.g. `variables[1].id`. */
14
+ path: string;
15
+ message: string;
16
+ };
17
+ /**
18
+ * Checks a library variable catalog against the share contract. Returns every
19
+ * issue found, each with a JSON path; an empty array means the catalog is
20
+ * valid. Shared by the CLI (before share) and the server (on receive).
21
+ *
22
+ * The catalog must already match the `StyleVariableCatalog` shape: the server
23
+ * parses the request body with its schema first. Color syntax is checked by
24
+ * the server, not here.
25
+ */
26
+ declare function validateStyleVariableCatalog(catalog: StyleVariableCatalog): StyleVariableCatalogIssue[];
27
+
28
+ /** Id of the implicit collection. Reserved as a collection id and as a mode id. */
29
+ declare const RESERVED_BASE_ID = "base";
30
+ /** PostCSS at-rule name, without the leading `@`. */
31
+ declare const WEBFLOW_COLLECTION_AT_RULE = "webflow-collection";
32
+
33
+ /**
34
+ * Returns the custom property names referenced by `var(--x)` in a raw value,
35
+ * in order of appearance, including nested references. The server rewrites
36
+ * these to variable placeholders, so it must match them the same way the
37
+ * validator does.
38
+ */
39
+ declare function extractVarReferences(raw: string): string[];
40
+
41
+ export { RESERVED_BASE_ID, type StyleVariableCatalogIssue, WEBFLOW_COLLECTION_AT_RULE, extractVarReferences, sanitizeVariableName, validateStyleVariableCatalog };
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ var M=/\s/;function m(s){if(s===void 0)return false;let e=s.charCodeAt(0);return (e>=128||e>=97&&e<=122||e>=65&&e<=90||e>=48&&e<=57||s==="_")&&!M.test(s)}function v(s){let e=s.length,t="",a=0,r=e;for(;a<e&&!m(s[a]);a+=1);for(;r>=1&&!m(s[r-1]);r-=1);for(;a<r;a+=1){let i=s[a];if(i==="/"){for(t[t.length-1]==="-"&&(t=t.substring(0,t.length-1));a<r;a+=1)if(i=s[a],m(i)){t+="--"+i;break}}else m(i)?t+=i:t[t.length-1]!=="-"&&(t+="-");}return t.toLowerCase()}var g="base",_="webflow-collection";var R=/var\(\s*(--[^\s,)(]+)/gi,F=/var\(\s*(--[^\s,)(]+)\s*,/gi;function h(s){return [...s.matchAll(R)].map(e=>e[1]).filter(e=>e!==void 0)}function V(s){return [...s.matchAll(F)].map(e=>e[1]).filter(e=>e!==void 0)}var A=/^--[A-Za-z0-9_\-\u0080-\uFFFF]+$/,E=/^(?:--|-?[A-Za-z_\u0080-\uFFFF])[A-Za-z0-9_\-\u0080-\uFFFF]*$/,L={px:true,em:true,rem:true,vh:true,vw:true,dvh:true,dvw:true,lvh:true,lvw:true,svh:true,svw:true,vmax:true,vmin:true,ch:true};function $(s,e,t,a){let r=new Map,i=[];return s.forEach((n,d)=>{let l=e(n),o=r.get(l);o===void 0?r.set(l,d):i.push({path:t(d),message:a(l,o,d)});}),i}function b(s){return s.variables.flatMap((e,t)=>[{path:`variables[${t}].value`,value:e.value,variableIndex:t},...Object.entries(e.modes??{}).map(([a,r])=>({path:`variables[${t}].modes.${a}`,value:r,variableIndex:t}))])}function x(s){let e=new Map;return s.variables.forEach((t,a)=>{e.has(t.id)||e.set(t.id,{variable:t,index:a});}),e}var w=50;function T(s){let e=x(s),t=new Map;for(let{value:l,variableIndex:o}of b(s)){let c=s.variables[o]?.id;if(c===void 0||e.get(c)?.index!==o)continue;let u=l.type==="ref"?l.value.variableId===c?[]:[l.value.variableId]:l.type==="raw"?h(l.value):[],p=t.get(c)??new Set;u.filter(f=>e.has(f)).forEach(f=>p.add(f)),t.set(c,p);}let a=[],r=new Set,i=new Map,n=false,d=l=>{let o=[],c=u=>{i.set(u,"visiting"),o.push({id:u,targets:(t.get(u)??new Set).values()});};for(c(l);o.length>0&&!n;){let u=o[o.length-1],p=u.targets.next();if(p.done){i.set(u.id,"done"),o.pop();continue}let f=p.value,S=i.get(f);if(S==="visiting"){let C=o.slice(o.findIndex(y=>y.id===f)).map(y=>y.id),I=[...C].sort().join(`
2
+ `);if(!r.has(I)){if(r.size===w){n=true,a.push({path:"variables",message:`More than ${w} circular references, the rest are not shown`});continue}r.add(I),a.push({path:`variables[${e.get(f)?.index}]`,message:`Circular reference: ${[...C,f].join(" \u2192 ")}`});}}else S===void 0&&c(f);}};for(let l of e.keys()){if(n)break;i.has(l)||d(l);}return a}var k=[({variables:s})=>s.flatMap((e,t)=>A.test(e.id)?[]:[{path:`variables[${t}].id`,message:`"${e.id}" is not a valid CSS custom property name (it must start with "--")`}]),({variables:s})=>$(s,e=>e.id,e=>`variables[${e}].id`,(e,t,a)=>`Duplicate variable id "${e}" at variables[${t}] and variables[${a}]`),({collections:s})=>s.flatMap((e,t)=>[...e.id===g?[{path:`collections[${t}].id`,message:`Collection id "${g}" is reserved`}]:E.test(e.id)?[]:[{path:`collections[${t}].id`,message:`"${e.id}" is not a valid CSS identifier`}],..."isDefault"in e?[{path:`collections[${t}].isDefault`,message:"Library collections cannot set isDefault"}]:[]]),({collections:s})=>$(s,e=>e.id,e=>`collections[${e}].id`,(e,t,a)=>`Duplicate collection id "${e}" at collections[${t}] and collections[${a}]`),({collections:s})=>s.flatMap((e,t)=>[...e.modes.flatMap((a,r)=>{let i=`collections[${t}].modes[${r}].id`;return a.id===g?[{path:i,message:`Mode id "${g}" is reserved`}]:E.test(a.id)?[]:[{path:i,message:`"${a.id}" is not a valid CSS identifier`}]}),...$(e.modes,a=>a.id,a=>`collections[${t}].modes[${a}].id`,(a,r,i)=>`Duplicate mode id "${a}" at modes[${r}] and modes[${i}] of collection "${e.id}"`)]),({collections:s,variables:e})=>{let t=[],a=(i,n)=>{let d=new Map,l=new Set;for(let{item:o,path:c}of i){if(l.has(o.id))continue;l.add(o.id);let u=o.name??o.id,p=v(u);if(p===""){t.push({path:c,message:`"${u}" has no characters usable in a CSS variable name`});continue}let f=d.get(p);f===void 0?d.set(p,{label:u,path:c}):t.push({path:c,message:`"${f.label}" at ${f.path} and "${u}" at ${c} both become "${p}" ${n}`});}};a(s.map((i,n)=>({item:i,path:`collections[${n}]`})),"in this library"),s.forEach((i,n)=>a(i.modes.map((d,l)=>({item:d,path:`collections[${n}].modes[${l}]`})),`in collection "${i.id}"`));let r=new Map;return e.forEach((i,n)=>{let d=i.collectionId??g,l=r.get(d)??[];l.push({item:i,path:`variables[${n}]`}),r.set(d,l);}),r.forEach((i,n)=>a(i,`in collection "${n}"`)),t},({collections:s,variables:e})=>{let t=new Map(s.map(a=>[a.id,new Set(a.modes.map(r=>r.id))]));return e.flatMap((a,r)=>{let i=a.collectionId===void 0?new Set:t.get(a.collectionId);return i===void 0?[{path:`variables[${r}].collectionId`,message:a.collectionId===g?`Collection id "${g}" is reserved; omit the id for the implicit base collection`:`Collection "${a.collectionId}" is not declared`}]:Object.keys(a.modes??{}).filter(n=>!i.has(n)).map(n=>({path:`variables[${r}].modes.${n}`,message:`Mode "${n}" is not declared in collection "${a.collectionId??g}"`}))})},s=>{let e=new Set(s.variables.map(t=>t.id));return b(s).flatMap(({path:t,value:a,variableIndex:r})=>{if(a.type!=="ref")return [];let i=a.value.variableId;return i===s.variables[r]?.id?[{path:t,message:`Variable "${i}" references itself`}]:e.has(i)?[]:[{path:t,message:`Referenced variable "${i}" does not exist`}]})},s=>{let e=new Set(s.variables.map(t=>t.id));return b(s).flatMap(({path:t,value:a})=>a.type!=="raw"?[]:h(a.value).filter(r=>!e.has(r)).map(r=>({path:t,message:`var(${r}) does not name a variable in this library`})))},s=>b(s).flatMap(({path:e,value:t})=>t.type!=="raw"?[]:V(t.value).map(a=>({path:e,message:`var(${a}, \u2026) has a fallback; library variables do not support var() fallbacks`}))),s=>b(s).flatMap(({path:e,value:t,variableIndex:a})=>{let r=s.variables[a]?.type;return t.type==="ref"||t.type==="raw"?[]:t.type===r?[]:[{path:e,message:`A "${t.type}" value does not match the variable type "${r}"`}]}),s=>b(s).flatMap(({path:e,value:t})=>t.type!=="length"?[]:Object.hasOwn(L,t.value.unit)?[]:[{path:e,message:`"${t.value.unit}" is not a supported length unit`}]),s=>{let e=x(s);return b(s).flatMap(({path:t,value:a,variableIndex:r})=>{if(a.type!=="ref")return [];let i=s.variables[r],n=e.get(a.value.variableId)?.variable;return i===void 0||n===void 0?[]:n.id===i.id?[]:n.type===i.type?[]:[{path:t,message:`"${i.id}" is a ${i.type} variable but references "${n.id}", a ${n.type} variable`}]})},T];function D(s){return k.flatMap(e=>e(s))}export{g as RESERVED_BASE_ID,_ as WEBFLOW_COLLECTION_AT_RULE,h as extractVarReferences,v as sanitizeVariableName,D as validateStyleVariableCatalog};
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@webflow/variables",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "2.8.0",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "sideEffects": false,
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./package.json": "./package.json"
18
+ },
19
+ "files": [
20
+ "CHANGELOG.md",
21
+ "dist/**/*"
22
+ ],
23
+ "scripts": {
24
+ "build": "tsup",
25
+ "lint": "eslint .",
26
+ "test": "jest tests --ci",
27
+ "test:unit": "jest tests --ci --testPathIgnorePatterns=/node_modules/ --testPathIgnorePatterns=package-entry.test",
28
+ "test:local": "jest tests",
29
+ "type-check": "tsc -p tsconfig.typecheck.json"
30
+ },
31
+ "dependencies": {
32
+ "@webflow/data-types": "^2.8.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/jest": "^30.0.0",
36
+ "eslint-config-custom": "*",
37
+ "jest": "^30.4.2",
38
+ "jest-config-custom": "*",
39
+ "tsconfig": "*",
40
+ "tsup": "^8.4.0",
41
+ "typescript": "^5.9.3"
42
+ }
43
+ }