chore: migrate monorepo to deno 2 #3
@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"*": ["deno fmt"],
|
"*": ["deno fmt --check"],
|
||||||
"*.ts": ["deno lint"]
|
"*.ts": ["deno lint --fix"]
|
||||||
}
|
}
|
||||||
|
|||||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -2,8 +2,6 @@
|
|||||||
"recommendations": [
|
"recommendations": [
|
||||||
"bierner.github-markdown-preview",
|
"bierner.github-markdown-preview",
|
||||||
"bradlc.vscode-tailwindcss",
|
"bradlc.vscode-tailwindcss",
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"streetsidesoftware.code-spell-checker",
|
"streetsidesoftware.code-spell-checker",
|
||||||
"streetsidesoftware.code-spell-checker-spanish",
|
"streetsidesoftware.code-spell-checker-spanish",
|
||||||
"usernamehw.errorlens",
|
"usernamehw.errorlens",
|
||||||
|
|||||||
18
deno.json
18
deno.json
@ -1,18 +0,0 @@
|
|||||||
{
|
|
||||||
"tasks": {
|
|
||||||
"dev": "deno run --watch main.ts",
|
|
||||||
"test": "deno test --watch",
|
|
||||||
"hook": "deno run --allow-read --allow-run --allow-write https://deno.land/x/deno_hooks@0.1.1/mod.ts"
|
|
||||||
},
|
|
||||||
"workspace": [
|
|
||||||
"packages/fabric/core",
|
|
||||||
"packages/fabric/domain",
|
|
||||||
"packages/fabric/sqlite-store"
|
|
||||||
],
|
|
||||||
"imports": {
|
|
||||||
"@std/assert": "jsr:@std/assert@1"
|
|
||||||
},
|
|
||||||
"compilerOptions": {
|
|
||||||
"strict": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
34
deno.jsonc
Normal file
34
deno.jsonc
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
"tasks": {
|
||||||
|
"test": "deno test --allow-all --unstable-ffi",
|
||||||
|
"test:dev": "deno test --allow-all --unstable-ffi --watch",
|
||||||
|
"hook": "deno run --allow-read --allow-run --allow-write https://deno.land/x/deno_hooks@0.1.1/mod.ts"
|
||||||
|
},
|
||||||
|
"workspace": [
|
||||||
|
"packages/fabric/core",
|
||||||
|
"packages/fabric/domain",
|
||||||
|
"packages/fabric/sqlite-store",
|
||||||
|
"packages/fabric/testing"
|
||||||
|
],
|
||||||
|
"imports": {
|
||||||
|
"@db/sqlite": "jsr:@db/sqlite@^0.12.0",
|
||||||
|
"@quentinadam/decimal": "jsr:@quentinadam/decimal@^0.1.6",
|
||||||
|
"@std/expect": "jsr:@std/expect@^1.0.5",
|
||||||
|
"@std/testing": "jsr:@std/testing@^1.0.3",
|
||||||
|
"expect-type": "npm:expect-type@^1.1.0"
|
||||||
|
},
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"useUnknownInCatchVariables": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
"noUncheckedIndexedAccess": true
|
||||||
|
},
|
||||||
|
"lint": {
|
||||||
|
"include": ["src/"],
|
||||||
|
"rules": {
|
||||||
|
"tags": ["recommended"],
|
||||||
|
"exclude": ["no-namespace"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,15 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
|
|
||||||
import eslint from "@eslint/js";
|
|
||||||
import tseslint from "typescript-eslint";
|
|
||||||
|
|
||||||
export default tseslint.config(
|
|
||||||
eslint.configs.recommended,
|
|
||||||
...tseslint.configs.strict,
|
|
||||||
...tseslint.configs.stylistic,
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
"@typescript-eslint/no-namespace": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
|
||||||
6
packages/fabric/core/deno.jsonc
Normal file
6
packages/fabric/core/deno.jsonc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"name": "@fabric/core",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@fabric/core",
|
|
||||||
"private": true,
|
|
||||||
"sideEffects": false,
|
|
||||||
"type": "module",
|
|
||||||
"main": "./dist/index.js",
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"exports": {
|
|
||||||
".": "./dist/index.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"packageManager": "yarn@4.1.1",
|
|
||||||
"devDependencies": {
|
|
||||||
"@vitest/coverage-v8": "^2.1.2",
|
|
||||||
"typescript": "^5.6.3",
|
|
||||||
"vitest": "^2.1.2"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "vitest",
|
|
||||||
"coverage": "vitest run --coverage",
|
|
||||||
"build": "tsc -p tsconfig.build.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,10 +1,8 @@
|
|||||||
import { describe, expectTypeOf, test } from "vitest";
|
import { describe, expectTypeOf, test } from "@fabric/testing";
|
||||||
|
import type { ArrayElement } from "./array-element.ts";
|
||||||
|
|
||||||
describe("ArrayElement utils", () => {
|
describe("ArrayElement", () => {
|
||||||
test("Given an array, it should return the element type of the array", () => {
|
test("Given an array, it should return the element type of the array", () => {
|
||||||
type ArrayElement<T extends readonly unknown[]> =
|
|
||||||
T extends readonly (infer U)[] ? U : never;
|
|
||||||
|
|
||||||
type result = ArrayElement<["a", "b", "c"]>;
|
type result = ArrayElement<["a", "b", "c"]>;
|
||||||
|
|
||||||
expectTypeOf<result>().toEqualTypeOf<"a" | "b" | "c">();
|
expectTypeOf<result>().toEqualTypeOf<"a" | "b" | "c">();
|
||||||
@ -1 +1 @@
|
|||||||
export * from "./array-element.js";
|
export * from "./array-element.ts";
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./is-error.js";
|
export * from "./is-error.ts";
|
||||||
export * from "./tagged-error.js";
|
export * from "./tagged-error.ts";
|
||||||
export * from "./unexpected-error.js";
|
export * from "./unexpected-error.ts";
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
||||||
import { Result } from "../result/result.js";
|
|
||||||
import { isError } from "./is-error.js";
|
|
||||||
import { TaggedError } from "./tagged-error.js";
|
|
||||||
|
|
||||||
describe("is-error", () => {
|
|
||||||
it("should determine if a value is an error", () => {
|
|
||||||
type DemoResult = Result<number, TaggedError<"DemoError">>;
|
|
||||||
|
|
||||||
//Ok should not be an error
|
|
||||||
const ok: DemoResult = 42;
|
|
||||||
expect(isError(ok)).toBe(false);
|
|
||||||
|
|
||||||
//Error should be an error
|
|
||||||
const error: DemoResult = new TaggedError("DemoError");
|
|
||||||
expect(isError(error)).toBe(true);
|
|
||||||
|
|
||||||
//After a check, typescript should be able to infer the type
|
|
||||||
if (isError(error)) {
|
|
||||||
expectTypeOf(error).toEqualTypeOf<TaggedError<"DemoError">>();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
16
packages/fabric/core/src/error/is-error.test.ts
Normal file
16
packages/fabric/core/src/error/is-error.test.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { describe, expect, expectTypeOf, test } from "@fabric/testing";
|
||||||
|
import { isError } from "./is-error.ts";
|
||||||
|
import { UnexpectedError } from "./unexpected-error.ts";
|
||||||
|
|
||||||
|
describe("is-error", () => {
|
||||||
|
test("Given a value that is an error, it should return true", () => {
|
||||||
|
const error = new UnexpectedError();
|
||||||
|
|
||||||
|
expect(isError(error)).toBe(true);
|
||||||
|
|
||||||
|
//After a check, typescript should be able to infer the type
|
||||||
|
if (isError(error)) {
|
||||||
|
expectTypeOf(error).toEqualTypeOf<UnexpectedError>();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { TaggedError } from "./tagged-error.js";
|
import { TaggedError } from "./tagged-error.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Indicates if a value is an error.
|
* Indicates if a value is an error.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { TaggedVariant, VariantTag } from "../variant/index.js";
|
import { type TaggedVariant, VariantTag } from "../variant/index.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A TaggedError is a tagged variant with an error message.
|
* A TaggedError is a tagged variant with an error message.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { TaggedError } from "./tagged-error.js";
|
import { TaggedError } from "./tagged-error.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* `UnexpectedError` represents any type of unexpected error.
|
* `UnexpectedError` represents any type of unexpected error.
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
export * from "./array/index.js";
|
export * from "./array/index.ts";
|
||||||
export * from "./error/index.js";
|
export * from "./error/index.ts";
|
||||||
export * from "./record/index.js";
|
export * from "./record/index.ts";
|
||||||
export * from "./result/index.js";
|
export * from "./result/index.ts";
|
||||||
export * from "./run/index.js";
|
export * from "./run/index.ts";
|
||||||
export * from "./time/index.js";
|
export * from "./time/index.ts";
|
||||||
export * from "./types/index.js";
|
export * from "./types/index.ts";
|
||||||
export * from "./utils/index.js";
|
export * from "./utils/index.ts";
|
||||||
export * from "./variant/index.js";
|
export * from "./variant/index.ts";
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./is-record-empty.js";
|
export * from "./is-record-empty.ts";
|
||||||
export * from "./is-record.js";
|
export * from "./is-record.ts";
|
||||||
|
|||||||
@ -1,19 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { isRecordEmpty } from "./is-record-empty.js";
|
|
||||||
|
|
||||||
describe("Record - Is Record Empty", () => {
|
|
||||||
it("should return true for an empty record", () => {
|
|
||||||
const result = isRecordEmpty({});
|
|
||||||
expect(result).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false for a non-empty record", () => {
|
|
||||||
const result = isRecordEmpty({ key: "value" });
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false for a record with multiple keys", () => {
|
|
||||||
const result = isRecordEmpty({ key1: "value1", key2: "value2" });
|
|
||||||
expect(result).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
19
packages/fabric/core/src/record/is-record-empty.test.ts
Normal file
19
packages/fabric/core/src/record/is-record-empty.test.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
|
import { isRecordEmpty } from "./is-record-empty.ts";
|
||||||
|
|
||||||
|
describe("Record - Is Record Empty", () => {
|
||||||
|
test("Given an empty record, it should return true", () => {
|
||||||
|
const result = isRecordEmpty({});
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given a record with a single key, it should return false", () => {
|
||||||
|
const result = isRecordEmpty({ key: "value" });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Given a record with multiple keys, it should return false", () => {
|
||||||
|
const result = isRecordEmpty({ key1: "value1", key2: "value2" });
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,23 +1,23 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
import { isRecord } from "./is-record.js";
|
import { isRecord } from "./is-record.ts";
|
||||||
|
|
||||||
describe("isRecord", () => {
|
describe("isRecord", () => {
|
||||||
it("should return true for an object", () => {
|
test("Given an empty object, it should return true", () => {
|
||||||
const obj = { name: "John", age: 30 };
|
const obj = { name: "John", age: 30 };
|
||||||
expect(isRecord(obj)).toBe(true);
|
expect(isRecord(obj)).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return false for an array", () => {
|
test("Given an array, it should return false", () => {
|
||||||
const arr = [1, 2, 3];
|
const arr = [1, 2, 3];
|
||||||
expect(isRecord(arr)).toBe(false);
|
expect(isRecord(arr)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return false for null", () => {
|
test("Given a number, it should return false", () => {
|
||||||
const value = null;
|
const value = null;
|
||||||
expect(isRecord(value)).toBe(false);
|
expect(isRecord(value)).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return false for a string", () => {
|
test("Given a string, it should return false", () => {
|
||||||
const value = "Hello";
|
const value = "Hello";
|
||||||
expect(isRecord(value)).toBe(false);
|
expect(isRecord(value)).toBe(false);
|
||||||
});
|
});
|
||||||
@ -1,8 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-namespace no-explicit-any
|
||||||
import { TaggedError } from "../error/tagged-error.js";
|
import type { TaggedError } from "../error/tagged-error.ts";
|
||||||
import { UnexpectedError } from "../error/unexpected-error.js";
|
import { UnexpectedError } from "../error/unexpected-error.ts";
|
||||||
import { MaybePromise } from "../types/maybe-promise.js";
|
import type { MaybePromise } from "../types/maybe-promise.ts";
|
||||||
import { Result } from "./result.js";
|
import { Result } from "./result.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An AsyncResult represents the result of an asynchronous operation that can
|
* An AsyncResult represents the result of an asynchronous operation that can
|
||||||
@ -10,13 +10,13 @@ import { Result } from "./result.js";
|
|||||||
*/
|
*/
|
||||||
export type AsyncResult<
|
export type AsyncResult<
|
||||||
TValue = any,
|
TValue = any,
|
||||||
TError extends TaggedError = never,
|
TError extends TaggedError = never
|
||||||
> = Promise<Result<TValue, TError>>;
|
> = Promise<Result<TValue, TError>>;
|
||||||
|
|
||||||
export namespace AsyncResult {
|
export namespace AsyncResult {
|
||||||
export async function tryFrom<T, TError extends TaggedError>(
|
export async function tryFrom<T, TError extends TaggedError>(
|
||||||
fn: () => MaybePromise<T>,
|
fn: () => MaybePromise<T>,
|
||||||
errorMapper: (error: any) => TError,
|
errorMapper: (error: any) => TError
|
||||||
): AsyncResult<T, TError> {
|
): AsyncResult<T, TError> {
|
||||||
try {
|
try {
|
||||||
return Result.succeedWith(await fn());
|
return Result.succeedWith(await fn());
|
||||||
@ -25,9 +25,7 @@ export namespace AsyncResult {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function from<T>(
|
export function from<T>(fn: () => MaybePromise<T>): AsyncResult<T, never> {
|
||||||
fn: () => MaybePromise<T>,
|
|
||||||
): AsyncResult<T, never> {
|
|
||||||
return tryFrom(fn, (error) => new UnexpectedError(error) as never);
|
return tryFrom(fn, (error) => new UnexpectedError(error) as never);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./async-result.js";
|
export * from "./async-result.ts";
|
||||||
export * from "./result.js";
|
export * from "./result.ts";
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
import { describe, expect, expectTypeOf, it, vitest } from "vitest";
|
import { describe, expect, expectTypeOf, fn, test } from "@fabric/testing";
|
||||||
import { UnexpectedError } from "../error/unexpected-error.js";
|
import { UnexpectedError } from "../error/unexpected-error.ts";
|
||||||
import { Result } from "./result.js";
|
import { Result } from "./result.ts";
|
||||||
|
|
||||||
describe("Result", () => {
|
describe("Result", () => {
|
||||||
describe("isOk", () => {
|
describe("isOk", () => {
|
||||||
it("should return true if the result is ok", () => {
|
test("should return true if the result is ok", () => {
|
||||||
const result = Result.succeedWith(1) as Result<number, UnexpectedError>;
|
const result = Result.succeedWith(1) as Result<number, UnexpectedError>;
|
||||||
|
|
||||||
expect(result.isOk()).toBe(true);
|
expect(result.isOk()).toBe(true);
|
||||||
@ -18,7 +18,7 @@ describe("Result", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("isError", () => {
|
describe("isError", () => {
|
||||||
it("should return true if the result is an error", () => {
|
test("should return true if the result is an error", () => {
|
||||||
const result = Result.failWith(new UnexpectedError()) as Result<
|
const result = Result.failWith(new UnexpectedError()) as Result<
|
||||||
number,
|
number,
|
||||||
UnexpectedError
|
UnexpectedError
|
||||||
@ -35,7 +35,7 @@ describe("Result", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("Map", () => {
|
describe("Map", () => {
|
||||||
it("should return the result of the last function", () => {
|
test("should return the result of the last function", () => {
|
||||||
const x = 0;
|
const x = 0;
|
||||||
|
|
||||||
const result = Result.succeedWith(x + 1).map((x) => x * 2);
|
const result = Result.succeedWith(x + 1).map((x) => x * 2);
|
||||||
@ -45,13 +45,13 @@ describe("Result", () => {
|
|||||||
expectTypeOf(result).toEqualTypeOf<Result<number, never>>();
|
expectTypeOf(result).toEqualTypeOf<Result<number, never>>();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not execute the function if the result is an error", () => {
|
test("should not execute the function if the result is an error", () => {
|
||||||
const fn = vitest.fn();
|
const mock = fn() as () => number;
|
||||||
const result = Result.failWith(new UnexpectedError()).map(fn);
|
const result = Result.failWith(new UnexpectedError()).map(mock);
|
||||||
|
|
||||||
expect(result.isError()).toBe(true);
|
expect(result.isError()).toBe(true);
|
||||||
|
|
||||||
expect(fn).not.toHaveBeenCalled();
|
expect(mock).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
|
|
||||||
import { isError } from "../error/is-error.js";
|
import { isError } from "../error/is-error.ts";
|
||||||
import { TaggedError } from "../error/tagged-error.js";
|
import type { TaggedError } from "../error/tagged-error.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Result represents the outcome of an operation
|
* A Result represents the outcome of an operation
|
||||||
@ -24,7 +24,7 @@ export class Result<TValue, TError extends TaggedError = never> {
|
|||||||
|
|
||||||
static tryFrom<T, TError extends TaggedError>(
|
static tryFrom<T, TError extends TaggedError>(
|
||||||
fn: () => T,
|
fn: () => T,
|
||||||
errorMapper: (error: any) => TError,
|
errorMapper: (error: any) => TError
|
||||||
): Result<T, TError> {
|
): Result<T, TError> {
|
||||||
try {
|
try {
|
||||||
return Result.succeedWith(fn());
|
return Result.succeedWith(fn());
|
||||||
@ -83,7 +83,7 @@ export class Result<TValue, TError extends TaggedError = never> {
|
|||||||
* Map a function over the value of the result.
|
* Map a function over the value of the result.
|
||||||
*/
|
*/
|
||||||
map<TMappedValue>(
|
map<TMappedValue>(
|
||||||
fn: (value: TValue) => TMappedValue,
|
fn: (value: TValue) => TMappedValue
|
||||||
): Result<TMappedValue, TError> {
|
): Result<TMappedValue, TError> {
|
||||||
if (!isError(this.value)) {
|
if (!isError(this.value)) {
|
||||||
return Result.succeedWith(fn(this.value as TValue));
|
return Result.succeedWith(fn(this.value as TValue));
|
||||||
@ -96,7 +96,7 @@ export class Result<TValue, TError extends TaggedError = never> {
|
|||||||
* Maps a function over the value of the result and flattens the result.
|
* Maps a function over the value of the result and flattens the result.
|
||||||
*/
|
*/
|
||||||
flatMap<TMappedValue, TMappedError extends TaggedError>(
|
flatMap<TMappedValue, TMappedError extends TaggedError>(
|
||||||
fn: (value: TValue) => Result<TMappedValue, TMappedError>,
|
fn: (value: TValue) => Result<TMappedValue, TMappedError>
|
||||||
): Result<TMappedValue, TError | TMappedError> {
|
): Result<TMappedValue, TError | TMappedError> {
|
||||||
if (!isError(this.value)) {
|
if (!isError(this.value)) {
|
||||||
return fn(this.value as TValue) as any;
|
return fn(this.value as TValue) as any;
|
||||||
@ -111,7 +111,7 @@ export class Result<TValue, TError extends TaggedError = never> {
|
|||||||
*/
|
*/
|
||||||
tryMap<TMappedValue>(
|
tryMap<TMappedValue>(
|
||||||
fn: (value: TValue) => TMappedValue,
|
fn: (value: TValue) => TMappedValue,
|
||||||
errMapper: (error: any) => TError,
|
errMapper: (error: any) => TError
|
||||||
): Result<TMappedValue, TError> {
|
): Result<TMappedValue, TError> {
|
||||||
if (!isError(this.value)) {
|
if (!isError(this.value)) {
|
||||||
try {
|
try {
|
||||||
@ -128,7 +128,7 @@ export class Result<TValue, TError extends TaggedError = never> {
|
|||||||
* Map a function over the error of the result.
|
* Map a function over the error of the result.
|
||||||
*/
|
*/
|
||||||
mapError<TMappedError extends TaggedError>(
|
mapError<TMappedError extends TaggedError>(
|
||||||
fn: (error: TError) => TMappedError,
|
fn: (error: TError) => TMappedError
|
||||||
): Result<TValue, TMappedError> {
|
): Result<TValue, TMappedError> {
|
||||||
if (isError(this.value)) {
|
if (isError(this.value)) {
|
||||||
return Result.failWith(fn(this.value as TError));
|
return Result.failWith(fn(this.value as TError));
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./run.js";
|
export * from "./run.ts";
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { UnexpectedError } from "../error/unexpected-error.js";
|
|
||||||
import { Result } from "../result/result.js";
|
|
||||||
import { Run } from "./run.js";
|
|
||||||
|
|
||||||
describe("Run", () => {
|
|
||||||
describe("In Sequence", () => {
|
|
||||||
it("should pipe the results of multiple async functions", async () => {
|
|
||||||
const result = await Run.seq(
|
|
||||||
async () => Result.succeedWith(1),
|
|
||||||
async (x) => Result.succeedWith(x + 1),
|
|
||||||
async (x) => Result.succeedWith(x * 2),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.unwrapOrThrow()).toEqual(4);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return the first error if one of the functions fails", async () => {
|
|
||||||
const result = await Run.seq(
|
|
||||||
async () => Result.succeedWith(1),
|
|
||||||
async () => Result.failWith(new UnexpectedError()),
|
|
||||||
async (x) => Result.succeedWith(x * 2),
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(result.isError()).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
29
packages/fabric/core/src/run/run.test.ts
Normal file
29
packages/fabric/core/src/run/run.test.ts
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// deno-lint-ignore-file require-await
|
||||||
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
|
import { UnexpectedError } from "../error/unexpected-error.ts";
|
||||||
|
import { Result } from "../result/result.ts";
|
||||||
|
import { Run } from "./run.ts";
|
||||||
|
|
||||||
|
describe("Run", () => {
|
||||||
|
describe("In Sequence", () => {
|
||||||
|
test("should pipe the results of multiple async functions", async () => {
|
||||||
|
const result = await Run.seq(
|
||||||
|
async () => Result.succeedWith(1),
|
||||||
|
async (x) => Result.succeedWith(x + 1),
|
||||||
|
async (x) => Result.succeedWith(x * 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.unwrapOrThrow()).toEqual(4);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return the first error if one of the functions fails", async () => {
|
||||||
|
const result = await Run.seq(
|
||||||
|
async () => Result.succeedWith(1),
|
||||||
|
async () => Result.failWith(new UnexpectedError()),
|
||||||
|
async (x) => Result.succeedWith(x * 2)
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result.isError()).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-namespace no-explicit-any
|
||||||
import { TaggedError } from "../error/tagged-error.js";
|
import type { TaggedError } from "../error/tagged-error.ts";
|
||||||
import { AsyncResult } from "../result/async-result.js";
|
import type { AsyncResult } from "../result/async-result.ts";
|
||||||
|
|
||||||
export namespace Run {
|
export namespace Run {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@ -36,14 +36,14 @@ export namespace Run {
|
|||||||
export async function seq(
|
export async function seq(
|
||||||
...fns: ((...args: any[]) => AsyncResult<any, any>)[]
|
...fns: ((...args: any[]) => AsyncResult<any, any>)[]
|
||||||
): AsyncResult<any, any> {
|
): AsyncResult<any, any> {
|
||||||
let result = await fns[0]();
|
let result = await fns[0]!();
|
||||||
|
|
||||||
for (let i = 1; i < fns.length; i++) {
|
for (let i = 1; i < fns.length; i++) {
|
||||||
if (result.isError()) {
|
if (result.isError()) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = await fns[i](result.unwrapOrThrow());
|
result = await fns[i]!(result.unwrapOrThrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -80,7 +80,7 @@ export namespace Run {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function UNSAFE<T, TError extends TaggedError>(
|
export async function UNSAFE<T, TError extends TaggedError>(
|
||||||
fn: () => AsyncResult<T, TError>,
|
fn: () => AsyncResult<T, TError>
|
||||||
): Promise<T> {
|
): Promise<T> {
|
||||||
return (await fn()).unwrapOrThrow();
|
return (await fn()).unwrapOrThrow();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,2 @@
|
|||||||
export * from "./posix-date.js";
|
export * from "./posix-date.ts";
|
||||||
export * from "./time-constants.js";
|
export * from "./time-constants.ts";
|
||||||
export * from "./timeout.js";
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { isRecord } from "../record/is-record.js";
|
import { isRecord } from "../record/is-record.ts";
|
||||||
import { TaggedVariant } from "../variant/variant.js";
|
import type { TaggedVariant } from "../variant/variant.ts";
|
||||||
|
|
||||||
export class PosixDate {
|
export class PosixDate {
|
||||||
constructor(public readonly timestamp: number = Date.now()) {}
|
constructor(public readonly timestamp: number = Date.now()) {}
|
||||||
|
|||||||
@ -1,44 +0,0 @@
|
|||||||
import { describe, expect, test } from "vitest";
|
|
||||||
import { timeout } from "./timeout.js";
|
|
||||||
|
|
||||||
const HEAVY_TESTS =
|
|
||||||
process.env.HEAVY_TESTS === "true" || process.env.RUN_ALL_TESTS === "true";
|
|
||||||
|
|
||||||
describe("timeout", () => {
|
|
||||||
test.runIf(HEAVY_TESTS)(
|
|
||||||
"timeout never triggers *before* the input time",
|
|
||||||
async () => {
|
|
||||||
const count = 10000;
|
|
||||||
const maxTimeInMs = 1000;
|
|
||||||
|
|
||||||
const result = await Promise.all(
|
|
||||||
new Array(count).fill(0).map(async (e, i) => {
|
|
||||||
const start = Date.now();
|
|
||||||
const ms = i % maxTimeInMs;
|
|
||||||
await timeout(ms);
|
|
||||||
const end = Date.now();
|
|
||||||
return end - start;
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
result
|
|
||||||
.map((t, i) => {
|
|
||||||
return [t, i % maxTimeInMs]; //Actual time and expected time
|
|
||||||
})
|
|
||||||
.filter((e) => {
|
|
||||||
return e[0] < e[1]; //Actual time is less than the expected time
|
|
||||||
}),
|
|
||||||
).toEqual([]);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
test("using timeout we can define a timeout in milliseconds", async () => {
|
|
||||||
const start = Date.now();
|
|
||||||
await timeout(100);
|
|
||||||
const end = Date.now();
|
|
||||||
|
|
||||||
const time = end - start;
|
|
||||||
|
|
||||||
expect(time).toBeGreaterThanOrEqual(100);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
export function timeout(ms: number) {
|
|
||||||
return new Promise<void>((resolve) => {
|
|
||||||
const start = Date.now();
|
|
||||||
setTimeout(() => {
|
|
||||||
const end = Date.now();
|
|
||||||
const remaining = ms - (end - start);
|
|
||||||
if (remaining > 0) {
|
|
||||||
timeout(remaining).then(resolve);
|
|
||||||
} else {
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
}, ms);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
|
// deno-lint-ignore-file no-explicit-any
|
||||||
/**
|
/**
|
||||||
* A function that takes an argument of type `T` and returns a value of type `R`.
|
* A function that takes an argument of type `T` and returns a value of type `R`.
|
||||||
*/
|
*/
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
||||||
export type Fn<T = any, R = any> = (arg: T) => R;
|
export type Fn<T = any, R = any> = (arg: T) => R;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
export * from "./enum.js";
|
export * from "./enum.ts";
|
||||||
export * from "./fn.js";
|
export * from "./fn.ts";
|
||||||
export * from "./keyof.js";
|
export * from "./keyof.ts";
|
||||||
export * from "./maybe-promise.js";
|
export * from "./maybe-promise.ts";
|
||||||
export * from "./optional.js";
|
export * from "./optional.ts";
|
||||||
export * from "./record.js";
|
export * from "./record.ts";
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { UnexpectedError } from "../error/unexpected-error.js";
|
import { UnexpectedError } from "../error/unexpected-error.ts";
|
||||||
|
|
||||||
export function ensureValue<T>(value?: T): T {
|
export function ensureValue<T>(value?: T): T {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
throw new UnexpectedError("Value is undefined");
|
throw new UnexpectedError("Value is nullish.");
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./ensure-value.js";
|
export * from "./ensure-value.ts";
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./match.js";
|
export * from "./match.ts";
|
||||||
export * from "./variant.js";
|
export * from "./variant.ts";
|
||||||
|
|||||||
@ -1,36 +0,0 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import { match } from "./match.js";
|
|
||||||
import { TaggedVariant, VariantTag } from "./variant.js";
|
|
||||||
|
|
||||||
interface V1 extends TaggedVariant<"V1"> {
|
|
||||||
a: number;
|
|
||||||
}
|
|
||||||
interface V2 extends TaggedVariant<"V2"> {
|
|
||||||
b: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type Variant = V1 | V2;
|
|
||||||
|
|
||||||
describe("Pattern matching", () => {
|
|
||||||
it("Should match a pattern", () => {
|
|
||||||
const v = { [VariantTag]: "V1", a: 42 } as Variant;
|
|
||||||
|
|
||||||
const result = match(v).case({
|
|
||||||
V1: (v) => v.a,
|
|
||||||
V2: (v) => v.b,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toBe(42);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("Should alert that a pattern is not exhaustive", () => {
|
|
||||||
const v = { [VariantTag]: "V1", a: 42 } as Variant;
|
|
||||||
|
|
||||||
expect(() =>
|
|
||||||
// @ts-expect-error Testing non-exhaustive pattern matching
|
|
||||||
match(v).case({
|
|
||||||
V2: (v) => v.b,
|
|
||||||
}),
|
|
||||||
).toThrowError("Non-exhaustive pattern match");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
35
packages/fabric/core/src/variant/match.test.ts
Normal file
35
packages/fabric/core/src/variant/match.test.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { expect } from "jsr:@std/expect";
|
||||||
|
import { match } from "./match.ts";
|
||||||
|
import { type TaggedVariant, VariantTag } from "./variant.ts";
|
||||||
|
|
||||||
|
interface V1 extends TaggedVariant<"V1"> {
|
||||||
|
a: number;
|
||||||
|
}
|
||||||
|
interface V2 extends TaggedVariant<"V2"> {
|
||||||
|
b: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type Variant = V1 | V2;
|
||||||
|
|
||||||
|
const v = { [VariantTag]: "V1", a: 42 } as Variant;
|
||||||
|
|
||||||
|
Deno.test("match().case() calls the correct function", () => {
|
||||||
|
const result = match(v).case({
|
||||||
|
V1: (v) => v.a,
|
||||||
|
V2: (v) => v.b,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toBe(42);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test(
|
||||||
|
"match().case() throws an error for non-exhaustive pattern matching",
|
||||||
|
() => {
|
||||||
|
expect(() =>
|
||||||
|
// @ts-expect-error Testing non-exhaustive pattern matching
|
||||||
|
match(v).case({
|
||||||
|
V2: (v) => v.b,
|
||||||
|
})
|
||||||
|
).toThrow("Non-exhaustive pattern match");
|
||||||
|
}
|
||||||
|
);
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { Fn } from "../types/fn.js";
|
import type { Fn } from "../types/fn.ts";
|
||||||
import { TaggedVariant, VariantFromTag, VariantTag } from "./variant.js";
|
import { type TaggedVariant, type VariantFromTag, VariantTag } from "./variant.ts";
|
||||||
|
|
||||||
export type VariantMatcher<TVariant extends TaggedVariant<string>, T> = {
|
export type VariantMatcher<TVariant extends TaggedVariant<string>, T> = {
|
||||||
[K in TVariant[VariantTag]]: Fn<VariantFromTag<TVariant, K>, T>;
|
[K in TVariant[VariantTag]]: Fn<VariantFromTag<TVariant, K>, T>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function match<const TVariant extends TaggedVariant<string>>(
|
export function match<const TVariant extends TaggedVariant<string>>(
|
||||||
v: TVariant,
|
v: TVariant
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
case<
|
case<
|
||||||
@ -14,14 +14,14 @@ export function match<const TVariant extends TaggedVariant<string>>(
|
|||||||
const TMatcher extends VariantMatcher<
|
const TMatcher extends VariantMatcher<
|
||||||
TVariant,
|
TVariant,
|
||||||
TReturnType
|
TReturnType
|
||||||
> = VariantMatcher<TVariant, TReturnType>,
|
> = VariantMatcher<TVariant, TReturnType>
|
||||||
>(cases: TMatcher): TReturnType {
|
>(cases: TMatcher): TReturnType {
|
||||||
if (!(v[VariantTag] in cases)) {
|
if (!(v[VariantTag] in cases)) {
|
||||||
throw new Error("Non-exhaustive pattern match");
|
throw new Error("Non-exhaustive pattern match");
|
||||||
}
|
}
|
||||||
|
|
||||||
return cases[v[VariantTag] as TVariant[VariantTag]](
|
return cases[v[VariantTag] as TVariant[VariantTag]](
|
||||||
v as Extract<TVariant, { [VariantTag]: TVariant[VariantTag] }>,
|
v as Extract<TVariant, { [VariantTag]: TVariant[VariantTag] }>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,41 +0,0 @@
|
|||||||
import { describe, expect, expectTypeOf, it } from "vitest";
|
|
||||||
import { TaggedVariant, Variant, VariantTag } from "./variant.js";
|
|
||||||
|
|
||||||
interface SuccessVariant extends TaggedVariant<"success"> {
|
|
||||||
[VariantTag]: "success";
|
|
||||||
data: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ErrorVariant extends TaggedVariant<"error"> {
|
|
||||||
[VariantTag]: "error";
|
|
||||||
message: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
describe("Variant", () => {
|
|
||||||
describe("isVariant", () => {
|
|
||||||
const successVariant = {
|
|
||||||
[VariantTag]: "success",
|
|
||||||
data: "Operation successful",
|
|
||||||
} as SuccessVariant | ErrorVariant;
|
|
||||||
|
|
||||||
const errorVariant = {
|
|
||||||
[VariantTag]: "error",
|
|
||||||
message: "Operation failed",
|
|
||||||
} as SuccessVariant | ErrorVariant;
|
|
||||||
|
|
||||||
it("should return true for a matching tag and correctly infer it", () => {
|
|
||||||
if (Variant.is(successVariant, "success")) {
|
|
||||||
expectTypeOf(successVariant).toEqualTypeOf<SuccessVariant>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Variant.is(errorVariant, "error")) {
|
|
||||||
expectTypeOf(errorVariant).toEqualTypeOf<ErrorVariant>();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false for a non-matching tag", () => {
|
|
||||||
expect(Variant.is(successVariant, "error")).toBe(false);
|
|
||||||
expect(Variant.is(errorVariant, "success")).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
32
packages/fabric/core/src/variant/variant.test.ts
Normal file
32
packages/fabric/core/src/variant/variant.test.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { expect } from "jsr:@std/expect";
|
||||||
|
import { type TaggedVariant, Variant, VariantTag } from "./variant.ts";
|
||||||
|
|
||||||
|
interface SuccessVariant extends TaggedVariant<"success"> {
|
||||||
|
[VariantTag]: "success";
|
||||||
|
data: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ErrorVariant extends TaggedVariant<"error"> {
|
||||||
|
[VariantTag]: "error";
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const successVariant = {
|
||||||
|
[VariantTag]: "success",
|
||||||
|
data: "Operation successful",
|
||||||
|
} as SuccessVariant | ErrorVariant;
|
||||||
|
|
||||||
|
const errorVariant = {
|
||||||
|
[VariantTag]: "error",
|
||||||
|
message: "Operation failed",
|
||||||
|
} as SuccessVariant | ErrorVariant;
|
||||||
|
|
||||||
|
Deno.test("is() should return true for a matching tag", () => {
|
||||||
|
expect(Variant.is(successVariant, "success")).toBe(true);
|
||||||
|
expect(Variant.is(errorVariant, "error")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
Deno.test("is() should return false for a non-matching tag", () => {
|
||||||
|
expect(Variant.is(successVariant, "error")).toBe(false);
|
||||||
|
expect(Variant.is(errorVariant, "success")).toBe(false);
|
||||||
|
});
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "./tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"noEmit": false,
|
|
||||||
"allowImportingTsExtensions": false,
|
|
||||||
"outDir": "dist"
|
|
||||||
},
|
|
||||||
"exclude": [
|
|
||||||
"src/**/*.spec.ts",
|
|
||||||
"dist",
|
|
||||||
"node_modules",
|
|
||||||
"coverage",
|
|
||||||
"vitest.config.ts"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../../tsconfig.json",
|
|
||||||
"exclude": ["dist", "node_modules"]
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { defineConfig } from "vitest/config";
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
test: {
|
|
||||||
coverage: {
|
|
||||||
exclude: ["dist/**", "vitest.config.ts", "**/index.ts", "**/*.spec.ts"],
|
|
||||||
},
|
|
||||||
passWithNoTests: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
7
packages/fabric/domain/deno.jsonc
Normal file
7
packages/fabric/domain/deno.jsonc
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"name": "@fabric/domain",
|
||||||
|
"exports": {
|
||||||
|
".": "./src/index.ts",
|
||||||
|
"./mocks": "./src/mocks.ts"
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,30 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@fabric/domain",
|
|
||||||
"private": true,
|
|
||||||
"sideEffects": false,
|
|
||||||
"type": "module",
|
|
||||||
"main": "./dist/index.js",
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"exports": {
|
|
||||||
".": "./dist/index.js",
|
|
||||||
"./mocks": "./dist/mocks.js"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"packageManager": "yarn@4.1.1",
|
|
||||||
"devDependencies": {
|
|
||||||
"@vitest/coverage-v8": "^2.1.2",
|
|
||||||
"typescript": "^5.6.3",
|
|
||||||
"vitest": "^2.1.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@fabric/core": "workspace:^",
|
|
||||||
"decimal.js": "^10.4.3"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "vitest",
|
|
||||||
"coverage": "vitest run --coverage",
|
|
||||||
"build": "tsc -p tsconfig.build.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./circular-dependency-error.js";
|
export * from "./circular-dependency-error.ts";
|
||||||
export * from "./query-error.js";
|
export * from "./query-error.ts";
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { TaggedError } from "@fabric/core";
|
import { TaggedError } from "@fabric/core";
|
||||||
|
|
||||||
export class StoreQueryError extends TaggedError<"StoreQueryError"> {
|
export class StoreQueryError extends TaggedError<"StoreQueryError"> {
|
||||||
constructor(public message: string) {
|
constructor(message: string) {
|
||||||
super("StoreQueryError", message);
|
super("StoreQueryError", message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +1,35 @@
|
|||||||
import {
|
import type {
|
||||||
AsyncResult,
|
AsyncResult,
|
||||||
MaybePromise,
|
MaybePromise,
|
||||||
PosixDate,
|
PosixDate,
|
||||||
VariantFromTag,
|
VariantFromTag,
|
||||||
VariantTag,
|
VariantTag,
|
||||||
} from "@fabric/core";
|
} from "@fabric/core";
|
||||||
import { StoreQueryError } from "../errors/query-error.js";
|
import type { StoreQueryError } from "../errors/query-error.ts";
|
||||||
import { UUID } from "../types/uuid.js";
|
import type { UUID } from "../types/uuid.ts";
|
||||||
import { Event } from "./event.js";
|
import type { Event } from "./event.ts";
|
||||||
import { StoredEvent } from "./stored-event.js";
|
import type { StoredEvent } from "./stored-event.ts";
|
||||||
|
|
||||||
export interface EventStore<TEvents extends Event> {
|
export interface EventStore<TEvents extends Event> {
|
||||||
/**
|
/**
|
||||||
* Store a new event in the event store.
|
* Store a new event in the event store.
|
||||||
*/
|
*/
|
||||||
append<T extends TEvents>(
|
append<T extends TEvents>(
|
||||||
event: T,
|
event: T
|
||||||
): AsyncResult<StoredEvent<T>, StoreQueryError>;
|
): AsyncResult<StoredEvent<T>, StoreQueryError>;
|
||||||
|
|
||||||
getEventsFromStream(
|
getEventsFromStream(
|
||||||
streamId: UUID,
|
streamId: UUID
|
||||||
): AsyncResult<StoredEvent<TEvents>[], StoreQueryError>;
|
): AsyncResult<StoredEvent<TEvents>[], StoreQueryError>;
|
||||||
|
|
||||||
subscribe<TEventKey extends TEvents[VariantTag]>(
|
subscribe<TEventKey extends TEvents[VariantTag]>(
|
||||||
events: TEventKey[],
|
events: TEventKey[],
|
||||||
subscriber: EventSubscriber<VariantFromTag<TEvents, TEventKey>>,
|
subscriber: EventSubscriber<VariantFromTag<TEvents, TEventKey>>
|
||||||
): void;
|
): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type EventSubscriber<TEvents extends Event = Event> = (
|
export type EventSubscriber<TEvents extends Event = Event> = (
|
||||||
event: StoredEvent<TEvents>,
|
event: StoredEvent<TEvents>
|
||||||
) => MaybePromise<void>;
|
) => MaybePromise<void>;
|
||||||
|
|
||||||
export interface EventFilterOptions {
|
export interface EventFilterOptions {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
import { VariantTag } from "@fabric/core";
|
import type { VariantTag } from "@fabric/core";
|
||||||
import { UUID } from "../types/uuid.js";
|
import type { UUID } from "../types/uuid.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event is a tagged variant with a payload and a timestamp.
|
* An event is a tagged variant with a payload and a timestamp.
|
||||||
@ -14,5 +14,5 @@ export interface Event<TTag extends string = string, TPayload = any> {
|
|||||||
|
|
||||||
export type EventFromKey<
|
export type EventFromKey<
|
||||||
TEvents extends Event,
|
TEvents extends Event,
|
||||||
TKey extends TEvents[VariantTag],
|
TKey extends TEvents[VariantTag]
|
||||||
> = Extract<TEvents, { [VariantTag]: TKey }>;
|
> = Extract<TEvents, { [VariantTag]: TKey }>;
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./event-store.js";
|
export * from "./event-store.ts";
|
||||||
export * from "./event.js";
|
export * from "./event.ts";
|
||||||
export * from "./stored-event.js";
|
export * from "./stored-event.ts";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { PosixDate } from "@fabric/core";
|
import type { PosixDate } from "@fabric/core";
|
||||||
import { Event } from "./event.js";
|
import type { Event } from "./event.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A stored event is an inmutable event, already stored, with it's version in the stream and timestamp.
|
* A stored event is an inmutable event, already stored, with it's version in the stream and timestamp.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { MimeType } from "./mime-type.js";
|
import type { MimeType } from "./mime-type.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a file. Its the base type for all files.
|
* Represents a file. Its the base type for all files.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { ImageMimeType } from "./mime-type.js";
|
import type { ImageMimeType } from "./mime-type.ts";
|
||||||
import { StoredFile } from "./stored-file.js";
|
import type { StoredFile } from "./stored-file.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an image file.
|
* Represents an image file.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Base64String } from "../types/base-64.js";
|
import type { Base64String } from "../types/base-64.ts";
|
||||||
import { BaseFile } from "./base-file.js";
|
import type { BaseFile } from "./base-file.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a file with its contents in memory.
|
* Represents a file with its contents in memory.
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
export * from "./base-file.js";
|
export * from "./base-file.ts";
|
||||||
export * from "./bytes.js";
|
export * from "./bytes.ts";
|
||||||
export * from "./image-file.js";
|
export * from "./image-file.ts";
|
||||||
export * from "./in-memory-file.js";
|
export * from "./in-memory-file.ts";
|
||||||
export * from "./invalid-file-type-error.js";
|
export * from "./invalid-file-type-error.ts";
|
||||||
export * from "./is-in-memory-file.js";
|
export * from "./is-in-memory-file.ts";
|
||||||
export * from "./is-mime-type.js";
|
export * from "./is-mime-type.ts";
|
||||||
export * from "./media-file.js";
|
export * from "./media-file.ts";
|
||||||
export * from "./mime-type.js";
|
export * from "./mime-type.ts";
|
||||||
export * from "./stored-file.js";
|
export * from "./stored-file.ts";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { isRecord } from "@fabric/core";
|
import { isRecord } from "@fabric/core";
|
||||||
import { InMemoryFile } from "./in-memory-file.js";
|
import type { InMemoryFile } from "./in-memory-file.ts";
|
||||||
|
|
||||||
export function isInMemoryFile(value: unknown): value is InMemoryFile {
|
export function isInMemoryFile(value: unknown): value is InMemoryFile {
|
||||||
try {
|
try {
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { describe, expect, expectTypeOf, it } from "vitest";
|
import { describe, expect, expectTypeOf, test } from "@fabric/testing";
|
||||||
import { isMimeType } from "./is-mime-type.js";
|
import { isMimeType } from "./is-mime-type.ts";
|
||||||
|
|
||||||
describe("isMimeType", () => {
|
describe("isMimeType", () => {
|
||||||
it("should return true if the file type is the same as the mime type", () => {
|
test("should return true if the file type is the same as the mime type", () => {
|
||||||
const fileType = "image/png" as string;
|
const fileType = "image/png" as string;
|
||||||
const result = isMimeType("image/.*", fileType);
|
const result = isMimeType("image/.*", fileType);
|
||||||
expect(result).toBe(true);
|
expect(result).toBe(true);
|
||||||
@ -11,7 +11,7 @@ describe("isMimeType", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return false if the file type is not the same as the mime type", () => {
|
test("should return false if the file type is not the same as the mime type", () => {
|
||||||
const fileType = "image/png" as string;
|
const fileType = "image/png" as string;
|
||||||
expect(isMimeType("image/jpeg", fileType)).toBe(false);
|
expect(isMimeType("image/jpeg", fileType)).toBe(false);
|
||||||
|
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import { MimeType } from "./mime-type.js";
|
import type { MimeType } from "./mime-type.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the actual file type is the same as the expected mime type.
|
* Checks if the actual file type is the same as the expected mime type.
|
||||||
*/
|
*/
|
||||||
export function isMimeType<T extends MimeType>(
|
export function isMimeType<T extends MimeType>(
|
||||||
expectedMimeType: T,
|
expectedMimeType: T,
|
||||||
actualMimeType: string,
|
actualMimeType: string
|
||||||
): actualMimeType is T {
|
): actualMimeType is T {
|
||||||
return actualMimeType.match("^" + expectedMimeType + "$") !== null;
|
return actualMimeType.match("^" + expectedMimeType + "$") !== null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { StoredFile } from "./stored-file.js";
|
import type { StoredFile } from "./stored-file.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a media file, either an image, a video or an audio file.
|
* Represents a media file, either an image, a video or an audio file.
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Entity } from "../types/entity.js";
|
import type { Entity } from "../types/entity.ts";
|
||||||
import { BaseFile } from "./base-file.js";
|
import type { BaseFile } from "./base-file.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a file as managed by the domain.
|
* Represents a file as managed by the domain.
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
export * from "./errors/index.js";
|
export * from "./errors/index.ts";
|
||||||
export * from "./events/index.js";
|
export * from "./events/index.ts";
|
||||||
export * from "./files/index.js";
|
export * from "./files/index.ts";
|
||||||
export * from "./models/index.js";
|
export * from "./models/index.ts";
|
||||||
export * from "./security/index.js";
|
export * from "./security/index.ts";
|
||||||
export * from "./services/index.js";
|
export * from "./services/index.ts";
|
||||||
export * from "./types/index.js";
|
export * from "./types/index.ts";
|
||||||
export * from "./use-case/index.js";
|
export * from "./use-case/index.ts";
|
||||||
export * from "./utils/index.js";
|
export * from "./utils/index.ts";
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./services/mocks.js";
|
export * from "./services/mocks.ts";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
export interface DecimalFieldOptions extends BaseField {
|
export interface DecimalFieldOptions extends BaseField {
|
||||||
isUnsigned?: boolean;
|
isUnsigned?: boolean;
|
||||||
@ -12,7 +12,7 @@ export interface DecimalField
|
|||||||
DecimalFieldOptions {}
|
DecimalFieldOptions {}
|
||||||
|
|
||||||
export function createDecimalField<T extends DecimalFieldOptions>(
|
export function createDecimalField<T extends DecimalFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): DecimalField & T {
|
): DecimalField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "DecimalField",
|
[VariantTag]: "DecimalField",
|
||||||
|
|||||||
@ -1,8 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type, @typescript-eslint/no-unused-vars
|
|
||||||
export interface EmbeddedFieldOptions<T = any> extends BaseField {}
|
export interface EmbeddedFieldOptions<T = any> extends BaseField {}
|
||||||
|
|
||||||
export interface EmbeddedField<T = any>
|
export interface EmbeddedField<T = any>
|
||||||
@ -11,7 +10,7 @@ export interface EmbeddedField<T = any>
|
|||||||
|
|
||||||
export function createEmbeddedField<
|
export function createEmbeddedField<
|
||||||
K = any,
|
K = any,
|
||||||
T extends EmbeddedFieldOptions<K> = EmbeddedFieldOptions<K>,
|
T extends EmbeddedFieldOptions<K> = EmbeddedFieldOptions<K>
|
||||||
>(opts: T = {} as T): EmbeddedField & T {
|
>(opts: T = {} as T): EmbeddedField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "EmbeddedField",
|
[VariantTag]: "EmbeddedField",
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
import { PosixDate } from "@fabric/core";
|
import type { PosixDate } from "@fabric/core";
|
||||||
import { Decimal } from "decimal.js";
|
import type Decimal from "jsr:@quentinadam/decimal";
|
||||||
import { UUID } from "../../types/uuid.js";
|
import type { UUID } from "../../types/uuid.ts";
|
||||||
import { DecimalField } from "./decimal.js";
|
import type { DecimalField } from "./decimal.ts";
|
||||||
import { EmbeddedField } from "./embedded.js";
|
import type { EmbeddedField } from "./embedded.ts";
|
||||||
import { FloatField } from "./float.js";
|
import type { FloatField } from "./float.ts";
|
||||||
import { IntegerField } from "./integer.js";
|
import type { IntegerField } from "./integer.ts";
|
||||||
import { ReferenceField } from "./reference-field.js";
|
import type { ReferenceField } from "./reference-field.ts";
|
||||||
import { StringField } from "./string-field.js";
|
import type { StringField } from "./string-field.ts";
|
||||||
import { TimestampField } from "./timestamp.js";
|
import type { TimestampField } from "./timestamp.ts";
|
||||||
import { UUIDField } from "./uuid-field.js";
|
import type { UUIDField } from "./uuid-field.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts a field definition to its corresponding TypeScript type.
|
* Converts a field definition to its corresponding TypeScript type.
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
||||||
export interface FloatFieldOptions extends BaseField {}
|
export interface FloatFieldOptions extends BaseField {}
|
||||||
|
|
||||||
export interface FloatField
|
export interface FloatField
|
||||||
@ -9,7 +8,7 @@ export interface FloatField
|
|||||||
FloatFieldOptions {}
|
FloatFieldOptions {}
|
||||||
|
|
||||||
export function createFloatField<T extends FloatFieldOptions>(
|
export function createFloatField<T extends FloatFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): FloatField & T {
|
): FloatField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "FloatField",
|
[VariantTag]: "FloatField",
|
||||||
|
|||||||
@ -1,14 +1,17 @@
|
|||||||
import { createDecimalField, DecimalField } from "./decimal.js";
|
import { createDecimalField, type DecimalField } from "./decimal.ts";
|
||||||
import { createEmbeddedField, EmbeddedField } from "./embedded.js";
|
import { createEmbeddedField, type EmbeddedField } from "./embedded.ts";
|
||||||
import { createFloatField, FloatField } from "./float.js";
|
import { createFloatField, type FloatField } from "./float.ts";
|
||||||
import { createIntegerField, IntegerField } from "./integer.js";
|
import { createIntegerField, type IntegerField } from "./integer.ts";
|
||||||
import { createReferenceField, ReferenceField } from "./reference-field.js";
|
import {
|
||||||
import { createStringField, StringField } from "./string-field.js";
|
createReferenceField,
|
||||||
import { createTimestampField, TimestampField } from "./timestamp.js";
|
type ReferenceField,
|
||||||
import { createUUIDField, UUIDField } from "./uuid-field.js";
|
} from "./reference-field.ts";
|
||||||
export * from "./base-field.js";
|
import { createStringField, type StringField } from "./string-field.ts";
|
||||||
export * from "./field-to-type.js";
|
import { createTimestampField, type TimestampField } from "./timestamp.ts";
|
||||||
export * from "./reference-field.js";
|
import { createUUIDField, type UUIDField } from "./uuid-field.ts";
|
||||||
|
export * from "./base-field.ts";
|
||||||
|
export * from "./field-to-type.ts";
|
||||||
|
export * from "./reference-field.ts";
|
||||||
|
|
||||||
export type FieldDefinition =
|
export type FieldDefinition =
|
||||||
| StringField
|
| StringField
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
export interface IntegerFieldOptions extends BaseField {
|
export interface IntegerFieldOptions extends BaseField {
|
||||||
isUnsigned?: boolean;
|
isUnsigned?: boolean;
|
||||||
@ -11,7 +11,7 @@ export interface IntegerField
|
|||||||
IntegerFieldOptions {}
|
IntegerFieldOptions {}
|
||||||
|
|
||||||
export function createIntegerField<T extends IntegerFieldOptions>(
|
export function createIntegerField<T extends IntegerFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): IntegerField & T {
|
): IntegerField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "IntegerField",
|
[VariantTag]: "IntegerField",
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { isError } from "@fabric/core";
|
import { isError } from "@fabric/core";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, test } from "@fabric/testing";
|
||||||
import { defineModel } from "../model.js";
|
import { defineModel } from "../model.ts";
|
||||||
import { Field } from "./index.js";
|
import { Field } from "./index.ts";
|
||||||
import {
|
import {
|
||||||
InvalidReferenceFieldError,
|
InvalidReferenceFieldError,
|
||||||
validateReferenceField,
|
validateReferenceField,
|
||||||
} from "./reference-field.js";
|
} from "./reference-field.ts";
|
||||||
|
|
||||||
describe("Validate Reference Field", () => {
|
describe("Validate Reference Field", () => {
|
||||||
const schema = {
|
const schema = {
|
||||||
@ -20,57 +20,57 @@ describe("Validate Reference Field", () => {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
it("should return an error when the target model is not in the schema", () => {
|
test("should return an error when the target model is not in the schema", () => {
|
||||||
const result = validateReferenceField(
|
const result = validateReferenceField(
|
||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "foo",
|
targetModel: "foo",
|
||||||
}),
|
})
|
||||||
).unwrapErrorOrThrow();
|
).unwrapErrorOrThrow();
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not return an error if the target model is in the schema", () => {
|
test("should not return an error if the target model is in the schema", () => {
|
||||||
validateReferenceField(
|
validateReferenceField(
|
||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
}),
|
})
|
||||||
).unwrapOrThrow();
|
).unwrapOrThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return an error if the target key is not in the target model", () => {
|
test("should return an error if the target key is not in the target model", () => {
|
||||||
const result = validateReferenceField(
|
const result = validateReferenceField(
|
||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
targetKey: "foo",
|
targetKey: "foo",
|
||||||
}),
|
})
|
||||||
).unwrapErrorOrThrow();
|
).unwrapErrorOrThrow();
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return error if the target key is not unique", () => {
|
test("should return error if the target key is not unique", () => {
|
||||||
const result = validateReferenceField(
|
const result = validateReferenceField(
|
||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
targetKey: "otherNotUnique",
|
targetKey: "otherNotUnique",
|
||||||
}),
|
})
|
||||||
).unwrapErrorOrThrow();
|
).unwrapErrorOrThrow();
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not return an error if the target key is in the target model and is unique", () => {
|
test("should not return an error if the target key is in the target model and is unique", () => {
|
||||||
const result = validateReferenceField(
|
const result = validateReferenceField(
|
||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
targetKey: "otherUnique",
|
targetKey: "otherUnique",
|
||||||
}),
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isError(result)) {
|
if (isError(result)) {
|
||||||
@ -1,6 +1,11 @@
|
|||||||
import { Result, TaggedError, TaggedVariant, VariantTag } from "@fabric/core";
|
import {
|
||||||
import { ModelSchema } from "../model-schema.js";
|
Result,
|
||||||
import { BaseField } from "./base-field.js";
|
TaggedError,
|
||||||
|
type TaggedVariant,
|
||||||
|
VariantTag,
|
||||||
|
} from "@fabric/core";
|
||||||
|
import type { ModelSchema } from "../model-schema.ts";
|
||||||
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
export interface ReferenceFieldOptions extends BaseField {
|
export interface ReferenceFieldOptions extends BaseField {
|
||||||
targetModel: string;
|
targetModel: string;
|
||||||
@ -12,7 +17,7 @@ export interface ReferenceField
|
|||||||
ReferenceFieldOptions {}
|
ReferenceFieldOptions {}
|
||||||
|
|
||||||
export function createReferenceField<T extends ReferenceFieldOptions>(
|
export function createReferenceField<T extends ReferenceFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): ReferenceField & T {
|
): ReferenceField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "ReferenceField",
|
[VariantTag]: "ReferenceField",
|
||||||
@ -26,32 +31,32 @@ export function getTargetKey(field: ReferenceField): string {
|
|||||||
|
|
||||||
export function validateReferenceField(
|
export function validateReferenceField(
|
||||||
schema: ModelSchema,
|
schema: ModelSchema,
|
||||||
field: ReferenceField,
|
field: ReferenceField
|
||||||
): Result<void, InvalidReferenceFieldError> {
|
): Result<void, InvalidReferenceFieldError> {
|
||||||
if (!schema[field.targetModel]) {
|
if (!schema[field.targetModel]) {
|
||||||
return Result.failWith(
|
return Result.failWith(
|
||||||
new InvalidReferenceFieldError(
|
new InvalidReferenceFieldError(
|
||||||
`The target model '${field.targetModel}' is not in the schema.`,
|
`The target model '${field.targetModel}' is not in the schema.`
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (field.targetKey && !schema[field.targetModel].fields[field.targetKey]) {
|
if (field.targetKey && !schema[field.targetModel]!.fields[field.targetKey]) {
|
||||||
return Result.failWith(
|
return Result.failWith(
|
||||||
new InvalidReferenceFieldError(
|
new InvalidReferenceFieldError(
|
||||||
`The target key '${field.targetKey}' is not in the target model '${field.targetModel}'.`,
|
`The target key '${field.targetKey}' is not in the target model '${field.targetModel}'.`
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
field.targetKey &&
|
field.targetKey &&
|
||||||
!schema[field.targetModel].fields[field.targetKey].isUnique
|
!schema[field.targetModel]!.fields[field.targetKey]!.isUnique
|
||||||
) {
|
) {
|
||||||
return Result.failWith(
|
return Result.failWith(
|
||||||
new InvalidReferenceFieldError(
|
new InvalidReferenceFieldError(
|
||||||
`The target key '${field.targetModel}'.'${field.targetKey}' is not unique.`,
|
`The target key '${field.targetModel}'.'${field.targetKey}' is not unique.`
|
||||||
),
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
export interface StringFieldOptions extends BaseField {
|
export interface StringFieldOptions extends BaseField {
|
||||||
maxLength?: number;
|
maxLength?: number;
|
||||||
@ -11,7 +11,7 @@ export interface StringField
|
|||||||
StringFieldOptions {}
|
StringFieldOptions {}
|
||||||
|
|
||||||
export function createStringField<T extends StringFieldOptions>(
|
export function createStringField<T extends StringFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): StringField & T {
|
): StringField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "StringField",
|
[VariantTag]: "StringField",
|
||||||
|
|||||||
@ -1,7 +1,6 @@
|
|||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
||||||
export interface TimestampFieldOptions extends BaseField {}
|
export interface TimestampFieldOptions extends BaseField {}
|
||||||
|
|
||||||
export interface TimestampField
|
export interface TimestampField
|
||||||
@ -9,7 +8,7 @@ export interface TimestampField
|
|||||||
TimestampFieldOptions {}
|
TimestampFieldOptions {}
|
||||||
|
|
||||||
export function createTimestampField<T extends TimestampFieldOptions>(
|
export function createTimestampField<T extends TimestampFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): TimestampField & T {
|
): TimestampField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "TimestampField",
|
[VariantTag]: "TimestampField",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { TaggedVariant, VariantTag } from "@fabric/core";
|
import { type TaggedVariant, VariantTag } from "@fabric/core";
|
||||||
import { BaseField } from "./base-field.js";
|
import type { BaseField } from "./base-field.ts";
|
||||||
|
|
||||||
export interface UUIDFieldOptions extends BaseField {
|
export interface UUIDFieldOptions extends BaseField {
|
||||||
isPrimaryKey?: boolean;
|
isPrimaryKey?: boolean;
|
||||||
@ -10,7 +10,7 @@ export interface UUIDField
|
|||||||
UUIDFieldOptions {}
|
UUIDFieldOptions {}
|
||||||
|
|
||||||
export function createUUIDField<T extends UUIDFieldOptions>(
|
export function createUUIDField<T extends UUIDFieldOptions>(
|
||||||
opts: T = {} as T,
|
opts: T = {} as T
|
||||||
): UUIDField & T {
|
): UUIDField & T {
|
||||||
return {
|
return {
|
||||||
[VariantTag]: "UUIDField",
|
[VariantTag]: "UUIDField",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
export * from "./fields/index.js";
|
export * from "./fields/index.ts";
|
||||||
export * from "./model-schema.js";
|
export * from "./model-schema.ts";
|
||||||
export * from "./model.js";
|
export * from "./model.ts";
|
||||||
export * from "./query/index.js";
|
export * from "./query/index.ts";
|
||||||
export * from "./state-store.js";
|
export * from "./state-store.ts";
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { Model } from "./model.js";
|
import type { Model } from "./model.ts";
|
||||||
|
|
||||||
export type ModelSchema = Record<string, Model>;
|
export type ModelSchema = Record<string, Model>;
|
||||||
|
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
import { describe, expectTypeOf, it } from "vitest";
|
import type { PosixDate } from "@fabric/core";
|
||||||
import { UUID } from "../types/uuid.js";
|
import { describe, expectTypeOf, test } from "@fabric/testing";
|
||||||
import { Field } from "./fields/index.js";
|
import type { UUID } from "../types/uuid.ts";
|
||||||
import { defineModel, ModelToType } from "./model.js";
|
import { Field } from "./fields/index.ts";
|
||||||
|
import { defineModel, type ModelToType } from "./model.ts";
|
||||||
|
|
||||||
describe("CreateModel", () => {
|
describe("CreateModel", () => {
|
||||||
it("should create a model and it's interface type", () => {
|
test("should create a model and it's interface type", () => {
|
||||||
const User = defineModel("User", {
|
const User = defineModel("User", {
|
||||||
name: Field.string(),
|
name: Field.string(),
|
||||||
password: Field.string(),
|
password: Field.string(),
|
||||||
@ -19,6 +20,7 @@ describe("CreateModel", () => {
|
|||||||
name: string;
|
name: string;
|
||||||
password: string;
|
password: string;
|
||||||
phone: string | null;
|
phone: string | null;
|
||||||
|
deletedAt: PosixDate | null;
|
||||||
}>();
|
}>();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -1,12 +1,12 @@
|
|||||||
import { Keyof } from "@fabric/core";
|
import type { Keyof } from "@fabric/core";
|
||||||
import { FieldToType } from "./fields/field-to-type.js";
|
import type { FieldToType } from "./fields/field-to-type.ts";
|
||||||
import { Field, FieldDefinition } from "./fields/index.js";
|
import { Field, type FieldDefinition } from "./fields/index.ts";
|
||||||
|
|
||||||
export type CustomModelFields = Record<string, FieldDefinition>;
|
export type CustomModelFields = Record<string, FieldDefinition>;
|
||||||
|
|
||||||
export interface Collection<
|
export interface Collection<
|
||||||
TName extends string = string,
|
TName extends string = string,
|
||||||
TFields extends CustomModelFields = CustomModelFields,
|
TFields extends CustomModelFields = CustomModelFields
|
||||||
> {
|
> {
|
||||||
name: TName;
|
name: TName;
|
||||||
fields: TFields;
|
fields: TFields;
|
||||||
@ -24,14 +24,14 @@ export const DefaultModelFields = {
|
|||||||
|
|
||||||
export interface Model<
|
export interface Model<
|
||||||
TName extends string = string,
|
TName extends string = string,
|
||||||
TFields extends CustomModelFields = CustomModelFields,
|
TFields extends CustomModelFields = CustomModelFields
|
||||||
> extends Collection<TName, TFields> {
|
> extends Collection<TName, TFields> {
|
||||||
fields: typeof DefaultModelFields & TFields;
|
fields: typeof DefaultModelFields & TFields;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function defineModel<
|
export function defineModel<
|
||||||
TName extends string,
|
TName extends string,
|
||||||
TFields extends CustomModelFields,
|
TFields extends CustomModelFields
|
||||||
>(name: TName, fields: TFields): Model<TName, TFields> {
|
>(name: TName, fields: TFields): Model<TName, TFields> {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
@ -41,7 +41,7 @@ export function defineModel<
|
|||||||
|
|
||||||
export function defineCollection<
|
export function defineCollection<
|
||||||
TName extends string,
|
TName extends string,
|
||||||
TFields extends CustomModelFields,
|
TFields extends CustomModelFields
|
||||||
>(name: TName, fields: TFields): Collection<TName, TFields> {
|
>(name: TName, fields: TFields): Collection<TName, TFields> {
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
|
import type { Keyof, TaggedVariant } from "@fabric/core";
|
||||||
import { Keyof, TaggedVariant } from "@fabric/core";
|
|
||||||
|
|
||||||
export type AggregateOptions<T = any> = Record<string, AggregateFn<T>>;
|
export type AggregateOptions<T = any> = Record<string, AggregateFn<T>>;
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
|
|
||||||
export type FilterOptions<T = any> =
|
export type FilterOptions<T = any> =
|
||||||
| SingleFilterOption<T>
|
| SingleFilterOption<T>
|
||||||
|
|||||||
@ -1,3 +1,3 @@
|
|||||||
export * from "./filter-options.js";
|
export * from "./filter-options.ts";
|
||||||
export * from "./order-by-options.js";
|
export * from "./order-by-options.ts";
|
||||||
export * from "./query.js";
|
export * from "./query.ts";
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
import { AsyncResult, Keyof, Optional } from "@fabric/core";
|
import type { AsyncResult, Keyof, Optional } from "@fabric/core";
|
||||||
import { StoreQueryError } from "../../errors/query-error.js";
|
import type { StoreQueryError } from "../../errors/query-error.ts";
|
||||||
import { FilterOptions } from "./filter-options.js";
|
import type { FilterOptions } from "./filter-options.ts";
|
||||||
import { OrderByOptions } from "./order-by-options.js";
|
import type { OrderByOptions } from "./order-by-options.ts";
|
||||||
|
|
||||||
export interface StoreQuery<T> {
|
export interface StoreQuery<T> {
|
||||||
where(where: FilterOptions<T>): StoreSortableQuery<T>;
|
where(where: FilterOptions<T>): StoreSortableQuery<T>;
|
||||||
@ -11,12 +11,12 @@ export interface StoreQuery<T> {
|
|||||||
|
|
||||||
select(): AsyncResult<T[], StoreQueryError>;
|
select(): AsyncResult<T[], StoreQueryError>;
|
||||||
select<K extends Keyof<T>>(
|
select<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
||||||
|
|
||||||
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
||||||
selectOne<K extends Keyof<T>>(
|
selectOne<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,12 +26,12 @@ export interface StoreSortableQuery<T> {
|
|||||||
|
|
||||||
select(): AsyncResult<T[], StoreQueryError>;
|
select(): AsyncResult<T[], StoreQueryError>;
|
||||||
select<K extends Keyof<T>>(
|
select<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
||||||
|
|
||||||
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
||||||
selectOne<K extends Keyof<T>>(
|
selectOne<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,24 +40,24 @@ export interface StoreLimitableQuery<T> {
|
|||||||
|
|
||||||
select(): AsyncResult<T[], StoreQueryError>;
|
select(): AsyncResult<T[], StoreQueryError>;
|
||||||
select<K extends Keyof<T>>(
|
select<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
||||||
|
|
||||||
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
||||||
selectOne<K extends Keyof<T>>(
|
selectOne<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SelectableQuery<T> {
|
export interface SelectableQuery<T> {
|
||||||
select(): AsyncResult<T[], StoreQueryError>;
|
select(): AsyncResult<T[], StoreQueryError>;
|
||||||
select<K extends Keyof<T>>(
|
select<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
): AsyncResult<Pick<T, K>[], StoreQueryError>;
|
||||||
|
|
||||||
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
selectOne(): AsyncResult<Optional<T>, StoreQueryError>;
|
||||||
selectOne<K extends Keyof<T>>(
|
selectOne<K extends Keyof<T>>(
|
||||||
keys: K[],
|
keys: K[]
|
||||||
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
): AsyncResult<Optional<Pick<T, K>>, StoreQueryError>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,12 +1,12 @@
|
|||||||
import { AsyncResult } from "@fabric/core";
|
import type { AsyncResult } from "@fabric/core";
|
||||||
import { StoreQueryError } from "../errors/query-error.js";
|
import type { StoreQueryError } from "../errors/query-error.ts";
|
||||||
import { ModelSchemaFromModels } from "./model-schema.js";
|
import type { ModelSchemaFromModels } from "./model-schema.ts";
|
||||||
import { Model, ModelToType } from "./model.js";
|
import type { Model, ModelToType } from "./model.ts";
|
||||||
import { StoreQuery } from "./query/query.js";
|
import type { StoreQuery } from "./query/query.ts";
|
||||||
|
|
||||||
export interface ReadonlyStateStore<TModel extends Model> {
|
export interface ReadonlyStateStore<TModel extends Model> {
|
||||||
from<T extends keyof ModelSchemaFromModels<TModel>>(
|
from<T extends keyof ModelSchemaFromModels<TModel>>(
|
||||||
collection: T,
|
collection: T
|
||||||
): StoreQuery<ModelToType<ModelSchemaFromModels<TModel>[T]>>;
|
): StoreQuery<ModelToType<ModelSchemaFromModels<TModel>[T]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,6 +14,6 @@ export interface WritableStateStore<TModel extends Model>
|
|||||||
extends ReadonlyStateStore<TModel> {
|
extends ReadonlyStateStore<TModel> {
|
||||||
insertInto<T extends keyof ModelSchemaFromModels<TModel>>(
|
insertInto<T extends keyof ModelSchemaFromModels<TModel>>(
|
||||||
collection: T,
|
collection: T,
|
||||||
record: ModelToType<ModelSchemaFromModels<TModel>[T]>,
|
record: ModelToType<ModelSchemaFromModels<TModel>[T]>
|
||||||
): AsyncResult<void, StoreQueryError>;
|
): AsyncResult<void, StoreQueryError>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import { VariantTag } from "@fabric/core";
|
import type { VariantTag } from "@fabric/core";
|
||||||
import { Event } from "../events/event.js";
|
import type { Event } from "../events/event.ts";
|
||||||
import { StoredEvent } from "../events/stored-event.js";
|
import type { StoredEvent } from "../events/stored-event.ts";
|
||||||
import { Model, ModelToType } from "../models/model.js";
|
import type { Model, ModelToType } from "../models/model.ts";
|
||||||
|
|
||||||
export interface Projection<TModel extends Model, TEvents extends Event> {
|
export interface Projection<TModel extends Model, TEvents extends Event> {
|
||||||
model: TModel;
|
model: TModel;
|
||||||
events: TEvents[VariantTag][];
|
events: TEvents[VariantTag][];
|
||||||
projection: (
|
projection: (
|
||||||
event: StoredEvent<TEvents>,
|
event: StoredEvent<TEvents>,
|
||||||
model?: ModelToType<TModel>,
|
model?: ModelToType<TModel>
|
||||||
) => ModelToType<TModel>;
|
) => ModelToType<TModel>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./policy.js";
|
export * from "./policy.ts";
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./uuid-generator.js";
|
export * from "./uuid-generator.ts";
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
export * from "./uuid-generator.mock.js";
|
export * from "./uuid-generator.mock.ts";
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { UUID } from "../types/uuid.js";
|
import type { UUID } from "../types/uuid.ts";
|
||||||
import { UUIDGenerator } from "./uuid-generator.js";
|
import type { UUIDGenerator } from "./uuid-generator.ts";
|
||||||
|
|
||||||
export const UUIDGeneratorMock: UUIDGenerator = {
|
export const UUIDGeneratorMock: UUIDGenerator = {
|
||||||
generate(): UUID {
|
generate(): UUID {
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { UUID } from "../types/uuid.js";
|
import type { UUID } from "../types/uuid.ts";
|
||||||
|
|
||||||
export interface UUIDGenerator {
|
export interface UUIDGenerator {
|
||||||
generate(): UUID;
|
generate(): UUID;
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
export { Decimal } from "decimal.js";
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { UUID } from "./uuid.js";
|
import type { UUID } from "./uuid.ts";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An entity is a domain object that is defined by its identity.
|
* An entity is a domain object that is defined by its identity.
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
export * from "./base-64.js";
|
export * from "./base-64.ts";
|
||||||
export * from "./decimal.js";
|
export * from "./email.ts";
|
||||||
export * from "./email.js";
|
export * from "./entity.ts";
|
||||||
export * from "./entity.js";
|
export * from "./semver.ts";
|
||||||
export * from "./semver.js";
|
export * from "./uuid.ts";
|
||||||
export * from "./uuid.js";
|
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./use-case-definition.js";
|
export * from "./use-case-definition.ts";
|
||||||
export * from "./use-case.js";
|
export * from "./use-case.ts";
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
// deno-lint-ignore-file no-explicit-any
|
||||||
import { TaggedError } from "@fabric/core";
|
import type { TaggedError } from "@fabric/core";
|
||||||
import { UseCase } from "./use-case.js";
|
import type { UseCase } from "./use-case.ts";
|
||||||
|
|
||||||
export type UseCaseDefinition<
|
export type UseCaseDefinition<
|
||||||
TDependencies = any,
|
TDependencies = any,
|
||||||
TPayload = any,
|
TPayload = any,
|
||||||
TOutput = any,
|
TOutput = any,
|
||||||
TErrors extends TaggedError<string> = any,
|
TErrors extends TaggedError<string> = any
|
||||||
> = BasicUseCaseDefinition<TDependencies, TPayload, TOutput, TErrors>;
|
> = BasicUseCaseDefinition<TDependencies, TPayload, TOutput, TErrors>;
|
||||||
|
|
||||||
interface BasicUseCaseDefinition<
|
interface BasicUseCaseDefinition<
|
||||||
TDependencies,
|
TDependencies,
|
||||||
TPayload,
|
TPayload,
|
||||||
TOutput,
|
TOutput,
|
||||||
TErrors extends TaggedError<string>,
|
TErrors extends TaggedError<string>
|
||||||
> {
|
> {
|
||||||
/**
|
/**
|
||||||
* The use case name.
|
* The use case name.
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { AsyncResult, TaggedError } from "@fabric/core";
|
import type { AsyncResult, TaggedError } from "@fabric/core";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A use case is a piece of domain logic that can be executed.
|
* A use case is a piece of domain logic that can be executed.
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from "./json-utils.js";
|
export * from "./json-utils.ts";
|
||||||
export * from "./sort-by-dependencies.js";
|
export * from "./sort-by-dependencies.ts";
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
import { expect } from "@std/expect";
|
||||||
import { describe, expect, it } from "vitest";
|
import { describe, it } from "@std/testing/bdd";
|
||||||
import { CircularDependencyError } from "../errors/circular-dependency-error.js";
|
import { CircularDependencyError } from "../errors/circular-dependency-error.ts";
|
||||||
import { sortByDependencies } from "./sort-by-dependencies.js";
|
import { sortByDependencies } from "./sort-by-dependencies.ts";
|
||||||
|
|
||||||
describe("sortByDependencies", () => {
|
describe("sortByDependencies", () => {
|
||||||
it("should sort an array of objects by their dependencies", () => {
|
it("should sort an array of objects by their dependencies", () => {
|
||||||
@ -35,11 +35,12 @@ describe("sortByDependencies", () => {
|
|||||||
sortByDependencies(array, {
|
sortByDependencies(array, {
|
||||||
keyGetter: (element) => element.name,
|
keyGetter: (element) => element.name,
|
||||||
depGetter: (element) => element.dependencies,
|
depGetter: (element) => element.dependencies,
|
||||||
}).unwrapErrorOrThrow(),
|
}).unwrapErrorOrThrow()
|
||||||
).toBeInstanceOf(CircularDependencyError);
|
).toBeInstanceOf(CircularDependencyError);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should return an empty array when the input array is empty", () => {
|
it("should return an empty array when the input array is empty", () => {
|
||||||
|
// deno-lint-ignore no-explicit-any
|
||||||
const array: any[] = [];
|
const array: any[] = [];
|
||||||
|
|
||||||
const result = sortByDependencies(array, {
|
const result = sortByDependencies(array, {
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user