Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 0 additions & 32 deletions .github/workflows/build_and_release.yml

This file was deleted.

90 changes: 90 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Publish new version

on:
push:
branches:
- main
paths:
- package.json
workflow_dispatch:

jobs:
tag:
name: Tag
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
created: ${{ steps.version_tag.outputs.created }}
version_tag: ${{ steps.version_tag.outputs.version_tag }}
steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Ensure version tag exists
id: version_tag
run: |
VERSION=$(bun -p "require('./package.json').version")
VERSION_TAG="v${VERSION}"

echo "version_tag=${VERSION_TAG}" >> $GITHUB_OUTPUT
echo "created=false" >> $GITHUB_OUTPUT

git fetch --tags

if ! git rev-parse "${VERSION_TAG}" >/dev/null 2>&1; then
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git tag "${VERSION_TAG}"
git push origin "${VERSION_TAG}"
echo "created=true" >> $GITHUB_OUTPUT
echo "Created and pushed ${VERSION_TAG}."
fi

release:
name: Release
runs-on: ubuntu-latest
needs: [tag]
if: needs.tag.outputs.created == 'true'
permissions:
contents: write
steps:
- name: Create release from new tag
uses: actions/github-script@v7
with:
script: |
const tag = '${{ needs.tag.outputs.version_tag }}';
const prerelease = tag.includes('-');

await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: tag,
name: tag,
prerelease,
generate_release_notes: true,
});

publish:
name: Publish
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install npm
run: bun add -g npm
- name: Install dependencies
run: NODE_ENV=development bun i
- name: Build and publish
run: bunx npm publish --provenance --access public
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2021 Ade Yahya Prasetyo, 2024 m1212e
Copyright (c) 2021 Ade Yahya Prasetyo, 2024 m1212e, 2026 Aran Leite

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
18 changes: 10 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
# prismabox
# @hyoretsu/prismabox
Generate versatile [typebox](https://github.com/sinclairzx81/typebox) schemes from your [prisma](https://github.com/prisma) schema.

> Currently does not support [mongoDB composite types](https://www.prisma.io/docs/orm/prisma-schema/data-model/models#defining-composite-types)

> Development is currently on hold, please see [here](https://github.com/m1212e/prismabox/issues/59)

Install it in your project,
```bash
npm i -D prismabox
pnpm i -D prismabox
bun add -D prismabox
npm i -D @hyoretsu/prismabox
pnpm i -D @hyoretsu/prismabox
bun a -D @hyoretsu/prismabox
```

then add
Expand All @@ -22,6 +20,10 @@ generator prismabox {
typeboxImportVariableName = "t"
// you also can specify the dependency from which the above import should happen. This is useful if a package re-exports the typebox package and you would like to use that
typeboxImportDependencyName = "elysia"
// optionally generate TS aliases for each schema: export type MySchema = UnwrapSchema<typeof MySchema>
generateTsTypes = true
// optionally customize the unwrap utility type symbol (imported from the same package as typeboxImportDependencyName)
unwrapSchemaImportName = "UnwrapSchema"
Comment on lines +23 to +26
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new config example introduces unwrapSchemaImportName = "UnwrapSchema", but the actual default in src/config.ts is currently "Type.Static" (which is not a valid named import). Please align the README with the real default and clarify that unwrapSchemaImportName must be a named export identifier (e.g. "Static" for @sinclair/typebox) that can be imported with import { type ... } from typeboxImportDependencyName.

Suggested change
// optionally generate TS aliases for each schema: export type MySchema = UnwrapSchema<typeof MySchema>
generateTsTypes = true
// optionally customize the unwrap utility type symbol (imported from the same package as typeboxImportDependencyName)
unwrapSchemaImportName = "UnwrapSchema"
// optionally generate TS aliases for each schema: export type MySchema = Static<typeof MySchema>
generateTsTypes = true
// optionally customize the unwrap utility type symbol (must be a named export identifier that can be imported with:
// import { type <unwrapSchemaImportName> } from typeboxImportDependencyName; defaults to "Type.Static" for @sinclair/typebox)
unwrapSchemaImportName = "Static"

Copilot uses AI. Check for mistakes.
// by default the generated schemes do not allow additional properties. You can allow them by setting this to true
additionalProperties = true
// optionally enable the data model generation. See the data model section below for more info
Expand All @@ -42,7 +44,7 @@ Prismabox offers annotations to adjust the output of models and fields.
| @prismabox.create.input.hide | - | Hides the field or model from the outputs only in the input create model|
| @prismabox.update.input.hide | - | Hides the field or model from the outputs only in the input update model|
| @prismabox.options | @prismabox.options{ min: 10, max: 20 } | Uses the provided options for the field or model in the generated schema. Be careful to use valid JS/TS syntax! |
| @prismabox.typeOverwrite | @prismabox.typeOverwrite=Type.CustomName | Overwrite the type prismabox outputs for a field with a custom string. See [m1212e/prismabox#29](https://github.com/m1212e/prismabox/issues/29) for an extended usecase |
| @prismabox.typeOverwrite | @prismabox.typeOverwrite=Type.CustomName | Overwrite the type prismabox outputs for a field with a custom string. See [hyoretsu/prismabox#29](https://github.com/hyoretsu/prismabox/issues/29) for an extended usecase |

> For a more detailed list of available annotations, please see [annotations.ts](src/annotations/annotations.ts)

Expand Down Expand Up @@ -113,6 +115,6 @@ If enabled, the generator will additonally output more schemes for each model wh
## Notes
### `__nullable__` vs `Type.Optional`

Prismabox wraps nullable fields in a custom `__nullable__` method which allows `null` in addition to `undefined`. From the relevant [issue comment](https://github.com/m1212e/prismabox/issues/33#issuecomment-2708755442):
Prismabox wraps nullable fields in a custom `__nullable__` method which allows `null` in addition to `undefined`. From the relevant [issue comment](https://github.com/hyoretsu/prismabox/issues/33#issuecomment-2708755442):
> prisma in some scenarios allows null OR undefined as types where optional only allows for undefined/is reflected as undefined in TS types

11 changes: 0 additions & 11 deletions build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,3 @@ let version = process.env.REF_NAME ?? packagejson.version;
if (!version) {
version = "0.0.1";
}

await copyFile("./README.md", "./dist/README.md");
await copyFile("./LICENSE", "./dist/LICENSE");
await writeFile(
"./dist/package.json",
JSON.stringify({
...packagejson,
version,
bin: { prismabox: "cli.js" },
}),
);
Binary file modified bun.lockb
Binary file not shown.
17 changes: 12 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "prismabox",
"version": "2.2.0",
"name": "@hyoretsu/prismabox",
"version": "1.2.2",
"license": "MIT",
"description": "Typebox generator for prisma schema",
"author": {
"name": "Ade Yahya Prasetyo, m1212e"
"name": "Ade Yahya Prasetyo, m1212e, Aran Leite"
},
"keywords": [
"prisma2",
Expand All @@ -16,15 +16,22 @@
"typebox-generator",
"prismabox"
],
"homepage": "https://github.com/m1212e/prismabox",
"homepage": "https://github.com/hyoretsu/prismabox",
"repository": {
"url": "https://github.com/m1212e/prismabox.git"
"url": "git+https://github.com/hyoretsu/prismabox.git"
},
"bin": {
"prismabox": "dist/cli.js"
},
"files": [
"dist"
],
"scripts": {
"dev": "bun run build && bunx prisma generate",
"build": "bun run typecheck && bun build.ts",
"lint": "biome check --write .",
"format": "bun run lint",
"prepublishOnly": "bun run build",
"typecheck": "tsc --noEmit"
},
"dependencies": {
Expand Down
14 changes: 13 additions & 1 deletion src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ const configSchema = Type.Object(
* The name of the dependency to import the Type from typebox
*/
typeboxImportDependencyName: Type.String({ default: "@sinclair/typebox" }),
/**
* Whether to generate TypeScript type aliases for each generated schema.
*
* When enabled, every `export const Foo = ...` also emits
* `export type Foo = UnwrapSchema<typeof Foo>`.
*/
generateTsTypes: Type.Boolean({ default: true }),
/**
* The symbol name used to unwrap a schema into a TypeScript type.
* Imported as a type from `typeboxImportDependencyName`.
*/
unwrapSchemaImportName: Type.String({ default: "Static" }),
/**
* Whether to allow additional properties in the generated schemes
*/
Expand Down Expand Up @@ -49,7 +61,7 @@ const configSchema = Type.Object(
allowRecursion: Type.Boolean({ default: true }),
/**
* Additional fields to add to the generated schemes (must be valid strings in the context of usage)
* @example
* @example
* ```prisma
* generator prismabox {
provider = "node ./dist/cli.js"
Expand Down
19 changes: 15 additions & 4 deletions src/generators/wrappers/composite.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { generateTypeboxOptions } from "../../annotations/options";
import { getConfig } from "../../config";

export function makeComposite(inputModels: string[]) {
return `${
getConfig().typeboxImportVariableName
}.Composite([${inputModels.map((i) => `${getConfig().exportedTypePrefix}${i}`).join(",")}], ${generateTypeboxOptions()})\n`;
function makePartial(input: string) {
return `${getConfig().typeboxImportVariableName}.Partial(${input})`;
}

export function makeComposite(inputModels: string[], partial: boolean[] = []) {
const { exportedTypePrefix, typeboxImportVariableName } = getConfig();

return `${typeboxImportVariableName}.Composite([${inputModels
.map((model, i) => {
let modelStr = `${exportedTypePrefix}${model}`;
if (partial[i]) modelStr = makePartial(modelStr);

return modelStr;
})
.join(", ")}], ${generateTypeboxOptions()})\n`;
}
23 changes: 20 additions & 3 deletions src/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,25 @@ export type ProcessedModel = {
function convertModelToStandalone(
input: Pick<ProcessedModel, "name" | "stringRepresentation">,
) {
return `export const ${getConfig().exportedTypePrefix}${input.name} = ${input.stringRepresentation}\n`;
const generatedName = `${getConfig().exportedTypePrefix}${input.name}`;

let exportStr = `export const ${generatedName} = ${input.stringRepresentation}\n`;

if (getConfig().generateTsTypes) {
exportStr += `export type ${generatedName} = ${getConfig().unwrapSchemaImportName}<typeof ${generatedName}>\n`;
}

return exportStr;
}

function typepoxImportStatement() {
return `import { ${getConfig().typeboxImportVariableName} } from "${
let imports = getConfig().typeboxImportVariableName;

if (getConfig().generateTsTypes) {
imports += `, type ${getConfig().unwrapSchemaImportName}`;
}
Comment on lines 41 to +46
Copy link

Copilot AI Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typepoxImportStatement() and convertModelToStandalone() assume unwrapSchemaImportName is a valid named import specifier and generic type identifier. With the current config default ("Type.Static"), this would emit import { ..., type Type.Static } ... and Type.Static<typeof X>, which is invalid TS. Consider validating unwrapSchemaImportName (must be an identifier) and/or supporting a separate config for the type expression vs the type import name so dotted paths don’t break codegen.

Copilot uses AI. Check for mistakes.

return `import { ${imports} } from "${
getConfig().typeboxImportDependencyName
}"\n`;
}
Expand Down Expand Up @@ -75,7 +89,10 @@ export function mapAllModelsForWrite() {
const relations = processedRelations.find((e) => e.name === key);
let composite: string;
if (plain && relations) {
composite = makeComposite([`${key}Plain`, `${key}Relations`]);
composite = makeComposite(
[`${key}Plain`, `${key}Relations`],
[false, true],
);
} else if (plain) {
composite = `${key}Plain`;
} else if (relations) {
Expand Down