@neverinfamous/mysql-mcp 2.3.0 → 3.0.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/.dockerignore +1 -0
- package/.gitattributes +18 -0
- package/.github/workflows/codeql.yml +2 -10
- package/.github/workflows/docker-publish.yml +15 -13
- package/CHANGELOG.md +287 -1
- package/DOCKER_README.md +100 -265
- package/Dockerfile +5 -0
- package/README.md +124 -59
- package/VERSION +1 -1
- package/dist/__tests__/mocks/adapter.d.ts.map +1 -1
- package/dist/__tests__/mocks/adapter.js +2 -0
- package/dist/__tests__/mocks/adapter.js.map +1 -1
- package/dist/adapters/DatabaseAdapter.d.ts.map +1 -1
- package/dist/adapters/DatabaseAdapter.js +50 -9
- package/dist/adapters/DatabaseAdapter.js.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.d.ts +6 -0
- package/dist/adapters/mysql/MySQLAdapter.d.ts.map +1 -1
- package/dist/adapters/mysql/MySQLAdapter.js +8 -0
- package/dist/adapters/mysql/MySQLAdapter.js.map +1 -1
- package/dist/adapters/mysql/SchemaManager.js +16 -15
- package/dist/adapters/mysql/SchemaManager.js.map +1 -1
- package/dist/adapters/mysql/prompts/index.js +10 -20
- package/dist/adapters/mysql/prompts/index.js.map +1 -1
- package/dist/adapters/mysql/prompts/proxysqlSetup.js +1 -1
- package/dist/adapters/mysql/resources/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/docstore.js +10 -7
- package/dist/adapters/mysql/resources/docstore.js.map +1 -1
- package/dist/adapters/mysql/resources/events.js +11 -8
- package/dist/adapters/mysql/resources/events.js.map +1 -1
- package/dist/adapters/mysql/resources/indexes.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/indexes.js +12 -15
- package/dist/adapters/mysql/resources/indexes.js.map +1 -1
- package/dist/adapters/mysql/resources/innodb.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/innodb.js +20 -17
- package/dist/adapters/mysql/resources/innodb.js.map +1 -1
- package/dist/adapters/mysql/resources/locks.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/locks.js +9 -6
- package/dist/adapters/mysql/resources/locks.js.map +1 -1
- package/dist/adapters/mysql/resources/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/performance.js +15 -15
- package/dist/adapters/mysql/resources/performance.js.map +1 -1
- package/dist/adapters/mysql/resources/spatial.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/spatial.js +9 -6
- package/dist/adapters/mysql/resources/spatial.js.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.d.ts.map +1 -1
- package/dist/adapters/mysql/resources/sysschema.js +12 -9
- package/dist/adapters/mysql/resources/sysschema.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/backup.js +170 -121
- package/dist/adapters/mysql/tools/admin/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/maintenance.js +106 -57
- package/dist/adapters/mysql/tools/admin/maintenance.js.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/admin/monitoring.js +183 -101
- package/dist/adapters/mysql/tools/admin/monitoring.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/group-replication.js +164 -120
- package/dist/adapters/mysql/tools/cluster/group-replication.js.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js +212 -145
- package/dist/adapters/mysql/tools/cluster/innodb-cluster.js.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/codemode/index.js +6 -4
- package/dist/adapters/mysql/tools/codemode/index.js.map +1 -1
- package/dist/adapters/mysql/tools/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/core.js +152 -29
- package/dist/adapters/mysql/tools/core.js.map +1 -1
- package/dist/adapters/mysql/tools/docstore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/docstore.js +340 -163
- package/dist/adapters/mysql/tools/docstore.js.map +1 -1
- package/dist/adapters/mysql/tools/events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/events.js +284 -198
- package/dist/adapters/mysql/tools/events.js.map +1 -1
- package/dist/adapters/mysql/tools/json/core.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/core.js +11 -39
- package/dist/adapters/mysql/tools/json/core.js.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/enhanced.js +15 -33
- package/dist/adapters/mysql/tools/json/enhanced.js.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/json/helpers.js +13 -24
- package/dist/adapters/mysql/tools/json/helpers.js.map +1 -1
- package/dist/adapters/mysql/tools/partitioning.js +3 -0
- package/dist/adapters/mysql/tools/partitioning.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/analysis.js +89 -60
- package/dist/adapters/mysql/tools/performance/analysis.js.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/performance/optimization.js +151 -127
- package/dist/adapters/mysql/tools/performance/optimization.js.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts +1 -1
- package/dist/adapters/mysql/tools/proxysql.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/proxysql.js +289 -176
- package/dist/adapters/mysql/tools/proxysql.js.map +1 -1
- package/dist/adapters/mysql/tools/replication.js +75 -49
- package/dist/adapters/mysql/tools/replication.js.map +1 -1
- package/dist/adapters/mysql/tools/roles.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/roles.js +224 -182
- package/dist/adapters/mysql/tools/roles.js.map +1 -1
- package/dist/adapters/mysql/tools/router.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/router.js +168 -67
- package/dist/adapters/mysql/tools/router.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/constraints.js +21 -3
- package/dist/adapters/mysql/tools/schema/constraints.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/management.js +61 -14
- package/dist/adapters/mysql/tools/schema/management.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/routines.js +27 -4
- package/dist/adapters/mysql/tools/schema/routines.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/scheduled_events.js +24 -3
- package/dist/adapters/mysql/tools/schema/scheduled_events.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/triggers.js +23 -2
- package/dist/adapters/mysql/tools/schema/triggers.js.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/schema/views.js +47 -7
- package/dist/adapters/mysql/tools/schema/views.js.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/audit.js +102 -34
- package/dist/adapters/mysql/tools/security/audit.js.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/data-protection.js +264 -205
- package/dist/adapters/mysql/tools/security/data-protection.js.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/security/encryption.js +137 -104
- package/dist/adapters/mysql/tools/security/encryption.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/backup.js +71 -59
- package/dist/adapters/mysql/tools/shell/backup.js.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/shell/restore.js +61 -47
- package/dist/adapters/mysql/tools/shell/restore.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/geometry.js +19 -5
- package/dist/adapters/mysql/tools/spatial/geometry.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/operations.js +42 -17
- package/dist/adapters/mysql/tools/spatial/operations.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/queries.js +109 -57
- package/dist/adapters/mysql/tools/spatial/queries.js.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/spatial/setup.js +103 -50
- package/dist/adapters/mysql/tools/spatial/setup.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/comparative.js +128 -79
- package/dist/adapters/mysql/tools/stats/comparative.js.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/stats/descriptive.js +174 -102
- package/dist/adapters/mysql/tools/stats/descriptive.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/activity.js +50 -25
- package/dist/adapters/mysql/tools/sysschema/activity.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/performance.js +121 -66
- package/dist/adapters/mysql/tools/sysschema/performance.js.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/sysschema/resources.js +101 -64
- package/dist/adapters/mysql/tools/sysschema/resources.js.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/text/fulltext.js +18 -32
- package/dist/adapters/mysql/tools/text/fulltext.js.map +1 -1
- package/dist/adapters/mysql/tools/transactions.d.ts.map +1 -1
- package/dist/adapters/mysql/tools/transactions.js +48 -23
- package/dist/adapters/mysql/tools/transactions.js.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.d.ts +15 -0
- package/dist/adapters/mysql/types/proxysql-types.d.ts.map +1 -1
- package/dist/adapters/mysql/types/proxysql-types.js +33 -1
- package/dist/adapters/mysql/types/proxysql-types.js.map +1 -1
- package/dist/adapters/mysql/types/router-types.d.ts +1 -1
- package/dist/adapters/mysql/types/router-types.js +1 -1
- package/dist/adapters/mysql/types/router-types.js.map +1 -1
- package/dist/adapters/mysql/types/shell-types.js +2 -2
- package/dist/adapters/mysql/types/shell-types.js.map +1 -1
- package/dist/adapters/mysql/types.d.ts +485 -21
- package/dist/adapters/mysql/types.d.ts.map +1 -1
- package/dist/adapters/mysql/types.js +546 -19
- package/dist/adapters/mysql/types.js.map +1 -1
- package/dist/auth/scopes.js +1 -1
- package/dist/auth/scopes.js.map +1 -1
- package/dist/codemode/api.d.ts +3 -2
- package/dist/codemode/api.d.ts.map +1 -1
- package/dist/codemode/api.js +80 -5
- package/dist/codemode/api.js.map +1 -1
- package/dist/codemode/sandbox-factory.js +1 -1
- package/dist/codemode/sandbox-factory.js.map +1 -1
- package/dist/codemode/types.d.ts +26 -0
- package/dist/codemode/types.d.ts.map +1 -1
- package/dist/codemode/types.js +2 -0
- package/dist/codemode/types.js.map +1 -1
- package/dist/codemode/worker-sandbox.d.ts +4 -2
- package/dist/codemode/worker-sandbox.d.ts.map +1 -1
- package/dist/codemode/worker-sandbox.js +66 -7
- package/dist/codemode/worker-sandbox.js.map +1 -1
- package/dist/codemode/worker-script.d.ts +3 -0
- package/dist/codemode/worker-script.d.ts.map +1 -1
- package/dist/codemode/worker-script.js +128 -75
- package/dist/codemode/worker-script.js.map +1 -1
- package/dist/constants/ServerInstructions.d.ts +1 -1
- package/dist/constants/ServerInstructions.d.ts.map +1 -1
- package/dist/constants/ServerInstructions.js +37 -31
- package/dist/constants/ServerInstructions.js.map +1 -1
- package/dist/filtering/ToolConstants.d.ts +1 -1
- package/dist/filtering/ToolConstants.d.ts.map +1 -1
- package/dist/filtering/ToolConstants.js +1 -2
- package/dist/filtering/ToolConstants.js.map +1 -1
- package/dist/pool/ConnectionPool.d.ts.map +1 -1
- package/dist/pool/ConnectionPool.js.map +1 -1
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +6 -0
- package/dist/transports/http.js.map +1 -1
- package/dist/utils/validators.d.ts +1 -1
- package/dist/utils/validators.d.ts.map +1 -1
- package/dist/utils/validators.js.map +1 -1
- package/package.json +4 -4
- package/releases/v2.3.0-release-notes.md +20 -20
- package/releases/v2.3.1-release-notes.md +34 -0
- package/releases/v3.0.0-release-notes.md +81 -0
- package/src/__tests__/mocks/adapter.ts +3 -0
- package/src/__tests__/perf.test.ts +6 -6
- package/src/adapters/DatabaseAdapter.ts +58 -9
- package/src/adapters/__tests__/DatabaseAdapter.test.ts +89 -8
- package/src/adapters/mysql/MySQLAdapter.ts +17 -2
- package/src/adapters/mysql/SchemaManager.ts +21 -21
- package/src/adapters/mysql/__tests__/MySQLAdapter.test.ts +1 -1
- package/src/adapters/mysql/prompts/index.ts +12 -22
- package/src/adapters/mysql/prompts/proxysqlSetup.ts +1 -1
- package/src/adapters/mysql/resources/docstore.ts +13 -10
- package/src/adapters/mysql/resources/events.ts +12 -12
- package/src/adapters/mysql/resources/indexes.ts +17 -19
- package/src/adapters/mysql/resources/innodb.ts +23 -22
- package/src/adapters/mysql/resources/locks.ts +9 -7
- package/src/adapters/mysql/resources/performance.ts +23 -18
- package/src/adapters/mysql/resources/spatial.ts +9 -7
- package/src/adapters/mysql/resources/sysschema.ts +12 -11
- package/src/adapters/mysql/tools/__tests__/core.test.ts +126 -55
- package/src/adapters/mysql/tools/__tests__/docstore.test.ts +459 -88
- package/src/adapters/mysql/tools/__tests__/events.test.ts +281 -103
- package/src/adapters/mysql/tools/__tests__/proxysql.test.ts +128 -28
- package/src/adapters/mysql/tools/__tests__/replication.test.ts +48 -2
- package/src/adapters/mysql/tools/__tests__/roles.test.ts +15 -18
- package/src/adapters/mysql/tools/__tests__/router.test.ts +32 -5
- package/src/adapters/mysql/tools/__tests__/security.test.ts +126 -2
- package/src/adapters/mysql/tools/__tests__/security_injection.test.ts +84 -76
- package/src/adapters/mysql/tools/__tests__/security_integration.test.ts +47 -50
- package/src/adapters/mysql/tools/__tests__/spatial.test.ts +11 -10
- package/src/adapters/mysql/tools/__tests__/spatial_handler.test.ts +54 -38
- package/src/adapters/mysql/tools/__tests__/stats.test.ts +285 -152
- package/src/adapters/mysql/tools/__tests__/transactions.test.ts +13 -13
- package/src/adapters/mysql/tools/admin/__tests__/backup.test.ts +171 -25
- package/src/adapters/mysql/tools/admin/__tests__/maintenance.test.ts +240 -4
- package/src/adapters/mysql/tools/admin/__tests__/monitoring-summary.test.ts +274 -0
- package/src/adapters/mysql/tools/admin/__tests__/monitoring.test.ts +94 -5
- package/src/adapters/mysql/tools/admin/backup.ts +193 -143
- package/src/adapters/mysql/tools/admin/maintenance.ts +118 -69
- package/src/adapters/mysql/tools/admin/monitoring.ts +201 -125
- package/src/adapters/mysql/tools/cluster/__tests__/group-replication.test.ts +69 -0
- package/src/adapters/mysql/tools/cluster/__tests__/innodb-cluster.test.ts +141 -0
- package/src/adapters/mysql/tools/cluster/group-replication.ts +172 -132
- package/src/adapters/mysql/tools/cluster/innodb-cluster.ts +231 -157
- package/src/adapters/mysql/tools/codemode/__tests__/codemode-tool.test.ts +227 -0
- package/src/adapters/mysql/tools/codemode/index.ts +5 -3
- package/src/adapters/mysql/tools/core.ts +152 -38
- package/src/adapters/mysql/tools/docstore.ts +422 -205
- package/src/adapters/mysql/tools/events.ts +334 -233
- package/src/adapters/mysql/tools/json/__tests__/core.test.ts +20 -0
- package/src/adapters/mysql/tools/json/__tests__/enhanced.test.ts +82 -50
- package/src/adapters/mysql/tools/json/__tests__/helpers.test.ts +42 -3
- package/src/adapters/mysql/tools/json/core.ts +21 -42
- package/src/adapters/mysql/tools/json/enhanced.ts +22 -37
- package/src/adapters/mysql/tools/json/helpers.ts +21 -25
- package/src/adapters/mysql/tools/partitioning.ts +3 -0
- package/src/adapters/mysql/tools/performance/__tests__/analysis.test.ts +98 -5
- package/src/adapters/mysql/tools/performance/__tests__/optimization-coverage.test.ts +515 -0
- package/src/adapters/mysql/tools/performance/__tests__/optimization.test.ts +187 -0
- package/src/adapters/mysql/tools/performance/analysis.ts +95 -69
- package/src/adapters/mysql/tools/performance/optimization.ts +182 -153
- package/src/adapters/mysql/tools/proxysql.ts +314 -209
- package/src/adapters/mysql/tools/replication.ts +84 -57
- package/src/adapters/mysql/tools/roles.ts +274 -226
- package/src/adapters/mysql/tools/router.ts +181 -85
- package/src/adapters/mysql/tools/schema/__tests__/constraints.test.ts +13 -0
- package/src/adapters/mysql/tools/schema/__tests__/management.test.ts +60 -25
- package/src/adapters/mysql/tools/schema/__tests__/scheduled_events.test.ts +11 -0
- package/src/adapters/mysql/tools/schema/__tests__/triggers.test.ts +25 -4
- package/src/adapters/mysql/tools/schema/__tests__/views.test.ts +46 -14
- package/src/adapters/mysql/tools/schema/constraints.ts +22 -3
- package/src/adapters/mysql/tools/schema/management.ts +60 -15
- package/src/adapters/mysql/tools/schema/routines.ts +26 -4
- package/src/adapters/mysql/tools/schema/scheduled_events.ts +25 -3
- package/src/adapters/mysql/tools/schema/triggers.ts +27 -2
- package/src/adapters/mysql/tools/schema/views.ts +46 -8
- package/src/adapters/mysql/tools/security/__tests__/audit.test.ts +90 -4
- package/src/adapters/mysql/tools/security/audit.ts +113 -39
- package/src/adapters/mysql/tools/security/data-protection.ts +293 -233
- package/src/adapters/mysql/tools/security/encryption.ts +172 -139
- package/src/adapters/mysql/tools/shell/__tests__/backup.test.ts +29 -0
- package/src/adapters/mysql/tools/shell/backup.ts +90 -73
- package/src/adapters/mysql/tools/shell/restore.ts +62 -48
- package/src/adapters/mysql/tools/spatial/__tests__/operations.test.ts +22 -14
- package/src/adapters/mysql/tools/spatial/__tests__/queries.test.ts +65 -51
- package/src/adapters/mysql/tools/spatial/geometry.ts +23 -7
- package/src/adapters/mysql/tools/spatial/operations.ts +60 -31
- package/src/adapters/mysql/tools/spatial/queries.ts +142 -65
- package/src/adapters/mysql/tools/spatial/setup.ts +121 -55
- package/src/adapters/mysql/tools/stats/__tests__/comparative.test.ts +12 -10
- package/src/adapters/mysql/tools/stats/comparative.ts +150 -98
- package/src/adapters/mysql/tools/stats/descriptive.ts +204 -127
- package/src/adapters/mysql/tools/sysschema/__tests__/error-paths.test.ts +222 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/performance.test.ts +45 -0
- package/src/adapters/mysql/tools/sysschema/__tests__/resources.test.ts +6 -3
- package/src/adapters/mysql/tools/sysschema/activity.ts +52 -27
- package/src/adapters/mysql/tools/sysschema/performance.ts +132 -68
- package/src/adapters/mysql/tools/sysschema/resources.ts +105 -67
- package/src/adapters/mysql/tools/text/__tests__/fulltext.test.ts +45 -17
- package/src/adapters/mysql/tools/text/fulltext.ts +27 -38
- package/src/adapters/mysql/tools/transactions.ts +49 -24
- package/src/adapters/mysql/types/proxysql-types.ts +38 -1
- package/src/adapters/mysql/types/router-types.ts +1 -1
- package/src/adapters/mysql/types/shell-types.ts +2 -2
- package/src/adapters/mysql/types.ts +632 -19
- package/src/auth/__tests__/scopes.test.ts +2 -2
- package/src/auth/scopes.ts +1 -1
- package/src/codemode/__tests__/api.test.ts +417 -0
- package/src/codemode/__tests__/sandbox-factory.test.ts +158 -0
- package/src/codemode/__tests__/sandbox.test.ts +301 -0
- package/src/codemode/__tests__/security.test.ts +368 -0
- package/src/codemode/__tests__/worker-sandbox.test.ts +179 -0
- package/src/codemode/__tests__/worker-script.test.ts +226 -0
- package/src/codemode/api.ts +89 -5
- package/src/codemode/sandbox-factory.ts +1 -1
- package/src/codemode/types.ts +34 -0
- package/src/codemode/worker-sandbox.ts +74 -7
- package/src/codemode/worker-script.ts +157 -86
- package/src/constants/ServerInstructions.ts +37 -31
- package/src/filtering/ToolConstants.ts +1 -2
- package/src/filtering/__tests__/ToolFilter.test.ts +9 -9
- package/src/pool/ConnectionPool.ts +4 -1
- package/src/transports/__tests__/http.test.ts +15 -3
- package/src/transports/http.ts +12 -0
- package/src/utils/validators.ts +2 -1
- package/vitest.config.ts +3 -1
- package/CODE_MODE.md +0 -245
|
@@ -67,6 +67,21 @@ describe("Sys Schema Performance Tools", () => {
|
|
|
67
67
|
const call = mockAdapter.executeQuery.mock.calls[0][0] as string;
|
|
68
68
|
expect(call).toContain("ORDER BY exec_count DESC");
|
|
69
69
|
});
|
|
70
|
+
|
|
71
|
+
it("should return structured error for invalid orderBy", async () => {
|
|
72
|
+
const tool = createSysStatementSummaryTool(
|
|
73
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
74
|
+
);
|
|
75
|
+
const result = (await tool.handler(
|
|
76
|
+
{ orderBy: "invalid_order" },
|
|
77
|
+
mockContext,
|
|
78
|
+
)) as { success: boolean; error: string };
|
|
79
|
+
|
|
80
|
+
expect(result.success).toBe(false);
|
|
81
|
+
expect(result.error).toContain("Invalid orderBy");
|
|
82
|
+
expect(result.error).toContain("invalid_order");
|
|
83
|
+
expect(mockAdapter.executeQuery).not.toHaveBeenCalled();
|
|
84
|
+
});
|
|
70
85
|
});
|
|
71
86
|
|
|
72
87
|
describe("createSysWaitSummaryTool", () => {
|
|
@@ -130,6 +145,21 @@ describe("Sys Schema Performance Tools", () => {
|
|
|
130
145
|
expect(call).toContain("AS avg_latency");
|
|
131
146
|
expect(call).toContain("AS total");
|
|
132
147
|
});
|
|
148
|
+
|
|
149
|
+
it("should return structured error for invalid type", async () => {
|
|
150
|
+
const tool = createSysWaitSummaryTool(
|
|
151
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
152
|
+
);
|
|
153
|
+
const result = (await tool.handler(
|
|
154
|
+
{ type: "invalid_type" },
|
|
155
|
+
mockContext,
|
|
156
|
+
)) as { success: boolean; error: string };
|
|
157
|
+
|
|
158
|
+
expect(result.success).toBe(false);
|
|
159
|
+
expect(result.error).toContain("Invalid type");
|
|
160
|
+
expect(result.error).toContain("invalid_type");
|
|
161
|
+
expect(mockAdapter.executeQuery).not.toHaveBeenCalled();
|
|
162
|
+
});
|
|
133
163
|
});
|
|
134
164
|
|
|
135
165
|
describe("createSysIOSummaryTool", () => {
|
|
@@ -179,5 +209,20 @@ describe("Sys Schema Performance Tools", () => {
|
|
|
179
209
|
expect(call).toContain("sys.io_global_by_wait_by_latency");
|
|
180
210
|
expect(call).toContain("event_name");
|
|
181
211
|
});
|
|
212
|
+
|
|
213
|
+
it("should return structured error for invalid type", async () => {
|
|
214
|
+
const tool = createSysIOSummaryTool(
|
|
215
|
+
mockAdapter as unknown as MySQLAdapter,
|
|
216
|
+
);
|
|
217
|
+
const result = (await tool.handler(
|
|
218
|
+
{ type: "invalid_type" },
|
|
219
|
+
mockContext,
|
|
220
|
+
)) as { success: boolean; error: string };
|
|
221
|
+
|
|
222
|
+
expect(result.success).toBe(false);
|
|
223
|
+
expect(result.error).toContain("Invalid type");
|
|
224
|
+
expect(result.error).toContain("invalid_type");
|
|
225
|
+
expect(mockAdapter.executeQuery).not.toHaveBeenCalled();
|
|
226
|
+
});
|
|
182
227
|
});
|
|
183
228
|
});
|
|
@@ -124,7 +124,7 @@ describe("Sys Schema Resource Tools", () => {
|
|
|
124
124
|
expect(result.autoIncrementStatusCount).toBe(0);
|
|
125
125
|
});
|
|
126
126
|
|
|
127
|
-
it("should return
|
|
127
|
+
it("should return structured error for nonexistent schema (P154)", async () => {
|
|
128
128
|
// Mock schema existence check returning empty
|
|
129
129
|
mockAdapter.executeQuery.mockResolvedValueOnce(createMockQueryResult([]));
|
|
130
130
|
|
|
@@ -134,9 +134,12 @@ describe("Sys Schema Resource Tools", () => {
|
|
|
134
134
|
const result = (await tool.handler(
|
|
135
135
|
{ schema: "nonexistent_db" },
|
|
136
136
|
mockContext,
|
|
137
|
-
)) as {
|
|
137
|
+
)) as { success: boolean; error: string };
|
|
138
138
|
|
|
139
|
-
expect(result).toEqual({
|
|
139
|
+
expect(result).toEqual({
|
|
140
|
+
success: false,
|
|
141
|
+
error: "Schema 'nonexistent_db' does not exist",
|
|
142
|
+
});
|
|
140
143
|
// Should only call the schema check, not the 3 stats queries
|
|
141
144
|
expect(mockAdapter.executeQuery).toHaveBeenCalledTimes(1);
|
|
142
145
|
});
|
|
@@ -5,13 +5,22 @@
|
|
|
5
5
|
* 2 tools: user_summary, host_summary.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { z } from "zod";
|
|
8
|
+
import { z, ZodError } from "zod";
|
|
9
9
|
import type { MySQLAdapter } from "../../MySQLAdapter.js";
|
|
10
10
|
import type {
|
|
11
11
|
ToolDefinition,
|
|
12
12
|
RequestContext,
|
|
13
13
|
} from "../../../../types/index.js";
|
|
14
14
|
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Helpers
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
/** Extract human-readable messages from a ZodError instead of raw JSON array */
|
|
20
|
+
function formatZodError(error: ZodError): string {
|
|
21
|
+
return error.issues.map((i) => i.message).join("; ");
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
// =============================================================================
|
|
16
25
|
// Zod Schemas
|
|
17
26
|
// =============================================================================
|
|
@@ -45,9 +54,10 @@ export function createSysUserSummaryTool(
|
|
|
45
54
|
idempotentHint: true,
|
|
46
55
|
},
|
|
47
56
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
48
|
-
|
|
57
|
+
try {
|
|
58
|
+
const { user, limit } = UserSummarySchema.parse(params);
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
let query = `
|
|
51
61
|
SELECT
|
|
52
62
|
user,
|
|
53
63
|
statements,
|
|
@@ -61,19 +71,26 @@ export function createSysUserSummaryTool(
|
|
|
61
71
|
FROM sys.user_summary
|
|
62
72
|
`;
|
|
63
73
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
74
|
+
const queryParams: unknown[] = [];
|
|
75
|
+
if (user) {
|
|
76
|
+
query += " WHERE user = ?";
|
|
77
|
+
queryParams.push(user);
|
|
78
|
+
}
|
|
69
79
|
|
|
70
|
-
|
|
80
|
+
query += ` ORDER BY statement_latency DESC LIMIT ${String(limit)}`;
|
|
71
81
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
82
|
+
const result = await adapter.executeQuery(query, queryParams);
|
|
83
|
+
return {
|
|
84
|
+
users: result.rows,
|
|
85
|
+
count: result.rows?.length ?? 0,
|
|
86
|
+
};
|
|
87
|
+
} catch (error) {
|
|
88
|
+
if (error instanceof ZodError) {
|
|
89
|
+
return { success: false, error: formatZodError(error) };
|
|
90
|
+
}
|
|
91
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
92
|
+
return { success: false, error: message };
|
|
93
|
+
}
|
|
77
94
|
},
|
|
78
95
|
};
|
|
79
96
|
}
|
|
@@ -96,9 +113,10 @@ export function createSysHostSummaryTool(
|
|
|
96
113
|
idempotentHint: true,
|
|
97
114
|
},
|
|
98
115
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
99
|
-
|
|
116
|
+
try {
|
|
117
|
+
const { host, limit } = HostSummarySchema.parse(params);
|
|
100
118
|
|
|
101
|
-
|
|
119
|
+
let query = `
|
|
102
120
|
SELECT
|
|
103
121
|
host,
|
|
104
122
|
statements,
|
|
@@ -112,19 +130,26 @@ export function createSysHostSummaryTool(
|
|
|
112
130
|
FROM sys.host_summary
|
|
113
131
|
`;
|
|
114
132
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
133
|
+
const queryParams: unknown[] = [];
|
|
134
|
+
if (host) {
|
|
135
|
+
query += " WHERE host = ?";
|
|
136
|
+
queryParams.push(host);
|
|
137
|
+
}
|
|
120
138
|
|
|
121
|
-
|
|
139
|
+
query += ` ORDER BY statement_latency DESC LIMIT ${String(limit)}`;
|
|
122
140
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
const result = await adapter.executeQuery(query, queryParams);
|
|
142
|
+
return {
|
|
143
|
+
hosts: result.rows,
|
|
144
|
+
count: result.rows?.length ?? 0,
|
|
145
|
+
};
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error instanceof ZodError) {
|
|
148
|
+
return { success: false, error: formatZodError(error) };
|
|
149
|
+
}
|
|
150
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
151
|
+
return { success: false, error: message };
|
|
152
|
+
}
|
|
128
153
|
},
|
|
129
154
|
};
|
|
130
155
|
}
|
|
@@ -5,44 +5,55 @@
|
|
|
5
5
|
* 3 tools: statement_summary, wait_summary, io_summary.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { z } from "zod";
|
|
8
|
+
import { z, ZodError } from "zod";
|
|
9
9
|
import type { MySQLAdapter } from "../../MySQLAdapter.js";
|
|
10
10
|
import type {
|
|
11
11
|
ToolDefinition,
|
|
12
12
|
RequestContext,
|
|
13
13
|
} from "../../../../types/index.js";
|
|
14
14
|
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Helpers
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
/** Extract human-readable messages from a ZodError instead of raw JSON array */
|
|
20
|
+
function formatZodError(error: ZodError): string {
|
|
21
|
+
return error.issues.map((i) => i.message).join("; ");
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
// =============================================================================
|
|
16
25
|
// Zod Schemas
|
|
17
26
|
// =============================================================================
|
|
18
27
|
|
|
28
|
+
const VALID_ORDER_BY = [
|
|
29
|
+
"total_latency",
|
|
30
|
+
"exec_count",
|
|
31
|
+
"avg_latency",
|
|
32
|
+
"rows_sent",
|
|
33
|
+
"rows_examined",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
19
36
|
const StatementSummarySchema = z.object({
|
|
20
|
-
orderBy: z
|
|
21
|
-
.enum([
|
|
22
|
-
"total_latency",
|
|
23
|
-
"exec_count",
|
|
24
|
-
"avg_latency",
|
|
25
|
-
"rows_sent",
|
|
26
|
-
"rows_examined",
|
|
27
|
-
])
|
|
28
|
-
.default("total_latency")
|
|
29
|
-
.describe("Order results by"),
|
|
37
|
+
orderBy: z.string().default("total_latency").describe("Order results by"),
|
|
30
38
|
limit: z.number().default(20).describe("Maximum number of results"),
|
|
31
39
|
});
|
|
32
40
|
|
|
41
|
+
const VALID_WAIT_TYPES = [
|
|
42
|
+
"global",
|
|
43
|
+
"by_host",
|
|
44
|
+
"by_user",
|
|
45
|
+
"by_instance",
|
|
46
|
+
] as const;
|
|
47
|
+
|
|
33
48
|
const WaitSummarySchema = z.object({
|
|
34
|
-
type: z
|
|
35
|
-
.enum(["global", "by_host", "by_user", "by_instance"])
|
|
36
|
-
.default("global")
|
|
37
|
-
.describe("Type of wait summary"),
|
|
49
|
+
type: z.string().default("global").describe("Type of wait summary"),
|
|
38
50
|
limit: z.number().default(20).describe("Maximum number of results"),
|
|
39
51
|
});
|
|
40
52
|
|
|
53
|
+
const VALID_IO_TYPES = ["file", "table", "global"] as const;
|
|
54
|
+
|
|
41
55
|
const IOSummarySchema = z.object({
|
|
42
|
-
type: z
|
|
43
|
-
.enum(["file", "table", "global"])
|
|
44
|
-
.default("table")
|
|
45
|
-
.describe("Type of I/O summary"),
|
|
56
|
+
type: z.string().default("table").describe("Type of I/O summary"),
|
|
46
57
|
limit: z.number().default(20).describe("Maximum number of results"),
|
|
47
58
|
});
|
|
48
59
|
|
|
@@ -65,9 +76,19 @@ export function createSysStatementSummaryTool(
|
|
|
65
76
|
idempotentHint: true,
|
|
66
77
|
},
|
|
67
78
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
68
|
-
|
|
79
|
+
try {
|
|
80
|
+
const { orderBy, limit } = StatementSummarySchema.parse(params);
|
|
69
81
|
|
|
70
|
-
|
|
82
|
+
if (
|
|
83
|
+
!VALID_ORDER_BY.includes(orderBy as (typeof VALID_ORDER_BY)[number])
|
|
84
|
+
) {
|
|
85
|
+
return {
|
|
86
|
+
success: false,
|
|
87
|
+
error: `Invalid orderBy: '${orderBy}' — expected one of: ${VALID_ORDER_BY.join(", ")}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const query = `
|
|
71
92
|
SELECT
|
|
72
93
|
query,
|
|
73
94
|
db,
|
|
@@ -84,12 +105,19 @@ export function createSysStatementSummaryTool(
|
|
|
84
105
|
LIMIT ${String(limit)}
|
|
85
106
|
`;
|
|
86
107
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
108
|
+
const result = await adapter.executeQuery(query);
|
|
109
|
+
return {
|
|
110
|
+
statements: result.rows,
|
|
111
|
+
orderedBy: orderBy,
|
|
112
|
+
count: result.rows?.length ?? 0,
|
|
113
|
+
};
|
|
114
|
+
} catch (error) {
|
|
115
|
+
if (error instanceof ZodError) {
|
|
116
|
+
return { success: false, error: formatZodError(error) };
|
|
117
|
+
}
|
|
118
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
119
|
+
return { success: false, error: message };
|
|
120
|
+
}
|
|
93
121
|
},
|
|
94
122
|
};
|
|
95
123
|
}
|
|
@@ -113,13 +141,23 @@ export function createSysWaitSummaryTool(
|
|
|
113
141
|
idempotentHint: true,
|
|
114
142
|
},
|
|
115
143
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
116
|
-
|
|
144
|
+
try {
|
|
145
|
+
const { type, limit } = WaitSummarySchema.parse(params);
|
|
117
146
|
|
|
118
|
-
|
|
147
|
+
if (
|
|
148
|
+
!VALID_WAIT_TYPES.includes(type as (typeof VALID_WAIT_TYPES)[number])
|
|
149
|
+
) {
|
|
150
|
+
return {
|
|
151
|
+
success: false,
|
|
152
|
+
error: `Invalid type: '${type}' — expected one of: ${VALID_WAIT_TYPES.join(", ")}`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
119
155
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
156
|
+
let query: string;
|
|
157
|
+
|
|
158
|
+
switch (type) {
|
|
159
|
+
case "global":
|
|
160
|
+
query = `
|
|
123
161
|
SELECT
|
|
124
162
|
events,
|
|
125
163
|
total,
|
|
@@ -129,9 +167,9 @@ export function createSysWaitSummaryTool(
|
|
|
129
167
|
ORDER BY total_latency DESC
|
|
130
168
|
LIMIT ${String(limit)}
|
|
131
169
|
`;
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
170
|
+
break;
|
|
171
|
+
case "by_host":
|
|
172
|
+
query = `
|
|
135
173
|
SELECT
|
|
136
174
|
host,
|
|
137
175
|
event,
|
|
@@ -142,9 +180,9 @@ export function createSysWaitSummaryTool(
|
|
|
142
180
|
ORDER BY total_latency DESC
|
|
143
181
|
LIMIT ${String(limit)}
|
|
144
182
|
`;
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
183
|
+
break;
|
|
184
|
+
case "by_user":
|
|
185
|
+
query = `
|
|
148
186
|
SELECT
|
|
149
187
|
user,
|
|
150
188
|
event,
|
|
@@ -155,9 +193,9 @@ export function createSysWaitSummaryTool(
|
|
|
155
193
|
ORDER BY total_latency DESC
|
|
156
194
|
LIMIT ${String(limit)}
|
|
157
195
|
`;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
break;
|
|
197
|
+
case "by_instance":
|
|
198
|
+
query = `
|
|
161
199
|
SELECT
|
|
162
200
|
event_name AS event,
|
|
163
201
|
count_star AS total,
|
|
@@ -167,15 +205,24 @@ export function createSysWaitSummaryTool(
|
|
|
167
205
|
ORDER BY sum_timer_wait DESC
|
|
168
206
|
LIMIT ${String(limit)}
|
|
169
207
|
`;
|
|
170
|
-
|
|
171
|
-
|
|
208
|
+
break;
|
|
209
|
+
default:
|
|
210
|
+
throw new Error(`Unexpected type: ${type}`);
|
|
211
|
+
}
|
|
172
212
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
213
|
+
const result = await adapter.executeQuery(query);
|
|
214
|
+
return {
|
|
215
|
+
waits: result.rows,
|
|
216
|
+
type,
|
|
217
|
+
count: result.rows?.length ?? 0,
|
|
218
|
+
};
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (error instanceof ZodError) {
|
|
221
|
+
return { success: false, error: formatZodError(error) };
|
|
222
|
+
}
|
|
223
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
224
|
+
return { success: false, error: message };
|
|
225
|
+
}
|
|
179
226
|
},
|
|
180
227
|
};
|
|
181
228
|
}
|
|
@@ -197,13 +244,21 @@ export function createSysIOSummaryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
197
244
|
idempotentHint: true,
|
|
198
245
|
},
|
|
199
246
|
handler: async (params: unknown, _context: RequestContext) => {
|
|
200
|
-
|
|
247
|
+
try {
|
|
248
|
+
const { type, limit } = IOSummarySchema.parse(params);
|
|
249
|
+
|
|
250
|
+
if (!VALID_IO_TYPES.includes(type as (typeof VALID_IO_TYPES)[number])) {
|
|
251
|
+
return {
|
|
252
|
+
success: false,
|
|
253
|
+
error: `Invalid type: '${type}' — expected one of: ${VALID_IO_TYPES.join(", ")}`,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
201
256
|
|
|
202
|
-
|
|
257
|
+
let query: string;
|
|
203
258
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
259
|
+
switch (type) {
|
|
260
|
+
case "file":
|
|
261
|
+
query = `
|
|
207
262
|
SELECT
|
|
208
263
|
file,
|
|
209
264
|
count_read,
|
|
@@ -218,9 +273,9 @@ export function createSysIOSummaryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
218
273
|
ORDER BY total DESC
|
|
219
274
|
LIMIT ${String(limit)}
|
|
220
275
|
`;
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
276
|
+
break;
|
|
277
|
+
case "table":
|
|
278
|
+
query = `
|
|
224
279
|
SELECT
|
|
225
280
|
table_schema,
|
|
226
281
|
table_name,
|
|
@@ -236,9 +291,9 @@ export function createSysIOSummaryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
236
291
|
ORDER BY (fetch_latency + insert_latency + update_latency + delete_latency) DESC
|
|
237
292
|
LIMIT ${String(limit)}
|
|
238
293
|
`;
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
294
|
+
break;
|
|
295
|
+
case "global":
|
|
296
|
+
query = `
|
|
242
297
|
SELECT
|
|
243
298
|
event_name,
|
|
244
299
|
total,
|
|
@@ -248,15 +303,24 @@ export function createSysIOSummaryTool(adapter: MySQLAdapter): ToolDefinition {
|
|
|
248
303
|
ORDER BY total_latency DESC
|
|
249
304
|
LIMIT ${String(limit)}
|
|
250
305
|
`;
|
|
251
|
-
|
|
252
|
-
|
|
306
|
+
break;
|
|
307
|
+
default:
|
|
308
|
+
throw new Error(`Unexpected type: ${type}`);
|
|
309
|
+
}
|
|
253
310
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
311
|
+
const result = await adapter.executeQuery(query);
|
|
312
|
+
return {
|
|
313
|
+
ioStats: result.rows,
|
|
314
|
+
type,
|
|
315
|
+
count: result.rows?.length ?? 0,
|
|
316
|
+
};
|
|
317
|
+
} catch (error) {
|
|
318
|
+
if (error instanceof ZodError) {
|
|
319
|
+
return { success: false, error: formatZodError(error) };
|
|
320
|
+
}
|
|
321
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
322
|
+
return { success: false, error: message };
|
|
323
|
+
}
|
|
260
324
|
},
|
|
261
325
|
};
|
|
262
326
|
}
|