mcp-grocy 2.7.0 → 2.7.2
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 +14 -0
- package/README.md +15 -1
- package/build/api/client.js +119 -0
- package/build/config/index.js +283 -0
- package/build/main.js +44 -0
- package/build/resources/CHANGELOG.md +15 -3
- package/build/resources/README.md +15 -1
- package/build/resources/api-reference.md +2 -1
- package/build/resources/response-format.md +1 -3
- package/build/server/http-server.js +398 -0
- package/build/server/mcp-server.js +230 -0
- package/build/server/resources.js +73 -0
- package/build/server/tool-input-zod.js +86 -0
- package/build/tools/base.js +157 -0
- package/build/tools/household/definitions.js +161 -0
- package/build/tools/household/handlers.js +109 -0
- package/build/tools/household/index.js +25 -0
- package/build/tools/index.js +2 -0
- package/build/tools/inventory/definitions.js +416 -0
- package/build/tools/inventory/handlers.js +443 -0
- package/build/tools/inventory/index.js +32 -0
- package/build/tools/module-loader.js +154 -0
- package/build/tools/recipes/definitions.js +278 -0
- package/build/tools/recipes/handlers.js +518 -0
- package/build/tools/recipes/index.js +33 -0
- package/build/tools/recipes/validations.js +29 -0
- package/build/tools/shopping/definitions.js +147 -0
- package/build/tools/shopping/handlers.js +198 -0
- package/build/tools/shopping/index.js +17 -0
- package/build/tools/system/definitions.js +89 -0
- package/build/tools/system/handlers.js +156 -0
- package/build/tools/system/index.js +16 -0
- package/build/tools/types.js +1 -0
- package/build/tools/validation-helpers.js +39 -0
- package/build/types/index.js +64 -0
- package/build/utils/errors.js +143 -0
- package/build/utils/logger.js +142 -0
- package/build/version.js +1 -1
- package/package.json +27 -25
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Improved type definitions for better type safety
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
// Configuration types
|
|
6
|
+
export const ConfigSchema = z.object({
|
|
7
|
+
env: z.object({
|
|
8
|
+
GROCY_API_KEY: z.string().optional(),
|
|
9
|
+
RELEASE_VERSION: z.string().optional(),
|
|
10
|
+
NODE_ENV: z.enum(['development', 'production', 'test']).optional(),
|
|
11
|
+
}),
|
|
12
|
+
yaml: z.object({
|
|
13
|
+
server: z.object({
|
|
14
|
+
enable_http_server: z.boolean().default(false),
|
|
15
|
+
http_server_port: z.number().min(1).max(65535).default(8080),
|
|
16
|
+
}),
|
|
17
|
+
grocy: z.object({
|
|
18
|
+
base_url: z.string().url(),
|
|
19
|
+
enable_ssl_verify: z.boolean().default(true),
|
|
20
|
+
response_size_limit: z.number().positive().default(10000),
|
|
21
|
+
}),
|
|
22
|
+
tools: z.record(z.string(), z
|
|
23
|
+
.object({
|
|
24
|
+
enabled: z.boolean().default(false),
|
|
25
|
+
ack_token: z.string().optional(),
|
|
26
|
+
})
|
|
27
|
+
.catchall(z.unknown())),
|
|
28
|
+
}),
|
|
29
|
+
});
|
|
30
|
+
// Validation schemas for runtime type checking
|
|
31
|
+
export const ToolResultSchema = z.object({
|
|
32
|
+
success: z.boolean(),
|
|
33
|
+
data: z.any().optional(),
|
|
34
|
+
error: z.string().optional(),
|
|
35
|
+
message: z.string().optional(),
|
|
36
|
+
details: z.any().optional(),
|
|
37
|
+
});
|
|
38
|
+
export const ToolDefinitionSchema = z.object({
|
|
39
|
+
name: z.string(),
|
|
40
|
+
description: z.string(),
|
|
41
|
+
inputSchema: z.object({
|
|
42
|
+
type: z.literal('object'),
|
|
43
|
+
properties: z.record(z.string(), z.any()),
|
|
44
|
+
required: z.array(z.string()).optional(),
|
|
45
|
+
}),
|
|
46
|
+
});
|
|
47
|
+
export const ToolModuleSchema = z.object({
|
|
48
|
+
definitions: z.array(ToolDefinitionSchema),
|
|
49
|
+
handlers: z.record(z.string(), z.custom((v) => typeof v === 'function')),
|
|
50
|
+
});
|
|
51
|
+
// Type guards
|
|
52
|
+
export function isToolResult(obj) {
|
|
53
|
+
return ToolResultSchema.safeParse(obj).success;
|
|
54
|
+
}
|
|
55
|
+
export function isToolDefinition(obj) {
|
|
56
|
+
return ToolDefinitionSchema.safeParse(obj).success;
|
|
57
|
+
}
|
|
58
|
+
export function isToolModule(obj) {
|
|
59
|
+
return (obj &&
|
|
60
|
+
typeof obj === 'object' &&
|
|
61
|
+
Array.isArray(obj.definitions) &&
|
|
62
|
+
typeof obj.handlers === 'object' &&
|
|
63
|
+
obj.definitions.every((def) => isToolDefinition(def)));
|
|
64
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simplified error handling system
|
|
3
|
+
*/
|
|
4
|
+
import { McpError, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
5
|
+
import { logger } from './logger.js';
|
|
6
|
+
/**
|
|
7
|
+
* Simplified application error
|
|
8
|
+
*/
|
|
9
|
+
export class AppError extends Error {
|
|
10
|
+
category;
|
|
11
|
+
statusCode;
|
|
12
|
+
operation;
|
|
13
|
+
details;
|
|
14
|
+
timestamp = new Date();
|
|
15
|
+
constructor(message, category, statusCode, operation, details) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'AppError';
|
|
18
|
+
this.category = category;
|
|
19
|
+
this.statusCode = statusCode;
|
|
20
|
+
this.operation = operation;
|
|
21
|
+
this.details = details;
|
|
22
|
+
Object.setPrototypeOf(this, AppError.prototype);
|
|
23
|
+
}
|
|
24
|
+
toMcpError() {
|
|
25
|
+
const errorCode = this.getErrorCode();
|
|
26
|
+
return new McpError(errorCode, this.message);
|
|
27
|
+
}
|
|
28
|
+
getErrorCode() {
|
|
29
|
+
switch (this.category) {
|
|
30
|
+
case 'VALIDATION':
|
|
31
|
+
return ErrorCode.InvalidParams;
|
|
32
|
+
case 'AUTH':
|
|
33
|
+
return ErrorCode.InvalidRequest;
|
|
34
|
+
case 'API':
|
|
35
|
+
if (this.statusCode === 404)
|
|
36
|
+
return ErrorCode.InvalidRequest;
|
|
37
|
+
if (this.statusCode === 401 || this.statusCode === 403)
|
|
38
|
+
return ErrorCode.InvalidRequest;
|
|
39
|
+
return ErrorCode.InternalError;
|
|
40
|
+
default:
|
|
41
|
+
return ErrorCode.InternalError;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* API-specific error
|
|
47
|
+
*/
|
|
48
|
+
export class ApiError extends AppError {
|
|
49
|
+
constructor(message, statusCode, operation, details) {
|
|
50
|
+
super(message, 'API', statusCode, operation, details);
|
|
51
|
+
this.name = 'ApiError';
|
|
52
|
+
}
|
|
53
|
+
static fromAxiosError(error, operation) {
|
|
54
|
+
if (error.response) {
|
|
55
|
+
return new ApiError(error.response.data?.message || `HTTP ${error.response.status}`, error.response.status, operation, { url: error.config?.url, method: error.config?.method });
|
|
56
|
+
}
|
|
57
|
+
if (error.request) {
|
|
58
|
+
return new ApiError('Network error - unable to reach server', undefined, operation, {
|
|
59
|
+
url: error.config?.url,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return new ApiError(error.message || 'Request error', undefined, operation);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Validation error
|
|
67
|
+
*/
|
|
68
|
+
export class ValidationError extends AppError {
|
|
69
|
+
constructor(message, operation, details) {
|
|
70
|
+
super(message, 'VALIDATION', undefined, operation, details);
|
|
71
|
+
this.name = 'ValidationError';
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Configuration error
|
|
76
|
+
*/
|
|
77
|
+
export class ConfigError extends AppError {
|
|
78
|
+
constructor(message, operation, details) {
|
|
79
|
+
super(message, 'CONFIG', undefined, operation, details);
|
|
80
|
+
this.name = 'ConfigError';
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Centralized error handler
|
|
85
|
+
*/
|
|
86
|
+
export class ErrorHandler {
|
|
87
|
+
/**
|
|
88
|
+
* Handle async operations with error wrapping
|
|
89
|
+
*/
|
|
90
|
+
static async handleAsync(operation, context) {
|
|
91
|
+
try {
|
|
92
|
+
return await operation();
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
this.logError(error, context);
|
|
96
|
+
throw this.wrapError(error, context);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Log error with context
|
|
101
|
+
*/
|
|
102
|
+
static logError(error, context) {
|
|
103
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
104
|
+
const fullContext = context ? `${context}: ${errorMessage}` : errorMessage;
|
|
105
|
+
if (error instanceof AppError) {
|
|
106
|
+
logger.warn(fullContext, 'ERROR', {
|
|
107
|
+
category: error.category,
|
|
108
|
+
statusCode: error.statusCode,
|
|
109
|
+
operation: error.operation,
|
|
110
|
+
details: error.details,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
logger.error(fullContext, 'ERROR', { error });
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Wrap unknown errors in AppError
|
|
119
|
+
*/
|
|
120
|
+
static wrapError(error, context) {
|
|
121
|
+
if (error instanceof AppError) {
|
|
122
|
+
return error;
|
|
123
|
+
}
|
|
124
|
+
if (error instanceof McpError) {
|
|
125
|
+
return new AppError(error.message, 'INTERNAL', undefined, context);
|
|
126
|
+
}
|
|
127
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
128
|
+
return new AppError(message, 'INTERNAL', undefined, context);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Convert error to MCP error
|
|
132
|
+
*/
|
|
133
|
+
static toMcpError(error, fallbackMessage = 'Internal error') {
|
|
134
|
+
if (error instanceof McpError) {
|
|
135
|
+
return error;
|
|
136
|
+
}
|
|
137
|
+
if (error instanceof AppError) {
|
|
138
|
+
return error.toMcpError();
|
|
139
|
+
}
|
|
140
|
+
const message = error instanceof Error ? error.message : fallbackMessage;
|
|
141
|
+
return new McpError(ErrorCode.InternalError, message);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Centralized logging utility with configurable levels and structured output
|
|
3
|
+
*/
|
|
4
|
+
export var LogLevel;
|
|
5
|
+
(function (LogLevel) {
|
|
6
|
+
LogLevel[LogLevel["ERROR"] = 0] = "ERROR";
|
|
7
|
+
LogLevel[LogLevel["WARN"] = 1] = "WARN";
|
|
8
|
+
LogLevel[LogLevel["INFO"] = 2] = "INFO";
|
|
9
|
+
LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG";
|
|
10
|
+
LogLevel[LogLevel["TRACE"] = 4] = "TRACE";
|
|
11
|
+
})(LogLevel || (LogLevel = {}));
|
|
12
|
+
export class Logger {
|
|
13
|
+
static instance;
|
|
14
|
+
logLevel;
|
|
15
|
+
enabledCategories = null;
|
|
16
|
+
constructor() {
|
|
17
|
+
// Default to INFO level, but allow override via environment
|
|
18
|
+
this.logLevel = this.parseLogLevel(process.env.LOG_LEVEL || 'INFO');
|
|
19
|
+
// Allow filtering by categories
|
|
20
|
+
const categories = process.env.LOG_CATEGORIES;
|
|
21
|
+
if (categories) {
|
|
22
|
+
this.enabledCategories = new Set(categories.split(',').map((c) => c.trim().toUpperCase()));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
static getInstance() {
|
|
26
|
+
if (!Logger.instance) {
|
|
27
|
+
Logger.instance = new Logger();
|
|
28
|
+
}
|
|
29
|
+
return Logger.instance;
|
|
30
|
+
}
|
|
31
|
+
parseLogLevel(level) {
|
|
32
|
+
const upperLevel = level.toUpperCase();
|
|
33
|
+
switch (upperLevel) {
|
|
34
|
+
case 'ERROR':
|
|
35
|
+
return LogLevel.ERROR;
|
|
36
|
+
case 'WARN':
|
|
37
|
+
return LogLevel.WARN;
|
|
38
|
+
case 'INFO':
|
|
39
|
+
return LogLevel.INFO;
|
|
40
|
+
case 'DEBUG':
|
|
41
|
+
return LogLevel.DEBUG;
|
|
42
|
+
case 'TRACE':
|
|
43
|
+
return LogLevel.TRACE;
|
|
44
|
+
default:
|
|
45
|
+
return LogLevel.INFO;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
shouldLog(level, category) {
|
|
49
|
+
// Check log level
|
|
50
|
+
if (level > this.logLevel) {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
// Check category filter
|
|
54
|
+
if (this.enabledCategories && category) {
|
|
55
|
+
return this.enabledCategories.has(category.toUpperCase());
|
|
56
|
+
}
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
formatMessage(entry) {
|
|
60
|
+
const timestamp = entry.timestamp.toISOString();
|
|
61
|
+
const level = LogLevel[entry.level].padEnd(5);
|
|
62
|
+
const category = entry.category ? `[${entry.category}]` : '';
|
|
63
|
+
const data = entry.data ? ` ${JSON.stringify(entry.data)}` : '';
|
|
64
|
+
return `${timestamp} ${level} ${category} ${entry.message}${data}`;
|
|
65
|
+
}
|
|
66
|
+
log(level, message, category, data) {
|
|
67
|
+
if (!this.shouldLog(level, category)) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const entry = {
|
|
71
|
+
level,
|
|
72
|
+
message,
|
|
73
|
+
category: category || undefined,
|
|
74
|
+
data,
|
|
75
|
+
timestamp: new Date(),
|
|
76
|
+
};
|
|
77
|
+
const formattedMessage = this.formatMessage(entry);
|
|
78
|
+
// Use stderr for all logs to avoid interfering with MCP stdio protocol
|
|
79
|
+
console.error(formattedMessage);
|
|
80
|
+
}
|
|
81
|
+
error(message, category, data) {
|
|
82
|
+
this.log(LogLevel.ERROR, message, category, data);
|
|
83
|
+
}
|
|
84
|
+
warn(message, category, data) {
|
|
85
|
+
this.log(LogLevel.WARN, message, category, data);
|
|
86
|
+
}
|
|
87
|
+
info(message, category, data) {
|
|
88
|
+
this.log(LogLevel.INFO, message, category, data);
|
|
89
|
+
}
|
|
90
|
+
debug(message, category, data) {
|
|
91
|
+
this.log(LogLevel.DEBUG, message, category, data);
|
|
92
|
+
}
|
|
93
|
+
trace(message, category, data) {
|
|
94
|
+
this.log(LogLevel.TRACE, message, category, data);
|
|
95
|
+
}
|
|
96
|
+
// Convenience methods for common categories
|
|
97
|
+
config(message, data) {
|
|
98
|
+
this.info(message, 'CONFIG', data);
|
|
99
|
+
}
|
|
100
|
+
api(message, data) {
|
|
101
|
+
this.debug(message, 'API', data);
|
|
102
|
+
}
|
|
103
|
+
module(message, data) {
|
|
104
|
+
this.debug(message, 'MODULE', data);
|
|
105
|
+
}
|
|
106
|
+
tools(message, data) {
|
|
107
|
+
this.info(message, 'TOOLS', data);
|
|
108
|
+
}
|
|
109
|
+
server(message, data) {
|
|
110
|
+
this.info(message, 'SERVER', data);
|
|
111
|
+
}
|
|
112
|
+
// Testing utilities
|
|
113
|
+
setLogLevel(level) {
|
|
114
|
+
this.logLevel = level;
|
|
115
|
+
}
|
|
116
|
+
setEnabledCategories(categories) {
|
|
117
|
+
this.enabledCategories = categories ? new Set(categories.map((c) => c.toUpperCase())) : null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// Export singleton instance
|
|
121
|
+
export const logger = Logger.getInstance();
|
|
122
|
+
// Convenience function for quick logging
|
|
123
|
+
export function log(level, message, category, data) {
|
|
124
|
+
// Access the private log method through the public methods
|
|
125
|
+
switch (level) {
|
|
126
|
+
case LogLevel.ERROR:
|
|
127
|
+
logger.error(message, category, data);
|
|
128
|
+
break;
|
|
129
|
+
case LogLevel.WARN:
|
|
130
|
+
logger.warn(message, category, data);
|
|
131
|
+
break;
|
|
132
|
+
case LogLevel.INFO:
|
|
133
|
+
logger.info(message, category, data);
|
|
134
|
+
break;
|
|
135
|
+
case LogLevel.DEBUG:
|
|
136
|
+
logger.debug(message, category, data);
|
|
137
|
+
break;
|
|
138
|
+
case LogLevel.TRACE:
|
|
139
|
+
logger.trace(message, category, data);
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
package/build/version.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-grocy",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.2",
|
|
4
4
|
"description": "Model Context Protocol (MCP) server for Grocy integration",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"scripts": {
|
|
21
21
|
"prebuild": "node scripts/build.js",
|
|
22
22
|
"build": "tsc",
|
|
23
|
+
"prepack": "npm run build",
|
|
23
24
|
"type-check": "tsc --noEmit",
|
|
24
25
|
"prepare": "husky",
|
|
25
26
|
"start": "node build/main.js",
|
|
@@ -27,7 +28,7 @@
|
|
|
27
28
|
"watch": "tsc --watch",
|
|
28
29
|
"inspector": "npx @modelcontextprotocol/inspector build/main.js",
|
|
29
30
|
"test": "vitest run",
|
|
30
|
-
"test:mcp-
|
|
31
|
+
"test:mcp-inspector": "bash scripts/run-mcp-inspector.sh",
|
|
31
32
|
"dev:mcp-tef": "bash scripts/run-mcp-tef.sh",
|
|
32
33
|
"report:mcp-tef": "bash scripts/mcp-tef-report.sh",
|
|
33
34
|
"start:mcp-http-test": "bash scripts/start-mcp-http-for-tests.sh",
|
|
@@ -48,38 +49,39 @@
|
|
|
48
49
|
"prepare-dev-release:dryrun": "node scripts/prepare-dev-release.js --dry-run"
|
|
49
50
|
},
|
|
50
51
|
"dependencies": {
|
|
51
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
52
|
-
"axios": "^1.
|
|
52
|
+
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
53
|
+
"axios": "^1.20.0",
|
|
53
54
|
"cors": "^2.8.6",
|
|
54
|
-
"dotenv": "^17.
|
|
55
|
+
"dotenv": "^17.4.2",
|
|
55
56
|
"express": "^5.2.1",
|
|
56
|
-
"fuse.js": "^7.
|
|
57
|
-
"yaml": "^2.
|
|
58
|
-
"zod": "^4.
|
|
57
|
+
"fuse.js": "^7.5.0",
|
|
58
|
+
"yaml": "^2.9.0",
|
|
59
|
+
"zod": "^4.6.1"
|
|
59
60
|
},
|
|
60
61
|
"devDependencies": {
|
|
61
|
-
"@commitlint/cli": "^
|
|
62
|
-
"@commitlint/config-conventional": "^
|
|
63
|
-
"@commitlint/types": "^
|
|
64
|
-
"@eslint/js": "^
|
|
65
|
-
"@
|
|
62
|
+
"@commitlint/cli": "^21.2.2",
|
|
63
|
+
"@commitlint/config-conventional": "^21.2.2",
|
|
64
|
+
"@commitlint/types": "^21.2.0",
|
|
65
|
+
"@eslint/js": "^10.0.1",
|
|
66
|
+
"@modelcontextprotocol/inspector": "^2.6.0",
|
|
67
|
+
"@semantic-release/changelog": "^7.0.0",
|
|
66
68
|
"@semantic-release/exec": "^7.1.0",
|
|
67
|
-
"@semantic-release/git": "^
|
|
69
|
+
"@semantic-release/git": "^11.0.1",
|
|
68
70
|
"@types/cors": "^2.8.19",
|
|
69
71
|
"@types/express": "^5.0.6",
|
|
70
|
-
"@types/node": "^22.
|
|
71
|
-
"
|
|
72
|
-
"eslint": "^9.39.4",
|
|
72
|
+
"@types/node": "^22.20.2",
|
|
73
|
+
"eslint": "^10.10.0",
|
|
73
74
|
"eslint-config-prettier": "^10.1.8",
|
|
74
|
-
"fs-extra": "^11.
|
|
75
|
+
"fs-extra": "^11.4.0",
|
|
75
76
|
"husky": "^9.1.7",
|
|
76
|
-
"lint-staged": "^17.
|
|
77
|
-
"prettier": "^3.
|
|
78
|
-
"semantic-release": "^25.0.
|
|
79
|
-
"ts-node": "^10.9.
|
|
77
|
+
"lint-staged": "^17.5.1",
|
|
78
|
+
"prettier": "^3.9.6",
|
|
79
|
+
"semantic-release": "^25.0.9",
|
|
80
|
+
"ts-node": "^10.9.2",
|
|
80
81
|
"typescript": "^5.9.3",
|
|
81
|
-
"typescript-eslint": "^8.
|
|
82
|
-
"
|
|
82
|
+
"typescript-eslint": "^8.70.0",
|
|
83
|
+
"vite": "^8.3.0",
|
|
84
|
+
"vitest": "^5.0.0"
|
|
83
85
|
},
|
|
84
86
|
"keywords": [
|
|
85
87
|
"grocy",
|
|
@@ -105,7 +107,7 @@
|
|
|
105
107
|
"node": ">=22.0.0"
|
|
106
108
|
},
|
|
107
109
|
"config": {
|
|
108
|
-
"supportedGrocyVersion": "4.
|
|
110
|
+
"supportedGrocyVersion": "4.7.1"
|
|
109
111
|
},
|
|
110
112
|
"lint-staged": {
|
|
111
113
|
"*.{ts,js,mjs,cjs}": [
|