Compare commits
No commits in common. "8aaf4c73e88de830f14b3ee70605df1199f0181e" and "c4483f073e8357106c43f481c3f1aefb82c637a4" have entirely different histories.
8aaf4c73e8
...
c4483f073e
@ -7,9 +7,4 @@ export default tseslint.config(
|
|||||||
eslint.configs.recommended,
|
eslint.configs.recommended,
|
||||||
...tseslint.configs.strict,
|
...tseslint.configs.strict,
|
||||||
...tseslint.configs.stylistic,
|
...tseslint.configs.stylistic,
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
"@typescript-eslint/no-namespace": "off",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,12 +1,10 @@
|
|||||||
import { TaggedVariant } from "../../variant/variant.js";
|
import { TaggedVariant } from "../../variant/variant.js";
|
||||||
import { UUID } from "../types/uuid.js";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An event is a tagged variant with a payload and a timestamp.
|
* An event is a tagged variant with a payload and a timestamp.
|
||||||
*/
|
*/
|
||||||
export interface Event<TTag extends string, TPayload>
|
export interface Event<TTag extends string, TPayload>
|
||||||
extends TaggedVariant<TTag> {
|
extends TaggedVariant<TTag> {
|
||||||
streamId: UUID;
|
|
||||||
payload: TPayload;
|
payload: TPayload;
|
||||||
timestamp: number;
|
timestamp: number;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
import { FieldDefinition } from "./fields/index.js";
|
|
||||||
|
|
||||||
export interface ModelDefinition<
|
|
||||||
TName extends string = string,
|
|
||||||
TFields extends Record<string, FieldDefinition> = Record<
|
|
||||||
string,
|
|
||||||
FieldDefinition
|
|
||||||
>,
|
|
||||||
> {
|
|
||||||
name: TName;
|
|
||||||
fields: TFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ModelName<
|
|
||||||
TModel extends ModelDefinition<string, Record<string, FieldDefinition>>,
|
|
||||||
> = TModel["name"];
|
|
||||||
|
|
||||||
export type ModelFromName<
|
|
||||||
TModels extends ModelDefinition<string, Record<string, FieldDefinition>>,
|
|
||||||
TName extends ModelName<TModels>,
|
|
||||||
> = Extract<TModels, { name: TName }>;
|
|
||||||
|
|
||||||
export type ModelFieldNames<
|
|
||||||
TModel extends ModelDefinition<string, Record<string, FieldDefinition>>,
|
|
||||||
> = keyof TModel["fields"];
|
|
||||||
|
|
||||||
export function createModel<
|
|
||||||
TName extends string,
|
|
||||||
TFields extends Record<string, FieldDefinition>,
|
|
||||||
>(opts: ModelDefinition<TName, TFields>): ModelDefinition<TName, TFields> {
|
|
||||||
return opts;
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
export interface BaseField {
|
|
||||||
isOptional?: boolean;
|
|
||||||
isUnique?: boolean;
|
|
||||||
isIndexed?: boolean;
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { UUID } from "../../types/uuid.js";
|
|
||||||
import { StringField } from "./string-field.js";
|
|
||||||
import { UUIDField } from "./uuid-field.js";
|
|
||||||
|
|
||||||
export type FieldToType<TField> = TField extends StringField
|
|
||||||
? string
|
|
||||||
: TField extends UUIDField
|
|
||||||
? UUID
|
|
||||||
: never;
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
import { createStringField, StringField } from "./string-field.js";
|
|
||||||
import { createUUIDField } from "./uuid-field.js";
|
|
||||||
|
|
||||||
export type FieldDefinition = StringField;
|
|
||||||
|
|
||||||
export namespace Field {
|
|
||||||
export const string = createStringField;
|
|
||||||
export const uuid = createUUIDField;
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
import { TaggedVariant, VariantTag } from "../../../variant/variant.js";
|
|
||||||
import { BaseField } from "./base-field.js";
|
|
||||||
|
|
||||||
export interface StringFieldOptions extends BaseField {
|
|
||||||
maxLength?: number;
|
|
||||||
minLength?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StringField
|
|
||||||
extends TaggedVariant<"StringField">,
|
|
||||||
StringFieldOptions {}
|
|
||||||
|
|
||||||
export function createStringField<T extends StringFieldOptions>(
|
|
||||||
opts: T,
|
|
||||||
): StringField & T {
|
|
||||||
return {
|
|
||||||
[VariantTag]: "StringField",
|
|
||||||
...opts,
|
|
||||||
} as const;
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
import { TaggedVariant, VariantTag } from "../../../variant/variant.js";
|
|
||||||
import { BaseField } from "./base-field.js";
|
|
||||||
|
|
||||||
export interface UUIDOptions extends BaseField {
|
|
||||||
isPrimaryKey?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UUIDField extends TaggedVariant<"UUID_FIELD">, UUIDOptions {}
|
|
||||||
|
|
||||||
export function createUUIDField(opts: UUIDOptions): UUIDField {
|
|
||||||
return {
|
|
||||||
[VariantTag]: "UUID_FIELD",
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import { ModelDefinition } from "./create-model.js";
|
|
||||||
import { FieldToType } from "./fields/field-to-type.js";
|
|
||||||
|
|
||||||
export type ModelToType<TModel extends ModelDefinition> = {
|
|
||||||
[K in keyof TModel["fields"]]: FieldToType<TModel["fields"][K]>;
|
|
||||||
};
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
import { OneToOneRelation } from "./one-to-one.js";
|
|
||||||
|
|
||||||
export type RelationDefinition = OneToOneRelation<string, string>;
|
|
||||||
|
|
||||||
export namespace Relations {}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
import { TaggedVariant, VariantTag } from "../../../variant/variant.js";
|
|
||||||
|
|
||||||
export interface OneToManyRelationOptions<
|
|
||||||
TOwner extends string,
|
|
||||||
TTarget extends string,
|
|
||||||
> {
|
|
||||||
/**
|
|
||||||
* The owner of the relation. In this case is the "one" side of the relation.
|
|
||||||
*/
|
|
||||||
owner: TOwner;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The target of the relation. In this case is the "many" side of the relation.
|
|
||||||
*/
|
|
||||||
target: TTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OneToManyRelation<
|
|
||||||
TOwner extends string,
|
|
||||||
TTarget extends string,
|
|
||||||
> extends TaggedVariant<"ONE_TO_MANY_RELATION">,
|
|
||||||
OneToManyRelationOptions<TOwner, TTarget> {}
|
|
||||||
|
|
||||||
export function createOneToManyRelation<
|
|
||||||
TOwner extends string,
|
|
||||||
TTarget extends string,
|
|
||||||
>(
|
|
||||||
opts: OneToManyRelationOptions<TOwner, TTarget>,
|
|
||||||
): OneToManyRelation<TOwner, TTarget> {
|
|
||||||
return {
|
|
||||||
[VariantTag]: "ONE_TO_MANY_RELATION",
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
import { TaggedVariant, VariantTag } from "../../../variant/variant.js";
|
|
||||||
|
|
||||||
export interface OneToOneRelationOptions<
|
|
||||||
TOwner extends string,
|
|
||||||
TTarget extends string,
|
|
||||||
> {
|
|
||||||
/**
|
|
||||||
* The owner of the relation.
|
|
||||||
*/
|
|
||||||
owner: TOwner;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The target of the relation
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
target: TTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface OneToOneRelation<TOwner extends string, TTarget extends string>
|
|
||||||
extends TaggedVariant<"ONE_TO_ONE_RELATION">,
|
|
||||||
OneToOneRelationOptions<TOwner, TTarget> {}
|
|
||||||
|
|
||||||
export function createOneToOneRelation<
|
|
||||||
TOwner extends string,
|
|
||||||
TTarget extends string,
|
|
||||||
>(
|
|
||||||
opts: OneToOneRelationOptions<TOwner, TTarget>,
|
|
||||||
): OneToOneRelation<TOwner, TTarget> {
|
|
||||||
return {
|
|
||||||
[VariantTag]: "ONE_TO_ONE_RELATION",
|
|
||||||
...opts,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1 +1 @@
|
|||||||
export * from "./policy.js";
|
export * from "./policy-map.js";
|
||||||
|
|||||||
7
packages/fabric/core/src/domain/security/policy-map.ts
Normal file
7
packages/fabric/core/src/domain/security/policy-map.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
/**
|
||||||
|
* A PolicyMap maps user types to their security policies.
|
||||||
|
*/
|
||||||
|
export type PolicyMap<
|
||||||
|
UserType extends string,
|
||||||
|
PolicyType extends string,
|
||||||
|
> = Record<UserType, PolicyType[]>;
|
||||||
@ -1,7 +0,0 @@
|
|||||||
/**
|
|
||||||
* A Policy maps permissions to which user types are allowed to perform them.
|
|
||||||
*/
|
|
||||||
export type Policy<
|
|
||||||
TUserType extends string,
|
|
||||||
TPolicyType extends string,
|
|
||||||
> = Record<TPolicyType, TUserType[]>;
|
|
||||||
@ -1,20 +1,13 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { TaggedError } from "../../error/tagged-error.js";
|
import { TaggedError } from "../../error/tagged-error.js";
|
||||||
import { UseCase } from "./use-case.js";
|
import { UseCase } from "./use-case.js";
|
||||||
|
|
||||||
export type UseCaseDefinition<
|
export type UseCaseDefinition<
|
||||||
TDependencies = any,
|
|
||||||
TPayload = any,
|
|
||||||
TOutput = any,
|
|
||||||
TErrors extends TaggedError<string> = any,
|
|
||||||
> = BasicUseCaseDefinition<TDependencies, TPayload, TOutput, TErrors>;
|
|
||||||
|
|
||||||
interface BasicUseCaseDefinition<
|
|
||||||
TDependencies,
|
TDependencies,
|
||||||
TPayload,
|
TPayload,
|
||||||
TOutput,
|
TOutput,
|
||||||
TErrors extends TaggedError<string>,
|
TErrors extends TaggedError<string>,
|
||||||
> {
|
> = TPayload extends undefined
|
||||||
|
? {
|
||||||
/**
|
/**
|
||||||
* The use case name.
|
* The use case name.
|
||||||
*/
|
*/
|
||||||
@ -23,11 +16,11 @@ interface BasicUseCaseDefinition<
|
|||||||
/**
|
/**
|
||||||
* Whether the use case requires authentication or not.
|
* Whether the use case requires authentication or not.
|
||||||
*/
|
*/
|
||||||
isAuthRequired: boolean;
|
isAuthRequired?: boolean;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The required permissions to execute the use case.
|
* The required permissions to execute the use case.
|
||||||
**/
|
*/
|
||||||
requiredPermissions?: string[];
|
requiredPermissions?: string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,3 +28,24 @@ interface BasicUseCaseDefinition<
|
|||||||
*/
|
*/
|
||||||
useCase: UseCase<TDependencies, TPayload, TOutput, TErrors>;
|
useCase: UseCase<TDependencies, TPayload, TOutput, TErrors>;
|
||||||
}
|
}
|
||||||
|
: {
|
||||||
|
/**
|
||||||
|
* The use case name.
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether the use case requires authentication or not.
|
||||||
|
*/
|
||||||
|
isAuthRequired?: boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The required permissions to execute the use case.
|
||||||
|
*/
|
||||||
|
requiredPermissions?: string[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The use case function.
|
||||||
|
*/
|
||||||
|
useCase: UseCase<TDependencies, TPayload, TOutput, TErrors>;
|
||||||
|
};
|
||||||
|
|||||||
@ -3,6 +3,5 @@ export * from "./domain/index.js";
|
|||||||
export * from "./error/index.js";
|
export * from "./error/index.js";
|
||||||
export * from "./record/index.js";
|
export * from "./record/index.js";
|
||||||
export * from "./result/index.js";
|
export * from "./result/index.js";
|
||||||
export * from "./time/index.js";
|
|
||||||
export * from "./types/index.js";
|
export * from "./types/index.js";
|
||||||
export * from "./variant/index.js";
|
export * from "./variant/index.js";
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
|
|
||||||
import { ModelDefinition } from "../domain/models/create-model.js";
|
|
||||||
import { TaggedError } from "../error/tagged-error.js";
|
|
||||||
import { AsyncResult } from "../result/async-result.js";
|
|
||||||
import { QueryDefinition } from "./query/query.js";
|
|
||||||
import { CircularDependencyError } from "./utils/sort-by-dependencies.js";
|
|
||||||
|
|
||||||
export interface StorageDriver {
|
|
||||||
/**
|
|
||||||
* Insert data into the store
|
|
||||||
*/
|
|
||||||
insert(collectionName: string, record: any): AsyncResult<void, QueryError>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a select query against the store.
|
|
||||||
*/
|
|
||||||
select(query: QueryDefinition): AsyncResult<any[], QueryError>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run a select query against the store.
|
|
||||||
*/
|
|
||||||
selectOne(query: QueryDefinition): AsyncResult<any, QueryError>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sincronice the store with the schema.
|
|
||||||
*/
|
|
||||||
sync(
|
|
||||||
schema: ModelDefinition[],
|
|
||||||
): AsyncResult<void, QueryError | CircularDependencyError>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Drop the store. This is a destructive operation.
|
|
||||||
*/
|
|
||||||
drop(): AsyncResult<void, QueryError>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update a record in the store.
|
|
||||||
*/
|
|
||||||
update(
|
|
||||||
collectionName: string,
|
|
||||||
id: string,
|
|
||||||
record: Record<string, any>,
|
|
||||||
): AsyncResult<void, QueryError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class QueryError extends TaggedError<"QueryError"> {
|
|
||||||
constructor(
|
|
||||||
public message: string,
|
|
||||||
public context: any,
|
|
||||||
) {
|
|
||||||
super("QueryError");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,93 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
|
|
||||||
export type FilterOptions<T = any> =
|
|
||||||
| SingleFilterOption<T>
|
|
||||||
| MultiFilterOption<T>;
|
|
||||||
|
|
||||||
export type SingleFilterOption<T = any> = {
|
|
||||||
[K in keyof T]?:
|
|
||||||
| T[K]
|
|
||||||
| LikeFilterOption<T[K]>
|
|
||||||
| ComparisonFilterOption<T[K]>
|
|
||||||
| InFilterOption<T[K]>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type MultiFilterOption<T = any> = SingleFilterOption<T>[];
|
|
||||||
|
|
||||||
export const FILTER_OPTION_TYPE_SYMBOL = Symbol("$type");
|
|
||||||
export const FILTER_OPTION_VALUE_SYMBOL = Symbol("$value");
|
|
||||||
export const FILTER_OPTION_OPERATOR_SYMBOL = Symbol("$operator");
|
|
||||||
|
|
||||||
export type LikeFilterOption<T> = T extends string
|
|
||||||
? {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "like";
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: string;
|
|
||||||
}
|
|
||||||
: never;
|
|
||||||
|
|
||||||
export interface InFilterOption<T> {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "in";
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: T[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface ComparisonFilterOption<T> {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "comparison";
|
|
||||||
[FILTER_OPTION_OPERATOR_SYMBOL]: ComparisonOperator;
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type ComparisonOperator = "<" | ">" | "<=" | ">=" | "<>";
|
|
||||||
|
|
||||||
export function isGreaterThan(value: any): ComparisonFilterOption<any> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "comparison",
|
|
||||||
[FILTER_OPTION_OPERATOR_SYMBOL]: ">",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isLessThan(value: any): ComparisonFilterOption<any> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "comparison",
|
|
||||||
[FILTER_OPTION_OPERATOR_SYMBOL]: "<",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isGreaterOrEqualTo(value: any): ComparisonFilterOption<any> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "comparison",
|
|
||||||
[FILTER_OPTION_OPERATOR_SYMBOL]: ">=",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isLessOrEqualTo(value: any): ComparisonFilterOption<any> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "comparison",
|
|
||||||
[FILTER_OPTION_OPERATOR_SYMBOL]: "<=",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNotEqualTo(value: any): ComparisonFilterOption<any> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "comparison",
|
|
||||||
[FILTER_OPTION_OPERATOR_SYMBOL]: "<>",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isLike(value: string): LikeFilterOption<string> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "like",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isIn<T>(values: T[]): InFilterOption<T> {
|
|
||||||
return {
|
|
||||||
[FILTER_OPTION_TYPE_SYMBOL]: "in",
|
|
||||||
[FILTER_OPTION_VALUE_SYMBOL]: values,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export type OrderByOptions<T> = {
|
|
||||||
[K in keyof T]?: "ASC" | "DESC";
|
|
||||||
};
|
|
||||||
@ -1,74 +0,0 @@
|
|||||||
import {
|
|
||||||
ModelDefinition,
|
|
||||||
ModelFromName,
|
|
||||||
ModelName,
|
|
||||||
} from "../../domain/models/create-model.js";
|
|
||||||
import { ModelToType } from "../../domain/models/model-to-type.js";
|
|
||||||
import { AsyncResult } from "../../result/async-result.js";
|
|
||||||
import { Keyof } from "../../types/index.js";
|
|
||||||
import { QueryError, StorageDriver } from "../driver.js";
|
|
||||||
import { FilterOptions } from "./filter-options.js";
|
|
||||||
import { OrderByOptions } from "./order-by-options.js";
|
|
||||||
import {
|
|
||||||
QueryDefinition,
|
|
||||||
SelectableQuery,
|
|
||||||
StoreLimitableQuery,
|
|
||||||
StoreQuery,
|
|
||||||
StoreSortableQuery,
|
|
||||||
} from "./query.js";
|
|
||||||
|
|
||||||
export class QueryBuilder<
|
|
||||||
TModels extends ModelDefinition,
|
|
||||||
TEntityName extends ModelName<TModels>,
|
|
||||||
T = ModelToType<ModelFromName<TModels, TEntityName>>,
|
|
||||||
> implements StoreQuery<T>
|
|
||||||
{
|
|
||||||
constructor(
|
|
||||||
private driver: StorageDriver,
|
|
||||||
private query: QueryDefinition<TEntityName>,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
where(where: FilterOptions<T>): StoreSortableQuery<T> {
|
|
||||||
this.query = {
|
|
||||||
...this.query,
|
|
||||||
where,
|
|
||||||
};
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
orderBy(opts: OrderByOptions<T>): StoreLimitableQuery<T> {
|
|
||||||
this.query = {
|
|
||||||
...this.query,
|
|
||||||
orderBy: opts,
|
|
||||||
};
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
limit(limit: number, offset?: number | undefined): SelectableQuery<T> {
|
|
||||||
this.query = {
|
|
||||||
...this.query,
|
|
||||||
limit,
|
|
||||||
offset,
|
|
||||||
};
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
select<K extends Keyof<T>>(
|
|
||||||
keys?: K[],
|
|
||||||
): AsyncResult<Pick<T, K>[], QueryError> {
|
|
||||||
return this.driver.select({
|
|
||||||
...this.query,
|
|
||||||
keys,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
selectOne<K extends Keyof<T>>(
|
|
||||||
keys?: K[],
|
|
||||||
): AsyncResult<Pick<T, K>, QueryError> {
|
|
||||||
return this.driver.selectOne({
|
|
||||||
...this.query,
|
|
||||||
keys,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { AsyncResult } from "../../result/async-result.js";
|
|
||||||
import { Keyof } from "../../types/keyof.js";
|
|
||||||
import { QueryError } from "../driver.js";
|
|
||||||
import { FilterOptions } from "./filter-options.js";
|
|
||||||
import { OrderByOptions } from "./order-by-options.js";
|
|
||||||
|
|
||||||
export interface StoreQuery<T> {
|
|
||||||
where(where: FilterOptions<T>): StoreSortableQuery<T>;
|
|
||||||
orderBy(opts: OrderByOptions<T>): StoreLimitableQuery<T>;
|
|
||||||
limit(limit: number, offset?: number): SelectableQuery<T>;
|
|
||||||
|
|
||||||
select(): AsyncResult<T[], QueryError>;
|
|
||||||
select<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>[], QueryError>;
|
|
||||||
|
|
||||||
selectOne(): AsyncResult<T, QueryError>;
|
|
||||||
selectOne<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>, QueryError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoreSortableQuery<T> {
|
|
||||||
orderBy(opts: OrderByOptions<T>): StoreLimitableQuery<T>;
|
|
||||||
limit(limit: number, offset?: number): SelectableQuery<T>;
|
|
||||||
|
|
||||||
select(): AsyncResult<T[], QueryError>;
|
|
||||||
select<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>[], QueryError>;
|
|
||||||
|
|
||||||
selectOne(): AsyncResult<T, QueryError>;
|
|
||||||
selectOne<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>, QueryError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface StoreLimitableQuery<T> {
|
|
||||||
limit(limit: number, offset?: number): SelectableQuery<T>;
|
|
||||||
|
|
||||||
select(): AsyncResult<T[], QueryError>;
|
|
||||||
select<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>[], QueryError>;
|
|
||||||
|
|
||||||
selectOne(): AsyncResult<T, QueryError>;
|
|
||||||
selectOne<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>, QueryError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface SelectableQuery<T> {
|
|
||||||
select(): AsyncResult<T[], QueryError>;
|
|
||||||
select<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>[], QueryError>;
|
|
||||||
|
|
||||||
selectOne(): AsyncResult<T, QueryError>;
|
|
||||||
selectOne<K extends Keyof<T>>(keys: K[]): AsyncResult<Pick<T, K>, QueryError>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueryDefinition<K extends string = string> {
|
|
||||||
from: K;
|
|
||||||
where?: FilterOptions<any>;
|
|
||||||
orderBy?: OrderByOptions<any>;
|
|
||||||
limit?: number;
|
|
||||||
offset?: number;
|
|
||||||
keys?: string[];
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import {
|
|
||||||
ModelDefinition,
|
|
||||||
ModelFromName,
|
|
||||||
ModelName,
|
|
||||||
} from "../domain/models/create-model.js";
|
|
||||||
import { ModelToType } from "../domain/models/model-to-type.js";
|
|
||||||
import { StoreQuery } from "./query/query.js";
|
|
||||||
|
|
||||||
export interface StateStore<
|
|
||||||
TModels extends ModelDefinition<string, Record<string, any>>,
|
|
||||||
> {
|
|
||||||
from<TEntityName extends ModelName<TModels>>(
|
|
||||||
entityName: TEntityName,
|
|
||||||
): StoreQuery<ModelToType<ModelFromName<TModels, TEntityName>>>;
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
import { describe, expect, it } from "vitest";
|
|
||||||
import {
|
|
||||||
CircularDependencyError,
|
|
||||||
sortByDependencies,
|
|
||||||
} from "./sort-by-dependencies.js";
|
|
||||||
|
|
||||||
describe("sortByDependencies", () => {
|
|
||||||
it("should sort an array of objects by their dependencies", () => {
|
|
||||||
const array = [
|
|
||||||
{ id: 1, name: "A", dependencies: ["C", "D"] },
|
|
||||||
{ id: 2, name: "B", dependencies: ["A", "D"] },
|
|
||||||
{ id: 3, name: "C", dependencies: [] },
|
|
||||||
{ id: 4, name: "D", dependencies: [] },
|
|
||||||
];
|
|
||||||
|
|
||||||
const result = sortByDependencies(array, {
|
|
||||||
keyGetter: (element) => element.name,
|
|
||||||
depGetter: (element) => element.dependencies,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toEqual([
|
|
||||||
{ id: 3, name: "C", dependencies: [] },
|
|
||||||
{ id: 4, name: "D", dependencies: [] },
|
|
||||||
{ id: 1, name: "A", dependencies: ["C", "D"] },
|
|
||||||
{ id: 2, name: "B", dependencies: ["A", "D"] },
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should throw a CircularDependencyError when circular dependencies are detected", () => {
|
|
||||||
const array = [
|
|
||||||
{ id: 1, name: "A", dependencies: ["B"] },
|
|
||||||
{ id: 2, name: "B", dependencies: ["A"] },
|
|
||||||
];
|
|
||||||
|
|
||||||
expect(
|
|
||||||
sortByDependencies(array, {
|
|
||||||
keyGetter: (element) => element.name,
|
|
||||||
depGetter: (element) => element.dependencies,
|
|
||||||
}),
|
|
||||||
).toBeInstanceOf(CircularDependencyError);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return an empty array when the input array is empty", () => {
|
|
||||||
const array: any[] = [];
|
|
||||||
|
|
||||||
const result = sortByDependencies(array, {
|
|
||||||
keyGetter: (element) => element.name,
|
|
||||||
depGetter: (element) => element.dependencies,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(result).toEqual([]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@ -1,55 +0,0 @@
|
|||||||
import { TaggedError } from "../../error/tagged-error.js";
|
|
||||||
import { Result } from "../../result/result.js";
|
|
||||||
|
|
||||||
export function sortByDependencies<T>(
|
|
||||||
array: T[],
|
|
||||||
{
|
|
||||||
keyGetter,
|
|
||||||
depGetter,
|
|
||||||
}: {
|
|
||||||
keyGetter: (element: T) => string;
|
|
||||||
depGetter: (element: T) => string[];
|
|
||||||
},
|
|
||||||
): Result<T[], CircularDependencyError> {
|
|
||||||
const graph = new Map<string, string[]>();
|
|
||||||
const visited = new Set<string>();
|
|
||||||
const sorted: string[] = [];
|
|
||||||
array.forEach((element) => {
|
|
||||||
const key = keyGetter(element);
|
|
||||||
const deps = depGetter(element);
|
|
||||||
graph.set(key, deps);
|
|
||||||
});
|
|
||||||
const visit = (key: string, ancestors: string[]) => {
|
|
||||||
if (visited.has(key)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
ancestors.push(key);
|
|
||||||
const deps = graph.get(key) || [];
|
|
||||||
deps.forEach((dep) => {
|
|
||||||
if (ancestors.includes(dep)) {
|
|
||||||
throw new CircularDependencyError(key, dep);
|
|
||||||
}
|
|
||||||
visit(dep, ancestors.slice());
|
|
||||||
});
|
|
||||||
visited.add(key);
|
|
||||||
sorted.push(key);
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
graph.forEach((deps, key) => {
|
|
||||||
visit(key, []);
|
|
||||||
});
|
|
||||||
} catch (e) {
|
|
||||||
return e as CircularDependencyError;
|
|
||||||
}
|
|
||||||
return sorted.map(
|
|
||||||
(key) => array.find((element) => keyGetter(element) === key) as T,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export class CircularDependencyError extends TaggedError<"CircularDependencyError"> {
|
|
||||||
context: { key: string; dep: string };
|
|
||||||
constructor(key: string, dep: string) {
|
|
||||||
super("CircularDependencyError");
|
|
||||||
this.context = { key, dep };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1 +0,0 @@
|
|||||||
export type EnumToValues<T extends Record<string, string>> = T[keyof T];
|
|
||||||
@ -1,4 +1,2 @@
|
|||||||
export * from "./enum.js";
|
|
||||||
export * from "./fn.js";
|
export * from "./fn.js";
|
||||||
export * from "./keyof.js";
|
|
||||||
export * from "./optional.js";
|
export * from "./optional.js";
|
||||||
|
|||||||
@ -1,4 +0,0 @@
|
|||||||
/**
|
|
||||||
* Only string keys are allowed in the keyof type
|
|
||||||
*/
|
|
||||||
export type Keyof<T> = Extract<keyof T, string>;
|
|
||||||
@ -1 +0,0 @@
|
|||||||
# template-domain
|
|
||||||
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@ulthar/template-domain",
|
|
||||||
"type": "module",
|
|
||||||
"module": "dist/index.js",
|
|
||||||
"main": "dist/index.js",
|
|
||||||
"files": [
|
|
||||||
"dist"
|
|
||||||
],
|
|
||||||
"private": true,
|
|
||||||
"packageManager": "yarn@4.1.1",
|
|
||||||
"devDependencies": {
|
|
||||||
"typescript": "^5.5.4",
|
|
||||||
"vitest": "^2.0.5"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@ulthar/fabric-core": "workspace:^"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"test": "vitest",
|
|
||||||
"build": "tsc -p tsconfig.build.json"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
export * from "./events/index.js";
|
|
||||||
export * from "./models/index.js";
|
|
||||||
export * from "./security/index.js";
|
|
||||||
export * from "./services/index.js";
|
|
||||||
export * from "./use-cases/index.js";
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
export * from "./permission.js";
|
|
||||||
export * from "./policy.js";
|
|
||||||
export * from "./users.js";
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
import { EnumToValues } from "@ulthar/fabric-core";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A permission is a string that represents a something that a user is allowed to do in the system. It should be in the form of: `ACTION_ENTITY`.
|
|
||||||
* - `ACTION`: The domain action that the user can perform on the domain object. This is a domain verb in the imperative mood. i.e. "CREATE", "EDIT", "VIEW", "FIX", "RELEASE", etc.
|
|
||||||
* - `ENTITY`: The domain object that the user can perform the action on. This is a domain noun in the singular form.
|
|
||||||
*/
|
|
||||||
export const Permission = {} as const;
|
|
||||||
|
|
||||||
export type Permission = EnumToValues<typeof Permission>;
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
import { Policy } from "@ulthar/fabric-core";
|
|
||||||
import { Permission } from "./permission.js";
|
|
||||||
import { UserType } from "./users.js";
|
|
||||||
|
|
||||||
export const policy = {} as const satisfies Policy<UserType, Permission>;
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
import { EnumToValues } from "@ulthar/fabric-core";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A User Type is a string that represents a user type.
|
|
||||||
* It should be in uppercase and singular form.
|
|
||||||
*/
|
|
||||||
export const UserType = {
|
|
||||||
// ADMIN: "ADMIN",
|
|
||||||
// SPECIAL_USER: "SPECIAL_USER",
|
|
||||||
};
|
|
||||||
export type UserType = EnumToValues<typeof UserType>;
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
import { UseCaseDefinition } from "@ulthar/fabric-core";
|
|
||||||
|
|
||||||
export const UseCases = [] as const satisfies UseCaseDefinition[];
|
|
||||||
|
|
||||||
export type UseCases = typeof UseCases;
|
|
||||||
@ -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: ["**/index.ts"],
|
|
||||||
},
|
|
||||||
passWithNoTests: true,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
59
yarn.lock
59
yarn.lock
@ -390,10 +390,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@eslint/js@npm:9.10.0, @eslint/js@npm:^9.9.1":
|
"@eslint/js@npm:9.9.1, @eslint/js@npm:^9.9.1":
|
||||||
version: 9.10.0
|
version: 9.9.1
|
||||||
resolution: "@eslint/js@npm:9.10.0"
|
resolution: "@eslint/js@npm:9.9.1"
|
||||||
checksum: 10c0/2ac45a002dc1ccf25be46ea61001ada8d77248d1313ab4e53f3735e5ae00738a757874e41f62ad6fbd49df7dffeece66e5f53ff0d7b78a99ce4c68e8fea66753
|
checksum: 10c0/a3a91de2ce78469f7c4eee78c1eba77360706e1d0fa0ace2e19102079bcf237b851217c85ea501dc92c4c3719d60d9df966977abc8554d4c38e3638c1f53dcb2
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -404,15 +404,6 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@eslint/plugin-kit@npm:^0.1.0":
|
|
||||||
version: 0.1.0
|
|
||||||
resolution: "@eslint/plugin-kit@npm:0.1.0"
|
|
||||||
dependencies:
|
|
||||||
levn: "npm:^0.4.1"
|
|
||||||
checksum: 10c0/fae97cd4efc1c32501c286abba1b5409848ce8c989e1ca6a5bb057a304a2cd721e6e957f6bc35ce95cfd0871e822ed42df3c759fecdad72c30e70802e26f83c7
|
|
||||||
languageName: node
|
|
||||||
linkType: hard
|
|
||||||
|
|
||||||
"@humanwhocodes/module-importer@npm:^1.0.1":
|
"@humanwhocodes/module-importer@npm:^1.0.1":
|
||||||
version: 1.0.1
|
version: 1.0.1
|
||||||
resolution: "@humanwhocodes/module-importer@npm:1.0.1"
|
resolution: "@humanwhocodes/module-importer@npm:1.0.1"
|
||||||
@ -704,11 +695,11 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"@types/node@npm:*, @types/node@npm:>=20":
|
"@types/node@npm:*, @types/node@npm:>=20":
|
||||||
version: 22.5.4
|
version: 22.5.3
|
||||||
resolution: "@types/node@npm:22.5.4"
|
resolution: "@types/node@npm:22.5.3"
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: "npm:~6.19.2"
|
undici-types: "npm:~6.19.2"
|
||||||
checksum: 10c0/b445daa7eecd761ad4d778b882d6ff7bcc3b4baad2086ea9804db7c5d4a4ab0298b00d7f5315fc640a73b5a1d52bbf9628e09c9fec0cf44dbf9b4df674a8717d
|
checksum: 10c0/a068e31dd424a0eadfba7d9a5c5b415b76cfd729f3caa299674ad257f59df85c5fe77f1d0a343e811864c790baefb9003d7627618ee3cf85400af338481ba29f
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -856,16 +847,6 @@ __metadata:
|
|||||||
languageName: unknown
|
languageName: unknown
|
||||||
linkType: soft
|
linkType: soft
|
||||||
|
|
||||||
"@ulthar/template-domain@workspace:packages/templates/domain":
|
|
||||||
version: 0.0.0-use.local
|
|
||||||
resolution: "@ulthar/template-domain@workspace:packages/templates/domain"
|
|
||||||
dependencies:
|
|
||||||
"@ulthar/fabric-core": "workspace:^"
|
|
||||||
typescript: "npm:^5.5.4"
|
|
||||||
vitest: "npm:^2.0.5"
|
|
||||||
languageName: unknown
|
|
||||||
linkType: soft
|
|
||||||
|
|
||||||
"@vitest/expect@npm:2.0.5":
|
"@vitest/expect@npm:2.0.5":
|
||||||
version: 2.0.5
|
version: 2.0.5
|
||||||
resolution: "@vitest/expect@npm:2.0.5"
|
resolution: "@vitest/expect@npm:2.0.5"
|
||||||
@ -1238,14 +1219,14 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.6":
|
"debug@npm:4, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.4, debug@npm:^4.3.5, debug@npm:~4.3.6":
|
||||||
version: 4.3.7
|
version: 4.3.6
|
||||||
resolution: "debug@npm:4.3.7"
|
resolution: "debug@npm:4.3.6"
|
||||||
dependencies:
|
dependencies:
|
||||||
ms: "npm:^2.1.3"
|
ms: "npm:2.1.2"
|
||||||
peerDependenciesMeta:
|
peerDependenciesMeta:
|
||||||
supports-color:
|
supports-color:
|
||||||
optional: true
|
optional: true
|
||||||
checksum: 10c0/1471db19c3b06d485a622d62f65947a19a23fbd0dd73f7fd3eafb697eec5360cde447fb075919987899b1a2096e85d35d4eb5a4de09a57600ac9cf7e6c8e768b
|
checksum: 10c0/3293416bff072389c101697d4611c402a6bacd1900ac20c0492f61a9cdd6b3b29750fc7f5e299f8058469ef60ff8fb79b86395a30374fbd2490113c1c7112285
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -1516,15 +1497,14 @@ __metadata:
|
|||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"eslint@npm:^9.9.1":
|
"eslint@npm:^9.9.1":
|
||||||
version: 9.10.0
|
version: 9.9.1
|
||||||
resolution: "eslint@npm:9.10.0"
|
resolution: "eslint@npm:9.9.1"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@eslint-community/eslint-utils": "npm:^4.2.0"
|
"@eslint-community/eslint-utils": "npm:^4.2.0"
|
||||||
"@eslint-community/regexpp": "npm:^4.11.0"
|
"@eslint-community/regexpp": "npm:^4.11.0"
|
||||||
"@eslint/config-array": "npm:^0.18.0"
|
"@eslint/config-array": "npm:^0.18.0"
|
||||||
"@eslint/eslintrc": "npm:^3.1.0"
|
"@eslint/eslintrc": "npm:^3.1.0"
|
||||||
"@eslint/js": "npm:9.10.0"
|
"@eslint/js": "npm:9.9.1"
|
||||||
"@eslint/plugin-kit": "npm:^0.1.0"
|
|
||||||
"@humanwhocodes/module-importer": "npm:^1.0.1"
|
"@humanwhocodes/module-importer": "npm:^1.0.1"
|
||||||
"@humanwhocodes/retry": "npm:^0.3.0"
|
"@humanwhocodes/retry": "npm:^0.3.0"
|
||||||
"@nodelib/fs.walk": "npm:^1.2.8"
|
"@nodelib/fs.walk": "npm:^1.2.8"
|
||||||
@ -1547,6 +1527,7 @@ __metadata:
|
|||||||
is-glob: "npm:^4.0.0"
|
is-glob: "npm:^4.0.0"
|
||||||
is-path-inside: "npm:^3.0.3"
|
is-path-inside: "npm:^3.0.3"
|
||||||
json-stable-stringify-without-jsonify: "npm:^1.0.1"
|
json-stable-stringify-without-jsonify: "npm:^1.0.1"
|
||||||
|
levn: "npm:^0.4.1"
|
||||||
lodash.merge: "npm:^4.6.2"
|
lodash.merge: "npm:^4.6.2"
|
||||||
minimatch: "npm:^3.1.2"
|
minimatch: "npm:^3.1.2"
|
||||||
natural-compare: "npm:^1.4.0"
|
natural-compare: "npm:^1.4.0"
|
||||||
@ -1560,7 +1541,7 @@ __metadata:
|
|||||||
optional: true
|
optional: true
|
||||||
bin:
|
bin:
|
||||||
eslint: bin/eslint.js
|
eslint: bin/eslint.js
|
||||||
checksum: 10c0/7357f3995b15043eea83c8c0ab16c385ce3f28925c1b11cfcd6b2ede8faab3d91ede84a68173dd5f6e3e176e177984e6218de58b7b8388e53e2881f1ec07c836
|
checksum: 10c0/5e71efda7c0a14ee95436d5cdfed04ee61dfb1d89d7a32b50a424de2e680af82849628ea6581950c2e0726491f786a3cfd0032ce013c1c5093786e475cfdfb33
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
@ -2383,10 +2364,10 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
"ms@npm:^2.1.3":
|
"ms@npm:2.1.2":
|
||||||
version: 2.1.3
|
version: 2.1.2
|
||||||
resolution: "ms@npm:2.1.3"
|
resolution: "ms@npm:2.1.2"
|
||||||
checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48
|
checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc
|
||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user