Template
1
0

feat: biome check

This commit is contained in:
2025-09-25 14:29:15 +02:00
parent f2ba21a7e3
commit 0819534901
68 changed files with 211 additions and 802 deletions

View File

@@ -1,9 +0,0 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true

View File

@@ -1,14 +0,0 @@
{
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 120,
"singleQuote": false,
"overrides": [
{
"files": "*.ts",
"options": {
"parser": "typescript"
}
}
]
}

20
.vscode/settings.json vendored
View File

@@ -1,23 +1,11 @@
{ {
"biome.enabled": true,
"deno.enable": true, "deno.enable": true,
"deno.lint": false, "deno.lint": false,
"editor.defaultFormatter": "biomejs.biome",
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.organizeImports.biome": "explicit",
}, "source.fixAll.biome": "explicit"
"[typescript]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
"[typescriptreact]": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
},
"[markdown]": {
"editor.defaultFormatter": null,
"editor.wordWrap": "off"
},
"eslint.options": {
"ignorePatterns": ["**/*.md"]
}, },
"files.exclude": { "files.exclude": {
"**/.git": true, "**/.git": true,

View File

@@ -1,4 +0,0 @@
export const config = {
mongodb: "mongo:8.0.3",
postgres: "postgres:17",
};

View File

@@ -1,154 +0,0 @@
import { getAvailablePort } from "@std/net";
import cookie from "cookie";
import { auth, Session } from "~libraries/auth/mod.ts";
import { Code } from "~libraries/code/aggregates/code.ts";
import { handler } from "~libraries/server/handler.ts";
import { Api, QueryMethod } from "../.generated/api.ts";
export class ApiTestContainer {
#server?: Deno.HttpServer;
#client?: Api;
#cookie?: string;
#session?: Session;
/*
|--------------------------------------------------------------------------------
| Accessors
|--------------------------------------------------------------------------------
*/
get accountId(): string | undefined {
if (this.#session?.valid === true) {
return this.#session.accountId;
}
}
get client() {
if (this.#client === undefined) {
throw new Error("ApiContainer > .start() has not been executed.");
}
return this.#client;
}
/*
|--------------------------------------------------------------------------------
| Lifecycle
|--------------------------------------------------------------------------------
*/
async start(): Promise<this> {
const port = await getAvailablePort();
this.#server = await Deno.serve({ port, hostname: "127.0.0.1" }, handler);
this.#client = makeApiClient(port, {
onBeforeRequest: (headers: Headers) => {
if (this.#cookie !== undefined) {
headers.set("cookie", this.#cookie);
}
},
onAfterResponse: (response) => {
const cookie = response.headers.get("set-cookie");
if (cookie !== null) {
this.#cookie = cookie;
}
},
});
return this;
}
async stop() {
await this.#server?.shutdown();
}
/*
|--------------------------------------------------------------------------------
| Utilities
|--------------------------------------------------------------------------------
*/
async authorize(accountId: string): Promise<void> {
const code = await Code.create({ identity: { type: "admin", accountId } }).save();
await this.client.auth.code(accountId, code.id, code.value, {});
this.#session = await this.getSession();
}
async getSession(): Promise<Session | undefined> {
const token = cookie.parse(this.#cookie ?? "").token;
if (token !== undefined) {
const session = await auth.resolve(token);
if (session.valid === true) {
return session;
}
}
}
unauthorize(): void {
this.#cookie = undefined;
}
}
function makeApiClient(
port: number,
{
onBeforeRequest,
onAfterResponse,
}: {
onBeforeRequest: (headers: Headers) => void;
onAfterResponse: (response: Response) => void;
},
): Api {
return new Api({
async command(payload) {
const headers = new Headers();
onBeforeRequest(headers);
headers.set("content-type", "application/json");
const response = await fetch(`http://127.0.0.1:${port}/api/v1/command`, {
method: "POST",
headers,
body: JSON.stringify(payload),
});
const text = await response.text();
if (response.status >= 300) {
console.error(
`Command '${payload.method}' responded with error status '${response.status} ${response.statusText}'.`,
);
}
if (response.headers.get("content-type")?.includes("json") === true) {
return JSON.parse(text);
}
},
async query(method: QueryMethod, path: string, query: Record<string, unknown>, body: any = {}) {
const headers = new Headers();
onBeforeRequest(headers);
if (method !== "GET") {
headers.set("content-type", "application/json");
}
const response = await fetch(`http://127.0.0.1:${port}${path}${getSearchQuery(query)}`, {
method,
headers,
body: method === "GET" ? undefined : JSON.stringify(body),
});
onAfterResponse(response);
const text = await response.text();
if (response.status >= 300) {
console.error(`Query '${path}' responded with error status '${response.status} ${response.statusText}'.`);
throw new Error(response.statusText);
}
if (response.headers.get("content-type")?.includes("json") === true) {
return JSON.parse(text);
}
},
});
}
function getSearchQuery(query: Record<string, unknown>): string {
const search: string[] = [];
for (const key in query) {
search.push(`${key}=${query[key]}`);
}
if (search.length === 0) {
return "";
}
return `?${search.join("&")}`;
}

View File

@@ -1,41 +0,0 @@
import { MongoTestContainer } from "@valkyr/testcontainers/mongodb";
import { container } from "~database/container.ts";
import { logger } from "~libraries/logger/mod.ts";
import { bootstrap } from "~libraries/utilities/bootstrap.ts";
import { API_DOMAINS_DIR, API_PACKAGES_DIR } from "~paths";
export class DatabaseTestContainer {
constructor(readonly mongo: MongoTestContainer) {
container.set("client", mongo.client);
}
/*
|--------------------------------------------------------------------------------
| Lifecycle
|--------------------------------------------------------------------------------
*/
async start(): Promise<this> {
logger.prefix("Database").info("DatabaseTestContainer Started");
await bootstrap(API_DOMAINS_DIR);
await bootstrap(API_PACKAGES_DIR);
return this;
}
async truncate() {
const promises: Promise<any>[] = [];
for (const dbName of ["balto:auth", "balto:code", "balto:consultant", "balto:task"]) {
const db = this.mongo.client.db(dbName);
const collections = await db.listCollections().toArray();
promises.push(...collections.map(({ name }) => db.collection(name).deleteMany({})));
}
await Promise.all(promises);
}
async stop() {
logger.prefix("Database").info("DatabaseTestContainer stopped");
}
}

View File

@@ -1,178 +0,0 @@
import { MongoTestContainer } from "@valkyr/testcontainers/mongodb";
import { config } from "../config.ts";
import { ApiTestContainer } from "./api-container.ts";
import { DatabaseTestContainer } from "./database-container.ts";
export class TestContainer {
readonly id = crypto.randomUUID();
// ### Enablers
// A map of services to enable when the TestContainer is started. These toggles
// must be toggled before the container is started.
#with: With = {
mongodb: false,
database: false,
api: false,
};
// ### Needs
#needs: Needs = {
mongodb: [],
database: ["mongodb"],
api: ["mongodb", "database"],
};
// ### Services
// Any services that has been enabled will be running under the following
// assignments. Make sure to .stop any running services to avoid shutdown
// leaks.
#mongodb?: MongoTestContainer;
#database?: DatabaseTestContainer;
#api?: ApiTestContainer;
/*
|--------------------------------------------------------------------------------
| Accessors
|--------------------------------------------------------------------------------
*/
get accountId() {
if (this.#api === undefined) {
throw new Error("TestContainer > .withApi() must be called before starting the TestContainer.");
}
return this.#api.accountId;
}
get mongodb(): MongoTestContainer {
if (this.#mongodb === undefined) {
throw new Error("TestContainer > .withMongo() must be called before starting the TestContainer.");
}
return this.#mongodb;
}
get database(): DatabaseTestContainer {
if (this.#database === undefined) {
throw new Error("TestContainer > .withDatabase() must be called before starting the TestContainer.");
}
return this.#database;
}
get api() {
if (this.#api === undefined) {
throw new Error("TestContainer > .withApi() must be called before starting the TestContainer.");
}
return this.#api.client;
}
get authorize() {
if (this.#api === undefined) {
throw new Error("TestContainer > .withApi() must be called before starting the TestContainer.");
}
return this.#api.authorize.bind(this.#api);
}
get unauthorize() {
if (this.#api === undefined) {
throw new Error("TestContainer > .withApi() must be called before starting the TestContainer.");
}
return this.#api.unauthorize.bind(this.#api);
}
/*
|--------------------------------------------------------------------------------
| Builder
|--------------------------------------------------------------------------------
*/
withMongo(): this {
this.#with.mongodb = true;
return this;
}
withDatabase(): this {
this.#with.database = true;
return this;
}
withApi(): this {
this.#with.api = true;
return this;
}
/*
|--------------------------------------------------------------------------------
| Lifecycle
|--------------------------------------------------------------------------------
*/
async start(): Promise<this> {
const promises: Promise<void>[] = [];
if (this.#isNeeded("mongodb") === true) {
promises.push(
(async () => {
this.#mongodb = await MongoTestContainer.start(config.mongodb);
if (this.#isNeeded("database") === true) {
this.#database = await new DatabaseTestContainer(this.mongodb).start();
}
})(),
);
}
if (this.#isNeeded("api") === true) {
promises.push(
(async () => {
this.#api = await new ApiTestContainer().start();
})(),
);
}
await Promise.all(promises);
return this;
}
async stop(): Promise<this> {
await this.#api?.stop();
await this.#database?.stop();
await this.#mongodb?.stop();
this.#api = undefined;
this.#database = undefined;
this.#mongodb = undefined;
return this;
}
/*
|--------------------------------------------------------------------------------
| Helpers
|--------------------------------------------------------------------------------
*/
#isNeeded(target: keyof With): boolean {
if (this.#with[target] !== false) {
return true;
}
for (const key in this.#needs) {
if (this.#with[key as keyof With] !== false && this.#needs[key as keyof With].includes(target) === true) {
return true;
}
}
return false;
}
}
/*
|--------------------------------------------------------------------------------
| Types
|--------------------------------------------------------------------------------
*/
type Needs = Record<keyof With, (keyof With)[]>;
type With = {
mongodb: boolean;
database: boolean;
api: boolean;
};

View File

@@ -1,24 +0,0 @@
import * as assertSuite from "@std/assert";
import * as bddSuite from "@std/testing/bdd";
import type { TestContainer } from "~libraries/testing/containers/test-container.ts";
import { authorize } from "./utilities/account.ts";
export function describe(name: string, runner: TestRunner): (container: TestContainer) => void {
return (container: TestContainer) =>
bddSuite.describe(name, () => runner(container, bddSuite, assertSuite, { authorize: authorize(container) }));
}
export type TestRunner = (
container: TestContainer,
bdd: {
[key in keyof typeof bddSuite]: (typeof bddSuite)[key];
},
assert: {
[key in keyof typeof assertSuite]: (typeof assertSuite)[key];
},
utils: {
authorize: ReturnType<typeof authorize>;
},
) => void;

View File

@@ -1,68 +0,0 @@
import type { EventData } from "@valkyr/event-store";
import { AccountCreated, AccountEmailAdded } from "~libraries/auth/.generated/events.ts";
import { Account } from "~libraries/auth/aggregates/account.ts";
import { Role } from "~libraries/auth/aggregates/role.ts";
import type { TestContainer } from "~libraries/testing/containers/test-container.ts";
type AuthorizationOptions = {
name?: { family?: string; given?: string };
email?: Partial<EventData<AccountEmailAdded>>;
};
/**
* Return a function which provides the ability to create a new account which
* is authorized and ready to use for testing authorized requests.
*
* @param container - Container to authorize against.
*/
export function authorize(container: TestContainer): AuthorizeFn {
return async (data: EventData<AccountCreated>, { name = {}, email = {} }: AuthorizationOptions = {}) => {
const role = await makeRole(data.type).save();
const account = await Account.create(data, "test")
.addName(name?.family ?? "Doe", name?.given ?? "John", "test")
.addEmail({ value: "john.doe@fixture.none", type: "work", primary: true, verified: true, ...email }, "test")
.addRole(role.id, "test")
.save();
await container.authorize(account.id);
return account;
};
}
function makeRole(type: "admin" | "consultant" | "organization"): Role {
switch (type) {
case "admin": {
return Role.create(
{
name: "Admin",
permissions: [
{ resource: "admin", actions: ["create", "update", "delete"] },
{ resource: "consultant", actions: ["create", "update", "delete"] },
{ resource: "organization", actions: ["create", "update", "delete"] },
],
},
"test",
);
}
case "consultant": {
return Role.create(
{
name: "Consultant",
permissions: [{ resource: "consultant", actions: ["create", "update", "delete"] }],
},
"test",
);
}
case "organization": {
return Role.create(
{
name: "Organization",
permissions: [{ resource: "organization", actions: ["create", "update", "delete"] }],
},
"test",
);
}
}
}
type AuthorizeFn = (data: EventData<AccountCreated>, optional?: AuthorizationOptions) => Promise<Account>;

View File

@@ -1,62 +0,0 @@
/**
* Removes excess indentation caused by using multiline template strings.
*
* Ported from `dedent-js` solution.
*
* @see https://github.com/MartinKolarik/dedent-js
*
* @param templateStrings - Template strings to dedent.
*
* @example
* {
* nested: {
* examples: [
* dedent(`
* I am 8 spaces off from the beginning of this file.
* But I will be 2 spaces based on the trimmed distance
* of the first line.
* `),
* ]
* }
* }
*/
export function dedent(templateStrings: TemplateStringsArray | string, ...values: any[]) {
const matches = [];
const strings = typeof templateStrings === "string" ? [templateStrings] : templateStrings.slice();
// Remove trailing whitespace.
strings[strings.length - 1] = strings[strings.length - 1].replace(/\r?\n([\t ]*)$/, "");
// Find all line breaks to determine the highest common indentation level.
for (let i = 0; i < strings.length; i++) {
const match = strings[i].match(/\n[\t ]+/g);
if (match) {
matches.push(...match);
}
}
// Remove the common indentation from all strings.
if (matches.length) {
const size = Math.min(...matches.map((value) => value.length - 1));
const pattern = new RegExp(`\n[\t ]{${size}}`, "g");
for (let i = 0; i < strings.length; i++) {
strings[i] = strings[i].replace(pattern, "\n");
}
}
// Remove leading whitespace.
strings[0] = strings[0].replace(/^\r?\n/, "");
// Perform interpolation.
let string = strings[0];
for (let i = 0; i < values.length; i++) {
string += values[i] + strings[i + 1];
}
return string;
}

View File

@@ -1,41 +0,0 @@
/**
* Traverse path and look for a `generate.ts` file in each folder found under
* the given path. If a `generate.ts` file is found it is imported so its content
* is executed.
*
* @param path - Path to resolve `generate.ts` files.
* @param filter - Which folders found under the given path to ignore.
*/
export async function generate(path: string, filter: string[] = []): Promise<void> {
const generate: string[] = [];
for await (const entry of Deno.readDir(path)) {
if (entry.isDirectory === true) {
const moduleName = path.split("/").pop();
if (moduleName === undefined) {
continue;
}
if (filter.length > 0 && filter.includes(moduleName) === false) {
continue;
}
const filePath = `${path}/${entry.name}/.tasks/generate.ts`;
if (await hasFile(filePath)) {
generate.push(filePath);
}
}
}
for (const filePath of generate) {
await import(filePath);
}
}
async function hasFile(filePath: string) {
try {
await Deno.lstat(filePath);
} catch (err) {
if (!(err instanceof Deno.errors.NotFound)) {
throw err;
}
return false;
}
return true;
}

View File

@@ -1,22 +0,0 @@
import { procedure } from "@platform/relay";
import z from "zod";
const EventSchema = z.object({
id: z.uuid(),
stream: z.uuid(),
type: z.string(),
data: z.any(),
meta: z.any(),
recorded: z.string(),
created: z.string(),
});
export default procedure
.method("event")
.access("public")
.params(EventSchema)
.response(z.uuid())
.handle(async (event) => {
console.log(event);
return crypto.randomUUID();
});

View File

@@ -8,7 +8,7 @@ import socket from "@platform/socket/server.ts";
import { storage } from "@platform/storage"; import { storage } from "@platform/storage";
import { config } from "./config.ts"; import { config } from "./config.ts";
import session from "./services/session.ts"; import session from "./session.ts";
const log = logger.prefix("Server"); const log = logger.prefix("Server");

View File

@@ -64,7 +64,7 @@ export class Controller<TState extends Unknown = Empty, TProps extends Unknown =
component: ReactComponent<TProps, TController>, component: ReactComponent<TProps, TController>,
setView: any, setView: any,
): InstanceType<TController> { ): InstanceType<TController> {
return new this(component, setView); return new Controller(component, setView);
} }
/* /*

View File

@@ -1,6 +1,7 @@
import React, { type FunctionComponent } from "react"; import type React from "react";
import type { FunctionComponent } from "react";
import { ControllerRefs } from "./refs.ts"; import type { ControllerRefs } from "./refs.ts";
export type ReactComponent<TProps extends Unknown, TController extends ControllerClass> = FunctionComponent<{ export type ReactComponent<TProps extends Unknown, TController extends ControllerClass> = FunctionComponent<{
props: TProps; props: TProps;

View File

@@ -82,7 +82,7 @@ function useController(controller: ControllerClass, component: any, props: any,
return () => { return () => {
instance.$destroy(); instance.$destroy();
}; };
}, []); }, [component, controller, setView]);
return error; return error;
} }

View File

@@ -5,7 +5,7 @@ import { TodosController } from "./todos.controller.ts";
export const TodosView = makeControllerView( export const TodosView = makeControllerView(
TodosController, TodosController,
({ state: { form, todos }, actions: { remove, stress } }) => { ({ state: { form, todos }, actions: { remove } }) => {
return ( return (
<div className="min-h-screen bg-gray-100 flex flex-col items-center py-10 px-4 font-sans"> <div className="min-h-screen bg-gray-100 flex flex-col items-center py-10 px-4 font-sans">
<div className="w-full max-w-2xl space-y-8"> <div className="w-full max-w-2xl space-y-8">

View File

@@ -2,8 +2,8 @@ module.exports = {
theme: { theme: {
extend: { extend: {
fontFamily: { fontFamily: {
sans: ['Inter', 'sans-serif'], sans: ["Inter", "sans-serif"],
} },
} },
} },
} };

View File

@@ -1,7 +1,4 @@
{ {
"files": [], "files": [],
"references": [ "references": [{ "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" }]
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
} }

45
biome.json Normal file
View File

@@ -0,0 +1,45 @@
{
"formatter": {
"enabled": true,
"formatWithErrors": false,
"indentStyle": "space",
"indentWidth": 2,
"lineEnding": "lf",
"lineWidth": 120,
"attributePosition": "auto"
},
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"suspicious": {
"noConfusingVoidType": "off",
"noExplicitAny": "off"
},
"complexity": {
"noBannedTypes": "off"
}
}
},
"assist": {
"enabled": true,
"actions": {
"source": {
"organizeImports": {
"level": "on",
"options": {
"groups": [
[":BUN:", ":NODE:"],
":BLANK_LINE:",
":PACKAGE:",
":BLANK_LINE:",
[":ALIAS:"],
":BLANK_LINE:",
":PATH:"
]
}
}
}
}
}
}

View File

@@ -43,16 +43,8 @@
"description": "Start react application instance." "description": "Start react application instance."
}, },
"check": { "check": {
"command": "deno check ./api/server.ts ./platform", "command": "deno run -A npm:@biomejs/biome check --write ./api ./modules ./platform",
"description": "Runs a check on all the projects main entry files." "description": "Format, lint, and organize imports of the entire project."
},
"lint": {
"command": "npx eslint -c eslint.config.mjs .",
"description": "Runs eslint across the entire project."
},
"fmt": {
"command": "npx prettier --write .",
"description": "Runs prettier formatting across the entire project."
}, },
"test": { "test": {
"command": "deno test --allow-all", "command": "deno test --allow-all",

75
deno.lock generated
View File

@@ -1,6 +1,8 @@
{ {
"version": "5", "version": "5",
"specifiers": { "specifiers": {
"npm:@biomejs/biome@*": "2.2.4",
"npm:@biomejs/biome@2.2.4": "2.2.4",
"npm:@cerbos/http@0.23.1": "0.23.1", "npm:@cerbos/http@0.23.1": "0.23.1",
"npm:@eslint/js@9.35.0": "9.35.0", "npm:@eslint/js@9.35.0": "9.35.0",
"npm:@jsr/std__assert@1.0.14": "1.0.14", "npm:@jsr/std__assert@1.0.14": "1.0.14",
@@ -23,7 +25,6 @@
"npm:cookie@1.0.2": "1.0.2", "npm:cookie@1.0.2": "1.0.2",
"npm:eslint-plugin-react-hooks@5.2.0": "5.2.0_eslint@9.35.0", "npm:eslint-plugin-react-hooks@5.2.0": "5.2.0_eslint@9.35.0",
"npm:eslint-plugin-react-refresh@0.4.20": "0.4.20_eslint@9.35.0", "npm:eslint-plugin-react-refresh@0.4.20": "0.4.20_eslint@9.35.0",
"npm:eslint-plugin-simple-import-sort@12.1.1": "12.1.1_eslint@9.35.0",
"npm:eslint@9.35.0": "9.35.0", "npm:eslint@9.35.0": "9.35.0",
"npm:fast-equals@5.2.2": "5.2.2", "npm:fast-equals@5.2.2": "5.2.2",
"npm:globals@16.4.0": "16.4.0", "npm:globals@16.4.0": "16.4.0",
@@ -31,7 +32,6 @@
"npm:mongodb@6.20.0": "6.20.0", "npm:mongodb@6.20.0": "6.20.0",
"npm:nanoid@5.1.5": "5.1.5", "npm:nanoid@5.1.5": "5.1.5",
"npm:path-to-regexp@8": "8.3.0", "npm:path-to-regexp@8": "8.3.0",
"npm:prettier@3.6.2": "3.6.2",
"npm:react-dom@19.1.1": "19.1.1_react@19.1.1", "npm:react-dom@19.1.1": "19.1.1_react@19.1.1",
"npm:react@19.1.1": "19.1.1", "npm:react@19.1.1": "19.1.1",
"npm:tailwindcss@4.1.13": "4.1.13", "npm:tailwindcss@4.1.13": "4.1.13",
@@ -184,6 +184,60 @@
"@better-fetch/fetch@1.1.18": { "@better-fetch/fetch@1.1.18": {
"integrity": "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA==" "integrity": "sha512-rEFOE1MYIsBmoMJtQbl32PGHHXuG2hDxvEd7rUHE0vCBoFQVSDqaVs9hkZEtHCxRoY+CljXKFCOuJ8uxqw1LcA=="
}, },
"@biomejs/biome@2.2.4": {
"integrity": "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==",
"optionalDependencies": [
"@biomejs/cli-darwin-arm64",
"@biomejs/cli-darwin-x64",
"@biomejs/cli-linux-arm64",
"@biomejs/cli-linux-arm64-musl",
"@biomejs/cli-linux-x64",
"@biomejs/cli-linux-x64-musl",
"@biomejs/cli-win32-arm64",
"@biomejs/cli-win32-x64"
],
"bin": true
},
"@biomejs/cli-darwin-arm64@2.2.4": {
"integrity": "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==",
"os": ["darwin"],
"cpu": ["arm64"]
},
"@biomejs/cli-darwin-x64@2.2.4": {
"integrity": "sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==",
"os": ["darwin"],
"cpu": ["x64"]
},
"@biomejs/cli-linux-arm64-musl@2.2.4": {
"integrity": "sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@biomejs/cli-linux-arm64@2.2.4": {
"integrity": "sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==",
"os": ["linux"],
"cpu": ["arm64"]
},
"@biomejs/cli-linux-x64-musl@2.2.4": {
"integrity": "sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==",
"os": ["linux"],
"cpu": ["x64"]
},
"@biomejs/cli-linux-x64@2.2.4": {
"integrity": "sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==",
"os": ["linux"],
"cpu": ["x64"]
},
"@biomejs/cli-win32-arm64@2.2.4": {
"integrity": "sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==",
"os": ["win32"],
"cpu": ["arm64"]
},
"@biomejs/cli-win32-x64@2.2.4": {
"integrity": "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==",
"os": ["win32"],
"cpu": ["x64"]
},
"@cerbos/core@0.24.1": { "@cerbos/core@0.24.1": {
"integrity": "sha512-Gt9ETQR3WDVcPlxN+HiGUDtNgWFulwS5ZjBgzJFsdb7e2GCw0tOPE9Ex1qHNZvG/0JHpFWJWIiYaSKyXcp35YQ==", "integrity": "sha512-Gt9ETQR3WDVcPlxN+HiGUDtNgWFulwS5ZjBgzJFsdb7e2GCw0tOPE9Ex1qHNZvG/0JHpFWJWIiYaSKyXcp35YQ==",
"dependencies": [ "dependencies": [
@@ -1379,12 +1433,6 @@
"eslint" "eslint"
] ]
}, },
"eslint-plugin-simple-import-sort@12.1.1_eslint@9.35.0": {
"integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==",
"dependencies": [
"eslint"
]
},
"eslint-scope@8.4.0": { "eslint-scope@8.4.0": {
"integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dependencies": [ "dependencies": [
@@ -1927,10 +1975,6 @@
"prelude-ls@1.2.1": { "prelude-ls@1.2.1": {
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
}, },
"prettier@3.6.2": {
"integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"bin": true
},
"punycode@2.3.1": { "punycode@2.3.1": {
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
}, },
@@ -2284,12 +2328,9 @@
"workspace": { "workspace": {
"packageJson": { "packageJson": {
"dependencies": [ "dependencies": [
"npm:@biomejs/biome@2.2.4",
"npm:@jsr/std__assert@1.0.14", "npm:@jsr/std__assert@1.0.14",
"npm:@jsr/std__testing@1.0.15", "npm:@jsr/std__testing@1.0.15"
"npm:eslint-plugin-simple-import-sort@12.1.1",
"npm:eslint@9.35.0",
"npm:prettier@3.6.2",
"npm:typescript-eslint@8.44.0"
] ]
}, },
"members": { "members": {

View File

@@ -1,36 +0,0 @@
import simpleImportSort from "eslint-plugin-simple-import-sort";
import tseslint from "typescript-eslint";
export default [
...tseslint.configs.recommended,
{
plugins: {
"simple-import-sort": simpleImportSort,
},
rules: {
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
},
},
{
files: ["**/*.ts"],
rules: {
"@typescript-eslint/ban-ts-comment": [
"error",
{
"ts-expect-error": "allow-with-description",
minimumDescriptionLength: 10,
},
],
"@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
},
},
];

View File

@@ -1,5 +1,5 @@
import { getEnvironmentVariable } from "@platform/config/environment.ts"; import { getEnvironmentVariable } from "@platform/config/environment.ts";
import { SerializeOptions } from "cookie"; import type { SerializeOptions } from "cookie";
import z from "zod"; import z from "zod";
export const config = { export const config = {

View File

@@ -1,6 +1,6 @@
import route from "./spec.ts"; import route from "./spec.ts";
export default route.access("session").handle(async ({ params: { id } }, { session, principal, access }) => { export default route.access("session").handle(async () => {
// const user = await getUserById(id); // const user = await getUserById(id);
// if (user === undefined) { // if (user === undefined) {
// return new NotFoundError("Identity does not exist, or has been removed."); // return new NotFoundError("Identity does not exist, or has been removed.");

View File

@@ -1,6 +1,6 @@
import route from "./spec.ts"; import route from "./spec.ts";
export default route.access("public").handle(async ({ body: { email } }) => { export default route.access("public").handle(async () => {
// const code = await Passwordless.createCode({ tenantId: "public", email }); // const code = await Passwordless.createCode({ tenantId: "public", email });
// if (code.status !== "OK") { // if (code.status !== "OK") {
// return logger.info({ // return logger.info({

View File

@@ -1,5 +1,4 @@
import { ForbiddenError } from "@platform/relay"; import { ForbiddenError, NotFoundError } from "@platform/relay";
import { NotFoundError } from "@platform/relay";
import { getPrincipalById, setPrincipalRolesById } from "../../services/database.ts"; import { getPrincipalById, setPrincipalRolesById } from "../../services/database.ts";
import route from "./spec.ts"; import route from "./spec.ts";

View File

@@ -1,6 +1,6 @@
import { cerbos } from "@platform/cerbos"; import { cerbos } from "@platform/cerbos";
import { Principal } from "../models/principal.ts"; import type { Principal } from "../models/principal.ts";
export function getAccessControlMethods(principal: Principal) { export function getAccessControlMethods(principal: Principal) {
return { return {

View File

@@ -1,11 +1,11 @@
import { getDatabaseAccessor } from "@platform/database/accessor.ts"; import { getDatabaseAccessor } from "@platform/database/accessor.ts";
import { import {
parsePrincipal,
type Principal,
PRINCIPAL_TYPE_NAMES, PRINCIPAL_TYPE_NAMES,
type Principal,
PrincipalSchema, PrincipalSchema,
PrincipalTypeId, PrincipalTypeId,
parsePrincipal,
} from "../models/principal.ts"; } from "../models/principal.ts";
export const db = getDatabaseAccessor<{ export const db = getDatabaseAccessor<{

View File

@@ -1,8 +1,8 @@
import { AuditActor, auditors } from "@platform/spec/audit/actor.ts"; import { type AuditActor, auditors } from "@platform/spec/audit/actor.ts";
import { AggregateRoot, getDate } from "@valkyr/event-store"; import { AggregateRoot, getDate } from "@valkyr/event-store";
import { db } from "../database.ts"; import { db } from "../database.ts";
import { EventRecord, EventStoreFactory, projector } from "../event-store.ts"; import { type EventRecord, type EventStoreFactory, projector } from "../event-store.ts";
export class WorkspaceUser extends AggregateRoot<EventStoreFactory> { export class WorkspaceUser extends AggregateRoot<EventStoreFactory> {
static override readonly name = "workspace:user"; static override readonly name = "workspace:user";

View File

@@ -1,8 +1,8 @@
import { AuditActor, auditors } from "@platform/spec/audit/actor.ts"; import { type AuditActor, auditors } from "@platform/spec/audit/actor.ts";
import { AggregateRoot, getDate } from "@valkyr/event-store"; import { AggregateRoot, getDate } from "@valkyr/event-store";
import { db } from "../database.ts"; import { db } from "../database.ts";
import { EventRecord, EventStoreFactory, projector } from "../event-store.ts"; import { type EventRecord, type EventStoreFactory, projector } from "../event-store.ts";
export class Workspace extends AggregateRoot<EventStoreFactory> { export class Workspace extends AggregateRoot<EventStoreFactory> {
static override readonly name = "workspace"; static override readonly name = "workspace";

View File

@@ -1,5 +1,3 @@
import z from "zod";
/* /*
export const resources = new ResourceRegistry([ export const resources = new ResourceRegistry([
{ {

View File

@@ -1,7 +1,7 @@
import { getDatabaseAccessor } from "@platform/database/accessor.ts"; import { getDatabaseAccessor } from "@platform/database/accessor.ts";
import { parseWorkspace, type Workspace } from "./models/workspace.ts"; import { parseWorkspace, type Workspace } from "./models/workspace.ts";
import { WorkspaceUser } from "./models/workspace-user.ts"; import type { WorkspaceUser } from "./models/workspace-user.ts";
export const db = getDatabaseAccessor<{ export const db = getDatabaseAccessor<{
workspaces: Workspace; workspaces: Workspace;

View File

@@ -1,5 +1,5 @@
import { mongo } from "@platform/database/client.ts"; import { mongo } from "@platform/database/client.ts";
import { EventFactory, EventStore, Prettify, Projector } from "@valkyr/event-store"; import { EventFactory, EventStore, type Prettify, Projector } from "@valkyr/event-store";
import { MongoAdapter } from "@valkyr/event-store/mongo"; import { MongoAdapter } from "@valkyr/event-store/mongo";
/* /*

View File

@@ -2,9 +2,6 @@
"devDependencies": { "devDependencies": {
"@std/assert": "npm:@jsr/std__assert@1.0.14", "@std/assert": "npm:@jsr/std__assert@1.0.14",
"@std/testing": "npm:@jsr/std__testing@1.0.15", "@std/testing": "npm:@jsr/std__testing@1.0.15",
"eslint": "9.35.0", "@biomejs/biome": "2.2.4"
"eslint-plugin-simple-import-sort": "12.1.1",
"prettier": "3.6.2",
"typescript-eslint": "8.44.0"
} }
} }

View File

@@ -1,5 +1,5 @@
import { load } from "@std/dotenv"; import { load } from "@std/dotenv";
import { z, type ZodType } from "zod"; import type { ZodType, z } from "zod";
import { InvalidEnvironmentKeyError } from "./errors.ts"; import { InvalidEnvironmentKeyError } from "./errors.ts";
import { getServiceEnvironment, type ServiceEnvironment } from "./service.ts"; import { getServiceEnvironment, type ServiceEnvironment } from "./service.ts";

View File

@@ -1,4 +1,4 @@
import { Collection, type CollectionOptions, type Db, type Document, type MongoClient } from "mongodb"; import type { Collection, CollectionOptions, Db, Document, MongoClient } from "mongodb";
import { mongo } from "./client.ts"; import { mongo } from "./client.ts";

View File

@@ -1,5 +1,5 @@
import type { Db } from "mongodb"; import type { Db } from "mongodb";
import z, { type ZodObject, type ZodType } from "zod"; import type { ZodObject, ZodType, z } from "zod";
/** /**
* TODO ... * TODO ...
@@ -29,7 +29,7 @@ export function makeDocumentParser<TSchema extends ZodObject>(schema: TSchema):
export function toParsedDocuments<TSchema extends ZodType>( export function toParsedDocuments<TSchema extends ZodType>(
schema: TSchema, schema: TSchema,
): (documents: unknown[]) => Promise<z.infer<TSchema>[]> { ): (documents: unknown[]) => Promise<z.infer<TSchema>[]> {
return async function (documents: unknown[]) { return async (documents: unknown[]) => {
const parsed = []; const parsed = [];
for (const document of documents) { for (const document of documents) {
parsed.push(await schema.parseAsync(document)); parsed.push(await schema.parseAsync(document));
@@ -44,7 +44,7 @@ export function toParsedDocuments<TSchema extends ZodType>(
export function toParsedDocument<TSchema extends ZodType>( export function toParsedDocument<TSchema extends ZodType>(
schema: TSchema, schema: TSchema,
): (document?: unknown) => Promise<z.infer<TSchema> | undefined> { ): (document?: unknown) => Promise<z.infer<TSchema> | undefined> {
return async function (document: unknown) { return async (document: unknown) => {
if (document === undefined || document === null) { if (document === undefined || document === null) {
return undefined; return undefined;
} }

View File

@@ -1,4 +1,4 @@
import { HexValue } from "./color/hex.ts"; import type { HexValue } from "./color/hex.ts";
import { type BGColor, type Color, hexToBgColor, hexToColor, type Modifier, styles } from "./color/styles.ts"; import { type BGColor, type Color, hexToBgColor, hexToColor, type Modifier, styles } from "./color/styles.ts";
export const chalk = { export const chalk = {
@@ -13,21 +13,15 @@ export const chalk = {
} as Chalk; } as Chalk;
for (const key in styles.modifier) { for (const key in styles.modifier) {
chalk[key as Modifier] = function (value: string) { chalk[key as Modifier] = (value: string) => toModifiedValue(key as Modifier, value);
return toModifiedValue(key as Modifier, value);
};
} }
for (const key in styles.color) { for (const key in styles.color) {
chalk[key as Color] = function (value: string) { chalk[key as Color] = (value: string) => toColorValue(key as Color, value);
return toColorValue(key as Color, value);
};
} }
for (const key in styles.bgColor) { for (const key in styles.bgColor) {
chalk[key as BGColor] = function (value: string) { chalk[key as BGColor] = (value: string) => toBGColorValue(key as BGColor, value);
return toBGColorValue(key as BGColor, value);
};
} }
function toModifiedValue(key: Modifier, value: string): string { function toModifiedValue(key: Modifier, value: string): string {

View File

@@ -1,4 +1,4 @@
import { hexToAnsi256, HexValue } from "./hex.ts"; import { type HexValue, hexToAnsi256 } from "./hex.ts";
import { toEscapeSequence } from "./utilities.ts"; import { toEscapeSequence } from "./utilities.ts";
export const styles = { export const styles = {

View File

@@ -2,12 +2,12 @@ import { encrypt } from "@platform/vault";
import { import {
assertServerErrorResponse, assertServerErrorResponse,
RelayAdapter, type RelayAdapter,
RelayInput, type RelayInput,
RelayResponse, type RelayResponse,
ServerErrorResponse, type ServerErrorResponse,
} from "../libraries/adapter.ts"; } from "../libraries/adapter.ts";
import { ServerError, ServerErrorType } from "../libraries/errors.ts"; import { ServerError, type ServerErrorType } from "../libraries/errors.ts";
/** /**
* HttpAdapter provides a unified transport layer for Relay. * HttpAdapter provides a unified transport layer for Relay.

View File

@@ -1,4 +1,4 @@
// eslint-disable-next-line @typescript-eslint/no-empty-object-type // eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface ServerContext {} export type ServerContext = {};
export const context: ServerContext = {} as any; export const context: ServerContext = {} as any;

View File

@@ -1,4 +1,4 @@
import { ZodError } from "zod"; import type { ZodError } from "zod";
export abstract class ServerError<TData = unknown> extends Error { export abstract class ServerError<TData = unknown> extends Error {
abstract readonly code: string; abstract readonly code: string;

View File

@@ -1,8 +1,9 @@
import z, { ZodType } from "zod"; import type z from "zod";
import type { ZodType } from "zod";
import { ServerContext } from "./context.ts"; import type { ServerContext } from "./context.ts";
import { ServerError, ServerErrorClass } from "./errors.ts"; import type { ServerError, ServerErrorClass } from "./errors.ts";
import { RouteAccess } from "./route.ts"; import type { RouteAccess } from "./route.ts";
export class Procedure<const TState extends State = State> { export class Procedure<const TState extends State = State> {
readonly type = "procedure" as const; readonly type = "procedure" as const;
@@ -234,7 +235,8 @@ type HandleFn<TArgs extends Array<any> = any[], TResponse = any> = (
? Promise<z.infer<TResponse> | Response | ServerError> ? Promise<z.infer<TResponse> | Response | ServerError>
: Promise<Response | ServerError | void>; : Promise<Response | ServerError | void>;
type ServerArgs<TState extends State> = type ServerArgs<TState extends State> = HasInputArgs<TState> extends true
HasInputArgs<TState> extends true ? [z.output<TState["params"]>, ServerContext] : [ServerContext]; ? [z.output<TState["params"]>, ServerContext]
: [ServerContext];
type HasInputArgs<TState extends State> = TState["params"] extends ZodType ? true : false; type HasInputArgs<TState extends State> = TState["params"] extends ZodType ? true : false;

View File

@@ -1,9 +1,9 @@
import { match, type MatchFunction } from "path-to-regexp"; import { type MatchFunction, match } from "path-to-regexp";
import z, { ZodObject, ZodRawShape, ZodType } from "zod"; import z, { type ZodObject, type ZodRawShape, type ZodType } from "zod";
import { ServerContext } from "./context.ts"; import type { ServerContext } from "./context.ts";
import { ServerError, ServerErrorClass } from "./errors.ts"; import { ServerError, type ServerErrorClass } from "./errors.ts";
import { Hooks } from "./hooks.ts"; import type { Hooks } from "./hooks.ts";
export class Route<const TState extends RouteState = RouteState> { export class Route<const TState extends RouteState = RouteState> {
readonly type = "route" as const; readonly type = "route" as const;
@@ -480,8 +480,7 @@ type HandleFn<TArgs extends Array<any> = any[], TResponse = any> = (
? Promise<z.infer<TResponse> | Response | ServerError> ? Promise<z.infer<TResponse> | Response | ServerError>
: Promise<Response | ServerError | void>; : Promise<Response | ServerError | void>;
type ServerArgs<TState extends RouteState> = type ServerArgs<TState extends RouteState> = HasInputArgs<TState> extends true
HasInputArgs<TState> extends true
? [ ? [
(TState["params"] extends ZodObject ? { params: z.output<TState["params"]> } : unknown) & (TState["params"] extends ZodObject ? { params: z.output<TState["params"]> } : unknown) &
(TState["query"] extends ZodObject ? { query: z.output<TState["query"]> } : unknown) & (TState["query"] extends ZodObject ? { query: z.output<TState["query"]> } : unknown) &

View File

@@ -6,8 +6,8 @@ import {
InternalServerError, InternalServerError,
NotFoundError, NotFoundError,
NotImplementedError, NotImplementedError,
Route, type Route,
RouteMethod, type RouteMethod,
ServerError, ServerError,
type ServerErrorResponse, type ServerErrorResponse,
UnauthorizedError, UnauthorizedError,

View File

@@ -1,9 +1,7 @@
import "./types.ts"; import "./types.ts";
import { context } from "@platform/relay"; import { context, InternalServerError } from "@platform/relay";
import { InternalServerError } from "@platform/relay"; import { getStorageContext, storage } from "@platform/storage";
import { storage } from "@platform/storage";
import { getStorageContext } from "@platform/storage";
export default { export default {
/** /**

View File

@@ -8,7 +8,7 @@ import type { Api } from "./api.ts";
/** /**
* TODO ... * TODO ...
*/ */
export function upgradeWebSocket(request: Request, api: Api) { export function upgradeWebSocket(request: Request, _api: Api) {
const { socket, response } = Deno.upgradeWebSocket(request); const { socket, response } = Deno.upgradeWebSocket(request);
socket.addEventListener("open", () => { socket.addEventListener("open", () => {

View File

@@ -1,7 +1,6 @@
import "./types.d.ts"; import "./types.d.ts";
import { InternalServerError } from "@platform/relay"; import { context, InternalServerError } from "@platform/relay";
import { context } from "@platform/relay";
import { getStorageContext, storage } from "@platform/storage"; import { getStorageContext, storage } from "@platform/storage";
import { SocketRegistry } from "./sockets.ts"; import { SocketRegistry } from "./sockets.ts";

View File

@@ -41,7 +41,9 @@ export class SocketRegistry {
* @param data - Data to send to each connected socket. * @param data - Data to send to each connected socket.
*/ */
send(data: string | ArrayBufferLike | Blob | ArrayBufferView): this { send(data: string | ArrayBufferLike | Blob | ArrayBufferView): this {
this.#sockets.forEach((socket) => socket.send(data)); this.#sockets.forEach((socket) => {
socket.send(data);
});
return this; return this;
} }
} }

View File

@@ -18,4 +18,4 @@ export function getStorageContext(): StorageContext {
} }
// eslint-disable-next-line @typescript-eslint/no-empty-object-type // eslint-disable-next-line @typescript-eslint/no-empty-object-type
export interface StorageContext {} export type StorageContext = {};

View File

@@ -46,6 +46,9 @@ function bufferToHex(buffer: ArrayBuffer): string {
} }
function hexToBuffer(hex: string): ArrayBuffer { function hexToBuffer(hex: string): ArrayBuffer {
const bytes = new Uint8Array(hex.match(/.{1,2}/g)!.map((byte) => parseInt(byte, 16))); const match = hex.match(/.{1,2}/g);
return bytes.buffer; if (match === null) {
return new Uint8Array().buffer;
}
return new Uint8Array(match.map((byte) => parseInt(byte, 16))).buffer;
} }

View File

@@ -1,6 +1,13 @@
import * as Jose from "jose"; import * as Jose from "jose";
import { createKeyPair, ExportedKeyPair, importPrivateKey, importPublicKey, KeyPair, loadKeyPair } from "./key-pair.ts"; import {
createKeyPair,
type ExportedKeyPair,
importPrivateKey,
importPublicKey,
type KeyPair,
loadKeyPair,
} from "./key-pair.ts";
/* /*
|-------------------------------------------------------------------------------- |--------------------------------------------------------------------------------