Skip to content

Commit

Permalink
Merge pull request #11 from NuroDev/feature/fake-store
Browse files Browse the repository at this point in the history
Added initial `@untypeable/fake-store` package
  • Loading branch information
NuroDev authored Apr 28, 2024
2 parents 18d453a + 340cf03 commit dbc097e
Show file tree
Hide file tree
Showing 28 changed files with 3,033 additions and 159 deletions.
54 changes: 54 additions & 0 deletions packages/fake-store/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# 🔐 @untypeable/fake-store

Untypeable router type definitions & validators for the [Fake Store](https://fakestoreapi.com/) API

## 🚀 Install

Install it locally in your project

```bash
# npm
npm install @untypeable/fake-store

# yarn
yarn add @untypeable/fake-store

# pnpm
pnpm install @untypeable/fake-store
```

## 🦄 Usage

Create a new client instance with the `UUIDRocksRouter` & your desired fetch handler

```typescript
import { createTypeLevelClient } from "untypeable";

import type { FakeStoreRouter } from "@untypeable/fake-store";

const client = createTypeLevelClient<FakeStoreRouter>(
async (method, path, input = {}) => {
const pathWithParams = path.replace(
/:([a-zA-Z0-9_]+)/g,
(_, key) => input[key]
);

const url = new URL(pathWithParams, "https://fakestoreapi.com/");
Object.entries(input).forEach(([key, value]) =>
url.searchParams.append(key, value as string)
);

const response = await fetch(url.href, {
body: ["POST", "PATCH", "PUT"].includes(method)
? JSON.stringify(input)
: undefined,
method,
headers: {
"Content-Type": "application/json",
},
});

return response.json();
}
);
```
82 changes: 82 additions & 0 deletions packages/fake-store/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
{
"name": "@untypeable/fake-store",
"version": "1.1.1",
"description": "Untypeable router type definitions & validators for the Fake Store API",
"publishConfig": {
"access": "public"
},
"repository": {
"directory": "packages/fake-store",
"type": "git",
"url": "https://github.com/nurodev/untypeable.git"
},
"homepage": "https://fakestoreapi.com/",
"bugs": "https://github.com/nurodev/untypeable/issues",
"readme": "README.md",
"author": {
"name": "nurodev",
"email": "ben@nuro.dev",
"url": "https://nuro.dev"
},
"keywords": [
"fake",
"mock",
"store",
"api",
"typescript",
"untypeable"
],
"license": "MIT",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
".": "./dist/index",
"./package.json": "./package.json",
"./runtime-safe": "./dist/runtime-safe",
"./types": "./dist/types",
"./zod": "./dist/zod"
},
"typesVersions": {
"*": {
"runtime-safe": [
"./dist/runtime-safe.d.ts"
],
"types": [
"./dist/types.d.ts"
],
"zod": [
"./dist/zod.d.ts"
]
}
},
"files": [
"dist/**/*",
"LICENSE",
"README.md"
],
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"test": "vitest run"
},
"dependencies": {
"untypeable": "^0.2.1"
},
"devDependencies": {
"@types/node": "^20.11.20",
"tsup": "^8.0.2",
"typescript": "^5.3.3",
"undici": "^6.6.2",
"zod": "^3.23.4"
},
"peerDependencies": {
"zod": "^3.23.4"
},
"peerDependenciesMeta": {
"zod": {
"optional": true
}
}
}
5 changes: 5 additions & 0 deletions packages/fake-store/src/_shared/_shared.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type { z } from "zod";

import type { SharedParamsSchema } from "./_shared.validators";

export type SharedParams = z.infer<typeof SharedParamsSchema>;
14 changes: 14 additions & 0 deletions packages/fake-store/src/_shared/_shared.validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { z } from "zod";

export const SharedParamsSchema = z
.object({
/** Limit results */
limit: z.number().int().positive(),
/**
* Sort results
*
* Default value is in ascending mode, you can use with `desc or `asc as you want.
*/
order: z.union([z.literal("asc"), z.literal("desc")]),
})
.partial();
32 changes: 32 additions & 0 deletions packages/fake-store/src/cart/cart.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import type { z } from "zod";

import type {
CartParamsSchema,
CartSchema,
CartsParamsSchema,
CartsSchema,
CreateCartParamsSchema,
DeleteCartParamsSchema,
UpdateCartParamsSchema,
UserCartsParamsSchema,
UserCartsSchema,
} from "./cart.validators";

export type CartParams = z.infer<typeof CartParamsSchema>;
export type Cart = z.infer<typeof CartSchema>;

export type CreateCartParams = z.infer<typeof CreateCartParamsSchema>;

export type UpdateCartParams = z.infer<typeof UpdateCartParamsSchema>;

export type DeleteCartParams = z.infer<typeof DeleteCartParamsSchema>;

// ------------------------------------------------------------------------

export type CartsParams = z.infer<typeof CartsParamsSchema>;
export type Carts = z.infer<typeof CartsSchema>;

// ------------------------------------------------------------------------

export type UserCartsParams = z.infer<typeof UserCartsParamsSchema>;
export type UserCarts = z.infer<typeof UserCartsSchema>;
60 changes: 60 additions & 0 deletions packages/fake-store/src/cart/cart.validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { z } from "zod";

import { SharedParamsSchema } from "../_shared/_shared.validators";

const SharedCartParamsSchema = SharedParamsSchema.extend({
startdate: z.string().date(),
enddate: z.string().date(),
}).partial();

export const CartParamsSchema = z.object({
/** The ID of the cart */
id: z.number().int().positive(),
});

export const CartSchema = z.object({
__v: z.number().optional(),
date: z.coerce.date(),
id: z.number(),
products: z.array(
z.object({
productId: z.number(),
quantity: z.number(),
})
),
userId: z.number(),
});

export const CreateCartParamsSchema = CartSchema.omit({
__v: true,
// An ID will be generated by the server
id: true,
});

export const UpdateCartParamsSchema = CartSchema.pick({
id: true,
}).and(CartSchema.partial());

export const UpdatedCartSchema = CartSchema.pick({
id: true,
}).and(CartSchema.partial());

export const DeleteCartParamsSchema = z.object({
/** The ID of the Cart to delete */
id: z.number().int().positive(),
});

// ------------------------------------------------------------------------

export const CartsParamsSchema = SharedCartParamsSchema.extend({});

export const CartsSchema = z.array(CartSchema);

// ------------------------------------------------------------------------

export const UserCartsParamsSchema = z.object({
/** The ID of the user */
userId: z.number(),
});

export const UserCartsSchema = z.array(CartSchema);
144 changes: 144 additions & 0 deletions packages/fake-store/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import { initUntypeable } from "untypeable";

import type {
Cart,
CartParams,
Carts,
CartsParams,
CreateCartParams,
DeleteCartParams,
UpdateCartParams,
UserCartsParams,
} from "./cart/cart.types";
import type { Login, LoginParams } from "./login/login.types";
import type {
Product,
ProductsParams,
Products,
CreateProductParams,
UpdateProductParams,
ProductParams,
ProductsCategories,
ProductsCategoriesParams,
ProductsCategoryParams,
ProductsCategory,
DeleteProductParams,
UpdateProduct,
CreateProduct,
} from "./products/products.types";
import type {
CreateUser,
CreateUserParams,
DeleteUserParams,
UpdateUser,
UpdateUserParams,
User,
UserParams,
Users,
UsersParams,
} from "./user/user.types";

const u = initUntypeable().args<
"GET" | "POST" | "PUT" | "PATCH" | "DELETE",
string
>();

const cartRouter = u.router({
DELETE: {
/** Delete a cart */
"/carts/:id": u.input<DeleteCartParams>().output<Cart>(),
},
GET: {
/** Get all carts */
"/carts": u.input<CartsParams>().output<Carts>(),
/** Get a single cart */
"/carts/:id": u.input<CartParams>().output<Cart>(),
/** Get user carts */
"/carts/user/:userId": u.input<UserCartsParams>().output<UserCartsParams>(),
},
PATCH: {
/** Update a product */
"/carts/:id": u.input<UpdateCartParams>().output<Cart>(),
},
POST: {
/** Add a new product */
"/carts": u.input<CreateCartParams>().output<Cart>(),
},
PUT: {
/** Update a product */
"/carts/:id": u.input<UpdateCartParams>().output<Cart>(),
},
});

const loginRouter = u.router({
POST: {
"/auth/login": u.input<LoginParams>().output<Login>(),
},
});

const productRouter = u.router({
DELETE: {
/** Delete a product */
"/products/:id": u.input<DeleteProductParams>().output<Product>(),
},
GET: {
/** Get all products */
"/products": u.input<ProductsParams>().output<Products>(),
/** Get a single product */
"/products/:id": u.input<ProductParams>().output<Product>(),
/** Get all categories */
"/products/categories": u
.input<ProductsCategoriesParams>()
.output<ProductsCategories>(),
/** Get products in a specific category */
"/products/category/:category": u
.input<ProductsCategoryParams>()
.output<ProductsCategory>(),
},
PATCH: {
/** Update a product */
"/products/:id": u.input<UpdateProductParams>().output<UpdateProduct>(),
},
POST: {
/** Add a new product */
"/products": u.input<CreateProductParams>().output<CreateProduct>(),
},
PUT: {
/** Update a product */
"/products/:id": u.input<UpdateProductParams>().output<UpdateProduct>(),
},
});

const userRouter = u.router({
DELETE: {
/** Delete a user */
"/users/:id": u.input<DeleteUserParams>().output<User>(),
},
GET: {
/** Get all users */
"/users": u.input<UsersParams>().output<Users>(),
/** Get a single user */
"/users/:id": u.input<UserParams>().output<User>(),
},
PATCH: {
/** Update a users */
"/users/:id": u.input<UpdateUserParams>().output<UpdateUser>(),
},
POST: {
/** Add a new user */
"/users": u.input<CreateUserParams>().output<CreateUser>(),
},
PUT: {
/** Update a users */
"/users/:id": u.input<UpdateUserParams>().output<UpdateUser>(),
},
});

const router = u
.router({})
.merge(productRouter)
.merge(cartRouter)
.merge(userRouter)
.merge(loginRouter);

export type FakeStoreRouter = typeof router;
Loading

0 comments on commit dbc097e

Please sign in to comment.