Skip to content

Commit

Permalink
Merge pull request #9 from NuroDev/feature/uuid.rocks
Browse files Browse the repository at this point in the history
Added initial `@untypeable/uuid.rocks` package
  • Loading branch information
NuroDev authored Apr 28, 2024
2 parents 435efb8 + 88dcbc9 commit 7f64ffb
Show file tree
Hide file tree
Showing 41 changed files with 1,024 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ The following are all currently supported `@untypeable` packages. Click on one b
- [🗿 JSONPlaceholder](packages/jsonplaceholder)
- [🚀 SpaceX](packages/spacex)
- [🪐 SWAPI](packages/swapi)
- [🔢 UUID.rocks](packages/uuid.rocks)

## 🏃‍♂️ Examples

Expand Down
55 changes: 55 additions & 0 deletions examples/uuid.rocks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createTypeLevelClient } from "../packages/uuid.rocks/node_modules/untypeable";

import type { UUIDRocksRouter } from "../packages/uuid.rocks/src";

export default async function main() {
const client = createTypeLevelClient<UUIDRocksRouter>(
async (path, input = {}) => {
const url = new URL(path, "https://uuid.rocks/");
Object.entries(input).forEach(([key, value]) =>
url.searchParams.append(key, value as string)
);

return fetch(url.href).then((response) => response.json());
}
);

const json = await client("/json");
console.log({ json });

const bulkJson = await client("/json/bulk", { count: 2 });
console.log({ bulkJson });

const jsonMap = await client("/json/map/:key", { key: "foobar" });
console.log({ jsonMap });

const nanoId = await client("/nanoid");
console.log({ nanoId });

const plain = await client("/plain");
console.log({ plain });

const plainBulk = await client("/plain/bulk", { count: 2 });
console.log({ plainBulk });

const plainMap = await client("/plain/map/:key", { key: "foobar" });
console.log({ plainMap });

const plainNamespaceMap = await client("/plain/map/:namespace/:key", {
namespace: "foo",
key: "bar",
});
console.log({ plainNamespaceMap });

const s = await client("/s");
console.log({ s });

const short = await client("/short");
console.log({ short });

const stats = await client("/stats");
console.log({ stats });

const ulid = await client("/ulid");
console.log({ ulid });
}
37 changes: 37 additions & 0 deletions packages/uuid.rocks/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# 🔐 @untypeable/uuid.rocks

Untypeable router type definitions & validators for the [uuid.rocks](https://uuid.rocks/) API

## 🚀 Install

Install it locally in your project

```bash
# npm
npm install @untypeable/uuid.rocks

# yarn
yarn add @untypeable/uuid.rocks

# pnpm
pnpm install @untypeable/uuid.rocks
```

## 🦄 Usage

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

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

import type { UUIDRocksRouter } from "@untypeable/uuid.rocks";

const client = createTypeLevelClient<UUIDRocksRouter>(async (path, input = {}) => {
const url = new URL(path, "https://uuid.rocks/");
Object.entries(input).forEach(([key, value]) =>
url.searchParams.append(key, value)
);

return fetch(url.href).then((response) => response.json());
});
```
81 changes: 81 additions & 0 deletions packages/uuid.rocks/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
{
"name": "@untypeable/uuid.rocks",
"version": "1.1.1",
"description": "Untypeable router type definitions & validators for the uuid.rocks API",
"publishConfig": {
"access": "public"
},
"repository": {
"directory": "packages/uuid.rocks",
"type": "git",
"url": "https://github.com/nurodev/untypeable.git"
},
"homepage": "https://lil.software/api",
"bugs": "https://github.com/nurodev/untypeable/issues",
"readme": "README.md",
"author": {
"name": "nurodev",
"email": "me@nuro.dev",
"url": "https://nuro.dev"
},
"keywords": [
"api",
"crypto",
"typescript",
"untypeable",
"uuid",
"uuid.rocks"
],
"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",
"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.22.4"
},
"peerDependencies": {
"zod": "^3.22.4"
},
"peerDependenciesMeta": {
"zod": {
"optional": true
}
}
}
7 changes: 7 additions & 0 deletions packages/uuid.rocks/src/_shared/_shared.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { z } from "zod";

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

export type UUID = `${string}-${string}-${string}-${string}-${string}`;

export type GlobalParams = z.infer<typeof GlobalParamsSchema>;
32 changes: 32 additions & 0 deletions packages/uuid.rocks/src/_shared/_shared.validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { z } from "zod";

const StandardUUIDSchema = z.string().uuid();
export const ReadableUUIDSchema = z
.string()
.regex(
/^(?:[A-Z][a-z]+){2,4}$/,
"Must be a grammatically correct Shakespearean sentence"
);

export const ShortUUIDSchema = z
.string()
.regex(/^[0-9A-Za-z]{22}$/, "Must be a Short UUID");

export const ULIDSchema = z
.string()
.regex(/^[0-9A-Za-z]{26}$/, "Must be a ULID");

/** @see https://github.com/colinhacks/zod/pull/2364 */
const nanoidRegex = /^[a-z0-9_-]{21}$/i;
export const NanoIDSchema = z.string().regex(nanoidRegex, "Must be a NANOID");

export const UUIDSchema = z.union([
StandardUUIDSchema,
ReadableUUIDSchema,
ShortUUIDSchema,
ULIDSchema,
NanoIDSchema,
]);

// TODO(@nurodev): Implement global search params once I have time to narrow down each endpoints support for each param.
export const GlobalParamsSchema = z.object({});
7 changes: 7 additions & 0 deletions packages/uuid.rocks/src/emoji/emoji.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { z } from "zod";

import type { EmojiJsonSchema, EmojiSchema } from "./emoji.validators";

export type Emoji = z.infer<typeof EmojiSchema>;

export type EmojiJson = z.infer<typeof EmojiJsonSchema>;
10 changes: 10 additions & 0 deletions packages/uuid.rocks/src/emoji/emoji.validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { z } from "zod";

// Note: We cannot use `z.string().uuid()` here because the emojis fail the regex pattern match.
export const EmojiSchema = z.string();

export const EmojiJsonSchema = z.object({
apiVersion: z.string(),
uuid: z.string(),
timestamp: z.coerce.date(),
});
7 changes: 7 additions & 0 deletions packages/uuid.rocks/src/hash/hash.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { z } from "zod";

import type { HashParamsSchema, HashSchema } from "./hash.validators";

export type HashParams = z.infer<typeof HashParamsSchema>;

export type Hash = z.infer<typeof HashSchema>;
8 changes: 8 additions & 0 deletions packages/uuid.rocks/src/hash/hash.validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from "zod";

export const HashParamsSchema = z.object({
algo: z.union([z.literal("md5"), z.literal("sha1"), z.literal("sha256")]),
data: z.string().min(1),
});

export const HashSchema = z.string();
90 changes: 90 additions & 0 deletions packages/uuid.rocks/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { initUntypeable } from "untypeable";

import type { GlobalParams } from "./types";
import type { Emoji, EmojiJson } from "./emoji/emoji.types";
import type { Hash, HashParams } from "./hash/hash.types";
import type { Ip, IpJson } from "./ip/ip.types";
import type {
Json,
JsonBulk,
JsonBulkParams,
JsonMap,
JsonMapParams,
} from "./json/json.types";
import type { NanoID, NanoIDParams } from "./nanoid/nanoid.types";
import type { PingPong, PingPongJson } from "./ping-pong/ping-pong.types";
import type {
Plain,
PlainBulk,
PlainBulkParams,
PlainMap,
PlainMapParams,
PlainNamespaceMap,
PlainNamespaceMapParams,
} from "./plain/plain.types";
import type { Stats } from "./stats/stats.types";

const u = initUntypeable();

const router = u.router({
/** Gets a uuid as plain text */
"/api/uuid/emoji": u.output<Emoji>(),

/** Gets single uuid with JSON output */
"/api/uuid/emoji?json": u.output<EmojiJson>(),

/** This api will hash data in the url or the body. */
"/api/hash/:algo/:data": u.input<HashParams>().output<Hash>(),

/** This api will return what IP you’re connecting from */
"/api/ip": u.output<Ip>(),

/** This api will return what IP you’re connecting from in JSON format with additional information */
"/api/ip?json": u.output<IpJson>(),

/** Responds with ‘pong’ or ‘ping’ */
"/api/ping": u.output<PingPong>(),

/** Responds with ‘pong’ or ‘ping’ in JSON format with some info about your request */
"/api/ping?json": u.output<PingPongJson>(),

/** Gets single uuid with JSON output */
"/json": u.input<GlobalParams>().output<Json>(),

/** Gets uuids in bulk (up to 20k) with JSON output */
"/json/bulk": u.input<JsonBulkParams>().output<JsonBulk>(),

/** Maps a key to the same UUID every time (in the “default” namespace) */
"/json/map/:key": u.input<JsonMapParams>().output<JsonMap>(),

/** Gets single NANOID in plaintext */
"/nanoid": u.input<NanoIDParams>().output<NanoID>(),

/** Gets single UUID in plaintext */
"/plain": u.input<GlobalParams>().output<Plain>(),

/** Gets uuids in bulk (up to 20k) in plaintext */
"/plain/bulk": u.input<PlainBulkParams>().output<PlainBulk>(),

/** Maps a namespace/key combo to the same UUID every time */
"/plain/map/:namespace/:key": u
.input<PlainNamespaceMapParams>()
.output<PlainNamespaceMap>(),

/** Maps a key to the same UUID every time (in the “default” namespace) */
"/plain/map/:key": u.input<PlainMapParams>().output<PlainMap>(),

/** Gets single Short UUID in plaintext */
"/s": u.input<GlobalParams>().output<string>(),

/** Gets single Short UUID in plaintext */
"/short": u.input<GlobalParams>().output<string>(),

/** Gets some stats about service usage, tracked via [countapi.xyz](https://countapi.xyz/) */
"/stats": u.output<Stats>(),

/** Gets single ULID in plaintext */
"/ulid": u.input<GlobalParams>().output<string>(),
});

export type UUIDRocksRouter = typeof router;
7 changes: 7 additions & 0 deletions packages/uuid.rocks/src/ip/ip.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { z } from "zod";

import type { IpJsonSchema, IpSchema } from "./ip.validators";

export type Ip = z.infer<typeof IpSchema>;

export type IpJson = z.infer<typeof IpJsonSchema>;
8 changes: 8 additions & 0 deletions packages/uuid.rocks/src/ip/ip.validators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { z } from "zod";

export const IpSchema = z.string().ip();

export const IpJsonSchema = z.object({
country: z.string(),
ip: IpSchema,
});
17 changes: 17 additions & 0 deletions packages/uuid.rocks/src/json/json.types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { z } from "zod";

import type {
JsonBulkParamsSchema,
JsonBulkSchema,
JsonMapParamsSchema,
JsonMapSchema,
JsonSchema,
} from "./json.validators";

export type Json = z.infer<typeof JsonSchema>;

export type JsonBulkParams = z.infer<typeof JsonBulkParamsSchema>;
export type JsonBulk = z.infer<typeof JsonBulkSchema>;

export type JsonMapParams = z.infer<typeof JsonMapParamsSchema>;
export type JsonMap = z.infer<typeof JsonMapSchema>;
Loading

0 comments on commit 7f64ffb

Please sign in to comment.