@localsecurity/name-id
Version:
A utility to create and parse lexicographically sortable ID's with embeded model names and created timestamp
169 lines (124 loc) • 3.67 kB
Markdown
# /name-id
A tiny TypeScript utility to create and parse lexicographically sortable IDs with embedded model names and creation timestamps.
> **Use case:**
Durable, globally unique IDs for NoSQL or single-table designs that sort newest-first.
## ✨ Features
✅ Generates stable IDs in the format:
`model` _ `reversedTimestamp` `random`
✅ Converts legacy UUIDs to sortable IDs
✅ Extracts model name and timestamp
✅ Zero dependencies (besides `/types`)
✅ TypeScript-native with branded types for safety
## 🚀 Install
```bash
npm install /name-id
```
## 🔧 Usage
### Create a NameID
```ts
import { NameID } from "@localsecurity/name-id";
// From a model name
const id1 = new NameID("User");
console.log(id1.value);
// => user_k9z8a1abcdef234567...
// From an existing object with __typename and createdAt
const id2 = new NameID({
__typename: "CameraEvent",
createdAt: "2024-07-01T00:00:00.000Z"
});
console.log(id2.value);
// => cameraevent_k9z8a1abcdef234567...
```
### Parse a NameID
```ts
const parsed = NameID.parse(id1.value);
console.log(parsed.name); // "User"
console.log(parsed.timestamp); // Date
```
### Type Guards
```ts
import { isNameId, isUUID } from "@localsecurity/name-id";
isNameId("user_abc..."); // true
isUUID("xxxxxxx-xxxx..."); // true
```
### MutableModel
You can extend MutableModel to get a base class with automatic nameId and timestamps:
```ts
import { MutableModel } from "@localsecurity/name-id";
class MyModel extends MutableModel {
data: string;
constructor(init: Partial<MyModel>) {
super("User");
Object.assign(this, init);
}
}
```
### Example Worker
```ts
import { Model } from '@localsecurity/types';
import { ModelName, NameID, NameIdString, isModelName } from "@localsecurity/name-id";
const ErrorResponse = (e: any) => Response.json({ error: e }, { status: 500 });
/**
* Handle a request with a Model Name in the body
*/
async function handleName(request: Request) {
const __typename = await request.text();
const execute = () => {
if (isModelName(__typename)) {
const nameId = new NameID(__typename).value;
const createdAt = NameID.parse(nameId).timestamp.toISOString();
return Response.json({ nameId, createdAt, __typename });
} else { return ErrorResponse(`Not a valid Model Name ('${__typename}')`) }
}
try { return execute() }
catch (e: any) { return ErrorResponse(e) }
};
/**
* Handle a request with a Model Object in the body
*/
async function handleObject(request: Request) {
const object: Model[ModelName] = await request.json();
const __typename = object['__typename'];
const execute = () => {
if (isModelName(__typename)) {
const nameId: NameIdString = new NameID(object).value;
return Response.json({ nameId, ...object });
} else { return ErrorResponse(`Not a valid Model Name ('${__typename}')`) }
}
try { return execute() }
catch (e: any) { return ErrorResponse(e) }
};
/**
* Worker entrypoint
*/
export default {
async fetch(request: Request, env: any, ctx: any) {
const contentType = request.headers.get("content-type") ?? '';
const execute = () => {
if (contentType.includes("json")) return handleObject(request)
else return handleName(request)
}
try { return execute() }
catch (e: any) { return ErrorResponse(e) }
}
};
```
## 🧑💻 Development
Build:
```bash
npm run build
```
Lint:
```bash
npm run lint
```
Format:
```bash
npm run format
```
Bundle ESM:
```bash
npm run bundle
```
## 📄 License
MIT © LocalSecurity