feat: add peerDependencies

This commit is contained in:
2025-07-07 13:11:16 +02:00
parent 2093dd7097
commit dd1570d52e
36 changed files with 481 additions and 274 deletions

14
.prettierrc Normal file
View File

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

View File

@@ -1,5 +1,6 @@
{ {
"deno.enable": true, "deno.enable": true,
"deno.lint": false,
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit" "source.fixAll.eslint": "explicit"

View File

@@ -77,9 +77,9 @@ Once we have defined our configs and printed our events we create a new event st
for `sqlite`, `postgres`, and `valkyr/db` which all works the same way. So for this example we will use the `sqlite` for `sqlite`, `postgres`, and `valkyr/db` which all works the same way. So for this example we will use the `sqlite`
store. store.
- Browser _(TODO)_ - Browser _(TODO)_
- Mongo _(TODO)_ - Mongo _(TODO)_
- [Postgres](./adapters/postgres) - [Postgres](./adapters/postgres)
### Reducers ### Reducers
@@ -92,26 +92,33 @@ import { makeReducer } from "@valkyr/event-store";
import type { EventRecord } from "./generated/events.ts"; import type { EventRecord } from "./generated/events.ts";
const reducer = makeReducer<{ const reducer = makeReducer<
name: string; {
email: string; name: string;
}, EventRecord>((state, event) => { email: string;
switch (event.type) { },
case "user:created": { EventRecord
state.name = `${event.data.name.given} ${event.data.name.family}`; >(
state.email = event.data.email; (state, event) => {
break; switch (event.type) {
case "user:created": {
state.name = `${event.data.name.given} ${event.data.name.family}`;
state.email = event.data.email;
break;
}
case "user:email-set": {
state.email = event.data.email;
break;
}
} }
case "user:email-set": { return state;
state.email = event.data.email; },
break; "user",
} () => ({
} name: "",
return state; email: "",
}, "user", () => ({ }),
name: "", );
email: "",
}));
``` ```
### Aggreates ### Aggreates
@@ -172,7 +179,7 @@ export class User extends AggregateRoot<EventRecord> {
type Name = { type Name = {
given: string; given: string;
family: string; family: string;
} };
``` ```
### Projectors ### Projectors
@@ -201,7 +208,6 @@ When handling events in a distributed system or during event replay operations,
This mechanism ensures that critical one-time operations (such as sending emails or initiating external API calls) are **not repeated** unnecessarily while still allowing stateful projections to update their read models correctly. This mechanism ensures that critical one-time operations (such as sending emails or initiating external API calls) are **not repeated** unnecessarily while still allowing stateful projections to update their read models correctly.
#### `.once("user:created", (event) => Promise<void>)` #### `.once("user:created", (event) => Promise<void>)`
This handler tells the projection that an event is only ever processed when the event is originating directly from the This handler tells the projection that an event is only ever processed when the event is originating directly from the

View File

@@ -1,4 +1,4 @@
import z from "zod"; import z from "zod/v4";
import type { CollectionRegistrar } from "../types.ts"; import type { CollectionRegistrar } from "../types.ts";

View File

@@ -3,8 +3,4 @@ import { registrar as events } from "./events.ts";
import { registrar as relations } from "./relations.ts"; import { registrar as relations } from "./relations.ts";
import { registrar as snapshots } from "./snapshots.ts"; import { registrar as snapshots } from "./snapshots.ts";
export const registrars: CollectionRegistrar[] = [ export const registrars: CollectionRegistrar[] = [events, relations, snapshots];
events,
relations,
snapshots,
];

View File

@@ -1,4 +1,4 @@
import z from "zod"; import z from "zod/v4";
import type { CollectionRegistrar } from "../types.ts"; import type { CollectionRegistrar } from "../types.ts";

View File

@@ -1,4 +1,4 @@
import z from "zod"; import z from "zod/v4";
import type { CollectionRegistrar } from "../types.ts"; import type { CollectionRegistrar } from "../types.ts";

View File

@@ -70,7 +70,10 @@ export class MongoEventsProvider implements EventsProvider {
* @param options - Read options for modifying the result. * @param options - Read options for modifying the result.
*/ */
async getByStreams(streams: string[], options: EventReadOptions = {}): Promise<EventRecord[]> { async getByStreams(streams: string[], options: EventReadOptions = {}): Promise<EventRecord[]> {
return (await this.#withReadOptions(this.collection.find({ stream: { $in: streams }, ...this.#withFilters(options) }), options) return (await this.#withReadOptions(
this.collection.find({ stream: { $in: streams }, ...this.#withFilters(options) }),
options,
)
.sort({ created: 1 }) .sort({ created: 1 })
.toArray() .toArray()
.then(toParsedRecords(schema))) as EventRecord[]; .then(toParsedRecords(schema))) as EventRecord[];

View File

@@ -1,5 +1,5 @@
import type { Db, WithId } from "mongodb"; import type { Db, WithId } from "mongodb";
import type { z, ZodObject } from "zod"; import type { z, ZodObject } from "zod/v4";
/** /**
* Take a list of records and run it through the given zod parser. This * Take a list of records and run it through the given zod parser. This
@@ -9,7 +9,9 @@ import type { z, ZodObject } from "zod";
* *
* @param parser - Zod parser to run the documents through. * @param parser - Zod parser to run the documents through.
*/ */
export function toParsedRecords<TSchema extends ZodObject>(parser: TSchema): (documents: WithId<object>[]) => z.infer<TSchema>[] { export function toParsedRecords<TSchema extends ZodObject>(
parser: TSchema,
): (documents: WithId<object>[]) => z.infer<TSchema>[] {
return parser.array().parse; return parser.array().parse;
} }
@@ -21,7 +23,9 @@ export function toParsedRecords<TSchema extends ZodObject>(parser: TSchema): (do
* *
* @param parser - Zod parser to run the document through. * @param parser - Zod parser to run the document through.
*/ */
export function toParsedRecord<TSchema extends ZodObject>(parser: TSchema): (document: WithId<object> | null) => z.infer<TSchema> | undefined { export function toParsedRecord<TSchema extends ZodObject>(
parser: TSchema,
): (document: WithId<object> | null) => z.infer<TSchema> | undefined {
return function (document) { return function (document) {
if (document === null) { if (document === null) {
return undefined; return undefined;

View File

@@ -1,6 +1,10 @@
import type { Options, Sql } from "postgres"; import type { Options, Sql } from "postgres";
export type PostgresConnection = [PostgresConnectionUrl, Options<any>?] | [Options<any>] | Sql | PostgresConnectionFactory; export type PostgresConnection =
| [PostgresConnectionUrl, Options<any>?]
| [Options<any>]
| Sql
| PostgresConnectionFactory;
type PostgresConnectionUrl = `postgres://${string}:${string}@${string}:${number}/${string}`; type PostgresConnectionUrl = `postgres://${string}:${string}@${string}:${number}/${string}`;

View File

@@ -76,7 +76,10 @@ export class PostgresEventsProvider implements EventsProvider {
* @param stream - Stream to fetch events for. * @param stream - Stream to fetch events for.
* @param options - Read options for modifying the result. * @param options - Read options for modifying the result.
*/ */
async getByStream(stream: string, { filter, cursor, direction, limit }: EventReadOptions = {}): Promise<EventRecord[]> { async getByStream(
stream: string,
{ filter, cursor, direction, limit }: EventReadOptions = {},
): Promise<EventRecord[]> {
return this.db.sql<PGEventRecord[]>` return this.db.sql<PGEventRecord[]>`
SELECT * FROM ${this.table} SELECT * FROM ${this.table}
WHERE WHERE
@@ -94,7 +97,10 @@ export class PostgresEventsProvider implements EventsProvider {
* @param streams - Stream to get events for. * @param streams - Stream to get events for.
* @param options - Read options for modifying the result. * @param options - Read options for modifying the result.
*/ */
async getByStreams(streams: string[], { filter, cursor, direction, limit }: EventReadOptions = {}): Promise<EventRecord[]> { async getByStreams(
streams: string[],
{ filter, cursor, direction, limit }: EventReadOptions = {},
): Promise<EventRecord[]> {
return this.db.sql<PGEventRecord[]>` return this.db.sql<PGEventRecord[]>`
SELECT * FROM ${this.table} SELECT * FROM ${this.table}
WHERE WHERE
@@ -112,7 +118,9 @@ export class PostgresEventsProvider implements EventsProvider {
* @param id - Event id. * @param id - Event id.
*/ */
async getById(id: string): Promise<EventRecord | undefined> { async getById(id: string): Promise<EventRecord | undefined> {
return this.db.sql<PGEventRecord[]>`SELECT * FROM ${this.table} WHERE id = ${id}`.then(this.#fromDriver).then(([record]) => record); return this.db.sql<PGEventRecord[]>`SELECT * FROM ${this.table} WHERE id = ${id}`
.then(this.#fromDriver)
.then(([record]) => record);
} }
/** /**

View File

@@ -35,9 +35,11 @@ export class PostgresRelationsProvider implements RelationsProvider {
* @param stream - Stream to add to the key. * @param stream - Stream to add to the key.
*/ */
async insert(key: string, stream: string): Promise<void> { async insert(key: string, stream: string): Promise<void> {
await this.db.sql`INSERT INTO ${this.table} (key, stream) VALUES (${key}, ${stream}) ON CONFLICT DO NOTHING`.catch((error) => { await this.db.sql`INSERT INTO ${this.table} (key, stream) VALUES (${key}, ${stream}) ON CONFLICT DO NOTHING`.catch(
throw new Error(`EventStore > 'relations.insert' failed with postgres error: ${error.message}`); (error) => {
}); throw new Error(`EventStore > 'relations.insert' failed with postgres error: ${error.message}`);
},
);
} }
/** /**
@@ -107,7 +109,9 @@ export class PostgresRelationsProvider implements RelationsProvider {
await this.db.sql await this.db.sql
.begin(async (sql) => { .begin(async (sql) => {
for (let i = 0; i < relations.length; i += batchSize) { for (let i = 0; i < relations.length; i += batchSize) {
const conditions = relations.slice(i, i + batchSize).map(({ key, stream }) => `(key = '${key}' AND stream = '${stream}')`); const conditions = relations
.slice(i, i + batchSize)
.map(({ key, stream }) => `(key = '${key}' AND stream = '${stream}')`);
await sql`DELETE FROM ${this.table} WHERE ${this.db.sql.unsafe(conditions.join(" OR "))}`; await sql`DELETE FROM ${this.table} WHERE ${this.db.sql.unsafe(conditions.join(" OR "))}`;
} }
}) })

View File

@@ -8,16 +8,12 @@
"./postgres": "./adapters/postgres/adapter.ts" "./postgres": "./adapters/postgres/adapter.ts"
}, },
"publish": { "publish": {
"exclude": [ "exclude": [".github", ".vscode", ".gitignore", "tests"]
".github",
".vscode",
".gitignore",
"tests"
]
}, },
"tasks": { "tasks": {
"check": "deno check ./mod.ts", "check": "deno check ./mod.ts",
"lint": "npx eslint -c eslint.config.mjs .", "lint": "npx eslint -c eslint.config.mjs .",
"fmt": "npx prettier --write .",
"test": "deno test --allow-all", "test": "deno test --allow-all",
"test:publish": "deno publish --dry-run", "test:publish": "deno publish --dry-run",
"ncu": "npx ncu -u -p npm" "ncu": "npx ncu -u -p npm"

322
deno.lock generated
View File

@@ -1,26 +1,24 @@
{ {
"version": "4", "version": "5",
"specifiers": { "specifiers": {
"npm:@jsr/std__assert@1.0.12": "1.0.12",
"npm:@jsr/std__assert@1.0.13": "1.0.13", "npm:@jsr/std__assert@1.0.13": "1.0.13",
"npm:@jsr/std__async@1.0.12": "1.0.12", "npm:@jsr/std__async@1.0.13": "1.0.13",
"npm:@jsr/std__testing@1.0.11": "1.0.11", "npm:@jsr/std__testing@1.0.14": "1.0.14",
"npm:@jsr/valkyr__testcontainers@2.0.0": "2.0.0", "npm:@jsr/valkyr__testcontainers@2.0.1": "2.0.1",
"npm:@valkyr/db@1.0.1": "1.0.1", "npm:@valkyr/db@^1.0.1": "1.0.1",
"npm:eslint-plugin-simple-import-sort@12.1.1": "12.1.1_eslint@9.25.1", "npm:eslint-plugin-simple-import-sort@12.1.1": "12.1.1_eslint@9.30.1",
"npm:eslint@9.25.1": "9.25.1", "npm:eslint@9.30.1": "9.30.1",
"npm:fake-indexeddb@6.0.0": "6.0.0", "npm:fake-indexeddb@6.0.1": "6.0.1",
"npm:mongodb@6.15.0": "6.15.0", "npm:mongodb@6.17.0": "6.17.0",
"npm:nanoid@5.1.5": "5.1.5", "npm:nanoid@5.1.5": "5.1.5",
"npm:postgres@3.4.5": "3.4.5", "npm:postgres@3.4.7": "3.4.7",
"npm:postgres@^3.4.5": "3.4.5", "npm:prettier@3.6.2": "3.6.2",
"npm:prettier@3.5.3": "3.5.3", "npm:typescript-eslint@8.35.1": "8.35.1_eslint@9.30.1_typescript@5.8.3_@typescript-eslint+parser@8.35.1__eslint@9.30.1__typescript@5.8.3",
"npm:typescript-eslint@8.31.0": "8.31.0_eslint@9.25.1_typescript@5.8.3_@typescript-eslint+parser@8.31.0__eslint@9.25.1__typescript@5.8.3", "npm:zod@3.25.75": "3.25.75"
"npm:zod@next": "4.0.0-beta.20250420T053007"
}, },
"npm": { "npm": {
"@eslint-community/eslint-utils@4.6.1_eslint@9.25.1": { "@eslint-community/eslint-utils@4.7.0_eslint@9.30.1": {
"integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
"dependencies": [ "dependencies": [
"eslint", "eslint",
"eslint-visitor-keys@3.4.3" "eslint-visitor-keys@3.4.3"
@@ -29,19 +27,25 @@
"@eslint-community/regexpp@4.12.1": { "@eslint-community/regexpp@4.12.1": {
"integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==" "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ=="
}, },
"@eslint/config-array@0.20.0": { "@eslint/config-array@0.21.0": {
"integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==",
"dependencies": [ "dependencies": [
"@eslint/object-schema", "@eslint/object-schema",
"debug", "debug",
"minimatch@3.1.2" "minimatch@3.1.2"
] ]
}, },
"@eslint/config-helpers@0.2.1": { "@eslint/config-helpers@0.3.0": {
"integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==" "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw=="
}, },
"@eslint/core@0.13.0": { "@eslint/core@0.14.0": {
"integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==",
"dependencies": [
"@types/json-schema"
]
},
"@eslint/core@0.15.1": {
"integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==",
"dependencies": [ "dependencies": [
"@types/json-schema" "@types/json-schema"
] ]
@@ -53,23 +57,23 @@
"debug", "debug",
"espree", "espree",
"globals", "globals",
"ignore", "ignore@5.3.2",
"import-fresh", "import-fresh",
"js-yaml", "js-yaml",
"minimatch@3.1.2", "minimatch@3.1.2",
"strip-json-comments" "strip-json-comments"
] ]
}, },
"@eslint/js@9.25.1": { "@eslint/js@9.30.1": {
"integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==" "integrity": "sha512-zXhuECFlyep42KZUhWjfvsmXGX39W8K8LFb8AWXM9gSV9dQB+MrJGLKvW6Zw0Ggnbpw0VHTtrhFXYe3Gym18jg=="
}, },
"@eslint/object-schema@2.1.6": { "@eslint/object-schema@2.1.6": {
"integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==" "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA=="
}, },
"@eslint/plugin-kit@0.2.8": { "@eslint/plugin-kit@0.3.3": {
"integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", "integrity": "sha512-1+WqvgNMhmlAambTvT3KPtCl/Ibr68VldY2XY40SL1CE0ZXiakFR/cbTspaF5HsnpDMvcYYoJHfl4980NBjGag==",
"dependencies": [ "dependencies": [
"@eslint/core", "@eslint/core@0.15.1",
"levn" "levn"
] ]
}, },
@@ -89,65 +93,72 @@
"@humanwhocodes/retry@0.3.1": { "@humanwhocodes/retry@0.3.1": {
"integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==" "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="
}, },
"@humanwhocodes/retry@0.4.2": { "@humanwhocodes/retry@0.4.3": {
"integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==" "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ=="
},
"@jsr/std__assert@1.0.12": {
"integrity": "sha512-9pmgjJhuljZCmLlbvsRV6aLT5+YCmhX/yIjaWYav7R7Vup2DOLAgpUOs4JkzRbwn7fdKYrwHT8+DjqPr7Ti8mg==",
"dependencies": [
"@jsr/std__internal"
]
}, },
"@jsr/std__assert@1.0.13": { "@jsr/std__assert@1.0.13": {
"integrity": "sha512-rZ44REoi2/p+gqu8OfkcNeaTOSiG1kD6v8gyA0YjkXsOkDsiGw9g8h7JuGC/OD7GgOVgTEY+9Cih49Y18rkrCQ==", "integrity": "sha512-rZ44REoi2/p+gqu8OfkcNeaTOSiG1kD6v8gyA0YjkXsOkDsiGw9g8h7JuGC/OD7GgOVgTEY+9Cih49Y18rkrCQ==",
"dependencies": [ "dependencies": [
"@jsr/std__internal" "@jsr/std__internal"
] ],
"tarball": "https://npm.jsr.io/~/11/@jsr/std__assert/1.0.13.tgz"
}, },
"@jsr/std__async@1.0.12": { "@jsr/std__async@1.0.13": {
"integrity": "sha512-NUaSOcwMetVeVkIqet2Ammy2A5YxG8ViFxryBbTaC4h7l/cgAkU59U3zF58ek4Y8HZ0Nx5De7qBptPfp62kcgw==" "integrity": "sha512-GEApyNtzauJ0kEZ/GxebSkdEN0t29qJtkw+WEvzYTwkL6fHX8cq3YWzRjCqHu+4jMl+rpHiwyr/lfitNInntzA==",
"tarball": "https://npm.jsr.io/~/11/@jsr/std__async/1.0.13.tgz"
}, },
"@jsr/std__data-structures@1.0.6": { "@jsr/std__data-structures@1.0.8": {
"integrity": "sha512-Ejc8mHLuoYxXLu2zPquvqijdgQ19OV+1DdVDrLc/Cg+tiuGh4Dq2FSnLiPINh4lO1AJ3XcZcYPx38RxdsZcCOg==" "integrity": "sha512-7BHBUlBEJ/9w2zv9sNmyuQOINBTEP1erxLHMpIDBa7GMCV1Nxm6LvgC4R5cgN90FFKpoCFa9PPB66Hkeem9Q2g==",
"tarball": "https://npm.jsr.io/~/11/@jsr/std__data-structures/1.0.8.tgz"
}, },
"@jsr/std__fs@1.0.16": { "@jsr/std__fs@1.0.19": {
"integrity": "sha512-xnqp8XqEFN+ttkERg9GG+AxyipSd+rfCquLPviF5ZSwN6oCV1TM0ZNoKHXNk/EJAsz28YjF4sfgdJt8XwTV2UQ==", "integrity": "sha512-TEjyE8g+46jPlu7dJHLrwc8NMGl8zfG+JjWxyNQyDbxP0RtqZ4JmYZfR9vy4RWYWJQbLpw6Kbt2n+K/2zAO/JA==",
"dependencies": [ "dependencies": [
"@jsr/std__internal",
"@jsr/std__path" "@jsr/std__path"
] ],
"tarball": "https://npm.jsr.io/~/11/@jsr/std__fs/1.0.19.tgz"
}, },
"@jsr/std__internal@1.0.6": { "@jsr/std__internal@1.0.9": {
"integrity": "sha512-1NLtCx9XAL44nt56gzmRSCgXjIthHVzK62fTkJdq8/XsP7eN9a21AZDpc0EGJ/cgvmmOB52UGh46OuKrrY7eVg==" "integrity": "sha512-s+f4qrJzZgPAy7XuFOtgaSaxyPLnnEmAfXGLvRXGxPTL76URLVHkF+hOzqXz+bmk8/awybF6BRsasxtAQOV23Q==",
"tarball": "https://npm.jsr.io/~/11/@jsr/std__internal/1.0.9.tgz"
}, },
"@jsr/std__net@1.0.4": { "@jsr/std__net@1.0.4": {
"integrity": "sha512-KJGU8ZpQ70sMW2Zk+wU3wFUkggS9lTLfRFBygnV9VaK8KI+1ggiqtB06rH4a14CNRGM9y46Mn/ZCbQUd4Q45Jg==" "integrity": "sha512-KJGU8ZpQ70sMW2Zk+wU3wFUkggS9lTLfRFBygnV9VaK8KI+1ggiqtB06rH4a14CNRGM9y46Mn/ZCbQUd4Q45Jg==",
"tarball": "https://npm.jsr.io/~/11/@jsr/std__net/1.0.4.tgz"
}, },
"@jsr/std__path@1.0.8": { "@jsr/std__path@1.1.1": {
"integrity": "sha512-eNBGlh/8ZVkMxtFH4bwIzlAeKoHYk5in4wrBZhi20zMdOiuX4QozP4+19mIXBT2lzHDjhuVLyECbhFeR304iDg==" "integrity": "sha512-+x5LgcNUSpMzOZIRmFSjqrMTCxHlgXjWzK8ZFr7lwgHfWZxoVXeis3MFQlkR5mN5uQ61Y1P30Li1PU0yx9uluA==",
},
"@jsr/std__testing@1.0.11": {
"integrity": "sha512-pqQDYtIsaDf+x4NHQ+WiixRJ8DfhgFQRdlHWWssFAzIYwleR+VHLTNlgsgg+AH3mIIR+gTkBmKk21hTkM/WbMQ==",
"dependencies": [ "dependencies": [
"@jsr/std__assert@1.0.12", "@jsr/std__internal"
],
"tarball": "https://npm.jsr.io/~/11/@jsr/std__path/1.1.1.tgz"
},
"@jsr/std__testing@1.0.14": {
"integrity": "sha512-WQ2ctU3AmV0dcaVEahIUfz4e+3+Y3UMyqFLjCZ6JKeI40zkDpeMFBsTop7e7ptGE4wgHrQj+FETh9XAgEuBlZA==",
"dependencies": [
"@jsr/std__assert",
"@jsr/std__async", "@jsr/std__async",
"@jsr/std__data-structures", "@jsr/std__data-structures",
"@jsr/std__fs", "@jsr/std__fs",
"@jsr/std__internal", "@jsr/std__internal",
"@jsr/std__path" "@jsr/std__path"
] ],
"tarball": "https://npm.jsr.io/~/11/@jsr/std__testing/1.0.14.tgz"
}, },
"@jsr/valkyr__testcontainers@2.0.0": { "@jsr/valkyr__testcontainers@2.0.1": {
"integrity": "sha512-aK78hRoVyQm3M0aFucuUV7Ghfx4295fJ6Q3fSjtJizYnu10VuktKfcIh5xHhOVAISk1Zh0y3SYGIiuZiKr57vw==", "integrity": "sha512-HInqMkCDj1ICrcz+Led/3jyLa70mwncxdlly8v/5WepuPW3gszKftq5U2jbjy2THOYUP7ibBK2o0recg7qhvcw==",
"dependencies": [ "dependencies": [
"@jsr/std__async", "@jsr/std__async",
"@jsr/std__fs", "@jsr/std__fs",
"@jsr/std__net", "@jsr/std__net",
"mongodb", "mongodb",
"postgres" "postgres"
] ],
"tarball": "https://npm.jsr.io/~/11/@jsr/valkyr__testcontainers/2.0.1.tgz"
}, },
"@mongodb-js/saslprep@1.2.2": { "@mongodb-js/saslprep@1.3.0": {
"integrity": "sha512-EB0O3SCSNRUFk66iRCpI+cXzIjdswfCs7F6nOC3RAGJ7xr5YhaicvsRwJ9eyzYvYRlCSDUO/c7g4yNulxKC1WA==", "integrity": "sha512-zlayKCsIjYb7/IdfqxorK5+xUMyi4vOKcFy10wKJYc63NSdKI8mNME+uJqfatkPmOSMMUiojrL58IePKBm3gvQ==",
"dependencies": [ "dependencies": [
"sparse-bitfield" "sparse-bitfield"
] ]
@@ -169,8 +180,8 @@
"fastq" "fastq"
] ]
}, },
"@types/estree@1.0.7": { "@types/estree@1.0.8": {
"integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==" "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
}, },
"@types/json-schema@7.0.15": { "@types/json-schema@7.0.15": {
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="
@@ -184,8 +195,8 @@
"@types/webidl-conversions" "@types/webidl-conversions"
] ]
}, },
"@typescript-eslint/eslint-plugin@8.31.0_@typescript-eslint+parser@8.31.0__eslint@9.25.1__typescript@5.8.3_eslint@9.25.1_typescript@5.8.3": { "@typescript-eslint/eslint-plugin@8.35.1_@typescript-eslint+parser@8.35.1__eslint@9.30.1__typescript@5.8.3_eslint@9.30.1_typescript@5.8.3": {
"integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", "integrity": "sha512-9XNTlo7P7RJxbVeICaIIIEipqxLKguyh+3UbXuT2XQuFp6d8VOeDEGuz5IiX0dgZo8CiI6aOFLg4e8cF71SFVg==",
"dependencies": [ "dependencies": [
"@eslint-community/regexpp", "@eslint-community/regexpp",
"@typescript-eslint/parser", "@typescript-eslint/parser",
@@ -195,14 +206,14 @@
"@typescript-eslint/visitor-keys", "@typescript-eslint/visitor-keys",
"eslint", "eslint",
"graphemer", "graphemer",
"ignore", "ignore@7.0.5",
"natural-compare", "natural-compare",
"ts-api-utils", "ts-api-utils",
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/parser@8.31.0_eslint@9.25.1_typescript@5.8.3": { "@typescript-eslint/parser@8.35.1_eslint@9.30.1_typescript@5.8.3": {
"integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", "integrity": "sha512-3MyiDfrfLeK06bi/g9DqJxP5pV74LNv4rFTyvGDmT3x2p1yp1lOd+qYZfiRPIOf/oON+WRZR5wxxuF85qOar+w==",
"dependencies": [ "dependencies": [
"@typescript-eslint/scope-manager", "@typescript-eslint/scope-manager",
"@typescript-eslint/types", "@typescript-eslint/types",
@@ -213,15 +224,30 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/scope-manager@8.31.0": { "@typescript-eslint/project-service@8.35.1_typescript@5.8.3": {
"integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", "integrity": "sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==",
"dependencies": [
"@typescript-eslint/tsconfig-utils",
"@typescript-eslint/types",
"debug",
"typescript"
]
},
"@typescript-eslint/scope-manager@8.35.1": {
"integrity": "sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==",
"dependencies": [ "dependencies": [
"@typescript-eslint/types", "@typescript-eslint/types",
"@typescript-eslint/visitor-keys" "@typescript-eslint/visitor-keys"
] ]
}, },
"@typescript-eslint/type-utils@8.31.0_eslint@9.25.1_typescript@5.8.3": { "@typescript-eslint/tsconfig-utils@8.35.1_typescript@5.8.3": {
"integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", "integrity": "sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==",
"dependencies": [
"typescript"
]
},
"@typescript-eslint/type-utils@8.35.1_eslint@9.30.1_typescript@5.8.3": {
"integrity": "sha512-HOrUBlfVRz5W2LIKpXzZoy6VTZzMu2n8q9C2V/cFngIC5U1nStJgv0tMV4sZPzdf4wQm9/ToWUFPMN9Vq9VJQQ==",
"dependencies": [ "dependencies": [
"@typescript-eslint/typescript-estree", "@typescript-eslint/typescript-estree",
"@typescript-eslint/utils", "@typescript-eslint/utils",
@@ -231,12 +257,14 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/types@8.31.0": { "@typescript-eslint/types@8.35.1": {
"integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==" "integrity": "sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ=="
}, },
"@typescript-eslint/typescript-estree@8.31.0_typescript@5.8.3": { "@typescript-eslint/typescript-estree@8.35.1_typescript@5.8.3": {
"integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", "integrity": "sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==",
"dependencies": [ "dependencies": [
"@typescript-eslint/project-service",
"@typescript-eslint/tsconfig-utils",
"@typescript-eslint/types", "@typescript-eslint/types",
"@typescript-eslint/visitor-keys", "@typescript-eslint/visitor-keys",
"debug", "debug",
@@ -248,8 +276,8 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/utils@8.31.0_eslint@9.25.1_typescript@5.8.3": { "@typescript-eslint/utils@8.35.1_eslint@9.30.1_typescript@5.8.3": {
"integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", "integrity": "sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==",
"dependencies": [ "dependencies": [
"@eslint-community/eslint-utils", "@eslint-community/eslint-utils",
"@typescript-eslint/scope-manager", "@typescript-eslint/scope-manager",
@@ -259,11 +287,11 @@
"typescript" "typescript"
] ]
}, },
"@typescript-eslint/visitor-keys@8.31.0": { "@typescript-eslint/visitor-keys@8.35.1": {
"integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", "integrity": "sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==",
"dependencies": [ "dependencies": [
"@typescript-eslint/types", "@typescript-eslint/types",
"eslint-visitor-keys@4.2.0" "eslint-visitor-keys@4.2.1"
] ]
}, },
"@valkyr/db@1.0.1": { "@valkyr/db@1.0.1": {
@@ -278,17 +306,15 @@
"rxjs" "rxjs"
] ]
}, },
"@zod/core@0.8.1": { "acorn-jsx@5.3.2_acorn@8.15.0": {
"integrity": "sha512-djj8hPhxIHcG8ptxITaw/Bout5HJZ9NyRbKr95Eilqwt9R0kvITwUQGDU+n+MVdsBIka5KwztmZSLti22F+P0A=="
},
"acorn-jsx@5.3.2_acorn@8.14.1": {
"integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
"dependencies": [ "dependencies": [
"acorn" "acorn"
] ]
}, },
"acorn@8.14.1": { "acorn@8.15.0": {
"integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==" "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"bin": true
}, },
"ajv@6.12.6": { "ajv@6.12.6": {
"integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
@@ -311,15 +337,15 @@
"balanced-match@1.0.2": { "balanced-match@1.0.2": {
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
}, },
"brace-expansion@1.1.11": { "brace-expansion@1.1.12": {
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
"dependencies": [ "dependencies": [
"balanced-match", "balanced-match",
"concat-map" "concat-map"
] ]
}, },
"brace-expansion@2.0.1": { "brace-expansion@2.0.2": {
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dependencies": [ "dependencies": [
"balanced-match" "balanced-match"
] ]
@@ -330,8 +356,8 @@
"fill-range" "fill-range"
] ]
}, },
"bson@6.10.3": { "bson@6.10.4": {
"integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==" "integrity": "sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng=="
}, },
"callsites@3.1.0": { "callsites@3.1.0": {
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==" "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
@@ -363,8 +389,8 @@
"which" "which"
] ]
}, },
"debug@4.4.0": { "debug@4.4.1": {
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
"dependencies": [ "dependencies": [
"ms" "ms"
] ]
@@ -381,14 +407,14 @@
"escape-string-regexp@4.0.0": { "escape-string-regexp@4.0.0": {
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
}, },
"eslint-plugin-simple-import-sort@12.1.1_eslint@9.25.1": { "eslint-plugin-simple-import-sort@12.1.1_eslint@9.30.1": {
"integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==", "integrity": "sha512-6nuzu4xwQtE3332Uz0to+TxDQYRLTKRESSc2hefVT48Zc8JthmN23Gx9lnYhu0FtkRSL1oxny3kJ2aveVhmOVA==",
"dependencies": [ "dependencies": [
"eslint" "eslint"
] ]
}, },
"eslint-scope@8.3.0": { "eslint-scope@8.4.0": {
"integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
"dependencies": [ "dependencies": [
"esrecurse", "esrecurse",
"estraverse" "estraverse"
@@ -397,23 +423,23 @@
"eslint-visitor-keys@3.4.3": { "eslint-visitor-keys@3.4.3": {
"integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==" "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="
}, },
"eslint-visitor-keys@4.2.0": { "eslint-visitor-keys@4.2.1": {
"integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==" "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ=="
}, },
"eslint@9.25.1": { "eslint@9.30.1": {
"integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", "integrity": "sha512-zmxXPNMOXmwm9E0yQLi5uqXHs7uq2UIiqEKo3Gq+3fwo1XrJ+hijAZImyF7hclW3E6oHz43Yk3RP8at6OTKflQ==",
"dependencies": [ "dependencies": [
"@eslint-community/eslint-utils", "@eslint-community/eslint-utils",
"@eslint-community/regexpp", "@eslint-community/regexpp",
"@eslint/config-array", "@eslint/config-array",
"@eslint/config-helpers", "@eslint/config-helpers",
"@eslint/core", "@eslint/core@0.14.0",
"@eslint/eslintrc", "@eslint/eslintrc",
"@eslint/js", "@eslint/js",
"@eslint/plugin-kit", "@eslint/plugin-kit",
"@humanfs/node", "@humanfs/node",
"@humanwhocodes/module-importer", "@humanwhocodes/module-importer",
"@humanwhocodes/retry@0.4.2", "@humanwhocodes/retry@0.4.3",
"@types/estree", "@types/estree",
"@types/json-schema", "@types/json-schema",
"ajv", "ajv",
@@ -422,7 +448,7 @@
"debug", "debug",
"escape-string-regexp", "escape-string-regexp",
"eslint-scope", "eslint-scope",
"eslint-visitor-keys@4.2.0", "eslint-visitor-keys@4.2.1",
"espree", "espree",
"esquery", "esquery",
"esutils", "esutils",
@@ -430,7 +456,7 @@
"file-entry-cache", "file-entry-cache",
"find-up", "find-up",
"glob-parent@6.0.2", "glob-parent@6.0.2",
"ignore", "ignore@5.3.2",
"imurmurhash", "imurmurhash",
"is-glob", "is-glob",
"json-stable-stringify-without-jsonify", "json-stable-stringify-without-jsonify",
@@ -438,14 +464,15 @@
"minimatch@3.1.2", "minimatch@3.1.2",
"natural-compare", "natural-compare",
"optionator" "optionator"
] ],
"bin": true
}, },
"espree@10.3.0_acorn@8.14.1": { "espree@10.4.0_acorn@8.15.0": {
"integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
"dependencies": [ "dependencies": [
"acorn", "acorn",
"acorn-jsx", "acorn-jsx",
"eslint-visitor-keys@4.2.0" "eslint-visitor-keys@4.2.1"
] ]
}, },
"esquery@1.6.0": { "esquery@1.6.0": {
@@ -466,8 +493,8 @@
"esutils@2.0.3": { "esutils@2.0.3": {
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="
}, },
"fake-indexeddb@6.0.0": { "fake-indexeddb@6.0.1": {
"integrity": "sha512-YEboHE5VfopUclOck7LncgIqskAqnv4q0EWbYCaxKKjAvO93c+TJIaBuGy8CBFdbg9nKdpN3AuPRwVBJ4k7NrQ==" "integrity": "sha512-He2AjQGHe46svIFq5+L2Nx/eHDTI1oKgoevBP+TthnjymXiKkeJQ3+ITeWey99Y5+2OaPFbI1qEsx/5RsGtWnQ=="
}, },
"fast-deep-equal@3.1.3": { "fast-deep-equal@3.1.3": {
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
@@ -553,6 +580,9 @@
"ignore@5.3.2": { "ignore@5.3.2": {
"integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==" "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g=="
}, },
"ignore@7.0.5": {
"integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="
},
"import-fresh@3.3.1": { "import-fresh@3.3.1": {
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
"dependencies": [ "dependencies": [
@@ -582,7 +612,8 @@
"integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
"dependencies": [ "dependencies": [
"argparse" "argparse"
] ],
"bin": true
}, },
"json-buffer@3.0.1": { "json-buffer@3.0.1": {
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ=="
@@ -634,13 +665,13 @@
"minimatch@3.1.2": { "minimatch@3.1.2": {
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
"dependencies": [ "dependencies": [
"brace-expansion@1.1.11" "brace-expansion@1.1.12"
] ]
}, },
"minimatch@9.0.5": { "minimatch@9.0.5": {
"integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
"dependencies": [ "dependencies": [
"brace-expansion@2.0.1" "brace-expansion@2.0.2"
] ]
}, },
"mongodb-connection-string-url@3.0.2": { "mongodb-connection-string-url@3.0.2": {
@@ -650,8 +681,8 @@
"whatwg-url" "whatwg-url"
] ]
}, },
"mongodb@6.15.0": { "mongodb@6.17.0": {
"integrity": "sha512-ifBhQ0rRzHDzqp9jAQP6OwHSH7dbYIQjD3SbJs9YYk9AikKEettW/9s/tbSFDTpXcRbF+u1aLrhHxDFaYtZpFQ==", "integrity": "sha512-neerUzg/8U26cgruLysKEjJvoNSXhyID3RvzvdcpsIi2COYM3FS3o9nlH7fxFtefTb942dX3W9i37oPfCVj4wA==",
"dependencies": [ "dependencies": [
"@mongodb-js/saslprep", "@mongodb-js/saslprep",
"bson", "bson",
@@ -662,10 +693,12 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
}, },
"nanoid@5.0.2": { "nanoid@5.0.2": {
"integrity": "sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==" "integrity": "sha512-2ustYUX1R2rL/Br5B/FMhi8d5/QzvkJ912rBYxskcpu0myTHzSZfTr1LAS2Sm7jxRUObRrSBFoyzwAhL49aVSg==",
"bin": true
}, },
"nanoid@5.1.5": { "nanoid@5.1.5": {
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==" "integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"bin": true
}, },
"natural-compare@1.4.0": { "natural-compare@1.4.0": {
"integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==" "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="
@@ -708,14 +741,15 @@
"picomatch@2.3.1": { "picomatch@2.3.1": {
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
}, },
"postgres@3.4.5": { "postgres@3.4.7": {
"integrity": "sha512-cDWgoah1Gez9rN3H4165peY9qfpEo+SA61oQv65O3cRUE1pOEoJWwddwcqKE8XZYjbblOJlYDlLV4h67HrEVDg==" "integrity": "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="
}, },
"prelude-ls@1.2.1": { "prelude-ls@1.2.1": {
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==" "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="
}, },
"prettier@3.5.3": { "prettier@3.6.2": {
"integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==" "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
"bin": true
}, },
"punycode@2.3.1": { "punycode@2.3.1": {
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==" "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="
@@ -744,8 +778,9 @@
"tslib" "tslib"
] ]
}, },
"semver@7.7.1": { "semver@7.7.2": {
"integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==" "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
"bin": true
}, },
"shebang-command@2.0.0": { "shebang-command@2.0.0": {
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
@@ -801,8 +836,8 @@
"type-fest@3.13.1": { "type-fest@3.13.1": {
"integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==" "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g=="
}, },
"typescript-eslint@8.31.0_eslint@9.25.1_typescript@5.8.3_@typescript-eslint+parser@8.31.0__eslint@9.25.1__typescript@5.8.3": { "typescript-eslint@8.35.1_eslint@9.30.1_typescript@5.8.3_@typescript-eslint+parser@8.35.1__eslint@9.30.1__typescript@5.8.3": {
"integrity": "sha512-u+93F0sB0An8WEAPtwxVhFby573E8ckdjwUUQUj9QA4v8JAvgtoDdIyYR3XFwFHq2W1KJ1AurwJCO+w+Y1ixyQ==", "integrity": "sha512-xslJjFzhOmHYQzSB/QTeASAHbjmxOGEP6Coh93TXmUBFQoJ1VU35UHIDmG06Jd6taf3wqqC1ntBnCMeymy5Ovw==",
"dependencies": [ "dependencies": [
"@typescript-eslint/eslint-plugin", "@typescript-eslint/eslint-plugin",
"@typescript-eslint/parser", "@typescript-eslint/parser",
@@ -812,7 +847,8 @@
] ]
}, },
"typescript@5.8.3": { "typescript@5.8.3": {
"integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==" "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
"bin": true
}, },
"uri-js@4.4.1": { "uri-js@4.4.1": {
"integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
@@ -834,7 +870,8 @@
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dependencies": [ "dependencies": [
"isexe" "isexe"
] ],
"bin": true
}, },
"word-wrap@1.2.5": { "word-wrap@1.2.5": {
"integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==" "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="
@@ -842,30 +879,27 @@
"yocto-queue@0.1.0": { "yocto-queue@0.1.0": {
"integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==" "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="
}, },
"zod@4.0.0-beta.20250420T053007": { "zod@3.25.75": {
"integrity": "sha512-5pp8Q0PNDaNcUptGiBE9akyioJh3RJpagIxrLtAVMR9IxwcSZiOsJD/1/98CyhItdTlI2H91MfhhLzRlU+fifA==", "integrity": "sha512-OhpzAmVzabPOL6C3A3gpAifqr9MqihV/Msx3gor2b2kviCgcb+HM9SEOpMWwwNp9MRunWnhtAKUoo0AHhjyPPg=="
"dependencies": [
"@zod/core"
]
} }
}, },
"workspace": { "workspace": {
"packageJson": { "packageJson": {
"dependencies": [ "dependencies": [
"npm:@jsr/std__assert@1.0.13", "npm:@jsr/std__assert@1.0.13",
"npm:@jsr/std__async@1.0.12", "npm:@jsr/std__async@1.0.13",
"npm:@jsr/std__testing@1.0.11", "npm:@jsr/std__testing@1.0.14",
"npm:@jsr/valkyr__testcontainers@2.0.0", "npm:@jsr/valkyr__testcontainers@2.0.1",
"npm:@valkyr/db@1.0.1", "npm:@valkyr/db@^1.0.1",
"npm:eslint-plugin-simple-import-sort@12.1.1", "npm:eslint-plugin-simple-import-sort@12.1.1",
"npm:eslint@9.25.1", "npm:eslint@9.30.1",
"npm:fake-indexeddb@6.0.0", "npm:fake-indexeddb@6.0.1",
"npm:mongodb@6.15.0", "npm:mongodb@6.17.0",
"npm:nanoid@5.1.5", "npm:nanoid@5.1.5",
"npm:postgres@3.4.5", "npm:postgres@3.4.7",
"npm:prettier@3.5.3", "npm:prettier@3.6.2",
"npm:typescript-eslint@8.31.0", "npm:typescript-eslint@8.35.1",
"npm:zod@next" "npm:zod@3.25.75"
] ]
} }
} }

View File

@@ -15,16 +15,22 @@ export default [
{ {
files: ["**/*.ts"], files: ["**/*.ts"],
rules: { rules: {
"@typescript-eslint/ban-ts-comment": ["error", { "@typescript-eslint/ban-ts-comment": [
"ts-expect-error": "allow-with-description", "error",
minimumDescriptionLength: 10, {
}], "ts-expect-error": "allow-with-description",
minimumDescriptionLength: 10,
},
],
"@typescript-eslint/ban-types": "off", "@typescript-eslint/ban-types": "off",
"@typescript-eslint/no-explicit-any": "off", "@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/no-unused-vars": ["error", { "@typescript-eslint/no-unused-vars": [
argsIgnorePattern: "^_", "error",
varsIgnorePattern: "^_", {
}], argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
}, },
}, },
]; ];

View File

@@ -75,7 +75,9 @@ export class HLCForwardJumpError extends Error {
readonly timejump: number, readonly timejump: number,
readonly tolerance: number, readonly tolerance: number,
) { ) {
super(`HLC Violation: Detected a forward time jump of ${timejump}ms, which exceed the allowed tolerance of ${tolerance}ms.`); super(
`HLC Violation: Detected a forward time jump of ${timejump}ms, which exceed the allowed tolerance of ${tolerance}ms.`,
);
} }
} }
@@ -96,7 +98,9 @@ export class HLCClockOffsetError extends Error {
readonly offset: number, readonly offset: number,
readonly maxOffset: number, readonly maxOffset: number,
) { ) {
super(`HLC Violation: Received time is ${offset}ms ahead of the wall time, exceeding the 'maxOffset' limit of ${maxOffset}ms.`); super(
`HLC Violation: Received time is ${offset}ms ahead of the wall time, exceeding the 'maxOffset' limit of ${maxOffset}ms.`,
);
} }
} }

View File

@@ -47,7 +47,9 @@ export class EventFactory<const TEvents extends Event[] = Event[]> {
* *
* @param type - Event type to retrieve. * @param type - Event type to retrieve.
*/ */
get<TType extends TEvents[number]["state"]["type"]>(type: TType): Extract<TEvents[number], { state: { type: TType } }> { get<TType extends TEvents[number]["state"]["type"]>(
type: TType,
): Extract<TEvents[number], { state: { type: TType } }> {
return this.#index.get(type) as Extract<TEvents[number], { state: { type: TType } }>; return this.#index.get(type) as Extract<TEvents[number], { state: { type: TType } }>;
} }
} }

View File

@@ -118,7 +118,9 @@ export class EventStore<
* *
* @param name - Aggregate name to retrieve. * @param name - Aggregate name to retrieve.
*/ */
aggregate<TName extends TAggregateFactory["$aggregates"][number]["name"]>(name: TName): Extract<TAggregateFactory["$aggregates"][number], { name: TName }> { aggregate<TName extends TAggregateFactory["$aggregates"][number]["name"]>(
name: TName,
): Extract<TAggregateFactory["$aggregates"][number], { name: TName }> {
return this.#aggregates.get(name) as Extract<TAggregateFactory["$aggregates"][number], { name: TName }>; return this.#aggregates.get(name) as Extract<TAggregateFactory["$aggregates"][number], { name: TName }>;
} }
@@ -128,7 +130,10 @@ export class EventStore<
* @param aggregate - Aggregate to push events from. * @param aggregate - Aggregate to push events from.
* @param settings - Event settings which can modify insertion behavior. * @param settings - Event settings which can modify insertion behavior.
*/ */
async pushAggregate(aggregate: InstanceType<TAggregateFactory["$aggregates"][number]>, settings?: EventsInsertSettings): Promise<void> { async pushAggregate(
aggregate: InstanceType<TAggregateFactory["$aggregates"][number]>,
settings?: EventsInsertSettings,
): Promise<void> {
await aggregate.save(settings); await aggregate.save(settings);
} }
@@ -144,7 +149,10 @@ export class EventStore<
* @param aggregates - Aggregates to push events from. * @param aggregates - Aggregates to push events from.
* @param settings - Event settings which can modify insertion behavior. * @param settings - Event settings which can modify insertion behavior.
*/ */
async pushManyAggregates(aggregates: InstanceType<TAggregateFactory["$aggregates"][number]>[], settings?: EventsInsertSettings): Promise<void> { async pushManyAggregates(
aggregates: InstanceType<TAggregateFactory["$aggregates"][number]>[],
settings?: EventsInsertSettings,
): Promise<void> {
const events: this["$events"][number]["$record"][] = []; const events: this["$events"][number]["$record"][] = [];
for (const aggregate of aggregates) { for (const aggregate of aggregates) {
events.push(...aggregate.toPending()); events.push(...aggregate.toPending());
@@ -209,7 +217,10 @@ export class EventStore<
* @param records - List of event records to insert. * @param records - List of event records to insert.
* @param settings - Event settings which can modify insertion behavior. * @param settings - Event settings which can modify insertion behavior.
*/ */
async pushManyEvents(records: this["$events"][number]["$record"][], settings: EventsInsertSettings = {}): Promise<void> { async pushManyEvents(
records: this["$events"][number]["$record"][],
settings: EventsInsertSettings = {},
): Promise<void> {
const events: this["$events"][number]["$record"][] = []; const events: this["$events"][number]["$record"][] = [];
for (const record of records) { for (const record of records) {
const event = this.#events.get(record.type); const event = this.#events.get(record.type);
@@ -270,7 +281,10 @@ export class EventStore<
* @param streams - Streams to retrieve events for. * @param streams - Streams to retrieve events for.
* @param options - Read options to pass to the provider. (Optional) * @param options - Read options to pass to the provider. (Optional)
*/ */
async getEventsByStreams(streams: string[], options?: EventReadOptions): Promise<TEventFactory["$events"][number]["$record"][]> { async getEventsByStreams(
streams: string[],
options?: EventReadOptions,
): Promise<TEventFactory["$events"][number]["$record"][]> {
return this.events.getByStreams(streams, options); return this.events.getByStreams(streams, options);
} }
@@ -280,7 +294,10 @@ export class EventStore<
* @param keys - Relational keys to retrieve events for. * @param keys - Relational keys to retrieve events for.
* @param options - Relational logic options. (Optional) * @param options - Relational logic options. (Optional)
*/ */
async getEventsByRelations(keys: string[], options?: EventReadOptions): Promise<TEventFactory["$events"][number]["$record"][]> { async getEventsByRelations(
keys: string[],
options?: EventReadOptions,
): Promise<TEventFactory["$events"][number]["$record"][]> {
const streamIds = await this.relations.getByKeys(keys); const streamIds = await this.relations.getByKeys(keys);
if (streamIds.length === 0) { if (streamIds.length === 0) {
return []; return [];
@@ -317,7 +334,10 @@ export class EventStore<
* const state = await eventStore.reduce({ name: "foo:reducer", stream: "stream-id", reducer }); * const state = await eventStore.reduce({ name: "foo:reducer", stream: "stream-id", reducer });
* ``` * ```
*/ */
makeReducer<TState extends Unknown>(foldFn: ReducerLeftFold<TState, TEventFactory>, stateFn: ReducerState<TState>): Reducer<TEventFactory, TState> { makeReducer<TState extends Unknown>(
foldFn: ReducerLeftFold<TState, TEventFactory>,
stateFn: ReducerState<TState>,
): Reducer<TEventFactory, TState> {
return makeReducer<TEventFactory, TState>(foldFn, stateFn); return makeReducer<TEventFactory, TState>(foldFn, stateFn);
} }
@@ -352,7 +372,9 @@ export class EventStore<
* }); * });
* ``` * ```
*/ */
makeAggregateReducer<TAggregateRoot extends typeof AggregateRoot<TEventFactory>>(aggregate: TAggregateRoot): Reducer<TEventFactory, InstanceType<TAggregateRoot>> { makeAggregateReducer<TAggregateRoot extends typeof AggregateRoot<TEventFactory>>(
aggregate: TAggregateRoot,
): Reducer<TEventFactory, InstanceType<TAggregateRoot>> {
return makeAggregateReducer<TEventFactory, TAggregateRoot>(aggregate); return makeAggregateReducer<TEventFactory, TAggregateRoot>(aggregate);
} }
@@ -392,7 +414,9 @@ export class EventStore<
} }
const events = ( const events = (
stream !== undefined ? await this.getEventsByStreams([id], { ...query, cursor }) : await this.getEventsByRelations([id], { ...query, cursor }) stream !== undefined
? await this.getEventsByStreams([id], { ...query, cursor })
: await this.getEventsByRelations([id], { ...query, cursor })
).concat(pending); ).concat(pending);
if (events.length === 0) { if (events.length === 0) {
@@ -430,9 +454,16 @@ export class EventStore<
* await eventStore.createSnapshot({ relation: `foo:${foo}:bars`, reducer }); * await eventStore.createSnapshot({ relation: `foo:${foo}:bars`, reducer });
* ``` * ```
*/ */
async createSnapshot<TReducer extends Reducer>({ name, stream, relation, reducer, ...query }: ReduceQuery<TReducer>): Promise<void> { async createSnapshot<TReducer extends Reducer>({
name,
stream,
relation,
reducer,
...query
}: ReduceQuery<TReducer>): Promise<void> {
const id = stream ?? relation; const id = stream ?? relation;
const events = stream !== undefined ? await this.getEventsByStreams([id], query) : await this.getEventsByRelations([id], query); const events =
stream !== undefined ? await this.getEventsByStreams([id], query) : await this.getEventsByRelations([id], query);
if (events.length === 0) { if (events.length === 0) {
return undefined; return undefined;
} }
@@ -543,7 +574,10 @@ export type EventStoreHooks<TEventFactory extends EventFactory> = Partial<{
* @param records - List of event records inserted. * @param records - List of event records inserted.
* @param settings - Event insert settings used. * @param settings - Event insert settings used.
*/ */
onEventsInserted(records: TEventFactory["$events"][number]["$record"][], settings: EventsInsertSettings): Promise<void>; onEventsInserted(
records: TEventFactory["$events"][number]["$record"][],
settings: EventsInsertSettings,
): Promise<void>;
/** /**
* Triggered when an unhandled exception is thrown during `.pushEvent` and * Triggered when an unhandled exception is thrown during `.pushEvent` and

View File

@@ -1,4 +1,4 @@
import z, { ZodType } from "zod"; import z, { ZodType } from "zod/v4";
import { EventValidationError } from "./errors.ts"; import { EventValidationError } from "./errors.ts";
import { makeId } from "./nanoid.ts"; import { makeId } from "./nanoid.ts";

View File

@@ -12,9 +12,13 @@ export class HLC {
last: Timestamp; last: Timestamp;
constructor( constructor({
{ time = getTime, maxOffset = 0, timeUpperBound = 0, toleratedForwardClockJump = 0, last }: Options = {}, time = getTime,
) { maxOffset = 0,
timeUpperBound = 0,
toleratedForwardClockJump = 0,
last,
}: Options = {}) {
this.time = time; this.time = time;
this.maxTime = timeUpperBound > 0 ? timeUpperBound : Number.MAX_SAFE_INTEGER; this.maxTime = timeUpperBound > 0 ? timeUpperBound : Number.MAX_SAFE_INTEGER;
this.maxOffset = maxOffset; this.maxOffset = maxOffset;

View File

@@ -132,7 +132,10 @@ export class Projector<TEventFactory extends EventFactory = EventFactory> {
*/ */
once< once<
TType extends TEventFactory["$events"][number]["$record"]["type"], TType extends TEventFactory["$events"][number]["$record"]["type"],
TRecord extends TEventFactory["$events"][number]["$record"] = Extract<TEventFactory["$events"][number]["$record"], { type: TType }>, TRecord extends TEventFactory["$events"][number]["$record"] = Extract<
TEventFactory["$events"][number]["$record"],
{ type: TType }
>,
TSuccessData extends Record<string, any> | void = void, TSuccessData extends Record<string, any> | void = void,
>( >(
type: TType, type: TType,
@@ -175,7 +178,10 @@ export class Projector<TEventFactory extends EventFactory = EventFactory> {
*/ */
on< on<
TType extends TEventFactory["$events"][number]["$record"]["type"], TType extends TEventFactory["$events"][number]["$record"]["type"],
TRecord extends TEventFactory["$events"][number]["$record"] = Extract<TEventFactory["$events"][number]["$record"], { type: TType }>, TRecord extends TEventFactory["$events"][number]["$record"] = Extract<
TEventFactory["$events"][number]["$record"],
{ type: TType }
>,
>(type: TType, handler: ProjectionHandler<TRecord>): Subscription { >(type: TType, handler: ProjectionHandler<TRecord>): Subscription {
return this.#subscribe(type, FILTER_CONTINUOUS, handler as any); return this.#subscribe(type, FILTER_CONTINUOUS, handler as any);
} }
@@ -194,7 +200,10 @@ export class Projector<TEventFactory extends EventFactory = EventFactory> {
*/ */
all< all<
TType extends TEventFactory["$events"][number]["$record"]["type"], TType extends TEventFactory["$events"][number]["$record"]["type"],
TRecord extends TEventFactory["$events"][number]["$record"] = Extract<TEventFactory["$events"][number]["$record"], { type: TType }>, TRecord extends TEventFactory["$events"][number]["$record"] = Extract<
TEventFactory["$events"][number]["$record"],
{ type: TType }
>,
>(type: TType, handler: ProjectionHandler<TRecord>): Subscription { >(type: TType, handler: ProjectionHandler<TRecord>): Subscription {
return this.#subscribe(type, FILTER_ALL, handler as any); return this.#subscribe(type, FILTER_ALL, handler as any);
} }

View File

@@ -8,9 +8,10 @@ import { EventFactory } from "./event-factory.ts";
* *
* @param aggregate - Aggregate to instantiate and create an instance of. * @param aggregate - Aggregate to instantiate and create an instance of.
*/ */
export function makeAggregateReducer<TEventFactory extends EventFactory, TAggregateRoot extends typeof AggregateRoot<TEventFactory>>( export function makeAggregateReducer<
aggregate: TAggregateRoot, TEventFactory extends EventFactory,
): Reducer<TEventFactory, InstanceType<TAggregateRoot>> { TAggregateRoot extends typeof AggregateRoot<TEventFactory>,
>(aggregate: TAggregateRoot): Reducer<TEventFactory, InstanceType<TAggregateRoot>> {
return { return {
from(snapshot: Unknown) { from(snapshot: Unknown) {
return aggregate.from(snapshot); return aggregate.from(snapshot);
@@ -45,7 +46,10 @@ export function makeReducer<TEventFactory extends EventFactory, TState extends U
}; };
} }
export type Reducer<TEventFactory extends EventFactory = EventFactory, TState extends Record<string, unknown> | AggregateRoot<TEventFactory> = any> = { export type Reducer<
TEventFactory extends EventFactory = EventFactory,
TState extends Record<string, unknown> | AggregateRoot<TEventFactory> = any,
> = {
/** /**
* Return result directly from a snapshot that does not have any subsequent * Return result directly from a snapshot that does not have any subsequent
* events to fold onto a state. * events to fold onto a state.
@@ -80,10 +84,10 @@ export type Reducer<TEventFactory extends EventFactory = EventFactory, TState ex
* }) * })
* ``` * ```
*/ */
export type ReducerLeftFold<TState extends Record<string, unknown> = any, TEventFactory extends EventFactory = EventFactory> = ( export type ReducerLeftFold<
state: TState, TState extends Record<string, unknown> = any,
event: TEventFactory["$events"][number]["$record"], TEventFactory extends EventFactory = EventFactory,
) => TState; > = (state: TState, event: TEventFactory["$events"][number]["$record"]) => TState;
export type ReducerState<TState extends Unknown> = () => TState; export type ReducerState<TState extends Unknown> = () => TState;

View File

@@ -1,4 +1,4 @@
import { ZodError } from "zod"; import { ZodError } from "zod/v4";
export function toPrettyErrorLines(error: ZodError, padding: number = 0): string[] { export function toPrettyErrorLines(error: ZodError, padding: number = 0): string[] {
const lines: string[] = []; const lines: string[] = [];

View File

@@ -1,20 +1,26 @@
{ {
"dependencies": { "dependencies": {
"@std/async": "npm:@jsr/std__async@1.0.12", "nanoid": "5.1.5"
"@valkyr/db": "1.0.1", },
"mongodb": "6.15.0", "peerDependencies": {
"nanoid": "5.1.5", "@valkyr/db": "^1.0.1",
"postgres": "3.4.5", "mongodb": "^6.0.0",
"zod": "next" "postgres": "^3.0.0",
"zod": "^3.25.0"
}, },
"devDependencies": { "devDependencies": {
"@std/async": "npm:@jsr/std__async@1.0.13",
"@std/assert": "npm:@jsr/std__assert@1.0.13", "@std/assert": "npm:@jsr/std__assert@1.0.13",
"@std/testing": "npm:@jsr/std__testing@1.0.11", "@std/testing": "npm:@jsr/std__testing@1.0.14",
"@valkyr/testcontainers": "npm:@jsr/valkyr__testcontainers@2.0.0", "@valkyr/db": "^1.0.1",
"eslint": "9.25.1", "@valkyr/testcontainers": "npm:@jsr/valkyr__testcontainers@2.0.1",
"eslint": "9.30.1",
"eslint-plugin-simple-import-sort": "12.1.1", "eslint-plugin-simple-import-sort": "12.1.1",
"fake-indexeddb": "6.0.0", "fake-indexeddb": "6.0.1",
"prettier": "3.5.3", "mongodb": "6.17.0",
"typescript-eslint": "8.31.0" "postgres": "3.4.7",
"prettier": "3.6.2",
"typescript-eslint": "8.35.1",
"zod": "3.25.75"
} }
} }

View File

@@ -1,5 +1,9 @@
export abstract class ServiceError<TData = unknown> extends Error { export abstract class ServiceError<TData = unknown> extends Error {
constructor(message: string, readonly status: number, readonly data?: TData) { constructor(
message: string,
readonly status: number,
readonly data?: TData,
) {
super(message); super(message);
} }

View File

@@ -1,4 +1,4 @@
import z from "zod"; import z from "zod/v4";
import { event } from "../../libraries/event.ts"; import { event } from "../../libraries/event.ts";
import { EventFactory } from "../../libraries/event-factory.ts"; import { EventFactory } from "../../libraries/event-factory.ts";
@@ -11,7 +11,10 @@ export const events = new EventFactory([
.data( .data(
z.strictObject({ z.strictObject({
name: z name: z
.union([z.strictObject({ given: z.string(), family: z.string().optional() }), z.strictObject({ given: z.string().optional(), family: z.string() })]) .union([
z.strictObject({ given: z.string(), family: z.string().optional() }),
z.strictObject({ given: z.string().optional(), family: z.string() }),
])
.optional(), .optional(),
email: z.string(), email: z.string(),
}), }),

View File

@@ -2,10 +2,10 @@ import { afterAll, afterEach, beforeAll, describe } from "@std/testing/bdd";
import { MongoTestContainer } from "@valkyr/testcontainers/mongodb"; import { MongoTestContainer } from "@valkyr/testcontainers/mongodb";
import { MongoAdapter, register } from "../adapters/mongo/adapter.ts"; import { MongoAdapter, register } from "../adapters/mongo/adapter.ts";
import { EventStore, EventStoreHooks } from "../libraries/event-store.ts"; import { EventStore, type EventStoreHooks } from "../libraries/event-store.ts";
import { Projector } from "../libraries/projector.ts"; import { Projector } from "../libraries/projector.ts";
import { aggregates } from "./mocks/aggregates.ts"; import { aggregates } from "./mocks/aggregates.ts";
import { events, EventStoreFactory } from "./mocks/events.ts"; import { events, type EventStoreFactory } from "./mocks/events.ts";
import testAddEvent from "./store/add-event.ts"; import testAddEvent from "./store/add-event.ts";
import testAddManyEvents from "./store/add-many-events.ts"; import testAddManyEvents from "./store/add-many-events.ts";
import testCreateSnapshot from "./store/create-snapshot.ts"; import testCreateSnapshot from "./store/create-snapshot.ts";
@@ -38,7 +38,11 @@ beforeAll(async () => {
afterEach(async () => { afterEach(async () => {
const db = container.client.db(DB_NAME); const db = container.client.db(DB_NAME);
await Promise.all([db.collection("events").deleteMany({}), db.collection("relations").deleteMany({}), db.collection("snapshots").deleteMany({})]); await Promise.all([
db.collection("events").deleteMany({}),
db.collection("relations").deleteMany({}),
db.collection("snapshots").deleteMany({}),
]);
}); });
afterAll(async () => { afterAll(async () => {

View File

@@ -26,7 +26,8 @@ const DB_NAME = "sandbox";
const container = await PostgresTestContainer.start("postgres:17"); const container = await PostgresTestContainer.start("postgres:17");
const sql = postgres(container.url(DB_NAME)); const sql = postgres(container.url(DB_NAME));
const eventStoreFn = async (options: { hooks?: EventStoreHooks<EventStoreFactory> } = {}) => getEventStore(sql, options); const eventStoreFn = async (options: { hooks?: EventStoreHooks<EventStoreFactory> } = {}) =>
getEventStore(sql, options);
/* /*
|-------------------------------------------------------------------------------- |--------------------------------------------------------------------------------
@@ -76,7 +77,9 @@ beforeAll(async () => {
}); });
afterEach(async () => { afterEach(async () => {
await container.client(DB_NAME)`TRUNCATE "event_store"."relations","event_store"."events","event_store"."snapshots" CASCADE`; await container.client(
DB_NAME,
)`TRUNCATE "event_store"."relations","event_store"."events","event_store"."snapshots" CASCADE`;
}); });
afterAll(async () => { afterAll(async () => {
@@ -111,7 +114,10 @@ describe("Adapter > Postgres", () => {
|-------------------------------------------------------------------------------- |--------------------------------------------------------------------------------
*/ */
async function getEventStore(connection: PostgresConnection, { hooks = {} }: { hooks?: EventStoreHooks<EventStoreFactory> }) { async function getEventStore(
connection: PostgresConnection,
{ hooks = {} }: { hooks?: EventStoreHooks<EventStoreFactory> },
) {
const store = new EventStore({ const store = new EventStore({
adapter: new PostgresAdapter(connection, { schema: "event_store" }), adapter: new PostgresAdapter(connection, { schema: "event_store" }),
events, events,

View File

@@ -8,7 +8,11 @@ export default describe<EventStoreFactory>(".makeAggregateReducer", (getEventSto
it("should reduce a user", async () => { it("should reduce a user", async () => {
const { store } = await getEventStore(); const { store } = await getEventStore();
const userA = await store.aggregate("user").create({ given: "John", family: "Doe" }, "john.doe@fixture.none").setGivenName("Jane").save(); const userA = await store
.aggregate("user")
.create({ given: "John", family: "Doe" }, "john.doe@fixture.none")
.setGivenName("Jane")
.save();
await userA.snapshot(); await userA.snapshot();

View File

@@ -82,7 +82,12 @@ export default describe<EventStoreFactory>(".makeReducer", (getEventStore) => {
}), }),
); );
const state = await store.reduce({ name: "user", stream: streamA, reducer: userReducer, filter: { types: ["user:created", "user:email-set"] } }); const state = await store.reduce({
name: "user",
stream: streamA,
reducer: userReducer,
filter: { types: ["user:created", "user:email-set"] },
});
assertEquals(state?.name, { given: "John", family: "Doe" }); assertEquals(state?.name, { given: "John", family: "Doe" });
assertEquals(state?.email, "jane.doe@fixture.none"); assertEquals(state?.email, "jane.doe@fixture.none");
@@ -100,10 +105,31 @@ export default describe<EventStoreFactory>(".makeReducer", (getEventStore) => {
const post2 = makeId(); const post2 = makeId();
const post3 = makeId(); const post3 = makeId();
await store.pushEvent(store.event({ stream: post1, type: "post:created", data: { title: "Post #1", body: "Sample #1" }, meta: { auditor } })); await store.pushEvent(
await store.pushEvent(store.event({ stream: post2, type: "post:created", data: { title: "Post #2", body: "Sample #2" }, meta: { auditor } })); store.event({
stream: post1,
type: "post:created",
data: { title: "Post #1", body: "Sample #1" },
meta: { auditor },
}),
);
await store.pushEvent(
store.event({
stream: post2,
type: "post:created",
data: { title: "Post #2", body: "Sample #2" },
meta: { auditor },
}),
);
await store.pushEvent(store.event({ stream: post2, type: "post:removed", meta: { auditor } })); await store.pushEvent(store.event({ stream: post2, type: "post:removed", meta: { auditor } }));
await store.pushEvent(store.event({ stream: post3, type: "post:created", data: { title: "Post #3", body: "Sample #3" }, meta: { auditor } })); await store.pushEvent(
store.event({
stream: post3,
type: "post:created",
data: { title: "Post #3", body: "Sample #3" },
meta: { auditor },
}),
);
const events = await store.getEventsByRelations([`user:${auditor}:posts`]); const events = await store.getEventsByRelations([`user:${auditor}:posts`]);

View File

@@ -25,7 +25,10 @@ export default describe<EventStoreFactory>(".pushAggregate", (getEventStore) =>
assertEquals(records.length, 3); assertEquals(records.length, 3);
assertObjectMatch(records[0], { stream: user.id, data: { name: { given: "Jane", family: "Doe" }, email: "jane.doe@fixture.none" } }); assertObjectMatch(records[0], {
stream: user.id,
data: { name: { given: "Jane", family: "Doe" }, email: "jane.doe@fixture.none" },
});
assertObjectMatch(records[1], { stream: user.id, data: "John" }); assertObjectMatch(records[1], { stream: user.id, data: "John" });
assertObjectMatch(records[2], { stream: user.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } }); assertObjectMatch(records[2], { stream: user.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } });

View File

@@ -33,10 +33,16 @@ export default describe<EventStoreFactory>(".pushManyAggregates", (getEventStore
assertEquals(records.length, 6); assertEquals(records.length, 6);
assertObjectMatch(records[0], { stream: userA.id, data: { name: { given: "Jane", family: "Doe" }, email: "jane.doe@fixture.none" } }); assertObjectMatch(records[0], {
stream: userA.id,
data: { name: { given: "Jane", family: "Doe" }, email: "jane.doe@fixture.none" },
});
assertObjectMatch(records[1], { stream: userA.id, data: "John" }); assertObjectMatch(records[1], { stream: userA.id, data: "John" });
assertObjectMatch(records[2], { stream: userA.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } }); assertObjectMatch(records[2], { stream: userA.id, data: "john.doe@fixture.none", meta: { auditor: "admin" } });
assertObjectMatch(records[3], { stream: userB.id, data: { name: { given: "Peter", family: "Doe" }, email: "peter.doe@fixture.none" } }); assertObjectMatch(records[3], {
stream: userB.id,
data: { name: { given: "Peter", family: "Doe" }, email: "peter.doe@fixture.none" },
});
assertObjectMatch(records[4], { stream: userB.id, data: "Barry" }); assertObjectMatch(records[4], { stream: userB.id, data: "Barry" });
assertObjectMatch(records[5], { stream: userB.id, data: "barry.doe@fixture.none", meta: { auditor: "admin" } }); assertObjectMatch(records[5], { stream: userB.id, data: "barry.doe@fixture.none", meta: { auditor: "admin" } });

View File

@@ -11,7 +11,9 @@ export function describe<TEventFactory extends EventFactory>(
return (getEventStore: EventStoreFn<TEventFactory>) => desc(name, () => runner(getEventStore)); return (getEventStore: EventStoreFn<TEventFactory>) => desc(name, () => runner(getEventStore));
} }
type EventStoreFn<TEventFactory extends EventFactory> = (options?: { hooks?: EventStoreHooks<TEventFactory> }) => Promise<{ type EventStoreFn<TEventFactory extends EventFactory> = (options?: {
hooks?: EventStoreHooks<TEventFactory>;
}) => Promise<{
store: EventStore<TEventFactory, any, any>; store: EventStore<TEventFactory, any, any>;
projector: Projector<TEventFactory>; projector: Projector<TEventFactory>;
}>; }>;

View File

@@ -1,8 +1,14 @@
import type { EventRecord } from "../libraries/event.ts"; import type { EventRecord } from "../libraries/event.ts";
export type BatchedProjectorListeners<TRecord extends EventRecord = EventRecord> = Record<string, Set<BatchedProjectorListenerFn<TRecord>> | undefined>; export type BatchedProjectorListeners<TRecord extends EventRecord = EventRecord> = Record<
string,
Set<BatchedProjectorListenerFn<TRecord>> | undefined
>;
export type ProjectorListeners<TRecord extends EventRecord = EventRecord> = Record<string, Set<ProjectorListenerFn<TRecord>> | undefined>; export type ProjectorListeners<TRecord extends EventRecord = EventRecord> = Record<
string,
Set<ProjectorListenerFn<TRecord>> | undefined
>;
export type ProjectorMessage<TRecord extends EventRecord = EventRecord> = { export type ProjectorMessage<TRecord extends EventRecord = EventRecord> = {
record: TRecord; record: TRecord;
@@ -11,11 +17,15 @@ export type ProjectorMessage<TRecord extends EventRecord = EventRecord> = {
export type BatchedProjectorListenerFn<TRecord extends EventRecord = EventRecord> = (records: TRecord[]) => void; export type BatchedProjectorListenerFn<TRecord extends EventRecord = EventRecord> = (records: TRecord[]) => void;
export type ProjectorListenerFn<TRecord extends EventRecord = EventRecord> = (record: TRecord, status: ProjectionStatus) => void; export type ProjectorListenerFn<TRecord extends EventRecord = EventRecord> = (
record: TRecord,
status: ProjectionStatus,
) => void;
export type ProjectionHandler<TRecord extends EventRecord = EventRecord, TSuccessData extends Record<string, any> | void = void> = TSuccessData extends void export type ProjectionHandler<
? (record: TRecord) => Promise<void> TRecord extends EventRecord = EventRecord,
: (record: TRecord) => Promise<TSuccessData>; TSuccessData extends Record<string, any> | void = void,
> = TSuccessData extends void ? (record: TRecord) => Promise<void> : (record: TRecord) => Promise<TSuccessData>;
export type BatchedProjectionHandler<TRecord extends EventRecord = EventRecord> = (records: TRecord[]) => Promise<void>; export type BatchedProjectionHandler<TRecord extends EventRecord = EventRecord> = (records: TRecord[]) => Promise<void>;