@kunk/database 3.0.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.
- package/.turbo/turbo-build.log +13 -0
- package/CHANGELOG.md +13 -0
- package/dist/chunk-q_BqMMLn.mjs +1 -0
- package/dist/index.d.mts +64 -0
- package/dist/index.mjs +1 -0
- package/package.json +22 -0
- package/src/index.ts +3 -0
- package/src/migration/+.ts +1 -0
- package/src/migration/migrate.ts +17 -0
- package/src/migration/setup.sql +61 -0
- package/src/schema/+.ts +1 -0
- package/src/schema/schema-types.ts +91 -0
- package/src/seed/+.ts +1 -0
- package/src/seed/seed.ts +13 -0
- package/tsconfig.json +16 -0
- package/tsdown.config.ts +15 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
$ tsdown
|
|
2
|
+
[34mℹ[39m tsdown [2mv0.20.3[22m powered by rolldown [2mv1.0.0-rc.3[22m
|
|
3
|
+
[34mℹ[39m config file: [4m/home/runner/work/kunk/kunk/packages/database/tsdown.config.ts[24m (unrun)
|
|
4
|
+
[34mℹ[39m entry: [34msrc/index.ts[39m
|
|
5
|
+
[34mℹ[39m tsconfig: [34mtsconfig.json[39m
|
|
6
|
+
[34mℹ[39m Build start
|
|
7
|
+
[34mℹ[39m [2mdist/[22m[1mindex.mjs[22m [2m2.23 kB[22m [2m│ gzip: 0.95 kB[22m
|
|
8
|
+
[34mℹ[39m [2mdist/[22mchunk-q_BqMMLn.mjs [2m0.21 kB[22m [2m│ gzip: 0.17 kB[22m
|
|
9
|
+
[34mℹ[39m [2mdist/[22m[32m[1mindex.d.mts[22m[39m [2m2.75 kB[22m [2m│ gzip: 0.88 kB[22m
|
|
10
|
+
[34mℹ[39m 3 files, total: 5.19 kB
|
|
11
|
+
[33m[PLUGIN_TIMINGS] Warning:[0m Your build spent significant time in plugin `rolldown-plugin-dts:generate`. See https://rolldown.rs/options/checks#plugintimings for more details.
|
|
12
|
+
|
|
13
|
+
[32m✔[39m Build complete in [32m4866ms[39m
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var __defProp=Object.defineProperty,__exportAll=(e,t)=>{let n={};for(var r in e)__defProp(n,r,{get:e[r],enumerable:!0});return t||__defProp(n,Symbol.toStringTag,{value:`Module`}),n};export{__exportAll as t};
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { Faker } from "@faker-js/faker";
|
|
2
|
+
import * as pg from "drizzle-orm/pg-core";
|
|
3
|
+
import { DeepKey, DeepValue } from "@kunk/api";
|
|
4
|
+
|
|
5
|
+
//#region src/migration/migrate.d.ts
|
|
6
|
+
declare function migrate(options: Bun.SQL.Options): Promise<void>;
|
|
7
|
+
//#endregion
|
|
8
|
+
//#region src/seed/seed.d.ts
|
|
9
|
+
declare const seed: <T extends {
|
|
10
|
+
$inferInsert: any;
|
|
11
|
+
}>(block: (faker: Faker) => T["$inferInsert"]) => (override?: { [key in DeepKey<T["$inferInsert"]>]?: DeepValue<T["$inferInsert"], key> }) => T["$inferInsert"];
|
|
12
|
+
declare namespace schema_types_d_exports {
|
|
13
|
+
export { code, date, email, full_name, id, pg, phone, ref, schema, table, text, uuid };
|
|
14
|
+
}
|
|
15
|
+
declare const schema: typeof pg.pgSchema;
|
|
16
|
+
declare const id: () => pg.SetHasDefault<pg.SetIsPrimaryKey<pg.PgUUIDBuilder>>;
|
|
17
|
+
declare const ref: (table: () => {
|
|
18
|
+
id: any;
|
|
19
|
+
}) => pg.PgUUIDBuilder;
|
|
20
|
+
declare const text: () => pg.PgVarcharBuilder<undefined>;
|
|
21
|
+
declare const date: () => pg.PgDateStringBuilder;
|
|
22
|
+
declare function audit(): {
|
|
23
|
+
created_by: pg.SetHasDefault<pg.SetNotNull<pg.PgUUIDBuilder>>;
|
|
24
|
+
created_at: pg.SetHasDefault<pg.SetNotNull<pg.PgTimestampBuilder>>;
|
|
25
|
+
updated_by: pg.SetHasDefault<pg.SetNotNull<pg.PgUUIDBuilder>>;
|
|
26
|
+
updated_at: pg.SetHasDefault<pg.SetNotNull<pg.PgTimestampBuilder>>;
|
|
27
|
+
};
|
|
28
|
+
declare function tableWithGlobal<TTableName extends string, TColumnsMap extends {
|
|
29
|
+
[key: string]: pg.AnyPgColumnBuilder;
|
|
30
|
+
}>(options: {
|
|
31
|
+
schema: ReturnType<typeof pg.pgSchema>;
|
|
32
|
+
name: TTableName;
|
|
33
|
+
columns: TColumnsMap;
|
|
34
|
+
extraConfig?: (self: pg.PgBuildExtraConfigColumns<TColumnsMap>) => pg.PgTableExtraConfigValue[];
|
|
35
|
+
}): [pg.PgTableWithColumns<{
|
|
36
|
+
name: TTableName;
|
|
37
|
+
schema: string | undefined;
|
|
38
|
+
columns: pg.PgBuildColumns<TTableName, TColumnsMap & ReturnType<typeof audit>>;
|
|
39
|
+
dialect: 'pg';
|
|
40
|
+
}>];
|
|
41
|
+
declare function tableWithTenant<TTableName extends string, TColumnsMap extends Record<string, pg.AnyPgColumnBuilder>>(options: {
|
|
42
|
+
schema?: ReturnType<typeof pg.pgSchema>;
|
|
43
|
+
name: TTableName;
|
|
44
|
+
columns: TColumnsMap;
|
|
45
|
+
extraConfig?: (self: pg.PgBuildExtraConfigColumns<TColumnsMap>) => pg.PgTableExtraConfigValue[];
|
|
46
|
+
}): [pg.PgTableWithColumns<{
|
|
47
|
+
name: TTableName;
|
|
48
|
+
schema: string | undefined;
|
|
49
|
+
columns: pg.PgBuildColumns<TTableName, TColumnsMap & ReturnType<typeof audit> & {
|
|
50
|
+
tenant_id: pg.AnyPgColumnBuilder;
|
|
51
|
+
}>;
|
|
52
|
+
dialect: 'pg';
|
|
53
|
+
}>, pg.PgPolicy];
|
|
54
|
+
declare const uuid: () => pg.PgUUIDBuilder;
|
|
55
|
+
declare const code: (size: number) => pg.PgVarcharBuilder<undefined>;
|
|
56
|
+
declare const phone: () => pg.PgVarcharBuilder<undefined>;
|
|
57
|
+
declare const email: () => pg.PgVarcharBuilder<undefined>;
|
|
58
|
+
declare const full_name: () => pg.PgVarcharBuilder<undefined>;
|
|
59
|
+
declare const table: {
|
|
60
|
+
global: typeof tableWithGlobal;
|
|
61
|
+
tenant: typeof tableWithTenant;
|
|
62
|
+
};
|
|
63
|
+
//#endregion
|
|
64
|
+
export { schema_types_d_exports as DB, migrate, seed };
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{t as __exportAll}from"./chunk-q_BqMMLn.mjs";import{drizzle}from"drizzle-orm/bun-sql";import*as path from"path";import{migrate as migrate$1}from"drizzle-orm/bun-sql/migrator";import*as fp from"lodash/fp";import{fakerPT_BR}from"@faker-js/faker";import{sql}from"drizzle-orm";import*as pg from"drizzle-orm/pg-core";async function migrate(e){let r=new Bun.SQL(e);if(e.adapter!==`postgres`)throw Error(`Only PostgreSQL is supported`);await r.file(path.resolve(__dirname,`./setup.sql`)),await migrate$1(drizzle({client:r}),{migrationsFolder:`drizzle/migrations`}),await r`select create_role_with_password('app_user', ${e.password})`,await r`grant writers to app_user`}const seed=e=>t=>fp.pipe(Object.entries,fp.reduce((e,[t,n])=>fp.set(t,n,e),e(fakerPT_BR)))(t||{});var schema_types_exports=__exportAll({code:()=>code,date:()=>date,email:()=>email,full_name:()=>full_name,id:()=>id,pg:()=>pg,phone:()=>phone,ref:()=>ref,schema:()=>schema,table:()=>table,text:()=>text,uuid:()=>uuid});const schema=pg.pgSchema,id=()=>pg.uuid().primaryKey().default(sql`uuidv7()`),ref=e=>pg.uuid().references(()=>e().id),text=()=>pg.varchar({length:255}),date=()=>pg.date();function audit(){return{created_by:pg.uuid().notNull().default(sql`uuidv7()`),created_at:pg.timestamp().notNull().defaultNow(),updated_by:pg.uuid().notNull().default(sql`uuidv7()`),updated_at:pg.timestamp().notNull().defaultNow()}}function tableWithGlobal(e){return[pg.pgTable(e.name,{...e.columns,tenant_id:pg.varchar({length:50}).notNull().default(sql`current_setting('app.tenant_id')`),...audit()},e.extraConfig)]}function tableWithTenant(e){let t=(e.schema?.table?.withRLS||pg.pgTable.withRLS)(e.name,{...e.columns,tenant_id:pg.varchar({length:50}).notNull().default(sql`current_setting('app.tenant_id')`),...audit()},e.extraConfig);return[t,pg.pgPolicy(`tenant_isolation_policy`,{using:sql`tenant_id = current_setting('app.tenant_id')`,withCheck:sql`tenant_id = current_setting('app.tenant_id')`}).link(t)]}const uuid=()=>pg.uuid(),code=e=>pg.varchar({length:e}),phone=()=>pg.varchar({length:20}),email=()=>pg.varchar({length:100}),full_name=()=>pg.varchar({length:100}),table={global:tableWithGlobal,tenant:tableWithTenant};export{schema_types_exports as DB,migrate,seed};
|
package/package.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kunk/database",
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.ts",
|
|
7
|
+
"module": "./src/index.ts",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsdown"
|
|
10
|
+
},
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@kunk/api": "workspace:*",
|
|
13
|
+
"@kunk/tsconfig": "workspace:*",
|
|
14
|
+
"drizzle-orm": "^1.0.0-beta",
|
|
15
|
+
"drizzle-kit": "^1.0.0-beta",
|
|
16
|
+
"@faker-js/faker": "^10.3.0",
|
|
17
|
+
"lodash": "^4.17.23"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
}
|
|
22
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./migrate"
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { drizzle } from "drizzle-orm/bun-sql";
|
|
2
|
+
import * as path from "path"
|
|
3
|
+
import { migrate as drizzleMigrate } from 'drizzle-orm/bun-sql/migrator'
|
|
4
|
+
|
|
5
|
+
export async function migrate(options: Bun.SQL.Options) {
|
|
6
|
+
const sql = new Bun.SQL(options)
|
|
7
|
+
if (options.adapter !== 'postgres') {
|
|
8
|
+
throw new Error('Only PostgreSQL is supported')
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
await sql.file(path.resolve(__dirname, './setup.sql'))
|
|
12
|
+
const db = drizzle({ client: sql })
|
|
13
|
+
await drizzleMigrate(db, { migrationsFolder: 'drizzle/migrations' });
|
|
14
|
+
|
|
15
|
+
await sql`select create_role_with_password('app_user', ${options.password})`
|
|
16
|
+
await sql`grant writers to app_user`
|
|
17
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
|
2
|
+
|
|
3
|
+
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
|
|
4
|
+
|
|
5
|
+
CREATE OR REPLACE FUNCTION create_role(role_name TEXT)
|
|
6
|
+
RETURNS VOID AS $$
|
|
7
|
+
BEGIN
|
|
8
|
+
IF to_regrole(role_name) IS NULL THEN
|
|
9
|
+
EXECUTE format('CREATE ROLE %I', role_name);
|
|
10
|
+
END IF;
|
|
11
|
+
END;
|
|
12
|
+
$$ LANGUAGE plpgsql;
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
CREATE OR REPLACE FUNCTION create_role_with_password(role_name TEXT, role_password TEXT)
|
|
16
|
+
RETURNS VOID AS $$
|
|
17
|
+
BEGIN
|
|
18
|
+
IF to_regrole(role_name) IS NULL THEN
|
|
19
|
+
EXECUTE format('CREATE ROLE %I WITH LOGIN PASSWORD %L', role_name, role_password);
|
|
20
|
+
END IF;
|
|
21
|
+
END;
|
|
22
|
+
$$ LANGUAGE plpgsql;
|
|
23
|
+
|
|
24
|
+
CREATE OR REPLACE FUNCTION grant_on_new_schema()
|
|
25
|
+
RETURNS event_trigger
|
|
26
|
+
LANGUAGE plpgsql
|
|
27
|
+
AS $$
|
|
28
|
+
DECLARE
|
|
29
|
+
obj record;
|
|
30
|
+
BEGIN
|
|
31
|
+
FOR obj IN
|
|
32
|
+
SELECT * FROM pg_event_trigger_ddl_commands()
|
|
33
|
+
LOOP
|
|
34
|
+
IF obj.object_type = 'schema' THEN
|
|
35
|
+
EXECUTE format(
|
|
36
|
+
'GRANT USAGE ON SCHEMA %I TO writers',
|
|
37
|
+
obj.object_identity
|
|
38
|
+
);
|
|
39
|
+
EXECUTE format(
|
|
40
|
+
'ALTER DEFAULT PRIVILEGES IN SCHEMA %I
|
|
41
|
+
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO writers',
|
|
42
|
+
obj.object_identity
|
|
43
|
+
);
|
|
44
|
+
EXECUTE format(
|
|
45
|
+
'ALTER DEFAULT PRIVILEGES IN SCHEMA %I
|
|
46
|
+
GRANT USAGE, SELECT ON SEQUENCES TO writers',
|
|
47
|
+
obj.object_identity
|
|
48
|
+
);
|
|
49
|
+
END IF;
|
|
50
|
+
END LOOP;
|
|
51
|
+
END;
|
|
52
|
+
$$;
|
|
53
|
+
|
|
54
|
+
DROP EVENT TRIGGER IF EXISTS trigger_grant_on_schema;
|
|
55
|
+
|
|
56
|
+
CREATE EVENT TRIGGER trigger_grant_on_schema
|
|
57
|
+
ON ddl_command_end
|
|
58
|
+
WHEN TAG IN ('CREATE SCHEMA')
|
|
59
|
+
EXECUTE FUNCTION grant_on_new_schema();
|
|
60
|
+
|
|
61
|
+
select create_role('writers');
|
package/src/schema/+.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * as DB from "./schema-types"
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { sql } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
import * as pg from "drizzle-orm/pg-core";
|
|
4
|
+
|
|
5
|
+
export const schema = pg.pgSchema;
|
|
6
|
+
|
|
7
|
+
export const id = () => pg.uuid().primaryKey().default(sql`uuidv7()`)
|
|
8
|
+
|
|
9
|
+
export const ref = (table: () => { id: any }) => pg.uuid().references(() => table().id)
|
|
10
|
+
|
|
11
|
+
export const text = () => pg.varchar({ length: 255 })
|
|
12
|
+
|
|
13
|
+
export const date = () => pg.date()
|
|
14
|
+
|
|
15
|
+
function audit() {
|
|
16
|
+
return {
|
|
17
|
+
created_by: pg.uuid().notNull().default(sql`uuidv7()`),
|
|
18
|
+
created_at: pg.timestamp().notNull().defaultNow(),
|
|
19
|
+
updated_by: pg.uuid().notNull().default(sql`uuidv7()`),
|
|
20
|
+
updated_at: pg.timestamp().notNull().defaultNow(),
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function tableWithGlobal<TTableName extends string, TColumnsMap extends {
|
|
25
|
+
[key: string]: pg.AnyPgColumnBuilder;
|
|
26
|
+
}>(options: {
|
|
27
|
+
schema: ReturnType<typeof pg.pgSchema>,
|
|
28
|
+
name: TTableName,
|
|
29
|
+
columns: TColumnsMap,
|
|
30
|
+
extraConfig?: (self: pg.PgBuildExtraConfigColumns<TColumnsMap>) => pg.PgTableExtraConfigValue[]
|
|
31
|
+
}): [pg.PgTableWithColumns<{
|
|
32
|
+
name: TTableName;
|
|
33
|
+
schema: string | undefined;
|
|
34
|
+
columns: pg.PgBuildColumns<TTableName, TColumnsMap & ReturnType<typeof audit>>;
|
|
35
|
+
dialect: 'pg';
|
|
36
|
+
}>] {
|
|
37
|
+
const table = pg.pgTable(options.name, {
|
|
38
|
+
...options.columns,
|
|
39
|
+
tenant_id: pg.varchar({ length: 50 }).notNull().default(sql`current_setting('app.tenant_id')`),
|
|
40
|
+
...audit(),
|
|
41
|
+
}, options.extraConfig)
|
|
42
|
+
return [table as any]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function tableWithTenant<TTableName extends string, TColumnsMap extends Record<string, pg.AnyPgColumnBuilder>>(options: {
|
|
46
|
+
schema?: ReturnType<typeof pg.pgSchema>,
|
|
47
|
+
name: TTableName
|
|
48
|
+
columns: TColumnsMap,
|
|
49
|
+
extraConfig?: (self: pg.PgBuildExtraConfigColumns<TColumnsMap>) => pg.PgTableExtraConfigValue[]
|
|
50
|
+
}): [pg.PgTableWithColumns<{
|
|
51
|
+
name: TTableName;
|
|
52
|
+
schema: string | undefined;
|
|
53
|
+
columns: pg.PgBuildColumns<TTableName, TColumnsMap & ReturnType<typeof audit> & {
|
|
54
|
+
tenant_id: pg.AnyPgColumnBuilder;
|
|
55
|
+
}>;
|
|
56
|
+
dialect: 'pg';
|
|
57
|
+
}>, pg.PgPolicy] {
|
|
58
|
+
const withRLS = (options.schema?.table?.withRLS || pg.pgTable.withRLS) as pg.PgTableFnInternal<string>;
|
|
59
|
+
|
|
60
|
+
const table = withRLS(options.name, {
|
|
61
|
+
...options.columns,
|
|
62
|
+
tenant_id: pg.varchar({ length: 50 }).notNull().default(sql`current_setting('app.tenant_id')`),
|
|
63
|
+
...audit()
|
|
64
|
+
}, options.extraConfig)
|
|
65
|
+
|
|
66
|
+
const policy = pg.pgPolicy('tenant_isolation_policy', {
|
|
67
|
+
using: sql`tenant_id = current_setting('app.tenant_id')`,
|
|
68
|
+
withCheck: sql`tenant_id = current_setting('app.tenant_id')`,
|
|
69
|
+
}).link(table)
|
|
70
|
+
|
|
71
|
+
return [table as any, policy]
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export const uuid = () => pg.uuid()
|
|
75
|
+
|
|
76
|
+
export const code = (size: number) => pg.varchar({ length: size })
|
|
77
|
+
|
|
78
|
+
export const phone = () => pg.varchar({ length: 20 })
|
|
79
|
+
|
|
80
|
+
export const email = () => pg.varchar({ length: 100 })
|
|
81
|
+
|
|
82
|
+
export const full_name = () => pg.varchar({ length: 100 })
|
|
83
|
+
|
|
84
|
+
export { pg };
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
export const table = {
|
|
88
|
+
global: tableWithGlobal,
|
|
89
|
+
tenant: tableWithTenant,
|
|
90
|
+
};
|
|
91
|
+
|
package/src/seed/+.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./seed"
|
package/src/seed/seed.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { DeepKey, DeepValue } from "@kunk/api"
|
|
2
|
+
import * as fp from "lodash/fp"
|
|
3
|
+
import { fakerPT_BR as faker, type Faker } from "@faker-js/faker"
|
|
4
|
+
|
|
5
|
+
export const seed = <T extends { $inferInsert: any }>(block: (faker: Faker) => T['$inferInsert']) => (override?: { [key in DeepKey<T['$inferInsert']>]?: DeepValue<T['$inferInsert'], key> }) => {
|
|
6
|
+
return fp.pipe(
|
|
7
|
+
Object.entries,
|
|
8
|
+
fp.reduce(
|
|
9
|
+
(acc: any, [key, value]) => fp.set(key, value, acc),
|
|
10
|
+
block(faker)
|
|
11
|
+
)
|
|
12
|
+
)(override || {} as { [key in DeepKey<T['$inferInsert']>]?: DeepValue<T['$inferInsert'], key> }) as T['$inferInsert'];
|
|
13
|
+
}
|
package/tsconfig.json
ADDED