From 627d9193b7310a14b54f55f1b5c371c596893976 Mon Sep 17 00:00:00 2001 From: "eric.crowell" Date: Sun, 18 Aug 2024 20:17:56 +0200 Subject: [PATCH] dev: improvement checkpoint --- package.json | 2 +- ...izzle.config.ts => drizzle.config.core.ts} | 4 +- packages/data/drizzle.config.web.ts | 13 ++ packages/data/generate.post.ts | 22 +++ packages/data/package.json | 5 +- packages/data/scripts/{data.sql => core.sql} | 150 ++++++++---------- packages/data/scripts/web.sql | 15 ++ packages/data/src/database.ts | 15 +- packages/data/src/pglite.ts | 13 +- packages/data/src/schema/core.ts | 4 +- packages/data/src/schema/core/dispatch.ts | 4 +- packages/data/src/schema/core/mutate.ts | 4 +- packages/data/src/schema/core/system.ts | 10 +- packages/data/src/seed.ts | 13 +- packages/data/src/seed/system.ts | 15 +- packages/data/test/action.test.ts | 2 +- packages/data/test/entity.test.ts | 2 +- 17 files changed, 166 insertions(+), 127 deletions(-) rename packages/data/{drizzle.config.ts => drizzle.config.core.ts} (69%) create mode 100644 packages/data/drizzle.config.web.ts create mode 100644 packages/data/generate.post.ts rename packages/data/scripts/{data.sql => core.sql} (93%) create mode 100644 packages/data/scripts/web.sql diff --git a/package.json b/package.json index 25b28ee..27738c5 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "private": true, "version": "0.0.0", "description": "do-ob workspace for core components", - "packageManager": "pnpm@9.2.0", + "packageManager": "pnpm@9.7.1+sha512.faf344af2d6ca65c4c5c8c2224ea77a81a5e8859cbc4e06b1511ddce2f0151512431dd19e6aff31f2c6a8f5f2aced9bd2273e1fed7dd4de1868984059d2c4247", "type": "module", "scripts": { "test": "pnpm --filter './packages/*' test", diff --git a/packages/data/drizzle.config.ts b/packages/data/drizzle.config.core.ts similarity index 69% rename from packages/data/drizzle.config.ts rename to packages/data/drizzle.config.core.ts index 195559c..2a84ad4 100644 --- a/packages/data/drizzle.config.ts +++ b/packages/data/drizzle.config.core.ts @@ -5,9 +5,9 @@ dotenv.config(); export default { schema: './src/schema/core.ts', - out: './_migrations', + out: './_migration_core', dialect: 'postgresql', dbCredentials: { - url: process.env.DATABASE_CONNECTION || 'file://./pglite', + url: process.env.DATABASE_CONNECTION || 'memory://./pglite', }, } satisfies Config; diff --git a/packages/data/drizzle.config.web.ts b/packages/data/drizzle.config.web.ts new file mode 100644 index 0000000..055c368 --- /dev/null +++ b/packages/data/drizzle.config.web.ts @@ -0,0 +1,13 @@ +import type { Config } from 'drizzle-kit'; +import * as dotenv from 'dotenv'; + +dotenv.config(); + +export default { + schema: './src/schema/web.ts', + out: './_migration_web', + dialect: 'postgresql', + dbCredentials: { + url: process.env.DATABASE_CONNECTION || 'memory://./pglite', + }, +} satisfies Config; diff --git a/packages/data/generate.post.ts b/packages/data/generate.post.ts new file mode 100644 index 0000000..2a73757 --- /dev/null +++ b/packages/data/generate.post.ts @@ -0,0 +1,22 @@ +import fs from 'node:fs'; +import path from 'node:path'; + +const scriptsPath = path.resolve(import.meta.url, './scripts'); + +// Copy the *.sql file from _migration_core to scripts core.sql. Overwrite if it exists. Ensure that paths are relative to where this script exists. +const coreMigrationPath = path.resolve(import.meta.url, './_migration_core'); +const coreSql = path.resolve(scriptsPath, './core.sql'); +if (fs.existsSync(coreMigrationPath) && fs.existsSync(scriptsPath)) { + fs.copyFileSync(coreMigrationPath, coreSql); + // Remove the _migration_core directory. + fs.rmdirSync(coreMigrationPath, { recursive: true }); +} + +// Copy the *.sql file from _migration_web to scripts web.sql. Overwrite if it exists. Ensure that paths are relative to where this script exists. +const webMigrationPath = path.resolve(import.meta.url, './_migration_web'); +const webSql = path.resolve(scriptsPath, './web.sql'); +if (fs.existsSync(webMigrationPath) && fs.existsSync(scriptsPath)) { + fs.copyFileSync(webMigrationPath, webSql); + // Remove the _migration_web directory. + fs.rmdirSync(webMigrationPath, { recursive: true }); +} diff --git a/packages/data/package.json b/packages/data/package.json index 651d292..d9f8dba 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -27,7 +27,10 @@ "scripts": { "build": "vite build --mode production", "test": "vitest --run", - "generate": "drizzle-kit generate", + "generate": "pnpm generate:core && pnpm generate:web", + "generate:core": "drizzle-kit generate --config ./drizzle.config.core.ts && pnpm generate:post", + "generate:web": "drizzle-kit generate --config ./drizzle.config.web.ts && pnpm generate:post", + "generate:post": "tsx ./generate.post.ts", "push": "drizzle-kit push" }, "dependencies": { diff --git a/packages/data/scripts/data.sql b/packages/data/scripts/core.sql similarity index 93% rename from packages/data/scripts/data.sql rename to packages/data/scripts/core.sql index 2798dd4..8b82cce 100644 --- a/packages/data/scripts/data.sql +++ b/packages/data/scripts/core.sql @@ -4,18 +4,6 @@ EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint -DO $$ BEGIN - CREATE TYPE "public"."mutate_operation" AS ENUM('insert', 'update', 'remove'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint -DO $$ BEGIN - CREATE TYPE "public"."system_type" AS ENUM('boolean', 'number', 'string'); -EXCEPTION - WHEN duplicate_object THEN null; -END $$; ---> statement-breakpoint CREATE TABLE IF NOT EXISTS "action" ( "id" varchar(64) PRIMARY KEY NOT NULL, "definition" jsonb NOT NULL, @@ -34,6 +22,17 @@ CREATE TABLE IF NOT EXISTS "dispatch" ( "message" text ); --> statement-breakpoint +CREATE TABLE IF NOT EXISTS "entity" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "type" varchar(64), + "created" timestamp DEFAULT now() NOT NULL, + "updated" timestamp DEFAULT now() NOT NULL, + "public" boolean DEFAULT false NOT NULL, + "deleted" boolean DEFAULT false NOT NULL, + "owner_id" uuid, + "creator_id" uuid +); +--> statement-breakpoint CREATE TABLE IF NOT EXISTS "entity_credential" ( "id" uuid PRIMARY KEY NOT NULL, "client_id" varchar(64) NOT NULL, @@ -58,6 +57,37 @@ CREATE TABLE IF NOT EXISTS "entity_email" ( CONSTRAINT "entity_email_address_unique" UNIQUE("address") ); --> statement-breakpoint +CREATE TABLE IF NOT EXISTS "entity_file" ( + "id" uuid PRIMARY KEY NOT NULL, + "name" varchar(32) NOT NULL, + "description" varchar(1024), + "mime_type" varchar(64) NOT NULL, + "size" bigint NOT NULL, + "path" varchar(2048), + CONSTRAINT "entity_file_name_unique" UNIQUE("name") +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "entity_file_audio" ( + "id" uuid PRIMARY KEY NOT NULL, + "length" integer NOT NULL, + "volume" smallint NOT NULL +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "entity_file_image" ( + "id" uuid PRIMARY KEY NOT NULL, + "height" smallint NOT NULL, + "width" smallint NOT NULL, + "animated" boolean DEFAULT false NOT NULL, + "frames" smallint +); +--> statement-breakpoint +CREATE TABLE IF NOT EXISTS "entity_file_video" ( + "id" uuid PRIMARY KEY NOT NULL, + "height" smallint NOT NULL, + "width" smallint NOT NULL, + "length" integer NOT NULL +); +--> statement-breakpoint CREATE TABLE IF NOT EXISTS "entity_locale" ( "id" uuid PRIMARY KEY NOT NULL, "code" varchar(8) NOT NULL, @@ -119,59 +149,17 @@ CREATE TABLE IF NOT EXISTS "entity_user" ( CONSTRAINT "entity_user_name_unique" UNIQUE("name") ); --> statement-breakpoint -CREATE TABLE IF NOT EXISTS "entity" ( - "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, - "type" varchar(64), - "created" timestamp DEFAULT now() NOT NULL, - "updated" timestamp DEFAULT now() NOT NULL, - "public" boolean DEFAULT false NOT NULL, - "deleted" boolean DEFAULT false NOT NULL, - "owner_id" uuid, - "creator_id" uuid -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "entity_file_audio" ( - "id" uuid PRIMARY KEY NOT NULL, - "length" integer NOT NULL, - "volume" smallint NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "entity_file_image" ( - "id" uuid PRIMARY KEY NOT NULL, - "height" smallint NOT NULL, - "width" smallint NOT NULL, - "animated" boolean DEFAULT false NOT NULL, - "frames" smallint -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "entity_file_video" ( - "id" uuid PRIMARY KEY NOT NULL, - "height" smallint NOT NULL, - "width" smallint NOT NULL, - "length" integer NOT NULL -); ---> statement-breakpoint -CREATE TABLE IF NOT EXISTS "entity_file" ( - "id" uuid PRIMARY KEY NOT NULL, - "name" varchar(32) NOT NULL, - "description" varchar(1024), - "mime_type" varchar(64) NOT NULL, - "size" bigint NOT NULL, - "path" varchar(2048), - CONSTRAINT "entity_file_name_unique" UNIQUE("name") -); ---> statement-breakpoint CREATE TABLE IF NOT EXISTS "join_assignment" ( "entity_id" uuid NOT NULL, "role_id" uuid NOT NULL, CONSTRAINT "join_assignment_entity_id_role_id_pk" PRIMARY KEY("entity_id","role_id") ); --> statement-breakpoint -CREATE TABLE IF NOT EXISTS "entitle" ( +CREATE TABLE IF NOT EXISTS "join_entitle" ( "entity_id" uuid NOT NULL, "target_id" uuid NOT NULL, "action_id" varchar(64) NOT NULL, - CONSTRAINT "entitle_entity_id_target_id_action_id_pk" PRIMARY KEY("entity_id","target_id","action_id") + CONSTRAINT "join_entitle_entity_id_target_id_action_id_pk" PRIMARY KEY("entity_id","target_id","action_id") ); --> statement-breakpoint CREATE TABLE IF NOT EXISTS "mutate" ( @@ -198,8 +186,8 @@ CREATE TABLE IF NOT EXISTS "storage" ( --> statement-breakpoint CREATE TABLE IF NOT EXISTS "system" ( "id" varchar PRIMARY KEY NOT NULL, - "type" "system_type" NOT NULL, "value" text NOT NULL, + "pattern" varchar(64), "description" text ); --> statement-breakpoint @@ -252,109 +240,109 @@ EXCEPTION END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_locale" ADD CONSTRAINT "entity_locale_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_file" ADD CONSTRAINT "entity_file_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "permit" ADD CONSTRAINT "permit_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_file_audio" ADD CONSTRAINT "entity_file_audio_id_entity_file_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity_file"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "permit" ADD CONSTRAINT "permit_entity_id_entity_id_fk" FOREIGN KEY ("entity_id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_file_image" ADD CONSTRAINT "entity_file_image_id_entity_file_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity_file"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "permit" ADD CONSTRAINT "permit_action_id_action_id_fk" FOREIGN KEY ("action_id") REFERENCES "public"."action"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_file_video" ADD CONSTRAINT "entity_file_video_id_entity_file_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity_file"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_phone" ADD CONSTRAINT "entity_phone_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_locale" ADD CONSTRAINT "entity_locale_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_phone" ADD CONSTRAINT "entity_phone_user_id_entity_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."entity_user"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "permit" ADD CONSTRAINT "permit_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "permit" ADD CONSTRAINT "permit_entity_id_entity_id_fk" FOREIGN KEY ("entity_id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_user_id_entity_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."entity_user"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "permit" ADD CONSTRAINT "permit_action_id_action_id_fk" FOREIGN KEY ("action_id") REFERENCES "public"."action"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_picture_id_entity_file_image_id_fk" FOREIGN KEY ("picture_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "entity_phone" ADD CONSTRAINT "entity_phone_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_cover_id_entity_file_image_id_fk" FOREIGN KEY ("cover_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "entity_phone" ADD CONSTRAINT "entity_phone_user_id_entity_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."entity_user"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_role" ADD CONSTRAINT "entity_role_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_role" ADD CONSTRAINT "entity_role_avatar_id_entity_file_image_id_fk" FOREIGN KEY ("avatar_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_user_id_entity_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."entity_user"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_user" ADD CONSTRAINT "entity_user_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_picture_id_entity_file_image_id_fk" FOREIGN KEY ("picture_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_user" ADD CONSTRAINT "entity_user_avatar_id_entity_file_image_id_fk" FOREIGN KEY ("avatar_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "entity_profile" ADD CONSTRAINT "entity_profile_cover_id_entity_file_image_id_fk" FOREIGN KEY ("cover_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_file_audio" ADD CONSTRAINT "entity_file_audio_id_entity_file_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity_file"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_role" ADD CONSTRAINT "entity_role_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_file_image" ADD CONSTRAINT "entity_file_image_id_entity_file_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity_file"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_role" ADD CONSTRAINT "entity_role_avatar_id_entity_file_image_id_fk" FOREIGN KEY ("avatar_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_file_video" ADD CONSTRAINT "entity_file_video_id_entity_file_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity_file"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_user" ADD CONSTRAINT "entity_user_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entity_file" ADD CONSTRAINT "entity_file_id_entity_id_fk" FOREIGN KEY ("id") REFERENCES "public"."entity"("id") ON DELETE cascade ON UPDATE no action; + ALTER TABLE "entity_user" ADD CONSTRAINT "entity_user_avatar_id_entity_file_image_id_fk" FOREIGN KEY ("avatar_id") REFERENCES "public"."entity_file_image"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; @@ -372,19 +360,19 @@ EXCEPTION END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entitle" ADD CONSTRAINT "entitle_entity_id_entity_id_fk" FOREIGN KEY ("entity_id") REFERENCES "public"."entity"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "join_entitle" ADD CONSTRAINT "join_entitle_entity_id_entity_id_fk" FOREIGN KEY ("entity_id") REFERENCES "public"."entity"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entitle" ADD CONSTRAINT "entitle_target_id_entity_id_fk" FOREIGN KEY ("target_id") REFERENCES "public"."entity"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "join_entitle" ADD CONSTRAINT "join_entitle_target_id_entity_id_fk" FOREIGN KEY ("target_id") REFERENCES "public"."entity"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; --> statement-breakpoint DO $$ BEGIN - ALTER TABLE "entitle" ADD CONSTRAINT "entitle_action_id_action_id_fk" FOREIGN KEY ("action_id") REFERENCES "public"."action"("id") ON DELETE no action ON UPDATE no action; + ALTER TABLE "join_entitle" ADD CONSTRAINT "join_entitle_action_id_action_id_fk" FOREIGN KEY ("action_id") REFERENCES "public"."action"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$; @@ -403,10 +391,10 @@ END $$; --> statement-breakpoint CREATE INDEX IF NOT EXISTS "credential_client_id_idx" ON "entity_credential" USING btree ("client_id");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "email_address_idx" ON "entity_email" USING btree ("address");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "file_name_idx" ON "entity_file" USING btree ("name");--> statement-breakpoint +CREATE INDEX IF NOT EXISTS "file_path_idx" ON "entity_file" USING btree ("path");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "locale_code_idx" ON "entity_locale" USING btree ("code");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "locale_name_idx" ON "entity_locale" USING btree ("name");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "profile_given_name_idx" ON "entity_profile" USING btree ("given_name");--> statement-breakpoint CREATE INDEX IF NOT EXISTS "profile_family_name_idx" ON "entity_profile" USING btree ("family_name");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "user_name_idx" ON "entity_user" USING btree ("name");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "file_name_idx" ON "entity_file" USING btree ("name");--> statement-breakpoint -CREATE INDEX IF NOT EXISTS "file_path_idx" ON "entity_file" USING btree ("path"); \ No newline at end of file +CREATE INDEX IF NOT EXISTS "user_name_idx" ON "entity_user" USING btree ("name"); \ No newline at end of file diff --git a/packages/data/scripts/web.sql b/packages/data/scripts/web.sql new file mode 100644 index 0000000..2ca51a8 --- /dev/null +++ b/packages/data/scripts/web.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS "thing" ( + "id" serial PRIMARY KEY NOT NULL, + "name" varchar(255) NOT NULL, + "description" text, + "url" varchar(2083), + "same_as" varchar(2083), + "identifier" varchar(255), + "image" varchar(2083), + "additional_type" varchar(2083), + "alternate_name" varchar(255), + "disambiguating_description" text, + "main_entity_of_page" varchar(2083), + "date_created" timestamp DEFAULT now(), + "date_modified" timestamp DEFAULT now() +); diff --git a/packages/data/src/database.ts b/packages/data/src/database.ts index d345450..201a6d4 100644 --- a/packages/data/src/database.ts +++ b/packages/data/src/database.ts @@ -20,18 +20,23 @@ declare global { */ globalThis.doob_database_instance = globalThis.doob_database_instance || null; -export function database(connection?: string): Database { +export async function database(connection: string = DATABASE_CONNECTION): Promise { + if (globalThis.doob_database_instance !== null) { return globalThis.doob_database_instance; } if (process.env.NODE_ENV === 'production') { - const sql = postgres(connection ?? DATABASE_CONNECTION); + const sql = postgres(connection); globalThis.doob_database_instance = drizzlePostgres(sql, { schema: schemaCore }); } else { - const sql = new PGlite(connection ?? DATABASE_CONNECTION); - const sqlFile = readFileSync(resolve(import.meta.dirname, '../scripts/data.sql'), 'utf8'); - sql.exec(sqlFile); + const sql = new PGlite(connection); + + if(connection.startsWith('memory://')) { + const coreSql = readFileSync(resolve(import.meta.dirname, '../scripts/core.sql'), 'utf8'); + await sql.exec(coreSql); + } + globalThis.doob_database_instance = drizzlePGlite(sql, { schema: schemaCore }) as unknown as Database; } diff --git a/packages/data/src/pglite.ts b/packages/data/src/pglite.ts index f65d5b4..a2cd8b6 100644 --- a/packages/data/src/pglite.ts +++ b/packages/data/src/pglite.ts @@ -20,10 +20,15 @@ declare global { */ globalThis.doob_pglite_database_instance = globalThis.doob_pglite_database_instance || null; -export function database(connection?: string): Database { - const sql = new PGlite(connection ?? DATABASE_CONNECTION); - const sqlFile = readFileSync(resolve(import.meta.dirname, '../scripts/data.sql'), 'utf8'); - sql.exec(sqlFile); +export function database(connection: string = DATABASE_CONNECTION): Database { + + const sql = new PGlite(connection); + + if(connection.startsWith('memory://')) { + const coreSql = readFileSync(resolve(import.meta.dirname, '../scripts/core.sql'), 'utf8'); + sql.exec(coreSql); + } + globalThis.doob_pglite_database_instance = drizzle(sql, { schema: schemaCore }) as unknown as Database; return globalThis.doob_pglite_database_instance; diff --git a/packages/data/src/schema/core.ts b/packages/data/src/schema/core.ts index d0aa5f2..0d74d67 100644 --- a/packages/data/src/schema/core.ts +++ b/packages/data/src/schema/core.ts @@ -1,5 +1,5 @@ export { table as action } from './core/action.ts'; -export { table as dispatch, relates as dispatchRelates } from './core/dispatch.ts'; +export { table as dispatch, relates as dispatchRelates, status as dispatchStatus } from './core/dispatch.ts'; export { table as entity_credential, relates as entity_credentialRelates } from './core/entity_credential.ts'; export { table as entity_document } from './core/entity_document.ts'; @@ -21,7 +21,7 @@ export { table as entity, relates as entityRelates } from './core/entity.ts'; export { table as join_assignment, relates as join_assignmentRelates } from './core/join_assignment.ts'; export { table as join_entitle, relates as join_entitleRelates } from './core/join_entitle.ts'; -export { table as mutate, relates as mutateRelates } from './core/mutate.ts'; +export { table as mutate, relates as mutateRelates, operation as mutateOperation } from './core/mutate.ts'; export { table as session } from './core/session.ts'; export { table as storage } from './core/storage.ts'; export { table as system } from './core/system.ts'; diff --git a/packages/data/src/schema/core/dispatch.ts b/packages/data/src/schema/core/dispatch.ts index 19898d7..7ae3412 100644 --- a/packages/data/src/schema/core/dispatch.ts +++ b/packages/data/src/schema/core/dispatch.ts @@ -16,7 +16,7 @@ import { table as action } from './action.ts'; /** * The possible data types for a dispatch status. */ -export const dispatchStatus = pgEnum('dispatch_status', [ 'success', 'rejected', 'pending' ]); +export const status = pgEnum('dispatch_status', [ 'success', 'rejected', 'pending' ]); /** * Whenever a subject performs an action, a dispatch is created to apply the action. @@ -29,7 +29,7 @@ export const table = pgTable('dispatch', { created: timestamp('created').defaultNow().notNull(), // When the dispatch was created. initiate: timestamp('initiate').defaultNow().notNull(), // When the dispatch was/will be initiated. payload: jsonb('payload'), // The payload data to send with the action. - status: dispatchStatus('status').notNull().default('pending'), // The status of the dispatch. + status: status('status').notNull().default('pending'), // The status of the dispatch. result: jsonb('result'), // The result data from the action. message: text('message'), // A message to describe the dispatch. }); diff --git a/packages/data/src/schema/core/mutate.ts b/packages/data/src/schema/core/mutate.ts index 5fc34c1..5fea49a 100644 --- a/packages/data/src/schema/core/mutate.ts +++ b/packages/data/src/schema/core/mutate.ts @@ -14,7 +14,7 @@ import { table as entity } from './entity.ts'; /** * Possible operations to a historical change. */ -export const mutateOperation = pgEnum('mutate_operation', [ 'insert', 'update', 'remove' ]); +export const operation = pgEnum('mutate_operation', [ 'insert', 'update', 'remove' ]); /** * Defines a log of CUD changes to records on other tables in the database. @@ -25,7 +25,7 @@ export const table = pgTable('mutate', { $dispatch: uuid('dispatch_id').notNull().references(() => dispatch.$id), // The dispatch ID that was responsible for the mutation. $entity: uuid('record_id').notNull().references(() => entity.$id), // The record ID that was changed. table: varchar('table').notNull(), // The table that was changed. - operation: mutateOperation('operation').notNull(), // The operation performed on the record. + operation: operation('operation').notNull(), // The operation performed on the record. occurred: timestamp('occurred').defaultNow().notNull(), // When the operation was performed. mutation: jsonb('mutation').notNull(), // The mutation that was performed. }); diff --git a/packages/data/src/schema/core/system.ts b/packages/data/src/schema/core/system.ts index 71e58d4..8ba6e66 100644 --- a/packages/data/src/schema/core/system.ts +++ b/packages/data/src/schema/core/system.ts @@ -1,22 +1,16 @@ import { - pgEnum, pgTable, text, varchar, } from 'drizzle-orm/pg-core'; -/** - * The possible data types for a system settings. - */ -export const systemType = pgEnum('system_type', [ 'boolean', 'number', 'string' ]); - /** * The system table serves as a flat registry for global configuration settings. */ export const table = pgTable('system', { $id: varchar('id').primaryKey(), // Name of the system setting. - type: systemType('type').notNull(), // Type of the system setting (boolean, number, string). - value: text('value').notNull(), // Value of the system setting. The string value will be converted to the type. + value: text('value').notNull(), // String value of the system setting. + pattern: varchar('pattern', { length: 64 }), // Optional pattern to validate the system setting value. description: text('description'), // Optional description of the system setting. }); diff --git a/packages/data/src/seed.ts b/packages/data/src/seed.ts index fc96922..3af6fcf 100644 --- a/packages/data/src/seed.ts +++ b/packages/data/src/seed.ts @@ -29,11 +29,10 @@ export const modules = { /** * Seeds a database with initial data necessary to run an application. */ -export async function seed(db?: Database) { - if(!db) { - db = await database(); - } - const systemValue = await db.query.system.findFirst({ +export async function seed(db?: Database | Promise): Promise { + const dbResolved = db ? await db : await database(); + + const systemValue = await dbResolved.query.system.findFirst({ where: (table, { eq }) => eq(table.$id, 'SEEDED'), }); @@ -41,7 +40,7 @@ export async function seed(db?: Database) { throw new Error('Database already seeded.'); } - await db.transaction(async (tx) => { + await dbResolved.transaction(async (tx) => { /** * The subject id to use for the seed operation. @@ -113,5 +112,5 @@ export async function seed(db?: Database) { }; }); - return db; + return dbResolved; } diff --git a/packages/data/src/seed/system.ts b/packages/data/src/seed/system.ts index 5665b17..993402c 100644 --- a/packages/data/src/seed/system.ts +++ b/packages/data/src/seed/system.ts @@ -3,55 +3,50 @@ import type { SchemaCore } from '@do-ob/data/schema'; export const records: Array = [ { $id: 'NAME', - type: 'string', value: 'My Application', description: 'The name of this web application.', }, { $id: 'DESCRIPTION', - type: 'string', value: 'A web application for the world.', description: 'A description of The web application purpose or mission.', }, { $id: 'VERSION', - type: 'string', value: '1.0.0', + pattern: '^\\d+\\.\\d+\\.\\d+$', description: 'The current version of this web application deployment.', }, { $id: 'SYSTEM_SUBJECT', - type: 'string', value: '00000000-0000-0000-0000-000000000000', + pattern: '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$', description: 'The subject identifier for the system to carry out unrestricted operations.', }, { $id: 'SEEDED', - type: 'boolean', value: 'true', + pattern: '^(true|false)$', description: 'Flag to indicate if the system has been seeded with initial data.', }, { $id: 'REGISTRATION_OPEN', - type: 'boolean', value: 'false', - description: 'Convenient flag to enable clients to register new user accounts if assigned with the registration actions.', + pattern: '^(true|false)$', + description: 'Convenient flag to enable or disable clients to register new user accounts through the register action.', }, { $id: 'ADMINISTRATOR_ROLE_NAME', - type: 'string', value: 'Administrator', description: 'The role assigned to administrators. This allows users with this role to perform any actions regardless of permissions.', }, { $id: 'ANONYMOUS_ROLE_NAME', - type: 'string', value: 'Anonymous', description: 'The role assigned to unauthenticated users. This allows anonymous clients to perform actions that are assigned to this role; like registering a new account.', }, { $id: 'INITIAL_ROLE_NAME', - type: 'string', value: 'Base', description: 'The role assigned to when a user account is created. This allows new users to perform actions that are assigned to this role; like updating their own profile.', } diff --git a/packages/data/test/action.test.ts b/packages/data/test/action.test.ts index 19d255c..27dd1c0 100644 --- a/packages/data/test/action.test.ts +++ b/packages/data/test/action.test.ts @@ -10,7 +10,7 @@ import { modules, seed } from '@do-ob/data/seed'; let db: Database; beforeAll(async () => { - db = database(); + db = await database(); await seed(); }); diff --git a/packages/data/test/entity.test.ts b/packages/data/test/entity.test.ts index 2a2b02a..9b6cdbe 100644 --- a/packages/data/test/entity.test.ts +++ b/packages/data/test/entity.test.ts @@ -10,7 +10,7 @@ let $creator: string; describe.sequential('create entity with owner', () => { beforeAll(async () => { - db = database(); + db = await database(); // Ensure all rows in the entity table are deleted. await db.delete(schemaCore.entity); });