Skip to content

Commit

Permalink
Merge pull request #164 from castore-dev/create-zod-command
Browse files Browse the repository at this point in the history
feature: Create zod command
  • Loading branch information
ThomasAribart committed Nov 3, 2023
2 parents a00ddf6 + 1646f81 commit 80c3a1f
Show file tree
Hide file tree
Showing 24 changed files with 811 additions and 13 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/release-to-npm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jobs:
with:
token: ${{ secrets.NPM_TOKEN }}
package: ./packages/command-json-schema/package.json
- uses: JS-DevTools/npm-publish@v2
with:
token: ${{ secrets.NPM_TOKEN }}
package: ./packages/command-zod/package.json
- uses: JS-DevTools/npm-publish@v2
with:
token: ${{ secrets.NPM_TOKEN }}
Expand Down
4 changes: 4 additions & 0 deletions castore.code-workspace
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@
"path": "packages/command-json-schema",
"name": "👮‍♀️ Json Schema Command"
},
{
"path": "packages/command-zod",
"name": "👮‍♀️ Zod Command"
},
{
"path": "packages/message-bus-adapter-event-bridge",
"name": "🚌 EventBridge"
Expand Down
1 change: 1 addition & 0 deletions docs/docs/4-packages.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ To add run-time validation to your event types:
To add run-time validation to your commands:

- [JSON Schema Command](https://www.npmjs.com/package/@castore/command-json-schema): DRY `Command` definition using [JSON Schemas](http://json-schema.org/understanding-json-schema/reference/index.html) and [`json-schema-to-ts`](https://github.com/ThomasAribart/json-schema-to-ts)
- [Zod Command](https://www.npmjs.com/package/@castore/command-zod): DRY `Command` definition using [`zod`](https://github.com/colinhacks/zod)

## 📨 Message Queue Adapters

Expand Down
13 changes: 10 additions & 3 deletions packages/command-json-schema/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,18 @@ const pokemonAppearCommand = new JSONSchemaCommand({
inputSchema: pokemonAppearedInputSchema,
outputSchema: pokemonAppearedOutputSchema,
// 👇 handler input/output types are correctly inferred
handler: async (commandInput, [pokemonsEventStore]) => {
handler: async (
commandInput,
[pokemonsEventStore],
{ generateUuid }: { generateUuid: () => string },
) => {
const { name, level } = commandInput;
const pokemonId = generateUuid();

await pokemonsEventStore.pushEvent({
aggregateId: pokemonId,
version: 1,
type: 'POKEMON_APPEARED',
timestamp: new Date().toISOString(),
payload: { name, level },
});

Expand All @@ -89,7 +92,11 @@ const pokemonAppearCommand = new Command<
>({
commandId: 'POKEMON_APPEAR',
requiredEventStores: [pokemonsEventStore],
handler: async (commandInput, [pokemonsEventStore]) => {
handler: async (
commandInput,
[pokemonsEventStore],
{ generateUuid }: { generateUuid: () => string },
) => {
// ...same code
},
});
Expand Down
4 changes: 1 addition & 3 deletions packages/command-json-schema/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,6 @@
"transpile": "babel src --extensions .ts --quiet",
"watch": "rm -rf dist && concurrently 'yarn:package-* --watch'"
},
"dependencies": {
"ts-toolbelt": "^9.6.0"
},
"devDependencies": {
"@babel/cli": "^7.17.6",
"@babel/core": "^7.17.9",
Expand All @@ -55,6 +52,7 @@
"json-schema-to-ts": "^2.5.4",
"prettier": "^2.6.2",
"ts-node": "^10.7.0",
"ts-toolbelt": "^9.6.0",
"tsc-alias": "^1.8.7",
"typescript": "^4.6.3",
"vitest": "^0.26.2"
Expand Down
7 changes: 1 addition & 6 deletions packages/command-json-schema/src/command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ import type { FromSchema, JSONSchema } from 'json-schema-to-ts';

import {
Command,
EventAlreadyExistsError,
EventStore,
$Contravariant,
OnEventAlreadyExistsCallback,
} from '@castore/core';

export type OnEventAlreadyExistsCallback = (
error: EventAlreadyExistsError,
context: { attemptNumber: number; retriesLeft: number },
) => Promise<void>;

export class JSONSchemaCommand<
COMMAND_ID extends string = string,
EVENT_STORES extends EventStore[] = EventStore[],
Expand Down
1 change: 0 additions & 1 deletion packages/command-json-schema/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
export { JSONSchemaCommand } from './command';
export type { OnEventAlreadyExistsCallback } from './command';
1 change: 1 addition & 0 deletions packages/command-zod/.eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
dist
2 changes: 2 additions & 0 deletions packages/command-zod/.lintstagedrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const baseConfig = require('../../.lintstagedrc');
module.exports = baseConfig;
108 changes: 108 additions & 0 deletions packages/command-zod/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Zod Command

DRY Castore [`Command`](https://github.com/castore-dev/castore/#--command) definition using [`zod`](https://github.com/colinhacks/zod).

## 📥 Installation

```bash
# npm
npm install @castore/command-zod

# yarn
yarn add @castore/command-zod
```

This package has `@castore/core` and `zod` (above v3) as peer dependencies, so you will have to install them as well:

```bash
# npm
npm install @castore/core zod

# yarn
yarn add @castore/core zod
```

## 👩‍💻 Usage

```ts
import z from 'zod';

import { ZodCommand } from '@castore/command-zod';
import { tuple } from '@castore/core';

const pokemonAppearedInputSchema = z.object({
name: z.string(),
level: z.number(),
});

const pokemonAppearedOutputSchema = z.object({
pokemonId: z.string().uuid(),
});

// 👇 generics are correctly inferred
const pokemonAppearCommand = new ZodCommand({
commandId: 'POKEMON_APPEAR',
requiredEventStores: tuple(pokemonsEventStore),
inputSchema: pokemonAppearedInputSchema,
outputSchema: pokemonAppearedOutputSchema,
// 👇 handler input/output types are correctly inferred
handler: async (
commandInput,
[pokemonsEventStore],
{ generateUuid }: { generateUuid: () => string },
) => {
const { name, level } = commandInput;
const pokemonId = generateUuid();

await pokemonsEventStore.pushEvent({
aggregateId: pokemonId,
version: 1,
type: 'POKEMON_APPEARED',
payload: { name, level },
});

return { pokemonId };
},
});
```

👇 Equivalent to:

```ts
import { Command } from '@castore/core';

type RequiredEventStores = [typeof pokemonsEventStore];
type CommandInput = { name: string; level: number };
type CommandOutput = { pokemonId: string };

const pokemonAppearCommand = new Command<
RequiredEventStores,
RequiredEventStores,
CommandInput,
CommandOutput
>({
commandId: 'POKEMON_APPEAR',
requiredEventStores: [pokemonsEventStore],
handler: async (commandInput, [pokemonsEventStore]) => {
// ...same code
},
});
```

## ⚙️ Properties & Methods

`ZodCommand` implements the [`Command`](https://github.com/castore-dev/castore/#--command) class and adds the following properties to it:

- <code>inputSchema <i>(?object)</i></code>: The command input zod schema

```ts
const inputSchema = pokemonAppearCommand.inputSchema;
// => pokemonAppearedInputSchema
```

- <code>outputSchema <i>(?object)</i></code>: The command output zod schema

```ts
const outputSchema = pokemonAppearCommand.outputSchema;
// => pokemonAppearedOutputSchema
```
3 changes: 3 additions & 0 deletions packages/command-zod/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const commonBabelConfig = require('../../commonConfiguration/babel.config');

module.exports = commonBabelConfig();
3 changes: 3 additions & 0 deletions packages/command-zod/dependency-cruiser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/** @type {import('dependency-cruiser').IConfiguration} */
const baseConfig = require('../../dependency-cruiser');
module.exports = baseConfig;
80 changes: 80 additions & 0 deletions packages/command-zod/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
{
"name": "@castore/command-zod",
"description": "DRY Castore Command definition using Zod",
"license": "MIT",
"homepage": "https://github.com/theodo/castore#readme",
"bugs": "https://github.com/theodo/castore/issues",
"repository": "theodo/castore.git",
"keywords": [
"event",
"source",
"store",
"typescript"
],
"publishConfig": {
"access": "public"
},
"sideEffects": false,
"files": [
"dist"
],
"main": "dist/cjs/index.js",
"module": "dist/esm/index.js",
"types": "dist/types/index.d.ts",
"scripts": {
"lint-fix": "yarn linter-base-config --fix",
"lint-fix-all": "yarn lint-fix .",
"linter-base-config": "eslint --ext=js,ts",
"package": "rm -rf dist && yarn package-cjs && yarn package-esm && yarn package-types",
"package-cjs": "NODE_ENV=cjs yarn transpile --out-dir dist/cjs --source-maps",
"package-esm": "NODE_ENV=esm yarn transpile --out-dir dist/esm --source-maps",
"package-types": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json",
"test": "yarn test-type && yarn test-unit && yarn test-circular && yarn test-linter",
"test-circular": "yarn depcruise --validate dependency-cruiser.js .",
"test-linter": "yarn linter-base-config .",
"test-type": "tsc --noEmit --emitDeclarationOnly false",
"test-unit": "yarn vitest run --passWithNoTests",
"transpile": "babel src --extensions .ts --quiet",
"watch": "rm -rf dist && concurrently 'yarn:package-* --watch'"
},
"devDependencies": {
"@babel/cli": "^7.17.6",
"@babel/core": "^7.17.9",
"@babel/plugin-transform-runtime": "^7.17.0",
"@babel/preset-env": "^7.16.11",
"@babel/preset-typescript": "^7.16.7",
"@castore/core": "workspace:",
"@types/node": "^17.0.29",
"babel-plugin-module-resolver": "^4.1.0",
"concurrently": "^7.1.0",
"dependency-cruiser": "^11.7.0",
"eslint": "^8.14.0",
"prettier": "^2.6.2",
"ts-node": "^10.7.0",
"ts-toolbelt": "^9.6.0",
"tsc-alias": "^1.8.7",
"typescript": "^4.6.3",
"vitest": "^0.26.2",
"zod": "^3.15.1"
},
"maintainers": [
"Thomas Aribart",
"Charles Géry",
"Juliette Fournier",
"Valentin Beggi",
"Stanislas Hannebelle"
],
"nx": {
"targets": {
"package": {
"outputs": [
"packages/command-zod/dist"
]
}
}
},
"peerDependencies": {
"@castore/core": "*",
"zod": "^3.0.0"
}
}
6 changes: 6 additions & 0 deletions packages/command-zod/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"root": "packages/command-zod",
"projectType": "library",
"tags": [],
"implicitDependencies": ["core"]
}
Loading

0 comments on commit 80c3a1f

Please sign in to comment.