feat: biome check
This commit is contained in:
@@ -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
|
|
||||||
14
.prettierrc
14
.prettierrc
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "all",
|
|
||||||
"tabWidth": 2,
|
|
||||||
"printWidth": 120,
|
|
||||||
"singleQuote": false,
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.ts",
|
|
||||||
"options": {
|
|
||||||
"parser": "typescript"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
20
.vscode/settings.json
vendored
20
.vscode/settings.json
vendored
@@ -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,
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
export const config = {
|
|
||||||
mongodb: "mongo:8.0.3",
|
|
||||||
postgres: "postgres:17",
|
|
||||||
};
|
|
||||||
@@ -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("&")}`;
|
|
||||||
}
|
|
||||||
@@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
};
|
|
||||||
@@ -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;
|
|
||||||
@@ -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>;
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -8,4 +8,4 @@
|
|||||||
"@module/workspace": "workspace:*",
|
"@module/workspace": "workspace:*",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
|
||||||
});
|
|
||||||
@@ -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");
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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">
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ module.exports = {
|
|||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['Inter', 'sans-serif'],
|
sans: ["Inter", "sans-serif"],
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
},
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -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
45
biome.json
Normal 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:"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
deno.json
12
deno.json
@@ -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
75
deno.lock
generated
@@ -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": {
|
||||||
|
|||||||
@@ -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: "^_",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -18,4 +18,4 @@
|
|||||||
"cookie": "1.0.2",
|
"cookie": "1.0.2",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.");
|
||||||
|
|||||||
@@ -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({
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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<{
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import z from "zod";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
export const resources = new ResourceRegistry([
|
export const resources = new ResourceRegistry([
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -16,4 +16,4 @@
|
|||||||
"cookie": "1.0.2",
|
"cookie": "1.0.2",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,4 +12,4 @@
|
|||||||
"@platform/logger": "workspace:*",
|
"@platform/logger": "workspace:*",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -7,4 +7,4 @@
|
|||||||
"@std/dotenv": "npm:@jsr/std__dotenv@0.225.5",
|
"@std/dotenv": "npm:@jsr/std__dotenv@0.225.5",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
|
|||||||
@@ -9,4 +9,4 @@
|
|||||||
"mongodb": "6.20.0",
|
"mongodb": "6.20.0",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -12,4 +12,4 @@
|
|||||||
"@valkyr/event-store": "npm:@jsr/valkyr__event-store@2.0.1",
|
"@valkyr/event-store": "npm:@jsr/valkyr__event-store@2.0.1",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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,15 +480,14 @@ 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) &
|
(TState["body"] extends ZodType ? { body: z.output<TState["body"]> } : unknown),
|
||||||
(TState["body"] extends ZodType ? { body: z.output<TState["body"]> } : unknown),
|
ServerContext,
|
||||||
ServerContext,
|
]
|
||||||
]
|
: [ServerContext];
|
||||||
: [ServerContext];
|
|
||||||
|
|
||||||
type HasInputArgs<TState extends RouteState> = TState["params"] extends ZodObject
|
type HasInputArgs<TState extends RouteState> = TState["params"] extends ZodObject
|
||||||
? true
|
? true
|
||||||
|
|||||||
@@ -15,4 +15,4 @@
|
|||||||
"path-to-regexp": "8",
|
"path-to-regexp": "8",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -13,4 +13,4 @@
|
|||||||
"@valkyr/json-rpc": "npm:@jsr/valkyr__json-rpc@1.1.0",
|
"@valkyr/json-rpc": "npm:@jsr/valkyr__json-rpc@1.1.0",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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", () => {
|
||||||
|
|||||||
@@ -11,4 +11,4 @@
|
|||||||
"@platform/storage": "workspace:*",
|
"@platform/storage": "workspace:*",
|
||||||
"@valkyr/json-rpc": "npm:@jsr/valkyr__json-rpc@1.1.0"
|
"@valkyr/json-rpc": "npm:@jsr/valkyr__json-rpc@1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,4 +8,4 @@
|
|||||||
"@platform/relay": "workspace:*",
|
"@platform/relay": "workspace:*",
|
||||||
"zod": "4.1.11"
|
"zod": "4.1.11"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,4 +10,4 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@platform/relay": "workspace:*"
|
"@platform/relay": "workspace:*"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = {};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,4 +7,4 @@
|
|||||||
"jose": "6.1.0",
|
"jose": "6.1.0",
|
||||||
"nanoid": "5.1.5"
|
"nanoid": "5.1.5"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------------
|
|--------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user