initial commit

This commit is contained in:
Mathias Beaulieu-Duncan 2025-06-23 17:25:57 -04:00
commit 18eaa2daaa
Signed by: mathias
GPG Key ID: 1C16CF05BAF9162D
22 changed files with 1964 additions and 0 deletions

View File

@ -0,0 +1,44 @@
name: Publish to npm
on:
release:
types: [published]
jobs:
publish:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
registry-url: 'https://registry.npmjs.org/'
- name: Install dependencies
run: npm ci
- name: Run type check
run: npm run type-check
- name: Build library
run: npm run build
- name: Determine npm tag
id: determine_tag
run: |
IS_PRERELEASE=${{ github.event.release.prerelease }}
VERSION=$(node -p "require('./package.json').version")
if [[ "$VERSION" =~ -(alpha|beta|rc|dev) ]]; then
echo "NPM_TAG=dev" >> $GITHUB_OUTPUT
else
echo "NPM_TAG=latest" >> $GITHUB_OUTPUT
fi
- name: Publish to npm
run: npm publish --access public --tag ${{ steps.determine_tag.outputs.NPM_TAG }}
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

24
.gitignore vendored Normal file
View File

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

324
README.md Normal file
View File

@ -0,0 +1,324 @@
# @openharbor/vue-data
A Vue 3 Composition API library for simplified data management with CQRS backends, powered by a flexible builder pattern. This library helps you effortlessly connect your Vue components to backend services using a POST-centric interaction model, providing reactive data, loading states, and robust error handling.
## Features
* **Vue 3 Composition API:** Fully integrated with Vue's reactivity system.
* **CQRS-Oriented:** Designed for backends that use POST requests for both queries and commands.
* **Flexible Data Source Builders:** Configure data fetching and command execution with a fluent API.
* **Reactive State Management:** Automatically tracks `data`/`items`, `total`, `loading` status, and `error` states.
* **Pluggable Commands:** Easily define domain-specific commands (e.g., `AddProduct`, `RemoveProduct`, `UpdateProductDetails`, `PlaceOrder`).
* **Standardized Error Handling:** Consistent approach to backend error messages and validation errors.
---
## Installation
To use this library in your Vue 3 project, you need to install it via npm or Yarn. Remember that `vue` is a peer dependency, so ensure you have Vue 3 installed in your project.
```bash
# Using npm
npm install @openharbor/vue-data
# Using Yarn
yarn add @openharbor/vue-data
```
## Usage
The library provides three main composables: `useHttpDataSource`, `useSingleDataSource`, and `useListDataSource`, each tailored for different data interaction patterns.
### Core Concepts
* **Query Criteria (`IQueryCriteria`):** An object defining the parameters for your data requests (e.g., filters, pagination, sorting).
* **Models (`TModel`):** The shape of the data objects you are fetching or sending as commands.
* **Commands (`ICommand`):** The data payload sent to your backend for actions (e.g., `AddProductCommand`, `RemoveProductCommand`, `UpdateProductDetailsCommand`).
* **`keyResolver`:** A function `(model: TModel) => TModel[keyof TModel]` that extracts a unique identifier (like an `id`) from your model. This is crucial for `useSingleDataSource` and for certain command resolution scenarios.
* **`autoFetch`:** A boolean option to automatically trigger a `read` operation when the component mounts or when `criteria` changes.
### `useHttpDataSource`
This is the most generic data source composable. It's suitable for a wide range of querying and commanding needs where the structure isn't strictly a single item or a traditional list.
```typescript
// Example: src/views/MyGenericDataComponent.vue
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
import { useHttpDataSource, type IQueryCriteria } from '@openharbor/vue-data'; // <-- Updated import path
interface Product {
id: string;
name: string;
price: number;
}
interface ProductQueryCriteria extends IQueryCriteria {
searchText?: string;
minPrice?: number;
}
const productDataSource = useHttpDataSource<ProductQueryCriteria, Product>({
queryUrl: '/api/products/query', // Your backend's POST endpoint for querying products
keyResolver: (product) => product.id,
commands: {
// Command to add a new product
'addProduct': {
url: '/api/products/add', // Your backend's POST endpoint for adding a product
},
// Command to update product details
'updateProductDetails': {
url: '/api/products/update', // Your backend's POST endpoint for updating
},
// Command to remove a product
'removeProduct': {
url: '/api/products/remove', // Your backend's POST endpoint for removing
}
},
defaultCriteria: {
searchText: '',
minPrice: 0
},
autoFetch: true // Fetch data on mount and when criteria changes
});
// Access reactive state
const { data, total, loading, error, criteria, read, executeCommand } = productDataSource;
// Modify criteria to trigger a re-fetch (if autoFetch is true)
criteria.value.searchText = 'new search term';
// Manually trigger a read (overrides current reactive criteria for this call)
// await read({ searchText: 'specific term' });
// Execute an 'addProduct' command
const addProduct = async () => {
try {
const newProduct = await executeCommand('addProduct', {
name: 'New Gadget',
price: 99.99
});
console.log('Product added:', newProduct);
// After adding, you might want to refresh the list manually
await read();
} catch (e) {
console.error('Error adding product:', e);
}
};
// Execute a 'removeProduct' command
const removeProductById = async (id: string) => {
try {
// Assuming your removeProduct command expects an object like { productId: '...' } as payload
await executeCommand('removeProduct', { productId: id });
console.log('Product removed:', id);
// After removing, autoFetch might re-trigger, or you can manually read()
} catch (e) {
console.error('Error removing product:', e);
}
};
watchEffect(() => {
if (error.value) {
console.error('Data source error:', error.value);
}
});
</script>
<template>
<div>
<h1>Products</h1>
<input v-model="criteria.searchText" placeholder="Search products" />
<p v-if="loading">Loading products...</p>
<p v-if="error">Error: {{ error.message }}</p>
<ul v-if="data">
<li v-for="product in data" :key="product.id">
{{ product.name }} - ${{ product.price }}
<button @click="removeProductById(product.id)">Remove</button>
</li>
</ul>
<p v-if="total !== null">Total products: {{ total }}</p>
<button @click="addProduct">Add New Product</button>
</div>
</template>
```
### `useSingleDataSource`
Best for managing a single entity (e.g., editing a product details page, displaying a user profile). It requires a `keyResolver` and can easily set up common CQRS commands for a single entity.
```typescript
// Example: src/views/ProductDetailComponent.vue
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
import { useSingleDataSource, type IQueryCriteria } from '@openharbor/vue-data'; // <-- Updated import path
interface Product {
id: string;
name: string;
description: string;
}
interface ProductDetailQueryCriteria extends IQueryCriteria {
productId: string;
}
const props = defineProps<{ id: string }>();
// Define reactive form data for editing
const editedProduct = ref<Product | null>(null);
const productDetailSource = useSingleDataSource<ProductDetailQueryCriteria, Product>({
queryUrl: '/api/products/detail', // Endpoint to fetch single product details
keyResolver: (product) => product.id,
defaultCriteria: {
productId: props.id // Initial query based on prop
},
// Automatically add common commands for a single entity
addStandardRestCommands: { // NOTE: This option name can be changed to 'addStandardCqrsCommands'
route: '/api/commands/products' // A single command endpoint for product-related commands
},
autoFetch: true // Fetch details on mount
});
const { data, loading, error, criteria, read, executeCommand } = productDetailSource;
// Watch for changes in the fetched data to populate the form
watchEffect(() => {
if (data.value) {
editedProduct.value = { ...data.value }; // Create a copy for editing
}
});
// Update criteria if props.id changes
watch(() => props.id, (newId) => {
if (newId) {
criteria.value.productId = newId;
}
}, { immediate: true });
const saveProductChanges = async () => {
if (!editedProduct.value) return;
try {
// Assuming 'update' command expects the full product object as payload
// Command type would likely be inferred by the backend from the payload, or a specific command object.
const updated = await executeCommand('update', editedProduct.value);
console.log('Product updated:', updated);
// After update, data.value will automatically be updated by the composable
} catch (e) {
console.error('Error saving product changes:', e);
}
};
const removeSelectedProduct = async () => {
if (!data.value) return;
if (!confirm(`Are you sure you want to remove ${data.value.name}?`)) return;
try {
// Assuming 'delete' (now 'removeProduct') command expects the product object or its ID in the payload
await executeCommand('delete', { productId: data.value.id }); // Using 'delete' as internal key for addStandardRestCommands
console.log('Product removed!');
// After remove, data.value will be set to null by the composable
// You might want to redirect the user after removal
} catch (e) {
console.error('Error removing product:', e);
}
};
</script>
<template>
<div>
<h1 v-if="loading">Loading Product Details...</h1>
<p v-if="error">Error: {{ error.message }}</p>
<div v-if="editedProduct">
<h2>Edit Product: {{ editedProduct.name }}</h2>
<label>Name: <input v-model="editedProduct.name" /></label><br />
<label>Description: <textarea v-model="editedProduct.description"></textarea></label><br />
<button @click="saveProductChanges">Save Changes</button>
<button @click="removeSelectedProduct">Remove Product</button>
</div>
</div>
</template>
```
### `useListDataSource`
Ideal for displaying collections of data, such as tables, grids, or paginated lists. It functions as a semantic wrapper around `useHttpDataSource` for clarity.
```typescript
// Example: src/views/ProductListingComponent.vue
<script setup lang="ts">
import { ref, watchEffect } from 'vue';
import { useListDataSource, type IQueryCriteria } from '@openharbor/vue-data'; // <-- Updated import path
interface Product {
id: string;
name: string;
category: string;
}
interface ProductsListQueryCriteria extends IQueryCriteria {
pageNumber: number;
pageSize: number;
sortBy: string;
filterByCategory?: string;
}
const paginationCriteria = ref<ProductsListQueryCriteria>({
pageNumber: 1,
pageSize: 10,
sortBy: 'name'
});
const productsListSource = useListDataSource<ProductsListQueryCriteria, Product>({
queryUrl: '/api/products/list', // Your backend's POST endpoint for listing products
defaultCriteria: paginationCriteria.value,
autoFetch: true // Fetch when criteria changes
});
const { items, total, loading, error, criteria, read } = productsListSource;
const nextPage = () => {
if (total.value && criteria.value.pageNumber * criteria.value.pageSize < total.value) {
criteria.value.pageNumber++;
}
};
const prevPage = () => {
if (criteria.value.pageNumber > 1) {
criteria.value.pageNumber--;
}
};
// You can still add commands here if needed for list-level operations (e.g., bulk remove)
// productsListSource.executeCommand('bulkRemove', { productIds: ['id1', 'id2'] });
watchEffect(() => {
if (error.value) {
console.error('Product list error:', error.value);
}
});
</script>
<template>
<div>
<h1>Product Catalog</h1>
<p v-if="loading">Loading products...</p>
<p v-if="error">Error: {{ error.message }}</p>
<div v-if="items">
<ul>
<li v-for="product in items" :key="product.id">
{{ product.name }} ({{ product.category }})
</li>
</ul>
<div>
<button @click="prevPage" :disabled="criteria.pageNumber === 1">Previous</button>
<span>Page {{ criteria.pageNumber }} of {{ total ? Math.ceil(total / criteria.pageSize) : 1 }}</span>
<button @click="nextPage" :disabled="total ? criteria.value.pageNumber * criteria.value.pageSize >= total : true">Next</button>
</div>
</div>
</div>
</template>
```

13
index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

47
package.json Normal file
View File

@ -0,0 +1,47 @@
{
"name": "@openharbor/vue-data",
"author": "Open Harbor",
"private": false,
"license": "MIT",
"version": "1.0.0",
"description": "Vue 3 Composition API implementation for OpenHarbor data management.",
"type": "module",
"main": "./dist/vue-openharbor-data.umd.cjs",
"module": "./dist/vue-openharbor-data.js",
"types": "./dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/vue-openharbor-data.js",
"require": "./dist/vue-openharbor-data.umd.cjs"
}
},
"files": [
"dist"
],
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"type-check": "vue-tsc --noEmit --composite false"
},
"peerDependenciesMeta": {
"vue": {
"optional": false
}
},
"peerDependencies": {
"vue": "^3.4.0"
},
"devDependencies": {
"@tsconfig/node20": "^20.1.6",
"@types/node": "^24.0.3",
"@vitejs/plugin-vue": "^5.2.3",
"@vue/tsconfig": "^0.7.0",
"typescript": "~5.8.3",
"vite": "^6.3.5",
"vue": "^3.5.13",
"vue-tsc": "^2.2.8"
},
"packageManager": "yarn@1.22.22+sha1.ac34549e6aa8e7ead463a7407e1c7390f61a6610"
}

0
src/composables/index.ts Normal file
View File

View File

@ -0,0 +1,157 @@
// src/composables/use-http-data-source.ts
import { ref, readonly, watch, type Ref } from 'vue';
import {
type IQueryCriteria,
type ICommand,
type IQueryExecutionResult,
type IQueryExecutionGroupResult,
type IDataSourceError,
type IDataSourceOptions,
type IResolveCommandModelEvent
} from '../core/types';
import { HttpDataSourceBuilder } from '../core/http-data-source-builder.ts';
export interface UseHttpDataSourceOptions<TQuery extends IQueryCriteria, TModel extends ICommand> {
queryUrl?: string;
queryHandler?: (query: TQuery) => Promise<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>;
beforeRead?: (query: IQueryCriteria) => Promise<TQuery>;
defaultCriteria?: TQuery;
keyResolver?: (model: TModel) => TModel[keyof TModel];
commands?: {
[name: string]: {
url?: string;
handler?: (command: any) => Promise<any>;
resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Promise<any>;
beforeCommand?: (command: any) => Promise<any>;
};
};
autoFetch?: boolean;
}
export function useHttpDataSource<TQuery extends IQueryCriteria = IQueryCriteria, TModel extends ICommand = any>(
options: UseHttpDataSourceOptions<TQuery, TModel>
) {
const data: Ref<TModel[] | null> = ref(null);
const total: Ref<number | null> = ref(null);
const loading = ref(false);
const error: Ref<IDataSourceError | null> = ref(null);
const criteria: Ref<TQuery> = ref(options.defaultCriteria || {} as TQuery) as Ref<TQuery>;
const builder = new HttpDataSourceBuilder<TQuery, TModel>();
if (options.keyResolver) {
builder.keyResolver(options.keyResolver);
}
if (options.defaultCriteria) {
builder.defaultCriteria(options.defaultCriteria);
}
if (options.beforeRead) {
builder.beforeRead(options.beforeRead);
}
if (options.queryUrl) {
builder.queryUrl(options.queryUrl);
} else if (options.queryHandler) {
builder.queryHandler(options.queryHandler);
} else {
console.warn("useHttpDataSource: No queryUrl or queryHandler provided. Data will not be fetched.");
}
if (options.commands) {
for (const name in options.commands) {
if (Object.prototype.hasOwnProperty.call(options.commands, name)) {
const commandOptions = options.commands[name];
if (commandOptions.handler) {
builder.addCommandByCallback(name, commandOptions.handler, commandOptions.resolveCommandModel);
} else if (commandOptions.url) {
builder.addCommandByUrl(name, commandOptions.url, commandOptions.resolveCommandModel, commandOptions.beforeCommand);
}
}
}
}
const dataSourceOptions: IDataSourceOptions<TQuery, TModel> = builder.createOptions();
const _read = async (query?: TQuery) => {
loading.value = true;
error.value = null;
data.value = null;
total.value = null;
try {
const finalQuery = query || criteria.value;
const result = await dataSourceOptions.transport.query.adapter.handle(finalQuery);
data.value = result.items;
total.value = result.total;
} catch (e: any) {
error.value = e as IDataSourceError;
console.error("Data source query failed:", e);
} finally {
loading.value = false;
}
};
const read = async (overrideQuery?: TQuery) => {
if (overrideQuery) {
criteria.value = overrideQuery;
}
await _read(criteria.value);
};
const executeCommand = async (commandName: string, commandData: any): Promise<any> => {
loading.value = true;
error.value = null;
try {
const commandAdapter = dataSourceOptions.transport.commands[commandName];
if (!commandAdapter) {
throw new Error(`Command "${commandName}" is not defined for this data source.`);
}
let finalCommandData = commandData;
if (commandAdapter.resolveCommandModel) {
const modelToResolveFrom = options.keyResolver && data.value && data.value.length > 0
? data.value.find(item => options.keyResolver!(item) === options.keyResolver!(commandData))
: null;
finalCommandData = await commandAdapter.resolveCommandModel({
model: modelToResolveFrom || commandData,
data: commandData
});
}
const result = await commandAdapter.adapter.handle(finalCommandData);
return result;
} catch (e: any) {
error.value = e as IDataSourceError;
console.error(`Data source command '${commandName}' failed:`, e);
throw e;
} finally {
loading.value = false;
}
};
if (options.autoFetch) {
watch(criteria, async () => {
await _read();
}, { immediate: true, deep: true });
}
return {
data: readonly(data),
total: readonly(total),
loading: readonly(loading),
error: readonly(error),
criteria,
read,
executeCommand,
};
}

View File

@ -0,0 +1,45 @@
import {
type IQueryCriteria,
type ICommand,
type IQueryExecutionResult,
type IQueryExecutionGroupResult,
type IResolveCommandModelEvent
} from '../core/types';
import { useHttpDataSource } from './use-http-data-source.ts';
export interface UseListDataSourceOptions<TQuery extends IQueryCriteria, TModel extends ICommand> {
queryUrl?: string;
queryHandler?: (query: TQuery) => Promise<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>;
beforeRead?: (query: IQueryCriteria) => Promise<TQuery>;
defaultCriteria?: TQuery;
keyResolver?: (model: TModel) => TModel[keyof TModel];
commands?: {
[name: string]: {
url?: string;
handler?: (command: any) => Promise<any>;
resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Promise<any>;
beforeCommand?: (command: any) => Promise<any>;
};
};
autoFetch?: boolean;
}
export function useListDataSource<TQuery extends IQueryCriteria = IQueryCriteria, TModel extends ICommand = any>(
options: UseListDataSourceOptions<TQuery, TModel>
) {
const { data, total, loading, error, criteria, read, executeCommand } = useHttpDataSource(options);
return {
items: data,
total,
loading,
error,
criteria,
read,
executeCommand,
};
}

View File

@ -0,0 +1,169 @@
// src/composables/use-single-data-source.ts
import { ref, readonly, watch, type Ref } from 'vue';
import {
type IQueryCriteria,
type ICommand,
type IQueryExecutionResult,
type IDataSourceError,
type IResolveCommandModelEvent,
type IDataSourceOptions,
type IQueryExecutionGroupResult
} from '../core/types';
import { SingleDataSourceBuilder } from '../core/single-data-source-builder';
export interface UseSingleDataSourceOptions<TQuery extends IQueryCriteria, TModel extends ICommand> {
queryUrl?: string;
queryHandler?: (query: TQuery) => Promise<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>;
beforeRead?: (query: IQueryCriteria) => Promise<TQuery>;
defaultCriteria?: TQuery;
keyResolver: (model: TModel) => TModel[keyof TModel];
commands?: {
[name: string]: {
url?: string;
handler?: (command: any) => Promise<any>;
resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Promise<any>;
beforeCommand?: (command: any) => Promise<any>;
};
};
addStandardRestCommands?: {
route: string;
};
autoFetch?: boolean;
}
export function useSingleDataSource<TQuery extends IQueryCriteria = IQueryCriteria, TModel extends ICommand = any>(
options: UseSingleDataSourceOptions<TQuery, TModel>
) {
const data: Ref<TModel | null> = ref(null);
const loading = ref(false);
const error: Ref<IDataSourceError | null> = ref(null);
const criteria: Ref<TQuery> = ref(options.defaultCriteria || {} as TQuery) as Ref<TQuery>;
const builder = new SingleDataSourceBuilder<TQuery, TModel>();
if (!options.keyResolver) {
throw new Error("useSingleDataSource: 'keyResolver' is mandatory for single data source operations.");
}
builder.keyResolver(options.keyResolver);
if (options.defaultCriteria) {
builder.defaultCriteria(options.defaultCriteria);
}
if (options.beforeRead) {
builder.beforeRead(options.beforeRead);
}
if (options.queryUrl) {
builder.queryUrl(options.queryUrl);
} else if (options.queryHandler) {
builder.queryHandler(options.queryHandler);
} else {
console.warn("useSingleDataSource: No queryUrl or queryHandler provided. Data will not be fetched.");
}
if (options.addStandardRestCommands) {
builder.addStandardRestCommands(options.addStandardRestCommands.route);
}
if (options.commands) {
for (const name in options.commands) {
if (Object.prototype.hasOwnProperty.call(options.commands, name)) {
const commandOptions = options.commands[name];
if (commandOptions.handler) {
builder.addCommandByCallback(name, commandOptions.handler, commandOptions.resolveCommandModel);
} else if (commandOptions.url) {
builder.addCommandByUrl(name, commandOptions.url, commandOptions.resolveCommandModel, commandOptions.beforeCommand);
}
}
}
}
const dataSourceOptions: IDataSourceOptions<TQuery, TModel> = builder.createOptions();
const _read = async (query?: TQuery) => {
loading.value = true;
error.value = null;
data.value = null;
try {
const finalQuery = query || criteria.value;
const result = await dataSourceOptions.transport.query.adapter.handle(finalQuery);
data.value = result.items && result.items.length > 0 ? result.items[0] : null;
} catch (e: any) {
error.value = e as IDataSourceError;
console.error("Single data source query failed:", e);
} finally {
loading.value = false;
}
};
const read = async (overrideQuery?: TQuery) => {
if (overrideQuery) {
criteria.value = overrideQuery;
}
await _read(criteria.value);
};
const executeCommand = async (commandName: string, commandData: any): Promise<any> => {
loading.value = true;
error.value = null;
try {
const commandAdapter = dataSourceOptions.transport.commands[commandName];
if (!commandAdapter) {
throw new Error(`Command "${commandName}" is not defined for this data source.`);
}
let finalCommandData = commandData;
if (commandAdapter.resolveCommandModel) {
let modelToResolveFrom: TModel | null = null;
modelToResolveFrom = data.value;
finalCommandData = await commandAdapter.resolveCommandModel({
model: modelToResolveFrom || commandData,
data: commandData
});
}
const result = await commandAdapter.adapter.handle(finalCommandData);
if (commandName === 'create' || commandName === 'update') {
data.value = result;
}
if (commandName === 'delete') {
data.value = null;
}
return result;
} catch (e: any) {
error.value = e as IDataSourceError;
console.error(`Single data source command '${commandName}' failed:`, e);
throw e;
} finally {
loading.value = false;
}
};
if (options.autoFetch) {
watch(criteria, async () => {
await _read();
}, { immediate: true, deep: true });
}
return {
data: readonly(data),
loading: readonly(loading),
error: readonly(error),
criteria,
read,
executeCommand,
};
}

View File

@ -0,0 +1,101 @@
// src/core/base-datasource-builder.ts
import { httpClient } from './http-client';
import {
type IQueryCriteria,
type IDataSourceCommandAdapterOptions,
type IResolveCommandModelEvent,
type IDataSourceOptions,
type IDataSourceTransportOptions,
type IDataSourceError
} from './types';
export abstract class BaseDataSourceBuilder<TQuery extends IQueryCriteria, TModel extends {}> {
protected _commands: { [key: string]: IDataSourceCommandAdapterOptions<any>; } = {};
protected _keyResolver?: (model: TModel) => TModel[keyof TModel];
protected _defaultCriteria!: IQueryCriteria;
protected constructor() { }
protected createTransport(): IDataSourceTransportOptions<TQuery, TModel> {
return {
query: {} as any,
commands: this._commands
};
}
public keyResolver(resolver: (model: TModel) => TModel[keyof TModel]) {
this._keyResolver = resolver;
return this;
}
public createOptions(): IDataSourceOptions<TQuery, TModel> { // <--- CHANGED FROM 'protected' to 'public'
return {
resolveIdField: this._keyResolver,
defaultCriteria: this._defaultCriteria,
transport: this.createTransport()
};
}
protected _handleErrorPipe(error: any): IDataSourceError {
if (error && (error.type === 'message' || error.type === 'validation')) {
return error as IDataSourceError;
}
return {
type: 'message',
message: 'UNEXPECTED_ERROR_MESSAGE',
originalError: error,
statusCode: 0
};
}
public addCommandByCallback<TCommand, TCommandResult>(
name: string,
commandHandler: (command: TCommand) => Promise<TCommandResult>,
resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Promise<TCommand & any>
) {
const handleWrapper = async (command: TCommand) => {
try {
return await commandHandler(command);
} catch (e) {
throw this._handleErrorPipe(e);
}
};
this._commands[name] = {
adapter: {
handle: handleWrapper
},
resolveCommandModel: resolveCommandModel
};
return this;
}
public addCommandByUrl<TCommand, TCommandResult>(
name: string,
url: string,
resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Promise<TCommand & any>,
beforeCommand?: (command: TCommand) => Promise<TCommand>
) {
const handleWrapper = async (command: TCommand) => {
const finalBeforeCommand = beforeCommand || (_ => Promise.resolve(command));
const processedCommand = await finalBeforeCommand(command);
try {
return await httpClient.post<TCommandResult>(url, processedCommand);
} catch (e) {
throw this._handleErrorPipe(e);
}
};
this._commands[name] = {
adapter: {
handle: handleWrapper
},
resolveCommandModel: resolveCommandModel
};
return this;
}
}

97
src/core/http-client.ts Normal file
View File

@ -0,0 +1,97 @@
export interface HttpError {
type: 'message' | 'validation';
message?: string;
errors?: { [key: string]: string[] };
statusCode?: number;
originalError?: any;
}
export const httpClient = {
async post<TResponse = any>(url: string, data: any): Promise<TResponse> {
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
if (!response.ok) {
let errorData: any = null;
try {
errorData = await response.json();
} catch (parseError) {
errorData = await response.text();
}
throw this._handleHttpError(response.status, errorData);
}
if (response.status === 204 || response.headers.get('content-length') === '0') {
return undefined as TResponse;
}
return await response.json() as TResponse;
} catch (error: any) {
if (error && typeof error.type === 'string' && (error.type === 'message' || error.type === 'validation')) {
throw error;
} else {
throw {
type: 'message',
message: 'NETWORK_ERROR_MESSAGE',
originalError: error,
statusCode: 0
} as HttpError;
}
}
},
_handleHttpError(status: number, errorBody: any): HttpError {
if (status === 500) {
return {
type: 'message',
message: 'UNEXPECTED_ERROR_MESSAGE',
statusCode: status,
originalError: errorBody
};
}
if (status === 400) {
if (errorBody && errorBody.errors) {
return {
type: 'validation',
errors: errorBody.errors,
statusCode: status,
originalError: errorBody
};
}
if (typeof errorBody === 'object' && (errorBody.Message || errorBody.message)) {
return {
type: 'message',
message: errorBody.Message || errorBody.message,
statusCode: status,
originalError: errorBody
};
}
}
if (typeof errorBody === 'string') {
return {
type: 'message',
message: errorBody,
statusCode: status,
originalError: errorBody
};
}
return {
type: 'message',
message: 'UNEXPECTED_ERROR_MESSAGE',
statusCode: status,
originalError: errorBody
};
}
};

View File

@ -0,0 +1,60 @@
import { httpClient } from './http-client';
import {
type IQueryCriteria,
type IQueryExecutionGroupResult,
type IQueryExecutionResult,
type IDataSourceQueryAdapterOptions,
} from './types';
import { BaseDataSourceBuilder } from './base-datasource-builder';
export class HttpDataSourceBuilder<TQuery extends IQueryCriteria, TModel extends {}> extends BaseDataSourceBuilder<TQuery, TModel>
{
private _beforeRead?: (query: IQueryCriteria) => Promise<TQuery>;
protected _query!: IDataSourceQueryAdapterOptions<TQuery, TModel>;
constructor() {
super();
}
protected createTransport() {
const baseTransport = super.createTransport();
baseTransport.query = this._query;
return baseTransport;
}
public beforeRead(beforeRead: (query: IQueryCriteria) => Promise<TQuery>) {
this._beforeRead = beforeRead;
return this;
}
public queryUrl(url: string) {
this._query = {
adapter: {
handle: async (query: IQueryCriteria) => {
const finalBeforeRead = this._beforeRead || (() => Promise.resolve(query as TQuery));
const finalQuery = await finalBeforeRead(query);
return httpClient.post<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>(url, finalQuery);
}
}
};
return this;
}
public queryHandler(queryHandler: (query: TQuery) => Promise<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>) {
this._query = {
adapter: {
handle: async (query: TQuery) => {
const finalBeforeRead = this._beforeRead || (() => Promise.resolve(query as TQuery));
const finalQuery = await finalBeforeRead(query);
return queryHandler(finalQuery);
}
}
};
return this;
}
public defaultCriteria(criteria: IQueryCriteria) {
this._defaultCriteria = criteria;
return this;
}
}

View File

@ -0,0 +1,8 @@
import { HttpDataSourceBuilder } from './http-data-source-builder.ts';
import { type IQueryCriteria, type ICommand } from './types';
export class ListDataSourceBuilder<TQuery extends IQueryCriteria, TModel extends ICommand> extends HttpDataSourceBuilder<TQuery, TModel> {
constructor() {
super();
}
}

View File

@ -0,0 +1,35 @@
// src/core/single-datasource-builder.ts (REVISED - RxJS compatible and delete data fix)
import { HttpDataSourceBuilder } from './http-data-source-builder.ts';
import { type IQueryCriteria, type ICommand } from './types';
import { httpClient } from './http-client';
export class SingleDataSourceBuilder<TQuery extends IQueryCriteria, TModel extends ICommand> extends HttpDataSourceBuilder<TQuery, TModel>
{
constructor() {
super();
}
public addStandardRestCommands(route: string) {
if (!this._keyResolver) {
console.warn("A key resolver is recommended for standard commands to help identify models.");
}
this.addCommandByCallback(
'create',
(command: TModel) => httpClient.post<TModel>(route, command)
);
this.addCommandByCallback(
'update',
(command: TModel) => httpClient.post<TModel>(route, command)
);
this.addCommandByCallback(
'delete',
(command: TModel) => httpClient.post<TModel>(route, command)
);
return this;
}
}

63
src/core/types.ts Normal file
View File

@ -0,0 +1,63 @@
export interface IQueryCriteria {
[key: string]: any;
}
export interface ICommand {
[key: string]: any;
}
export interface IQueryExecutionResult<TModel> {
items: TModel[];
total: number;
}
export interface IQueryExecutionGroupResult { }
export interface IDataSourceErrorMessage {
type: 'message';
message: string;
statusCode?: number;
originalError?: any;
}
export interface IDataSourceValidationError {
type: 'validation';
errors: { [key: string]: string[] };
statusCode?: number;
originalError?: any;
}
export type IDataSourceError = IDataSourceErrorMessage | IDataSourceValidationError;
export interface IResolveCommandModelEvent<TModel> {
model: TModel;
data: any;
}
export interface IDataSourceCommandAdapterOptions<TModel> {
adapter: {
handle: (command: any) => Promise<any>;
};
resolveCommandModel?: (event: IResolveCommandModelEvent<TModel>) => Promise<any>;
}
export interface IDataSourceQueryAdapterOptions<TQuery extends IQueryCriteria, TModel> {
adapter: {
handle: (query: TQuery) => Promise<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>;
};
}
export interface IDataSourceTransportOptions<TQuery extends IQueryCriteria, TModel> {
query: IDataSourceQueryAdapterOptions<TQuery, TModel>;
commands: { [key: string]: IDataSourceCommandAdapterOptions<TModel> };
}
export interface IDataSourceOptions<TQuery extends IQueryCriteria, TModel> {
resolveIdField?: (model: TModel) => TModel[keyof TModel];
defaultCriteria?: IQueryCriteria;
transport: IDataSourceTransportOptions<TQuery, TModel>;
}
export interface IDataSource<TQuery extends IQueryCriteria, TModel> {
read(query?: TQuery): Promise<IQueryExecutionResult<TModel> & IQueryExecutionGroupResult>;
}

0
src/index.ts Normal file
View File

79
src/style.css Normal file
View File

@ -0,0 +1,79 @@
:root {
font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

1
src/vite-env.d.ts vendored Normal file
View File

@ -0,0 +1 @@
/// <reference types="vite/client" />

32
tsconfig.json Normal file
View File

@ -0,0 +1,32 @@
{
"extends": "@vue/tsconfig/tsconfig.dom.json",
"compilerOptions": {
"composite": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"target": "ESNext",
"module": "ESNext",
"lib": ["esnext", "dom"],
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"declaration": true,
"declarationDir": "./dist",
"emitDeclarationOnly": false,
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true, // Good practice
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
},
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "vite.config.ts"],
"exclude": ["node_modules", "dist"]
}

33
vite.config.ts Normal file
View File

@ -0,0 +1,33 @@
import { defineConfig } from 'vite';
import vue from '@vitejs/plugin-vue';
import { fileURLToPath, URL } from 'node:url';
import { resolve } from 'path';
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'VueOpenHarborData',
fileName: (format) => `vue-openharbor-data.${format}.js`,
},
rollupOptions: {
external: ['vue', '@openharbor/data'],
output: {
globals: {
vue: 'Vue',
'@openharbor/data': 'OpenHarborData',
},
},
},
},
});

629
yarn.lock Normal file
View File

@ -0,0 +1,629 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@babel/helper-string-parser@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz#54da796097ab19ce67ed9f88b47bb2ec49367687"
integrity sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==
"@babel/helper-validator-identifier@^7.27.1":
version "7.27.1"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz#a7054dcc145a967dd4dc8fee845a57c1316c9df8"
integrity sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==
"@babel/parser@^7.27.5":
version "7.27.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.27.5.tgz#ed22f871f110aa285a6fd934a0efed621d118826"
integrity sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==
dependencies:
"@babel/types" "^7.27.3"
"@babel/types@^7.27.3":
version "7.27.6"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.27.6.tgz#a434ca7add514d4e646c80f7375c0aa2befc5535"
integrity sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==
dependencies:
"@babel/helper-string-parser" "^7.27.1"
"@babel/helper-validator-identifier" "^7.27.1"
"@esbuild/aix-ppc64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz#4e0f91776c2b340e75558f60552195f6fad09f18"
integrity sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==
"@esbuild/android-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz#bc766407f1718923f6b8079c8c61bf86ac3a6a4f"
integrity sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==
"@esbuild/android-arm@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.25.5.tgz#4290d6d3407bae3883ad2cded1081a234473ce26"
integrity sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==
"@esbuild/android-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/android-x64/-/android-x64-0.25.5.tgz#40c11d9cbca4f2406548c8a9895d321bc3b35eff"
integrity sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==
"@esbuild/darwin-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz#49d8bf8b1df95f759ac81eb1d0736018006d7e34"
integrity sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==
"@esbuild/darwin-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz#e27a5d92a14886ef1d492fd50fc61a2d4d87e418"
integrity sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==
"@esbuild/freebsd-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz#97cede59d638840ca104e605cdb9f1b118ba0b1c"
integrity sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==
"@esbuild/freebsd-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz#71c77812042a1a8190c3d581e140d15b876b9c6f"
integrity sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==
"@esbuild/linux-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz#f7b7c8f97eff8ffd2e47f6c67eb5c9765f2181b8"
integrity sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==
"@esbuild/linux-arm@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz#2a0be71b6cd8201fa559aea45598dffabc05d911"
integrity sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==
"@esbuild/linux-ia32@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz#763414463cd9ea6fa1f96555d2762f9f84c61783"
integrity sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==
"@esbuild/linux-loong64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz#428cf2213ff786a502a52c96cf29d1fcf1eb8506"
integrity sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==
"@esbuild/linux-mips64el@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz#5cbcc7fd841b4cd53358afd33527cd394e325d96"
integrity sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==
"@esbuild/linux-ppc64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz#0d954ab39ce4f5e50f00c4f8c4fd38f976c13ad9"
integrity sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==
"@esbuild/linux-riscv64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz#0e7dd30730505abd8088321e8497e94b547bfb1e"
integrity sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==
"@esbuild/linux-s390x@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz#5669af81327a398a336d7e40e320b5bbd6e6e72d"
integrity sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==
"@esbuild/linux-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz#b2357dd153aa49038967ddc1ffd90c68a9d2a0d4"
integrity sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==
"@esbuild/netbsd-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz#53b4dfb8fe1cee93777c9e366893bd3daa6ba63d"
integrity sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==
"@esbuild/netbsd-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz#a0206f6314ce7dc8713b7732703d0f58de1d1e79"
integrity sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==
"@esbuild/openbsd-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz#2a796c87c44e8de78001d808c77d948a21ec22fd"
integrity sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==
"@esbuild/openbsd-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz#28d0cd8909b7fa3953af998f2b2ed34f576728f0"
integrity sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==
"@esbuild/sunos-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz#a28164f5b997e8247d407e36c90d3fd5ddbe0dc5"
integrity sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==
"@esbuild/win32-arm64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz#6eadbead38e8bd12f633a5190e45eff80e24007e"
integrity sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==
"@esbuild/win32-ia32@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz#bab6288005482f9ed2adb9ded7e88eba9a62cc0d"
integrity sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==
"@esbuild/win32-x64@0.25.5":
version "0.25.5"
resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz#7fc114af5f6563f19f73324b5d5ff36ece0803d1"
integrity sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==
"@jridgewell/sourcemap-codec@^1.5.0":
version "1.5.0"
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
"@rollup/rollup-android-arm-eabi@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz#a3e4e4b2baf0bade6918cf5135c3ef7eee653196"
integrity sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==
"@rollup/rollup-android-arm64@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz#63566b0e76c62d4f96d44448f38a290562280200"
integrity sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==
"@rollup/rollup-darwin-arm64@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz#60a51a61b22b1f4fdf97b4adf5f0f447f492759d"
integrity sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==
"@rollup/rollup-darwin-x64@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz#bfe3059440f7032de11e749ece868cd7f232e609"
integrity sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==
"@rollup/rollup-freebsd-arm64@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz#d5d4c6cd3b8acb7493b76227d8b2b4a2d732a37b"
integrity sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==
"@rollup/rollup-freebsd-x64@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz#cb4e1547b572cd0144c5fbd6c4a0edfed5fe6024"
integrity sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==
"@rollup/rollup-linux-arm-gnueabihf@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz#feb81bd086f6a469777f75bec07e1bdf93352e69"
integrity sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==
"@rollup/rollup-linux-arm-musleabihf@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz#68bff1c6620c155c9d8f5ee6a83c46eb50486f18"
integrity sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==
"@rollup/rollup-linux-arm64-gnu@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz#dbc5036a85e3ca3349887c8bdbebcfd011e460b0"
integrity sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==
"@rollup/rollup-linux-arm64-musl@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz#72efc633aa0b93531bdfc69d70bcafa88e6152fc"
integrity sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==
"@rollup/rollup-linux-loongarch64-gnu@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz#9b6a49afde86c8f57ca11efdf8fd8d7c52048817"
integrity sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==
"@rollup/rollup-linux-powerpc64le-gnu@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz#93cb96073efab0cdbf419c8dfc44b5e2bd815139"
integrity sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==
"@rollup/rollup-linux-riscv64-gnu@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz#028708f73c8130ae924e5c3755de50fe93687249"
integrity sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==
"@rollup/rollup-linux-riscv64-musl@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz#878bfb158b2cf6671b7611fd58e5c80d9144ac6c"
integrity sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==
"@rollup/rollup-linux-s390x-gnu@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz#59b4ebb2129d34b7807ed8c462ff0baaefca9ad4"
integrity sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==
"@rollup/rollup-linux-x64-gnu@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz#597d40f60d4b15bedbbacf2491a69c5b67a58e93"
integrity sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==
"@rollup/rollup-linux-x64-musl@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz#0a062d6fee35ec4fbb607b2a9d933a9372ccf63a"
integrity sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==
"@rollup/rollup-win32-arm64-msvc@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz#41ffab489857987c75385b0fc8cccf97f7e69d0a"
integrity sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==
"@rollup/rollup-win32-ia32-msvc@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz#d9fb61d98eedfa52720b6ed9f31442b3ef4b839f"
integrity sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==
"@rollup/rollup-win32-x64-msvc@4.44.0":
version "4.44.0"
resolved "https://registry.yarnpkg.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz#a36e79b6ccece1533f777a1bca1f89c13f0c5f62"
integrity sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==
"@tsconfig/node20@^20.1.6":
version "20.1.6"
resolved "https://registry.yarnpkg.com/@tsconfig/node20/-/node20-20.1.6.tgz#cdf11db8322e1c245d5a4bb2e398239c82ae78b2"
integrity sha512-sz+Hqx9zwZDpZIV871WSbUzSqNIsXzghZydypnfgzPKLltVJfkINfUeTct31n/tTSa9ZE1ZOfKdRre1uHHquYQ==
"@types/estree@1.0.8":
version "1.0.8"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.8.tgz#958b91c991b1867ced318bedea0e215ee050726e"
integrity sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
"@types/node@^24.0.3":
version "24.0.3"
resolved "https://registry.yarnpkg.com/@types/node/-/node-24.0.3.tgz#f935910f3eece3a3a2f8be86b96ba833dc286cab"
integrity sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg==
dependencies:
undici-types "~7.8.0"
"@vitejs/plugin-vue@^5.2.3":
version "5.2.4"
resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-5.2.4.tgz#9e8a512eb174bfc2a333ba959bbf9de428d89ad8"
integrity sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA==
"@volar/language-core@2.4.14", "@volar/language-core@~2.4.11":
version "2.4.14"
resolved "https://registry.yarnpkg.com/@volar/language-core/-/language-core-2.4.14.tgz#dac7573014d4f3bafb186cb16888ffea5698be71"
integrity sha512-X6beusV0DvuVseaOEy7GoagS4rYHgDHnTrdOj5jeUb49fW5ceQyP9Ej5rBhqgz2wJggl+2fDbbojq1XKaxDi6w==
dependencies:
"@volar/source-map" "2.4.14"
"@volar/source-map@2.4.14":
version "2.4.14"
resolved "https://registry.yarnpkg.com/@volar/source-map/-/source-map-2.4.14.tgz#cdcecd533c2e767449b2414cc22327d2bda7ef95"
integrity sha512-5TeKKMh7Sfxo8021cJfmBzcjfY1SsXsPMMjMvjY7ivesdnybqqS+GxGAoXHAOUawQTwtdUxgP65Im+dEmvWtYQ==
"@volar/typescript@~2.4.11":
version "2.4.14"
resolved "https://registry.yarnpkg.com/@volar/typescript/-/typescript-2.4.14.tgz#b99a1025dd6a8b751e96627ebcb0739ceed0e5f1"
integrity sha512-p8Z6f/bZM3/HyCdRNFZOEEzts51uV8WHeN8Tnfnm2EBv6FDB2TQLzfVx7aJvnl8ofKAOnS64B2O8bImBFaauRw==
dependencies:
"@volar/language-core" "2.4.14"
path-browserify "^1.0.1"
vscode-uri "^3.0.8"
"@vue/compiler-core@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.5.17.tgz#23d291bd01b863da3ef2e26e7db84d8e01a9b4c5"
integrity sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA==
dependencies:
"@babel/parser" "^7.27.5"
"@vue/shared" "3.5.17"
entities "^4.5.0"
estree-walker "^2.0.2"
source-map-js "^1.2.1"
"@vue/compiler-dom@3.5.17", "@vue/compiler-dom@^3.5.0":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.17.tgz#7bc19a20e23b670243a64b47ce3a890239b870be"
integrity sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ==
dependencies:
"@vue/compiler-core" "3.5.17"
"@vue/shared" "3.5.17"
"@vue/compiler-sfc@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.5.17.tgz#c518871276e26593612bdab36f3f5bcd053b13bf"
integrity sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww==
dependencies:
"@babel/parser" "^7.27.5"
"@vue/compiler-core" "3.5.17"
"@vue/compiler-dom" "3.5.17"
"@vue/compiler-ssr" "3.5.17"
"@vue/shared" "3.5.17"
estree-walker "^2.0.2"
magic-string "^0.30.17"
postcss "^8.5.6"
source-map-js "^1.2.1"
"@vue/compiler-ssr@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.5.17.tgz#14ba3b7bba6e0e1fd02002316263165a5d1046c7"
integrity sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ==
dependencies:
"@vue/compiler-dom" "3.5.17"
"@vue/shared" "3.5.17"
"@vue/compiler-vue2@^2.7.16":
version "2.7.16"
resolved "https://registry.yarnpkg.com/@vue/compiler-vue2/-/compiler-vue2-2.7.16.tgz#2ba837cbd3f1b33c2bc865fbe1a3b53fb611e249"
integrity sha512-qYC3Psj9S/mfu9uVi5WvNZIzq+xnXMhOwbTFKKDD7b1lhpnn71jXSFdTQ+WsIEk0ONCd7VV2IMm7ONl6tbQ86A==
dependencies:
de-indent "^1.0.2"
he "^1.2.0"
"@vue/language-core@2.2.10":
version "2.2.10"
resolved "https://registry.yarnpkg.com/@vue/language-core/-/language-core-2.2.10.tgz#5ae1e71a4e16dd59d1e4bac167f4b9c8c04d9f17"
integrity sha512-+yNoYx6XIKuAO8Mqh1vGytu8jkFEOH5C8iOv3i8Z/65A7x9iAOXA97Q+PqZ3nlm2lxf5rOJuIGI/wDtx/riNYw==
dependencies:
"@volar/language-core" "~2.4.11"
"@vue/compiler-dom" "^3.5.0"
"@vue/compiler-vue2" "^2.7.16"
"@vue/shared" "^3.5.0"
alien-signals "^1.0.3"
minimatch "^9.0.3"
muggle-string "^0.4.1"
path-browserify "^1.0.1"
"@vue/reactivity@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.5.17.tgz#169b5dcf96c7f23788e5ed9745ec8a7227f2125e"
integrity sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw==
dependencies:
"@vue/shared" "3.5.17"
"@vue/runtime-core@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.5.17.tgz#b17bd41e13011e85e9b1025545292d43f5512730"
integrity sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q==
dependencies:
"@vue/reactivity" "3.5.17"
"@vue/shared" "3.5.17"
"@vue/runtime-dom@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.5.17.tgz#8e325e29cd03097fe179032fc8df384a426fc83a"
integrity sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g==
dependencies:
"@vue/reactivity" "3.5.17"
"@vue/runtime-core" "3.5.17"
"@vue/shared" "3.5.17"
csstype "^3.1.3"
"@vue/server-renderer@3.5.17":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.5.17.tgz#9b8fd6a40a3d55322509fafe78ac841ede649fbe"
integrity sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA==
dependencies:
"@vue/compiler-ssr" "3.5.17"
"@vue/shared" "3.5.17"
"@vue/shared@3.5.17", "@vue/shared@^3.5.0":
version "3.5.17"
resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.17.tgz#e8b3a41f0be76499882a89e8ed40d86a70fa4b70"
integrity sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg==
"@vue/tsconfig@^0.7.0":
version "0.7.0"
resolved "https://registry.yarnpkg.com/@vue/tsconfig/-/tsconfig-0.7.0.tgz#67044c847b7a137b8cbfd6b23104c36dbaf80d1d"
integrity sha512-ku2uNz5MaZ9IerPPUyOHzyjhXoX2kVJaVf7hL315DC17vS6IiZRmmCPfggNbU16QTvM80+uYYy3eYJB59WCtvg==
alien-signals@^1.0.3:
version "1.0.13"
resolved "https://registry.yarnpkg.com/alien-signals/-/alien-signals-1.0.13.tgz#8d6db73462f742ee6b89671fbd8c37d0b1727a7e"
integrity sha512-OGj9yyTnJEttvzhTUWuscOvtqxq5vrhF7vL9oS0xJ2mK0ItPYP1/y+vCFebfxoEyAz0++1AIwJ5CMr+Fk3nDmg==
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
brace-expansion@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7"
integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
dependencies:
balanced-match "^1.0.0"
csstype@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81"
integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
de-indent@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d"
integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==
entities@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48"
integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==
esbuild@^0.25.0:
version "0.25.5"
resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.25.5.tgz#71075054993fdfae76c66586f9b9c1f8d7edd430"
integrity sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==
optionalDependencies:
"@esbuild/aix-ppc64" "0.25.5"
"@esbuild/android-arm" "0.25.5"
"@esbuild/android-arm64" "0.25.5"
"@esbuild/android-x64" "0.25.5"
"@esbuild/darwin-arm64" "0.25.5"
"@esbuild/darwin-x64" "0.25.5"
"@esbuild/freebsd-arm64" "0.25.5"
"@esbuild/freebsd-x64" "0.25.5"
"@esbuild/linux-arm" "0.25.5"
"@esbuild/linux-arm64" "0.25.5"
"@esbuild/linux-ia32" "0.25.5"
"@esbuild/linux-loong64" "0.25.5"
"@esbuild/linux-mips64el" "0.25.5"
"@esbuild/linux-ppc64" "0.25.5"
"@esbuild/linux-riscv64" "0.25.5"
"@esbuild/linux-s390x" "0.25.5"
"@esbuild/linux-x64" "0.25.5"
"@esbuild/netbsd-arm64" "0.25.5"
"@esbuild/netbsd-x64" "0.25.5"
"@esbuild/openbsd-arm64" "0.25.5"
"@esbuild/openbsd-x64" "0.25.5"
"@esbuild/sunos-x64" "0.25.5"
"@esbuild/win32-arm64" "0.25.5"
"@esbuild/win32-ia32" "0.25.5"
"@esbuild/win32-x64" "0.25.5"
estree-walker@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
fdir@^6.4.4:
version "6.4.6"
resolved "https://registry.yarnpkg.com/fdir/-/fdir-6.4.6.tgz#2b268c0232697063111bbf3f64810a2a741ba281"
integrity sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==
fsevents@~2.3.2, fsevents@~2.3.3:
version "2.3.3"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
magic-string@^0.30.17:
version "0.30.17"
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
dependencies:
"@jridgewell/sourcemap-codec" "^1.5.0"
minimatch@^9.0.3:
version "9.0.5"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5"
integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
dependencies:
brace-expansion "^2.0.1"
muggle-string@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/muggle-string/-/muggle-string-0.4.1.tgz#3b366bd43b32f809dc20659534dd30e7c8a0d328"
integrity sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==
nanoid@^3.3.11:
version "3.3.11"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b"
integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-browserify/-/path-browserify-1.0.1.tgz#d98454a9c3753d5790860f16f68867b9e46be1fd"
integrity sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==
picocolors@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.1.1.tgz#3d321af3eab939b083c8f929a1d12cda81c26b6b"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
picomatch@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-4.0.2.tgz#77c742931e8f3b8820946c76cd0c1f13730d1dab"
integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
postcss@^8.5.3, postcss@^8.5.6:
version "8.5.6"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c"
integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==
dependencies:
nanoid "^3.3.11"
picocolors "^1.1.1"
source-map-js "^1.2.1"
rollup@^4.34.9:
version "4.44.0"
resolved "https://registry.yarnpkg.com/rollup/-/rollup-4.44.0.tgz#0e10b98339b306edad1e612f1e5590a79aef521c"
integrity sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==
dependencies:
"@types/estree" "1.0.8"
optionalDependencies:
"@rollup/rollup-android-arm-eabi" "4.44.0"
"@rollup/rollup-android-arm64" "4.44.0"
"@rollup/rollup-darwin-arm64" "4.44.0"
"@rollup/rollup-darwin-x64" "4.44.0"
"@rollup/rollup-freebsd-arm64" "4.44.0"
"@rollup/rollup-freebsd-x64" "4.44.0"
"@rollup/rollup-linux-arm-gnueabihf" "4.44.0"
"@rollup/rollup-linux-arm-musleabihf" "4.44.0"
"@rollup/rollup-linux-arm64-gnu" "4.44.0"
"@rollup/rollup-linux-arm64-musl" "4.44.0"
"@rollup/rollup-linux-loongarch64-gnu" "4.44.0"
"@rollup/rollup-linux-powerpc64le-gnu" "4.44.0"
"@rollup/rollup-linux-riscv64-gnu" "4.44.0"
"@rollup/rollup-linux-riscv64-musl" "4.44.0"
"@rollup/rollup-linux-s390x-gnu" "4.44.0"
"@rollup/rollup-linux-x64-gnu" "4.44.0"
"@rollup/rollup-linux-x64-musl" "4.44.0"
"@rollup/rollup-win32-arm64-msvc" "4.44.0"
"@rollup/rollup-win32-ia32-msvc" "4.44.0"
"@rollup/rollup-win32-x64-msvc" "4.44.0"
fsevents "~2.3.2"
source-map-js@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.2.1.tgz#1ce5650fddd87abc099eda37dcff024c2667ae46"
integrity sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==
tinyglobby@^0.2.13:
version "0.2.14"
resolved "https://registry.yarnpkg.com/tinyglobby/-/tinyglobby-0.2.14.tgz#5280b0cf3f972b050e74ae88406c0a6a58f4079d"
integrity sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==
dependencies:
fdir "^6.4.4"
picomatch "^4.0.2"
typescript@~5.8.3:
version "5.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.8.3.tgz#92f8a3e5e3cf497356f4178c34cd65a7f5e8440e"
integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
undici-types@~7.8.0:
version "7.8.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.8.0.tgz#de00b85b710c54122e44fbfd911f8d70174cd294"
integrity sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw==
vite@^6.3.5:
version "6.3.5"
resolved "https://registry.yarnpkg.com/vite/-/vite-6.3.5.tgz#fec73879013c9c0128c8d284504c6d19410d12a3"
integrity sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==
dependencies:
esbuild "^0.25.0"
fdir "^6.4.4"
picomatch "^4.0.2"
postcss "^8.5.3"
rollup "^4.34.9"
tinyglobby "^0.2.13"
optionalDependencies:
fsevents "~2.3.3"
vscode-uri@^3.0.8:
version "3.1.0"
resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c"
integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==
vue-tsc@^2.2.8:
version "2.2.10"
resolved "https://registry.yarnpkg.com/vue-tsc/-/vue-tsc-2.2.10.tgz#7b51a666cb90788884efd0caedc69fc1fc9c5b78"
integrity sha512-jWZ1xSaNbabEV3whpIDMbjVSVawjAyW+x1n3JeGQo7S0uv2n9F/JMgWW90tGWNFRKya4YwKMZgCtr0vRAM7DeQ==
dependencies:
"@volar/typescript" "~2.4.11"
"@vue/language-core" "2.2.10"
vue@^3.5.13:
version "3.5.17"
resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.17.tgz#ea8a6a45abb2b0620e7d479319ce8434b55650cf"
integrity sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g==
dependencies:
"@vue/compiler-dom" "3.5.17"
"@vue/compiler-sfc" "3.5.17"
"@vue/runtime-dom" "3.5.17"
"@vue/server-renderer" "3.5.17"
"@vue/shared" "3.5.17"