bunsane 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ import db from "database";
2
+ import { logger as MainLogger } from "core/Logger";
3
+ const logger = MainLogger.child({ scope: "DatabaseHelper" });
4
+
5
+ export const GetSchema = async () => {
6
+ const dbSchema = await db`SELECT table_name
7
+ FROM information_schema.tables
8
+ WHERE table_type = 'BASE TABLE'
9
+ AND table_schema NOT IN
10
+ ('pg_catalog', 'information_schema');`.values();
11
+ const tables = dbSchema.map((row: string[]) => row[0]);
12
+ return tables;
13
+ }
14
+
15
+ export const HasValidBaseTable = async (): Promise<boolean> => {
16
+ const tables = await GetSchema();
17
+ const neededTables = ["entities", "components", "entity_components"];
18
+ return neededTables.every(t => tables.includes(t));
19
+ }
20
+
21
+ export const PrepareDatabase = async () => {
22
+ logger.trace(`Initializing Database.`);
23
+ await SetupDatabaseExtensions();
24
+ await CreateEntityTable();
25
+ await CreateComponentTable();
26
+ await CreateEntityComponentTable();
27
+ }
28
+
29
+ export const SetupDatabaseExtensions = async () => {
30
+ return new Promise(async resolve => {
31
+ // await db`CREATE EXTENSION IF NOT EXISTS "uuid-ossp";`;
32
+ return resolve(true);
33
+ })
34
+ }
35
+
36
+ export const CreateEntityTable = async () => {
37
+ return new Promise(async resolve => {
38
+ await db`CREATE TABLE IF NOT EXISTS entities (
39
+ id UUID PRIMARY KEY,
40
+ created_at TIMESTAMP DEFAULT NOW(),
41
+ updated_at TIMESTAMP DEFAULT NOW()
42
+ );`;
43
+ return resolve(true);
44
+ });
45
+ }
46
+
47
+ export const CreateComponentTable = () => {
48
+ return new Promise(async resolve => {
49
+ await db`CREATE TABLE IF NOT EXISTS components (
50
+ id UUID,
51
+ entity_id UUID REFERENCES entities(id),
52
+ type_id varchar(64) NOT NULL,
53
+ name varchar(128),
54
+ data jsonb,
55
+ created_at TIMESTAMP DEFAULT NOW(),
56
+ updated_at TIMESTAMP DEFAULT NOW(),
57
+ PRIMARY KEY (id, type_id, entity_id)
58
+ ) PARTITION BY LIST (type_id);`;
59
+ await db`CREATE INDEX IF NOT EXISTS idx_components_entity_id ON components (entity_id);`
60
+ await db`CREATE INDEX IF NOT EXISTS idx_components_type_id ON components (type_id);`
61
+ await db`CREATE INDEX IF NOT EXISTS idx_components_data_gin ON components USING GIN (data);`
62
+ return resolve(true);
63
+ });
64
+ }
65
+
66
+
67
+ export const CreateComponentPartitionTable = async (comp_name: string, type_id: string, indexedProperties?: string[]) => {
68
+ try {
69
+ logger.trace(`Attempt adding partition table for component: ${comp_name}`);
70
+ const table_name = `components_${comp_name.toLowerCase().replace(/\s+/g, '_')}`;
71
+ logger.trace(`Checking for existing partition table: ${table_name}`);
72
+ const existingPartition = await db`
73
+ SELECT 1 FROM information_schema.tables
74
+ WHERE table_name = ${table_name}
75
+ AND table_schema = 'public'
76
+ `;
77
+ logger.trace(`Existing partition check result: ${existingPartition.length > 0 ? 'found' : 'not found'}`);
78
+
79
+ if (existingPartition.length > 0) {
80
+ logger.info(`Partition table ${table_name} already exists`);
81
+ return;
82
+ }
83
+ logger.trace(`Creating partition table: ${table_name}`);
84
+
85
+ await db.unsafe(`CREATE TABLE IF NOT EXISTS ${table_name}
86
+ PARTITION OF components
87
+ FOR VALUES IN ('${type_id}');`);
88
+
89
+ if (indexedProperties && indexedProperties.length > 0) {
90
+ for (const prop of indexedProperties) {
91
+ const indexName = `idx_${table_name}_${prop}`;
92
+ await db.unsafe(`CREATE INDEX IF NOT EXISTS ${indexName} ON ${table_name} USING GIN ((data->'${prop}'))`);
93
+ logger.trace(`Created index ${indexName} for property ${prop}`);
94
+ }
95
+ }
96
+
97
+ logger.trace(`Successfully created partition table: ${table_name}`);
98
+
99
+ } catch (error) {
100
+ logger.error(`Failed to create component partition table for ${comp_name}: ${error}`);
101
+ throw error;
102
+ }
103
+ }
104
+
105
+ export const DeleteComponentPartitionTable = async (comp_name: string) => {
106
+ try {
107
+ const table_name = `components_${comp_name.toLowerCase().replace(/\s+/g, '_')}`;
108
+
109
+ const existingPartition = await db`
110
+ SELECT 1 FROM information_schema.tables
111
+ WHERE table_name = ${table_name}
112
+ AND table_schema = 'public'
113
+ `;
114
+
115
+ if (existingPartition.length === 0) {
116
+ logger.info(`Partition table ${table_name} does not exist`);
117
+ return;
118
+ }
119
+
120
+ await db.unsafe(`DROP TABLE IF EXISTS ${table_name}`);
121
+ logger.info(`Successfully deleted partition table: ${table_name}`);
122
+
123
+ } catch (error) {
124
+ logger.error(`Failed to delete component partition table for ${comp_name}: ${error}`);
125
+ throw error;
126
+ }
127
+ }
128
+
129
+ export const CreateEntityComponentTable = async () => {
130
+ await db`CREATE TABLE IF NOT EXISTS entity_components (
131
+ entity_id UUID REFERENCES entities(id),
132
+ type_id VARCHAR(64) NOT NULL,
133
+ UNIQUE(entity_id, type_id)
134
+ );`;
135
+ await db`CREATE INDEX IF NOT EXISTS idx_entity_components_entity_id ON entity_components (entity_id);`
136
+ await db`CREATE INDEX IF NOT EXISTS idx_entity_components_type_id ON entity_components (type_id);`
137
+ await db`CREATE INDEX IF NOT EXISTS idx_entity_components_type_entity ON entity_components (type_id, entity_id);`
138
+ }
@@ -0,0 +1,26 @@
1
+ import {SQL} from "bun";
2
+ import { logger } from "core/Logger";
3
+
4
+ const db = new SQL({
5
+ url: `postgres://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:5432/${process.env.POSTGRES_DB}`,
6
+ // Connection pool settings
7
+ max: 10, // Maximum connections in pool
8
+ idleTimeout: 0, // Close idle connections after 30s
9
+ maxLifetime: 0, // Connection lifetime in seconds (0 = forever)
10
+ connectionTimeout: 30, // Timeout when establishing new connections
11
+ onclose: (err) => {
12
+ if (err) {
13
+ if((err as unknown as { code: string }).code === "ERR_POSTGRES_IDLE_TIMEOUT") {
14
+ logger.trace("Closing connection. Idle");
15
+ } else {
16
+ logger.error("Database connection closed with error:");
17
+ logger.error(err);
18
+ }
19
+ } else {
20
+ logger.info("Database connection closed.");
21
+ }
22
+ },
23
+ });
24
+
25
+
26
+ export default db;
@@ -0,0 +1,159 @@
1
+ import { GraphQLSchema, GraphQLError } from "graphql";
2
+ import {makeExecutableSchema} from "@graphql-tools/schema";
3
+ import { logger as MainLogger } from "core/Logger";
4
+ const logger = MainLogger.child({ scope: "GraphQLGenerator" });
5
+ export interface GraphQLTypeMeta {
6
+ name: string;
7
+ fields: Record<string, string>;
8
+ }
9
+
10
+ export interface GraphQLOperationMeta {
11
+ type: "Query" | "Mutation";
12
+ name?: string;
13
+ input?: Record<string, string>;
14
+ output: Record<string, string> | string;
15
+ }
16
+
17
+ export interface GraphQLFieldMeta {
18
+ type: string;
19
+ field: string;
20
+ }
21
+
22
+ export function GraphQLType(meta: GraphQLTypeMeta) {
23
+ return (target: any) => {
24
+ target.__graphqlType = meta;
25
+ }
26
+ }
27
+
28
+ export function GraphQLOperation(meta: GraphQLOperationMeta) {
29
+ return function (target: any, context: ClassMethodDecoratorContext) {
30
+ if (!target.__graphqlOperations) target.__graphqlOperations = [];
31
+ const operationName = meta.name ?? context;
32
+ if (!operationName) {
33
+ throw new Error("GraphQLOperation: Operation name is required (either meta.name or context.name must be defined)");
34
+ }
35
+ const operationMeta = { ...meta, name: operationName, propertyKey: context};
36
+ target.__graphqlOperations.push(operationMeta);
37
+ };
38
+ }
39
+
40
+ export function GraphQLField(meta: GraphQLFieldMeta) {
41
+ return function (target: any, context: ClassMethodDecoratorContext) {
42
+ if (!target.__graphqlFields) target.__graphqlFields = [];
43
+ target.__graphqlFields.push({ ...meta, propertyKey: context });
44
+ };
45
+ }
46
+
47
+ export function generateGraphQLSchema(systems: any[]): { schema: GraphQLSchema | null; resolvers: any } {
48
+ let typeDefs = "";
49
+ const resolvers: any = { Query: {}, Mutation: {} };
50
+ const queryFields: string[] = [];
51
+ const mutationFields: string[] = [];
52
+
53
+ systems.forEach(system => {
54
+ logger.trace(`Processing system: ${system.constructor.name}`);
55
+ if (system.constructor.__graphqlType) {
56
+ const { name, fields } = system.constructor.__graphqlType;
57
+ typeDefs += `type ${name} {\n${Object.entries(fields).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
58
+ }
59
+ if (system.__graphqlOperations) {
60
+ system.__graphqlOperations.forEach((op: any) => {
61
+ const { type, name, input, output, propertyKey } = op;
62
+ let fieldDef = `${name}`;
63
+ if (input) {
64
+ const inputName = `${name}Input`;
65
+ typeDefs += `input ${inputName} {\n${Object.entries(input).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
66
+ fieldDef += `(input: ${inputName}!)`;
67
+ resolvers[type][name] = async (_: any, args: any, context: any) => {
68
+ try {
69
+ return await system[propertyKey](args.input || args, context);
70
+ } catch (error) {
71
+ logger.error(`Error in ${type}.${name}:`);
72
+ logger.error(error);
73
+ if (error instanceof GraphQLError) {
74
+ throw error;
75
+ }
76
+ throw new GraphQLError(`Internal error in ${name}`, {
77
+ extensions: {
78
+ code: "INTERNAL_ERROR",
79
+ originalError: process.env.NODE_ENV === 'development' ? error : undefined
80
+ }
81
+ });
82
+ }
83
+ };
84
+ } else {
85
+ resolvers[type][name] = async (_: any, args: any, context: any) => {
86
+ try {
87
+ return await system[propertyKey]({}, context);
88
+ } catch (error) {
89
+ logger.error(`Error in ${type}.${name}:`);
90
+ logger.error(error);
91
+ if (error instanceof GraphQLError) {
92
+ throw error;
93
+ }
94
+ throw new GraphQLError(`Internal error in ${name}`, {
95
+ extensions: {
96
+ code: "INTERNAL_ERROR",
97
+ originalError: process.env.NODE_ENV === 'development' ? error : undefined
98
+ }
99
+ });
100
+ }
101
+ };
102
+ }
103
+ if (typeof output === 'string') {
104
+ fieldDef += `: ${output}`;
105
+ } else if (typeof output === 'object') {
106
+ const outputName = `${name}Output`;
107
+ typeDefs += `type ${outputName} {\n${Object.entries(output).map(([k, v]) => ` ${k}: ${v}`).join('\n')}\n}\n`;
108
+ fieldDef += `: ${outputName}`;
109
+ }
110
+ if (type === 'Query') {
111
+ queryFields.push(fieldDef);
112
+ } else if (type === 'Mutation') {
113
+ mutationFields.push(fieldDef);
114
+ }
115
+ });
116
+ }
117
+ });
118
+
119
+ // Process field resolvers
120
+ systems.forEach(system => {
121
+ if (system.__graphqlFields) {
122
+ system.__graphqlFields.forEach((fieldMeta: any) => {
123
+ const { type, field, propertyKey } = fieldMeta;
124
+ if (!resolvers[type]) resolvers[type] = {};
125
+ resolvers[type][field] = async (parent: any, args: any, context: any) => {
126
+ try {
127
+ return await system[propertyKey](parent, args, context);
128
+ } catch (error) {
129
+ logger.error(`Error in ${type}.${field}:`);
130
+ logger.error(error);
131
+ if (error instanceof GraphQLError) {
132
+ throw error;
133
+ }
134
+ throw new GraphQLError(`Internal error in ${field}`, {
135
+ extensions: {
136
+ code: "INTERNAL_ERROR",
137
+ originalError: process.env.NODE_ENV === 'development' ? error : undefined
138
+ }
139
+ });
140
+ }
141
+ };
142
+ });
143
+ }
144
+ });
145
+
146
+ if (queryFields.length > 0) {
147
+ typeDefs += `type Query {\n${queryFields.map(f => ` ${f}`).join('\n')}\n}\n`;
148
+ }
149
+ if (mutationFields.length > 0) {
150
+ typeDefs += `type Mutation {\n${mutationFields.map(f => ` ${f}`).join('\n')}\n}\n`;
151
+ }
152
+
153
+ logger.trace(`System Type Defs: ${typeDefs}`);
154
+ let schema : GraphQLSchema | null = null;
155
+ if(typeDefs !== "") {
156
+ schema = makeExecutableSchema({ typeDefs, resolvers });
157
+ }
158
+ return { schema, resolvers };
159
+ }
package/gql/index.ts ADDED
@@ -0,0 +1,96 @@
1
+ import {createSchema, createYoga} from 'graphql-yoga';
2
+ import { GraphQLSchema, GraphQLError } from 'graphql';
3
+ import { GraphQLType, GraphQLField, GraphQLOperation } from './Generator';
4
+ import {GraphQLTypes} from "./types"
5
+ export {
6
+ GraphQLType,
7
+ GraphQLField,
8
+ GraphQLOperation,
9
+ GraphQLTypes
10
+ }
11
+ interface Entity {
12
+ id: string;
13
+ name: string;
14
+ description: string;
15
+ }
16
+
17
+ const staticTypeDefs = `
18
+ type Query {
19
+ greetings: String
20
+ entities: [Entity]
21
+ entity(id: ID!): Entity
22
+ }
23
+
24
+ type Entity {
25
+ id: ID!
26
+ name: String!
27
+ description: String
28
+ }
29
+ `;
30
+
31
+ const staticResolvers = {
32
+ Query: {
33
+ greetings: (): string => "Hello, world!",
34
+ entities: (): Entity[] => {
35
+ // Fetch entities from the database or any other source
36
+ return [
37
+ {
38
+ id: "1",
39
+ name: "Entity 1",
40
+ description: "Description for Entity 1"
41
+ }
42
+ ];
43
+ },
44
+ entity: (_parent: any, args: { id: string }): Entity | null => {
45
+ const { id } = args;
46
+ // Fetch a single entity by ID from the database or any other source
47
+ return null;
48
+ }
49
+ }
50
+ };
51
+
52
+ export function createYogaInstance(schema?: GraphQLSchema) {
53
+ if (schema) {
54
+ return createYoga({
55
+ schema,
56
+ // Configure error handling to preserve error messages for clients
57
+ maskedErrors: {
58
+ // In development, show full error details
59
+ // In production, you might want to mask sensitive information
60
+ maskError: (error: any, message: string): GraphQLError => {
61
+ if (process.env.NODE_ENV === 'production') {
62
+ // Mask sensitive error details in production
63
+ return new GraphQLError('Internal server error', {
64
+ extensions: {
65
+ code: 'INTERNAL_SERVER_ERROR',
66
+ },
67
+ });
68
+ }
69
+ // In development, return the original error
70
+ return error instanceof GraphQLError ? error : new GraphQLError(message, { originalError: error });
71
+ },
72
+ },
73
+ });
74
+ } else {
75
+ return createYoga({
76
+ schema: createSchema({
77
+ typeDefs: staticTypeDefs,
78
+ resolvers: staticResolvers,
79
+ }),
80
+ maskedErrors: {
81
+ maskError: (error: any, message: string): GraphQLError => {
82
+ if (process.env.NODE_ENV === 'production') {
83
+ return new GraphQLError('Internal server error', {
84
+ extensions: {
85
+ code: 'INTERNAL_SERVER_ERROR',
86
+ },
87
+ });
88
+ }
89
+ return error instanceof GraphQLError ? error : new GraphQLError(message, { originalError: error });
90
+ },
91
+ },
92
+ });
93
+ }
94
+ }
95
+
96
+ export const yoga = createYogaInstance();
package/gql/types.ts ADDED
@@ -0,0 +1,28 @@
1
+ export enum GraphQLScalar {
2
+ ID = "ID",
3
+ String = "String",
4
+ Int = "Int",
5
+ Float = "Float",
6
+ Boolean = "Boolean",
7
+ Date = "Date",
8
+ }
9
+
10
+ export enum GraphQLTypes {
11
+ ID_REQUIRED = "ID!",
12
+ ID_OPTIONAL = "ID",
13
+ STRING_REQUIRED = "String!",
14
+ STRING_OPTIONAL = "String",
15
+ INT_REQUIRED = "Int!",
16
+ INT_OPTIONAL = "Int",
17
+ FLOAT_REQUIRED = "Float!",
18
+ FLOAT_OPTIONAL = "Float",
19
+ BOOLEAN_REQUIRED = "Boolean!",
20
+ BOOLEAN_OPTIONAL = "Boolean",
21
+ }
22
+
23
+ export const GraphQLList = {
24
+ of: (type: string) => `[${type}]`,
25
+ ofRequired: (type: string) => `[${type}]!`,
26
+ } as const;
27
+
28
+
package/index.ts ADDED
@@ -0,0 +1,22 @@
1
+ import App from "core/App";
2
+ import ServiceRegistry from "service/ServiceRegistry";
3
+ import BaseService from "service/Service";
4
+ import { Component, CompData, BaseComponent } from "core/Components";
5
+ import { Entity } from "core/Entity";
6
+ import Query from "core/Query";
7
+ import {logger} from "core/Logger";
8
+ import { handleGraphQLError, responseError } from "core/ErrorHandler";
9
+ export {
10
+ App,
11
+ ServiceRegistry,
12
+ BaseService,
13
+ BaseComponent,
14
+ Component,
15
+ CompData,
16
+ Entity,
17
+ Query,
18
+ logger,
19
+
20
+ responseError,
21
+ handleGraphQLError
22
+ };
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "bunsane",
3
+ "version": "0.1.0",
4
+ "author": {
5
+ "name": "yaaruu"
6
+ },
7
+ "module": "index.ts",
8
+ "type": "module",
9
+ "devDependencies": {
10
+ "@types/bun": "latest"
11
+ },
12
+ "peerDependencies": {
13
+ "typescript": "^5"
14
+ },
15
+ "dependencies": {
16
+ "graphql": "^16.11.0",
17
+ "graphql-yoga": "^5.15.1",
18
+ "pino": "^9.9.0",
19
+ "pino-pretty": "^13.1.1",
20
+ "reflect-metadata": "^0.2.2",
21
+ "zod": "^4.1.5"
22
+ },
23
+ "graphql": {
24
+ "schema": "https://localhost:3001/graphql",
25
+ "documents": "**/*.{graphql,js,ts,jsx,tsx}"
26
+ },
27
+ "license": "MIT"
28
+ }
@@ -0,0 +1,7 @@
1
+ class BaseService {
2
+ constructor() {
3
+
4
+ }
5
+ }
6
+
7
+ export default BaseService;
@@ -0,0 +1,51 @@
1
+ import type BaseService from "./Service";
2
+ import ApplicationLifecycle, {ApplicationPhase} from "core/ApplicationLifecycle";
3
+ import { generateGraphQLSchema } from "gql/Generator";
4
+ import { GraphQLSchema } from "graphql";
5
+
6
+ class ServiceRegistry {
7
+ static #instance: ServiceRegistry;
8
+
9
+ private systems: Map<string, BaseService> = new Map();
10
+ private schema: GraphQLSchema | null = null;
11
+
12
+
13
+ constructor() {
14
+
15
+ }
16
+
17
+ public init() {
18
+ ApplicationLifecycle.addPhaseListener((event) => {
19
+ switch(event.detail) {
20
+ case ApplicationPhase.SYSTEM_REGISTERING: {
21
+ const systemsArray = Array.from(this.systems.values());
22
+ const { schema } = generateGraphQLSchema(systemsArray);
23
+ this.schema = schema;
24
+ ApplicationLifecycle.setPhase(ApplicationPhase.SYSTEM_READY);
25
+ break;
26
+ };
27
+ }
28
+ });
29
+ }
30
+
31
+
32
+
33
+ public static get instance() : ServiceRegistry {
34
+ if (!ServiceRegistry.#instance) {
35
+ ServiceRegistry.#instance = new ServiceRegistry();
36
+ }
37
+ return ServiceRegistry.#instance;
38
+ }
39
+
40
+ public registerService(system: BaseService) {
41
+ if(!this.systems.has(system.constructor.name)) {
42
+ this.systems.set(system.constructor.name, system);
43
+ }
44
+ }
45
+
46
+ public getSchema(): GraphQLSchema | null {
47
+ return this.schema;
48
+ }
49
+ }
50
+
51
+ export default ServiceRegistry.instance;
@@ -0,0 +1,6 @@
1
+ import BaseService from "./Service";
2
+ import { ServiceRegistry } from "index";
3
+ export {
4
+ BaseService,
5
+ ServiceRegistry
6
+ }
@@ -0,0 +1,39 @@
1
+ import {describe, test, expect, beforeAll} from "bun:test"
2
+ import App from "core/App"
3
+ import { BaseComponent, CompData, Component } from "core/Components";
4
+ import { Entity } from "core/Entity";
5
+
6
+ let app;
7
+ beforeAll(async () => {
8
+ app = new App();
9
+ await app.waitForAppReady();
10
+ });
11
+
12
+ @Component
13
+ class TestComponent extends BaseComponent {
14
+ @CompData()
15
+ value: string = "";
16
+ }
17
+
18
+ describe("Query test", async () => {
19
+ test("Create and Update Entity", async () => {
20
+ const entity = Entity.Create()
21
+ .add(TestComponent, {value: "Test"})
22
+ await entity.save();
23
+
24
+ const fetchedEntity = await Entity.FindById(entity.id);
25
+ expect(fetchedEntity).not.toBeNull();
26
+ expect(fetchedEntity?.componentList().length).toBe(1);
27
+
28
+ await fetchedEntity?.set(TestComponent, {value: "UpdatedTest"});
29
+ console.log("Updating Entity");
30
+ await fetchedEntity?.save();
31
+
32
+ const updatedEntity = await Entity.FindById(entity.id);
33
+ expect(updatedEntity).not.toBeNull();
34
+ expect(updatedEntity?.componentList().length).toBe(1);
35
+ const comp = await updatedEntity?.get(TestComponent)
36
+ expect(comp?.value).toBe("UpdatedTest");
37
+
38
+ });
39
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false,
28
+
29
+ "baseUrl": ".",
30
+ "experimentalDecorators": true,
31
+ "emitDecoratorMetadata": true,
32
+ "typeRoots": ["./types", "./node_modules/@types"]
33
+ }
34
+ }
package/utils/uuid.ts ADDED
@@ -0,0 +1,10 @@
1
+ export const uuidv7 = () => {
2
+ return 'tttttttt-tttt-7xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
3
+ const r = Math.trunc(Math.random() * 16);
4
+ const v = c == 'x' ? r : (r & 0x3 | 0x8);
5
+ return v.toString(16);
6
+ }).replace(/^[t]{8}-[t]{4}/, function() {
7
+ const unixtimestamp = Date.now().toString(16).padStart(12, '0');
8
+ return unixtimestamp.slice(0, 8) + '-' + unixtimestamp.slice(8);
9
+ });
10
+ }