refactor(model): extract dashboard model package #34

Merged
vince merged 2 commits from codex/turbo-package-boundaries into main 2026-06-20 06:36:32 +02:00
36 changed files with 197 additions and 57 deletions

View file

@ -5,13 +5,16 @@ reusable React component library.
This project is not a Homepage customization and does not depend on Homepage This project is not a Homepage customization and does not depend on Homepage
runtime, frontend code, or configuration. The dashboard will be model-driven: runtime, frontend code, or configuration. The dashboard will be model-driven:
the reusable UI package stays content-free, while environment-specific data the reusable UI package stays content-free, the reusable dashboard model
lives in validated dashboard model state inside the web app. package owns schema and validation, and environment-specific data lives in
validated dashboard model state inside the web app.
## Workspace Layout ## Workspace Layout
- `apps/web`: Vite React website, Bun API server, model fixtures, Drizzle - `apps/web`: Vite React website, Bun API server, model fixtures, Drizzle
persistence, Playwright e2e checks, and container build. persistence, Playwright e2e checks, and container build.
- `packages/dashboard-model`: reusable dashboard schema, validation, and
generic model fixtures shared by apps and tooling.
- `packages/ui`: reusable dashboard React components, design tokens, - `packages/ui`: reusable dashboard React components, design tokens,
shadcn/radix primitives, generic fixtures, and Storybook. shadcn/radix primitives, generic fixtures, and Storybook.
- `docs/superpowers`: migration specs and execution plans used for this repo. - `docs/superpowers`: migration specs and execution plans used for this repo.
@ -65,7 +68,7 @@ with an explicit migration error.
## Seed Data ## Seed Data
The initial Dimension Lab dashboard lives in The initial Dimension Lab dashboard lives in
`apps/web/src/lib/model/fixtures/dimensionlab.ts` as validated model data. It `apps/web/src/lib/dashboard-seed/dimensionlab.ts` as validated model data. It
includes the first-screen telemetry, service groups, status strip, weather includes the first-screen telemetry, service groups, status strip, weather
module, Iconify icon identifiers, links, and datasource references. Values that module, Iconify icon identifiers, links, and datasource references. Values that
are not live yet are labeled as fallback values in the data so later datasource are not live yet are labeled as fallback values in the data so later datasource

View file

@ -4,6 +4,7 @@ WORKDIR /repo
ENV PATH=/repo/apps/web/node_modules/.bin:/repo/packages/ui/node_modules/.bin:/repo/node_modules/.bin:$PATH ENV PATH=/repo/apps/web/node_modules/.bin:/repo/packages/ui/node_modules/.bin:/repo/node_modules/.bin:$PATH
COPY package.json bun.lock turbo.json tsconfig.base.json ./ COPY package.json bun.lock turbo.json tsconfig.base.json ./
COPY apps/web/package.json apps/web/package.json COPY apps/web/package.json apps/web/package.json
COPY packages/dashboard-model/package.json packages/dashboard-model/package.json
COPY packages/ui/package.json packages/ui/package.json COPY packages/ui/package.json packages/ui/package.json
RUN bun install --frozen-lockfile RUN bun install --frozen-lockfile

View file

@ -15,6 +15,7 @@
"db:check": "drizzle-kit check" "db:check": "drizzle-kit check"
}, },
"dependencies": { "dependencies": {
"@dimensionlab/dashboard-model": "workspace:*",
"@dimensionlab/ui": "workspace:*", "@dimensionlab/ui": "workspace:*",
"@sinclair/typebox": "^0.34.49", "@sinclair/typebox": "^0.34.49",
"ajv": "^8.20.0", "ajv": "^8.20.0",

View file

@ -7,7 +7,7 @@ import type {
DatasourceReference, DatasourceReference,
ServiceEntry, ServiceEntry,
TelemetryCard, TelemetryCard,
} from "../schema"; } from "@dimensionlab/dashboard-model";
describe("Dimension Lab dashboard seed", () => { describe("Dimension Lab dashboard seed", () => {
test("defines the primary first-screen sections as model data", () => { test("defines the primary first-screen sections as model data", () => {

View file

@ -7,7 +7,7 @@ import {
type ServiceGroup, type ServiceGroup,
type Severity, type Severity,
type TelemetryCard, type TelemetryCard,
} from "../schema"; } from "@dimensionlab/dashboard-model";
type NumericValueKind = "percent" | "bytes" | "temperature" | "latency" | "number"; type NumericValueKind = "percent" | "bytes" | "temperature" | "latency" | "number";

View file

@ -1,2 +0,0 @@
export { dimensionLabDashboardFixture } from "./dimensionlab";
export { genericDashboardFixture } from "./generic";

View file

@ -74,5 +74,7 @@ function findFiles(path: string, extension: string): string[] {
} }
function withoutInternalPackageScope(source: string): string { function withoutInternalPackageScope(source: string): string {
return source.replaceAll("@dimensionlab/ui", "@internal/ui"); return source
.replaceAll("@dimensionlab/dashboard-model", "@internal/dashboard-model")
.replaceAll("@dimensionlab/ui", "@internal/ui");
} }

View file

@ -3,8 +3,8 @@ import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { afterEach, describe, expect, test } from "vitest"; import { afterEach, describe, expect, test } from "vitest";
import type { DashboardDocument } from "$lib/model"; import type { DashboardDocument } from "@dimensionlab/dashboard-model";
import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import { createDashboardStore, type DashboardStore } from "$lib/server/db/dashboard-store"; import { createDashboardStore, type DashboardStore } from "$lib/server/db/dashboard-store";
import { import {
AgentConfigAuthorizationError, AgentConfigAuthorizationError,

View file

@ -16,7 +16,7 @@ import {
type ServiceGroup, type ServiceGroup,
type StatusItem, type StatusItem,
type TelemetryCard, type TelemetryCard,
} from "$lib/model"; } from "@dimensionlab/dashboard-model";
import { import {
createDashboardStore, createDashboardStore,
DashboardRevisionNotFoundError, DashboardRevisionNotFoundError,

View file

@ -3,8 +3,8 @@ import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { afterEach, describe, expect, test } from "vitest"; import { afterEach, describe, expect, test } from "vitest";
import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab"; import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import { createDashboardStore, type DashboardStore } from "./db/dashboard-store"; import { createDashboardStore, type DashboardStore } from "./db/dashboard-store";
import { loadDashboardRuntime } from "./dashboard"; import { loadDashboardRuntime } from "./dashboard";

View file

@ -1,5 +1,5 @@
import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab"; import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
import type { DashboardDocument } from "$lib/model"; import type { DashboardDocument } from "@dimensionlab/dashboard-model";
import { import {
createDashboardStore, createDashboardStore,
DashboardPersistenceValidationError, DashboardPersistenceValidationError,

View file

@ -2,7 +2,7 @@ import { describe, expect, test, vi } from "vitest";
import { import {
DASHBOARD_SCHEMA_VERSION, DASHBOARD_SCHEMA_VERSION,
type DashboardDocument, type DashboardDocument,
} from "$lib/model"; } from "@dimensionlab/dashboard-model";
import { resolveDashboardDatasources } from "."; import { resolveDashboardDatasources } from ".";
describe("dashboard datasource resolution", () => { describe("dashboard datasource resolution", () => {

View file

@ -8,7 +8,7 @@ import type {
StatusItem, StatusItem,
StatusStrip, StatusStrip,
TelemetryCard, TelemetryCard,
} from "$lib/model"; } from "@dimensionlab/dashboard-model";
export interface DatasourceResolutionOptions { export interface DatasourceResolutionOptions {
fetch?: DatasourceFetch; fetch?: DatasourceFetch;

View file

@ -3,8 +3,8 @@ import { mkdtemp } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { afterEach, describe, expect, test, vi } from "vitest"; import { afterEach, describe, expect, test, vi } from "vitest";
import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import type { DashboardDocument } from "$lib/model"; import type { DashboardDocument } from "@dimensionlab/dashboard-model";
import { import {
DashboardPersistenceValidationError, DashboardPersistenceValidationError,
createDashboardStore, createDashboardStore,

View file

@ -3,7 +3,7 @@ import { desc, eq } from "drizzle-orm";
import { import {
type DashboardDocument, type DashboardDocument,
type DashboardValidationFailure, type DashboardValidationFailure,
} from "$lib/model"; } from "@dimensionlab/dashboard-model";
import { import {
type DashboardDatabaseConnection, type DashboardDatabaseConnection,
openDashboardDatabase, openDashboardDatabase,

View file

@ -1,6 +1,6 @@
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
import { DASHBOARD_SCHEMA_VERSION } from "$lib/model"; import { DASHBOARD_SCHEMA_VERSION } from "@dimensionlab/dashboard-model";
import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import { import {
UnsupportedDashboardModelVersionError, UnsupportedDashboardModelVersionError,
migrateDashboardDocumentForPersistence, migrateDashboardDocumentForPersistence,

View file

@ -3,7 +3,7 @@ import {
validateDashboardDocument, validateDashboardDocument,
type DashboardDocument, type DashboardDocument,
type DashboardValidationFailure, type DashboardValidationFailure,
} from "$lib/model"; } from "@dimensionlab/dashboard-model";
export interface DashboardModelMigrationSuccess { export interface DashboardModelMigrationSuccess {
valid: true; valid: true;

View file

@ -1,4 +1,4 @@
import type { DashboardDocument } from "$lib/model"; import type { DashboardDocument } from "@dimensionlab/dashboard-model";
import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { index, integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
export const dashboardDocuments = sqliteTable("dashboard_documents", { export const dashboardDocuments = sqliteTable("dashboard_documents", {

View file

@ -1,9 +1,9 @@
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { join } from "node:path"; import { join } from "node:path";
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
import { type DashboardDocument } from "$lib/model"; import { type DashboardDocument } from "@dimensionlab/dashboard-model";
import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab"; import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import { dashboardDocumentToUiDashboard } from "./model-renderer"; import { dashboardDocumentToUiDashboard } from "./model-renderer";
const appRoot = process.cwd().endsWith(`${join("apps", "web")}`) const appRoot = process.cwd().endsWith(`${join("apps", "web")}`)
@ -93,6 +93,7 @@ describe("dashboard model renderer", () => {
"utf8", "utf8",
) )
.toLowerCase() .toLowerCase()
.replaceAll("@dimensionlab/dashboard-model", "@internal/dashboard-model")
.replaceAll("@dimensionlab/ui", "@internal/ui"); .replaceAll("@dimensionlab/ui", "@internal/ui");
expect(source).not.toContain("dimension"); expect(source).not.toContain("dimension");

View file

@ -6,7 +6,7 @@ import type {
StatusItem, StatusItem,
StatusStrip, StatusStrip,
TelemetryCard, TelemetryCard,
} from "$lib/model"; } from "@dimensionlab/dashboard-model";
import type { import type {
UiDashboardPreview, UiDashboardPreview,
UiModuleBlock, UiModuleBlock,

View file

@ -88,4 +88,61 @@ describe("workspace boundaries", () => {
expect(uiPackage.exports).toHaveProperty("."); expect(uiPackage.exports).toHaveProperty(".");
expect(uiPackage.exports).toHaveProperty("./styles.css"); expect(uiPackage.exports).toHaveProperty("./styles.css");
}); });
test("keeps the dashboard model in a reusable internal package", () => {
const webPackage = JSON.parse(
readFileSync(join(root, "apps/web/package.json"), "utf8"),
) as { dependencies?: Record<string, string> };
const modelPackage = JSON.parse(
readFileSync(join(root, "packages/dashboard-model/package.json"), "utf8"),
) as { exports?: Record<string, unknown>; name?: string };
const tsconfig = JSON.parse(
readFileSync(join(root, "tsconfig.json"), "utf8"),
) as { references?: Array<{ path: string }> };
expect(webPackage.dependencies?.["@dimensionlab/dashboard-model"]).toBe(
"workspace:*",
);
expect(modelPackage.name).toBe("@dimensionlab/dashboard-model");
expect(modelPackage.exports).toHaveProperty(".");
expect(modelPackage.exports).toHaveProperty("./fixtures");
expect(tsconfig.references).toEqual(
expect.arrayContaining([{ path: "./packages/dashboard-model" }]),
);
expect(existsSync(join(root, "apps/web/src/lib/model/index.ts"))).toBe(
false,
);
});
test("stages web workspace dependency manifests before container install", () => {
const webPackage = JSON.parse(
readFileSync(join(root, "apps/web/package.json"), "utf8"),
) as { dependencies?: Record<string, string> };
const workspacePackages = [
"packages/ui/package.json",
"packages/dashboard-model/package.json",
].map((manifestPath) => {
const packageJson = JSON.parse(
readFileSync(join(root, manifestPath), "utf8"),
) as { name?: string };
return [packageJson.name, manifestPath] as const;
});
const manifestsByPackageName = new Map(workspacePackages);
const containerfile = readFileSync(
join(root, "apps/web/Containerfile"),
"utf8",
);
const workspaceDependencyManifests = Object.entries(
webPackage.dependencies ?? {},
)
.filter(([, version]) => version.startsWith("workspace:"))
.map(([packageName]) => manifestsByPackageName.get(packageName));
expect(workspaceDependencyManifests).not.toContain(undefined);
for (const manifestPath of workspaceDependencyManifests) {
expect(containerfile).toContain(`COPY ${manifestPath} ${manifestPath}`);
}
});
}); });

View file

@ -1,6 +1,6 @@
import { renderToString } from "react-dom/server"; import { renderToString } from "react-dom/server";
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
import { genericDashboardFixture } from "$lib/model/fixtures/generic"; import { genericDashboardFixture } from "@dimensionlab/dashboard-model/fixtures";
import { AppStateView, resolveDocumentMetadata } from "./App"; import { AppStateView, resolveDocumentMetadata } from "./App";
describe("home page model renderer", () => { describe("home page model renderer", () => {

View file

@ -1,5 +1,5 @@
import { describe, expect, test } from "vitest"; import { describe, expect, test } from "vitest";
import { dimensionLabDashboardFixture } from "$lib/model/fixtures/dimensionlab"; import { dimensionLabDashboardFixture } from "$lib/dashboard-seed/dimensionlab";
import { loadDashboardResponse } from "./dashboard"; import { loadDashboardResponse } from "./dashboard";
describe("dashboard API route", () => { describe("dashboard API route", () => {

View file

@ -3,6 +3,10 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": ".", "baseUrl": ".",
"paths": { "paths": {
"@dimensionlab/dashboard-model": ["../../packages/dashboard-model/src/index.ts"],
"@dimensionlab/dashboard-model/fixtures": [
"../../packages/dashboard-model/src/fixtures/index.ts"
],
"@dimensionlab/ui": ["../../packages/ui/src/index.ts"], "@dimensionlab/ui": ["../../packages/ui/src/index.ts"],
"@dimensionlab/ui/styles.css": ["../../packages/ui/src/styles.css"], "@dimensionlab/ui/styles.css": ["../../packages/ui/src/styles.css"],
"$lib/*": ["src/lib/*"] "$lib/*": ["src/lib/*"]

View file

@ -30,6 +30,12 @@ export default defineConfig({
"@dimensionlab/ui": fileURLToPath( "@dimensionlab/ui": fileURLToPath(
new URL("../../packages/ui/src/index.ts", import.meta.url), new URL("../../packages/ui/src/index.ts", import.meta.url),
), ),
"@dimensionlab/dashboard-model/fixtures": fileURLToPath(
new URL("../../packages/dashboard-model/src/fixtures/index.ts", import.meta.url),
),
"@dimensionlab/dashboard-model": fileURLToPath(
new URL("../../packages/dashboard-model/src/index.ts", import.meta.url),
),
$lib: fileURLToPath(new URL("./src/lib", import.meta.url)), $lib: fileURLToPath(new URL("./src/lib", import.meta.url)),
}, },
}, },

View file

@ -12,6 +12,7 @@
"name": "@dimensionlab/web", "name": "@dimensionlab/web",
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@dimensionlab/dashboard-model": "workspace:*",
"@dimensionlab/ui": "workspace:*", "@dimensionlab/ui": "workspace:*",
"@sinclair/typebox": "^0.34.49", "@sinclair/typebox": "^0.34.49",
"ajv": "^8.20.0", "ajv": "^8.20.0",
@ -40,6 +41,22 @@
"vitest": "^4.1.9", "vitest": "^4.1.9",
}, },
}, },
"packages/dashboard-model": {
"name": "@dimensionlab/dashboard-model",
"version": "0.0.1",
"dependencies": {
"@sinclair/typebox": "^0.34.49",
"ajv": "^8.20.0",
"ajv-formats": "^3.0.1",
},
"devDependencies": {
"@types/bun": "^1.3.14",
"@types/node": "^25.9.3",
"bun-types": "^1.3.14",
"typescript": "^6.0.3",
"vitest": "^4.1.9",
},
},
"packages/ui": { "packages/ui": {
"name": "@dimensionlab/ui", "name": "@dimensionlab/ui",
"version": "0.0.1", "version": "0.0.1",
@ -137,6 +154,8 @@
"@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="], "@babel/types": ["@babel/types@7.29.7", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA=="],
"@dimensionlab/dashboard-model": ["@dimensionlab/dashboard-model@workspace:packages/dashboard-model"],
"@dimensionlab/ui": ["@dimensionlab/ui@workspace:packages/ui"], "@dimensionlab/ui": ["@dimensionlab/ui@workspace:packages/ui"],
"@dimensionlab/web": ["@dimensionlab/web@workspace:apps/web"], "@dimensionlab/web": ["@dimensionlab/web@workspace:apps/web"],
@ -671,7 +690,7 @@
"@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="], "@vitejs/plugin-react": ["@vitejs/plugin-react@6.0.2", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.0" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", "babel-plugin-react-compiler": "^1.0.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler"] }, "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg=="],
"@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], "@vitest/expect": ["@vitest/expect@4.1.9", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA=="],
"@vitest/mocker": ["@vitest/mocker@4.1.9", "", { "dependencies": { "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw=="], "@vitest/mocker": ["@vitest/mocker@4.1.9", "", { "dependencies": { "@vitest/spy": "4.1.9", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-EVkXzBjrPGM+cK8/ANWgBrkUCfJfb38/EfTSO8h7pWvKkyPkpWxvR7BkD2MyItMF62C97zAEoqdpUixwR/e+Rw=="],
@ -681,7 +700,7 @@
"@vitest/snapshot": ["@vitest/snapshot@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA=="], "@vitest/snapshot": ["@vitest/snapshot@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "@vitest/utils": "4.1.9", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-Jc7RKGNBo8Z28WYIm0Niej4xdSPByRf6mU58VpHQkd6Zh05rlnA+twjbK5HyeIGHxrzsc3mJgS43uM0CZKzaIA=="],
"@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], "@vitest/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="],
"@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="], "@vitest/utils": ["@vitest/utils@4.1.9", "", { "dependencies": { "@vitest/pretty-format": "4.1.9", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-A51o8ymO5PpqlWNnBP9ZHPXDIpuMtTLlGSjN7la4US+LJzoUMyhwjA5QXlm39JexgwHKW4Xjs8Z2d3dLCXOeuA=="],
@ -745,7 +764,7 @@
"caniuse-lite": ["caniuse-lite@1.0.30001799", "", {}, "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw=="], "caniuse-lite": ["caniuse-lite@1.0.30001799", "", {}, "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw=="],
"chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], "chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
"chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="],
@ -873,7 +892,7 @@
"esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="], "esprima": ["esprima@4.0.1", "", { "bin": { "esparse": "./bin/esparse.js", "esvalidate": "./bin/esvalidate.js" } }, "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A=="],
"estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="],
@ -1479,6 +1498,8 @@
"@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="], "@rolldown/binding-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA=="],
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-l9Oo58x0HOP5znGzVhYW9U3e5wVuA4LAZU2AGezTmkhO1CgQRFDhDg4nneHsu/t3WniXg9QrG2nIXL/ZS8ln8Q=="],
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg=="],
@ -1495,14 +1516,6 @@
"@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="], "@testing-library/dom/dom-accessibility-api": ["dom-accessibility-api@0.5.16", "", {}, "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg=="],
"@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
"@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
"@vitest/mocker/@vitest/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="],
"@vitest/mocker/estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="],
"body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "body-parser/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
"cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "cliui/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
@ -1549,14 +1562,14 @@
"shadcn/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="], "shadcn/open": ["open@11.0.0", "", { "dependencies": { "default-browser": "^5.4.0", "define-lazy-prop": "^3.0.0", "is-in-ssh": "^1.0.0", "is-inside-container": "^1.0.0", "powershell-utils": "^0.1.0", "wsl-utils": "^0.3.0" } }, "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw=="],
"storybook/@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="],
"storybook/@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="],
"string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
"type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="],
"vitest/@vitest/expect": ["@vitest/expect@4.1.9", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.9", "@vitest/utils": "4.1.9", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-vl/rYsUKcBr3SnQn166+XR5ZQcgMx3DQhFWdfli/cWpLnLUmbxZvyrJZotLFUryib+LtArYMSTJ5RbQ57ZqrlA=="],
"vitest/@vitest/spy": ["@vitest/spy@4.1.9", "", {}, "sha512-fHpsS6mIi+PiEW+vcRVOMkX1oSaPKne3VOclSFICPcGOmfKgXPU5iAah+wcNcj2xPrCCmfq99IDGf+EojhhvhA=="],
"wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "wrap-ansi/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
"wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], "wrap-ansi/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="],
@ -1625,8 +1638,6 @@
"@oxc-resolver/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="], "@oxc-resolver/binding-wasm32-wasi/@emnapi/core/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA=="],
"@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
"cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "cliui/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
@ -1689,10 +1700,16 @@
"shadcn/open/wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="], "shadcn/open/wsl-utils": ["wsl-utils@0.3.1", "", { "dependencies": { "is-wsl": "^3.1.0", "powershell-utils": "^0.1.0" } }, "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg=="],
"storybook/@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="],
"storybook/@vitest/expect/chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="],
"storybook/@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="],
"string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "string-width/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="],
"wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="],
"storybook/@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="],
} }
} }

View file

@ -0,0 +1,34 @@
{
"name": "@dimensionlab/dashboard-model",
"version": "0.0.1",
"private": true,
"type": "module",
"exports": {
".": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
},
"./fixtures": {
"types": "./dist/fixtures/index.d.ts",
"default": "./dist/fixtures/index.js"
}
},
"scripts": {
"build": "rm -rf dist && tsc -p tsconfig.build.json",
"check": "tsc --noEmit",
"test": "vitest run",
"test:unit": "vitest run"
},
"dependencies": {
"@sinclair/typebox": "^0.34.49",
"ajv": "^8.20.0",
"ajv-formats": "^3.0.1"
},
"devDependencies": {
"@types/bun": "^1.3.14",
"@types/node": "^25.9.3",
"bun-types": "^1.3.14",
"typescript": "^6.0.3",
"vitest": "^4.1.9"
}
}

View file

@ -0,0 +1 @@
export { genericDashboardFixture } from "./generic";

View file

@ -5,7 +5,6 @@ import {
validateDashboardDocument, validateDashboardDocument,
} from "."; } from ".";
import { import {
dimensionLabDashboardFixture,
genericDashboardFixture, genericDashboardFixture,
} from "./fixtures"; } from "./fixtures";
@ -19,12 +18,6 @@ describe("dashboard model validation", () => {
} }
}); });
it("accepts the Dimension Lab dashboard fixture", () => {
const result = validateDashboardDocument(dimensionLabDashboardFixture);
expect(result.valid).toBe(true);
});
it("rejects documents with an unsupported schema version", () => { it("rejects documents with an unsupported schema version", () => {
const invalid = { const invalid = {
...genericDashboardFixture, ...genericDashboardFixture,

View file

@ -0,0 +1,12 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": false,
"noEmit": false,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src/**/*.ts"],
"exclude": ["dist", "node_modules", "src/**/*.test.ts"]
}

View file

@ -0,0 +1,9 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"baseUrl": ".",
"types": ["node", "bun-types"]
},
"include": ["src/**/*.ts"],
"exclude": ["dist", "node_modules"]
}

View file

@ -1,6 +1,7 @@
{ {
"files": [], "files": [],
"references": [ "references": [
{ "path": "./packages/dashboard-model" },
{ "path": "./apps/web" }, { "path": "./apps/web" },
{ "path": "./packages/ui" } { "path": "./packages/ui" }
] ]