dsfsdfs
199
bootstrap_btn.ts
Normal file
@@ -0,0 +1,199 @@
|
|||||||
|
import { type Preset, symbols } from 'unocss';
|
||||||
|
function compressCSS(css: string, isDev = false) {
|
||||||
|
if (isDev)
|
||||||
|
return css.trim();
|
||||||
|
return css.trim().replace(/\s+/g, " ").replace(/\/\*[\s\S]*?\*\//g, "");
|
||||||
|
}
|
||||||
|
function get<T>(obj: Record<string, T> | undefined = {}, key: string): T {
|
||||||
|
// key = "key" or "key.subkey"
|
||||||
|
const keys = key.split('.');
|
||||||
|
let result: any = obj;
|
||||||
|
|
||||||
|
for (const k of keys) {
|
||||||
|
if (result == null || typeof result !== 'object') return undefined as any;
|
||||||
|
result = result[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
type ColorRecordType = string | Record<string, string>;
|
||||||
|
class ColorRecord {
|
||||||
|
// [key: string]: string | ColorRecord;
|
||||||
|
_colors: Record<string, ColorRecordType> = {};
|
||||||
|
constructor(obj?: Record<string, string | Record<string, string>>) {
|
||||||
|
if (obj) {
|
||||||
|
this._add(obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_add(obj: Record<string, string | Record<string, string>>) {
|
||||||
|
this._colors = { ...this._colors, ...obj };
|
||||||
|
}
|
||||||
|
get colors() {
|
||||||
|
return this._colors;
|
||||||
|
}
|
||||||
|
get(key: string): ColorRecord | string | undefined {
|
||||||
|
const color = get(this._colors, key);
|
||||||
|
if (color !== undefined) {
|
||||||
|
return typeof color === 'string' ? color : new ColorRecord(color);
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function extractColors(theme: {colors: Record<string, string | Record<string, string>>}): ColorRecord {
|
||||||
|
const colors: any = {};
|
||||||
|
Object.entries(theme.colors).forEach(([key, value]) => {
|
||||||
|
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
|
||||||
|
colors[key] = value as any
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return new ColorRecord(colors);
|
||||||
|
}
|
||||||
|
|
||||||
|
const colorsMap = new Map<string, Record<string, string>>();
|
||||||
|
export const presetBootstrapBtn = (): Preset => ({
|
||||||
|
name: 'preset-bootstrap-btn',
|
||||||
|
preflights: [
|
||||||
|
{
|
||||||
|
layer: 'base',
|
||||||
|
getCSS: (ctx) => {
|
||||||
|
const colors = (ctx.generator.config.theme as { colors: Record<string, Record<string, string>> }).colors || {}
|
||||||
|
Object.entries(colors).forEach(([key, value]) => {
|
||||||
|
colorsMap.set(key, value)
|
||||||
|
})
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
rules: [
|
||||||
|
// Base .btn style
|
||||||
|
[
|
||||||
|
/^btn$/,
|
||||||
|
() => ({
|
||||||
|
'display': 'inline-flex',
|
||||||
|
'font-weight': '500',
|
||||||
|
'line-height': '1.5',
|
||||||
|
'text-align': 'center',
|
||||||
|
'text-decoration': 'none',
|
||||||
|
'vertical-align': 'middle',
|
||||||
|
'user-select': 'none',
|
||||||
|
'align-items': 'center',
|
||||||
|
'border': '1px solid transparent',
|
||||||
|
'padding': '0.375rem 0.75rem',
|
||||||
|
'font-size': '1rem',
|
||||||
|
'border-radius': '0.375rem',
|
||||||
|
'transition': 'color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out',
|
||||||
|
'cursor': 'pointer',
|
||||||
|
'gap': '.375rem',
|
||||||
|
// "cursor": "pointer",
|
||||||
|
// "border": "1px solid #0000",
|
||||||
|
// "border-radius": ".375rem",
|
||||||
|
// "outline": "none",
|
||||||
|
// "align-items": "center",
|
||||||
|
// "gap": ".375rem",
|
||||||
|
// "height": "2.5rem",
|
||||||
|
// "padding-inline": "1rem",
|
||||||
|
// "font-size": ".8125rem",
|
||||||
|
// "font-weight": 500,
|
||||||
|
// "line-height": 1,
|
||||||
|
// "display": "inline-flex"
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
|
||||||
|
// Variants
|
||||||
|
[/^btn-(primary|secondary|success|danger|warning|info|light|dark)$/, ([, c], {theme}) => {
|
||||||
|
const color = extractColors(theme as any).get(`${c}.DEFAULT`)
|
||||||
|
const colorLight = extractColors(theme as any).get(`${c}.light`) ?? `color-mix(in srgb, ${color} 85%, black)`;
|
||||||
|
const text = ['light', 'warning'].includes(c) ? '#000' : '#fff'
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'color': text,
|
||||||
|
'background-color': color,
|
||||||
|
'border-color': color,
|
||||||
|
'transition': 'all 0.15s ease-in-out',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[symbols.selector]: selector => `${selector}:hover`,
|
||||||
|
// background: `color-mix(in srgb, ${color} 90%, black)`,
|
||||||
|
// "border-color": `color-mix(in srgb, ${color} 85%, black)`,
|
||||||
|
"box-shadow": `.25rem .25rem 0 ${colorLight}`,
|
||||||
|
'transform': 'translate(-0.125rem, -0.125rem)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[symbols.selector]: selector => `${selector}:focus`,
|
||||||
|
outline: `0`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[symbols.selector]: selector => `${selector}:disabled`,
|
||||||
|
opacity: `.65`,
|
||||||
|
"pointer-events": `none`,
|
||||||
|
},
|
||||||
|
// Active
|
||||||
|
{
|
||||||
|
[symbols.selector]: selector => `${selector}.active`,
|
||||||
|
background: `color-mix(in srgb, ${color} 80%, black)`,
|
||||||
|
"border-color": `color-mix(in srgb, ${color} 75%, black)`,
|
||||||
|
"box-shadow": `inset 0 .15rem .3rem rgba(0,0,0,.15)`,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
|
||||||
|
// Outline variants
|
||||||
|
[/^btn-outline-(primary|secondary|success|danger|warning|info|light|dark)$/, ([, c], {theme}) => {
|
||||||
|
const color = extractColors(theme as any).get(`${c}.DEFAULT`)
|
||||||
|
const colorLight = extractColors(theme as any).get(`${c}.light`)
|
||||||
|
|
||||||
|
return [{
|
||||||
|
'color': color,
|
||||||
|
'border-color': color,
|
||||||
|
'background-color': 'transparent',
|
||||||
|
'transition': 'all 0.15s ease-in-out',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
[symbols.selector]: selector => `${selector}:hover`,
|
||||||
|
// color: '#fff',
|
||||||
|
// "background-color": color,
|
||||||
|
// "border-color": color,
|
||||||
|
'box-shadow': `0.25rem .25rem 0 ${colorLight}`,
|
||||||
|
'transform': 'translate(-0.125rem, -0.125rem)',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
// Size variants
|
||||||
|
[/^btn-(lg|sm)$/, ([, size], {theme}) => {
|
||||||
|
const padding = size === 'lg' ? '0.5rem 1rem' : '0.25rem 0.5rem';
|
||||||
|
const fontSize = size === 'lg' ? '1.125rem' : '0.875rem';
|
||||||
|
const borderRadius = size === 'lg' ? '0.5rem' : '0.25rem';
|
||||||
|
return [{
|
||||||
|
'padding': padding,
|
||||||
|
'font-size': fontSize,
|
||||||
|
'border-radius': borderRadius,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}],
|
||||||
|
// Button group
|
||||||
|
],
|
||||||
|
shortcuts: [
|
||||||
|
// ==== Button Group ====
|
||||||
|
[
|
||||||
|
'btn-group',
|
||||||
|
[
|
||||||
|
'inline-flex items-stretch align-middle',
|
||||||
|
'[&>.btn]:rounded-none',
|
||||||
|
'[&>.btn:first-child]:rounded-l-md',
|
||||||
|
'[&>.btn:last-child]:rounded-r-md',
|
||||||
|
'[&>.btn:not(:last-child)]:border-r-0',
|
||||||
|
].join(' '),
|
||||||
|
],
|
||||||
|
|
||||||
|
[
|
||||||
|
'btn-group-vertical',
|
||||||
|
[
|
||||||
|
'inline-flex flex-col items-stretch align-middle',
|
||||||
|
'[&>.btn]:rounded-none',
|
||||||
|
'[&>.btn:first-child]:rounded-t-md',
|
||||||
|
'[&>.btn:last-child]:rounded-b-md',
|
||||||
|
'[&>.btn:not(:last-child)]:border-b-0',
|
||||||
|
].join(' '),
|
||||||
|
],
|
||||||
|
]
|
||||||
|
})
|
||||||
293
bun.lock
@@ -4,22 +4,33 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "holistream",
|
"name": "holistream",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.946.0",
|
||||||
|
"@aws-sdk/s3-presigned-post": "^3.946.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.946.0",
|
||||||
|
"@hiogawa/tiny-rpc": "^0.2.3-pre.18",
|
||||||
"@hiogawa/utils": "^1.7.0",
|
"@hiogawa/utils": "^1.7.0",
|
||||||
"@primeuix/themes": "^2.0.2",
|
"@primeuix/themes": "^2.0.2",
|
||||||
"@primevue/forms": "^4.5.4",
|
"@primevue/forms": "^4.5.4",
|
||||||
"@unhead/vue": "^2.1.1",
|
"@unhead/vue": "^2.1.1",
|
||||||
|
"@vueuse/core": "^14.1.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"hono": "^4.11.3",
|
"hono": "^4.11.3",
|
||||||
|
"is-mobile": "^5.0.0",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
|
"primevue": "^4.5.4",
|
||||||
|
"tailwind-merge": "^3.4.0",
|
||||||
"vue": "^3.5.26",
|
"vue": "^3.5.26",
|
||||||
"vue-router": "^4.6.4",
|
"vue-router": "^4.6.4",
|
||||||
|
"zod": "^4.3.2",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/vite-plugin": "^1.17.1",
|
"@cloudflare/vite-plugin": "^1.17.1",
|
||||||
|
"@primevue/auto-import-resolver": "^4.5.4",
|
||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||||
"unocss": "^66.5.12",
|
"unocss": "^66.5.12",
|
||||||
|
"unplugin-vue-components": "^30.0.0",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.0",
|
||||||
"vite-ssr-components": "^0.5.2",
|
"vite-ssr-components": "^0.5.2",
|
||||||
"wrangler": "^4.54.0",
|
"wrangler": "^4.54.0",
|
||||||
@@ -29,6 +40,94 @@
|
|||||||
"packages": {
|
"packages": {
|
||||||
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
|
"@antfu/install-pkg": ["@antfu/install-pkg@1.1.0", "", { "dependencies": { "package-manager-detector": "^1.3.0", "tinyexec": "^1.0.1" } }, "sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ=="],
|
||||||
|
|
||||||
|
"@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="],
|
||||||
|
|
||||||
|
"@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha1-browser": ["@aws-crypto/sha1-browser@5.2.0", "", { "dependencies": { "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="],
|
||||||
|
|
||||||
|
"@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="],
|
||||||
|
|
||||||
|
"@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.958.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-node": "3.958.0", "@aws-sdk/middleware-bucket-endpoint": "3.957.0", "@aws-sdk/middleware-expect-continue": "3.957.0", "@aws-sdk/middleware-flexible-checksums": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-location-constraint": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-sdk-s3": "3.957.0", "@aws-sdk/middleware-ssec": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/signature-v4-multi-region": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/eventstream-serde-browser": "^4.2.7", "@smithy/eventstream-serde-config-resolver": "^4.3.7", "@smithy/eventstream-serde-node": "^4.2.7", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-blob-browser": "^4.2.8", "@smithy/hash-node": "^4.2.7", "@smithy/hash-stream-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/md5-js": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/util-waiter": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-ol8Sw37AToBWb6PjRuT/Wu40SrrZSA0N4F7U3yTkjUNX0lirfO1VFLZ0hZtZplVJv8GNPITbiczxQ8VjxESXxg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-6qNCIeaMzKzfqasy2nNRuYnMuaMebCcCPP4J2CVGkA8QYMbIVKPlkn9bpB20Vxe6H/r3jtCCLQaOJjVTx/6dXg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/core": ["@aws-sdk/core@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/xml-builder": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrZgDnF1lQZv75a52nFWs6MExihJF2GZB6ETZRqr6jMwhrk2kbJPUtvgbifwcL7AYmVqHQDJBrR/MqkwwFCpiw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/crc64-nvme": ["@aws-sdk/crc64-nvme@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-qSwSfI+qBU9HDsd6/4fM9faCxYJx2yDuHtj+NVOQ6XYDWQzFab/hUdwuKZ77Pi6goLF1pBZhJ2azaC2w7LbnTA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-475mkhGaWCr+Z52fOOVb/q2VHuNvqEDixlYIkeaO6xJ6t9qR0wpLt4hOQaR6zR1wfZV0SlE7d8RErdYq/PByog=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-8dS55QHRxXgJlHkEYaCGZIhieCs9NU1HU1BcqQ4RfUdSsfRdxxktqUKgCnBnOOn0oD3PPA8cQOCAVgIyRb3Rfw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-login": "3.958.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-u7twvZa1/6GWmPBZs6DbjlegCoNzNjBsMS/6fvh5quByYrcJr/uLd8YEr7S3UIq4kR/gSnHqcae7y2nL2bqZdg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-login": ["@aws-sdk/credential-provider-login@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-sDwtDnBSszUIbzbOORGh5gmXGl9aK25+BHb4gb1aVlqB+nNL2+IUEJA62+CE55lXSH8qXF90paivjK8tOHTwPA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.958.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.957.0", "@aws-sdk/credential-provider-http": "3.957.0", "@aws-sdk/credential-provider-ini": "3.958.0", "@aws-sdk/credential-provider-process": "3.957.0", "@aws-sdk/credential-provider-sso": "3.958.0", "@aws-sdk/credential-provider-web-identity": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-vdoZbNG2dt66I7EpN3fKCzi6fp9xjIiwEA/vVVgqO4wXCGw8rKPIdDUus4e13VvTr330uQs2W0UNg/7AgtquEQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/KIz9kadwbeLy6SKvT79W81Y+hb/8LMDyeloA2zhouE28hmne+hLn0wNCQXAAupFFlYOAtZR2NTBs7HBAReJlg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.958.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.958.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/token-providers": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-CBYHJ5ufp8HC4q+o7IJejCUctJXWaksgpmoFpXerbjAso7/Fg7LLUu9inXVOxlHKLlvYekDXjIUBXDJS2WYdgg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-dgnvwjMq5Y66WozzUzxNkCFap+umHUtqMMKlr8z/vl9NYMLem/WUbWNpFFOVFWquXikc+ewtpBMR4KEDXfZ+KA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws-sdk/util-arn-parser": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iczcn/QRIBSpvsdAS/rbzmoBpleX1JBjXvCynMbDceVLBIcVrwT1hXECrhtIC2cjh4HaLo9ClAbiOiWuqt+6MA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-AlbK3OeVNwZZil0wlClgeI/ISlOt/SPUxBsIns876IFaVu/Pj3DgImnYhpcJuFRek4r4XM51xzIaGQXM6GDHGg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.957.0", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/crc64-nvme": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/is-array-buffer": "^4.2.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-iJpeVR5V8se1hl2pt+k8bF/e9JO4KWgPCMjg8BtRspNtKIUGy7j6msYvbDixaKZaF2Veg9+HoYcOhwnZumjXSA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-BBgKawVyfQZglEkNTuBBdC3azlyqNXsvvN4jPkWAiNYcY0x1BasaJFl+7u/HisfULstryweJq/dAvIZIxzlZaA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-y8/W7TOQpmDJg/fPYlqAhwA4+I15LrS7TwgUEoxogtkD8gfur9wFMRLT8LCyc9o4NMEcAnK50hSb4+wB0qv6tQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-w1qfKrSKHf9b5a8O76yQ1t69u6NWuBjr5kBX+jRWFx/5mu6RLpqERXRpVJxfosbep7k3B+DSB5tZMZ82GKcJtQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-D2H/WoxhAZNYX+IjkKTdOhOkWQaK0jjJrDBj56hKjU5c9ltQiaX/1PqJ4dfjHntEshJfu0w+E6XJ+/6A6ILBBA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-arn-parser": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-5B2qY2nR2LYpxoQP0xUum5A1UNvH2JQpLHDH1nWFNF/XetV7ipFHksMxPNhtJJ6ARaWhQIDXfOUj0jcnkJxXUg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-qwkmrK0lizdjNt5qxl4tHYfASh8DFpHXM1iDVo+qHe+zuslfMqQEGRkzxS8tJq/I+8F0c6v3IKOveKJAfIvfqQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.957.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@smithy/core": "^3.20.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-50vcHu96XakQnIvlKJ1UoltrFODjsq2KvtTgHiPFteUS884lQnK5VC/8xd1Msz/1ONpLMzdCVproCQqhDTtMPQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.958.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.957.0", "@aws-sdk/middleware-host-header": "3.957.0", "@aws-sdk/middleware-logger": "3.957.0", "@aws-sdk/middleware-recursion-detection": "3.957.0", "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/region-config-resolver": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-endpoints": "3.957.0", "@aws-sdk/util-user-agent-browser": "3.957.0", "@aws-sdk/util-user-agent-node": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/core": "^3.20.0", "@smithy/fetch-http-handler": "^5.3.8", "@smithy/hash-node": "^4.2.7", "@smithy/invalid-dependency": "^4.2.7", "@smithy/middleware-content-length": "^4.2.7", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-retry": "^4.4.17", "@smithy/middleware-serde": "^4.2.8", "@smithy/middleware-stack": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/node-http-handler": "^4.4.7", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-body-length-node": "^4.2.1", "@smithy/util-defaults-mode-browser": "^4.3.16", "@smithy/util-defaults-mode-node": "^4.2.19", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/KuCcS8b5TpQXkYOrPLYytrgxBhv81+5pChkOlhegbeHttjM69pyUpQVJqyfDM/A7wPLnDrzCAnk4zaAOkY0Nw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/config-resolver": "^4.4.5", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-V8iY3blh8l2iaOqXWW88HbkY5jDoWjH56jonprG/cpyqqCnprvpMUZWPWYJoI8rHRf2bqzZeql1slxG6EnKI7A=="],
|
||||||
|
|
||||||
|
"@aws-sdk/s3-presigned-post": ["@aws-sdk/s3-presigned-post@3.958.0", "", { "dependencies": { "@aws-sdk/client-s3": "3.958.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-format-url": "3.957.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/signature-v4": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-/ZIXeezt9QSkruWlk9yj4XkA2hQyKcYyykOWZtphqcZZXBL5Y1NcF/bjzPohbzOYTYxV1ddWZGzodiSdnBeXPw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/s3-request-presigner": ["@aws-sdk/s3-request-presigner@3.958.0", "", { "dependencies": { "@aws-sdk/signature-v4-multi-region": "3.957.0", "@aws-sdk/types": "3.957.0", "@aws-sdk/util-format-url": "3.957.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/protocol-http": "^5.3.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bFKsofead/fl3lyhdES+aNo+MZ+qv1ixSPSsF8O1oj6/KgGE0t1UH9AHw2vPq6iSQMTeEuyV0F5pC+Ns40kBgA=="],
|
||||||
|
|
||||||
|
"@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.957.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/protocol-http": "^5.3.7", "@smithy/signature-v4": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-t6UfP1xMUigMMzHcb7vaZcjv7dA2DQkk9C/OAP1dKyrE0vb4lFGDaTApi17GN6Km9zFxJthEMUbBc7DL0hq1Bg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.958.0", "", { "dependencies": { "@aws-sdk/core": "3.957.0", "@aws-sdk/nested-clients": "3.958.0", "@aws-sdk/types": "3.957.0", "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-UCj7lQXODduD1myNJQkV+LYcGYJ9iiMggR8ow8Hva1g3A/Na5imNXzz6O67k7DAee0TYpy+gkNw+SizC6min8Q=="],
|
||||||
|
|
||||||
|
"@aws-sdk/types": ["@aws-sdk/types@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-wzWC2Nrt859ABk6UCAVY/WYEbAd7FjkdrQL6m24+tfmWYDNRByTJ9uOgU/kw9zqLCAwb//CPvrJdhqjTznWXAg=="],
|
||||||
|
|
||||||
|
"@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.957.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Aj6m+AyrhWyg8YQ4LDPg2/gIfGHCEcoQdBt5DeSFogN5k9mmJPOJ+IAmNSWmWRjpOxEy6eY813RNDI6qS97M0g=="],
|
||||||
|
|
||||||
|
"@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-endpoints": "^3.2.7", "tslib": "^2.6.2" } }, "sha512-xwF9K24mZSxcxKS3UKQFeX/dPYkEps9wF1b+MGON7EvnbcucrJGyQyK1v1xFPn1aqXkBTFi+SZaMRx5E5YCVFw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/util-format-url": ["@aws-sdk/util-format-url@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-Yyo/tlc0iGFGTPPkuxub1uRAv6XrnVnvSNjslZh5jIYA8GZoeEFPgJa3Qdu0GUS/YwoK8GOLnnaL9h/eH5LDJQ=="],
|
||||||
|
|
||||||
|
"@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.957.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nhmgKHnNV9K+i9daumaIz8JTLsIIML9PE/HUks5liyrjUzenjW/aHoc7WJ9/Td/gPZtayxFnXQSJRb/fDlBuJw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.957.0", "", { "dependencies": { "@aws-sdk/types": "3.957.0", "@smithy/types": "^4.11.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-exueuwxef0lUJRnGaVkNSC674eAiWU07ORhxBnevFFZEKisln+09Qrtw823iyv5I1N8T+wKfh95xvtWQrNKNQw=="],
|
||||||
|
|
||||||
|
"@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.957.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.957.0", "@aws-sdk/types": "3.957.0", "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-ycbYCwqXk4gJGp0Oxkzf2KBeeGBdTxz559D41NJP8FlzSej1Gh7Rk40Zo6AyTfsNWkrl/kVi1t937OIzC5t+9Q=="],
|
||||||
|
|
||||||
|
"@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.957.0", "", { "dependencies": { "@smithy/types": "^4.11.0", "fast-xml-parser": "5.2.5", "tslib": "^2.6.2" } }, "sha512-Ai5iiQqS8kJ5PjzMhWcLKN0G2yasAkvpnPlq2EnqlIMdB48HsizElt62qcktdxp4neRMyGkFq4NzgmDbXnhRiA=="],
|
||||||
|
|
||||||
|
"@aws/lambda-invoke-store": ["@aws/lambda-invoke-store@0.2.2", "", {}, "sha512-C0NBLsIqzDIae8HFw9YIrIBsbc0xTiOtt7fAukGPnqQ/+zZNaq+4jhuccltK0QuWHBnNm/a6kLIRA6GFiM10eg=="],
|
||||||
|
|
||||||
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
"@babel/code-frame": ["@babel/code-frame@7.27.1", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg=="],
|
||||||
|
|
||||||
"@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
|
"@babel/compat-data": ["@babel/compat-data@7.28.5", "", {}, "sha512-6uFXyCayocRbqhZOB+6XcuZbkMNimwfVGFji8CTZnCzOHVGvDqzvitu1re2AU5LROliz7eQPhB8CpAMvnx9EjA=="],
|
||||||
@@ -153,6 +252,8 @@
|
|||||||
|
|
||||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.0", "", { "os": "win32", "cpu": "x64" }, "sha512-aIitBcjQeyOhMTImhLZmtxfdOcuNRpwlPNmlFKPcHQYPhEssw75Cl1TSXJXpMkzaua9FUetx/4OQKq7eJul5Cg=="],
|
||||||
|
|
||||||
|
"@hiogawa/tiny-rpc": ["@hiogawa/tiny-rpc@0.2.3-pre.18", "", {}, "sha512-BiNHrutG9G9yV622QvkxZxF+PhkaH2Aspp4/X1KYTfnaQTcg4fFUTBWf5Kf533swon2SuVJwi6U6H1LQbhVOQQ=="],
|
||||||
|
|
||||||
"@hiogawa/utils": ["@hiogawa/utils@1.7.0", "", {}, "sha512-ghiEFWBR1NENoHn+lSuW7liicTIzVPN+8Srm5UedCTw43gus0mlse6Wp2lz6GmbOXJ/CalMPp/0Tz2X8tajkAg=="],
|
"@hiogawa/utils": ["@hiogawa/utils@1.7.0", "", {}, "sha512-ghiEFWBR1NENoHn+lSuW7liicTIzVPN+8Srm5UedCTw43gus0mlse6Wp2lz6GmbOXJ/CalMPp/0Tz2X8tajkAg=="],
|
||||||
|
|
||||||
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
"@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="],
|
||||||
@@ -219,14 +320,22 @@
|
|||||||
|
|
||||||
"@primeuix/styled": ["@primeuix/styled@0.7.4", "", { "dependencies": { "@primeuix/utils": "^0.6.1" } }, "sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ=="],
|
"@primeuix/styled": ["@primeuix/styled@0.7.4", "", { "dependencies": { "@primeuix/utils": "^0.6.1" } }, "sha512-QSO/NpOQg8e9BONWRBx9y8VGMCMYz0J/uKfNJEya/RGEu7ARx0oYW0ugI1N3/KB1AAvyGxzKBzGImbwg0KUiOQ=="],
|
||||||
|
|
||||||
|
"@primeuix/styles": ["@primeuix/styles@2.0.2", "", { "dependencies": { "@primeuix/styled": "^0.7.4" } }, "sha512-LNtkJsTonNHF5ag+9s3+zQzm00+LRmffw68QRIHy6S/dam1JpdrrAnUzNYlWbaY7aE2EkZvQmx7Np7+PyHn+ow=="],
|
||||||
|
|
||||||
"@primeuix/themes": ["@primeuix/themes@2.0.2", "", { "dependencies": { "@primeuix/styled": "^0.7.4" } }, "sha512-prwQvA3tDGBz8yWSUenaJUttEMCEvPvxwOfFhDPmSe1vwsfVKL2Nmh5eZvtPFQnxmIOPsHZS7zc0/L3CzJ83Eg=="],
|
"@primeuix/themes": ["@primeuix/themes@2.0.2", "", { "dependencies": { "@primeuix/styled": "^0.7.4" } }, "sha512-prwQvA3tDGBz8yWSUenaJUttEMCEvPvxwOfFhDPmSe1vwsfVKL2Nmh5eZvtPFQnxmIOPsHZS7zc0/L3CzJ83Eg=="],
|
||||||
|
|
||||||
"@primeuix/utils": ["@primeuix/utils@0.6.3", "", {}, "sha512-/SLNQSKQ73WbBIsflKVqbpVjCfFYvQO3Sf1LMheXyxh8JqxO4M63dzP56wwm9OPGuCQ6MYOd2AHgZXz+g7PZcg=="],
|
"@primeuix/utils": ["@primeuix/utils@0.6.3", "", {}, "sha512-/SLNQSKQ73WbBIsflKVqbpVjCfFYvQO3Sf1LMheXyxh8JqxO4M63dzP56wwm9OPGuCQ6MYOd2AHgZXz+g7PZcg=="],
|
||||||
|
|
||||||
|
"@primevue/auto-import-resolver": ["@primevue/auto-import-resolver@4.5.4", "", { "dependencies": { "@primevue/metadata": "4.5.4" } }, "sha512-YQHrZ9PQSG/4K2BwthA2Xuna4WyS0JMHajiHD9PljaDyQtBVwCadX5ZpKcrAUWR8E/1gjva8x/si0RYxxYrRJw=="],
|
||||||
|
|
||||||
"@primevue/core": ["@primevue/core@4.5.4", "", { "dependencies": { "@primeuix/styled": "^0.7.4", "@primeuix/utils": "^0.6.2" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-lYJJB3wTrDJ8MkLctzHfrPZAqXVxoatjIsswSJzupatf6ZogJHVYADUKcn1JAkLLk8dtV1FA2AxDek663fHO5Q=="],
|
"@primevue/core": ["@primevue/core@4.5.4", "", { "dependencies": { "@primeuix/styled": "^0.7.4", "@primeuix/utils": "^0.6.2" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-lYJJB3wTrDJ8MkLctzHfrPZAqXVxoatjIsswSJzupatf6ZogJHVYADUKcn1JAkLLk8dtV1FA2AxDek663fHO5Q=="],
|
||||||
|
|
||||||
"@primevue/forms": ["@primevue/forms@4.5.4", "", { "dependencies": { "@primeuix/forms": "^0.1.0", "@primeuix/utils": "^0.6.2", "@primevue/core": "4.5.4" } }, "sha512-2TlD8oJEtb8vuKzY3jY0W+7NVBC/Qj0m57iWzpMUmGnEKg9sbQ2/ZiU1sTof710/liYgm4FneRTOYHIpVkiJNA=="],
|
"@primevue/forms": ["@primevue/forms@4.5.4", "", { "dependencies": { "@primeuix/forms": "^0.1.0", "@primeuix/utils": "^0.6.2", "@primevue/core": "4.5.4" } }, "sha512-2TlD8oJEtb8vuKzY3jY0W+7NVBC/Qj0m57iWzpMUmGnEKg9sbQ2/ZiU1sTof710/liYgm4FneRTOYHIpVkiJNA=="],
|
||||||
|
|
||||||
|
"@primevue/icons": ["@primevue/icons@4.5.4", "", { "dependencies": { "@primeuix/utils": "^0.6.2", "@primevue/core": "4.5.4" } }, "sha512-DxgryEc7ZmUqcEhYMcxGBRyFzdtLIoy3jLtlH1zsVSRZaG+iSAcjQ88nvfkZxGUZtZBFL7sRjF6KLq3bJZJwUw=="],
|
||||||
|
|
||||||
|
"@primevue/metadata": ["@primevue/metadata@4.5.4", "", {}, "sha512-jJFD0KYm8bPYgFo0JP3Dc2RkyXzrMI1XHQGsEKTysx9Jx2d1XdxtFji/ZsQeoo/RmwUNof5ciZ72URq37rnK+g=="],
|
||||||
|
|
||||||
"@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="],
|
"@quansync/fs": ["@quansync/fs@1.0.0", "", { "dependencies": { "quansync": "^1.0.0" } }, "sha512-4TJ3DFtlf1L5LDMaM6CanJ/0lckGNtJcMjQ1NAV6zDmA0tEHKZtxNKin8EgPaVX1YzljbxckyT2tJrpQKAtngQ=="],
|
||||||
|
|
||||||
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.8.1", "", {}, "sha512-J1dev372wtJqmqn9U/qbpbZxbJSQrogNN2+Qv1lKlpATpe/WQ9aCZfl/xSb9d2Rgh1IyLSvNxZAXPZxruO6Xig=="],
|
"@remix-run/node-fetch-server": ["@remix-run/node-fetch-server@0.8.1", "", {}, "sha512-J1dev372wtJqmqn9U/qbpbZxbJSQrogNN2+Qv1lKlpATpe/WQ9aCZfl/xSb9d2Rgh1IyLSvNxZAXPZxruO6Xig=="],
|
||||||
@@ -279,12 +388,116 @@
|
|||||||
|
|
||||||
"@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="],
|
"@sindresorhus/is": ["@sindresorhus/is@7.2.0", "", {}, "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw=="],
|
||||||
|
|
||||||
|
"@smithy/abort-controller": ["@smithy/abort-controller@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-rzMY6CaKx2qxrbYbqjXWS0plqEy7LOdKHS0bg4ixJ6aoGDPNUcLWk/FRNuCILh7GKLG9TFUXYYeQQldMBBwuyw=="],
|
||||||
|
|
||||||
|
"@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-WmU0TnhEAJLWvfSeMxBNe5xtbselEO8+4wG0NtZeL8oR21WgH1xiO37El+/Y+H/Ie4SCwBy3MxYWmOYaGgZueA=="],
|
||||||
|
|
||||||
|
"@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.2.1", "", { "dependencies": { "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-lX9Ay+6LisTfpLid2zZtIhSEjHMZoAR5hHCR4H7tBz/Zkfr5ea8RcQ7Tk4mi0P76p4cN+Btz16Ffno7YHpKXnQ=="],
|
||||||
|
|
||||||
|
"@smithy/config-resolver": ["@smithy/config-resolver@4.4.5", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "@smithy/util-config-provider": "^4.2.0", "@smithy/util-endpoints": "^3.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-HAGoUAFYsUkoSckuKbCPayECeMim8pOu+yLy1zOxt1sifzEbrsRpYa+mKcMdiHKMeiqOibyPG0sFJnmaV/OGEg=="],
|
||||||
|
|
||||||
|
"@smithy/core": ["@smithy/core@3.20.0", "", { "dependencies": { "@smithy/middleware-serde": "^4.2.8", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-body-length-browser": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-stream": "^4.5.8", "@smithy/util-utf8": "^4.2.0", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-WsSHCPq/neD5G/MkK4csLI5Y5Pkd9c1NMfpYEKeghSGaD4Ja1qLIohRQf2D5c1Uy5aXp76DeKHkzWZ9KAlHroQ=="],
|
||||||
|
|
||||||
|
"@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-CmduWdCiILCRNbQWFR0OcZlUPVtyE49Sr8yYL0rZQ4D/wKxiNzBNS/YHemvnbkIWj623fplgkexUd/c9CAKdoA=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.2.7", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-DrpkEoM3j9cBBWhufqBwnbbn+3nf1N9FP6xuVJ+e220jbactKuQgaZwjwP5CP1t+O94brm2JgVMD2atMGX3xIQ=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.2.7", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ujzPk8seYoDBmABDE5YqlhQZAXLOrtxtJLrbhHMKjBoG5b4dK4i6/mEU+6/7yXIAkqOO8sJ6YxZl+h0QQ1IJ7g=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-x7BtAiIPSaNaWuzm24Q/mtSkv+BrISO/fmheiJ39PKRNH3RmH2Hph/bUKSOBOBC9unqfIYDhKTHwpyZycLGPVQ=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.2.7", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-roySCtHC5+pQq5lK4be1fZ/WR6s/AxnPaLfCODIPArtN2du8s5Ot4mKVK3pPtijL/L654ws592JHJ1PbZFF6+A=="],
|
||||||
|
|
||||||
|
"@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.2.7", "", { "dependencies": { "@smithy/eventstream-codec": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-QVD+g3+icFkThoy4r8wVFZMsIP08taHVKjE6Jpmz8h5CgX/kk6pTODq5cht0OMtcapUx+xrPzUTQdA+TmO0m1g=="],
|
||||||
|
|
||||||
|
"@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.3.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "tslib": "^2.6.2" } }, "sha512-h/Fi+o7mti4n8wx1SR6UHWLaakwHRx29sizvp8OOm7iqwKGFneT06GCSFhml6Bha5BT6ot5pj3CYZnCHhGC2Rg=="],
|
||||||
|
|
||||||
|
"@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.2.8", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.2.0", "@smithy/chunked-blob-reader-native": "^4.2.1", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-07InZontqsM1ggTCPSRgI7d8DirqRrnpL7nIACT4PW0AWrgDiHhjGZzbAE5UtRSiU0NISGUYe7/rri9ZeWyDpw=="],
|
||||||
|
|
||||||
|
"@smithy/hash-node": ["@smithy/hash-node@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-PU/JWLTBCV1c8FtB8tEFnY4eV1tSfBc7bDBADHfn1K+uRbPgSJ9jnJp0hyjiFN2PMdPzxsf1Fdu0eo9fJ760Xw=="],
|
||||||
|
|
||||||
|
"@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZQVoAwNYnFMIbd4DUc517HuwNelJUY6YOzwqrbcAgCnVn+79/OK7UjwA93SPpdTOpKDVkLIzavWm/Ck7SmnDPQ=="],
|
||||||
|
|
||||||
|
"@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-ncvgCr9a15nPlkhIUx3CU4d7E7WEuVJOV7fS7nnK2hLtPK9tYRBkMHQbhXU1VvvKeBm/O0x26OEoBq+ngFpOEQ=="],
|
||||||
|
|
||||||
|
"@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-DZZZBvC7sjcYh4MazJSGiWMI2L7E0oCiRHREDzIxi/M2LY79/21iXt6aPLHge82wi5LsuRF5A06Ds3+0mlh6CQ=="],
|
||||||
|
|
||||||
|
"@smithy/md5-js": ["@smithy/md5-js@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-Wv6JcUxtOLTnxvNjDnAiATUsk8gvA6EeS8zzHig07dotpByYsLot+m0AaQEniUBjx97AC41MQR4hW0baraD1Xw=="],
|
||||||
|
|
||||||
|
"@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.2.7", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-GszfBfCcvt7kIbJ41LuNa5f0wvQCHhnGx/aDaZJCCT05Ld6x6U2s0xsc/0mBFONBZjQJp2U/0uSJ178OXOwbhg=="],
|
||||||
|
|
||||||
|
"@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.4.1", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-serde": "^4.2.8", "@smithy/node-config-provider": "^4.3.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "@smithy/url-parser": "^4.2.7", "@smithy/util-middleware": "^4.2.7", "tslib": "^2.6.2" } }, "sha512-gpLspUAoe6f1M6H0u4cVuFzxZBrsGZmjx2O9SigurTx4PbntYa4AJ+o0G0oGm1L2oSX6oBhcGHwrfJHup2JnJg=="],
|
||||||
|
|
||||||
|
"@smithy/middleware-retry": ["@smithy/middleware-retry@4.4.17", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/protocol-http": "^5.3.7", "@smithy/service-error-classification": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-retry": "^4.2.7", "@smithy/uuid": "^1.1.0", "tslib": "^2.6.2" } }, "sha512-MqbXK6Y9uq17h+4r0ogu/sBT6V/rdV+5NvYL7ZV444BKfQygYe8wAhDrVXagVebN6w2RE0Fm245l69mOsPGZzg=="],
|
||||||
|
|
||||||
|
"@smithy/middleware-serde": ["@smithy/middleware-serde@4.2.8", "", { "dependencies": { "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-8rDGYen5m5+NV9eHv9ry0sqm2gI6W7mc1VSFMtn6Igo25S507/HaOX9LTHAS2/J32VXD0xSzrY0H5FJtOMS4/w=="],
|
||||||
|
|
||||||
|
"@smithy/middleware-stack": ["@smithy/middleware-stack@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-bsOT0rJ+HHlZd9crHoS37mt8qRRN/h9jRve1SXUhVbkRzu0QaNYZp1i1jha4n098tsvROjcwfLlfvcFuJSXEsw=="],
|
||||||
|
|
||||||
|
"@smithy/node-config-provider": ["@smithy/node-config-provider@4.3.7", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/shared-ini-file-loader": "^4.4.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-7r58wq8sdOcrwWe+klL9y3bc4GW1gnlfnFOuL7CXa7UzfhzhxKuzNdtqgzmTV+53lEp9NXh5hY/S4UgjLOzPfw=="],
|
||||||
|
|
||||||
|
"@smithy/node-http-handler": ["@smithy/node-http-handler@4.4.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/querystring-builder": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-NELpdmBOO6EpZtWgQiHjoShs1kmweaiNuETUpuup+cmm/xJYjT4eUjfhrXRP4jCOaAsS3c3yPsP3B+K+/fyPCQ=="],
|
||||||
|
|
||||||
|
"@smithy/property-provider": ["@smithy/property-provider@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-jmNYKe9MGGPoSl/D7JDDs1C8b3dC8f/w78LbaVfoTtWy4xAd5dfjaFG9c9PWPihY4ggMQNQSMtzU77CNgAJwmA=="],
|
||||||
|
|
||||||
|
"@smithy/protocol-http": ["@smithy/protocol-http@5.3.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-1r07pb994I20dD/c2seaZhoCuNYm0rWrvBxhCQ70brNh11M5Ml2ew6qJVo0lclB3jMIXirD4s2XRXRe7QEi0xA=="],
|
||||||
|
|
||||||
|
"@smithy/querystring-builder": ["@smithy/querystring-builder@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "@smithy/util-uri-escape": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-eKONSywHZxK4tBxe2lXEysh8wbBdvDWiA+RIuaxZSgCMmA0zMgoDpGLJhnyj+c0leOQprVnXOmcB4m+W9Rw7sg=="],
|
||||||
|
|
||||||
|
"@smithy/querystring-parser": ["@smithy/querystring-parser@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3X5ZvzUHmlSTHAXFlswrS6EGt8fMSIxX/c3Rm1Pni3+wYWB6cjGocmRIoqcQF9nU5OgGmL0u7l9m44tSUpfj9w=="],
|
||||||
|
|
||||||
|
"@smithy/service-error-classification": ["@smithy/service-error-classification@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0" } }, "sha512-YB7oCbukqEb2Dlh3340/8g8vNGbs/QsNNRms+gv3N2AtZz9/1vSBx6/6tpwQpZMEJFs7Uq8h4mmOn48ZZ72MkA=="],
|
||||||
|
|
||||||
|
"@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.4.2", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-M7iUUff/KwfNunmrgtqBfvZSzh3bmFgv/j/t1Y1dQ+8dNo34br1cqVEqy6v0mYEgi0DkGO7Xig0AnuOaEGVlcg=="],
|
||||||
|
|
||||||
|
"@smithy/signature-v4": ["@smithy/signature-v4@5.3.7", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-middleware": "^4.2.7", "@smithy/util-uri-escape": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-9oNUlqBlFZFOSdxgImA6X5GFuzE7V2H7VG/7E70cdLhidFbdtvxxt81EHgykGK5vq5D3FafH//X+Oy31j3CKOg=="],
|
||||||
|
|
||||||
|
"@smithy/smithy-client": ["@smithy/smithy-client@4.10.2", "", { "dependencies": { "@smithy/core": "^3.20.0", "@smithy/middleware-endpoint": "^4.4.1", "@smithy/middleware-stack": "^4.2.7", "@smithy/protocol-http": "^5.3.7", "@smithy/types": "^4.11.0", "@smithy/util-stream": "^4.5.8", "tslib": "^2.6.2" } }, "sha512-D5z79xQWpgrGpAHb054Fn2CCTQZpog7JELbVQ6XAvXs5MNKWf28U9gzSBlJkOyMl9LA1TZEjRtwvGXfP0Sl90g=="],
|
||||||
|
|
||||||
|
"@smithy/types": ["@smithy/types@4.11.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-mlrmL0DRDVe3mNrjTcVcZEgkFmufITfUAPBEA+AHYiIeYyJebso/He1qLbP3PssRe22KUzLRpQSdBPbXdgZ2VA=="],
|
||||||
|
|
||||||
|
"@smithy/url-parser": ["@smithy/url-parser@4.2.7", "", { "dependencies": { "@smithy/querystring-parser": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/RLtVsRV4uY3qPWhBDsjwahAtt3x2IsMGnP5W1b2VZIe+qgCqkLxI1UOHDZp1Q1QSOrdOR32MF3Ph2JfWT1VHg=="],
|
||||||
|
|
||||||
|
"@smithy/util-base64": ["@smithy/util-base64@4.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-GkXZ59JfyxsIwNTWFnjmFEI8kZpRNIBfxKjv09+nkAWPt/4aGaEWMM04m4sxgNVWkbt2MdSvE3KF/PfX4nFedQ=="],
|
||||||
|
|
||||||
|
"@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Fkoh/I76szMKJnBXWPdFkQJl2r9SjPt3cMzLdOB6eJ4Pnpas8hVoWPYemX/peO0yrrvldgCUVJqOAjUrOLjbxg=="],
|
||||||
|
|
||||||
|
"@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.2.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-h53dz/pISVrVrfxV1iqXlx5pRg3V2YWFcSQyPyXZRrZoZj4R4DeWRDo1a7dd3CPTcFi3kE+98tuNyD2axyZReA=="],
|
||||||
|
|
||||||
|
"@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-kAY9hTKulTNevM2nlRtxAG2FQ3B2OR6QIrPY3zE5LqJy1oxzmgBGsHLWTcNhWXKchgA0WHW+mZkQrng/pgcCew=="],
|
||||||
|
|
||||||
|
"@smithy/util-config-provider": ["@smithy/util-config-provider@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YEjpl6XJ36FTKmD+kRJJWYvrHeUvm5ykaUS5xK+6oXffQPHeEM4/nXlZPe+Wu0lsgRUcNZiliYNh/y7q9c2y6Q=="],
|
||||||
|
|
||||||
|
"@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.3.16", "", { "dependencies": { "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-/eiSP3mzY3TsvUOYMeL4EqUX6fgUOj2eUOU4rMMgVbq67TiRLyxT7Xsjxq0bW3OwuzK009qOwF0L2OgJqperAQ=="],
|
||||||
|
|
||||||
|
"@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.2.19", "", { "dependencies": { "@smithy/config-resolver": "^4.4.5", "@smithy/credential-provider-imds": "^4.2.7", "@smithy/node-config-provider": "^4.3.7", "@smithy/property-provider": "^4.2.7", "@smithy/smithy-client": "^4.10.2", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-3a4+4mhf6VycEJyHIQLypRbiwG6aJvbQAeRAVXydMmfweEPnLLabRbdyo/Pjw8Rew9vjsh5WCdhmDaHkQnhhhA=="],
|
||||||
|
|
||||||
|
"@smithy/util-endpoints": ["@smithy/util-endpoints@3.2.7", "", { "dependencies": { "@smithy/node-config-provider": "^4.3.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-s4ILhyAvVqhMDYREeTS68R43B1V5aenV5q/V1QpRQJkCXib5BPRo4s7uNdzGtIKxaPHCfU/8YkvPAEvTpxgspg=="],
|
||||||
|
|
||||||
|
"@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-CCQBwJIvXMLKxVbO88IukazJD9a4kQ9ZN7/UMGBjBcJYvatpWk+9g870El4cB8/EJxfe+k+y0GmR9CAzkF+Nbw=="],
|
||||||
|
|
||||||
|
"@smithy/util-middleware": ["@smithy/util-middleware@4.2.7", "", { "dependencies": { "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-i1IkpbOae6NvIKsEeLLM9/2q4X+M90KV3oCFgWQI4q0Qz+yUZvsr+gZPdAEAtFhWQhAHpTsJO8DRJPuwVyln+w=="],
|
||||||
|
|
||||||
|
"@smithy/util-retry": ["@smithy/util-retry@4.2.7", "", { "dependencies": { "@smithy/service-error-classification": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-SvDdsQyF5CIASa4EYVT02LukPHVzAgUA4kMAuZ97QJc2BpAqZfA4PINB8/KOoCXEw9tsuv/jQjMeaHFvxdLNGg=="],
|
||||||
|
|
||||||
|
"@smithy/util-stream": ["@smithy/util-stream@4.5.8", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.3.8", "@smithy/node-http-handler": "^4.4.7", "@smithy/types": "^4.11.0", "@smithy/util-base64": "^4.3.0", "@smithy/util-buffer-from": "^4.2.0", "@smithy/util-hex-encoding": "^4.2.0", "@smithy/util-utf8": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-ZnnBhTapjM0YPGUSmOs0Mcg/Gg87k503qG4zU2v/+Js2Gu+daKOJMeqcQns8ajepY8tgzzfYxl6kQyZKml6O2w=="],
|
||||||
|
|
||||||
|
"@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-igZpCKV9+E/Mzrpq6YacdTQ0qTiLm85gD6N/IrmyDvQFA4UnU3d5g3m8tMT/6zG/vVkWSU+VxeUyGonL62DuxA=="],
|
||||||
|
|
||||||
|
"@smithy/util-utf8": ["@smithy/util-utf8@4.2.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.2.0", "tslib": "^2.6.2" } }, "sha512-zBPfuzoI8xyBtR2P6WQj63Rz8i3AmfAaJLuNG8dWsfvPe8lO4aCPYLn879mEgHndZH1zQ2oXmG8O1GGzzaoZiw=="],
|
||||||
|
|
||||||
|
"@smithy/util-waiter": ["@smithy/util-waiter@4.2.7", "", { "dependencies": { "@smithy/abort-controller": "^4.2.7", "@smithy/types": "^4.11.0", "tslib": "^2.6.2" } }, "sha512-vHJFXi9b7kUEpHWUCY3Twl+9NPOZvQ0SAi+Ewtn48mbiJk4JY9MZmKQjGB4SCvVb9WPiSphZJYY6RIbs+grrzw=="],
|
||||||
|
|
||||||
|
"@smithy/uuid": ["@smithy/uuid@1.1.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-4aUIteuyxtBUhVdiQqcDhKFitwfd9hqoSDYY2KRXiWtgoWJ9Bmise+KfEPDiVHWeJepvF8xJO9/9+WDIciMFFw=="],
|
||||||
|
|
||||||
"@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="],
|
"@speed-highlight/core": ["@speed-highlight/core@1.2.14", "", {}, "sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
|
"@types/node": ["@types/node@25.0.3", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA=="],
|
||||||
|
|
||||||
|
"@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="],
|
||||||
|
|
||||||
"@unhead/vue": ["@unhead/vue@2.1.1", "", { "dependencies": { "hookable": "^5.5.3", "unhead": "2.1.1" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-WYa8ORhfv7lWDSoNpkMKhbW1Dbsux/3HqMcVkZS3xZ2/c/VrcChLj+IMadpCd1WNR0srITfRJhBYZ1i9hON5Qw=="],
|
"@unhead/vue": ["@unhead/vue@2.1.1", "", { "dependencies": { "hookable": "^5.5.3", "unhead": "2.1.1" }, "peerDependencies": { "vue": ">=3.5.18" } }, "sha512-WYa8ORhfv7lWDSoNpkMKhbW1Dbsux/3HqMcVkZS3xZ2/c/VrcChLj+IMadpCd1WNR0srITfRJhBYZ1i9hON5Qw=="],
|
||||||
|
|
||||||
"@unocss/astro": ["@unocss/astro@66.5.12", "", { "dependencies": { "@unocss/core": "66.5.12", "@unocss/reset": "66.5.12", "@unocss/vite": "66.5.12" }, "peerDependencies": { "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["vite"] }, "sha512-ynhlljsTGTHAcQHbpqxe3IXEDXjPm9IdeDWAhPet7UiGXhW230vEZ+1/OoARqLysVSVz4pPb81MDgS167Oo4Nw=="],
|
"@unocss/astro": ["@unocss/astro@66.5.12", "", { "dependencies": { "@unocss/core": "66.5.12", "@unocss/reset": "66.5.12", "@unocss/vite": "66.5.12" }, "peerDependencies": { "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["vite"] }, "sha512-ynhlljsTGTHAcQHbpqxe3IXEDXjPm9IdeDWAhPet7UiGXhW230vEZ+1/OoARqLysVSVz4pPb81MDgS167Oo4Nw=="],
|
||||||
@@ -369,6 +582,12 @@
|
|||||||
|
|
||||||
"@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="],
|
"@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="],
|
||||||
|
|
||||||
|
"@vueuse/core": ["@vueuse/core@14.1.0", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "14.1.0", "@vueuse/shared": "14.1.0" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-rgBinKs07hAYyPF834mDTigH7BtPqvZ3Pryuzt1SD/lg5wEcWqvwzXXYGEDb2/cP0Sj5zSvHl3WkmMELr5kfWw=="],
|
||||||
|
|
||||||
|
"@vueuse/metadata": ["@vueuse/metadata@14.1.0", "", {}, "sha512-7hK4g015rWn2PhKcZ99NyT+ZD9sbwm7SGvp7k+k+rKGWnLjS/oQozoIZzWfCewSUeBmnJkIb+CNr7Zc/EyRnnA=="],
|
||||||
|
|
||||||
|
"@vueuse/shared": ["@vueuse/shared@14.1.0", "", { "peerDependencies": { "vue": "^3.5.0" } }, "sha512-EcKxtYvn6gx1F8z9J5/rsg3+lTQnvOruQd8fUecW99DCK04BkWD7z5KQ/wTAx+DazyoEE9dJt/zV8OIEQbM6kw=="],
|
||||||
|
|
||||||
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
"acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="],
|
||||||
|
|
||||||
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
|
"acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="],
|
||||||
@@ -379,13 +598,15 @@
|
|||||||
|
|
||||||
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
|
"blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="],
|
||||||
|
|
||||||
|
"bowser": ["bowser@2.13.1", "", {}, "sha512-OHawaAbjwx6rqICCKgSG0SAnT05bzd7ppyKLVUITZpANBaaMFBAsaNkto3LoQ31tyFP5kNujE8Cdx85G9VzOkw=="],
|
||||||
|
|
||||||
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
"browserslist": ["browserslist@4.28.1", "", { "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", "electron-to-chromium": "^1.5.263", "node-releases": "^2.0.27", "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" } }, "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA=="],
|
||||||
|
|
||||||
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
"cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="],
|
||||||
|
|
||||||
"caniuse-lite": ["caniuse-lite@1.0.30001762", "", {}, "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw=="],
|
"caniuse-lite": ["caniuse-lite@1.0.30001762", "", {}, "sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw=="],
|
||||||
|
|
||||||
"chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
"chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="],
|
||||||
|
|
||||||
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
"clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="],
|
||||||
|
|
||||||
@@ -399,7 +620,7 @@
|
|||||||
|
|
||||||
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
"colorette": ["colorette@2.0.20", "", {}, "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w=="],
|
||||||
|
|
||||||
"confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
"confbox": ["confbox@0.2.2", "", {}, "sha512-1NB+BKqhtNipMsov4xI/NnhCKp9XG9NamYp5PVm9klAT0fsrNPjaFICsCFhNhwZJKNh7zB/3q8qXz0E9oaMNtQ=="],
|
||||||
|
|
||||||
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
"consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="],
|
||||||
|
|
||||||
@@ -437,6 +658,10 @@
|
|||||||
|
|
||||||
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
|
"exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="],
|
||||||
|
|
||||||
|
"exsolve": ["exsolve@1.0.8", "", {}, "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA=="],
|
||||||
|
|
||||||
|
"fast-xml-parser": ["fast-xml-parser@5.2.5", "", { "dependencies": { "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
@@ -457,6 +682,8 @@
|
|||||||
|
|
||||||
"is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
"is-arrayish": ["is-arrayish@0.3.4", "", {}, "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA=="],
|
||||||
|
|
||||||
|
"is-mobile": ["is-mobile@5.0.0", "", {}, "sha512-Tz/yndySvLAEXh+Uk8liFCxOwVH6YutuR74utvOcu7I9Di+DwM0mtdPVZNaVvvBUM2OXxne/NhOs1zAO7riusQ=="],
|
||||||
|
|
||||||
"is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
|
"is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="],
|
||||||
|
|
||||||
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||||
@@ -469,6 +696,8 @@
|
|||||||
|
|
||||||
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
"kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="],
|
||||||
|
|
||||||
|
"local-pkg": ["local-pkg@1.1.2", "", { "dependencies": { "mlly": "^1.7.4", "pkg-types": "^2.3.0", "quansync": "^0.2.11" } }, "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A=="],
|
||||||
|
|
||||||
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
|
||||||
|
|
||||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
@@ -509,13 +738,15 @@
|
|||||||
|
|
||||||
"pinia": ["pinia@3.0.4", "", { "dependencies": { "@vue/devtools-api": "^7.7.7" }, "peerDependencies": { "typescript": ">=4.5.0", "vue": "^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw=="],
|
"pinia": ["pinia@3.0.4", "", { "dependencies": { "@vue/devtools-api": "^7.7.7" }, "peerDependencies": { "typescript": ">=4.5.0", "vue": "^3.5.11" }, "optionalPeers": ["typescript"] }, "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw=="],
|
||||||
|
|
||||||
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
"pkg-types": ["pkg-types@2.3.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig=="],
|
||||||
|
|
||||||
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
"postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="],
|
||||||
|
|
||||||
"quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="],
|
"primevue": ["primevue@4.5.4", "", { "dependencies": { "@primeuix/styled": "^0.7.4", "@primeuix/styles": "^2.0.2", "@primeuix/utils": "^0.6.2", "@primevue/core": "4.5.4", "@primevue/icons": "4.5.4" } }, "sha512-nTyEohZABFJhVIpeUxgP0EJ8vKcJAhD+Z7DYj95e7ie/MNUCjRNcGjqmE1cXtXi4z54qDfTSI9h2uJ51qz2DIw=="],
|
||||||
|
|
||||||
"readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
"quansync": ["quansync@0.2.11", "", {}, "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA=="],
|
||||||
|
|
||||||
|
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||||
|
|
||||||
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
|
"rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="],
|
||||||
|
|
||||||
@@ -535,10 +766,14 @@
|
|||||||
|
|
||||||
"stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
|
"stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="],
|
||||||
|
|
||||||
|
"strnum": ["strnum@2.1.2", "", {}, "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ=="],
|
||||||
|
|
||||||
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
|
"superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="],
|
||||||
|
|
||||||
"supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
"supports-color": ["supports-color@10.2.2", "", {}, "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g=="],
|
||||||
|
|
||||||
|
"tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="],
|
||||||
|
|
||||||
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
"tinyexec": ["tinyexec@1.0.2", "", {}, "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg=="],
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
@@ -563,8 +798,12 @@
|
|||||||
|
|
||||||
"unocss": ["unocss@66.5.12", "", { "dependencies": { "@unocss/astro": "66.5.12", "@unocss/cli": "66.5.12", "@unocss/core": "66.5.12", "@unocss/postcss": "66.5.12", "@unocss/preset-attributify": "66.5.12", "@unocss/preset-icons": "66.5.12", "@unocss/preset-mini": "66.5.12", "@unocss/preset-tagify": "66.5.12", "@unocss/preset-typography": "66.5.12", "@unocss/preset-uno": "66.5.12", "@unocss/preset-web-fonts": "66.5.12", "@unocss/preset-wind": "66.5.12", "@unocss/preset-wind3": "66.5.12", "@unocss/preset-wind4": "66.5.12", "@unocss/transformer-attributify-jsx": "66.5.12", "@unocss/transformer-compile-class": "66.5.12", "@unocss/transformer-directives": "66.5.12", "@unocss/transformer-variant-group": "66.5.12", "@unocss/vite": "66.5.12" }, "peerDependencies": { "@unocss/webpack": "66.5.12", "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["@unocss/webpack", "vite"] }, "sha512-3WdSuM+SOjVpXDtffTuSvYTMuufpFzBehu2b4Tr7DcoIUxGouZn3mdxCLx3PiEuK0ih40Fo7Sjm+J4mccHfwLg=="],
|
"unocss": ["unocss@66.5.12", "", { "dependencies": { "@unocss/astro": "66.5.12", "@unocss/cli": "66.5.12", "@unocss/core": "66.5.12", "@unocss/postcss": "66.5.12", "@unocss/preset-attributify": "66.5.12", "@unocss/preset-icons": "66.5.12", "@unocss/preset-mini": "66.5.12", "@unocss/preset-tagify": "66.5.12", "@unocss/preset-typography": "66.5.12", "@unocss/preset-uno": "66.5.12", "@unocss/preset-web-fonts": "66.5.12", "@unocss/preset-wind": "66.5.12", "@unocss/preset-wind3": "66.5.12", "@unocss/preset-wind4": "66.5.12", "@unocss/transformer-attributify-jsx": "66.5.12", "@unocss/transformer-compile-class": "66.5.12", "@unocss/transformer-directives": "66.5.12", "@unocss/transformer-variant-group": "66.5.12", "@unocss/vite": "66.5.12" }, "peerDependencies": { "@unocss/webpack": "66.5.12", "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0 || ^8.0.0-0" }, "optionalPeers": ["@unocss/webpack", "vite"] }, "sha512-3WdSuM+SOjVpXDtffTuSvYTMuufpFzBehu2b4Tr7DcoIUxGouZn3mdxCLx3PiEuK0ih40Fo7Sjm+J4mccHfwLg=="],
|
||||||
|
|
||||||
|
"unplugin": ["unplugin@2.3.11", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "acorn": "^8.15.0", "picomatch": "^4.0.3", "webpack-virtual-modules": "^0.6.2" } }, "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww=="],
|
||||||
|
|
||||||
"unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="],
|
"unplugin-utils": ["unplugin-utils@0.3.1", "", { "dependencies": { "pathe": "^2.0.3", "picomatch": "^4.0.3" } }, "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog=="],
|
||||||
|
|
||||||
|
"unplugin-vue-components": ["unplugin-vue-components@30.0.0", "", { "dependencies": { "chokidar": "^4.0.3", "debug": "^4.4.3", "local-pkg": "^1.1.2", "magic-string": "^0.30.19", "mlly": "^1.8.0", "tinyglobby": "^0.2.15", "unplugin": "^2.3.10", "unplugin-utils": "^0.3.1" }, "peerDependencies": { "@babel/parser": "^7.15.8", "@nuxt/kit": "^3.2.2 || ^4.0.0", "vue": "2 || 3" }, "optionalPeers": ["@babel/parser", "@nuxt/kit"] }, "sha512-4qVE/lwCgmdPTp6h0qsRN2u642tt4boBQtcpn4wQcWZAsr8TQwq+SPT3NDu/6kBFxzo/sSEK4ioXhOOBrXc3iw=="],
|
||||||
|
|
||||||
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
"update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="],
|
||||||
|
|
||||||
"vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="],
|
"vite": ["vite@7.3.0", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg=="],
|
||||||
@@ -577,6 +816,8 @@
|
|||||||
|
|
||||||
"vue-router": ["vue-router@4.6.4", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg=="],
|
"vue-router": ["vue-router@4.6.4", "", { "dependencies": { "@vue/devtools-api": "^6.6.4" }, "peerDependencies": { "vue": "^3.5.0" } }, "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg=="],
|
||||||
|
|
||||||
|
"webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="],
|
||||||
|
|
||||||
"workerd": ["workerd@1.20251210.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251210.0", "@cloudflare/workerd-darwin-arm64": "1.20251210.0", "@cloudflare/workerd-linux-64": "1.20251210.0", "@cloudflare/workerd-linux-arm64": "1.20251210.0", "@cloudflare/workerd-windows-64": "1.20251210.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-9MUUneP1BnRE9XAYi94FXxHmiLGbO75EHQZsgWqSiOXjoXSqJCw8aQbIEPxCy19TclEl/kHUFYce8ST2W+Qpjw=="],
|
"workerd": ["workerd@1.20251210.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20251210.0", "@cloudflare/workerd-darwin-arm64": "1.20251210.0", "@cloudflare/workerd-linux-64": "1.20251210.0", "@cloudflare/workerd-linux-arm64": "1.20251210.0", "@cloudflare/workerd-windows-64": "1.20251210.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-9MUUneP1BnRE9XAYi94FXxHmiLGbO75EHQZsgWqSiOXjoXSqJCw8aQbIEPxCy19TclEl/kHUFYce8ST2W+Qpjw=="],
|
||||||
|
|
||||||
"wrangler": ["wrangler@4.54.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.1", "@cloudflare/unenv-preset": "2.7.13", "blake3-wasm": "2.1.5", "esbuild": "0.27.0", "miniflare": "4.20251210.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251210.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251210.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-bANFsjDwJLbprYoBK+hUDZsVbUv2SqJd8QvArLIcZk+fPq4h/Ohtj5vkKXD3k0s2bD1DXLk08D+hYmeNH+xC6A=="],
|
"wrangler": ["wrangler@4.54.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.1", "@cloudflare/unenv-preset": "2.7.13", "blake3-wasm": "2.1.5", "esbuild": "0.27.0", "miniflare": "4.20251210.0", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", "workerd": "1.20251210.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20251210.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-bANFsjDwJLbprYoBK+hUDZsVbUv2SqJd8QvArLIcZk+fPq4h/Ohtj5vkKXD3k0s2bD1DXLk08D+hYmeNH+xC6A=="],
|
||||||
@@ -589,7 +830,13 @@
|
|||||||
|
|
||||||
"youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
|
"youch-core": ["youch-core@0.3.3", "", { "dependencies": { "@poppinss/exception": "^1.2.2", "error-stack-parser-es": "^1.0.5" } }, "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA=="],
|
||||||
|
|
||||||
"zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
"zod": ["zod@4.3.2", "", {}, "sha512-b8L8yn4rIVfiXyHAmnr52/ZEpDumlT0bmxiq3Ws1ybrinhflGpt12Hvv54kYnEsGPRs6o/Ka3/ppA2OWY21IVg=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
||||||
|
|
||||||
|
"@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="],
|
||||||
|
|
||||||
"@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
"@babel/generator/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
@@ -597,20 +844,54 @@
|
|||||||
|
|
||||||
"@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
"@jridgewell/remapping/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
|
"@quansync/fs/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="],
|
||||||
|
|
||||||
|
"@unocss/cli/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||||
|
|
||||||
"@unocss/transformer-attributify-jsx/@babel/parser": ["@babel/parser@7.27.7", "", { "dependencies": { "@babel/types": "^7.27.7" }, "bin": "./bin/babel-parser.js" }, "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q=="],
|
"@unocss/transformer-attributify-jsx/@babel/parser": ["@babel/parser@7.27.7", "", { "dependencies": { "@babel/types": "^7.27.7" }, "bin": "./bin/babel-parser.js" }, "sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q=="],
|
||||||
|
|
||||||
"@unocss/transformer-attributify-jsx/@babel/traverse": ["@babel/traverse@7.27.7", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/template": "^7.27.2", "@babel/types": "^7.27.7", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw=="],
|
"@unocss/transformer-attributify-jsx/@babel/traverse": ["@babel/traverse@7.27.7", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.7", "@babel/template": "^7.27.2", "@babel/types": "^7.27.7", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw=="],
|
||||||
|
|
||||||
|
"@unocss/vite/chokidar": ["chokidar@5.0.0", "", { "dependencies": { "readdirp": "^5.0.0" } }, "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw=="],
|
||||||
|
|
||||||
"@vitejs/plugin-vue-jsx/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5", "", {}, "sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ=="],
|
"@vitejs/plugin-vue-jsx/@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5", "", {}, "sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ=="],
|
||||||
|
|
||||||
"@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
"@vue/devtools-kit/perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="],
|
||||||
|
|
||||||
|
"miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="],
|
||||||
|
|
||||||
"mlly/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
"mlly/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||||
|
|
||||||
|
"mlly/pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||||
|
|
||||||
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
"sharp/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="],
|
||||||
|
|
||||||
|
"unconfig/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="],
|
||||||
|
|
||||||
|
"unconfig-core/quansync": ["quansync@1.0.0", "", {}, "sha512-5xZacEEufv3HSTPQuchrvV6soaiACMFnq1H8wkVioctoH3TRha9Sz66lOxRwPK/qZj7HPiSveih9yAyh98gvqA=="],
|
||||||
|
|
||||||
|
"unplugin/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="],
|
||||||
|
|
||||||
"vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
"vue-router/@vue/devtools-api": ["@vue/devtools-api@6.6.4", "", {}, "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
||||||
|
|
||||||
|
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="],
|
||||||
|
|
||||||
|
"@unocss/cli/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
||||||
"@unocss/transformer-attributify-jsx/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
|
"@unocss/transformer-attributify-jsx/@babel/traverse/@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="],
|
||||||
|
|
||||||
|
"@unocss/vite/chokidar/readdirp": ["readdirp@5.0.0", "", {}, "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ=="],
|
||||||
|
|
||||||
|
"mlly/pkg-types/confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||||
|
|
||||||
|
"@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||||
|
|
||||||
|
"@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
package.json
@@ -4,27 +4,38 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "$npm_execpath run build && vite preview",
|
"preview": "vite preview",
|
||||||
"deploy": "$npm_execpath run build && wrangler deploy",
|
"deploy": "wrangler deploy",
|
||||||
"cf-typegen": "wrangler types --env-interface CloudflareBindings"
|
"cf-typegen": "wrangler types --env-interface CloudflareBindings"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@aws-sdk/client-s3": "^3.946.0",
|
||||||
|
"@aws-sdk/s3-presigned-post": "^3.946.0",
|
||||||
|
"@aws-sdk/s3-request-presigner": "^3.946.0",
|
||||||
|
"@hiogawa/tiny-rpc": "^0.2.3-pre.18",
|
||||||
"@hiogawa/utils": "^1.7.0",
|
"@hiogawa/utils": "^1.7.0",
|
||||||
"@primeuix/themes": "^2.0.2",
|
"@primeuix/themes": "^2.0.2",
|
||||||
"@primevue/forms": "^4.5.4",
|
"@primevue/forms": "^4.5.4",
|
||||||
"@unhead/vue": "^2.1.1",
|
"@unhead/vue": "^2.1.1",
|
||||||
|
"is-mobile": "^5.0.0",
|
||||||
|
"@vueuse/core": "^14.1.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"hono": "^4.11.3",
|
"hono": "^4.11.3",
|
||||||
"pinia": "^3.0.4",
|
"pinia": "^3.0.4",
|
||||||
|
"primevue": "^4.5.4",
|
||||||
"vue": "^3.5.26",
|
"vue": "^3.5.26",
|
||||||
"vue-router": "^4.6.4"
|
"vue-router": "^4.6.4",
|
||||||
|
"tailwind-merge": "^3.4.0",
|
||||||
|
"zod": "^4.3.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cloudflare/vite-plugin": "^1.17.1",
|
"@cloudflare/vite-plugin": "^1.17.1",
|
||||||
|
"@primevue/auto-import-resolver": "^4.5.4",
|
||||||
"@types/node": "^25.0.3",
|
"@types/node": "^25.0.3",
|
||||||
"@vitejs/plugin-vue": "^6.0.3",
|
"@vitejs/plugin-vue": "^6.0.3",
|
||||||
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
"@vitejs/plugin-vue-jsx": "^5.1.3",
|
||||||
"unocss": "^66.5.12",
|
"unocss": "^66.5.12",
|
||||||
|
"unplugin-vue-components": "^30.0.0",
|
||||||
"vite": "^7.3.0",
|
"vite": "^7.3.0",
|
||||||
"vite-ssr-components": "^0.5.2",
|
"vite-ssr-components": "^0.5.2",
|
||||||
"wrangler": "^4.54.0"
|
"wrangler": "^4.54.0"
|
||||||
|
|||||||
BIN
public/android-chrome-192x192.png
Normal file
|
After Width: | Height: | Size: 35 KiB |
BIN
public/android-chrome-512x512.png
Normal file
|
After Width: | Height: | Size: 243 KiB |
BIN
public/apple-touch-icon.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
public/favicon-16x16.png
Normal file
|
After Width: | Height: | Size: 762 B |
BIN
public/favicon-32x32.png
Normal file
|
After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
1549
public/index.m3u8
Normal file
BIN
public/logomin.jpg
Normal file
|
After Width: | Height: | Size: 38 KiB |
1
public/site.webmanifest
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||||
1
public/vite.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
242
src/api/rpc/auth.ts
Normal file
@@ -0,0 +1,242 @@
|
|||||||
|
import { getContext } from "hono/context-storage";
|
||||||
|
import { setCookie, deleteCookie, getCookie } from 'hono/cookie';
|
||||||
|
import { HonoVarTypes } from "types";
|
||||||
|
import { sign, verify } from "hono/jwt";
|
||||||
|
|
||||||
|
interface RegisterModel {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mock user database (in-memory)
|
||||||
|
const mockUsers: Map<string, { password: string; user: User }> = new Map([
|
||||||
|
['admin', {
|
||||||
|
password: 'admin123',
|
||||||
|
user: {
|
||||||
|
id: '1',
|
||||||
|
username: 'admin',
|
||||||
|
email: 'admin@example.com',
|
||||||
|
name: 'Admin User'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
['user@example.com', {
|
||||||
|
password: 'password',
|
||||||
|
user: {
|
||||||
|
id: '2',
|
||||||
|
username: 'user',
|
||||||
|
email: 'user@example.com',
|
||||||
|
name: 'Test User'
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
]);
|
||||||
|
|
||||||
|
// CSRF token storage (in-memory, in production use Redis or similar)
|
||||||
|
const csrfTokens = new Map<string, { token: string; expires: number }>();
|
||||||
|
|
||||||
|
// Secret for JWT signing
|
||||||
|
const JWT_SECRET = process.env.JWT_SECRET || 'your-secret-key-change-in-production';
|
||||||
|
|
||||||
|
function generateCSRFToken(): string {
|
||||||
|
return crypto.randomUUID();
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateCSRFToken(sessionId: string, token: string): boolean {
|
||||||
|
const stored = csrfTokens.get(sessionId);
|
||||||
|
if (!stored) return false;
|
||||||
|
if (stored.expires < Date.now()) {
|
||||||
|
csrfTokens.delete(sessionId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return stored.token === token;
|
||||||
|
}
|
||||||
|
|
||||||
|
const register = async (registerModel: RegisterModel) => {
|
||||||
|
// Check if user already exists
|
||||||
|
if (mockUsers.has(registerModel.username) || mockUsers.has(registerModel.email)) {
|
||||||
|
throw new Error('User already exists');
|
||||||
|
}
|
||||||
|
|
||||||
|
const newUser: User = {
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
username: registerModel.username,
|
||||||
|
email: registerModel.email,
|
||||||
|
name: registerModel.username
|
||||||
|
};
|
||||||
|
|
||||||
|
mockUsers.set(registerModel.username, {
|
||||||
|
password: registerModel.password,
|
||||||
|
user: newUser
|
||||||
|
});
|
||||||
|
|
||||||
|
mockUsers.set(registerModel.email, {
|
||||||
|
password: registerModel.password,
|
||||||
|
user: newUser
|
||||||
|
});
|
||||||
|
|
||||||
|
const context = getContext<HonoVarTypes>();
|
||||||
|
const sessionId = crypto.randomUUID();
|
||||||
|
const csrfToken = generateCSRFToken();
|
||||||
|
|
||||||
|
// Store CSRF token (expires in 1 hour)
|
||||||
|
csrfTokens.set(sessionId, {
|
||||||
|
token: csrfToken,
|
||||||
|
expires: Date.now() + 60 * 60 * 1000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create JWT token with user info
|
||||||
|
const token = await sign({
|
||||||
|
sub: newUser.id,
|
||||||
|
username: newUser.username,
|
||||||
|
email: newUser.email,
|
||||||
|
sessionId,
|
||||||
|
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 // 24 hours
|
||||||
|
}, JWT_SECRET);
|
||||||
|
|
||||||
|
// Set HTTP-only cookie
|
||||||
|
setCookie(context, 'auth_token', token, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: process.env.NODE_ENV === 'production',
|
||||||
|
sameSite: 'Lax',
|
||||||
|
path: '/',
|
||||||
|
maxAge: 60 * 60 * 24 // 24 hours
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
user: newUser,
|
||||||
|
csrfToken // Return CSRF token to client for subsequent requests
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const login = async (username: string, password: string) => {
|
||||||
|
// Try to find user by username or email
|
||||||
|
const userRecord = mockUsers.get(username);
|
||||||
|
|
||||||
|
if (!userRecord) {
|
||||||
|
throw new Error('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userRecord.password !== password) {
|
||||||
|
throw new Error('Invalid credentials');
|
||||||
|
}
|
||||||
|
|
||||||
|
const context = getContext<HonoVarTypes>();
|
||||||
|
const sessionId = crypto.randomUUID();
|
||||||
|
const csrfToken = generateCSRFToken();
|
||||||
|
|
||||||
|
// Store CSRF token (expires in 1 hour)
|
||||||
|
csrfTokens.set(sessionId, {
|
||||||
|
token: csrfToken,
|
||||||
|
expires: Date.now() + 60 * 60 * 1000
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create JWT token with user info
|
||||||
|
const token = await sign({
|
||||||
|
sub: userRecord.user.id,
|
||||||
|
username: userRecord.user.username,
|
||||||
|
email: userRecord.user.email,
|
||||||
|
sessionId,
|
||||||
|
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 // 24 hours
|
||||||
|
}, JWT_SECRET);
|
||||||
|
|
||||||
|
// Set HTTP-only cookie
|
||||||
|
setCookie(context, 'auth_token', token, {
|
||||||
|
httpOnly: true,
|
||||||
|
secure: process.env.NODE_ENV === 'production',
|
||||||
|
sameSite: 'Lax',
|
||||||
|
path: '/',
|
||||||
|
maxAge: 60 * 60 * 24 // 24 hours
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
user: userRecord.user,
|
||||||
|
csrfToken // Return CSRF token to client for subsequent requests
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
async function checkAuth() {
|
||||||
|
const context = getContext<HonoVarTypes>();
|
||||||
|
const token = getCookie(context, 'auth_token');
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
return { authenticated: false, user: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payload = await verify(token, JWT_SECRET) as any;
|
||||||
|
|
||||||
|
// Find user
|
||||||
|
const userRecord = Array.from(mockUsers.values()).find(
|
||||||
|
record => record.user.id === payload.sub
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userRecord) {
|
||||||
|
return { authenticated: false, user: null };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
authenticated: true,
|
||||||
|
user: userRecord.user
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
return { authenticated: false, user: null };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
const context = getContext<HonoVarTypes>();
|
||||||
|
const token = getCookie(context, 'auth_token');
|
||||||
|
|
||||||
|
if (token) {
|
||||||
|
try {
|
||||||
|
const payload = await verify(token, JWT_SECRET) as any;
|
||||||
|
// Remove CSRF token
|
||||||
|
if (payload.sessionId) {
|
||||||
|
csrfTokens.delete(payload.sessionId);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Token invalid, just delete cookie
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteCookie(context, 'auth_token', { path: '/' });
|
||||||
|
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getCSRFToken() {
|
||||||
|
const context = getContext<HonoVarTypes>();
|
||||||
|
const token = getCookie(context, 'auth_token');
|
||||||
|
|
||||||
|
if (!token) {
|
||||||
|
throw new Error('Not authenticated');
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = await verify(token, JWT_SECRET) as any;
|
||||||
|
const stored = csrfTokens.get(payload.sessionId);
|
||||||
|
|
||||||
|
if (!stored) {
|
||||||
|
throw new Error('CSRF token not found');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { csrfToken: stored.token };
|
||||||
|
}
|
||||||
|
|
||||||
|
export const authMethods = {
|
||||||
|
register,
|
||||||
|
login,
|
||||||
|
checkAuth,
|
||||||
|
logout,
|
||||||
|
getCSRFToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
export { validateCSRFToken };
|
||||||
1
src/api/rpc/commom.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export const secret = "123_it-is-very-secret_123";
|
||||||
333
src/api/rpc/index.ts
Normal file
@@ -0,0 +1,333 @@
|
|||||||
|
import {
|
||||||
|
exposeTinyRpc,
|
||||||
|
httpServerAdapter,
|
||||||
|
validateFn,
|
||||||
|
} from "@hiogawa/tiny-rpc";
|
||||||
|
import { tinyassert } from "@hiogawa/utils";
|
||||||
|
import { MiddlewareHandler, type Context, type Next } from "hono";
|
||||||
|
import { getContext } from "hono/context-storage";
|
||||||
|
import { register } from "module";
|
||||||
|
import { z } from "zod";
|
||||||
|
import { authMethods } from "./auth";
|
||||||
|
import { jwt } from "hono/jwt";
|
||||||
|
import { secret } from "./commom";
|
||||||
|
import { abortChunk, chunkedUpload, completeChunk, createPresignedUrls, imageContentTypes, nanoid, presignedPut, videoContentTypes } from "./s3_handle";
|
||||||
|
// import { createElement } from "react";
|
||||||
|
|
||||||
|
let counter = 0;
|
||||||
|
const listCourses = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Lập trình Web Fullstack",
|
||||||
|
description:
|
||||||
|
"Học cách xây dựng ứng dụng web hoàn chỉnh từ frontend đến backend. Khóa học bao gồm HTML, CSS, JavaScript, React, Node.js và MongoDB.",
|
||||||
|
category: "Lập trình",
|
||||||
|
rating: 4.9,
|
||||||
|
price: "1.200.000 VNĐ",
|
||||||
|
icon: "fas fa-code",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Web%20Fullstack",
|
||||||
|
slug: "lap-trinh-web-fullstack",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Phân tích dữ liệu với Python",
|
||||||
|
description:
|
||||||
|
"Khám phá sức mạnh của Python trong việc phân tích và trực quan hóa dữ liệu. Sử dụng Pandas, NumPy, Matplotlib và Seaborn.",
|
||||||
|
category: "Phân tích dữ liệu",
|
||||||
|
rating: 4.8,
|
||||||
|
price: "900.000 VNĐ",
|
||||||
|
icon: "fas fa-chart-bar",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Data%20Analysis",
|
||||||
|
slug: "phan-tich-du-lieu-voi-python",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Thiết kế UI/UX chuyên nghiệp",
|
||||||
|
description:
|
||||||
|
"Học các nguyên tắc thiết kế giao diện và trải nghiệm người dùng hiện đại. Sử dụng Figma và Adobe XD.",
|
||||||
|
category: "Thiết kế",
|
||||||
|
rating: 4.7,
|
||||||
|
price: "800.000 VNĐ",
|
||||||
|
icon: "fas fa-paint-brush",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=UI/UX%20Design",
|
||||||
|
slug: "thiet-ke-ui-ux-chuyen-nghiep",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "Machine Learning cơ bản",
|
||||||
|
description:
|
||||||
|
"Nhập môn Machine Learning với Python. Tìm hiểu về các thuật toán học máy cơ bản như Linear Regression, Logistic Regression, Decision Trees.",
|
||||||
|
category: "AI/ML",
|
||||||
|
rating: 4.6,
|
||||||
|
price: "1.500.000 VNĐ",
|
||||||
|
icon: "fas fa-brain",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Machine%20Learning",
|
||||||
|
slug: "machine-learning-co-ban",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
title: "Digital Marketing toàn diện",
|
||||||
|
description:
|
||||||
|
"Chiến lược Marketing trên các nền tảng số. SEO, Google Ads, Facebook Ads và Content Marketing.",
|
||||||
|
category: "Marketing",
|
||||||
|
rating: 4.5,
|
||||||
|
price: "700.000 VNĐ",
|
||||||
|
icon: "fas fa-bullhorn",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Digital%20Marketing",
|
||||||
|
slug: "digital-marketing-toan-dien",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
title: "Lập trình Mobile với Flutter",
|
||||||
|
description:
|
||||||
|
"Xây dựng ứng dụng di động đa nền tảng (iOS & Android) với Flutter và Dart.",
|
||||||
|
category: "Lập trình",
|
||||||
|
rating: 4.8,
|
||||||
|
price: "1.100.000 VNĐ",
|
||||||
|
icon: "fas fa-mobile-alt",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Flutter%20Mobile",
|
||||||
|
slug: "lap-trinh-mobile-voi-flutter",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 7,
|
||||||
|
title: "Tiếng Anh giao tiếp công sở",
|
||||||
|
description:
|
||||||
|
"Cải thiện kỹ năng giao tiếp tiếng Anh trong môi trường làm việc chuyên nghiệp.",
|
||||||
|
category: "Ngoại ngữ",
|
||||||
|
rating: 4.4,
|
||||||
|
price: "600.000 VNĐ",
|
||||||
|
icon: "fas fa-language",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Business%20English",
|
||||||
|
slug: "tieng-anh-giao-tiep-cong-so",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 8,
|
||||||
|
title: "Quản trị dự án Agile/Scrum",
|
||||||
|
description:
|
||||||
|
"Phương pháp quản lý dự án linh hoạt Agile và khung làm việc Scrum.",
|
||||||
|
category: "Kỹ năng mềm",
|
||||||
|
rating: 4.7,
|
||||||
|
price: "950.000 VNĐ",
|
||||||
|
icon: "fas fa-tasks",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Agile%20Scrum",
|
||||||
|
slug: "quan-tri-du-an-agile-scrum",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 9,
|
||||||
|
title: "Nhiếp ảnh cơ bản",
|
||||||
|
description:
|
||||||
|
"Làm chủ máy ảnh và nghệ thuật nhiếp ảnh. Bố cục, ánh sáng và chỉnh sửa ảnh.",
|
||||||
|
category: "Nghệ thuật",
|
||||||
|
rating: 4.9,
|
||||||
|
price: "500.000 VNĐ",
|
||||||
|
icon: "fas fa-camera",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Photography",
|
||||||
|
slug: "nhiep-anh-co-ban",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 10,
|
||||||
|
title: "Blockchain 101",
|
||||||
|
description:
|
||||||
|
"Hiểu về công nghệ Blockchain, Bitcoin, Ethereum và Smart Contracts.",
|
||||||
|
category: "Công nghệ",
|
||||||
|
rating: 4.6,
|
||||||
|
price: "1.300.000 VNĐ",
|
||||||
|
icon: "fas fa-link",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Blockchain",
|
||||||
|
slug: "blockchain-101",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 11,
|
||||||
|
title: "ReactJS Nâng cao",
|
||||||
|
description:
|
||||||
|
"Các kỹ thuật nâng cao trong React: Hooks, Context, Redux, Performance Optimization.",
|
||||||
|
category: "Lập trình",
|
||||||
|
rating: 4.9,
|
||||||
|
price: "1.000.000 VNĐ",
|
||||||
|
icon: "fas fa-code",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Advanced%20React",
|
||||||
|
slug: "reactjs-nang-cao",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 12,
|
||||||
|
title: "Viết Content Marketing thu hút",
|
||||||
|
description:
|
||||||
|
"Kỹ thuật viết bài chuẩn SEO, thu hút người đọc và tăng tỷ lệ chuyển đổi.",
|
||||||
|
category: "Marketing",
|
||||||
|
rating: 4.5,
|
||||||
|
price: "550.000 VNĐ",
|
||||||
|
icon: "fas fa-pen-nib",
|
||||||
|
bgImg: "https://placehold.co/600x400/EEE/31343C?font=playfair-display&text=Content%20Marketing",
|
||||||
|
slug: "viet-content-marketing",
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const courseContent = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Giới thiệu khóa học",
|
||||||
|
type: "video",
|
||||||
|
duration: "5:00",
|
||||||
|
completed: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Cài đặt môi trường",
|
||||||
|
type: "video",
|
||||||
|
duration: "15:00",
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Kiến thức cơ bản",
|
||||||
|
type: "video",
|
||||||
|
duration: "25:00",
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "Bài tập thực hành 1",
|
||||||
|
type: "quiz",
|
||||||
|
duration: "10:00",
|
||||||
|
completed: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const routes = {
|
||||||
|
// define as a bare function
|
||||||
|
checkId: (id: string) => {
|
||||||
|
const context = getContext();
|
||||||
|
console.log(context.req.raw.headers);
|
||||||
|
return id === "good";
|
||||||
|
},
|
||||||
|
|
||||||
|
checkIdThrow: (id: string) => {
|
||||||
|
tinyassert(id === "good", "Invalid ID");
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
|
||||||
|
getCounter: () => {
|
||||||
|
const context = getContext();
|
||||||
|
console.log(context.get("jwtPayload"));
|
||||||
|
return counter;
|
||||||
|
},
|
||||||
|
|
||||||
|
// define with zod validation + input type inference
|
||||||
|
incrementCounter: validateFn(z.object({ delta: z.number().default(1) }))(
|
||||||
|
(input) => {
|
||||||
|
// expectTypeOf(input).toEqualTypeOf<{ delta: number }>();
|
||||||
|
counter += input.delta;
|
||||||
|
return counter;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
|
||||||
|
// access context
|
||||||
|
components: async () => {},
|
||||||
|
getHomeCourses: async () => {
|
||||||
|
return listCourses.slice(0, 3);
|
||||||
|
},
|
||||||
|
getCourses: validateFn(
|
||||||
|
z.object({
|
||||||
|
page: z.number().default(1),
|
||||||
|
limit: z.number().default(6),
|
||||||
|
search: z.string().optional(),
|
||||||
|
category: z.string().optional(),
|
||||||
|
})
|
||||||
|
)(async ({ page, limit, search, category }) => {
|
||||||
|
let filtered = listCourses;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
const lowerSearch = search.toLowerCase();
|
||||||
|
filtered = filtered.filter(
|
||||||
|
(c) =>
|
||||||
|
c.title.toLowerCase().includes(lowerSearch) ||
|
||||||
|
c.description.toLowerCase().includes(lowerSearch)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (category && category !== "All") {
|
||||||
|
filtered = filtered.filter((c) => c.category === category);
|
||||||
|
}
|
||||||
|
|
||||||
|
const start = (page - 1) * limit;
|
||||||
|
const end = start + limit;
|
||||||
|
const paginated = filtered.slice(start, end);
|
||||||
|
|
||||||
|
return {
|
||||||
|
data: paginated,
|
||||||
|
total: filtered.length,
|
||||||
|
page,
|
||||||
|
totalPages: Math.ceil(filtered.length / limit),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
getCourseBySlug: validateFn(z.object({ slug: z.string() }))(async ({ slug }) => {
|
||||||
|
const course = listCourses.find((c) => c.slug === slug);
|
||||||
|
if (!course) {
|
||||||
|
throw new Error("Course not found");
|
||||||
|
}
|
||||||
|
return course;
|
||||||
|
}),
|
||||||
|
getCourseContent: validateFn(z.object({ slug: z.string() }))(async ({ slug }) => {
|
||||||
|
// In a real app, we would fetch content specific to the course
|
||||||
|
return courseContent;
|
||||||
|
}),
|
||||||
|
presignedPut: validateFn(z.object({ fileName: z.string(), contentType: z.string().refine((val) => imageContentTypes.includes(val), { message: "Invalid content type" }) }))(async ({ fileName, contentType }) => {
|
||||||
|
return await presignedPut(fileName, contentType);
|
||||||
|
}),
|
||||||
|
chunkedUpload: validateFn(z.object({ fileName: z.string(), contentType: z.string().refine((val) => videoContentTypes.includes(val), { message: "Invalid content type" }), fileSize: z.number().min(1024 * 10).max(3 * 1024 * 1024 * 1024).default(1024 * 256) }))(async ({ fileName, contentType, fileSize }) => {
|
||||||
|
const key = nanoid() + "_" + fileName;
|
||||||
|
const { UploadId } = await chunkedUpload(key, contentType, fileSize);
|
||||||
|
const chunkSize = 1024 * 1024 * 20; // 20MB
|
||||||
|
const presignedUrls = await createPresignedUrls({
|
||||||
|
key,
|
||||||
|
uploadId: UploadId!,
|
||||||
|
totalParts: Math.ceil(fileSize / chunkSize),
|
||||||
|
});
|
||||||
|
return { uploadId: UploadId!, presignedUrls, chunkSize, key, totalParts: presignedUrls.length };
|
||||||
|
}),
|
||||||
|
completeChunk: validateFn(z.object({ key: z.string(), uploadId: z.string(), parts: z.array(z.object({ PartNumber: z.number(), ETag: z.string() })) }))(async ({ key, uploadId, parts }) => {
|
||||||
|
await completeChunk(key, uploadId, parts);
|
||||||
|
return { success: true };
|
||||||
|
}),
|
||||||
|
abortChunk: validateFn(z.object({ key: z.string(), uploadId: z.string() }))(async ({ key, uploadId }) => {
|
||||||
|
await abortChunk(key, uploadId);
|
||||||
|
return { success: true };
|
||||||
|
}),
|
||||||
|
...authMethods
|
||||||
|
};
|
||||||
|
export type RpcRoutes = typeof routes;
|
||||||
|
export const endpoint = "/rpc";
|
||||||
|
export const pathsForGET: (keyof typeof routes)[] = ["getCounter"];
|
||||||
|
export const jwtRpc: MiddlewareHandler = async (c, next) => {
|
||||||
|
const publicPaths: (keyof typeof routes)[] = ["getHomeCourses", "getCourses", "getCourseBySlug", "getCourseContent", "login", "register"];
|
||||||
|
const isPublic = publicPaths.some((path) => c.req.path.split("/").includes(path));
|
||||||
|
c.set("isPublic", isPublic);
|
||||||
|
// return await next();
|
||||||
|
if (c.req.path !== endpoint && !c.req.path.startsWith(endpoint + "/") || isPublic) {
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
console.log("JWT RPC Middleware:", c.req.path);
|
||||||
|
const jwtMiddleware = jwt({
|
||||||
|
secret,
|
||||||
|
cookie: 'auth_token',
|
||||||
|
verification: {
|
||||||
|
aud: "ez.lms_users",
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return jwtMiddleware(c, next)
|
||||||
|
}
|
||||||
|
export const rpcServer = async (c: Context, next: Next) => {
|
||||||
|
if (c.req.path !== endpoint && !c.req.path.startsWith(endpoint + "/")) {
|
||||||
|
return await next();
|
||||||
|
}
|
||||||
|
// c.get("redis").has(`auth_token:${}`)
|
||||||
|
const handler = exposeTinyRpc({
|
||||||
|
routes,
|
||||||
|
adapter: httpServerAdapter({ endpoint }),
|
||||||
|
});
|
||||||
|
const res = await handler({ request: c.req.raw });
|
||||||
|
if (res) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return await next();
|
||||||
|
};
|
||||||
198
src/api/rpc/s3_handle.ts
Normal file
@@ -0,0 +1,198 @@
|
|||||||
|
import {
|
||||||
|
S3Client,
|
||||||
|
ListBucketsCommand,
|
||||||
|
ListObjectsV2Command,
|
||||||
|
GetObjectCommand,
|
||||||
|
PutObjectCommand,
|
||||||
|
DeleteObjectCommand,
|
||||||
|
CreateMultipartUploadCommand,
|
||||||
|
UploadPartCommand,
|
||||||
|
AbortMultipartUploadCommand,
|
||||||
|
CompleteMultipartUploadCommand,
|
||||||
|
ListPartsCommand,
|
||||||
|
} from "@aws-sdk/client-s3";
|
||||||
|
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
||||||
|
import { createPresignedPost } from "@aws-sdk/s3-presigned-post";
|
||||||
|
import { randomBytes } from "crypto";
|
||||||
|
const urlAlphabet = 'useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict';
|
||||||
|
|
||||||
|
export function nanoid(size = 21) {
|
||||||
|
let id = '';
|
||||||
|
const bytes = randomBytes(size); // Node.js specific method
|
||||||
|
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
id += urlAlphabet[bytes[i] & 63];
|
||||||
|
}
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
// createPresignedPost
|
||||||
|
const S3 = new S3Client({
|
||||||
|
region: "auto", // Required by SDK but not used by R2
|
||||||
|
endpoint: `https://s3.cloudfly.vn`,
|
||||||
|
credentials: {
|
||||||
|
// accessKeyId: "Q3AM3UQ867SPQQA43P2F",
|
||||||
|
// secretAccessKey: "Ik7nlCaUUCFOKDJAeSgFcbF5MEBGh9sVGBUrsUOp",
|
||||||
|
accessKeyId: "BD707P5W8J5DHFPUKYZ6",
|
||||||
|
secretAccessKey: "LTX7IizSDn28XGeQaHNID2fOtagfLc6L2henrP6P",
|
||||||
|
},
|
||||||
|
forcePathStyle: true,
|
||||||
|
});
|
||||||
|
// const S3 = new S3Client({
|
||||||
|
// region: "auto", // Required by SDK but not used by R2
|
||||||
|
// endpoint: `https://u.pipic.fun`,
|
||||||
|
// credentials: {
|
||||||
|
// // accessKeyId: "Q3AM3UQ867SPQQA43P2F",
|
||||||
|
// // secretAccessKey: "Ik7nlCaUUCFOKDJAeSgFcbF5MEBGh9sVGBUrsUOp",
|
||||||
|
// accessKeyId: "cdnadmin",
|
||||||
|
// secretAccessKey: "D@tkhong9",
|
||||||
|
// },
|
||||||
|
// forcePathStyle: true,
|
||||||
|
// });
|
||||||
|
export const imageContentTypes = ["image/png", "image/jpg", "image/jpeg", "image/webp"];
|
||||||
|
export const videoContentTypes = ["video/mp4", "video/webm", "video/ogg", "video/*"];
|
||||||
|
const nanoId = () => {
|
||||||
|
// return crypto.randomUUID().replace(/-/g, "").slice(0, 10);
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
export async function presignedPut(fileName: string, contentType: string){
|
||||||
|
if (!imageContentTypes.includes(contentType)) {
|
||||||
|
throw new Error("Invalid content type");
|
||||||
|
}
|
||||||
|
const key = nanoId()+"_"+fileName;
|
||||||
|
const url = await getSignedUrl(
|
||||||
|
S3,
|
||||||
|
new PutObjectCommand({
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key: key,
|
||||||
|
ContentType: contentType,
|
||||||
|
CacheControl: "public, max-age=31536000, immutable",
|
||||||
|
// ContentLength: 31457280, // Max 30MB
|
||||||
|
// ACL: "public-read", // Uncomment if you want the object to be publicly readable
|
||||||
|
}),
|
||||||
|
{ expiresIn: 600 } // URL valid for 10 minutes
|
||||||
|
);
|
||||||
|
return { url, key };
|
||||||
|
}
|
||||||
|
export async function createPresignedUrls({
|
||||||
|
key,
|
||||||
|
uploadId,
|
||||||
|
totalParts,
|
||||||
|
expiresIn = 60 * 15, // 15 phút
|
||||||
|
}: {
|
||||||
|
key: string;
|
||||||
|
uploadId: string;
|
||||||
|
totalParts: number;
|
||||||
|
expiresIn?: number;
|
||||||
|
}) {
|
||||||
|
const urls = [];
|
||||||
|
|
||||||
|
for (let partNumber = 1; partNumber <= totalParts; partNumber++) {
|
||||||
|
const command = new UploadPartCommand({
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key: key,
|
||||||
|
UploadId: uploadId,
|
||||||
|
PartNumber: partNumber,
|
||||||
|
});
|
||||||
|
|
||||||
|
const url = await getSignedUrl(S3, command, {
|
||||||
|
expiresIn,
|
||||||
|
});
|
||||||
|
|
||||||
|
urls.push({
|
||||||
|
partNumber,
|
||||||
|
url,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
export async function chunkedUpload(Key: string, contentType: string, fileSize: number) {
|
||||||
|
// lớn hơn 3gb thì cút
|
||||||
|
if (fileSize > 3 * 1024 * 1024 * 1024) {
|
||||||
|
throw new Error("File size exceeds 3GB");
|
||||||
|
}
|
||||||
|
// CreateMultipartUploadCommand
|
||||||
|
const uploadParams = {
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key,
|
||||||
|
ContentType: contentType,
|
||||||
|
CacheControl: "public, max-age=31536000, immutable",
|
||||||
|
};
|
||||||
|
let data = await S3.send(new CreateMultipartUploadCommand(uploadParams));
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
export async function abortChunk(key: string, uploadId: string) {
|
||||||
|
await S3.send(
|
||||||
|
new AbortMultipartUploadCommand({
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key: key,
|
||||||
|
UploadId: uploadId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export async function completeChunk(key: string, uploadId: string, parts: { ETag: string; PartNumber: number }[]) {
|
||||||
|
const listed = await S3.send(
|
||||||
|
new ListPartsCommand({
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key: key,
|
||||||
|
UploadId: uploadId,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
if (!listed.Parts || listed.Parts.length !== parts.length) {
|
||||||
|
throw new Error("Not all parts have been uploaded");
|
||||||
|
}
|
||||||
|
await S3.send(
|
||||||
|
new CompleteMultipartUploadCommand({
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key: key,
|
||||||
|
UploadId: uploadId,
|
||||||
|
MultipartUpload: {
|
||||||
|
Parts: parts.sort((a, b) => a.PartNumber - b.PartNumber),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export async function deleteObject(bucketName: string, objectKey: string) {
|
||||||
|
await S3.send(
|
||||||
|
new DeleteObjectCommand({
|
||||||
|
Bucket: bucketName,
|
||||||
|
Key: objectKey,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export async function listBuckets() {
|
||||||
|
const data = await S3.send(new ListBucketsCommand({}));
|
||||||
|
return data.Buckets;
|
||||||
|
}
|
||||||
|
export async function listObjects(bucketName: string) {
|
||||||
|
const data = await S3.send(
|
||||||
|
new ListObjectsV2Command({
|
||||||
|
Bucket: bucketName,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
return data.Contents;
|
||||||
|
}
|
||||||
|
export async function generateUploadForm(fileName: string, contentType: string) {
|
||||||
|
if (!imageContentTypes.includes(contentType)) {
|
||||||
|
throw new Error("Invalid content type");
|
||||||
|
}
|
||||||
|
return await createPresignedPost(S3, {
|
||||||
|
Bucket: "tmp",
|
||||||
|
Key: nanoId()+"_"+fileName,
|
||||||
|
Expires: 10 * 60, // URL valid for 10 minutes
|
||||||
|
Conditions: [
|
||||||
|
["starts-with", "$Content-Type", contentType],
|
||||||
|
["content-length-range", 0, 31457280], // Max 30MB
|
||||||
|
],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// generateUploadUrl("tmp", "cat.png", "image/png").then(console.log);
|
||||||
|
export async function createDownloadUrl(key: string): Promise<string> {
|
||||||
|
const url = await getSignedUrl(
|
||||||
|
S3,
|
||||||
|
new GetObjectCommand({ Bucket: "tmp", Key: key }),
|
||||||
|
{ expiresIn: 600 } // 600 giây = 10 phút
|
||||||
|
);
|
||||||
|
return url;
|
||||||
|
}
|
||||||
79
src/api/rpcclient.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import {
|
||||||
|
proxyTinyRpc,
|
||||||
|
TinyRpcClientAdapter,
|
||||||
|
TinyRpcError,
|
||||||
|
} from "@hiogawa/tiny-rpc";
|
||||||
|
import type { RpcRoutes } from "./rpc";
|
||||||
|
import { Result } from "@hiogawa/utils";
|
||||||
|
declare let __host__: string;
|
||||||
|
const endpoint = "/rpc";
|
||||||
|
const url = import.meta.env.SSR ? "http://localhost" : "";
|
||||||
|
const headers: Record<string, string> = {}; // inject headers to demonstrate context
|
||||||
|
export const client = proxyTinyRpc<RpcRoutes>({
|
||||||
|
adapter: httpClientAdapter({
|
||||||
|
url: url + endpoint,
|
||||||
|
pathsForGET: [],
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
const GET_PAYLOAD_PARAM = "payload";
|
||||||
|
function httpClientAdapter(opts: {
|
||||||
|
url: string;
|
||||||
|
pathsForGET?: string[];
|
||||||
|
}): TinyRpcClientAdapter {
|
||||||
|
return {
|
||||||
|
send: async (data) => {
|
||||||
|
const url = [opts.url, data.path].join("/");
|
||||||
|
const payload = JSON.stringify(data.args);
|
||||||
|
const method = opts.pathsForGET?.includes(data.path)
|
||||||
|
? "GET"
|
||||||
|
: "POST";
|
||||||
|
let req: Request;
|
||||||
|
if (method === "GET") {
|
||||||
|
req = new Request(
|
||||||
|
url +
|
||||||
|
"?" +
|
||||||
|
new URLSearchParams({ [GET_PAYLOAD_PARAM]: payload })
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
req = new Request(url, {
|
||||||
|
method: "POST",
|
||||||
|
body: payload,
|
||||||
|
headers: {
|
||||||
|
"content-type": "application/json; charset=utf-8",
|
||||||
|
},
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
let res: Response;
|
||||||
|
if (import.meta.env.SSR) {
|
||||||
|
const { getContext } = await import("hono/context-storage");
|
||||||
|
const c = getContext<any>();
|
||||||
|
Object.entries(c.req.header()).forEach(([k, v]) => {
|
||||||
|
req.headers.append(k, v);
|
||||||
|
});
|
||||||
|
res = await c.get("fetch")(req);
|
||||||
|
} else {
|
||||||
|
res = await fetch(req);
|
||||||
|
}
|
||||||
|
if (!res.ok) {
|
||||||
|
// throw new Error(`HTTP error: ${res.status}`);
|
||||||
|
throw new Error(
|
||||||
|
JSON.stringify({
|
||||||
|
status: res.status,
|
||||||
|
statusText: res.statusText,
|
||||||
|
data: { message: await res.text() },
|
||||||
|
internal: true,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
// throw TinyRpcError.deserialize(res.status);
|
||||||
|
}
|
||||||
|
const result: Result<unknown, unknown> = JSON.parse(
|
||||||
|
await res.text()
|
||||||
|
);
|
||||||
|
if (!result.ok) {
|
||||||
|
throw TinyRpcError.deserialize(result.value);
|
||||||
|
}
|
||||||
|
return result.value;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
11
src/client.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { createApp } from './main';
|
||||||
|
import 'uno.css';
|
||||||
|
async function render() {
|
||||||
|
const { app, router } = createApp();
|
||||||
|
router.isReady().then(() => {
|
||||||
|
app.mount('body', true)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
render().catch((error) => {
|
||||||
|
console.error('Error during app initialization:', error)
|
||||||
|
})
|
||||||
59
src/components/DashboardLayout.vue
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { Search } from "@/components/icons";
|
||||||
|
import Home from "@/components/icons/Home.vue";
|
||||||
|
import HomeFilled from "@/components/icons/HomeFilled.vue";
|
||||||
|
import Layout from "@/components/icons/Layout.vue";
|
||||||
|
import LayoutFilled from "@/components/icons/LayoutFilled.vue";
|
||||||
|
import { createStaticVNode, inject, Ref, watch } from "vue";
|
||||||
|
import Add from "@/components/icons/Add.vue";
|
||||||
|
import AddFilled from "@/components/icons/AddFilled.vue";
|
||||||
|
import Bell from "@/components/icons/Bell.vue";
|
||||||
|
import BellFilled from "@/components/icons/BellFilled.vue";
|
||||||
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
|
||||||
|
const auth = useAuthStore();
|
||||||
|
|
||||||
|
const className = ":uno: w-12 h-12 p-2 rounded-2xl hover:bg-primary/10 flex press-animated"
|
||||||
|
const homeHoist = createStaticVNode(`<img class="h-8 w-8" src="/apple-touch-icon.png" alt="Logo" />`, 1);
|
||||||
|
const links = [
|
||||||
|
{ href: "/", label: "app", icon: homeHoist, exact: homeHoist, type: "a", exactClass: "" },
|
||||||
|
{ href: "/", label: "Home", icon: Home, exact: HomeFilled, type: "a", exactClass: 'bg-primary/10' },
|
||||||
|
{ href: "/search", label: "Search", icon: Search, exact: Search, type: "btn", exactClass: "" },
|
||||||
|
{ href: "/video", label: "Video", icon: Layout, exact: LayoutFilled, type: "a", exactClass: 'bg-primary/10' },
|
||||||
|
{ href: "/add", label: "Add", icon: Add, exact: AddFilled, type: "a", exactClass: 'bg-primary/10' },
|
||||||
|
{ href: "/notification", label: "Notification", icon: Bell, exact: BellFilled, type: "a", exactClass: 'bg-primary/10' },
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="fixed left-0 w-18 flex flex-col items-center pt-4 gap-6 z-41">
|
||||||
|
<template v-for="i in links" :key="i.label">
|
||||||
|
<router-link v-if="i.type === 'a'" v-tooltip="i.label" :exact-active-class="i.exactClass" :to="i.href"
|
||||||
|
v-slot="{ isExactActive }" :class="className">
|
||||||
|
<component :is="isExactActive ? i.exact : i.icon" />
|
||||||
|
</router-link>
|
||||||
|
<div v-else :class="className" v-tooltip="i.label">
|
||||||
|
<component :is="i.icon" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="w-12 h-12 rounded-2xl hover:bg-primary/10 flex">
|
||||||
|
<button class="h-[38px] w-[38px] rounded-full m-a ring-2 ring flex press-animated" @click="auth.logout()">
|
||||||
|
<img class="h-8 w-8 rounded-full m-a ring-1 ring-white"
|
||||||
|
src="https://picsum.photos/seed/user123/40/40.jpg" alt="User avatar" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<main class="flex flex-1 overflow-hidden md:ps-18">
|
||||||
|
<div class="flex-1 overflow-auto p-4 bg-white rounded-lg md:(mr-2 mb-2) min-h-[calc(100vh-8rem)]">
|
||||||
|
<router-view v-slot="{ Component }">
|
||||||
|
<Transition enter-active-class="transition-all duration-300 ease-in-out"
|
||||||
|
enter-from-class="opacity-0 transform translate-y-4"
|
||||||
|
enter-to-class="opacity-100 transform translate-y-0"
|
||||||
|
leave-active-class="transition-all duration-200 ease-in-out"
|
||||||
|
leave-from-class="opacity-100 transform translate-y-0"
|
||||||
|
leave-to-class="opacity-0 transform -translate-y-4" mode="out-in">
|
||||||
|
<component :is="Component" />
|
||||||
|
</Transition>
|
||||||
|
</router-view>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</template>
|
||||||
3
src/components/RootLayout.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<router-view />
|
||||||
|
</template>
|
||||||
7
src/components/icons/Add.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" viewBox="-10 -226 468 468">
|
||||||
|
<path
|
||||||
|
d="M64-184c-18 0-32 14-32 32v320c0 18 14 32 32 32h320c18 0 32-14 32-32v-320c0-18-14-32-32-32H64zM0-152c0-35 29-64 64-64h320c35 0 64 29 64 64v320c0 35-29 64-64 64H64c-35 0-64-29-64-64v-320zm208 256V24h-80c-9 0-16-7-16-16s7-16 16-16h80v-80c0-9 7-16 16-16s16 7 16 16v80h80c9 0 16 7 16 16s-7 16-16 16h-80v80c0 9-7 16-16 16s-16-7-16-16z"
|
||||||
|
fill="#1e3050" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
10
src/components/icons/AddFilled.vue
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<template>
|
||||||
|
<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 468 468" width="24">
|
||||||
|
<path
|
||||||
|
d="M42 74v320c0 18 14 32 32 32h320c18 0 32-14 32-32V74c0-18-14-32-32-32H74c-18 0-32 14-32 32zm80 160c0-9 7-16 16-16h80v-80c0-9 7-16 16-16s16 7 16 16v80h80c9 0 16 7 16 16s-7 16-16 16h-80v80c0 9-7 16-16 16s-16-7-16-16v-80h-80c-9 0-16-7-16-16z"
|
||||||
|
fill="#a6acb9" />
|
||||||
|
<path
|
||||||
|
d="M74 42c-18 0-32 14-32 32v320c0 18 14 32 32 32h320c18 0 32-14 32-32V74c0-18-14-32-32-32H74zM10 74c0-35 29-64 64-64h320c35 0 64 29 64 64v320c0 35-29 64-64 64H74c-35 0-64-29-64-64V74zm208 256v-80h-80c-9 0-16-7-16-16s7-16 16-16h80v-80c0-9 7-16 16-16s16 7 16 16v80h80c9 0 16 7 16 16s-7 16-16 16h-80v80c0 9-7 16-16 16s-16-7-16-16z"
|
||||||
|
fill="#1e3050" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
3
src/components/icons/Bell.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" viewBox="-10 -258 468 532"><path d="M224-248c-13 0-24 11-24 24v10C119-203 56-133 56-48v15C56 4 46 41 27 74L5 111c-3 6-5 13-5 19 0 21 17 38 38 38h372c21 0 38-17 38-38 0-6-2-13-5-19l-22-37c-19-33-29-70-29-108v-14c0-85-63-155-144-166v-10c0-13-11-24-24-24zm168 368H56l12-22c24-40 36-85 36-131v-15c0-66 54-120 120-120s120 54 120 120v15c0 46 12 91 36 131l12 22zm-236 96c10 28 37 48 68 48s58-20 68-48H156z" fill="#1e3050"/></svg>
|
||||||
|
</template>
|
||||||
3
src/components/icons/BellFilled.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" width="24" viewBox="0 0 468 532"><path d="M66 378h337l-13-22c-24-40-36-85-36-131v-15c0-66-54-120-120-120s-120 54-120 120v15c0 46-12 91-35 131l-13 22z" fill="#a6acb9"/><path d="M234 10c-13 0-24 11-24 24v10C129 55 66 125 66 210v15c0 37-10 74-29 107l-22 37c-3 6-5 13-5 19 0 21 17 38 38 38h372c21 0 38-17 38-38 0-6-2-13-5-19l-22-37c-19-33-29-70-29-108v-14c0-85-63-155-144-166V34c0-13-11-24-24-24zm168 368H66l12-22c24-40 36-85 36-131v-15c0-66 54-120 120-120s120 54 120 120v15c0 46 12 91 36 131l12 22zm-236 96c10 28 37 48 68 48s58-20 68-48H166z" fill="#1e3050"/></svg>
|
||||||
|
</template>
|
||||||
3
src/components/icons/Home.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" width="24" viewBox="-11 -259 535 533"><path d="M272-242c-9-8-23-8-32 0L8-34C-2-25-3-10 6 0s24 11 34 2l8-7v205c0 35 29 64 64 64h288c35 0 64-29 64-64V-5l8 7c10 9 25 8 34-2s8-25-2-34L272-242zM416-48v248c0 9-7 16-16 16H112c-9 0-16-7-16-16V-48l160-144L416-48z" fill="#1e3050"/></svg>
|
||||||
|
</template>
|
||||||
3
src/components/icons/HomeFilled.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" width="24" viewBox="0 0 539 535"><path d="M61 281c2-1 4-3 6-5L269 89l202 187c2 2 4 4 6 5v180c0 35-29 64-64 64H125c-35 0-64-29-64-64V281z" fill="#a6acb9"/><path d="M247 22c13-12 32-12 44 0l224 208c13 12 13 32 1 45s-32 14-45 2L269 89 67 276c-13 12-33 12-45-1s-12-33 1-45L247 22z" fill="#1e3050"/></svg>
|
||||||
|
</template>
|
||||||
3
src/components/icons/Layout.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" width="24" viewBox="-10 -226 468 468"><path d="M384-184c18 0 32 14 32 32v64H32v-64c0-18 14-32 32-32h320zM32 168V-56h96v256H64c-18 0-32-14-32-32zm128 32V-56h256v224c0 18-14 32-32 32H160zM64-216c-35 0-64 29-64 64v320c0 35 29 64 64 64h320c35 0 64-29 64-64v-320c0-35-29-64-64-64H64z" fill="#1e3050"/></svg>
|
||||||
|
</template>
|
||||||
3
src/components/icons/LayoutFilled.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="v-mid m-a" height="24" width="24" viewBox="0 0 468 468"><path d="M42 74v64h384V74c0-18-14-32-32-32H74c-18 0-32 14-32 32zm0 96v224c0 18 14 32 32 32h64V170H42zm128 0v256h224c18 0 32-14 32-32V170H170z" fill="#a6acb9"/><path d="M394 42c18 0 32 14 32 32v64H42V74c0-18 14-32 32-32h320zM42 394V170h96v256H74c-18 0-32-14-32-32zm128 32V170h256v224c0 18-14 32-32 32H170zM74 10c-35 0-64 29-64 64v320c0 35 29 64 64 64h320c35 0 64-29 64-64V74c0-35-29-64-64-64H74z" fill="#1e3050"/></svg>
|
||||||
|
</template>
|
||||||
7
src/components/icons/TestIcon.vue
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<template>
|
||||||
|
<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M20.54 14.24A3.15 3.15 0 0 0 23.66 17H24v2h-8v1h-.02a3.4 3.4 0 0 1-3.38 3h-1.2a3.4 3.4 0 0 1-3.38-3H8v-1H0v-2h.34a3.15 3.15 0 0 0 3.12-2.76l.8-6.41a7.8 7.8 0 0 1 15.48 0zM10 19.6c0 .77.63 1.4 1.4 1.4h1.2c.77 0 1.4-.63 1.4-1.4a.6.6 0 0 0-.6-.6h-2.8a.6.6 0 0 0-.6.6">
|
||||||
|
</path>
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
46
src/components/icons/index.ts
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import { createStaticVNode } from "vue";
|
||||||
|
|
||||||
|
export const Home = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M4.6 22.73A107 107 0 0 0 11 23h2.22c2.43-.04 4.6-.16 6.18-.27A3.9 3.9 0 0 0 23 18.8v-8.46a4 4 0 0 0-1.34-3L14.4.93a3.63 3.63 0 0 0-4.82 0L2.34 7.36A4 4 0 0 0 1 10.35v8.46a3.9 3.9 0 0 0 3.6 3.92M13.08 2.4l7.25 6.44a2 2 0 0 1 .67 1.5v8.46a1.9 1.9 0 0 1-1.74 1.92q-1.39.11-3.26.19V16a4 4 0 0 0-8 0v4.92q-1.87-.08-3.26-.19A1.9 1.9 0 0 1 3 18.81v-8.46a2 2 0 0 1 .67-1.5l7.25-6.44a1.63 1.63 0 0 1 2.16 0M13.12 21h-2.24a1 1 0 0 1-.88-1v-4a2 2 0 1 1 4 0v4a1 1 0 0 1-.88 1">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const HomeFilled = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M9.59.92a3.63 3.63 0 0 1 4.82 0l7.25 6.44A4 4 0 0 1 23 10.35v8.46a3.9 3.9 0 0 1-3.6 3.92 106 106 0 0 1-14.8 0A3.9 3.9 0 0 1 1 18.8v-8.46a4 4 0 0 1 1.34-3zM12 16a5 5 0 0 1-3.05-1.04l-1.23 1.58a7 7 0 0 0 8.56 0l-1.23-1.58A5 5 0 0 1 12 16">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const Dashboard = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M23 5a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v14a4 4 0 0 0 4 4h14a4 4 0 0 0 4-4zm-10 6V3h6a2 2 0 0 1 2 2v6zm8 8a2 2 0 0 1-2 2h-6v-8h8zM5 3h6v18H5a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const DashboardFilled = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M11 23H5a4 4 0 0 1-4-4V5a4 4 0 0 1 4-4h6zm12-4a4 4 0 0 1-4 4h-6V13h10zM19 1a4 4 0 0 1 4 4v6H13V1z">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const Add = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M11 11H6v2h5v5h2v-5h5v-2h-5V6h-2zM5 1a4 4 0 0 0-4 4v14a4 4 0 0 0 4 4h14a4 4 0 0 0 4-4V5a4 4 0 0 0-4-4zm16 4v14a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5c0-1.1.9-2 2-2h14a2 2 0 0 1 2 2">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const AddFilled = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M1 5a4 4 0 0 1 4-4h14a4 4 0 0 1 4 4v14a4 4 0 0 1-4 4H5a4 4 0 0 1-4-4zm10 6H6v2h5v5h2v-5h5v-2h-5V6h-2z">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const Bell = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M16 19h8v-2h-.34a3.15 3.15 0 0 1-3.12-2.76l-.8-6.41a7.8 7.8 0 0 0-15.48 0l-.8 6.41A3.15 3.15 0 0 1 .34 17H0v2h8v1h.02a3.4 3.4 0 0 0 3.38 3h1.2a3.4 3.4 0 0 0 3.38-3H16zm1.75-10.92.8 6.4c.12.95.5 1.81 1.04 2.52H4.4c.55-.7.92-1.57 1.04-2.51l.8-6.41a5.8 5.8 0 0 1 11.5 0M13.4 19c.33 0 .6.27.6.6 0 .77-.63 1.4-1.4 1.4h-1.2a1.4 1.4 0 0 1-1.4-1.4c0-.33.27-.6.6-.6z">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const BellFilled = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M20.54 14.24A3.15 3.15 0 0 0 23.66 17H24v2h-8v1h-.02a3.4 3.4 0 0 1-3.38 3h-1.2a3.4 3.4 0 0 1-3.38-3H8v-1H0v-2h.34a3.15 3.15 0 0 0 3.12-2.76l.8-6.41a7.8 7.8 0 0 1 15.48 0zM10 19.6c0 .77.63 1.4 1.4 1.4h1.2c.77 0 1.4-.63 1.4-1.4a.6.6 0 0 0-.6-.6h-2.8a.6.6 0 0 0-.6.6" ></path>
|
||||||
|
</svg>`, 1);
|
||||||
|
export const Search = createStaticVNode(`<svg aria-hidden="true" aria-label="" class="v-mid m-a" height="24" role="img" viewBox="0 0 24 24" width="24">
|
||||||
|
<path
|
||||||
|
d="M17.33 18.74a10 10 0 1 1 1.41-1.41l4.47 4.47-1.41 1.41zM11 3a8 8 0 1 0 0 16 8 8 0 0 0 0-16">
|
||||||
|
</path>
|
||||||
|
</svg>`, 1);
|
||||||
@@ -1,18 +1,30 @@
|
|||||||
import { Hono } from 'hono'
|
import { Hono } from 'hono'
|
||||||
import { renderer } from './renderer'
|
|
||||||
import { createApp } from './main';
|
import { createApp } from './main';
|
||||||
import { renderToWebStream } from 'vue/server-renderer';
|
import { renderToWebStream } from 'vue/server-renderer';
|
||||||
import { streamText } from 'hono/streaming';
|
import { streamText } from 'hono/streaming';
|
||||||
import { renderSSRHead } from '@unhead/vue/server';
|
import { renderSSRHead } from '@unhead/vue/server';
|
||||||
|
import { buildBootstrapScript, getHrefFromManifest } from './lib/manifest';
|
||||||
|
import { contextStorage } from 'hono/context-storage';
|
||||||
|
import { cors } from "hono/cors";
|
||||||
|
import { jwtRpc, rpcServer } from './api/rpc';
|
||||||
|
import isMobile from 'is-mobile';
|
||||||
|
|
||||||
const app = new Hono()
|
const app = new Hono()
|
||||||
|
|
||||||
// app.use(renderer)
|
// app.use(renderer)
|
||||||
|
|
||||||
// app.get('/', (c) => {
|
app.use(cors(), async (c, next) => {
|
||||||
// return c.text('Hello World!')
|
c.set("fetch", app.request.bind(app));
|
||||||
// // return c.render(<h1>Hello!</h1>)
|
const ua = c.req.header("User-Agent")
|
||||||
// })
|
if (!ua) {
|
||||||
|
return c.json({ error: "User-Agent header is missing" }, 400);
|
||||||
|
};
|
||||||
|
c.set("isMobile", isMobile({ ua }));
|
||||||
|
await next();
|
||||||
|
}, contextStorage(), rpcServer);
|
||||||
|
app.get("/.well-known/*", (c) => {
|
||||||
|
return c.json({ ok: true });
|
||||||
|
});
|
||||||
app.get("*", async (c) => {
|
app.get("*", async (c) => {
|
||||||
const url = new URL(c.req.url);
|
const url = new URL(c.req.url);
|
||||||
const { app, router, head } = createApp();
|
const { app, router, head } = createApp();
|
||||||
@@ -26,27 +38,28 @@ app.get("*", async (c) => {
|
|||||||
await stream.write("<!DOCTYPE html><html lang='en'><head>");
|
await stream.write("<!DOCTYPE html><html lang='en'><head>");
|
||||||
await stream.write("<base href='" + url.origin + "'/>");
|
await stream.write("<base href='" + url.origin + "'/>");
|
||||||
await renderSSRHead(head).then((headString) => stream.write(headString.headTags.replace(/\n/g, "")));
|
await renderSSRHead(head).then((headString) => stream.write(headString.headTags.replace(/\n/g, "")));
|
||||||
await stream.write(`<link
|
await stream.write(`<link href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"rel="stylesheet"></link>`);
|
||||||
href="https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap"rel="stylesheet"></link>`);
|
await stream.write('<link rel="icon" href="/favicon.ico" />');
|
||||||
await stream.write("</head><body class='font-sans bg-[#f9fafd] text-gray-800 antialiased flex flex-col'>");
|
await stream.write(buildBootstrapScript());
|
||||||
await stream.pipe(appStream);
|
await stream.write("</head><body class='font-sans bg-[#f9fafd] text-gray-800 antialiased flex flex-col min-h-screen'>");
|
||||||
let json = htmlEscape(JSON.stringify(JSON.stringify(ctx)));
|
await stream.pipe(appStream);
|
||||||
await stream.write(`<script>window.__SSR_STATE__ = JSON.parse(${json});</script>`);
|
let json = htmlEscape(JSON.stringify(JSON.stringify(ctx)));
|
||||||
await stream.write("</body></html>");
|
await stream.write(`<script>window.__SSR_STATE__ = JSON.parse(${json});</script>`);
|
||||||
|
await stream.write("</body></html>");
|
||||||
});
|
});
|
||||||
// return c.body(renderToWebStream(app, {}));
|
// return c.body(renderToWebStream(app, {}));
|
||||||
})
|
})
|
||||||
const ESCAPE_LOOKUP: { [match: string]: string } = {
|
const ESCAPE_LOOKUP: { [match: string]: string } = {
|
||||||
"&": "\\u0026",
|
"&": "\\u0026",
|
||||||
">": "\\u003e",
|
">": "\\u003e",
|
||||||
"<": "\\u003c",
|
"<": "\\u003c",
|
||||||
"\u2028": "\\u2028",
|
"\u2028": "\\u2028",
|
||||||
"\u2029": "\\u2029",
|
"\u2029": "\\u2029",
|
||||||
};
|
};
|
||||||
|
|
||||||
const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
const ESCAPE_REGEX = /[&><\u2028\u2029]/g;
|
||||||
|
|
||||||
function htmlEscape(str: string): string {
|
function htmlEscape(str: string): string {
|
||||||
return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
return str.replace(ESCAPE_REGEX, (match) => ESCAPE_LOOKUP[match]);
|
||||||
}
|
}
|
||||||
export default app
|
export default app
|
||||||
|
|||||||
3
src/lib/constants.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export const requestCtxKey = Symbol("requestCtx");
|
||||||
|
export const isRouteLoading = Symbol("isRouteLoading");
|
||||||
|
export const routerKey = Symbol("routerKey");
|
||||||
94
src/lib/manifest.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import type { Manifest } from "vite";
|
||||||
|
export interface GetHrefOptions {
|
||||||
|
href: string;
|
||||||
|
manifest?: Manifest;
|
||||||
|
prod?: boolean;
|
||||||
|
baseUrl?: string;
|
||||||
|
}
|
||||||
|
// Use Vite's import.meta.glob to dynamically search for manifest.json
|
||||||
|
export const loadManifest = (): Manifest | undefined => {
|
||||||
|
// Check if manifest content is provided via plugin
|
||||||
|
const manifestContent = import.meta.env.VITE_MANIFEST_CONTENT as
|
||||||
|
| string
|
||||||
|
| undefined;
|
||||||
|
if (manifestContent) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(manifestContent) as Manifest;
|
||||||
|
} catch {
|
||||||
|
// Fall through to auto-detection if parsing fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Placeholder replaced by inject-manifest plugin during SSR build
|
||||||
|
const MANIFEST = "__VITE_MANIFEST_CONTENT__" as unknown as Record<
|
||||||
|
string,
|
||||||
|
{ default: Manifest }
|
||||||
|
>;
|
||||||
|
|
||||||
|
let manifestData = {};
|
||||||
|
for (const [, manifestFile] of Object.entries(MANIFEST)) {
|
||||||
|
manifestData = { ...manifestData, ...manifestFile.default };
|
||||||
|
}
|
||||||
|
// Return merged values
|
||||||
|
return manifestData;
|
||||||
|
};
|
||||||
|
const ensureTrailingSlash = (path: string) => {
|
||||||
|
return path.endsWith("/") ? path : path + "/";
|
||||||
|
};
|
||||||
|
export const getHrefFromManifest = ({
|
||||||
|
href,
|
||||||
|
manifest,
|
||||||
|
prod,
|
||||||
|
baseUrl = "/",
|
||||||
|
}: GetHrefOptions) => {
|
||||||
|
if (!href) return undefined;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/prefer-optional-chain, @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (prod ?? (import.meta.env && import.meta.env.PROD)) {
|
||||||
|
manifest ??= loadManifest();
|
||||||
|
|
||||||
|
if (manifest) {
|
||||||
|
const assetInManifest = manifest[href.replace(/^\//, "")];
|
||||||
|
return href.startsWith("/")
|
||||||
|
? `${ensureTrailingSlash(baseUrl)}${assetInManifest.file}`
|
||||||
|
: assetInManifest.file;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
} else {
|
||||||
|
return href;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export function buildBootstrapScript() {
|
||||||
|
let script = "";
|
||||||
|
let styles = "";
|
||||||
|
let manifest: Manifest = import.meta.env.PROD
|
||||||
|
? loadManifest() ?? {}
|
||||||
|
: {
|
||||||
|
"0": {
|
||||||
|
file: "@vite/client",
|
||||||
|
isEntry: true,
|
||||||
|
css: [],
|
||||||
|
imports: [],
|
||||||
|
dynamicImports: [],
|
||||||
|
assets: [],
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
file: "src/client.ts",
|
||||||
|
isEntry: true,
|
||||||
|
css: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Object.values(manifest).forEach((chunk) => {
|
||||||
|
if (chunk.isEntry) {
|
||||||
|
script += `<script type="module" src="/${chunk.file}"></script>`;
|
||||||
|
(chunk.css || []).forEach((cssFile) => {
|
||||||
|
styles += `<link rel="stylesheet" crossorigin href="/${cssFile}">`;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
script += `<link rel="modulepreload" href="/${chunk.file}">`;
|
||||||
|
(chunk.css || []).forEach((cssFile) => {
|
||||||
|
styles += `<link rel="preload" as="style" href="/${cssFile}">`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return styles + script;
|
||||||
|
}
|
||||||
@@ -5,3 +5,46 @@ import { twMerge } from "tailwind-merge";
|
|||||||
export function cn(...inputs: ClassValue[]) {
|
export function cn(...inputs: ClassValue[]) {
|
||||||
return twMerge(clsx(inputs));
|
return twMerge(clsx(inputs));
|
||||||
}
|
}
|
||||||
|
export function debounce<Func extends (...args: any[]) => any>(func: Func, wait: number): Func {
|
||||||
|
let timeout: ReturnType<typeof setTimeout> | null;
|
||||||
|
return function(this: any, ...args: any[]) {
|
||||||
|
if (timeout) clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(() => {
|
||||||
|
func.apply(this, args);
|
||||||
|
}, wait);
|
||||||
|
} as Func;
|
||||||
|
}
|
||||||
|
type AspectInfo = {
|
||||||
|
width: number;
|
||||||
|
height: number;
|
||||||
|
ratio: string; // ví dụ: "16:9"
|
||||||
|
float: number; // ví dụ: 1.777...
|
||||||
|
};
|
||||||
|
|
||||||
|
function gcd(a: number, b: number): number {
|
||||||
|
return b === 0 ? a : gcd(b, a % b);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getImageAspectRatio(url: string): Promise<AspectInfo> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const img = new Image();
|
||||||
|
|
||||||
|
img.onload = () => {
|
||||||
|
const w = img.naturalWidth;
|
||||||
|
const h = img.naturalHeight;
|
||||||
|
|
||||||
|
const g = gcd(w, h);
|
||||||
|
|
||||||
|
resolve({
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
ratio: `${w / g}:${h / g}`,
|
||||||
|
float: w / h
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
img.onerror = () => reject(new Error("Cannot load image"));
|
||||||
|
|
||||||
|
img.src = url;
|
||||||
|
});
|
||||||
|
}
|
||||||
45
src/main.ts
@@ -5,13 +5,42 @@ import { RouterView } from 'vue-router';
|
|||||||
import { withErrorBoundary } from './lib/hoc/withErrorBoundary';
|
import { withErrorBoundary } from './lib/hoc/withErrorBoundary';
|
||||||
import { vueSWR } from './lib/swr/use-swrv';
|
import { vueSWR } from './lib/swr/use-swrv';
|
||||||
import router from './routes';
|
import router from './routes';
|
||||||
// import { appComponents } from './components'
|
import PrimeVue from 'primevue/config';
|
||||||
export function createApp() {
|
import Aura from '@primeuix/themes/aura';
|
||||||
|
import { createPinia } from "pinia";
|
||||||
|
import { useAuthStore } from './stores/auth';
|
||||||
|
|
||||||
const app = createSSRApp(withErrorBoundary(RouterView))
|
const pinia = createPinia();
|
||||||
const head = import.meta.env.SSR ? SSRHead() : CSRHead()
|
|
||||||
app.use(head)
|
export function createApp() {
|
||||||
app.use(vueSWR({revalidateOnFocus: false}))
|
const app = createSSRApp(withErrorBoundary(RouterView));
|
||||||
app.use(router)
|
const head = import.meta.env.SSR ? SSRHead() : CSRHead();
|
||||||
return { app, router, head }
|
|
||||||
|
app.use(head);
|
||||||
|
app.use(PrimeVue, {
|
||||||
|
theme: {
|
||||||
|
preset: Aura,
|
||||||
|
options: {
|
||||||
|
darkModeSelector: '.my-app-dark',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.directive('no-hydrate', {
|
||||||
|
created(el) {
|
||||||
|
el.__v_skip = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
app.use(vueSWR({revalidateOnFocus: false}));
|
||||||
|
app.use(router);
|
||||||
|
app.use(pinia);
|
||||||
|
|
||||||
|
// Initialize auth store on client side
|
||||||
|
if (!import.meta.env.SSR) {
|
||||||
|
router.isReady().then(() => {
|
||||||
|
const auth = useAuthStore();
|
||||||
|
auth.init();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return { app, router, head };
|
||||||
}
|
}
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
import { jsxRenderer } from 'hono/jsx-renderer'
|
|
||||||
import { Link, ViteClient } from 'vite-ssr-components/hono'
|
|
||||||
|
|
||||||
export const renderer = jsxRenderer(({ children }) => {
|
|
||||||
return (
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<ViteClient />
|
|
||||||
<Link href="/src/style.css" rel="stylesheet" />
|
|
||||||
</head>
|
|
||||||
<body>{children}</body>
|
|
||||||
</html>
|
|
||||||
)
|
|
||||||
})
|
|
||||||
3
src/routes/add/Add.vue
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<div>Add video</div>
|
||||||
|
</template>
|
||||||
56
src/routes/auth/forgot.vue
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit"
|
||||||
|
class="flex flex-col gap-4 w-full">
|
||||||
|
<div class="text-sm text-gray-600 mb-2">
|
||||||
|
Enter your email address and we'll send you a link to reset your password.
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<label for="email" class="text-sm font-medium text-gray-700">Email address</label>
|
||||||
|
<InputText name="email" type="email" placeholder="you@example.com" fluid />
|
||||||
|
<Message v-if="$form.email?.invalid" severity="error" size="small" variant="simple">{{
|
||||||
|
$form.email.error?.message }}</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" label="Send Reset Link" fluid />
|
||||||
|
|
||||||
|
<div class="text-center mt-2">
|
||||||
|
<router-link to="/login" replace
|
||||||
|
class="inline-flex items-center text-sm font-medium text-gray-600 hover:text-gray-900 transition-colors">
|
||||||
|
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
|
||||||
|
</svg>
|
||||||
|
Back to Sign in
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { Form, type FormSubmitEvent } from '@primevue/forms';
|
||||||
|
import { zodResolver } from '@primevue/forms/resolvers/zod';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
|
||||||
|
const initialValues = reactive({
|
||||||
|
email: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolver = zodResolver(
|
||||||
|
z.object({
|
||||||
|
email: z.string().min(1, { message: 'Email is required.' }).email({ message: 'Invalid email address.' })
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFormSubmit = ({ valid, values }: FormSubmitEvent) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log('Form submitted:', values);
|
||||||
|
// toast.add({ severity: 'success', summary: 'Success', detail: 'Reset link sent', life: 3000 });
|
||||||
|
// Handle actual forgot password logic here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
35
src/routes/auth/layout.vue
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full max-w-md bg-white p-8 rounded-xl border border-primary m-auto">
|
||||||
|
<div class="text-center mb-8">
|
||||||
|
<div class="inline-flex items-center justify-center w-12 h-12 mb-4">
|
||||||
|
<img class="w-12 h-12" src="/apple-touch-icon.png" alt="Logo" />
|
||||||
|
</div>
|
||||||
|
<h2 class="text-2xl font-bold text-gray-900">
|
||||||
|
{{ content[route.name as keyof typeof content]?.title || '' }}
|
||||||
|
</h2>
|
||||||
|
<p class="text-gray-500 text-sm mt-1">
|
||||||
|
{{ content[route.name as keyof typeof content]?.subtitle || '' }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<router-view />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const content = {
|
||||||
|
login: {
|
||||||
|
title: 'Welcome back',
|
||||||
|
subtitle: 'Please enter your details to sign in.'
|
||||||
|
},
|
||||||
|
signup: {
|
||||||
|
title: 'Create your account',
|
||||||
|
subtitle: 'Please fill in the information to create your account.'
|
||||||
|
},
|
||||||
|
forgot: {
|
||||||
|
title: 'Forgot your password?',
|
||||||
|
subtitle: "Enter your email address and we'll send you a link to reset your password."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
114
src/routes/auth/login.vue
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit"
|
||||||
|
class="flex flex-col gap-4 w-full">
|
||||||
|
|
||||||
|
<!-- Global error message -->
|
||||||
|
<Message v-if="auth.error" severity="error" size="small" variant="simple">
|
||||||
|
{{ auth.error }}
|
||||||
|
</Message>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<label for="email" class="text-sm font-medium text-gray-700">Email or Username</label>
|
||||||
|
<InputText name="email" type="text" placeholder="admin or user@example.com" fluid
|
||||||
|
:disabled="auth.loading" />
|
||||||
|
<Message v-if="$form.email?.invalid" severity="error" size="small" variant="simple">{{
|
||||||
|
$form.email.error?.message }}</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<label for="password" class="text-sm font-medium text-gray-700">Password</label>
|
||||||
|
<Password name="password" placeholder="••••••••" :feedback="false" toggleMask fluid
|
||||||
|
:inputStyle="{ width: '100%' }" :disabled="auth.loading" />
|
||||||
|
<Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
|
||||||
|
$form.password.error?.message }}</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<Checkbox inputId="remember-me" name="rememberMe" binary :disabled="auth.loading" />
|
||||||
|
<label for="remember-me" class="text-sm text-gray-900">Remember me</label>
|
||||||
|
</div>
|
||||||
|
<div class="text-sm">
|
||||||
|
<router-link to="/forgot"
|
||||||
|
class="font-medium text-blue-600 hover:text-blue-500 hover:underline">Forgot
|
||||||
|
password?</router-link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" :label="auth.loading ? 'Signing in...' : 'Sign in'" fluid :loading="auth.loading" />
|
||||||
|
|
||||||
|
<div class="relative my-4">
|
||||||
|
<div class="absolute inset-0 flex items-center">
|
||||||
|
<div class="w-full border-t border-gray-300"></div>
|
||||||
|
</div>
|
||||||
|
<div class="relative flex justify-center text-sm">
|
||||||
|
<span class="px-2 bg-white text-gray-500">Or continue with</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="button" variant="outlined" severity="secondary"
|
||||||
|
class="w-full flex items-center justify-center gap-2" @click="loginWithGoogle" :disabled="auth.loading">
|
||||||
|
<svg class="h-5 w-5" viewBox="0 0 24 24" fill="currentColor">
|
||||||
|
<path
|
||||||
|
d="M12.545,10.239v3.821h5.445c-0.712,2.315-2.647,3.972-5.445,3.972c-3.332,0-6.033-2.701-6.033-6.032s2.701-6.032,6.033-6.032c1.498,0,2.866,0.549,3.921,1.453l2.814-2.814C17.503,2.988,15.139,2,12.545,2C7.021,2,2.543,6.477,2.543,12s4.478,10,10.002,10c8.396,0,10.249-7.85,9.426-11.748L12.545,10.239z" />
|
||||||
|
</svg>
|
||||||
|
Google
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<p class="mt-4 text-center text-sm text-gray-600">
|
||||||
|
Don't have an account?
|
||||||
|
<router-link to="/signup" class="font-medium text-blue-600 hover:text-blue-500 hover:underline">Sign up
|
||||||
|
for free</router-link>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<!-- Hint for demo credentials -->
|
||||||
|
<div class="mt-2 p-3 bg-blue-50 border border-blue-200 rounded-lg">
|
||||||
|
<p class="text-xs text-blue-800 font-medium mb-1">Demo Credentials:</p>
|
||||||
|
<p class="text-xs text-blue-600">Username: <code class="bg-blue-100 px-1 rounded">admin</code> |
|
||||||
|
Password: <code class="bg-blue-100 px-1 rounded">admin123</code></p>
|
||||||
|
<p class="text-xs text-blue-600">Email: <code class="bg-blue-100 px-1 rounded">user@example.com</code> |
|
||||||
|
Password: <code class="bg-blue-100 px-1 rounded">password</code></p>
|
||||||
|
</div>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { Form, type FormSubmitEvent } from '@primevue/forms';
|
||||||
|
import { zodResolver } from '@primevue/forms/resolvers/zod';
|
||||||
|
import { z } from 'zod';
|
||||||
|
import { useAuthStore } from '@/stores/auth';
|
||||||
|
|
||||||
|
const auth = useAuthStore();
|
||||||
|
|
||||||
|
const initialValues = reactive({
|
||||||
|
email: '',
|
||||||
|
password: '',
|
||||||
|
rememberMe: false
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolver = zodResolver(
|
||||||
|
z.object({
|
||||||
|
email: z.string().min(1, { message: 'Email or username is required.' }),
|
||||||
|
password: z.string().min(1, { message: 'Password is required.' })
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFormSubmit = async ({ valid, values }: FormSubmitEvent) => {
|
||||||
|
if (valid) {
|
||||||
|
try {
|
||||||
|
await auth.login(values.email, values.password);
|
||||||
|
} catch (error) {
|
||||||
|
// Error is already set in the store
|
||||||
|
console.error('Login failed:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const loginWithGoogle = () => {
|
||||||
|
console.log('Login with Google');
|
||||||
|
// Handle Google login logic here
|
||||||
|
};
|
||||||
|
</script>
|
||||||
67
src/routes/auth/signup.vue
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w-full">
|
||||||
|
<Form v-slot="$form" :resolver="resolver" :initialValues="initialValues" @submit="onFormSubmit"
|
||||||
|
class="flex flex-col gap-4 w-full">
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<label for="name" class="text-sm font-medium text-gray-700">Full Name</label>
|
||||||
|
<InputText name="name" placeholder="John Doe" fluid />
|
||||||
|
<Message v-if="$form.name?.invalid" severity="error" size="small" variant="simple">{{
|
||||||
|
$form.name.error?.message }}</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<label for="email" class="text-sm font-medium text-gray-700">Email address</label>
|
||||||
|
<InputText name="email" type="email" placeholder="you@example.com" fluid />
|
||||||
|
<Message v-if="$form.email?.invalid" severity="error" size="small" variant="simple">{{
|
||||||
|
$form.email.error?.message }}</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col gap-1">
|
||||||
|
<label for="password" class="text-sm font-medium text-gray-700">Password</label>
|
||||||
|
<Password name="password" placeholder="Create a password" :feedback="true" toggleMask fluid
|
||||||
|
:inputStyle="{ width: '100%' }" />
|
||||||
|
<small class="text-gray-500">Must be at least 8 characters.</small>
|
||||||
|
<Message v-if="$form.password?.invalid" severity="error" size="small" variant="simple">{{
|
||||||
|
$form.password.error?.message }}</Message>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button type="submit" label="Create Account" fluid />
|
||||||
|
|
||||||
|
<p class="mt-4 text-center text-sm text-gray-600">
|
||||||
|
Already have an account?
|
||||||
|
<router-link to="/login" class="font-medium text-blue-600 hover:text-blue-500 hover:underline">Sign
|
||||||
|
in</router-link>
|
||||||
|
</p>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive } from 'vue';
|
||||||
|
import { Form, type FormSubmitEvent } from '@primevue/forms';
|
||||||
|
import { zodResolver } from '@primevue/forms/resolvers/zod';
|
||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
|
||||||
|
const initialValues = reactive({
|
||||||
|
name: '',
|
||||||
|
email: '',
|
||||||
|
password: ''
|
||||||
|
});
|
||||||
|
|
||||||
|
const resolver = zodResolver(
|
||||||
|
z.object({
|
||||||
|
name: z.string().min(1, { message: 'Name is required.' }),
|
||||||
|
email: z.string().min(1, { message: 'Email is required.' }).email({ message: 'Invalid email address.' }),
|
||||||
|
password: z.string().min(8, { message: 'Password must be at least 8 characters.' })
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
const onFormSubmit = ({ valid, values }: FormSubmitEvent) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log('Form submitted:', values);
|
||||||
|
// toast.add({ severity: 'success', summary: 'Success', detail: 'Account created successfully', life: 3000 });
|
||||||
|
// Handle actual signup logic here
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -1,7 +1,231 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>Home</div>
|
<nav class="fixed w-full z-50 glass-nav transition-all duration-300" id="navbar">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="flex items-center justify-between h-16">
|
||||||
|
<!-- Logo -->
|
||||||
|
<div class="flex items-center gap-2 cursor-pointer" onclick="window.scrollTo(0,0)">
|
||||||
|
<img class="h-8 w-8" src="/apple-touch-icon.png" alt="Logo" />
|
||||||
|
<span class="font-bold text-xl tracking-tight text-slate-900">EcoStream</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop Menu -->
|
||||||
|
<div class="hidden md:flex items-center space-x-8">
|
||||||
|
<a href="#features" class="text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors">Features</a>
|
||||||
|
<a href="#analytics" class="text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors">Analytics</a>
|
||||||
|
<a href="#pricing" class="text-sm font-medium text-slate-600 hover:text-brand-600 transition-colors">Pricing</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Auth Buttons -->
|
||||||
|
<div class="hidden md:flex items-center gap-4">
|
||||||
|
<RouterLink to="/login" class="text-sm font-semibold text-slate-600 hover:text-slate-900 cursor-pointer">Log in</RouterLink>
|
||||||
|
<RouterLink to="/signup" class="bg-slate-900 hover:bg-black text-white px-5 py-2.5 rounded-lg text-sm font-semibold cursor-pointer">
|
||||||
|
Start for free
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
<section class="relative pt-32 pb-20 lg:pt-48 lg:pb-32 overflow-hidden">
|
||||||
|
<!-- Background Elements -->
|
||||||
|
<div class="absolute inset-0 opacity-[0.4] -z-10"></div>
|
||||||
|
<div class="absolute top-0 right-0 -translate-y-1/2 translate-x-1/2 w-[800px] h-[800px] bg-brand-100/50 rounded-full blur-3xl -z-10 mix-blend-multiply"></div>
|
||||||
|
<div class="absolute bottom-0 left-0 translate-y-1/2 -translate-x-1/2 w-[600px] h-[600px] bg-teal-100/50 rounded-full blur-3xl -z-10 mix-blend-multiply"></div>
|
||||||
|
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 text-center">
|
||||||
|
<h1 class="text-5xl md:text-7xl font-extrabold tracking-tight text-slate-900 mb-6 leading-[1.1]">
|
||||||
|
Video infrastructure for <br>
|
||||||
|
<span class="text-gradient">modern internet.</span>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<p class="text-xl text-slate-500 max-w-2xl mx-auto mb-10 leading-relaxed">
|
||||||
|
Seamlessly host, encode, and stream video with our developer-first API.
|
||||||
|
Optimized for speed, built for scale.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row justify-center gap-4">
|
||||||
|
<RouterLink to="/get-started" class="flex btn btn-secondary !rounded-xl !p-4 press-animated">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="46 -286 524 580"><path d="M56 284v-560L560 4 56 284z" fill="#fff"/></svg>
|
||||||
|
Get Started
|
||||||
|
</RouterLink>
|
||||||
|
<RouterLink to="/docs" class="flex btn btn-outline-primary !rounded-xl">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="-10 -261 468 503"><path d="M256-139V72c0 18-14 32-32 32s-32-14-32-32v-211l-41 42c-13 12-33 12-46 0-12-13-12-33 0-46l96-96c13-12 33-12 46 0l96 96c12 13 12 33 0 46-13 12-33 12-46 0l-41-42zm-32 291c44 0 80-36 80-80h80c35 0 64 29 64 64v32c0 35-29 64-64 64H64c-35 0-64-29-64-64v-32c0-35 29-64 64-64h80c0 44 36 80 80 80zm144 24c13 0 24-11 24-24s-11-24-24-24-24 11-24 24 11 24 24 24z" fill="#14a74b"/></svg>
|
||||||
|
Upload video
|
||||||
|
</RouterLink>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section id="features" class="py-24 bg-white">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="mb-16 md:text-center max-w-3xl mx-auto">
|
||||||
|
<h2 class="text-3xl font-bold text-slate-900 mb-4">Everything you need to ship video</h2>
|
||||||
|
<p class="text-lg text-slate-500">Focus on building your product. We'll handle the complex video infrastructure.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
<!-- Large Feature -->
|
||||||
|
<div class="md:col-span-2 bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-primary/60 transition-all group overflow-hidden relative">
|
||||||
|
<div class="relative z-10">
|
||||||
|
<div class="w-12 h-12 bg-white rounded-xl flex items-center justify-center mb-6 border border-slate-100">
|
||||||
|
<!-- <i class="fas fa-globe text-xl"></i> -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="532" viewBox="-8 -258 529 532"><path d="M342 32c-2 69-16 129-35 172-10 23-22 40-32 49-10 10-16 11-19 11h-1c-3 0-9-1-19-11-10-9-22-26-32-49-19-43-33-103-35-172h173zm169 0c-9 103-80 188-174 219 30-51 50-129 53-219h121zm-390 0c3 89 23 167 53 218C80 219 11 134 2 32h119zm53-266c-30 51-50 129-53 218H2c9-102 78-186 172-218zm82-14c3 0 9 1 19 11 10 9 22 26 32 50 19 42 33 102 35 171H169c3-69 16-129 35-171 10-24 22-41 32-50s16-11 19-11h1zm81 13c94 31 165 116 174 219H390c-3-90-23-168-53-219z" fill="#059669"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-bold text-slate-900 mb-2">Global Edge Network</h3>
|
||||||
|
<p class="text-slate-500 max-w-md">Content delivered from 200+ PoPs worldwide. Automatic region selection ensures the lowest latency for every viewer.</p>
|
||||||
|
</div>
|
||||||
|
<!-- Decor -->
|
||||||
|
<div class="absolute right-0 bottom-0 opacity-10 translate-x-1/4 translate-y-1/4">
|
||||||
|
<!-- <i class="fas fa-globe-americas text-[200px] text-brand-900"></i> -->
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="-10 -258 532 532"><path d="M464 8c0-19-3-38-8-56l-27-5c-8-2-15 2-19 9-6 11-19 17-31 13l-14-5c-8-2-17 0-22 5-4 4-4 10 0 14l33 33c5 5 8 12 8 19 0 12-8 23-20 26l-6 1c-3 1-6 5-6 9v12c0 13-4 27-13 38l-25 34c-6 8-16 13-26 13-18 0-32-14-32-32V88c0-9-7-16-16-16h-32c-26 0-48-22-48-48V-4c0-13 6-24 16-32l39-30c6-4 13-6 20-6 3 0 7 1 10 2l32 10c7 3 15 3 22 1l36-9c10-2 17-11 17-22 0-8-5-16-13-20l-29-15c-3-2-8-1-11 2l-4 4c-4 4-11 7-17 7-4 0-8-1-11-3l-15-7c-7-4-15-2-20 4l-13 17c-6 7-16 8-22 1-3-2-5-6-5-10v-41c0-6-1-11-4-16l-10-18C102-154 48-79 48 8c0 115 93 208 208 208S464 123 464 8zM0 8c0-141 115-256 256-256S512-133 512 8 397 264 256 264 0 149 0 8z" fill="#1e3050"/></svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tall Feature -->
|
||||||
|
<div class="md:row-span-2 bg-slate-900 rounded-2xl p-8 text-white relative overflow-hidden group">
|
||||||
|
<div class="absolute inset-0 bg-gradient-to-b from-slate-800/50 to-transparent"></div>
|
||||||
|
<div class="relative z-10">
|
||||||
|
<div class="w-12 h-12 bg-white/10 rounded-xl flex items-center justify-center mb-6 backdrop-blur-sm border border-white/10">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="-10 -146 468 384"><path d="M392-136c-31 0-56 25-56 56v280c0 16 13 28 28 28h28c31 0 56-25 56-56V-80c0-31-25-56-56-56zM168 4c0-31 25-56 56-56h28c16 0 28 13 28 28v224c0 16-12 28-28 28h-56c-15 0-28-12-28-28V4zM0 88c0-31 25-56 56-56h28c16 0 28 13 28 28v140c0 16-12 28-28 28H56c-31 0-56-25-56-56V88z" fill="#fff"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-bold mb-2">Live Streaming API</h3>
|
||||||
|
<p class="text-slate-400 text-sm leading-relaxed mb-8">Scale to millions of concurrent viewers with ultra-low latency. RTMP ingest and HLS playback supported natively.</p>
|
||||||
|
|
||||||
|
<!-- Visual -->
|
||||||
|
<div class="bg-slate-800/50 rounded-lg p-4 border border-white/5 font-mono text-xs text-brand-300">
|
||||||
|
<div class="flex justify-between items-center mb-3 border-b border-white/5 pb-2">
|
||||||
|
<span class="text-slate-500">Live Status</span>
|
||||||
|
<span class="flex items-center gap-1.5 text-red-500 text-[10px] uppercase font-bold tracking-wider animate-pulse"><span class="w-1.5 h-1.5 rounded-full bg-red-500 animate-pulse"></span> On Air</span>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-1">
|
||||||
|
<div class="flex justify-between"><span class="text-slate-400">Bitrate:</span> <span class="text-white">6000 kbps</span></div>
|
||||||
|
<div class="flex justify-between"><span class="text-slate-400">FPS:</span> <span class="text-white">60</span></div>
|
||||||
|
<div class="flex justify-between"><span class="text-slate-400">Latency:</span> <span class="text-brand-400">~2s</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Standard Feature -->
|
||||||
|
<div class="bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-brand-200 transition-all group hover:shadow-lg hover:shadow-brand-500/5">
|
||||||
|
<div class="w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center mb-6 text-purple-600 border border-slate-100">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="0 0 570 570"><path d="M50 428c-5 5-5 14 0 19s14 5 19 0l237-237c5-5 5-14 0-19s-14-5-19 0L50 428zm16-224c-5 5-5 13 0 19 5 5 14 5 19 0l12-12c5-5 5-14 0-19-6-5-14-5-20 0l-11 12zM174 60c-5 5-5 13 0 19 5 5 14 5 19 0l12-12c5-5 5-14 0-19-6-5-14-5-20 0l-11 12zm215 29c-5 5-5 14 0 19s14 5 19 0l39-39c5-5 5-14 0-19s-14-5-19 0l-39 39zm21 357c-5 5-5 14 0 19s14 5 19 0l18-18c5-5 5-14 0-19s-14-5-19 0l-18 18z" fill="#a6acb9"/><path d="M170 26c14-15 36-15 50 0l18 18c15 14 15 36 0 50l-18 18c-14 15-36 15-50 0l-18-18c-15-14-15-36 0-50l18-18zm35 41c5-5 5-14 0-19-6-5-14-5-20 0l-11 12c-5 5-5 13 0 19 5 5 14 5 19 0l12-12zm204 342c21-21 55-21 76 0l18 18c21 21 21 55 0 76l-18 18c-21 21-55 21-76 0l-18-18c-21-21-21-55 0-76l18-18zm38 38c5-5 5-14 0-19s-14-5-19 0l-18 18c-5 5-5 14 0 19s14 5 19 0l18-18zM113 170c-15-15-37-15-51 0l-18 18c-14 14-14 36 0 50l18 18c14 15 37 15 51 0l18-18c14-14 14-36 0-50l-18-18zm-16 41-12 12c-5 5-14 5-19 0-5-6-5-14 0-20l11-11c6-5 14-5 20 0 5 5 5 14 0 19zM485 31c-21-21-55-21-76 0l-39 39c-21 21-21 55 0 76l54 54c21 21 55 21 76 0l39-39c21-21 21-55 0-76l-54-54zm-38 38-39 39c-5 5-14 5-19 0s-5-14 0-19l39-39c5-5 14-5 19 0s5 14 0 19zm-49 233c21-21 21-55 0-76l-54-54c-21-21-55-21-76 0L31 409c-21 21-21 55 0 76l54 54c21 21 55 21 76 0l237-237zm-92-92L69 447c-5 5-14 5-19 0s-5-14 0-19l237-237c5-5 14-5 19 0s5 14 0 19z" fill="#1e3050"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-bold text-slate-900 mb-2">Instant Encoding</h3>
|
||||||
|
<p class="text-slate-500 text-sm">Upload raw files and get optimized HLS/DASH streams in seconds.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Standard Feature -->
|
||||||
|
<div class="bg-slate-50 rounded-2xl p-8 border border-slate-100 hover:border-brand-200 transition-all group hover:shadow-lg hover:shadow-brand-500/5">
|
||||||
|
<div class="w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center mb-6 text-orange-600 border border-slate-100">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" viewBox="-10 -226 532 468"><path d="M32-216c18 0 32 14 32 32v336c0 9 7 16 16 16h400c18 0 32 14 32 32s-14 32-32 32H80c-44 0-80-36-80-80v-336c0-18 14-32 32-32zM144-24c18 0 32 14 32 32v64c0 18-14 32-32 32s-32-14-32-32V8c0-18 14-32 32-32zm144-64V72c0 18-14 32-32 32s-32-14-32-32V-88c0-18 14-32 32-32s32 14 32 32zm80 32c18 0 32 14 32 32v96c0 18-14 32-32 32s-32-14-32-32v-96c0-18 14-32 32-32zm144-96V72c0 18-14 32-32 32s-32-14-32-32v-224c0-18 14-32 32-32s32 14 32 32z" fill="#1e3050"/></svg>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-bold text-slate-900 mb-2">Deep Analytics</h3>
|
||||||
|
<p class="text-slate-500 text-sm">Session-level insights, quality of experience (QoE) metrics, and more.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- Pricing -->
|
||||||
|
<section id="pricing" class="py-24 bg-white border-t border-slate-100">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="text-center mb-16">
|
||||||
|
<h2 class="text-3xl font-bold text-slate-900 mb-4">Simple, transparent pricing</h2>
|
||||||
|
<p class="text-slate-500">No hidden fees. Pay as you grow.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-8 max-w-5xl mx-auto">
|
||||||
|
<!-- Hobby -->
|
||||||
|
<div class="p-8 rounded-2xl border border-slate-200 hover:border-slate-300 transition-colors">
|
||||||
|
<h3 class="font-semibold text-slate-900 mb-2">Hobby</h3>
|
||||||
|
<div class="flex items-baseline gap-1 mb-6">
|
||||||
|
<span class="text-4xl font-bold text-slate-900">$0</span>
|
||||||
|
<span class="text-slate-500">/mo</span>
|
||||||
|
</div>
|
||||||
|
<ul class="space-y-3 mb-8 text-sm text-slate-600">
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-brand-500"></i> 100 GB Bandwidth</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-brand-500"></i> 1 Hour of Storage</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-brand-500"></i> Standard Support</li>
|
||||||
|
</ul>
|
||||||
|
<button class="w-full py-2.5 rounded-lg border border-slate-200 font-semibold text-slate-700 hover:bg-slate-50 transition-colors">Start Free</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Pro -->
|
||||||
|
<div class="p-8 rounded-2xl bg-slate-900 text-white shadow-2xl relative overflow-hidden transform">
|
||||||
|
<div class="absolute top-0 right-0 bg-primary/50 text-white text-xs font-bold px-3 py-1 rounded-bl-lg">POPULAR</div>
|
||||||
|
<h3 class="font-semibold mb-2 text-brand-400">Pro</h3>
|
||||||
|
<div class="flex items-baseline gap-1 mb-6">
|
||||||
|
<span class="text-4xl font-bold">$0</span>
|
||||||
|
<span class="text-lg font-bold line-through">$29</span>
|
||||||
|
<span class="text-slate-400">/mo</span>
|
||||||
|
</div>
|
||||||
|
<ul class="space-y-3 mb-8 text-sm text-slate-300">
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-primary/60"></i> 1 TB Bandwidth</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-primary/60"></i> 100 Hours Storage</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-primary/60"></i> Remove Branding</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-primary/60"></i> 4K Encoding</li>
|
||||||
|
</ul>
|
||||||
|
<button class="w-full py-2.5 rounded-lg bg-primary/60 hover:bg-primary/70 font-semibold transition-colors shadow-lg shadow-primary/30">Get Started</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Scale -->
|
||||||
|
<div class="p-8 rounded-2xl border border-slate-200 hover:border-slate-300 transition-colors">
|
||||||
|
<h3 class="font-semibold text-slate-900 mb-2">Scale</h3>
|
||||||
|
<div class="flex items-baseline gap-1 mb-6">
|
||||||
|
<span class="text-4xl font-bold text-slate-900">$99</span>
|
||||||
|
<span class="text-slate-500">/mo</span>
|
||||||
|
</div>
|
||||||
|
<ul class="space-y-3 mb-8 text-sm text-slate-600">
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-brand-500"></i> 5 TB Bandwidth</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-brand-500"></i> 500 Hours Storage</li>
|
||||||
|
<li class="flex items-center gap-3"><i class="fas fa-check text-brand-500"></i> Priority Support</li>
|
||||||
|
</ul>
|
||||||
|
<button class="w-full py-2.5 rounded-lg border border-slate-200 font-semibold text-slate-700 hover:bg-slate-50 transition-colors">Contact Sales</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer class="bg-white border-t border-slate-100 pt-16 pb-8">
|
||||||
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||||
|
<div class="grid grid-cols-2 md:grid-cols-5 gap-8 mb-12">
|
||||||
|
<div class="col-span-2">
|
||||||
|
<div class="flex items-center gap-2 mb-4">
|
||||||
|
<div class="w-6 h-6 bg-brand-600 rounded flex items-center justify-center text-white">
|
||||||
|
<img class="h-6 w-6" src="/apple-touch-icon.png" alt="Logo" />
|
||||||
|
</div>
|
||||||
|
<span class="font-bold text-lg text-slate-900">EcoStream</span>
|
||||||
|
</div>
|
||||||
|
<p class="text-slate-500 text-sm max-w-xs">Building the video layer of the internet. Designed for developers.</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 class="font-semibold text-slate-900 mb-4 text-sm">Product</h4>
|
||||||
|
<ul class="space-y-2 text-sm text-slate-500">
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Features</a></li>
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Pricing</a></li>
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Showcase</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 class="font-semibold text-slate-900 mb-4 text-sm">Company</h4>
|
||||||
|
<ul class="space-y-2 text-sm text-slate-500">
|
||||||
|
<li><a href="#" class="hover:text-brand-600">About</a></li>
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Blog</a></li>
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Careers</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h4 class="font-semibold text-slate-900 mb-4 text-sm">Legal</h4>
|
||||||
|
<ul class="space-y-2 text-sm text-slate-500">
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Privacy</a></li>
|
||||||
|
<li><a href="#" class="hover:text-brand-600">Terms</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="pt-8 border-t border-slate-100 text-center text-sm text-slate-400">
|
||||||
|
© 2026 EcoStream Inc. All rights reserved.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
@@ -5,15 +5,78 @@ import {
|
|||||||
createWebHistory,
|
createWebHistory,
|
||||||
type RouteRecordRaw,
|
type RouteRecordRaw,
|
||||||
} from "vue-router";
|
} from "vue-router";
|
||||||
|
import { useAuthStore } from "@/stores/auth";
|
||||||
|
|
||||||
type RouteData = RouteRecordRaw & {
|
type RouteData = RouteRecordRaw & {
|
||||||
meta?: ResolvableValue<ReactiveHead>;
|
meta?: ResolvableValue<ReactiveHead> & { requiresAuth?: boolean };
|
||||||
children?: RouteData[];
|
children?: RouteData[];
|
||||||
};
|
};
|
||||||
const routes: RouteData[] = [
|
const routes: RouteData[] = [
|
||||||
{
|
{
|
||||||
path: "/",
|
path: "/",
|
||||||
component: () => import("./home/Home.vue")
|
component: () => import("@/components/RootLayout.vue"),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: () => import("./home/Home.vue"),
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
const auth = useAuthStore();
|
||||||
|
if (auth.user) {
|
||||||
|
next({ name: "overview" });
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: () => import("./auth/layout.vue"),
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "login",
|
||||||
|
name: "login",
|
||||||
|
component: () => import("./auth/login.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "signup",
|
||||||
|
name: "signup",
|
||||||
|
component: () => import("./auth/signup.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "forgot",
|
||||||
|
name: "forgot",
|
||||||
|
component: () => import("./auth/forgot.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
component: () => import("@/components/DashboardLayout.vue"),
|
||||||
|
meta: { requiresAuth: true },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: "",
|
||||||
|
name: "overview",
|
||||||
|
component: () => import("./add/Add.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "video",
|
||||||
|
name: "video",
|
||||||
|
component: () => import("./add/Add.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "add",
|
||||||
|
name: "add",
|
||||||
|
component: () => import("./add/Add.vue"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "notification",
|
||||||
|
name: "notification",
|
||||||
|
component: () => import("./add/Add.vue"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
@@ -22,4 +85,18 @@ const router = createRouter({
|
|||||||
: createWebHistory(), // client
|
: createWebHistory(), // client
|
||||||
routes,
|
routes,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.beforeEach((to, from, next) => {
|
||||||
|
const auth = useAuthStore();
|
||||||
|
if (to.matched.some((record) => record.meta.requiresAuth)) {
|
||||||
|
if (!auth.user) {
|
||||||
|
next({ name: "login" });
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
|||||||
88
src/stores/auth.ts
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { client } from '@/api/rpcclient';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: string;
|
||||||
|
username: string;
|
||||||
|
email: string;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useAuthStore = defineStore('auth', () => {
|
||||||
|
const user = ref<User | null>(null);
|
||||||
|
const router = useRouter();
|
||||||
|
const loading = ref(false);
|
||||||
|
const error = ref<string | null>(null);
|
||||||
|
const csrfToken = ref<string | null>(null);
|
||||||
|
const initialized = ref(false);
|
||||||
|
|
||||||
|
// Check auth status on init (reads from cookie)
|
||||||
|
async function init() {
|
||||||
|
if (initialized.value) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await client.checkAuth();
|
||||||
|
if (response.authenticated && response.user) {
|
||||||
|
user.value = response.user;
|
||||||
|
// Get CSRF token if authenticated
|
||||||
|
try {
|
||||||
|
const csrfResponse = await client.getCSRFToken();
|
||||||
|
csrfToken.value = csrfResponse.csrfToken;
|
||||||
|
} catch (e) {
|
||||||
|
// CSRF token might not be available yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
// Not authenticated, that's fine
|
||||||
|
} finally {
|
||||||
|
initialized.value = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(username: string, password: string) {
|
||||||
|
loading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
try {
|
||||||
|
const response = await client.login(username, password);
|
||||||
|
user.value = response.user;
|
||||||
|
csrfToken.value = response.csrfToken;
|
||||||
|
router.push('/');
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e.message || 'Login failed';
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function register(username: string, email: string, password: string) {
|
||||||
|
loading.value = true;
|
||||||
|
error.value = null;
|
||||||
|
try {
|
||||||
|
const response = await client.register({ username, email, password });
|
||||||
|
user.value = response.user;
|
||||||
|
csrfToken.value = response.csrfToken;
|
||||||
|
router.push('/');
|
||||||
|
} catch (e: any) {
|
||||||
|
error.value = e.message || 'Registration failed';
|
||||||
|
throw e;
|
||||||
|
} finally {
|
||||||
|
loading.value = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
try {
|
||||||
|
await client.logout();
|
||||||
|
} catch (e) {
|
||||||
|
// Ignore errors on logout
|
||||||
|
}
|
||||||
|
user.value = null;
|
||||||
|
csrfToken.value = null;
|
||||||
|
router.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
return { user, loading, error, csrfToken, initialized, init, login, register, logout };
|
||||||
|
});
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
h1 {
|
|
||||||
font-family: Arial, Helvetica, sans-serif;
|
|
||||||
}
|
|
||||||
56
src/worker/html.ts
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* @module
|
||||||
|
* html Helper for Hono.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { escapeToBuffer, raw, resolveCallbackSync, stringBufferToString } from 'hono/utils/html'
|
||||||
|
import type { HtmlEscaped, HtmlEscapedString, StringBufferWithCallbacks } from 'hono/utils/html'
|
||||||
|
|
||||||
|
|
||||||
|
export const html = (
|
||||||
|
strings: TemplateStringsArray,
|
||||||
|
...values: unknown[]
|
||||||
|
): HtmlEscapedString | Promise<HtmlEscapedString> => {
|
||||||
|
const buffer: StringBufferWithCallbacks = [''] as StringBufferWithCallbacks
|
||||||
|
|
||||||
|
for (let i = 0, len = strings.length - 1; i < len; i++) {
|
||||||
|
buffer[0] += strings[i]
|
||||||
|
|
||||||
|
const children = Array.isArray(values[i])
|
||||||
|
? (values[i] as Array<unknown>).flat(Infinity)
|
||||||
|
: [values[i]]
|
||||||
|
for (let i = 0, len = children.length; i < len; i++) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const child = children[i] as any
|
||||||
|
if (typeof child === 'string') {
|
||||||
|
escapeToBuffer(child, buffer)
|
||||||
|
} else if (typeof child === 'number') {
|
||||||
|
;(buffer[0] as string) += child
|
||||||
|
} else if (typeof child === 'boolean' || child === null || child === undefined) {
|
||||||
|
continue
|
||||||
|
} else if (typeof child === 'object' && (child as HtmlEscaped).isEscaped) {
|
||||||
|
if ((child as HtmlEscapedString).callbacks) {
|
||||||
|
buffer.unshift('', child)
|
||||||
|
} else {
|
||||||
|
const tmp = child.toString()
|
||||||
|
if (tmp instanceof Promise) {
|
||||||
|
buffer.unshift('', tmp)
|
||||||
|
} else {
|
||||||
|
buffer[0] += tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (child instanceof Promise) {
|
||||||
|
buffer.unshift('', child)
|
||||||
|
} else {
|
||||||
|
escapeToBuffer(child.toString(), buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buffer[0] += strings.at(-1) as string
|
||||||
|
|
||||||
|
return buffer.length === 1
|
||||||
|
? 'callbacks' in buffer
|
||||||
|
? raw(resolveCallbackSync(raw(buffer[0], buffer.callbacks)))
|
||||||
|
: raw(buffer[0])
|
||||||
|
: stringBufferToString(buffer, buffer.callbacks)
|
||||||
|
}
|
||||||
65
src/worker/ssrLayout.ts
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
import { createContext, jsx, Suspense } from "hono/jsx";
|
||||||
|
import { renderToReadableStream, StreamingContext } from "hono/jsx/streaming";
|
||||||
|
import { HtmlEscapedCallback, HtmlEscapedString, raw } from "hono/utils/html";
|
||||||
|
// import { jsxs } from "hono/jsx-renderer";
|
||||||
|
import { Context } from "hono";
|
||||||
|
import type {
|
||||||
|
FC,
|
||||||
|
Context as JSXContext,
|
||||||
|
JSXNode
|
||||||
|
} from "hono/jsx";
|
||||||
|
import { jsxTemplate } from "hono/jsx/jsx-runtime";
|
||||||
|
export const RequestContext: JSXContext<Context<any, any, {}> | null> =
|
||||||
|
createContext<Context | null>(null);
|
||||||
|
export function renderSSRLayout(c: Context, appStream: ReadableStream) {
|
||||||
|
const body = jsxTemplate`${raw("<!DOCTYPE html>")}${_c(
|
||||||
|
RequestContext.Provider,
|
||||||
|
{ value: c },
|
||||||
|
// currentLayout as any
|
||||||
|
_c(
|
||||||
|
"html",
|
||||||
|
{ lang: "en" },
|
||||||
|
_c(
|
||||||
|
"head",
|
||||||
|
null,
|
||||||
|
raw('<meta charset="UTF-8"/>'),
|
||||||
|
raw(
|
||||||
|
'<meta name="viewport" content="width=device-width, initial-scale=1.0"/>'
|
||||||
|
),
|
||||||
|
raw('<link rel="icon" href="/favicon.ico" />'),
|
||||||
|
raw(`<base href="${new URL(c.req.url).origin}/"/>`)
|
||||||
|
),
|
||||||
|
_c(
|
||||||
|
"body",
|
||||||
|
{
|
||||||
|
class:
|
||||||
|
"font-sans bg-[#f9fafd] text-gray-800 antialiased flex flex-col",
|
||||||
|
},
|
||||||
|
_c(
|
||||||
|
StreamingContext,
|
||||||
|
{ value: { scriptNonce: "random-nonce-value" } },
|
||||||
|
_c(
|
||||||
|
Suspense,
|
||||||
|
{ fallback: _c("div", { class: "loading" }, raw("Loading...")) },
|
||||||
|
raw(appStream.getReader())
|
||||||
|
)
|
||||||
|
),
|
||||||
|
_c("script", {
|
||||||
|
dangerouslySetInnerHTML: {
|
||||||
|
__html: `window.__SSR_STATE__ = ${JSON.stringify(
|
||||||
|
JSON.stringify(c.get("ssrContext") || {})
|
||||||
|
)};`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)}`;
|
||||||
|
return renderToReadableStream(body);
|
||||||
|
}
|
||||||
|
function _c(
|
||||||
|
tag: string | FC<any>,
|
||||||
|
props: any,
|
||||||
|
...children: (JSXNode | HtmlEscapedCallback | HtmlEscapedString | null)[]
|
||||||
|
): JSXNode {
|
||||||
|
return jsx(tag, props, ...(children as any));
|
||||||
|
}
|
||||||
142
ssrPlugin.ts
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
import { readFileSync } from "node:fs";
|
||||||
|
import path from "node:path";
|
||||||
|
import type { Plugin } from "vite";
|
||||||
|
export function createVirtualPlugin(name: string, load: Plugin["load"]) {
|
||||||
|
name = "virtual:" + name;
|
||||||
|
return {
|
||||||
|
name,
|
||||||
|
resolveId(source, _importer, _options) {
|
||||||
|
if (source === name || source.startsWith(`${name}?`)) {
|
||||||
|
return `\0${source}`;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
},
|
||||||
|
load(id, options) {
|
||||||
|
if (id === `\0${name}` || id.startsWith(`\0${name}?`)) {
|
||||||
|
return (load as any).apply(this, [id, options]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
} satisfies Plugin;
|
||||||
|
}
|
||||||
|
export function clientFirstBuild(): Plugin {
|
||||||
|
return {
|
||||||
|
name: "client-first-build",
|
||||||
|
config(config) {
|
||||||
|
config.builder ??= {};
|
||||||
|
config.builder.buildApp = async (builder) => {
|
||||||
|
const clientEnvironment = builder.environments.client;
|
||||||
|
const workerEnvironments = Object.keys(builder.environments)
|
||||||
|
.filter((name) => name !== "client" && name !== "ssr")
|
||||||
|
.map((name) => builder.environments[name]);
|
||||||
|
// console.log('Client First Build Plugin: Starting builds...', workerEnvironments)
|
||||||
|
// Client build first
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||||
|
if (clientEnvironment) {
|
||||||
|
await builder.build(clientEnvironment);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then worker builds
|
||||||
|
for (const workerEnv of workerEnvironments) {
|
||||||
|
await builder.build(workerEnv);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export function injectManifest(): Plugin {
|
||||||
|
let clientOutDir = "dist/client";
|
||||||
|
|
||||||
|
return {
|
||||||
|
name: "inject-manifest",
|
||||||
|
config(config) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const viteConfig = config as any;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
||||||
|
clientOutDir =
|
||||||
|
viteConfig.environments?.client?.build?.outDir ?? "dist/client";
|
||||||
|
},
|
||||||
|
async transform(code, id, options) {
|
||||||
|
// Only transform in SSR environment (non-client)
|
||||||
|
if (!options?.ssr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Only transform files that contain the placeholder
|
||||||
|
if (!code.includes("__VITE_MANIFEST_CONTENT__")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read manifest from client build output
|
||||||
|
const manifestPath = path.resolve(
|
||||||
|
process.cwd(),
|
||||||
|
clientOutDir,
|
||||||
|
".vite/manifest.json"
|
||||||
|
);
|
||||||
|
let manifestContent: string | undefined;
|
||||||
|
try {
|
||||||
|
manifestContent = await this.fs
|
||||||
|
.readFile(manifestPath)
|
||||||
|
.then((data) => data.toString());
|
||||||
|
} catch {
|
||||||
|
// Manifest not found
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manifestContent) return;
|
||||||
|
|
||||||
|
// Replace placeholder string with actual manifest data
|
||||||
|
// Format: { "__manifest__": { default: <manifest> } } to match the Object.entries loop
|
||||||
|
const newCode = code.replace(
|
||||||
|
/"__VITE_MANIFEST_CONTENT__"/g,
|
||||||
|
`{ "__manifest__": { default: ${manifestContent} } }`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (newCode !== code) {
|
||||||
|
return { code: newCode, map: null };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
export default function ssrPlugin(): Plugin[] {
|
||||||
|
// const { hotReload: hotReloadOption = true, entry: entryOption = {} } = options
|
||||||
|
|
||||||
|
const plugins: Plugin[] = [];
|
||||||
|
|
||||||
|
plugins.push(clientFirstBuild());
|
||||||
|
plugins.push({
|
||||||
|
name: "ssr-auto-entry",
|
||||||
|
config(config) {
|
||||||
|
config.define = config.define || {};
|
||||||
|
},
|
||||||
|
async configResolved(config) {
|
||||||
|
const viteConfig = config as any;
|
||||||
|
|
||||||
|
if (!viteConfig.environments) {
|
||||||
|
viteConfig.environments = {};
|
||||||
|
}
|
||||||
|
if (!viteConfig.environments.client) {
|
||||||
|
viteConfig.environments.client = {};
|
||||||
|
}
|
||||||
|
if (!viteConfig.environments.client.build) {
|
||||||
|
viteConfig.environments.client.build = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientBuild = viteConfig.environments.client.build;
|
||||||
|
clientBuild.manifest = true;
|
||||||
|
clientBuild.rollupOptions = clientBuild.rollupOptions || {};
|
||||||
|
clientBuild.rollupOptions.input = "src/client.ts";
|
||||||
|
if (!viteConfig.environments.ssr) {
|
||||||
|
const manifestPath = path.join(clientBuild.outDir as string, '.vite/manifest.json')
|
||||||
|
try {
|
||||||
|
const resolvedPath = path.resolve(process.cwd(), manifestPath)
|
||||||
|
const manifestContent = readFileSync(resolvedPath, 'utf-8')
|
||||||
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||||
|
// @ts-ignore
|
||||||
|
config.define['import.meta.env.VITE_MANIFEST_CONTENT'] = JSON.stringify(manifestContent)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
plugins.push(injectManifest());
|
||||||
|
|
||||||
|
return plugins;
|
||||||
|
}
|
||||||
@@ -10,6 +10,10 @@
|
|||||||
],
|
],
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client"],
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"jsxImportSource": "vue"
|
"jsxImportSource": "vue",
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {
|
||||||
|
"@/*": ["./src/*"]
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -1,18 +1,12 @@
|
|||||||
// unocss.config.ts
|
import { defineConfig, presetAttributify, presetTypography, presetWind4, transformerCompileClass, transformerVariantGroup } from 'unocss'
|
||||||
import {
|
import { presetBootstrapBtn } from "./bootstrap_btn";
|
||||||
defineConfig,
|
|
||||||
presetWind4,
|
|
||||||
presetIcons,
|
|
||||||
transformerVariantGroup,
|
|
||||||
transformerCompileClass,
|
|
||||||
} from "unocss";
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
presets: [
|
presets: [
|
||||||
presetWind4() as any,
|
presetWind4() as any,
|
||||||
presetIcons({
|
presetTypography(),
|
||||||
extraProperties: { display: "block" },
|
presetBootstrapBtn(),
|
||||||
}),
|
presetAttributify(),
|
||||||
],
|
],
|
||||||
// By default, `.ts` and `.js` files are NOT extracted.
|
// By default, `.ts` and `.js` files are NOT extracted.
|
||||||
// If you want to extract them, use the following configuration.
|
// If you want to extract them, use the following configuration.
|
||||||
|
|||||||
@@ -1,14 +1,30 @@
|
|||||||
import { cloudflare } from '@cloudflare/vite-plugin'
|
import { cloudflare } from "@cloudflare/vite-plugin";
|
||||||
import { defineConfig } from 'vite'
|
import { defineConfig, Manifest, Plugin } from "vite";
|
||||||
import vue from '@vitejs/plugin-vue'
|
import vue from "@vitejs/plugin-vue";
|
||||||
import vueJsx from '@vitejs/plugin-vue-jsx'
|
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||||
import unocss from "unocss/vite";
|
import unocss from "unocss/vite";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
export default defineConfig({
|
import ssrPlugin from "./ssrPlugin";
|
||||||
plugins: [unocss(), cloudflare(), vue(), vueJsx()],
|
import Components from 'unplugin-vue-components/vite';
|
||||||
resolve: {
|
import {PrimeVueResolver} from '@primevue/auto-import-resolver';
|
||||||
alias: {
|
export default defineConfig((env) => {
|
||||||
"@": path.resolve(__dirname, "./src"),
|
return {
|
||||||
|
plugins: [
|
||||||
|
unocss(),
|
||||||
|
vue(),
|
||||||
|
vueJsx(),
|
||||||
|
ssrPlugin(),
|
||||||
|
cloudflare(),
|
||||||
|
Components({
|
||||||
|
resolvers: [
|
||||||
|
PrimeVueResolver()
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
"@": path.resolve(__dirname, "./src"),
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
};
|
||||||
})
|
});
|
||||||
|
|||||||
@@ -2,5 +2,8 @@
|
|||||||
"$schema": "node_modules/wrangler/config-schema.json",
|
"$schema": "node_modules/wrangler/config-schema.json",
|
||||||
"name": "holistream",
|
"name": "holistream",
|
||||||
"compatibility_date": "2025-08-03",
|
"compatibility_date": "2025-08-03",
|
||||||
"main": "./src/index.tsx"
|
"main": "./src/index.tsx",
|
||||||
|
"compatibility_flags": [
|
||||||
|
"nodejs_compat"
|
||||||
|
]
|
||||||
}
|
}
|
||||||