@augmenting-integrations/eslint-plugin 4.0.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/README.md ADDED
@@ -0,0 +1,58 @@
1
+ # @augmenting-integrations/eslint-plugin
2
+
3
+ ESLint rules for Augmenting Integrations Next.js apps under path-routing.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pnpm add -D @augmenting-integrations/eslint-plugin
9
+ ```
10
+
11
+ ## Use (flat config, ESLint 9+)
12
+
13
+ ```js
14
+ // eslint.config.mjs
15
+ import auginiPlugin from "@augmenting-integrations/eslint-plugin";
16
+
17
+ export default [
18
+ // ... your other configs
19
+ {
20
+ plugins: { "@augmenting-integrations": auginiPlugin },
21
+ rules: {
22
+ "@augmenting-integrations/no-hardcoded-internal-paths": [
23
+ "error",
24
+ { basePath: "/leads-marketplace" },
25
+ ],
26
+ },
27
+ },
28
+ ];
29
+ ```
30
+
31
+ For an apex app with no basePath, omit the option (or pass `basePath: "/"`) — the rule becomes a no-op.
32
+
33
+ ## Rules
34
+
35
+ ### `no-hardcoded-internal-paths`
36
+
37
+ Flags hardcoding the current app's basePath in `href` attributes. Under Next.js `basePath`, `<Link>` prepends it automatically; a raw `<a>` for an in-app link is wrong and a `<Link href>` that includes the basePath produces a doubled-prefix path.
38
+
39
+ ```jsx
40
+ // App with basePath: "/leads-marketplace"
41
+
42
+ // ❌ Flagged: <Link> would render /leads-marketplace/leads-marketplace/foo
43
+ <Link href="/leads-marketplace/foo">In-app</Link>
44
+
45
+ // ❌ Flagged: raw <a> won't get basePath, so it's wrong for in-app
46
+ <a href="/leads-marketplace/foo">In-app</a>
47
+
48
+ // ✅ Allowed: <Link> renders /leads-marketplace/foo
49
+ <Link href="/foo">In-app</Link>
50
+
51
+ // ✅ Allowed: cross-app link to peer app's basePath; raw <a> is correct here
52
+ <a href="/quotes/dashboard">Cross-app</a>
53
+
54
+ // ✅ Allowed: external URL
55
+ <a href="https://example.com/external">External</a>
56
+ ```
57
+
58
+ The rule is configurable per-app: each tenant repo passes its own `basePath` in `eslint.config.mjs`.
package/dist/index.cjs ADDED
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ default: () => index_default
24
+ });
25
+ module.exports = __toCommonJS(index_exports);
26
+
27
+ // src/rules/no-hardcoded-internal-paths.ts
28
+ var rule = {
29
+ meta: {
30
+ type: "problem",
31
+ docs: {
32
+ description: "Disallow hardcoding the current app's basePath in href attributes. Under Next.js basePath, <Link> prepends it automatically; raw <a> with the basePath in it produces a doubled-prefix path.",
33
+ recommended: true
34
+ },
35
+ schema: [
36
+ {
37
+ type: "object",
38
+ properties: {
39
+ basePath: {
40
+ type: "string",
41
+ description: "The current app's basePath, e.g. '/leads-marketplace'. The rule is a no-op for apex apps (basePath unset or '/')."
42
+ }
43
+ },
44
+ additionalProperties: false
45
+ }
46
+ ],
47
+ messages: {
48
+ hardcodedBasePath: "Hardcoded basePath '{{ basePath }}' in href '{{ path }}'. For in-app navigation, use <Link href='{{ stripped }}'> \u2014 Next.js prepends '{{ basePath }}' automatically. For cross-app navigation, the href should NOT start with '{{ basePath }}'."
49
+ }
50
+ },
51
+ create(context) {
52
+ const options = context.options[0] ?? {};
53
+ const basePath = options.basePath;
54
+ if (!basePath || basePath === "/" || basePath === "") {
55
+ return {};
56
+ }
57
+ const prefix = basePath.endsWith("/") ? basePath : basePath + "/";
58
+ return {
59
+ JSXAttribute(node) {
60
+ const attr = node;
61
+ if (attr.name?.name !== "href") return;
62
+ const value = attr.value;
63
+ if (!value || value.type !== "Literal") return;
64
+ const v = value.value;
65
+ if (typeof v !== "string") return;
66
+ if (v !== basePath && !v.startsWith(prefix)) return;
67
+ const stripped = v === basePath ? "/" : "/" + v.slice(prefix.length);
68
+ context.report({
69
+ node: value,
70
+ messageId: "hardcodedBasePath",
71
+ data: { basePath, path: v, stripped }
72
+ });
73
+ }
74
+ };
75
+ }
76
+ };
77
+ var no_hardcoded_internal_paths_default = rule;
78
+
79
+ // src/index.ts
80
+ var plugin = {
81
+ meta: {
82
+ name: "@augmenting-integrations/eslint-plugin",
83
+ version: "4.0.1"
84
+ },
85
+ rules: {
86
+ "no-hardcoded-internal-paths": no_hardcoded_internal_paths_default
87
+ },
88
+ configs: {}
89
+ };
90
+ plugin.configs = {
91
+ recommended: {
92
+ plugins: { "@augmenting-integrations": plugin },
93
+ rules: {
94
+ "@augmenting-integrations/no-hardcoded-internal-paths": "error"
95
+ }
96
+ }
97
+ };
98
+ var index_default = plugin;
99
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/rules/no-hardcoded-internal-paths.ts"],"sourcesContent":["// ESLint plugin for Augmenting Integrations Next.js apps.\n//\n// The flagship rule, `no-hardcoded-internal-paths`, prevents the most common\n// subpath-routing failure mode: a contributor writes `<a href=\"/leads/foo\">`\n// thinking it'll be a normal link, but under Next.js basePath=\"/leads\" that\n// renders as `/leads/leads/foo` and 404s. Worse, in dev (basePath often \"/\")\n// it works fine, so the bug only surfaces in deployed environments.\n//\n// The fix in source is to use <Link> from next/link or useRouter().push(),\n// both of which are basePath-aware. This rule flags the raw-anchor pattern\n// at lint time so it never reaches review.\n\nimport noHardcodedInternalPaths from \"./rules/no-hardcoded-internal-paths.js\";\n\nconst plugin = {\n meta: {\n name: \"@augmenting-integrations/eslint-plugin\",\n version: \"4.0.1\",\n },\n rules: {\n \"no-hardcoded-internal-paths\": noHardcodedInternalPaths,\n },\n configs: {} as Record<string, unknown>,\n};\n\n// Recommended config (flat config style — ESLint 9+).\nplugin.configs = {\n recommended: {\n plugins: { \"@augmenting-integrations\": plugin },\n rules: {\n \"@augmenting-integrations/no-hardcoded-internal-paths\": \"error\",\n },\n },\n};\n\nexport default plugin;\n","import type { Rule } from \"eslint\";\n\n// Renamed conceptually: the bug isn't \"hardcoded internal paths\" — raw\n// <a href=\"/peer-app\"> is correct for cross-app navigation under path-routing\n// (next/link's <Link> would prepend basePath, which we don't want for cross-app).\n// The actual bug is hardcoding THE CURRENT APP'S basePath in any href, because\n// <Link> prepends it automatically and <a> for an in-app link is wrong anyway.\n\ninterface Options {\n basePath?: string;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow hardcoding the current app's basePath in href attributes. Under Next.js basePath, <Link> prepends it automatically; raw <a> with the basePath in it produces a doubled-prefix path.\",\n recommended: true,\n },\n schema: [\n {\n type: \"object\",\n properties: {\n basePath: {\n type: \"string\",\n description:\n \"The current app's basePath, e.g. '/leads-marketplace'. The rule is a no-op for apex apps (basePath unset or '/').\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n hardcodedBasePath:\n \"Hardcoded basePath '{{ basePath }}' in href '{{ path }}'. For in-app navigation, use <Link href='{{ stripped }}'> — Next.js prepends '{{ basePath }}' automatically. For cross-app navigation, the href should NOT start with '{{ basePath }}'.\",\n },\n },\n create(context) {\n const options: Options = (context.options[0] as Options) ?? {};\n const basePath = options.basePath;\n if (!basePath || basePath === \"/\" || basePath === \"\") {\n // Apex app or unconfigured — rule is a no-op.\n return {};\n }\n const prefix = basePath.endsWith(\"/\") ? basePath : basePath + \"/\";\n\n return {\n JSXAttribute(node: Rule.Node) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const attr = node as any;\n if (attr.name?.name !== \"href\") return;\n\n const value = attr.value;\n if (!value || value.type !== \"Literal\") return;\n const v = value.value;\n if (typeof v !== \"string\") return;\n if (v !== basePath && !v.startsWith(prefix)) return;\n\n const stripped = v === basePath ? \"/\" : \"/\" + v.slice(prefix.length);\n context.report({\n node: value,\n messageId: \"hardcodedBasePath\",\n data: { basePath, path: v, stripped },\n });\n },\n } as Rule.RuleListener;\n },\n};\n\nexport default rule;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACYA,IAAM,OAAwB;AAAA,EAC5B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,MACF,aAAa;AAAA,IACf;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,UAAU;AAAA,YACR,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,mBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,UAAM,UAAoB,QAAQ,QAAQ,CAAC,KAAiB,CAAC;AAC7D,UAAM,WAAW,QAAQ;AACzB,QAAI,CAAC,YAAY,aAAa,OAAO,aAAa,IAAI;AAEpD,aAAO,CAAC;AAAA,IACV;AACA,UAAM,SAAS,SAAS,SAAS,GAAG,IAAI,WAAW,WAAW;AAE9D,WAAO;AAAA,MACL,aAAa,MAAiB;AAE5B,cAAM,OAAO;AACb,YAAI,KAAK,MAAM,SAAS,OAAQ;AAEhC,cAAM,QAAQ,KAAK;AACnB,YAAI,CAAC,SAAS,MAAM,SAAS,UAAW;AACxC,cAAM,IAAI,MAAM;AAChB,YAAI,OAAO,MAAM,SAAU;AAC3B,YAAI,MAAM,YAAY,CAAC,EAAE,WAAW,MAAM,EAAG;AAE7C,cAAM,WAAW,MAAM,WAAW,MAAM,MAAM,EAAE,MAAM,OAAO,MAAM;AACnE,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,UACN,WAAW;AAAA,UACX,MAAM,EAAE,UAAU,MAAM,GAAG,SAAS;AAAA,QACtC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,sCAAQ;;;ADxDf,IAAM,SAAS;AAAA,EACb,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA,OAAO;AAAA,IACL,+BAA+B;AAAA,EACjC;AAAA,EACA,SAAS,CAAC;AACZ;AAGA,OAAO,UAAU;AAAA,EACf,aAAa;AAAA,IACX,SAAS,EAAE,4BAA4B,OAAO;AAAA,IAC9C,OAAO;AAAA,MACL,wDAAwD;AAAA,IAC1D;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":[]}
package/dist/index.js ADDED
@@ -0,0 +1,76 @@
1
+ // src/rules/no-hardcoded-internal-paths.ts
2
+ var rule = {
3
+ meta: {
4
+ type: "problem",
5
+ docs: {
6
+ description: "Disallow hardcoding the current app's basePath in href attributes. Under Next.js basePath, <Link> prepends it automatically; raw <a> with the basePath in it produces a doubled-prefix path.",
7
+ recommended: true
8
+ },
9
+ schema: [
10
+ {
11
+ type: "object",
12
+ properties: {
13
+ basePath: {
14
+ type: "string",
15
+ description: "The current app's basePath, e.g. '/leads-marketplace'. The rule is a no-op for apex apps (basePath unset or '/')."
16
+ }
17
+ },
18
+ additionalProperties: false
19
+ }
20
+ ],
21
+ messages: {
22
+ hardcodedBasePath: "Hardcoded basePath '{{ basePath }}' in href '{{ path }}'. For in-app navigation, use <Link href='{{ stripped }}'> \u2014 Next.js prepends '{{ basePath }}' automatically. For cross-app navigation, the href should NOT start with '{{ basePath }}'."
23
+ }
24
+ },
25
+ create(context) {
26
+ const options = context.options[0] ?? {};
27
+ const basePath = options.basePath;
28
+ if (!basePath || basePath === "/" || basePath === "") {
29
+ return {};
30
+ }
31
+ const prefix = basePath.endsWith("/") ? basePath : basePath + "/";
32
+ return {
33
+ JSXAttribute(node) {
34
+ const attr = node;
35
+ if (attr.name?.name !== "href") return;
36
+ const value = attr.value;
37
+ if (!value || value.type !== "Literal") return;
38
+ const v = value.value;
39
+ if (typeof v !== "string") return;
40
+ if (v !== basePath && !v.startsWith(prefix)) return;
41
+ const stripped = v === basePath ? "/" : "/" + v.slice(prefix.length);
42
+ context.report({
43
+ node: value,
44
+ messageId: "hardcodedBasePath",
45
+ data: { basePath, path: v, stripped }
46
+ });
47
+ }
48
+ };
49
+ }
50
+ };
51
+ var no_hardcoded_internal_paths_default = rule;
52
+
53
+ // src/index.ts
54
+ var plugin = {
55
+ meta: {
56
+ name: "@augmenting-integrations/eslint-plugin",
57
+ version: "4.0.1"
58
+ },
59
+ rules: {
60
+ "no-hardcoded-internal-paths": no_hardcoded_internal_paths_default
61
+ },
62
+ configs: {}
63
+ };
64
+ plugin.configs = {
65
+ recommended: {
66
+ plugins: { "@augmenting-integrations": plugin },
67
+ rules: {
68
+ "@augmenting-integrations/no-hardcoded-internal-paths": "error"
69
+ }
70
+ }
71
+ };
72
+ var index_default = plugin;
73
+ export {
74
+ index_default as default
75
+ };
76
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/rules/no-hardcoded-internal-paths.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from \"eslint\";\n\n// Renamed conceptually: the bug isn't \"hardcoded internal paths\" — raw\n// <a href=\"/peer-app\"> is correct for cross-app navigation under path-routing\n// (next/link's <Link> would prepend basePath, which we don't want for cross-app).\n// The actual bug is hardcoding THE CURRENT APP'S basePath in any href, because\n// <Link> prepends it automatically and <a> for an in-app link is wrong anyway.\n\ninterface Options {\n basePath?: string;\n}\n\nconst rule: Rule.RuleModule = {\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow hardcoding the current app's basePath in href attributes. Under Next.js basePath, <Link> prepends it automatically; raw <a> with the basePath in it produces a doubled-prefix path.\",\n recommended: true,\n },\n schema: [\n {\n type: \"object\",\n properties: {\n basePath: {\n type: \"string\",\n description:\n \"The current app's basePath, e.g. '/leads-marketplace'. The rule is a no-op for apex apps (basePath unset or '/').\",\n },\n },\n additionalProperties: false,\n },\n ],\n messages: {\n hardcodedBasePath:\n \"Hardcoded basePath '{{ basePath }}' in href '{{ path }}'. For in-app navigation, use <Link href='{{ stripped }}'> — Next.js prepends '{{ basePath }}' automatically. For cross-app navigation, the href should NOT start with '{{ basePath }}'.\",\n },\n },\n create(context) {\n const options: Options = (context.options[0] as Options) ?? {};\n const basePath = options.basePath;\n if (!basePath || basePath === \"/\" || basePath === \"\") {\n // Apex app or unconfigured — rule is a no-op.\n return {};\n }\n const prefix = basePath.endsWith(\"/\") ? basePath : basePath + \"/\";\n\n return {\n JSXAttribute(node: Rule.Node) {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const attr = node as any;\n if (attr.name?.name !== \"href\") return;\n\n const value = attr.value;\n if (!value || value.type !== \"Literal\") return;\n const v = value.value;\n if (typeof v !== \"string\") return;\n if (v !== basePath && !v.startsWith(prefix)) return;\n\n const stripped = v === basePath ? \"/\" : \"/\" + v.slice(prefix.length);\n context.report({\n node: value,\n messageId: \"hardcodedBasePath\",\n data: { basePath, path: v, stripped },\n });\n },\n } as Rule.RuleListener;\n },\n};\n\nexport default rule;\n","// ESLint plugin for Augmenting Integrations Next.js apps.\n//\n// The flagship rule, `no-hardcoded-internal-paths`, prevents the most common\n// subpath-routing failure mode: a contributor writes `<a href=\"/leads/foo\">`\n// thinking it'll be a normal link, but under Next.js basePath=\"/leads\" that\n// renders as `/leads/leads/foo` and 404s. Worse, in dev (basePath often \"/\")\n// it works fine, so the bug only surfaces in deployed environments.\n//\n// The fix in source is to use <Link> from next/link or useRouter().push(),\n// both of which are basePath-aware. This rule flags the raw-anchor pattern\n// at lint time so it never reaches review.\n\nimport noHardcodedInternalPaths from \"./rules/no-hardcoded-internal-paths.js\";\n\nconst plugin = {\n meta: {\n name: \"@augmenting-integrations/eslint-plugin\",\n version: \"4.0.1\",\n },\n rules: {\n \"no-hardcoded-internal-paths\": noHardcodedInternalPaths,\n },\n configs: {} as Record<string, unknown>,\n};\n\n// Recommended config (flat config style — ESLint 9+).\nplugin.configs = {\n recommended: {\n plugins: { \"@augmenting-integrations\": plugin },\n rules: {\n \"@augmenting-integrations/no-hardcoded-internal-paths\": \"error\",\n },\n },\n};\n\nexport default plugin;\n"],"mappings":";AAYA,IAAM,OAAwB;AAAA,EAC5B,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,aACE;AAAA,MACF,aAAa;AAAA,IACf;AAAA,IACA,QAAQ;AAAA,MACN;AAAA,QACE,MAAM;AAAA,QACN,YAAY;AAAA,UACV,UAAU;AAAA,YACR,MAAM;AAAA,YACN,aACE;AAAA,UACJ;AAAA,QACF;AAAA,QACA,sBAAsB;AAAA,MACxB;AAAA,IACF;AAAA,IACA,UAAU;AAAA,MACR,mBACE;AAAA,IACJ;AAAA,EACF;AAAA,EACA,OAAO,SAAS;AACd,UAAM,UAAoB,QAAQ,QAAQ,CAAC,KAAiB,CAAC;AAC7D,UAAM,WAAW,QAAQ;AACzB,QAAI,CAAC,YAAY,aAAa,OAAO,aAAa,IAAI;AAEpD,aAAO,CAAC;AAAA,IACV;AACA,UAAM,SAAS,SAAS,SAAS,GAAG,IAAI,WAAW,WAAW;AAE9D,WAAO;AAAA,MACL,aAAa,MAAiB;AAE5B,cAAM,OAAO;AACb,YAAI,KAAK,MAAM,SAAS,OAAQ;AAEhC,cAAM,QAAQ,KAAK;AACnB,YAAI,CAAC,SAAS,MAAM,SAAS,UAAW;AACxC,cAAM,IAAI,MAAM;AAChB,YAAI,OAAO,MAAM,SAAU;AAC3B,YAAI,MAAM,YAAY,CAAC,EAAE,WAAW,MAAM,EAAG;AAE7C,cAAM,WAAW,MAAM,WAAW,MAAM,MAAM,EAAE,MAAM,OAAO,MAAM;AACnE,gBAAQ,OAAO;AAAA,UACb,MAAM;AAAA,UACN,WAAW;AAAA,UACX,MAAM,EAAE,UAAU,MAAM,GAAG,SAAS;AAAA,QACtC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,sCAAQ;;;ACxDf,IAAM,SAAS;AAAA,EACb,MAAM;AAAA,IACJ,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA,OAAO;AAAA,IACL,+BAA+B;AAAA,EACjC;AAAA,EACA,SAAS,CAAC;AACZ;AAGA,OAAO,UAAU;AAAA,EACf,aAAa;AAAA,IACX,SAAS,EAAE,4BAA4B,OAAO;AAAA,IAC9C,OAAO;AAAA,MACL,wDAAwD;AAAA,IAC1D;AAAA,EACF;AACF;AAEA,IAAO,gBAAQ;","names":[]}
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@augmenting-integrations/eslint-plugin",
3
+ "version": "4.0.1",
4
+ "description": "Shared ESLint rules for Augmenting Integrations Next.js apps.",
5
+ "license": "MIT",
6
+ "publishConfig": {
7
+ "access": "public"
8
+ },
9
+ "sideEffects": false,
10
+ "main": "./dist/index.cjs",
11
+ "module": "./dist/index.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "require": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist",
22
+ "README.md"
23
+ ],
24
+ "scripts": {
25
+ "build": "tsup",
26
+ "clean": "rm -rf dist",
27
+ "test": "vitest run --passWithNoTests"
28
+ },
29
+ "peerDependencies": {
30
+ "eslint": "^9.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "@types/eslint": "^9.6.1",
34
+ "eslint": "^9.16.0",
35
+ "tsup": "^8.3.5",
36
+ "typescript": "^5.7.2",
37
+ "vitest": "^4.1.5"
38
+ }
39
+ }