wip
This commit is contained in:
parent
3f308597c7
commit
a75833f77a
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
enum MatchCondition: string
|
enum MatchCondition: string
|
||||||
{
|
{
|
||||||
case And = 'AND';
|
case ALL = 'ALL';
|
||||||
case Or = 'OR';
|
case ANY = 'ANY';
|
||||||
|
case NONE = 'NONE';
|
||||||
}
|
}
|
||||||
|
16
app/Models/AssignmentPanel.php
Normal file
16
app/Models/AssignmentPanel.php
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use App\Models\Traits\Active;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
|
|
||||||
|
class AssignmentPanel extends BaseModel
|
||||||
|
{
|
||||||
|
use Active;
|
||||||
|
|
||||||
|
public function radiologists(): HasManyThrough
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(User::class, AssignmentPanelRadiologist::class);
|
||||||
|
}
|
||||||
|
}
|
5
app/Models/AssignmentPanelRadiologist.php
Normal file
5
app/Models/AssignmentPanelRadiologist.php
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
class AssignmentPanelRadiologist extends BaseModel {}
|
@ -6,6 +6,7 @@
|
|||||||
use App\Models\Traits\Active;
|
use App\Models\Traits\Active;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
|
|
||||||
class DicomRoutingRule extends BaseModel
|
class DicomRoutingRule extends BaseModel
|
||||||
{
|
{
|
||||||
@ -26,6 +27,16 @@ public function facility(): BelongsTo
|
|||||||
return $this->belongsTo(Facility::class);
|
return $this->belongsTo(Facility::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function panel(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(AssignmentPanel::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function radiologist(): HasOne
|
||||||
|
{
|
||||||
|
return $this->hasOne(User::class, 'radiologist_id');
|
||||||
|
}
|
||||||
|
|
||||||
protected function casts(): array
|
protected function casts(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
|
||||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||||
use Illuminate\Notifications\Notifiable;
|
use Illuminate\Notifications\Notifiable;
|
||||||
@ -173,6 +174,11 @@ public function canAccessPanel(Panel $panel): bool
|
|||||||
return $this->isAdmin();
|
return $this->isAdmin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function panels(): HasManyThrough
|
||||||
|
{
|
||||||
|
return $this->hasManyThrough(AssignmentPanel::class, AssignmentPanelRadiologist::class);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the attributes that should be cast.
|
* Get the attributes that should be cast.
|
||||||
*
|
*
|
||||||
|
@ -108,7 +108,7 @@ public function fetchStudyDetails(string $orthanc_uuid): ?array
|
|||||||
public function transformData(mixed $orthanc_src): array
|
public function transformData(mixed $orthanc_src): array
|
||||||
{
|
{
|
||||||
$inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName');
|
$inst_name = data_get($orthanc_src, 'MainDicomTags.InstitutionName');
|
||||||
$inst_id = DicomStudyRouter::map($inst_name);
|
$inst_id = DicomStudyRouter::matchStudy($inst_name);
|
||||||
|
|
||||||
$patient_name = data_get($orthanc_src, 'PatientMainDicomTags.PatientName');
|
$patient_name = data_get($orthanc_src, 'PatientMainDicomTags.PatientName');
|
||||||
|
|
||||||
|
@ -2,45 +2,137 @@
|
|||||||
|
|
||||||
namespace App\Services\StudyRouter;
|
namespace App\Services\StudyRouter;
|
||||||
|
|
||||||
|
use App\Domain\ACL\Role;
|
||||||
|
use App\Domain\Rule\MatchCondition;
|
||||||
use App\Domain\Rule\MatchMode;
|
use App\Domain\Rule\MatchMode;
|
||||||
|
use App\Models\AssignmentPanel;
|
||||||
use App\Models\DicomRoutingRule;
|
use App\Models\DicomRoutingRule;
|
||||||
use App\Services\ContentMatcher;
|
use App\Models\DicomRuleCondition;
|
||||||
|
use App\Models\Institute;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
use Illuminate\Support\Facades\Cache;
|
use Illuminate\Support\Facades\Cache;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
final class DicomStudyRouter
|
final class DicomStudyRouter
|
||||||
{
|
{
|
||||||
private static ?Collection $rules;
|
private static ?Collection $rules;
|
||||||
|
private static ?Collection $activeRads;
|
||||||
private static int $catchAll = -1;
|
private static int $catchAll = -1;
|
||||||
|
const int CACHE_TTL = 15;
|
||||||
|
|
||||||
/**
|
private static function fallbackRoute(): array
|
||||||
* @return array<int, ?int>
|
|
||||||
*/
|
|
||||||
public static function map(?string $input): array
|
|
||||||
{
|
{
|
||||||
if (is_null(self::$rules)) {
|
return [
|
||||||
self::$rules = Cache::remember('dicom.routers',
|
'institute_id' => self::$catchAll,
|
||||||
now()->addMinutes(15),
|
'facility_id' => null,
|
||||||
fn () => DicomRoutingRule::active()->with('conditions')->get()
|
'rule_id' => null,
|
||||||
);
|
'radiologists' => null,
|
||||||
self::$catchAll = DB::table('institutes')
|
];
|
||||||
->where('name', 'Catch-all')
|
}
|
||||||
->first('id')
|
|
||||||
->id;
|
public static function matchStudy(array $dicomHeaders): array
|
||||||
|
{
|
||||||
|
self::initialize();
|
||||||
|
|
||||||
|
if (blank($dicomHeaders)) {
|
||||||
|
return self::fallbackRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (! blank($input)) {
|
foreach (self::$rules as $rule) {
|
||||||
$input = strtolower($input);
|
$conditions = $rule->conditions()->orderByDesc('priority')->get();
|
||||||
|
$matchCondition = MatchCondition::from($rule->match_condition);
|
||||||
|
$matches = $matchCondition === MatchCondition::ALL
|
||||||
|
? $conditions->every(fn ($condition) => self::matchCondition($condition, $dicomHeaders))
|
||||||
|
: $conditions->contains(fn ($condition) => self::matchCondition($condition, $dicomHeaders));
|
||||||
|
|
||||||
foreach (self::$rules as $pattern) {
|
if ($matches) {
|
||||||
if (ContentMatcher::match($input, $pattern->name, MatchMode::from($pattern->match_mode))) {
|
return [
|
||||||
return [$pattern->institute_id, $pattern->facility_id];
|
'institute_id' => $rule->institute_id,
|
||||||
}
|
'facility_id' => $rule->facility_id,
|
||||||
|
'rule_id' => $rule->id,
|
||||||
|
'radiologists' => self::getRadiologists($rule),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return [self::$catchAll, null];
|
return self::fallbackRoute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function initialize(): void
|
||||||
|
{
|
||||||
|
if (is_null(self::$rules)) {
|
||||||
|
self::$rules = Cache::remember('dicom.rules',
|
||||||
|
now()->addMinutes(self::CACHE_TTL),
|
||||||
|
fn () => DicomRoutingRule::active()
|
||||||
|
->with('conditions')
|
||||||
|
->orderByDesc('priority')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_null(self::$activeRads)) {
|
||||||
|
self::$activeRads = Cache::remember('dicom.rads',
|
||||||
|
now()->addMinutes(self::CACHE_TTL),
|
||||||
|
fn () => User::active()
|
||||||
|
->role(Role::Radiologist)
|
||||||
|
->pluck('id')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (self::$catchAll < 0) {
|
||||||
|
self::$catchAll = Institute::where('name', 'CATCH-ALL')
|
||||||
|
->first('id')
|
||||||
|
->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function matchCondition(DicomRuleCondition $condition, array $dicomHeaders): bool
|
||||||
|
{
|
||||||
|
$dicomTag = $condition->dicom_tag;
|
||||||
|
$dicomValue = $dicomHeaders[$dicomTag] ?? '';
|
||||||
|
$searchPattern = $condition->search_pattern;
|
||||||
|
$matchMode = MatchMode::from($condition->match_mode);
|
||||||
|
|
||||||
|
if (! $condition->case_sensitive) {
|
||||||
|
$dicomValue = strtolower($dicomValue);
|
||||||
|
if ($matchMode != MatchMode::Regex) {
|
||||||
|
$searchPattern = strtolower($searchPattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ($matchMode) {
|
||||||
|
case MatchMode::Exact:
|
||||||
|
return $dicomValue === $searchPattern;
|
||||||
|
case MatchMode::StartsWith:
|
||||||
|
return str_starts_with($dicomValue, $searchPattern);
|
||||||
|
case MatchMode::EndsWith:
|
||||||
|
return str_ends_with($dicomValue, $searchPattern);
|
||||||
|
case MatchMode::Contains:
|
||||||
|
return str_contains($dicomValue, $searchPattern);
|
||||||
|
case MatchMode::Regex:
|
||||||
|
return preg_match($searchPattern, $dicomValue) === 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function getRadiologists(DicomRoutingRule $rule): array
|
||||||
|
{
|
||||||
|
if ($rule->assignment_panel_id) {
|
||||||
|
$panel = AssignmentPanel::active()
|
||||||
|
->with('radiologists:id')
|
||||||
|
->find($rule->assignment_panel_id);
|
||||||
|
if ($panel) {
|
||||||
|
$rads = $panel->radiologists->pluck('id')->toArray();
|
||||||
|
if (! empty($rads)) {
|
||||||
|
// return only existing id from self::$activeRads
|
||||||
|
return array_intersect($rads, self::$activeRads->toArray());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} elseif ($rule->radiologist_id) {
|
||||||
|
if (self::$activeRads->contains($rule->radiologist_id)) {
|
||||||
|
return [$rule->radiologist_id];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,23 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('assignment_panels', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->boolean('is_active')->index();
|
||||||
|
$table->string('name')->unique();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('assignment_panels');
|
||||||
|
}
|
||||||
|
};
|
@ -0,0 +1,26 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use App\Models\AssignmentPanel;
|
||||||
|
use App\Models\User;
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('assignment_panel_radiologists', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignIdFor(AssignmentPanel::class)->constrained()->cascadeOnDelete();
|
||||||
|
$table->foreignIdFor(User::class)->constrained()->cascadeOnDelete();
|
||||||
|
$table->timestamps();
|
||||||
|
$table->unique(['assignment_panel_id', 'user_id']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('assignment_panel_radiologists');
|
||||||
|
}
|
||||||
|
};
|
@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Domain\Rule\MatchCondition;
|
use App\Domain\Rule\MatchCondition;
|
||||||
|
use App\Models\AssignmentPanel;
|
||||||
use App\Models\Facility;
|
use App\Models\Facility;
|
||||||
use App\Models\Institute;
|
use App\Models\Institute;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Database\Migrations\Migration;
|
use Illuminate\Database\Migrations\Migration;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
@ -16,8 +18,10 @@ public function up(): void
|
|||||||
$table->boolean('is_active')->default(true);
|
$table->boolean('is_active')->default(true);
|
||||||
$table->foreignIdFor(Institute::class)->constrained()->cascadeOnDelete();
|
$table->foreignIdFor(Institute::class)->constrained()->cascadeOnDelete();
|
||||||
$table->foreignIdFor(Facility::class)->nullable()->constrained()->cascadeOnDelete();
|
$table->foreignIdFor(Facility::class)->nullable()->constrained()->cascadeOnDelete();
|
||||||
|
$table->foreignIdFor(User::class, 'radiologist_id')->nullable()->constrained()->nullOnDelete();
|
||||||
|
$table->foreignIdFor(AssignmentPanel::class)->nullable()->constrained()->nullOnDelete();
|
||||||
$table->string('description')->nullable();
|
$table->string('description')->nullable();
|
||||||
$table->string('match_condition')->default(MatchCondition::Or->value);
|
$table->string('match_condition')->default(MatchCondition::ANY->value);
|
||||||
$table->unsignedTinyInteger('priority')->default(0);
|
$table->unsignedTinyInteger('priority')->default(0);
|
||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ class InstituteSeeder extends Seeder
|
|||||||
public function run(): void
|
public function run(): void
|
||||||
{
|
{
|
||||||
Institute::create([
|
Institute::create([
|
||||||
'name' => 'Catch-all',
|
'name' => 'CATCH-ALL',
|
||||||
'is_active' => true,
|
'is_active' => true,
|
||||||
]);
|
]);
|
||||||
$chev = Institute::create([
|
$chev = Institute::create([
|
||||||
|
98
package-lock.json
generated
98
package-lock.json
generated
@ -97,27 +97,27 @@
|
|||||||
"@prettier/plugin-php": "0.22.1",
|
"@prettier/plugin-php": "0.22.1",
|
||||||
"@rollup/plugin-html": "1.0.3",
|
"@rollup/plugin-html": "1.0.3",
|
||||||
"@types/typeahead": "0.11.32",
|
"@types/typeahead": "0.11.32",
|
||||||
"ajv": "8.16.0",
|
"ajv": "^8.17.1",
|
||||||
"autoprefixer": "10.4.19",
|
"autoprefixer": "10.4.19",
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"babel-loader": "9.1.3",
|
"babel-loader": "9.1.3",
|
||||||
"browser-sync": "2.29.3",
|
"browser-sync": "2.29.3",
|
||||||
"cross-env": "7.0.3",
|
"cross-env": "7.0.3",
|
||||||
"eslint": "8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-config-airbnb-base": "15.0.0",
|
"eslint-config-airbnb-base": "15.0.0",
|
||||||
"eslint-config-prettier": "9.1.0",
|
"eslint-config-prettier": "9.1.0",
|
||||||
"eslint-plugin-import": "2.29.1",
|
"eslint-plugin-import": "2.29.1",
|
||||||
"eslint-plugin-prettier": "5.1.3",
|
"eslint-plugin-prettier": "5.1.3",
|
||||||
"glob": "10.4.5",
|
"glob": "^10.4.5",
|
||||||
"laravel-datatables-vite": "^0.6.1",
|
"laravel-datatables-vite": "^0.6.1",
|
||||||
"laravel-vite-plugin": "^1.1.1",
|
"laravel-vite-plugin": "^1.1.1",
|
||||||
"lodash": "4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"postcss": "8.4.39",
|
"postcss": "^8.5.1",
|
||||||
"prettier": "3.2.2",
|
"prettier": "^3.4.2",
|
||||||
"resolve-url-loader": "5.0.0",
|
"resolve-url-loader": "^5.0.0",
|
||||||
"sass": "1.76.0",
|
"sass": "1.76.0",
|
||||||
"sass-loader": "14.0.0",
|
"sass-loader": "^14.0.0",
|
||||||
"vite": "^6.0.7",
|
"vite": "^6.0.10",
|
||||||
"vite-plugin-static-copy": "^2.2.0"
|
"vite-plugin-static-copy": "^2.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -4906,16 +4906,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.16.0",
|
"version": "8.17.1",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
|
||||||
"integrity": "sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw==",
|
"integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fast-deep-equal": "^3.1.3",
|
"fast-deep-equal": "^3.1.3",
|
||||||
|
"fast-uri": "^3.0.1",
|
||||||
"json-schema-traverse": "^1.0.0",
|
"json-schema-traverse": "^1.0.0",
|
||||||
"require-from-string": "^2.0.2",
|
"require-from-string": "^2.0.2"
|
||||||
"uri-js": "^4.4.1"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "github",
|
"type": "github",
|
||||||
@ -7513,6 +7513,23 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/fast-uri": {
|
||||||
|
"version": "3.0.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
|
||||||
|
"integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
|
||||||
|
"dev": true,
|
||||||
|
"funding": [
|
||||||
|
{
|
||||||
|
"type": "github",
|
||||||
|
"url": "https://github.com/sponsors/fastify"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/fastify"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "BSD-3-Clause"
|
||||||
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.18.0",
|
"version": "1.18.0",
|
||||||
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz",
|
||||||
@ -10375,9 +10392,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/postcss": {
|
"node_modules/postcss": {
|
||||||
"version": "8.4.39",
|
"version": "8.5.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz",
|
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz",
|
||||||
"integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==",
|
"integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@ -10395,9 +10412,9 @@
|
|||||||
],
|
],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.7",
|
"nanoid": "^3.3.8",
|
||||||
"picocolors": "^1.0.1",
|
"picocolors": "^1.1.1",
|
||||||
"source-map-js": "^1.2.0"
|
"source-map-js": "^1.2.1"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "^10 || ^12 || >=14"
|
"node": "^10 || ^12 || >=14"
|
||||||
@ -10437,9 +10454,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/prettier": {
|
"node_modules/prettier": {
|
||||||
"version": "3.2.2",
|
"version": "3.4.2",
|
||||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz",
|
||||||
"integrity": "sha512-HTByuKZzw7utPiDO523Tt2pLtEyK7OibUD9suEJQrPUCYQqrHr74GGX6VidMrovbf/I50mPqr8j/II6oBAuc5A==",
|
"integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@ -12927,9 +12944,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.0.7",
|
"version": "6.0.11",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.0.11.tgz",
|
||||||
"integrity": "sha512-RDt8r/7qx9940f8FcOIAH9PTViRrghKaK2K1jY3RaAURrEUbm9Du1mJ72G+jlhtG3WwodnfzY8ORQZbBavZEAQ==",
|
"integrity": "sha512-4VL9mQPKoHy4+FE0NnRE/kbY51TOfaknxAjt3fJbGJxhIpBZiqVzlZDEesWWsuREXHwNdAoOFZ9MkPEVXczHwg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -13066,35 +13083,6 @@
|
|||||||
"node": ">= 10.0.0"
|
"node": ">= 10.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite/node_modules/postcss": {
|
|
||||||
"version": "8.4.49",
|
|
||||||
"resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz",
|
|
||||||
"integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==",
|
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
|
||||||
{
|
|
||||||
"type": "opencollective",
|
|
||||||
"url": "https://opencollective.com/postcss/"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "tidelift",
|
|
||||||
"url": "https://tidelift.com/funding/github/npm/postcss"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/ai"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"nanoid": "^3.3.7",
|
|
||||||
"picocolors": "^1.1.1",
|
|
||||||
"source-map-js": "^1.2.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^10 || ^12 || >=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/vt-pbf": {
|
"node_modules/vt-pbf": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/vt-pbf/-/vt-pbf-3.1.3.tgz",
|
||||||
|
Loading…
Reference in New Issue
Block a user