feat: init database package

This commit is contained in:
Justin Lin committed 2026-10-11 18:21:20 +11:00
1 parent 4cf79a164c
commit ba5026fa56
14 files changed
+2375 -12

No files matched your search

+1014 -12
View File
File diff suppressed because it is too large. Load diff
+5
View File
@@ -0,0 +1,5 @@
# Copy this file to `.env` and replace the placeholder with your real connection string.
# Connection string for PostgreSQL.
# Requires PostgreSQL >= 15.
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
+6
View File
@@ -0,0 +1,6 @@
src/prisma/contract.json linguist-generated
src/prisma/contract.d.ts linguist-generated
src/prisma/ops.json linguist-generated
src/prisma/migration.json linguist-generated
migrations/snapshots/**/contract.json linguist-generated
migrations/snapshots/**/contract.d.ts linguist-generated
+36
View File
@@ -0,0 +1,36 @@
# dependencies (bun install)
node_modules
# output
out
dist
*.tgz
# code coverage
coverage
*.lcov
# logs
logs
_.log
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# caches
.eslintcache
.cache
*.tsbuildinfo
# IntelliJ based IDEs
.idea
# Finder (MacOS) folder config
.DS_Store
node_modules/
dist/
+15
View File
@@ -0,0 +1,15 @@
# @odyssey/database
To install dependencies:
```bash
bun install
```
To run:
```bash
bun run index.ts
```
This project was created using `bun init` in bun v1.3.14. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
+21
View File
@@ -0,0 +1,21 @@
{
"name": "@odyssey/database",
"module": "src/index.ts",
"type": "module",
"devDependencies": {
"@prisma/cli-engine": "0.7.0",
"@types/bun": "latest",
"@types/node": "^26.6.5",
"prisma": "^8.0.0-rc.22"
},
"peerDependencies": {
"typescript": "^5"
},
"scripts": {
"contract:emit": "prisma contract emit"
},
"dependencies": {
"@prisma/orm-postgres": "^8.0.0-rc.17",
"dotenv": "^18.0.7"
}
}
+106
View File
@@ -0,0 +1,106 @@
# Welcome to Prisma ORM!
Prisma ORM lets you query your database in simple, easy-to-read TypeScript. Define what your data looks like, and Prisma ORM gives you a fully typed client — with autocomplete for every table, column, and relation.
This project is set up for PostgreSQL. Prisma ORM also supports other databases.
## Requirements
- **PostgreSQL 15 or newer.** Older servers are not supported. Run `SELECT version()` against your server to verify.
- The CLI never connects to your database without explicit consent. Pass `--probe-db` to `bun prisma orm init` if you want `init` to verify the server version itself.
## Your data contract
Your data contract is the heart of your application. It lives at [`src/prisma/contract.prisma`](src/prisma/contract.prisma) and describes your models:
```prisma
model User {
id Int @id @default(autoincrement())
email String @unique
username String?
name String?
}
```
Every model you define in your contract can be queried from your app. Your editor will autocomplete the query methods and show you what type each model field is:
```typescript
import { db } from './src/prisma/db';
const user = await db.orm.public.User
.where({ email: 'alice@example.com' })
.first();
// Your editor will show the type of user as
// { id: number; email: string; username: string | null; name: string | null; createdAt: Date; posts: Post[] } | null
```
Your contract has two companion files in the same directory:
- **`contract.json`** — this tells your application what models exist, just like `package-lock.json` tells your package manager what dependencies your project has
- **`contract.d.ts`** — this powers autocomplete and type checking in your editor
Commit both files to git. When you change your contract, run `bun prisma contract emit` to update them.
If you use a framework like Next.js or Vite, the Prisma ORM plugin will do this for you automatically.
## Configuration
[`prisma.config.ts`](prisma.config.ts) tells the CLI where your contract lives and how to connect to your database. It loads environment variables from `.env` automatically:
```typescript
import 'dotenv/config';
import { definePrismaConfig } from '@prisma/cli-engine';
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
export default definePrismaConfig({
orm: ormConfig({
contract: './src/prisma/contract.prisma',
db: {
connection: process.env['DATABASE_URL']!,
},
}),
});
```
Notice the `DATABASE_URL` above? It's defined in your [`.env`](./.env) file:
```env
DATABASE_URL="postgresql://user:password@localhost:5432/mydb"
```
You can customize how your environment variables are loaded by changing or removing the `import 'dotenv/config'` line.
## Quick reference
### Commands
```bash
bun prisma contract emit # Update contract.json and contract.d.ts
bun prisma db init # Create tables in the database
bun prisma migration status # Show migration status
```
### Files
| File | Purpose |
|---|---|
| [`src/prisma/contract.prisma`](src/prisma/contract.prisma) | Your data contract — define your models here |
| [`prisma.config.ts`](prisma.config.ts) | CLI configuration |
| [`src/prisma/db.ts`](src/prisma/db.ts) | Database client — `import { db } from './src/prisma/db'` |
| `src/prisma/contract.json` | Compiled contract (generated) |
| `src/prisma/contract.d.ts` | Contract types (generated) |
### Workflow
1. Edit [`src/prisma/contract.prisma`](src/prisma/contract.prisma) to add or change models.
2. Run `bun prisma contract emit` to regenerate the contract.
3. Query your models — your IDE will autocomplete everything.
## Monorepo notes (pnpm workspaces)
If this project lives inside a pnpm workspace, a few things are worth knowing:
- **Catalogs.** When the workspace's `pnpm-workspace.yaml` defines a `catalogs` entry for `prisma` or `@prisma/orm-postgres`, pnpm uses the catalog version everywhere — `init` does too. If you wanted the published `latest` instead, update or remove the catalog entry, then re-run `pnpm install`.
- **`pnpm dlx`.** `pnpm dlx prisma@latest orm init …` works in any directory. Inside a workspace, pnpm still resolves dependencies through the workspace's catalog/overrides rather than the registry; expect the installed Prisma ORM packages to reflect the workspace's catalog rather than `latest`.
- **`pnpm` → `npm` fallback.** If `pnpm` ever fails to install Prisma ORM with a `workspace:*` or `catalog:` resolution error (a leak in a published artefact), `init` falls back to `npm install` and surfaces a warning. Once the offending package republishes a clean version you can switch back with `pnpm install`.
+12
View File
@@ -0,0 +1,12 @@
import 'dotenv/config';
import { definePrismaConfig } from '@prisma/cli-engine';
import { defineConfig as ormConfig } from '@prisma/orm-postgres/config';
export default definePrismaConfig({
orm: ormConfig({
contract: "./src/prisma/contract.prisma",
db: {
connection: process.env['DATABASE_URL']!,
},
}),
});
+1
View File
@@ -0,0 +1 @@
console.log('Hello via Bun!')
+677
View File
@@ -0,0 +1,677 @@
// ⚠️ GENERATED FILE - DO NOT EDIT
// This file is automatically generated by 'prisma contract emit'.
// To regenerate, run: prisma contract emit
import type { CodecTypes as PgTypes } from '@prisma/orm-postgres/target/codec-types';
import type { QueryOperationTypes as PgTargetQueryOps } from '@prisma/orm-postgres/target/operation-types';
import type {
ContractWithTypeMaps,
RelationKeys,
TypeMaps as TypeMapsType,
} from '@prisma/orm-postgres/family-contract/types';
import type {
Contract as ContractType,
ExecutionHashBase,
NamespaceId,
ProfileHashBase,
StorageHashBase,
} from '@prisma/orm-postgres/contract/types';
export type StorageHash =
StorageHashBase<'a51b4833fdaffe730572670ccc27dd89af7e0726ab3de7f428d21702d687cc15'>;
export type ExecutionHash =
ExecutionHashBase<'4e88564095b5e0ba7904ffdba3c563ef916d87688c8c483c67055f71a9194f0f'>;
export type ProfileHash =
ProfileHashBase<'3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2'>;
export type CodecTypes = PgTypes;
export type LaneCodecTypes = CodecTypes;
export type QueryOperationTypes = PgTargetQueryOps<CodecTypes>;
export type AggregateTypes = {
readonly avg: {
readonly byCodec: {
readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/float4@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/int2@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/int4@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/int8@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/int8number@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/unboundedint@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'sql/int@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
};
};
readonly avgDecimal: {
readonly byCodec: {
readonly 'pg/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/int2@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/int4@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/int8@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/int8number@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/unboundedint@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'sql/int@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
};
};
readonly count: {
readonly byCodec: {};
readonly withoutInput: { readonly output: 'pg/int8number@1'; readonly nullable: false };
readonly anyInput: { readonly output: 'pg/int8number@1'; readonly nullable: false };
};
readonly countBigInt: {
readonly byCodec: {};
readonly withoutInput: { readonly output: 'pg/int8@1'; readonly nullable: false };
readonly anyInput: { readonly output: 'pg/int8@1'; readonly nullable: false };
};
readonly max: {
readonly byCodec: {
readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true };
readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true };
readonly 'pg/date-temporal@1': {
readonly output: 'pg/date-temporal@1';
readonly nullable: true;
};
readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true };
readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true };
readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true };
readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true };
readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true };
readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true };
readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true };
readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true };
readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true };
readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true };
readonly 'pg/time-temporal@1': {
readonly output: 'pg/time-temporal@1';
readonly nullable: true;
};
readonly 'pg/timestamp-string@1': {
readonly output: 'pg/timestamp-string@1';
readonly nullable: true;
};
readonly 'pg/timestamp-temporal@1': {
readonly output: 'pg/timestamp-temporal@1';
readonly nullable: true;
};
readonly 'pg/timestamptz-date@1': {
readonly output: 'pg/timestamptz-date@1';
readonly nullable: true;
};
readonly 'pg/timestamptz-string@1': {
readonly output: 'pg/timestamptz-string@1';
readonly nullable: true;
};
readonly 'pg/timestamptz-temporal@1': {
readonly output: 'pg/timestamptz-temporal@1';
readonly nullable: true;
};
readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true };
readonly 'pg/unboundedint@1': {
readonly output: 'pg/unboundedint@1';
readonly nullable: true;
};
readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true };
readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true };
readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true };
readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true };
readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true };
readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true };
};
};
readonly min: {
readonly byCodec: {
readonly 'pg/char@1': { readonly output: 'pg/char@1'; readonly nullable: true };
readonly 'pg/date-string@1': { readonly output: 'pg/date-string@1'; readonly nullable: true };
readonly 'pg/date-temporal@1': {
readonly output: 'pg/date-temporal@1';
readonly nullable: true;
};
readonly 'pg/enum@1': { readonly output: 'pg/enum@1'; readonly nullable: true };
readonly 'pg/float@1': { readonly output: 'pg/float@1'; readonly nullable: true };
readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true };
readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/inet@1': { readonly output: 'pg/inet@1'; readonly nullable: true };
readonly 'pg/int@1': { readonly output: 'pg/int@1'; readonly nullable: true };
readonly 'pg/int2@1': { readonly output: 'pg/int2@1'; readonly nullable: true };
readonly 'pg/int4@1': { readonly output: 'pg/int4@1'; readonly nullable: true };
readonly 'pg/int8@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/text-array@1': { readonly output: 'pg/text-array@1'; readonly nullable: true };
readonly 'pg/text@1': { readonly output: 'pg/text@1'; readonly nullable: true };
readonly 'pg/time-string@1': { readonly output: 'pg/time-string@1'; readonly nullable: true };
readonly 'pg/time-temporal@1': {
readonly output: 'pg/time-temporal@1';
readonly nullable: true;
};
readonly 'pg/timestamp-string@1': {
readonly output: 'pg/timestamp-string@1';
readonly nullable: true;
};
readonly 'pg/timestamp-temporal@1': {
readonly output: 'pg/timestamp-temporal@1';
readonly nullable: true;
};
readonly 'pg/timestamptz-date@1': {
readonly output: 'pg/timestamptz-date@1';
readonly nullable: true;
};
readonly 'pg/timestamptz-string@1': {
readonly output: 'pg/timestamptz-string@1';
readonly nullable: true;
};
readonly 'pg/timestamptz-temporal@1': {
readonly output: 'pg/timestamptz-temporal@1';
readonly nullable: true;
};
readonly 'pg/timetz@1': { readonly output: 'pg/timetz@1'; readonly nullable: true };
readonly 'pg/unboundedint@1': {
readonly output: 'pg/unboundedint@1';
readonly nullable: true;
};
readonly 'pg/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true };
readonly 'sql/char@1': { readonly output: 'sql/char@1'; readonly nullable: true };
readonly 'sql/float@1': { readonly output: 'sql/float@1'; readonly nullable: true };
readonly 'sql/int@1': { readonly output: 'sql/int@1'; readonly nullable: true };
readonly 'sql/text@1': { readonly output: 'sql/text@1'; readonly nullable: true };
readonly 'sql/varchar@1': { readonly output: 'pg/text@1'; readonly nullable: true };
};
};
readonly sum: {
readonly byCodec: {
readonly 'pg/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/float4@1': { readonly output: 'pg/float4@1'; readonly nullable: true };
readonly 'pg/float8@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'pg/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/int2@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/int4@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/int8@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/int8number@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
readonly 'pg/interval@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/numeric@1': { readonly output: 'pg/numeric@1'; readonly nullable: true };
readonly 'pg/time-string@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/time-temporal@1': { readonly output: 'pg/interval@1'; readonly nullable: true };
readonly 'pg/unboundedint@1': {
readonly output: 'pg/unboundedint@1';
readonly nullable: true;
};
readonly 'sql/float@1': { readonly output: 'pg/float8@1'; readonly nullable: true };
readonly 'sql/int@1': { readonly output: 'pg/int8number@1'; readonly nullable: true };
};
};
readonly sumBigInt: {
readonly byCodec: {
readonly 'pg/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
readonly 'pg/int2@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
readonly 'pg/int4@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
readonly 'pg/int8@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true };
readonly 'pg/int8number@1': { readonly output: 'pg/unboundedint@1'; readonly nullable: true };
readonly 'pg/unboundedint@1': {
readonly output: 'pg/unboundedint@1';
readonly nullable: true;
};
readonly 'sql/int@1': { readonly output: 'pg/int8@1'; readonly nullable: true };
};
};
};
type DefaultLiteralValue<CodecId extends string, Encoded> = CodecId extends keyof CodecTypes
? Encoded extends CodecTypes[CodecId]['json']
? Encoded
: CodecTypes[CodecId]['json']
: Encoded;
export type FieldOutputTypes = {
readonly public: {
readonly Post: {
readonly authorId: CodecTypes['pg/int4@1']['output'];
readonly content: CodecTypes['pg/text@1']['output'] | null;
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['output'];
readonly id: CodecTypes['pg/int4@1']['output'];
readonly title: CodecTypes['pg/text@1']['output'];
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['output'];
};
readonly User: {
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['output'];
readonly email: CodecTypes['pg/text@1']['output'];
readonly id: CodecTypes['pg/int4@1']['output'];
readonly name: CodecTypes['pg/text@1']['output'] | null;
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['output'];
readonly username: CodecTypes['pg/text@1']['output'] | null;
};
};
};
export type FieldInputTypes = {
readonly public: {
readonly Post: {
readonly authorId: CodecTypes['pg/int4@1']['input'];
readonly content: CodecTypes['pg/text@1']['input'] | null;
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['input'];
readonly id: CodecTypes['pg/int4@1']['input'];
readonly title: CodecTypes['pg/text@1']['input'];
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['input'];
};
readonly User: {
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['input'];
readonly email: CodecTypes['pg/text@1']['input'];
readonly id: CodecTypes['pg/int4@1']['input'];
readonly name: CodecTypes['pg/text@1']['input'] | null;
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['input'];
readonly username: CodecTypes['pg/text@1']['input'] | null;
};
};
};
export type StorageColumnTypes = {
readonly public: {
readonly Post: {
readonly authorId: CodecTypes['pg/int4@1']['output'];
readonly content: CodecTypes['pg/text@1']['output'] | null;
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['output'];
readonly id: CodecTypes['pg/int4@1']['output'];
readonly title: CodecTypes['pg/text@1']['output'];
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['output'];
};
readonly User: {
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['output'];
readonly email: CodecTypes['pg/text@1']['output'];
readonly id: CodecTypes['pg/int4@1']['output'];
readonly name: CodecTypes['pg/text@1']['output'] | null;
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['output'];
readonly username: CodecTypes['pg/text@1']['output'] | null;
};
};
};
export type StorageColumnInputTypes = {
readonly public: {
readonly Post: {
readonly authorId: CodecTypes['pg/int4@1']['input'];
readonly content: CodecTypes['pg/text@1']['input'] | null;
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['input'];
readonly id: CodecTypes['pg/int4@1']['input'];
readonly title: CodecTypes['pg/text@1']['input'];
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['input'];
};
readonly User: {
readonly createdAt: CodecTypes['pg/timestamptz-string@1']['input'];
readonly email: CodecTypes['pg/text@1']['input'];
readonly id: CodecTypes['pg/int4@1']['input'];
readonly name: CodecTypes['pg/text@1']['input'] | null;
readonly updatedAt: CodecTypes['pg/timestamptz-string@1']['input'];
readonly username: CodecTypes['pg/text@1']['input'] | null;
};
};
};
export namespace Models {
export type public_Post = {
authorId: CodecTypes['pg/int4@1']['output'];
content: CodecTypes['pg/text@1']['output'] | null;
createdAt: CodecTypes['pg/timestamptz-string@1']['output'];
id: CodecTypes['pg/int4@1']['output'];
title: CodecTypes['pg/text@1']['output'];
updatedAt: CodecTypes['pg/timestamptz-string@1']['output'];
author: public_User;
readonly [RelationKeys]?: 'author';
};
export type public_User = {
createdAt: CodecTypes['pg/timestamptz-string@1']['output'];
email: CodecTypes['pg/text@1']['output'];
id: CodecTypes['pg/int4@1']['output'];
name: CodecTypes['pg/text@1']['output'] | null;
updatedAt: CodecTypes['pg/timestamptz-string@1']['output'];
username: CodecTypes['pg/text@1']['output'] | null;
posts: public_Post[];
readonly [RelationKeys]?: 'posts';
};
}
export declare const models: {
public: {
Post: Models.public_Post;
User: Models.public_User;
};
};
export type TypeMaps = TypeMapsType<
CodecTypes,
QueryOperationTypes,
FieldOutputTypes,
FieldInputTypes,
StorageColumnTypes,
StorageColumnInputTypes,
AggregateTypes
>;
type ContractBase = Omit<
ContractType<{
readonly namespaces: {
readonly public: {
readonly id: 'public';
readonly kind: 'postgres-schema';
readonly entries: {
readonly table: {
readonly Post: {
columns: {
readonly authorId: {
readonly dataType: 'pg/int4';
readonly codecId: 'pg/int4@1';
readonly nullable: false;
readonly many: false;
};
readonly content: {
readonly dataType: 'pg/text';
readonly codecId: 'pg/text@1';
readonly nullable: true;
readonly many: false;
};
readonly createdAt: {
readonly dataType: 'pg/timestamptz';
readonly codecId: 'pg/timestamptz-string@1';
readonly nullable: false;
readonly default: { readonly kind: 'function'; readonly expression: 'now()' };
readonly many: false;
};
readonly id: {
readonly dataType: 'pg/int4';
readonly codecId: 'pg/int4@1';
readonly nullable: false;
readonly default: {
readonly kind: 'function';
readonly expression: 'autoincrement()';
};
readonly many: false;
};
readonly title: {
readonly dataType: 'pg/text';
readonly codecId: 'pg/text@1';
readonly nullable: false;
readonly many: false;
};
readonly updatedAt: {
readonly dataType: 'pg/timestamptz';
readonly codecId: 'pg/timestamptz-string@1';
readonly nullable: false;
readonly many: false;
};
};
primaryKey: { readonly columns: readonly ['id'] };
uniques: readonly [];
indexes: readonly [
{
readonly name: 'Post_authorId_idx_e47547ed';
readonly prefix: 'Post_authorId_idx';
readonly columns: readonly ['authorId'];
readonly unique: false;
},
];
foreignKeys: readonly [
{
readonly source: {
readonly namespaceId: 'public' & NamespaceId;
readonly tableName: 'Post';
readonly columns: readonly ['authorId'];
};
readonly target: {
readonly namespaceId: 'public' & NamespaceId;
readonly tableName: 'User';
readonly columns: readonly ['id'];
};
},
];
};
readonly User: {
columns: {
readonly createdAt: {
readonly dataType: 'pg/timestamptz';
readonly codecId: 'pg/timestamptz-string@1';
readonly nullable: false;
readonly default: { readonly kind: 'function'; readonly expression: 'now()' };
readonly many: false;
};
readonly email: {
readonly dataType: 'pg/text';
readonly codecId: 'pg/text@1';
readonly nullable: false;
readonly many: false;
};
readonly id: {
readonly dataType: 'pg/int4';
readonly codecId: 'pg/int4@1';
readonly nullable: false;
readonly default: {
readonly kind: 'function';
readonly expression: 'autoincrement()';
};
readonly many: false;
};
readonly name: {
readonly dataType: 'pg/text';
readonly codecId: 'pg/text@1';
readonly nullable: true;
readonly many: false;
};
readonly updatedAt: {
readonly dataType: 'pg/timestamptz';
readonly codecId: 'pg/timestamptz-string@1';
readonly nullable: false;
readonly many: false;
};
readonly username: {
readonly dataType: 'pg/text';
readonly codecId: 'pg/text@1';
readonly nullable: true;
readonly many: false;
};
};
primaryKey: { readonly columns: readonly ['id'] };
uniques: readonly [{ readonly columns: readonly ['email'] }];
indexes: readonly [];
foreignKeys: readonly [];
};
};
};
};
};
readonly storageHash: StorageHash;
}>,
'roots' | 'domain'
> & {
readonly target: 'postgres';
readonly targetFamily: 'sql';
readonly roots: {
readonly Post: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' };
readonly User: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' };
};
readonly domain: {
readonly namespaces: {
readonly public: {
readonly models: {
readonly Post: {
readonly fields: {
readonly authorId: {
readonly nullable: false;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' };
};
readonly content: {
readonly nullable: true;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' };
};
readonly createdAt: {
readonly nullable: false;
readonly type: {
readonly kind: 'scalar';
readonly codecId: 'pg/timestamptz-string@1';
};
};
readonly id: {
readonly nullable: false;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' };
};
readonly title: {
readonly nullable: false;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' };
};
readonly updatedAt: {
readonly nullable: false;
readonly type: {
readonly kind: 'scalar';
readonly codecId: 'pg/timestamptz-string@1';
};
};
};
readonly relations: {
readonly author: {
readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'User' };
readonly cardinality: 'N:1';
readonly nullable: false;
readonly on: {
readonly localFields: readonly ['authorId'];
readonly targetFields: readonly ['id'];
};
};
};
readonly storage: {
readonly table: 'Post';
readonly namespaceId: 'public';
readonly fields: {
readonly authorId: { readonly column: 'authorId' };
readonly content: { readonly column: 'content' };
readonly createdAt: { readonly column: 'createdAt' };
readonly id: { readonly column: 'id' };
readonly title: { readonly column: 'title' };
readonly updatedAt: { readonly column: 'updatedAt' };
};
};
};
readonly User: {
readonly fields: {
readonly createdAt: {
readonly nullable: false;
readonly type: {
readonly kind: 'scalar';
readonly codecId: 'pg/timestamptz-string@1';
};
};
readonly email: {
readonly nullable: false;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' };
};
readonly id: {
readonly nullable: false;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/int4@1' };
};
readonly name: {
readonly nullable: true;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' };
};
readonly updatedAt: {
readonly nullable: false;
readonly type: {
readonly kind: 'scalar';
readonly codecId: 'pg/timestamptz-string@1';
};
};
readonly username: {
readonly nullable: true;
readonly type: { readonly kind: 'scalar'; readonly codecId: 'pg/text@1' };
};
};
readonly relations: {
readonly posts: {
readonly to: { readonly namespace: 'public' & NamespaceId; readonly model: 'Post' };
readonly cardinality: '1:N';
readonly on: {
readonly localFields: readonly ['id'];
readonly targetFields: readonly ['authorId'];
};
};
};
readonly storage: {
readonly table: 'User';
readonly namespaceId: 'public';
readonly fields: {
readonly createdAt: { readonly column: 'createdAt' };
readonly email: { readonly column: 'email' };
readonly id: { readonly column: 'id' };
readonly name: { readonly column: 'name' };
readonly updatedAt: { readonly column: 'updatedAt' };
readonly username: { readonly column: 'username' };
};
};
};
};
};
};
};
readonly capabilities: {
readonly postgres: {
readonly distinctOn: true;
readonly forKeyShare: true;
readonly forNoKeyUpdate: true;
readonly jsonAgg: true;
readonly lateral: true;
readonly limit: true;
readonly orderBy: true;
readonly returning: true;
};
readonly sql: {
readonly checkConstraint: true;
readonly defaultInInsert: true;
readonly enums: true;
readonly forShare: true;
readonly forUpdate: true;
readonly insertOnConflictSkip: true;
readonly insertOnConflictWithoutTarget: true;
readonly lateral: true;
readonly lockNowait: true;
readonly lockOf: true;
readonly lockSkipLocked: true;
readonly returning: true;
readonly scalarList: true;
};
};
readonly extensions: {};
readonly execution: {
readonly executionHash: ExecutionHash;
readonly mutations: {
readonly defaults: readonly [
{
readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' };
readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' };
readonly ref: {
readonly entry: 'Post';
readonly field: 'updatedAt';
readonly namespace: 'public';
};
},
{
readonly onCreate: { readonly id: 'timestampNow'; readonly kind: 'generator' };
readonly onUpdate: { readonly id: 'timestampNow'; readonly kind: 'generator' };
readonly ref: {
readonly entry: 'User';
readonly field: 'updatedAt';
readonly namespace: 'public';
};
},
];
};
};
readonly meta: {};
readonly profileHash: ProfileHash;
};
export type Contract = ContractWithTypeMaps<ContractBase, TypeMaps>;
export type Namespaces = Contract['storage']['namespaces'];
+418
View File
@@ -0,0 +1,418 @@
{
"schemaVersion": "1",
"targetFamily": "sql",
"target": "postgres",
"profileHash": "3916f444a8a17ad749191acf9e08dad97d1a327b88c2f1d45d12f240296aa8b2",
"roots": {
"Post": {
"model": "Post",
"namespace": "public"
},
"User": {
"model": "User",
"namespace": "public"
}
},
"domain": {
"namespaces": {
"public": {
"models": {
"Post": {
"fields": {
"authorId": {
"nullable": false,
"type": {
"codecId": "pg/int4@1",
"kind": "scalar"
}
},
"content": {
"nullable": true,
"type": {
"codecId": "pg/text@1",
"kind": "scalar"
}
},
"createdAt": {
"nullable": false,
"type": {
"codecId": "pg/timestamptz-string@1",
"kind": "scalar"
}
},
"id": {
"nullable": false,
"type": {
"codecId": "pg/int4@1",
"kind": "scalar"
}
},
"title": {
"nullable": false,
"type": {
"codecId": "pg/text@1",
"kind": "scalar"
}
},
"updatedAt": {
"nullable": false,
"type": {
"codecId": "pg/timestamptz-string@1",
"kind": "scalar"
}
}
},
"relations": {
"author": {
"cardinality": "N:1",
"nullable": false,
"on": {
"localFields": [
"authorId"
],
"targetFields": [
"id"
]
},
"to": {
"model": "User",
"namespace": "public"
}
}
},
"storage": {
"fields": {
"authorId": {
"column": "authorId"
},
"content": {
"column": "content"
},
"createdAt": {
"column": "createdAt"
},
"id": {
"column": "id"
},
"title": {
"column": "title"
},
"updatedAt": {
"column": "updatedAt"
}
},
"namespaceId": "public",
"table": "Post"
}
},
"User": {
"fields": {
"createdAt": {
"nullable": false,
"type": {
"codecId": "pg/timestamptz-string@1",
"kind": "scalar"
}
},
"email": {
"nullable": false,
"type": {
"codecId": "pg/text@1",
"kind": "scalar"
}
},
"id": {
"nullable": false,
"type": {
"codecId": "pg/int4@1",
"kind": "scalar"
}
},
"name": {
"nullable": true,
"type": {
"codecId": "pg/text@1",
"kind": "scalar"
}
},
"updatedAt": {
"nullable": false,
"type": {
"codecId": "pg/timestamptz-string@1",
"kind": "scalar"
}
},
"username": {
"nullable": true,
"type": {
"codecId": "pg/text@1",
"kind": "scalar"
}
}
},
"relations": {
"posts": {
"cardinality": "1:N",
"on": {
"localFields": [
"id"
],
"targetFields": [
"authorId"
]
},
"to": {
"model": "Post",
"namespace": "public"
}
}
},
"storage": {
"fields": {
"createdAt": {
"column": "createdAt"
},
"email": {
"column": "email"
},
"id": {
"column": "id"
},
"name": {
"column": "name"
},
"updatedAt": {
"column": "updatedAt"
},
"username": {
"column": "username"
}
},
"namespaceId": "public",
"table": "User"
}
}
}
}
}
},
"storage": {
"namespaces": {
"public": {
"entries": {
"table": {
"Post": {
"columns": {
"authorId": {
"codecId": "pg/int4@1",
"dataType": "pg/int4",
"nullable": false
},
"content": {
"codecId": "pg/text@1",
"dataType": "pg/text",
"nullable": true
},
"createdAt": {
"codecId": "pg/timestamptz-string@1",
"dataType": "pg/timestamptz",
"default": {
"expression": "now()",
"kind": "function"
},
"nullable": false
},
"id": {
"codecId": "pg/int4@1",
"dataType": "pg/int4",
"default": {
"expression": "autoincrement()",
"kind": "function"
},
"nullable": false
},
"title": {
"codecId": "pg/text@1",
"dataType": "pg/text",
"nullable": false
},
"updatedAt": {
"codecId": "pg/timestamptz-string@1",
"dataType": "pg/timestamptz",
"nullable": false
}
},
"foreignKeys": [
{
"index": {
"name": "Post_authorId_idx_e47547ed"
},
"source": {
"columns": [
"authorId"
],
"namespaceId": "public",
"tableName": "Post"
},
"target": {
"columns": [
"id"
],
"namespaceId": "public",
"tableName": "User"
}
}
],
"indexes": [
{
"columns": [
"authorId"
],
"name": "Post_authorId_idx_e47547ed",
"prefix": "Post_authorId_idx",
"unique": false
}
],
"primaryKey": {
"columns": [
"id"
]
},
"uniques": []
},
"User": {
"columns": {
"createdAt": {
"codecId": "pg/timestamptz-string@1",
"dataType": "pg/timestamptz",
"default": {
"expression": "now()",
"kind": "function"
},
"nullable": false
},
"email": {
"codecId": "pg/text@1",
"dataType": "pg/text",
"nullable": false
},
"id": {
"codecId": "pg/int4@1",
"dataType": "pg/int4",
"default": {
"expression": "autoincrement()",
"kind": "function"
},
"nullable": false
},
"name": {
"codecId": "pg/text@1",
"dataType": "pg/text",
"nullable": true
},
"updatedAt": {
"codecId": "pg/timestamptz-string@1",
"dataType": "pg/timestamptz",
"nullable": false
},
"username": {
"codecId": "pg/text@1",
"dataType": "pg/text",
"nullable": true
}
},
"foreignKeys": [],
"indexes": [],
"primaryKey": {
"columns": [
"id"
]
},
"uniques": [
{
"columns": [
"email"
]
}
]
}
}
},
"id": "public",
"kind": "postgres-schema"
}
},
"storageHash": "a51b4833fdaffe730572670ccc27dd89af7e0726ab3de7f428d21702d687cc15"
},
"execution": {
"executionHash": "4e88564095b5e0ba7904ffdba3c563ef916d87688c8c483c67055f71a9194f0f",
"mutations": {
"defaults": [
{
"onCreate": {
"id": "timestampNow",
"kind": "generator"
},
"onUpdate": {
"id": "timestampNow",
"kind": "generator"
},
"ref": {
"entry": "Post",
"field": "updatedAt",
"namespace": "public"
}
},
{
"onCreate": {
"id": "timestampNow",
"kind": "generator"
},
"onUpdate": {
"id": "timestampNow",
"kind": "generator"
},
"ref": {
"entry": "User",
"field": "updatedAt",
"namespace": "public"
}
}
]
}
},
"capabilities": {
"postgres": {
"distinctOn": true,
"forKeyShare": true,
"forNoKeyUpdate": true,
"jsonAgg": true,
"lateral": true,
"limit": true,
"orderBy": true,
"returning": true
},
"sql": {
"checkConstraint": true,
"defaultInInsert": true,
"enums": true,
"forShare": true,
"forUpdate": true,
"insertOnConflictSkip": true,
"insertOnConflictWithoutTarget": true,
"lateral": true,
"lockNowait": true,
"lockOf": true,
"lockSkipLocked": true,
"returning": true,
"scalarList": true
}
},
"extensions": {},
"meta": {},
"_generated": {
"warning": "⚠️ GENERATED FILE - DO NOT EDIT",
"message": "This file is automatically generated by \"prisma contract emit\".",
"regenerate": "To regenerate, run: prisma contract emit"
}
}
@@ -0,0 +1,21 @@
// use prisma-8
model User {
id Int @id @default(autoincrement())
email String @unique
username String?
name String?
posts Post[]
createdAt TimestamptzString @default(now())
updatedAt temporal.updatedAtString()
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
author User @relation(fields: [authorId], references: [id])
authorId Int
createdAt TimestamptzString @default(now())
updatedAt temporal.updatedAtString()
}
+9
View File
@@ -0,0 +1,9 @@
import 'dotenv/config';
import postgres from '@prisma/orm-postgres/runtime';
import type { Contract } from './contract.d';
import contractJson from './contract.json' with { type: 'json' };
export const db = postgres<Contract>({
contractJson,
url: process.env['DATABASE_URL']!,
});
+34
View File
@@ -0,0 +1,34 @@
{
"compilerOptions": {
// Environment setup & latest features
"lib": ["ESNext"],
"target": "ESNext",
"module": "preserve",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
"types": [
"bun",
"node"
],
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
// Some stricter flags (disabled by default)
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false,
"resolveJsonModule": true
}
}