Skip to content

Commit 6f60f14

Browse files
chore: wip
1 parent bfb6d44 commit 6f60f14

File tree

14 files changed

+291
-661
lines changed

14 files changed

+291
-661
lines changed

storage/framework/core/orm/src/utils.ts

Lines changed: 232 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import { getTraitTables } from '@stacksjs/database'
1818
import { handleError } from '@stacksjs/error-handling'
1919
import { path } from '@stacksjs/path'
2020
import { fs } from '@stacksjs/storage'
21-
import { camelCase, kebabCase, plural, singular, slugify, snakeCase } from '@stacksjs/strings'
21+
import { camelCase, kebabCase, pascalCase, plural, singular, slugify, snakeCase } from '@stacksjs/strings'
2222
import { isString } from '@stacksjs/validation'
2323
import { globSync } from 'tinyglobby'
2424
import { generateModelString } from './generate'
@@ -1133,30 +1133,57 @@ export async function generateApiRoutes(modelFiles: string[]): Promise<void> {
11331133
await writer.end()
11341134
}
11351135

1136+
export async function deleteExistingTypes(): Promise<void> {
1137+
try {
1138+
const typeFiles = globSync(path.frameworkPath('orm/src/types/*Type.ts'))
1139+
for (const file of typeFiles) {
1140+
await Bun.write(file, '')
1141+
log.info('Deleted type file:', file)
1142+
}
1143+
}
1144+
catch (error) {
1145+
handleError(error)
1146+
}
1147+
}
1148+
11361149
export async function deleteExistingModels(modelStringFile?: string): Promise<void> {
1137-
const typePath = path.frameworkPath(`orm/src/types.ts`)
1150+
try {
1151+
const typePath = path.frameworkPath(`orm/src/types.ts`)
11381152

1139-
await fs.writeFile(typePath, '')
1153+
await fs.writeFile(typePath, '')
11401154

1141-
if (modelStringFile) {
1142-
const modelPath = path.frameworkPath(`orm/src/models/${modelStringFile}.ts`)
1143-
if (fs.existsSync(modelPath))
1144-
await fs.promises.unlink(modelPath)
1155+
if (modelStringFile) {
1156+
const modelPath = path.frameworkPath(`orm/src/models/${modelStringFile}.ts`)
1157+
if (fs.existsSync(modelPath))
1158+
await fs.promises.unlink(modelPath)
11451159

1146-
return
1147-
}
1160+
return
1161+
}
11481162

1149-
const modelPaths = globSync([path.frameworkPath(`orm/src/models/*.ts`)], { absolute: true })
1163+
const modelPaths = globSync([path.frameworkPath(`orm/src/models/*.ts`)], { absolute: true })
11501164

1151-
await Promise.all(
1152-
modelPaths.map(async (modelPath) => {
1153-
if (fs.existsSync(modelPath)) {
1154-
log.info(`Deleting Model: ${italic(modelPath)}`)
1155-
await fs.promises.unlink(modelPath)
1156-
log.success(`Deleted Model: ${italic(modelPath)}`)
1157-
}
1158-
}),
1159-
)
1165+
await Promise.all(
1166+
modelPaths.map(async (modelPath) => {
1167+
if (fs.existsSync(modelPath)) {
1168+
log.info(`Deleting Model: ${italic(modelPath)}`)
1169+
await fs.promises.unlink(modelPath)
1170+
log.success(`Deleted Model: ${italic(modelPath)}`)
1171+
}
1172+
}),
1173+
)
1174+
1175+
await deleteExistingTypes()
1176+
await deleteExistingOrmActions(modelStringFile)
1177+
await deleteExistingModelNameTypes()
1178+
await deleteAttributeTypes()
1179+
await deleteModelEvents()
1180+
await deleteOrmImports()
1181+
await deleteExistingModelRequest(modelStringFile)
1182+
await deleteExistingOrmRoute()
1183+
}
1184+
catch (error) {
1185+
handleError(error)
1186+
}
11601187
}
11611188

11621189
export async function deleteExistingOrmActions(modelStringFile?: string): Promise<void> {
@@ -1449,6 +1476,8 @@ export async function generateModelFiles(modelStringFile?: string): Promise<void
14491476
const imports = extractImports(modelFile)
14501477
const classString = await generateModelString(tableName, modelName, model, fields, imports)
14511478

1479+
await writeTypeFile(tableName, modelName, model, fields)
1480+
14521481
const writer = file.writer()
14531482
log.info(`Writing Model: ${italic(modelName)}`)
14541483
writer.write(classString)
@@ -1480,8 +1509,9 @@ async function writeModelOrmImports(modelFiles: string[]): Promise<void> {
14801509
const model = (await import(modelFile)).default as Model
14811510

14821511
const modelName = getModelName(model, modelFile)
1512+
const tableName = getTableName(model, modelFile)
14831513

1484-
ormImportString += `export { default as ${modelName}, type ${modelName}JsonResponse, ${modelName}Model, type New${modelName}, type ${modelName}Update } from './models/${modelName}'\n\n`
1514+
ormImportString += `export { default as ${modelName}, type ${modelName}JsonResponse, ${modelName}Model, type ${pascalCase(tableName)}Table, type New${modelName}, type ${modelName}Update } from './models/${modelName}'\n\n`
14851515
}
14861516

14871517
const file = Bun.file(path.frameworkPath(`orm/src/index.ts`))
@@ -1596,3 +1626,185 @@ export function formatDate(date: Date): string {
15961626
export function toTimestamp(date: Date): number {
15971627
return date.getTime()
15981628
}
1629+
1630+
export async function generateTypeString(
1631+
tableName: string,
1632+
modelName: string,
1633+
model: Model,
1634+
attributes: ModelElement[],
1635+
): Promise<string> {
1636+
const formattedTableName = pascalCase(tableName)
1637+
1638+
// Generate the base table interface
1639+
let tableInterface = `export interface ${formattedTableName}Table {
1640+
id: Generated<number>
1641+
`
1642+
1643+
// Add attributes to the table interface
1644+
for (const attribute of attributes) {
1645+
const entity = mapEntity(attribute)
1646+
const optionalIndicator = attribute.required === false ? '?' : ''
1647+
tableInterface += ` ${snakeCase(attribute.field)}${optionalIndicator}: ${entity}\n`
1648+
}
1649+
1650+
// Add common fields
1651+
if (model.traits?.useUuid)
1652+
tableInterface += ' uuid?: string\n'
1653+
1654+
if (model.traits?.useTimestamps ?? model.traits?.timestampable ?? true) {
1655+
tableInterface += ' created_at?: string\n'
1656+
tableInterface += ' updated_at?: string\n'
1657+
}
1658+
1659+
if (model.traits?.useSoftDeletes ?? model.traits?.softDeletable ?? false)
1660+
tableInterface += ' deleted_at?: string\n'
1661+
1662+
tableInterface += '}\n\n'
1663+
1664+
// Generate the read type
1665+
const readType = `export type ${modelName}Read = ${formattedTableName}Table\n\n`
1666+
1667+
// Generate the write type
1668+
const writeType = `export type ${modelName}Write = Omit<${formattedTableName}Table, 'created_at'> & {
1669+
created_at?: string
1670+
}\n\n`
1671+
1672+
// Generate the response interface
1673+
const responseInterface = `export interface ${modelName}Response {
1674+
data: ${modelName}JsonResponse[]
1675+
paging: {
1676+
total_records: number
1677+
page: number
1678+
total_pages: number
1679+
}
1680+
next_cursor: number | null
1681+
}\n\n`
1682+
1683+
// Generate the JSON response interface
1684+
const jsonResponseInterface = `export interface ${modelName}JsonResponse extends Omit<Selectable<${modelName}Read>, 'password'> {
1685+
[key: string]: any
1686+
}\n\n`
1687+
1688+
// Generate the new and update types
1689+
const newType = `export type New${modelName} = Insertable<${modelName}Write>\n`
1690+
const updateType = `export type ${modelName}Update = Updateable<${modelName}Write>\n\n`
1691+
1692+
// Generate the static interface
1693+
const staticInterface = `export interface I${modelName}ModelStatic {
1694+
with: (relations: string[]) => I${modelName}Model
1695+
select: (params: (keyof ${modelName}JsonResponse)[] | RawBuilder<string> | string) => I${modelName}Model
1696+
find: (id: number) => Promise<I${modelName}Model | undefined>
1697+
first: () => Promise<I${modelName}Model | undefined>
1698+
last: () => Promise<I${modelName}Model | undefined>
1699+
firstOrFail: () => Promise<I${modelName}Model | undefined>
1700+
all: () => Promise<I${modelName}Model[]>
1701+
findOrFail: (id: number) => Promise<I${modelName}Model | undefined>
1702+
findMany: (ids: number[]) => Promise<I${modelName}Model[]>
1703+
latest: (column?: keyof ${formattedTableName}Table) => Promise<I${modelName}Model | undefined>
1704+
oldest: (column?: keyof ${formattedTableName}Table) => Promise<I${modelName}Model | undefined>
1705+
skip: (count: number) => I${modelName}Model
1706+
take: (count: number) => I${modelName}Model
1707+
where: <V = string>(column: keyof ${formattedTableName}Table, ...args: [V] | [Operator, V]) => I${modelName}Model
1708+
orWhere: (...conditions: [string, any][]) => I${modelName}Model
1709+
whereNotIn: <V = number>(column: keyof ${formattedTableName}Table, values: V[]) => I${modelName}Model
1710+
whereBetween: <V = number>(column: keyof ${formattedTableName}Table, range: [V, V]) => I${modelName}Model
1711+
whereRef: (column: keyof ${formattedTableName}Table, ...args: string[]) => I${modelName}Model
1712+
when: (condition: boolean, callback: (query: I${modelName}Model) => I${modelName}Model) => I${modelName}Model
1713+
whereNull: (column: keyof ${formattedTableName}Table) => I${modelName}Model
1714+
whereNotNull: (column: keyof ${formattedTableName}Table) => I${modelName}Model
1715+
whereLike: (column: keyof ${formattedTableName}Table, value: string) => I${modelName}Model
1716+
orderBy: (column: keyof ${formattedTableName}Table, order: 'asc' | 'desc') => I${modelName}Model
1717+
orderByAsc: (column: keyof ${formattedTableName}Table) => I${modelName}Model
1718+
orderByDesc: (column: keyof ${formattedTableName}Table) => I${modelName}Model
1719+
groupBy: (column: keyof ${formattedTableName}Table) => I${modelName}Model
1720+
having: <V = string>(column: keyof ${formattedTableName}Table, operator: Operator, value: V) => I${modelName}Model
1721+
inRandomOrder: () => I${modelName}Model
1722+
whereColumn: (first: keyof ${formattedTableName}Table, operator: Operator, second: keyof ${formattedTableName}Table) => I${modelName}Model
1723+
max: (field: keyof ${formattedTableName}Table) => Promise<number>
1724+
min: (field: keyof ${formattedTableName}Table) => Promise<number>
1725+
avg: (field: keyof ${formattedTableName}Table) => Promise<number>
1726+
sum: (field: keyof ${formattedTableName}Table) => Promise<number>
1727+
count: () => Promise<number>
1728+
get: () => Promise<I${modelName}Model[]>
1729+
pluck: <K extends keyof I${modelName}Model>(field: K) => Promise<I${modelName}Model[K][]>
1730+
chunk: (size: number, callback: (models: I${modelName}Model[]) => Promise<void>) => Promise<void>
1731+
paginate: (options?: { limit?: number, offset?: number, page?: number }) => Promise<{
1732+
data: I${modelName}Model[]
1733+
paging: {
1734+
total_records: number
1735+
page: number
1736+
total_pages: number
1737+
}
1738+
next_cursor: number | null
1739+
}>
1740+
create: (new${modelName}: New${modelName}) => Promise<I${modelName}Model>
1741+
firstOrCreate: (search: Partial<${formattedTableName}Table>, values?: New${modelName}) => Promise<I${modelName}Model>
1742+
updateOrCreate: (search: Partial<${formattedTableName}Table>, values?: New${modelName}) => Promise<I${modelName}Model>
1743+
createMany: (new${modelName}: New${modelName}[]) => Promise<void>
1744+
forceCreate: (new${modelName}: New${modelName}) => Promise<I${modelName}Model>
1745+
remove: (id: number) => Promise<any>
1746+
whereIn: <V = number>(column: keyof ${formattedTableName}Table, values: V[]) => I${modelName}Model
1747+
distinct: (column: keyof ${modelName}JsonResponse) => I${modelName}Model
1748+
join: (table: string, firstCol: string, secondCol: string) => I${modelName}Model
1749+
}\n\n`
1750+
1751+
// Generate the instance interface
1752+
let instanceInterface = `export interface I${modelName}Model {
1753+
// Properties
1754+
readonly id: number\n`
1755+
1756+
// Add getters and setters for each attribute
1757+
for (const attribute of attributes) {
1758+
const field = snakeCase(attribute.field)
1759+
const optionalIndicator = attribute.required === false ? ' | undefined' : ''
1760+
instanceInterface += ` get ${field}(): ${mapEntity(attribute)}${optionalIndicator}\n`
1761+
instanceInterface += ` set ${field}(value: ${mapEntity(attribute)})\n`
1762+
}
1763+
1764+
// Add common getters and setters
1765+
if (model.traits?.useUuid) {
1766+
instanceInterface += ' get uuid(): string | undefined\n'
1767+
instanceInterface += ' set uuid(value: string)\n'
1768+
}
1769+
1770+
if (model.traits?.useTimestamps ?? model.traits?.timestampable ?? true) {
1771+
instanceInterface += ' get created_at(): string | undefined\n'
1772+
instanceInterface += ' get updated_at(): string | undefined\n'
1773+
instanceInterface += ' set updated_at(value: string)\n'
1774+
}
1775+
1776+
// Add instance methods
1777+
instanceInterface += `
1778+
// Instance methods
1779+
createInstance: (data: ${modelName}JsonResponse) => I${modelName}Model
1780+
create: (new${modelName}: New${modelName}) => Promise<I${modelName}Model>
1781+
update: (new${modelName}: ${modelName}Update) => Promise<I${modelName}Model | undefined>
1782+
forceUpdate: (new${modelName}: ${modelName}Update) => Promise<I${modelName}Model | undefined>
1783+
save: () => Promise<I${modelName}Model>
1784+
delete: () => Promise<number>
1785+
toSearchableObject: () => Partial<${modelName}JsonResponse>
1786+
toJSON: () => ${modelName}JsonResponse
1787+
parseResult: (model: I${modelName}Model) => I${modelName}Model
1788+
}\n\n`
1789+
1790+
// Generate the combined type
1791+
const combinedType = `export type ${modelName}ModelType = I${modelName}Model & I${modelName}ModelStatic\n`
1792+
1793+
// Combine all type declarations
1794+
return `import type { Generated, Insertable, Selectable, Updateable, RawBuilder } from '@stacksjs/database'
1795+
import type { Operator } from '@stacksjs/orm'
1796+
1797+
${tableInterface}${readType}${writeType}${responseInterface}${jsonResponseInterface}${newType}${updateType}${staticInterface}${instanceInterface}${combinedType}`
1798+
}
1799+
1800+
export async function writeTypeFile(
1801+
tableName: string,
1802+
modelName: string,
1803+
model: Model,
1804+
attributes: ModelElement[],
1805+
): Promise<void> {
1806+
log.info('Writing type file for', modelName)
1807+
const typeString = await generateTypeString(tableName, modelName, model, attributes)
1808+
const typeFilePath = path.frameworkPath(`orm/src/types/${modelName}Type.ts`)
1809+
await Bun.write(typeFilePath, typeString)
1810+
}

storage/framework/core/payments/src/billable/charge.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import type { UserModel } from '@stacksjs/orm'
12
import type Stripe from 'stripe'
2-
import type { UserModel } from '../../../../orm/src/models/User'
33
import { log } from '@stacksjs/logging'
44
import { stripe } from '..'
55

@@ -24,12 +24,12 @@ export const manageCharge: ManageCharge = (() => {
2424

2525
const mergedOptions = { ...defaultOptions, ...options }
2626

27-
return await stripe.paymentIntent.create(mergedOptions)
27+
return await stripe.paymentIntents.create(mergedOptions)
2828
}
2929

3030
async function findPayment(id: string): Promise<Stripe.PaymentIntent | null> {
3131
try {
32-
const stripePaymentIntent = await stripe.paymentIntent.retrieve(id)
32+
const stripePaymentIntent = await stripe.paymentIntents.retrieve(id)
3333
return stripePaymentIntent
3434
}
3535
catch (error) {
@@ -45,7 +45,7 @@ export const manageCharge: ManageCharge = (() => {
4545
...options,
4646
}
4747

48-
return await stripe.refund.create(refundParams)
48+
return await stripe.refunds.create(refundParams)
4949
}
5050

5151
async function charge(user: UserModel, amount: number, paymentMethod: string, options: Stripe.PaymentIntentCreateParams): Promise<Stripe.Response<Stripe.PaymentIntent>> {

storage/framework/core/payments/src/billable/checkout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const manageCheckout: Checkout = (() => {
2121

2222
const mergedParams = { ...defaultParams, ...params }
2323

24-
return await stripe.checkout.create(mergedParams)
24+
return await stripe.checkout.sessions.create(mergedParams)
2525
}
2626

2727
return { create }

storage/framework/core/payments/src/billable/customer.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1+
import type { UserModel } from '@stacksjs/orm'
12
import type { StripeCustomerOptions } from '@stacksjs/types'
23
import type Stripe from 'stripe'
3-
import type { UserModel } from '../../../../orm/src/models/User'
44
import { stripe } from '..'
55

66
export interface ManageCustomer {
@@ -61,15 +61,15 @@ export const manageCustomer: ManageCustomer = (() => {
6161
options.email = stripeEmail(user)
6262
}
6363

64-
const customer = await stripe.customer.create(options)
64+
const customer = await stripe.customers.create(options)
6565

6666
await user.update({ stripe_id: customer.id })
6767

6868
return customer
6969
}
7070

7171
async function updateStripeCustomer(user: UserModel, options: Stripe.CustomerCreateParams = {}): Promise<Stripe.Response<Stripe.Customer>> {
72-
const customer = await stripe.customer.update(user.stripe_id || '', options)
72+
const customer = await stripe.customers.update(user.stripe_id || '', options)
7373

7474
return customer
7575
}
@@ -80,7 +80,7 @@ export const manageCustomer: ManageCustomer = (() => {
8080
}
8181

8282
try {
83-
const deletedCustomer = await stripe.customer.del(user.stripe_id || '')
83+
const deletedCustomer = await stripe.customers.del(user.stripe_id || '')
8484

8585
// Update the user model to remove the Stripe ID
8686
await user.update({ stripe_id: '' })
@@ -101,7 +101,7 @@ export const manageCustomer: ManageCustomer = (() => {
101101
}
102102

103103
try {
104-
const customer = await stripe.customer.retrieve(user.stripe_id || '')
104+
const customer = await stripe.customers.retrieve(user.stripe_id || '')
105105
if ((customer as Stripe.DeletedCustomer).deleted) {
106106
throw new Error('Customer was deleted')
107107
}
@@ -122,7 +122,7 @@ export const manageCustomer: ManageCustomer = (() => {
122122
}
123123

124124
try {
125-
const customer = await stripe.customer.retrieve(user.stripe_id || '')
125+
const customer = await stripe.customers.retrieve(user.stripe_id || '')
126126

127127
if ((customer as Stripe.DeletedCustomer).deleted) {
128128
throw new Error('Customer was deleted in Stripe')
@@ -144,7 +144,7 @@ export const manageCustomer: ManageCustomer = (() => {
144144
}
145145

146146
try {
147-
const customer = await stripe.customer.retrieve(user.stripe_id || '')
147+
const customer = await stripe.customers.retrieve(user.stripe_id || '')
148148
if ((customer as Stripe.DeletedCustomer).deleted) {
149149
return await createStripeCustomer(user, options)
150150
}

storage/framework/core/payments/src/billable/intent.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1+
import type { UserModel } from '@stacksjs/orm'
12
import type Stripe from 'stripe'
2-
import type { UserModel } from '../../../../orm/src/models/User'
33
import { manageCustomer, stripe } from '..'
44

55
export interface SetupIntent {

0 commit comments

Comments
 (0)