Format files and configure workspace formatting in vscode
This commit is contained in:
parent
a79683d9d4
commit
4cc99aec91
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -17,5 +17,8 @@
|
|||||||
},
|
},
|
||||||
"typescript.preferences.importModuleSpecifierEnding": "js",
|
"typescript.preferences.importModuleSpecifierEnding": "js",
|
||||||
"cSpell.words": ["autodocs", "Syntropy"],
|
"cSpell.words": ["autodocs", "Syntropy"],
|
||||||
"typescript.preferences.autoImportFileExcludePatterns": ["**/chai/**"]
|
"typescript.preferences.autoImportFileExcludePatterns": ["**/chai/**"],
|
||||||
|
"[typescript]": {
|
||||||
|
"editor.defaultFormatter": "denoland.vscode-deno"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,17 @@
|
|||||||
/**
|
/**
|
||||||
* Get the element type of an array.
|
* Get the element type of an array.
|
||||||
*/
|
*/
|
||||||
export type ArrayElement<T extends readonly unknown[]> =
|
export type ArrayElement<T extends readonly unknown[]> = T extends
|
||||||
T extends readonly (infer U)[] ? U : never;
|
readonly (infer U)[] ? U : never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the first element type of a tuple.
|
* Get the first element type of a tuple.
|
||||||
*/
|
*/
|
||||||
export type TupleFirstElement<T extends readonly unknown[]> =
|
export type TupleFirstElement<T extends readonly unknown[]> = T extends
|
||||||
T extends readonly [infer U, ...unknown[]] ? U : never;
|
readonly [infer U, ...unknown[]] ? U : never;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the LAST element type of a tuple.
|
* Get the LAST element type of a tuple.
|
||||||
*/
|
*/
|
||||||
export type TupleLastElement<T extends readonly unknown[]> =
|
export type TupleLastElement<T extends readonly unknown[]> = T extends
|
||||||
T extends readonly [...unknown[], infer U] ? U : never;
|
readonly [...unknown[], infer U] ? U : never;
|
||||||
|
|||||||
@ -3,10 +3,8 @@ 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.
|
||||||
*/
|
*/
|
||||||
export abstract class TaggedError<Tag extends string = string>
|
export abstract class TaggedError<Tag extends string = string> extends Error
|
||||||
extends Error
|
implements TaggedVariant<Tag> {
|
||||||
implements TaggedVariant<Tag>
|
|
||||||
{
|
|
||||||
readonly [VariantTag]: Tag;
|
readonly [VariantTag]: Tag;
|
||||||
|
|
||||||
constructor(tag: Tag, message?: string) {
|
constructor(tag: Tag, message?: string) {
|
||||||
|
|||||||
@ -10,13 +10,13 @@ import { Result } from "./result.ts";
|
|||||||
*/
|
*/
|
||||||
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());
|
||||||
|
|||||||
@ -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));
|
||||||
|
|||||||
@ -10,7 +10,7 @@ describe("Run", () => {
|
|||||||
const result = await Run.seq(
|
const result = await Run.seq(
|
||||||
async () => Result.succeedWith(1),
|
async () => Result.succeedWith(1),
|
||||||
async (x) => Result.succeedWith(x + 1),
|
async (x) => Result.succeedWith(x + 1),
|
||||||
async (x) => Result.succeedWith(x * 2)
|
async (x) => Result.succeedWith(x * 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.unwrapOrThrow()).toEqual(4);
|
expect(result.unwrapOrThrow()).toEqual(4);
|
||||||
@ -20,7 +20,7 @@ describe("Run", () => {
|
|||||||
const result = await Run.seq(
|
const result = await Run.seq(
|
||||||
async () => Result.succeedWith(1),
|
async () => Result.succeedWith(1),
|
||||||
async () => Result.failWith(new UnexpectedError()),
|
async () => Result.failWith(new UnexpectedError()),
|
||||||
async (x) => Result.succeedWith(x * 2)
|
async (x) => Result.succeedWith(x * 2),
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.isError()).toBe(true);
|
expect(result.isError()).toBe(true);
|
||||||
|
|||||||
@ -5,17 +5,22 @@ import type { AsyncResult } from "../result/async-result.ts";
|
|||||||
export namespace Run {
|
export namespace Run {
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export async function seq<
|
export async function seq<
|
||||||
T1, TE1 extends TaggedError,
|
T1,
|
||||||
T2, TE2 extends TaggedError,
|
TE1 extends TaggedError,
|
||||||
|
T2,
|
||||||
|
TE2 extends TaggedError,
|
||||||
>(
|
>(
|
||||||
fn1: () => AsyncResult<T1, TE1>,
|
fn1: () => AsyncResult<T1, TE1>,
|
||||||
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
||||||
): AsyncResult<T2, TE1 | TE2>;
|
): AsyncResult<T2, TE1 | TE2>;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export async function seq<
|
export async function seq<
|
||||||
T1, TE1 extends TaggedError,
|
T1,
|
||||||
T2, TE2 extends TaggedError,
|
TE1 extends TaggedError,
|
||||||
T3, TE3 extends TaggedError,
|
T2,
|
||||||
|
TE2 extends TaggedError,
|
||||||
|
T3,
|
||||||
|
TE3 extends TaggedError,
|
||||||
>(
|
>(
|
||||||
fn1: () => AsyncResult<T1, TE1>,
|
fn1: () => AsyncResult<T1, TE1>,
|
||||||
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
||||||
@ -23,10 +28,14 @@ export namespace Run {
|
|||||||
): AsyncResult<T3, TE1 | TE2 | TE3>;
|
): AsyncResult<T3, TE1 | TE2 | TE3>;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export async function seq<
|
export async function seq<
|
||||||
T1, TE1 extends TaggedError,
|
T1,
|
||||||
T2, TE2 extends TaggedError,
|
TE1 extends TaggedError,
|
||||||
T3, TE3 extends TaggedError,
|
T2,
|
||||||
T4, TE4 extends TaggedError,
|
TE2 extends TaggedError,
|
||||||
|
T3,
|
||||||
|
TE3 extends TaggedError,
|
||||||
|
T4,
|
||||||
|
TE4 extends TaggedError,
|
||||||
>(
|
>(
|
||||||
fn1: () => AsyncResult<T1, TE1>,
|
fn1: () => AsyncResult<T1, TE1>,
|
||||||
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
||||||
@ -51,17 +60,22 @@ export namespace Run {
|
|||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export async function seqUNSAFE<
|
export async function seqUNSAFE<
|
||||||
T1, TE1 extends TaggedError,
|
T1,
|
||||||
T2, TE2 extends TaggedError,
|
TE1 extends TaggedError,
|
||||||
|
T2,
|
||||||
|
TE2 extends TaggedError,
|
||||||
>(
|
>(
|
||||||
fn1: () => AsyncResult<T1, TE1>,
|
fn1: () => AsyncResult<T1, TE1>,
|
||||||
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
||||||
): Promise<T2>;
|
): Promise<T2>;
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
export async function seqUNSAFE<
|
export async function seqUNSAFE<
|
||||||
T1,TE1 extends TaggedError,
|
T1,
|
||||||
T2,TE2 extends TaggedError,
|
TE1 extends TaggedError,
|
||||||
T3,TE3 extends TaggedError,
|
T2,
|
||||||
|
TE2 extends TaggedError,
|
||||||
|
T3,
|
||||||
|
TE3 extends TaggedError,
|
||||||
>(
|
>(
|
||||||
fn1: () => AsyncResult<T1, TE1>,
|
fn1: () => AsyncResult<T1, TE1>,
|
||||||
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
fn2: (value: T1) => AsyncResult<T2, TE2>,
|
||||||
@ -80,7 +94,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();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,8 +22,9 @@ export class PosixDate {
|
|||||||
"timestamp" in value &&
|
"timestamp" in value &&
|
||||||
value["type"] === "posix-date" &&
|
value["type"] === "posix-date" &&
|
||||||
typeof value["timestamp"] === "number"
|
typeof value["timestamp"] === "number"
|
||||||
)
|
) {
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,5 +31,5 @@ Deno.test(
|
|||||||
V2: (v) => v.b,
|
V2: (v) => v.b,
|
||||||
})
|
})
|
||||||
).toThrow("Non-exhaustive pattern match");
|
).toThrow("Non-exhaustive pattern match");
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,12 +1,16 @@
|
|||||||
import type { Fn } from "../types/fn.ts";
|
import type { Fn } from "../types/fn.ts";
|
||||||
import { type TaggedVariant, type VariantFromTag, VariantTag } from "./variant.ts";
|
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 +18,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,6 +1,7 @@
|
|||||||
import { TaggedError } from "@fabric/core";
|
import { TaggedError } from "@fabric/core";
|
||||||
|
|
||||||
export class CircularDependencyError extends TaggedError<"CircularDependencyError"> {
|
export class CircularDependencyError
|
||||||
|
extends TaggedError<"CircularDependencyError"> {
|
||||||
context: { key: string; dep: string };
|
context: { key: string; dep: string };
|
||||||
constructor(key: string, dep: string) {
|
constructor(key: string, dep: string) {
|
||||||
super("CircularDependencyError");
|
super("CircularDependencyError");
|
||||||
|
|||||||
@ -15,21 +15,21 @@ 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 {
|
||||||
|
|||||||
@ -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 }>;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import type { MimeType } from "./mime-type.ts";
|
|||||||
*/
|
*/
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,11 +8,10 @@ export interface DecimalFieldOptions extends BaseField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface DecimalField
|
export interface DecimalField
|
||||||
extends TaggedVariant<"DecimalField">,
|
extends TaggedVariant<"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",
|
||||||
|
|||||||
@ -5,12 +5,11 @@ import type { BaseField } from "./base-field.ts";
|
|||||||
export interface EmbeddedFieldOptions<T = any> extends BaseField {}
|
export interface EmbeddedFieldOptions<T = any> extends BaseField {}
|
||||||
|
|
||||||
export interface EmbeddedField<T = any>
|
export interface EmbeddedField<T = any>
|
||||||
extends TaggedVariant<"EmbeddedField">,
|
extends TaggedVariant<"EmbeddedField">, EmbeddedFieldOptions<T> {}
|
||||||
EmbeddedFieldOptions<T> {}
|
|
||||||
|
|
||||||
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",
|
||||||
|
|||||||
@ -14,20 +14,21 @@ 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.
|
||||||
*/
|
*/
|
||||||
//prettier-ignore
|
//prettier-ignore
|
||||||
export type FieldToType<TField> =
|
export type FieldToType<TField> = TField extends StringField
|
||||||
TField extends StringField ? MaybeOptional<TField, string>
|
? MaybeOptional<TField, string>
|
||||||
: TField extends UUIDField ? MaybeOptional<TField, UUID>
|
: TField extends UUIDField ? MaybeOptional<TField, UUID>
|
||||||
: TField extends IntegerField ? IntegerFieldToType<TField>
|
: TField extends IntegerField ? IntegerFieldToType<TField>
|
||||||
: TField extends ReferenceField ? MaybeOptional<TField, UUID>
|
: TField extends ReferenceField ? MaybeOptional<TField, UUID>
|
||||||
: TField extends DecimalField ? MaybeOptional<TField, Decimal>
|
: TField extends DecimalField ? MaybeOptional<TField, Decimal>
|
||||||
: TField extends FloatField ? MaybeOptional<TField, number>
|
: TField extends FloatField ? MaybeOptional<TField, number>
|
||||||
: TField extends TimestampField ? MaybeOptional<TField, PosixDate>
|
: TField extends TimestampField ? MaybeOptional<TField, PosixDate>
|
||||||
: TField extends EmbeddedField<infer TSubModel> ? MaybeOptional<TField, TSubModel>
|
: TField extends EmbeddedField<infer TSubModel>
|
||||||
|
? MaybeOptional<TField, TSubModel>
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
//prettier-ignore
|
//prettier-ignore
|
||||||
type IntegerFieldToType<TField extends IntegerField> = TField["hasArbitraryPrecision"] extends true
|
type IntegerFieldToType<TField extends IntegerField> =
|
||||||
? MaybeOptional<TField, bigint>
|
TField["hasArbitraryPrecision"] extends true ? MaybeOptional<TField, bigint>
|
||||||
: TField["hasArbitraryPrecision"] extends false
|
: TField["hasArbitraryPrecision"] extends false
|
||||||
? MaybeOptional<TField, number>
|
? MaybeOptional<TField, number>
|
||||||
: MaybeOptional<TField, number | bigint>;
|
: MaybeOptional<TField, number | bigint>;
|
||||||
|
|||||||
@ -4,11 +4,10 @@ import type { BaseField } from "./base-field.ts";
|
|||||||
export interface FloatFieldOptions extends BaseField {}
|
export interface FloatFieldOptions extends BaseField {}
|
||||||
|
|
||||||
export interface FloatField
|
export interface FloatField
|
||||||
extends TaggedVariant<"FloatField">,
|
extends TaggedVariant<"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",
|
||||||
|
|||||||
@ -7,11 +7,10 @@ export interface IntegerFieldOptions extends BaseField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface IntegerField
|
export interface IntegerField
|
||||||
extends TaggedVariant<"IntegerField">,
|
extends TaggedVariant<"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",
|
||||||
|
|||||||
@ -25,7 +25,7 @@ describe("Validate Reference Field", () => {
|
|||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "foo",
|
targetModel: "foo",
|
||||||
})
|
}),
|
||||||
).unwrapErrorOrThrow();
|
).unwrapErrorOrThrow();
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
||||||
@ -36,7 +36,7 @@ describe("Validate Reference Field", () => {
|
|||||||
schema,
|
schema,
|
||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
})
|
}),
|
||||||
).unwrapOrThrow();
|
).unwrapOrThrow();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -46,7 +46,7 @@ describe("Validate Reference Field", () => {
|
|||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
targetKey: "foo",
|
targetKey: "foo",
|
||||||
})
|
}),
|
||||||
).unwrapErrorOrThrow();
|
).unwrapErrorOrThrow();
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
||||||
@ -58,7 +58,7 @@ describe("Validate Reference Field", () => {
|
|||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
targetKey: "otherNotUnique",
|
targetKey: "otherNotUnique",
|
||||||
})
|
}),
|
||||||
).unwrapErrorOrThrow();
|
).unwrapErrorOrThrow();
|
||||||
|
|
||||||
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
expect(result).toBeInstanceOf(InvalidReferenceFieldError);
|
||||||
@ -70,7 +70,7 @@ describe("Validate Reference Field", () => {
|
|||||||
Field.reference({
|
Field.reference({
|
||||||
targetModel: "User",
|
targetModel: "User",
|
||||||
targetKey: "otherUnique",
|
targetKey: "otherUnique",
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isError(result)) {
|
if (isError(result)) {
|
||||||
|
|||||||
@ -13,11 +13,10 @@ export interface ReferenceFieldOptions extends BaseField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface ReferenceField
|
export interface ReferenceField
|
||||||
extends TaggedVariant<"ReferenceField">,
|
extends TaggedVariant<"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",
|
||||||
@ -31,21 +30,21 @@ 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}'.`,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,15 +54,16 @@ export function validateReferenceField(
|
|||||||
) {
|
) {
|
||||||
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.`,
|
||||||
)
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result.ok();
|
return Result.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
export class InvalidReferenceFieldError extends TaggedError<"InvalidReferenceField"> {
|
export class InvalidReferenceFieldError
|
||||||
|
extends TaggedError<"InvalidReferenceField"> {
|
||||||
constructor(readonly reason: string) {
|
constructor(readonly reason: string) {
|
||||||
super("InvalidReferenceField");
|
super("InvalidReferenceField");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,11 +7,10 @@ export interface StringFieldOptions extends BaseField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface StringField
|
export interface StringField
|
||||||
extends TaggedVariant<"StringField">,
|
extends TaggedVariant<"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",
|
||||||
|
|||||||
@ -4,11 +4,10 @@ import type { BaseField } from "./base-field.ts";
|
|||||||
export interface TimestampFieldOptions extends BaseField {}
|
export interface TimestampFieldOptions extends BaseField {}
|
||||||
|
|
||||||
export interface TimestampField
|
export interface TimestampField
|
||||||
extends TaggedVariant<"TimestampField">,
|
extends TaggedVariant<"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",
|
||||||
|
|||||||
@ -6,11 +6,10 @@ export interface UUIDFieldOptions extends BaseField {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface UUIDField
|
export interface UUIDField
|
||||||
extends TaggedVariant<"UUIDField">,
|
extends TaggedVariant<"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",
|
||||||
|
|||||||
@ -6,7 +6,7 @@ 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,
|
||||||
|
|||||||
@ -20,8 +20,7 @@ export const FILTER_OPTION_TYPE_SYMBOL = "_filter_type";
|
|||||||
export const FILTER_OPTION_VALUE_SYMBOL = "_filter_value";
|
export const FILTER_OPTION_VALUE_SYMBOL = "_filter_value";
|
||||||
export const FILTER_OPTION_OPERATOR_SYMBOL = "_filter_operator";
|
export const FILTER_OPTION_OPERATOR_SYMBOL = "_filter_operator";
|
||||||
|
|
||||||
export type LikeFilterOption<T> = T extends string
|
export type LikeFilterOption<T> = T extends string ? {
|
||||||
? {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "like";
|
[FILTER_OPTION_TYPE_SYMBOL]: "like";
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: string;
|
[FILTER_OPTION_VALUE_SYMBOL]: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@ 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>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,6 @@ export interface Projection<TModel extends Model, TEvents extends Event> {
|
|||||||
events: TEvents[VariantTag][];
|
events: TEvents[VariantTag][];
|
||||||
projection: (
|
projection: (
|
||||||
event: StoredEvent<TEvents>,
|
event: StoredEvent<TEvents>,
|
||||||
model?: ModelToType<TModel>
|
model?: ModelToType<TModel>,
|
||||||
) => ModelToType<TModel>;
|
) => ModelToType<TModel>;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,14 +6,14 @@ 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.
|
||||||
|
|||||||
@ -35,7 +35,7 @@ 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);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ export function sortByDependencies<T>(
|
|||||||
}: {
|
}: {
|
||||||
keyGetter: (element: T) => string;
|
keyGetter: (element: T) => string;
|
||||||
depGetter: (element: T) => string[];
|
depGetter: (element: T) => string[];
|
||||||
}
|
},
|
||||||
): Result<T[], CircularDependencyError> {
|
): Result<T[], CircularDependencyError> {
|
||||||
const graph = new Map<string, string[]>();
|
const graph = new Map<string, string[]>();
|
||||||
const visited = new Set<string>();
|
const visited = new Set<string>();
|
||||||
@ -40,9 +40,9 @@ export function sortByDependencies<T>(
|
|||||||
visit(key, []);
|
visit(key, []);
|
||||||
});
|
});
|
||||||
return sorted.map(
|
return sorted.map(
|
||||||
(key) => array.find((element) => keyGetter(element) === key) as T
|
(key) => array.find((element) => keyGetter(element) === key) as T,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(e) => e as CircularDependencyError
|
(e) => e as CircularDependencyError,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,8 +18,7 @@ import {
|
|||||||
import { SQLiteDatabase } from "../sqlite/sqlite-database.ts";
|
import { SQLiteDatabase } from "../sqlite/sqlite-database.ts";
|
||||||
|
|
||||||
export class SQLiteEventStore<TEvents extends Event>
|
export class SQLiteEventStore<TEvents extends Event>
|
||||||
implements EventStore<TEvents>
|
implements EventStore<TEvents> {
|
||||||
{
|
|
||||||
private db: SQLiteDatabase;
|
private db: SQLiteDatabase;
|
||||||
|
|
||||||
private streamVersions = new Map<UUID, bigint>();
|
private streamVersions = new Map<UUID, bigint>();
|
||||||
@ -46,15 +45,15 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
timestamp NUMERIC NOT NULL,
|
timestamp NUMERIC NOT NULL,
|
||||||
payload TEXT NOT NULL,
|
payload TEXT NOT NULL,
|
||||||
UNIQUE(streamId, version)
|
UNIQUE(streamId, version)
|
||||||
)`
|
)`,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
getEventsFromStream(
|
getEventsFromStream(
|
||||||
streamId: UUID
|
streamId: UUID,
|
||||||
): AsyncResult<StoredEvent<TEvents>[], StoreQueryError> {
|
): AsyncResult<StoredEvent<TEvents>[], StoreQueryError> {
|
||||||
return AsyncResult.tryFrom(
|
return AsyncResult.tryFrom(
|
||||||
() => {
|
() => {
|
||||||
@ -70,16 +69,16 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
version: BigInt(e.version),
|
version: BigInt(e.version),
|
||||||
timestamp: new PosixDate(e.timestamp),
|
timestamp: new PosixDate(e.timestamp),
|
||||||
payload: JSONUtils.parse(e.payload),
|
payload: JSONUtils.parse(e.payload),
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
return events;
|
return events;
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
append<T extends TEvents>(
|
append<T extends TEvents>(
|
||||||
event: T
|
event: T,
|
||||||
): AsyncResult<StoredEvent<T>, StoreQueryError> {
|
): AsyncResult<StoredEvent<T>, StoreQueryError> {
|
||||||
return Run.seq(
|
return Run.seq(
|
||||||
() => this.getLastVersion(event.streamId),
|
() => this.getLastVersion(event.streamId),
|
||||||
@ -93,7 +92,7 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
AsyncResult.from(async () => {
|
AsyncResult.from(async () => {
|
||||||
await this.notifySubscribers(storedEvent);
|
await this.notifySubscribers(storedEvent);
|
||||||
return storedEvent;
|
return storedEvent;
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,20 +110,20 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
`SELECT max(version) as lastVersion FROM events WHERE streamId = $id`,
|
`SELECT max(version) as lastVersion FROM events WHERE streamId = $id`,
|
||||||
{
|
{
|
||||||
$id: streamId,
|
$id: streamId,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
return !lastVersion ? 0n : BigInt(lastVersion);
|
return !lastVersion ? 0n : BigInt(lastVersion);
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe<TEventKey extends TEvents[VariantTag]>(
|
subscribe<TEventKey extends TEvents[VariantTag]>(
|
||||||
eventNames: TEventKey[],
|
eventNames: TEventKey[],
|
||||||
subscriber: (
|
subscriber: (
|
||||||
event: StoredEvent<EventFromKey<TEvents, TEventKey>>
|
event: StoredEvent<EventFromKey<TEvents, TEventKey>>,
|
||||||
) => MaybePromise<void>
|
) => MaybePromise<void>,
|
||||||
): void {
|
): void {
|
||||||
eventNames.forEach((event) => {
|
eventNames.forEach((event) => {
|
||||||
const subscribers = this.eventSubscribers.get(event) || [];
|
const subscribers = this.eventSubscribers.get(event) || [];
|
||||||
@ -139,14 +138,14 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
close(): AsyncResult<void, StoreQueryError> {
|
close(): AsyncResult<void, StoreQueryError> {
|
||||||
return AsyncResult.tryFrom(
|
return AsyncResult.tryFrom(
|
||||||
() => this.db.close(),
|
() => this.db.close(),
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private storeEvent<T extends Event>(
|
private storeEvent<T extends Event>(
|
||||||
streamId: UUID,
|
streamId: UUID,
|
||||||
version: bigint,
|
version: bigint,
|
||||||
event: T
|
event: T,
|
||||||
): AsyncResult<StoredEvent<T>, StoreQueryError> {
|
): AsyncResult<StoredEvent<T>, StoreQueryError> {
|
||||||
return AsyncResult.tryFrom(
|
return AsyncResult.tryFrom(
|
||||||
() => {
|
() => {
|
||||||
@ -165,11 +164,11 @@ export class SQLiteEventStore<TEvents extends Event>
|
|||||||
$version: storedEvent.version.toString(),
|
$version: storedEvent.version.toString(),
|
||||||
$timestamp: storedEvent.timestamp.timestamp,
|
$timestamp: storedEvent.timestamp.timestamp,
|
||||||
$payload: JSON.stringify(storedEvent.payload),
|
$payload: JSON.stringify(storedEvent.payload),
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return storedEvent;
|
return storedEvent;
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -135,7 +135,7 @@ describe("SQL where clause from filter options", () => {
|
|||||||
const result = filterToSQL(opts);
|
const result = filterToSQL(opts);
|
||||||
const params = filterToParams(col, opts);
|
const params = filterToParams(col, opts);
|
||||||
expect(result).toEqual(
|
expect(result).toEqual(
|
||||||
"WHERE (name IN ($where_name_0_0,$where_name_0_1) AND age > $where_age_0) OR (status <> $where_status_1 AND salary > $where_salary_1) OR (rating < $where_rating_2 AND quantity >= $where_quantity_2)"
|
"WHERE (name IN ($where_name_0_0,$where_name_0_1) AND age > $where_age_0) OR (status <> $where_status_1 AND salary > $where_salary_1) OR (rating < $where_rating_2 AND quantity >= $where_quantity_2)",
|
||||||
);
|
);
|
||||||
expect(params).toEqual({
|
expect(params).toEqual({
|
||||||
$where_name_0_0: "John",
|
$where_name_0_0: "John",
|
||||||
|
|||||||
@ -16,20 +16,22 @@ import { fieldValueToSQL } from "./value-to-sql.ts";
|
|||||||
export function filterToSQL(filterOptions?: FilterOptions) {
|
export function filterToSQL(filterOptions?: FilterOptions) {
|
||||||
if (!filterOptions) return "";
|
if (!filterOptions) return "";
|
||||||
|
|
||||||
if (Array.isArray(filterOptions))
|
if (Array.isArray(filterOptions)) {
|
||||||
return `WHERE ${getWhereFromMultiOption(filterOptions)}`;
|
return `WHERE ${getWhereFromMultiOption(filterOptions)}`;
|
||||||
|
}
|
||||||
|
|
||||||
return `WHERE ${getWhereFromSingleOption(filterOptions)}`;
|
return `WHERE ${getWhereFromSingleOption(filterOptions)}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function filterToParams(
|
export function filterToParams(
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
filterOptions?: FilterOptions
|
filterOptions?: FilterOptions,
|
||||||
) {
|
) {
|
||||||
if (!filterOptions) return {};
|
if (!filterOptions) return {};
|
||||||
|
|
||||||
if (Array.isArray(filterOptions))
|
if (Array.isArray(filterOptions)) {
|
||||||
return getParamsFromMultiFilterOption(collection, filterOptions);
|
return getParamsFromMultiFilterOption(collection, filterOptions);
|
||||||
|
}
|
||||||
|
|
||||||
return getParamsFromSingleFilterOption(collection, filterOptions);
|
return getParamsFromSingleFilterOption(collection, filterOptions);
|
||||||
}
|
}
|
||||||
@ -38,14 +40,14 @@ function getWhereFromMultiOption(filterOptions: MultiFilterOption) {
|
|||||||
return filterOptions
|
return filterOptions
|
||||||
.map(
|
.map(
|
||||||
(option, i) =>
|
(option, i) =>
|
||||||
`(${getWhereFromSingleOption(option, { postfix: `_${i}` })})`
|
`(${getWhereFromSingleOption(option, { postfix: `_${i}` })})`,
|
||||||
)
|
)
|
||||||
.join(" OR ");
|
.join(" OR ");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWhereFromSingleOption(
|
function getWhereFromSingleOption(
|
||||||
filterOptions: SingleFilterOption,
|
filterOptions: SingleFilterOption,
|
||||||
opts: { postfix?: string } = {}
|
opts: { postfix?: string } = {},
|
||||||
) {
|
) {
|
||||||
return Object.entries(filterOptions)
|
return Object.entries(filterOptions)
|
||||||
.map(([key, value]) => getWhereFromKeyValue(key, value, opts))
|
.map(([key, value]) => getWhereFromKeyValue(key, value, opts))
|
||||||
@ -61,7 +63,7 @@ function getWhereParamKey(key: string, opts: { postfix?: string } = {}) {
|
|||||||
function getWhereFromKeyValue(
|
function getWhereFromKeyValue(
|
||||||
key: string,
|
key: string,
|
||||||
value: FilterValue,
|
value: FilterValue,
|
||||||
opts: { postfix?: string } = {}
|
opts: { postfix?: string } = {},
|
||||||
) {
|
) {
|
||||||
if (value == undefined) {
|
if (value == undefined) {
|
||||||
return `${key} IS NULL`;
|
return `${key} IS NULL`;
|
||||||
@ -73,19 +75,25 @@ function getWhereFromKeyValue(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (value[FILTER_OPTION_TYPE_SYMBOL] === "in") {
|
if (value[FILTER_OPTION_TYPE_SYMBOL] === "in") {
|
||||||
return `${key} IN (${value[FILTER_OPTION_VALUE_SYMBOL].map(
|
return `${key} IN (${
|
||||||
|
value[FILTER_OPTION_VALUE_SYMBOL].map(
|
||||||
(_v: any, i: number) =>
|
(_v: any, i: number) =>
|
||||||
`${getWhereParamKey(key, {
|
`${
|
||||||
|
getWhereParamKey(key, {
|
||||||
postfix: opts.postfix ? `${opts.postfix}_${i}` : `_${i}`,
|
postfix: opts.postfix ? `${opts.postfix}_${i}` : `_${i}`,
|
||||||
})}`
|
})
|
||||||
).join(",")})`;
|
}`,
|
||||||
|
).join(",")
|
||||||
|
})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value[FILTER_OPTION_TYPE_SYMBOL] === "comparison") {
|
if (value[FILTER_OPTION_TYPE_SYMBOL] === "comparison") {
|
||||||
return `${key} ${value[FILTER_OPTION_OPERATOR_SYMBOL]} ${getWhereParamKey(
|
return `${key} ${value[FILTER_OPTION_OPERATOR_SYMBOL]} ${
|
||||||
|
getWhereParamKey(
|
||||||
key,
|
key,
|
||||||
opts
|
opts,
|
||||||
)}`;
|
)
|
||||||
|
}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return `${key} = ${getWhereParamKey(key, opts)}`;
|
return `${key} = ${getWhereParamKey(key, opts)}`;
|
||||||
@ -93,7 +101,7 @@ function getWhereFromKeyValue(
|
|||||||
|
|
||||||
function getParamsFromMultiFilterOption(
|
function getParamsFromMultiFilterOption(
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
filterOptions: MultiFilterOption
|
filterOptions: MultiFilterOption,
|
||||||
) {
|
) {
|
||||||
return filterOptions.reduce(
|
return filterOptions.reduce(
|
||||||
(acc, filterOption, i) => ({
|
(acc, filterOption, i) => ({
|
||||||
@ -102,14 +110,14 @@ function getParamsFromMultiFilterOption(
|
|||||||
postfix: `_${i}`,
|
postfix: `_${i}`,
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getParamsFromSingleFilterOption(
|
function getParamsFromSingleFilterOption(
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
filterOptions: SingleFilterOption,
|
filterOptions: SingleFilterOption,
|
||||||
opts: { postfix?: string } = {}
|
opts: { postfix?: string } = {},
|
||||||
) {
|
) {
|
||||||
return Object.entries(filterOptions)
|
return Object.entries(filterOptions)
|
||||||
.filter(([, value]) => {
|
.filter(([, value]) => {
|
||||||
@ -122,10 +130,10 @@ function getParamsFromSingleFilterOption(
|
|||||||
collection.fields[key]!,
|
collection.fields[key]!,
|
||||||
key,
|
key,
|
||||||
value,
|
value,
|
||||||
opts
|
opts,
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +155,7 @@ function getParamsForFilterKeyValue(
|
|||||||
field: FieldDefinition,
|
field: FieldDefinition,
|
||||||
key: string,
|
key: string,
|
||||||
value: FilterValue,
|
value: FilterValue,
|
||||||
opts: { postfix?: string } = {}
|
opts: { postfix?: string } = {},
|
||||||
) {
|
) {
|
||||||
if (typeof value === "object") {
|
if (typeof value === "object") {
|
||||||
if (value[FILTER_OPTION_TYPE_SYMBOL] === "in") {
|
if (value[FILTER_OPTION_TYPE_SYMBOL] === "in") {
|
||||||
@ -155,12 +163,14 @@ function getParamsForFilterKeyValue(
|
|||||||
(acc: Record<string, any>, _: any, i: number) => {
|
(acc: Record<string, any>, _: any, i: number) => {
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[getWhereParamKey(key, {
|
[
|
||||||
|
getWhereParamKey(key, {
|
||||||
postfix: opts.postfix ? `${opts.postfix}_${i}` : `_${i}`,
|
postfix: opts.postfix ? `${opts.postfix}_${i}` : `_${i}`,
|
||||||
})]: value[FILTER_OPTION_VALUE_SYMBOL][i],
|
})
|
||||||
|
]: value[FILTER_OPTION_VALUE_SYMBOL][i],
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ describe("ModelToSQL", () => {
|
|||||||
const result = modelToSql(model);
|
const result = modelToSql(model);
|
||||||
|
|
||||||
expect(result).toEqual(
|
expect(result).toEqual(
|
||||||
`CREATE TABLE something (id TEXT PRIMARY KEY, name TEXT NOT NULL, age INTEGER NOT NULL, date NUMERIC NOT NULL, reference TEXT NOT NULL REFERENCES somethingElse(id))`
|
`CREATE TABLE something (id TEXT PRIMARY KEY, name TEXT NOT NULL, age INTEGER NOT NULL, date NUMERIC NOT NULL, reference TEXT NOT NULL REFERENCES somethingElse(id))`,
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { Collection, FieldDefinition, getTargetKey } from "@fabric/domain";
|
|||||||
type FieldSQLDefinitionMap = {
|
type FieldSQLDefinitionMap = {
|
||||||
[K in FieldDefinition[VariantTag]]: (
|
[K in FieldDefinition[VariantTag]]: (
|
||||||
name: string,
|
name: string,
|
||||||
field: Extract<FieldDefinition, { [VariantTag]: K }>
|
field: Extract<FieldDefinition, { [VariantTag]: K }>,
|
||||||
) => string;
|
) => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ function modifiersFromOpts(field: FieldDefinition) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function modelToSql(
|
export function modelToSql(
|
||||||
model: Collection<string, Record<string, FieldDefinition>>
|
model: Collection<string, Record<string, FieldDefinition>>,
|
||||||
) {
|
) {
|
||||||
const fields = Object.entries(model.fields)
|
const fields = Object.entries(model.fields)
|
||||||
.map(([name, type]) => fieldDefinitionToSQL(name, type))
|
.map(([name, type]) => fieldDefinitionToSQL(name, type))
|
||||||
|
|||||||
@ -29,7 +29,7 @@ export function recordToSQLParams(model: Model, record: Record<string, any>) {
|
|||||||
...acc,
|
...acc,
|
||||||
[keyToParam(key)]: fieldValueToSQL(model.fields[key]!, record[key]),
|
[keyToParam(key)]: fieldValueToSQL(model.fields[key]!, record[key]),
|
||||||
}),
|
}),
|
||||||
{}
|
{},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ function valueFromSQL(field: FieldDefinition, value: any): any {
|
|||||||
type FieldSQLInsertMap = {
|
type FieldSQLInsertMap = {
|
||||||
[K in FieldDefinition[VariantTag]]: (
|
[K in FieldDefinition[VariantTag]]: (
|
||||||
field: Extract<FieldDefinition, { [VariantTag]: K }>,
|
field: Extract<FieldDefinition, { [VariantTag]: K }>,
|
||||||
value: any
|
value: any,
|
||||||
) => FieldToType<Extract<FieldDefinition, { [VariantTag]: K }>>;
|
) => FieldToType<Extract<FieldDefinition, { [VariantTag]: K }>>;
|
||||||
};
|
};
|
||||||
const FieldSQLInsertMap: FieldSQLInsertMap = {
|
const FieldSQLInsertMap: FieldSQLInsertMap = {
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export class SQLiteDatabase {
|
|||||||
allPrepared(
|
allPrepared(
|
||||||
sql: string,
|
sql: string,
|
||||||
params?: Record<string, any>,
|
params?: Record<string, any>,
|
||||||
transformer?: (row: any) => any
|
transformer?: (row: any) => any,
|
||||||
) {
|
) {
|
||||||
const cachedStmt = this.getCachedStatement(sql);
|
const cachedStmt = this.getCachedStatement(sql);
|
||||||
|
|
||||||
@ -56,7 +56,7 @@ export class SQLiteDatabase {
|
|||||||
onePrepared(
|
onePrepared(
|
||||||
sql: string,
|
sql: string,
|
||||||
params?: Record<string, any>,
|
params?: Record<string, any>,
|
||||||
transformer?: (row: any) => any
|
transformer?: (row: any) => any,
|
||||||
) {
|
) {
|
||||||
const cachedStmt = this.getCachedStatement(sql);
|
const cachedStmt = this.getCachedStatement(sql);
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { FieldDefinition, FieldToType } from "@fabric/domain";
|
|||||||
type FieldSQLInsertMap = {
|
type FieldSQLInsertMap = {
|
||||||
[K in FieldDefinition[VariantTag]]: (
|
[K in FieldDefinition[VariantTag]]: (
|
||||||
field: Extract<FieldDefinition, { [VariantTag]: K }>,
|
field: Extract<FieldDefinition, { [VariantTag]: K }>,
|
||||||
value: FieldToType<Extract<FieldDefinition, { [VariantTag]: K }>>
|
value: FieldToType<Extract<FieldDefinition, { [VariantTag]: K }>>,
|
||||||
) => any;
|
) => any;
|
||||||
};
|
};
|
||||||
const FieldSQLInsertMap: FieldSQLInsertMap = {
|
const FieldSQLInsertMap: FieldSQLInsertMap = {
|
||||||
|
|||||||
@ -20,7 +20,7 @@ export class QueryBuilder<T> implements StoreQuery<T> {
|
|||||||
constructor(
|
constructor(
|
||||||
private db: SQLiteDatabase,
|
private db: SQLiteDatabase,
|
||||||
private schema: ModelSchema,
|
private schema: ModelSchema,
|
||||||
private query: QueryDefinition
|
private query: QueryDefinition,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
where(where: FilterOptions<T>): StoreSortableQuery<T> {
|
where(where: FilterOptions<T>): StoreSortableQuery<T> {
|
||||||
@ -47,7 +47,7 @@ export class QueryBuilder<T> implements 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>;
|
||||||
select<K extends Keyof<T>>(keys?: K[]): AsyncResult<any, StoreQueryError> {
|
select<K extends Keyof<T>>(keys?: K[]): AsyncResult<any, StoreQueryError> {
|
||||||
return AsyncResult.tryFrom(
|
return AsyncResult.tryFrom(
|
||||||
@ -57,21 +57,21 @@ export class QueryBuilder<T> implements StoreQuery<T> {
|
|||||||
{
|
{
|
||||||
...this.query,
|
...this.query,
|
||||||
keys: keys!,
|
keys: keys!,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return this.db.allPrepared(
|
return this.db.allPrepared(
|
||||||
sql,
|
sql,
|
||||||
params,
|
params,
|
||||||
transformRow(this.schema[this.query.from]!)
|
transformRow(this.schema[this.query.from]!),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(err) => new StoreQueryError(err.message)
|
(err) => new StoreQueryError(err.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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>;
|
||||||
selectOne<K extends Keyof<T>>(keys?: K[]): AsyncResult<any, StoreQueryError> {
|
selectOne<K extends Keyof<T>>(keys?: K[]): AsyncResult<any, StoreQueryError> {
|
||||||
return AsyncResult.tryFrom(
|
return AsyncResult.tryFrom(
|
||||||
@ -82,22 +82,22 @@ export class QueryBuilder<T> implements StoreQuery<T> {
|
|||||||
...this.query,
|
...this.query,
|
||||||
keys: keys!,
|
keys: keys!,
|
||||||
limit: 1,
|
limit: 1,
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
return await this.db.onePrepared(
|
return await this.db.onePrepared(
|
||||||
stmt,
|
stmt,
|
||||||
params,
|
params,
|
||||||
transformRow(this.schema[this.query.from]!)
|
transformRow(this.schema[this.query.from]!),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(err) => new StoreQueryError(err.message)
|
(err) => new StoreQueryError(err.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getSelectStatement(
|
export function getSelectStatement(
|
||||||
collection: Collection,
|
collection: Collection,
|
||||||
query: QueryDefinition
|
query: QueryDefinition,
|
||||||
): [string, Record<string, any>] {
|
): [string, Record<string, any>] {
|
||||||
const selectFields = query.keys ? query.keys.join(", ") : "*";
|
const selectFields = query.keys ? query.keys.join(", ") : "*";
|
||||||
|
|
||||||
|
|||||||
@ -110,7 +110,7 @@ describe("State Store", () => {
|
|||||||
streamId: UUIDGeneratorMock.generate(),
|
streamId: UUIDGeneratorMock.generate(),
|
||||||
streamVersion: 1n,
|
streamVersion: 1n,
|
||||||
deletedAt: null,
|
deletedAt: null,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const result = await Run.UNSAFE(() =>
|
const result = await Run.UNSAFE(() =>
|
||||||
|
|||||||
@ -20,8 +20,7 @@ import { SQLiteDatabase } from "../sqlite/sqlite-database.ts";
|
|||||||
import { QueryBuilder } from "./query-builder.ts";
|
import { QueryBuilder } from "./query-builder.ts";
|
||||||
|
|
||||||
export class SQLiteStateStore<TModel extends Model>
|
export class SQLiteStateStore<TModel extends Model>
|
||||||
implements WritableStateStore<TModel>
|
implements WritableStateStore<TModel> {
|
||||||
{
|
|
||||||
private schema: ModelSchemaFromModels<TModel>;
|
private schema: ModelSchemaFromModels<TModel>;
|
||||||
private db: SQLiteDatabase;
|
private db: SQLiteDatabase;
|
||||||
|
|
||||||
@ -38,25 +37,27 @@ export class SQLiteStateStore<TModel extends Model>
|
|||||||
|
|
||||||
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> {
|
||||||
const model = this.schema[collection];
|
const model = this.schema[collection];
|
||||||
|
|
||||||
return AsyncResult.tryFrom(
|
return AsyncResult.tryFrom(
|
||||||
() => {
|
() => {
|
||||||
this.db.runPrepared(
|
this.db.runPrepared(
|
||||||
`INSERT INTO ${model.name} (${recordToSQLKeys(
|
`INSERT INTO ${model.name} (${
|
||||||
record
|
recordToSQLKeys(
|
||||||
)}) VALUES (${recordToSQLKeyParams(record)})`,
|
record,
|
||||||
recordToSQLParams(model, record)
|
)
|
||||||
|
}) VALUES (${recordToSQLKeyParams(record)})`,
|
||||||
|
recordToSQLParams(model, record),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
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]>> {
|
||||||
return new QueryBuilder(this.db, this.schema, {
|
return new QueryBuilder(this.db, this.schema, {
|
||||||
from: collection,
|
from: collection,
|
||||||
@ -66,7 +67,7 @@ export class SQLiteStateStore<TModel extends Model>
|
|||||||
update<T extends keyof ModelSchemaFromModels<TModel>>(
|
update<T extends keyof ModelSchemaFromModels<TModel>>(
|
||||||
collection: T,
|
collection: T,
|
||||||
id: UUID,
|
id: UUID,
|
||||||
record: Partial<ModelToType<ModelSchemaFromModels<TModel>[T]>>
|
record: Partial<ModelToType<ModelSchemaFromModels<TModel>[T]>>,
|
||||||
): AsyncResult<void, StoreQueryError> {
|
): AsyncResult<void, StoreQueryError> {
|
||||||
const model = this.schema[collection];
|
const model = this.schema[collection];
|
||||||
|
|
||||||
@ -77,19 +78,21 @@ export class SQLiteStateStore<TModel extends Model>
|
|||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
this.db.runPrepared(
|
this.db.runPrepared(
|
||||||
`UPDATE ${model.name} SET ${recordToSQLSet(
|
`UPDATE ${model.name} SET ${
|
||||||
record
|
recordToSQLSet(
|
||||||
)} WHERE id = ${keyToParam("id")}`,
|
record,
|
||||||
params
|
)
|
||||||
|
} WHERE id = ${keyToParam("id")}`,
|
||||||
|
params,
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
delete<T extends keyof ModelSchemaFromModels<TModel>>(
|
delete<T extends keyof ModelSchemaFromModels<TModel>>(
|
||||||
collection: T,
|
collection: T,
|
||||||
id: UUID
|
id: UUID,
|
||||||
): AsyncResult<void, StoreQueryError> {
|
): AsyncResult<void, StoreQueryError> {
|
||||||
const model = this.schema[collection];
|
const model = this.schema[collection];
|
||||||
|
|
||||||
@ -97,10 +100,10 @@ export class SQLiteStateStore<TModel extends Model>
|
|||||||
() => {
|
() => {
|
||||||
this.db.runPrepared(
|
this.db.runPrepared(
|
||||||
`DELETE FROM ${model.name} WHERE id = ${keyToParam("id")}`,
|
`DELETE FROM ${model.name} WHERE id = ${keyToParam("id")}`,
|
||||||
{ $id: id }
|
{ $id: id },
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -116,7 +119,7 @@ export class SQLiteStateStore<TModel extends Model>
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
(error) => new StoreQueryError(error.message)
|
(error) => new StoreQueryError(error.message),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user