kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: drizzle-zod and hono openapi improvements
rodzic
f1437a47d1
commit
05d6b25781
|
@ -36,16 +36,17 @@
|
||||||
"test:unit": "vitest run"
|
"test:unit": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@agentic/faas-utils": "workspace:*",
|
"@agentic/validators": "workspace:*",
|
||||||
|
"@fisch0920/drizzle-zod": "^0.7.2",
|
||||||
"@google-cloud/logging": "^11.2.0",
|
"@google-cloud/logging": "^11.2.0",
|
||||||
"@hono/node-server": "^1.14.1",
|
"@hono/node-server": "^1.14.1",
|
||||||
"@hono/sentry": "^1.2.1",
|
"@hono/sentry": "^1.2.1",
|
||||||
|
"@hono/zod-openapi": "^0.19.5",
|
||||||
"@hono/zod-validator": "^0.4.3",
|
"@hono/zod-validator": "^0.4.3",
|
||||||
"@paralleldrive/cuid2": "^2.2.2",
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
"@sentry/node": "^9.14.0",
|
"@sentry/node": "^9.14.0",
|
||||||
"@workos-inc/node": "^7.47.0",
|
"@workos-inc/node": "^7.47.0",
|
||||||
"drizzle-orm": "^0.43.0",
|
"drizzle-orm": "^0.43.0",
|
||||||
"drizzle-zod": "^0.7.1",
|
|
||||||
"eventid": "^2.0.1",
|
"eventid": "^2.0.1",
|
||||||
"exit-hook": "catalog:",
|
"exit-hook": "catalog:",
|
||||||
"hono": "^4.7.7",
|
"hono": "^4.7.7",
|
||||||
|
|
|
@ -1,5 +1,24 @@
|
||||||
import type { Context } from 'hono'
|
import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
||||||
|
|
||||||
export async function healthCheck(c: Context) {
|
const route = createRoute({
|
||||||
return c.json({ status: 'ok' })
|
method: 'get',
|
||||||
|
path: 'health',
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'OK',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: z.object({
|
||||||
|
status: z.string()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export function registerHealthCheck(app: OpenAPIHono) {
|
||||||
|
return app.openapi(route, async (c) => {
|
||||||
|
return c.json({ status: 'ok' })
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,23 @@
|
||||||
import { Hono } from 'hono'
|
import { OpenAPIHono } from '@hono/zod-openapi'
|
||||||
|
|
||||||
import type { AuthenticatedEnv } from '@/lib/types'
|
import type { AuthenticatedEnv } from '@/lib/types'
|
||||||
import * as middleware from '@/lib/middleware'
|
import * as middleware from '@/lib/middleware'
|
||||||
|
|
||||||
import { healthCheck } from './health-check'
|
import { registerHealthCheck } from './health-check'
|
||||||
|
import { registerV1UsersGetUser } from './users/get-user'
|
||||||
|
|
||||||
export const apiV1 = new Hono()
|
export const apiV1 = new OpenAPIHono()
|
||||||
|
|
||||||
const pub = new Hono()
|
const pub = new OpenAPIHono()
|
||||||
const pri = new Hono<AuthenticatedEnv>()
|
const pri = new OpenAPIHono<AuthenticatedEnv>()
|
||||||
|
|
||||||
pub.get('/health', healthCheck)
|
registerHealthCheck(pub)
|
||||||
|
|
||||||
apiV1.route('', pub)
|
// users
|
||||||
|
registerV1UsersGetUser(pri)
|
||||||
|
|
||||||
|
apiV1.route('/', pub)
|
||||||
apiV1.use(middleware.authenticate)
|
apiV1.use(middleware.authenticate)
|
||||||
apiV1.use(middleware.team)
|
apiV1.use(middleware.team)
|
||||||
apiV1.use(middleware.me)
|
apiV1.use(middleware.me)
|
||||||
apiV1.route('', pri)
|
apiV1.route('/', pri)
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
import { createRoute, type OpenAPIHono, z } from '@hono/zod-openapi'
|
||||||
|
|
||||||
|
import type { AuthenticatedEnv } from '@/lib/types'
|
||||||
|
import { db, eq, schema, userIdSchema } from '@/db'
|
||||||
|
import { assert, parseZodSchema } from '@/lib/utils'
|
||||||
|
|
||||||
|
const ParamsSchema = z.object({
|
||||||
|
userId: userIdSchema.openapi({
|
||||||
|
param: {
|
||||||
|
name: 'userId',
|
||||||
|
in: 'path'
|
||||||
|
},
|
||||||
|
example: 'pfh0haxfpzowht3oi213cqos'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = createRoute({
|
||||||
|
tags: ['users'],
|
||||||
|
operationId: 'getUser',
|
||||||
|
method: 'get',
|
||||||
|
path: 'users/{userId}',
|
||||||
|
security: [{ bearerAuth: [] }],
|
||||||
|
request: {
|
||||||
|
params: ParamsSchema
|
||||||
|
},
|
||||||
|
responses: {
|
||||||
|
200: {
|
||||||
|
description: 'A user object',
|
||||||
|
content: {
|
||||||
|
'application/json': {
|
||||||
|
schema: schema.userSelectSchema
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// TODO
|
||||||
|
// ...openApiErrorResponses
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export type Route = typeof route
|
||||||
|
export type V1UsersGetUserResponse = z.infer<
|
||||||
|
(typeof route.responses)[200]['content']['application/json']['schema']
|
||||||
|
>
|
||||||
|
|
||||||
|
export const registerV1UsersGetUser = (app: OpenAPIHono<AuthenticatedEnv>) =>
|
||||||
|
app.openapi(route, async (c) => {
|
||||||
|
const { userId } = c.req.valid('param')
|
||||||
|
|
||||||
|
const user = await db.query.users.findFirst({
|
||||||
|
where: eq(schema.users.id, userId)
|
||||||
|
})
|
||||||
|
assert(user, 404, `User not found: ${userId}`)
|
||||||
|
|
||||||
|
return c.json(parseZodSchema(schema.userSelectSchema, user))
|
||||||
|
})
|
|
@ -12,4 +12,30 @@ const postgresClient =
|
||||||
export const db = drizzle({ client: postgresClient, schema })
|
export const db = drizzle({ client: postgresClient, schema })
|
||||||
|
|
||||||
export * as schema from './schema'
|
export * as schema from './schema'
|
||||||
|
export * from './schemas'
|
||||||
export type * from './types'
|
export type * from './types'
|
||||||
|
export {
|
||||||
|
and,
|
||||||
|
arrayContained,
|
||||||
|
arrayContains,
|
||||||
|
between,
|
||||||
|
eq,
|
||||||
|
exists,
|
||||||
|
gt,
|
||||||
|
gte,
|
||||||
|
ilike,
|
||||||
|
inArray,
|
||||||
|
isNotNull,
|
||||||
|
isNull,
|
||||||
|
like,
|
||||||
|
lt,
|
||||||
|
lte,
|
||||||
|
ne,
|
||||||
|
not,
|
||||||
|
notBetween,
|
||||||
|
notExists,
|
||||||
|
notIlike,
|
||||||
|
notInArray,
|
||||||
|
notLike,
|
||||||
|
or
|
||||||
|
} from 'drizzle-orm'
|
||||||
|
|
|
@ -1,10 +1,16 @@
|
||||||
import { validators } from '@agentic/faas-utils'
|
import { validators } from '@agentic/validators'
|
||||||
|
import { z } from '@hono/zod-openapi'
|
||||||
import { relations } from 'drizzle-orm'
|
import { relations } from 'drizzle-orm'
|
||||||
import { boolean, index, jsonb, pgTable, text } from 'drizzle-orm/pg-core'
|
import { boolean, index, jsonb, pgTable, text } from 'drizzle-orm/pg-core'
|
||||||
|
|
||||||
import type { Coupon, PricingPlan } from './types'
|
|
||||||
import { projects } from './project'
|
import { projects } from './project'
|
||||||
import { teams } from './team'
|
import { teams } from './team'
|
||||||
|
import {
|
||||||
|
type Coupon,
|
||||||
|
couponSchema,
|
||||||
|
type PricingPlan,
|
||||||
|
pricingPlanSchema
|
||||||
|
} from './types'
|
||||||
import { users } from './user'
|
import { users } from './user'
|
||||||
import {
|
import {
|
||||||
createInsertSchema,
|
createInsertSchema,
|
||||||
|
@ -105,12 +111,24 @@ export const deploymentInsertSchema = createInsertSchema(deployments, {
|
||||||
message: 'Invalid deployment hash'
|
message: 'Invalid deployment hash'
|
||||||
}),
|
}),
|
||||||
|
|
||||||
_url: (schema) => schema.url()
|
_url: (schema) => schema.url(),
|
||||||
|
|
||||||
|
build: z.object({}),
|
||||||
|
env: z.object({}),
|
||||||
|
pricingPlans: z.array(pricingPlanSchema),
|
||||||
|
coupons: z.array(couponSchema).optional()
|
||||||
})
|
})
|
||||||
|
|
||||||
export const deploymentSelectSchema = createSelectSchema(deployments).omit({
|
export const deploymentSelectSchema = createSelectSchema(deployments, {
|
||||||
_url: true
|
build: z.object({}),
|
||||||
|
env: z.object({}),
|
||||||
|
pricingPlans: z.array(pricingPlanSchema),
|
||||||
|
coupons: z.array(couponSchema)
|
||||||
})
|
})
|
||||||
|
.omit({
|
||||||
|
_url: true
|
||||||
|
})
|
||||||
|
.openapi('Deployment')
|
||||||
|
|
||||||
export const deploymentUpdateSchema = createUpdateSchema(deployments).pick({
|
export const deploymentUpdateSchema = createUpdateSchema(deployments).pick({
|
||||||
enabled: true,
|
enabled: true,
|
||||||
|
|
|
@ -4,4 +4,3 @@ export * from './team'
|
||||||
export * from './team-member'
|
export * from './team-member'
|
||||||
export type * from './types'
|
export type * from './types'
|
||||||
export * from './user'
|
export * from './user'
|
||||||
export * from './utils'
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { validators } from '@agentic/faas-utils'
|
import { validators } from '@agentic/validators'
|
||||||
|
import { z } from '@hono/zod-openapi'
|
||||||
import { relations } from 'drizzle-orm'
|
import { relations } from 'drizzle-orm'
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
|
@ -11,9 +12,9 @@ import {
|
||||||
|
|
||||||
import { getProviderToken } from '@/lib/auth/get-provider-token'
|
import { getProviderToken } from '@/lib/auth/get-provider-token'
|
||||||
|
|
||||||
import type { Webhook } from './types'
|
|
||||||
import { deployments } from './deployment'
|
import { deployments } from './deployment'
|
||||||
import { teams } from './team'
|
import { teams } from './team'
|
||||||
|
import { type Webhook, webhookSchema } from './types'
|
||||||
import { users } from './user'
|
import { users } from './user'
|
||||||
import {
|
import {
|
||||||
createInsertSchema,
|
createInsertSchema,
|
||||||
|
@ -149,15 +150,30 @@ export const projectInsertSchema = createInsertSchema(projects, {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
export const projectSelectSchema = createSelectSchema(projects).omit({
|
export const projectSelectSchema = createSelectSchema(projects, {
|
||||||
_secret: true,
|
_webhooks: z.array(webhookSchema),
|
||||||
_providerToken: true,
|
stripeMetricProductIds: z.record(z.string(), z.string()).optional(),
|
||||||
_text: true,
|
_stripeCouponIds: z.record(z.string(), z.string()).optional(),
|
||||||
_webhooks: true,
|
_stripePlanIds: z
|
||||||
_stripeCouponIds: true,
|
.record(
|
||||||
_stripePlanIds: true,
|
z.string(),
|
||||||
_stripeAccountId: true
|
z.object({
|
||||||
|
basePlanId: z.string(),
|
||||||
|
requestPlanId: z.string()
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.optional()
|
||||||
})
|
})
|
||||||
|
.omit({
|
||||||
|
_secret: true,
|
||||||
|
_providerToken: true,
|
||||||
|
_text: true,
|
||||||
|
_webhooks: true,
|
||||||
|
_stripeCouponIds: true,
|
||||||
|
_stripePlanIds: true,
|
||||||
|
_stripeAccountId: true
|
||||||
|
})
|
||||||
|
.openapi('Project')
|
||||||
|
|
||||||
// TODO: narrow update schema
|
// TODO: narrow update schema
|
||||||
export const projectUpdateSchema = createUpdateSchema(projects)
|
export const projectUpdateSchema = createUpdateSchema(projects)
|
||||||
|
|
|
@ -9,7 +9,12 @@ import {
|
||||||
|
|
||||||
import { teams } from './team'
|
import { teams } from './team'
|
||||||
import { users } from './user'
|
import { users } from './user'
|
||||||
import { cuid, teamMemberRoleEnum, timestamps } from './utils'
|
import {
|
||||||
|
createSelectSchema,
|
||||||
|
cuid,
|
||||||
|
teamMemberRoleEnum,
|
||||||
|
timestamps
|
||||||
|
} from './utils'
|
||||||
|
|
||||||
export const teamMembers = pgTable(
|
export const teamMembers = pgTable(
|
||||||
'team_members',
|
'team_members',
|
||||||
|
@ -25,7 +30,7 @@ export const teamMembers = pgTable(
|
||||||
role: teamMemberRoleEnum().default('user').notNull(),
|
role: teamMemberRoleEnum().default('user').notNull(),
|
||||||
|
|
||||||
confirmed: boolean().default(false),
|
confirmed: boolean().default(false),
|
||||||
confirmedAt: timestamp()
|
confirmedAt: timestamp({ mode: 'string' })
|
||||||
},
|
},
|
||||||
(table) => [
|
(table) => [
|
||||||
primaryKey({ columns: [table.userId, table.teamId] }),
|
primaryKey({ columns: [table.userId, table.teamId] }),
|
||||||
|
@ -46,3 +51,6 @@ export const teamMembersRelations = relations(teamMembers, ({ one }) => ({
|
||||||
references: [teams.id]
|
references: [teams.id]
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
export const teamMemberSelectSchema =
|
||||||
|
createSelectSchema(teamMembers).openapi('TeamMember')
|
||||||
|
|
|
@ -41,5 +41,5 @@ export const teamsRelations = relations(teams, ({ one, many }) => ({
|
||||||
export const teamInsertSchema = createInsertSchema(teams, {
|
export const teamInsertSchema = createInsertSchema(teams, {
|
||||||
slug: (schema) => schema.min(3).max(20) // TODO
|
slug: (schema) => schema.min(3).max(20) // TODO
|
||||||
})
|
})
|
||||||
export const teamSelectSchema = createSelectSchema(teams)
|
export const teamSelectSchema = createSelectSchema(teams).openapi('Team')
|
||||||
export const teamUpdateSchema = createUpdateSchema(teams).omit({ slug: true })
|
export const teamUpdateSchema = createUpdateSchema(teams).omit({ slug: true })
|
||||||
|
|
|
@ -1,146 +1,161 @@
|
||||||
export type AuthProviderType =
|
import { z } from '@hono/zod-openapi'
|
||||||
| 'github'
|
|
||||||
| 'google'
|
|
||||||
| 'spotify'
|
|
||||||
| 'twitter'
|
|
||||||
| 'linkedin'
|
|
||||||
| 'stripe'
|
|
||||||
|
|
||||||
export type AuthProvider = {
|
export const authProviderTypeSchema = z
|
||||||
provider: AuthProviderType
|
.enum(['github', 'google', 'spotify', 'twitter', 'linkedin', 'stripe'])
|
||||||
|
.openapi('AuthProviderType')
|
||||||
|
export type AuthProviderType = z.infer<typeof authProviderTypeSchema>
|
||||||
|
|
||||||
/** Provider-specific user id */
|
export const authProviderSchema = z
|
||||||
id: string
|
.object({
|
||||||
|
provider: authProviderTypeSchema,
|
||||||
|
|
||||||
/** Provider-specific username */
|
/** Provider-specific user id */
|
||||||
username?: string
|
id: z.string(),
|
||||||
|
|
||||||
/** Standard oauth2 access token */
|
/** Provider-specific username */
|
||||||
accessToken?: string
|
username: z.string().optional(),
|
||||||
|
|
||||||
/** Standard oauth2 refresh token */
|
/** Standard oauth2 access token */
|
||||||
refreshToken?: string
|
accessToken: z.string().optional(),
|
||||||
|
|
||||||
/** Stripe public key */
|
/** Standard oauth2 refresh token */
|
||||||
publicKey?: string
|
refreshToken: z.string().optional(),
|
||||||
|
|
||||||
/** OAuth scope(s) */
|
/** Stripe public key */
|
||||||
scope?: string
|
publicKey: z.string().optional(),
|
||||||
}
|
|
||||||
|
|
||||||
export type AuthProviders = {
|
/** OAuth scope(s) */
|
||||||
github?: AuthProvider
|
scope: z.string().optional()
|
||||||
google?: AuthProvider
|
})
|
||||||
spotify?: AuthProvider
|
.openapi('AuthProvider')
|
||||||
twitter?: AuthProvider
|
export type AuthProvider = z.infer<typeof authProviderSchema>
|
||||||
linkedin?: AuthProvider
|
|
||||||
stripeTest?: AuthProvider
|
|
||||||
stripeLive?: AuthProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
export type Webhook = {
|
export const authProvidersSchema = z
|
||||||
url: string
|
.record(authProviderTypeSchema, authProviderSchema)
|
||||||
events: string[]
|
.openapi('AuthProviders')
|
||||||
}
|
export type AuthProviders = z.infer<typeof authProvidersSchema>
|
||||||
|
|
||||||
export type RateLimit = {
|
export const webhookSchema = z
|
||||||
enabled: boolean
|
.object({
|
||||||
|
url: z.string(),
|
||||||
|
events: z.array(z.string())
|
||||||
|
})
|
||||||
|
.openapi('Webhook')
|
||||||
|
export type Webhook = z.infer<typeof webhookSchema>
|
||||||
|
|
||||||
// informal description that overrides any other properties
|
export const rateLimitSchema = z
|
||||||
desc?: string
|
.object({
|
||||||
|
enabled: z.boolean(),
|
||||||
|
|
||||||
interval: number // seconds
|
// informal description that overrides any other properties
|
||||||
maxPerInterval: number // unitless
|
desc: z.string().optional(),
|
||||||
}
|
|
||||||
|
|
||||||
export type PricingPlanTier = {
|
interval: z.number(), // seconds
|
||||||
unitAmount?: number
|
maxPerInterval: z.number() // unitless
|
||||||
flatAmount?: number
|
})
|
||||||
upTo: string
|
.openapi('RateLimit')
|
||||||
} & (
|
export type RateLimit = z.infer<typeof rateLimitSchema>
|
||||||
| {
|
|
||||||
unitAmount: number
|
export const pricingPlanTierSchema = z
|
||||||
|
.object({
|
||||||
|
unitAmount: z.number().optional(),
|
||||||
|
flatAmount: z.number().optional(),
|
||||||
|
upTo: z.string()
|
||||||
|
})
|
||||||
|
.refine(
|
||||||
|
(data) =>
|
||||||
|
(data.unitAmount !== undefined) !== (data.flatAmount !== undefined),
|
||||||
|
{
|
||||||
|
message: 'Either unitAmount or flatAmount must be provided, but not both'
|
||||||
}
|
}
|
||||||
| {
|
)
|
||||||
flatAmount: number
|
.openapi('PricingPlanTier')
|
||||||
}
|
export type PricingPlanTier = z.infer<typeof pricingPlanTierSchema>
|
||||||
)
|
|
||||||
|
|
||||||
export type PricingPlanMetric = {
|
export const pricingPlanMetricSchema = z
|
||||||
// slug acts as a primary key for metrics
|
.object({
|
||||||
slug: string
|
// slug acts as a primary key for metrics
|
||||||
|
slug: z.string(),
|
||||||
|
|
||||||
amount: number
|
amount: z.number(),
|
||||||
|
|
||||||
label: string
|
label: z.string(),
|
||||||
unitLabel: string
|
unitLabel: z.string(),
|
||||||
|
|
||||||
// TODO: should this default be 'licensed' or 'metered'?
|
// TODO: should this default be 'licensed' or 'metered'?
|
||||||
// methinks licensed for "sites", "jobs", etc...
|
// methinks licensed for "sites", "jobs", etc...
|
||||||
// TODO: this should probably be explicit since its easy to confuse
|
// TODO: this should probably be explicit since its easy to confuse
|
||||||
usageType: 'licensed' | 'metered'
|
usageType: z.enum(['licensed', 'metered']),
|
||||||
|
|
||||||
billingScheme: 'per_unit' | 'tiered'
|
billingScheme: z.enum(['per_unit', 'tiered']),
|
||||||
|
|
||||||
tiersMode: 'graduated' | 'volume'
|
tiersMode: z.enum(['graduated', 'volume']),
|
||||||
tiers: PricingPlanTier[]
|
tiers: z.array(pricingPlanTierSchema),
|
||||||
|
|
||||||
// TODO (low priority): add aggregateUsage
|
// TODO (low priority): add aggregateUsage
|
||||||
|
|
||||||
rateLimit?: RateLimit
|
rateLimit: rateLimitSchema.optional()
|
||||||
}
|
})
|
||||||
|
.openapi('PricingPlanMetric')
|
||||||
|
export type PricingPlanMetric = z.infer<typeof pricingPlanMetricSchema>
|
||||||
|
|
||||||
export type PricingPlan = {
|
export const pricingPlanSchema = z
|
||||||
name: string
|
.object({
|
||||||
slug: string
|
name: z.string(),
|
||||||
|
slug: z.string(),
|
||||||
|
|
||||||
desc?: string
|
desc: z.string().optional(),
|
||||||
features: string[]
|
features: z.array(z.string()),
|
||||||
|
|
||||||
auth: boolean
|
auth: z.boolean(),
|
||||||
amount: number
|
amount: z.number(),
|
||||||
trialPeriodDays?: number
|
trialPeriodDays: z.number().optional(),
|
||||||
|
|
||||||
requests: PricingPlanMetric
|
requests: pricingPlanMetricSchema,
|
||||||
metrics: PricingPlanMetric[]
|
metrics: z.array(pricingPlanMetricSchema),
|
||||||
|
|
||||||
rateLimit?: RateLimit
|
rateLimit: rateLimitSchema.optional(),
|
||||||
|
|
||||||
// used to uniquely identify this plan across deployments
|
// used to uniquely identify this plan across deployments
|
||||||
baseId: string
|
baseId: z.string(),
|
||||||
|
|
||||||
// used to uniquely identify this plan across deployments
|
// used to uniquely identify this plan across deployments
|
||||||
requestsId: string
|
requestsId: z.string(),
|
||||||
|
|
||||||
// [metricSlug: string]: string
|
// [metricSlug: string]: string
|
||||||
metricIds: Record<string, string>
|
metricIds: z.record(z.string()),
|
||||||
|
|
||||||
// NOTE: the stripe billing plan id(s) for this PricingPlan are referenced
|
// NOTE: the stripe billing plan id(s) for this PricingPlan are referenced
|
||||||
// in the Project._stripePlans mapping via the plan's hash.
|
// in the Project._stripePlans mapping via the plan's hash.
|
||||||
// NOTE: all metered billing usage is stored in stripe
|
// NOTE: all metered billing usage is stored in stripe
|
||||||
stripeBasePlan: string
|
stripeBasePlan: z.string(),
|
||||||
stripeRequestPlan: string
|
stripeRequestPlan: z.string(),
|
||||||
|
|
||||||
// [metricSlug: string]: string
|
// [metricSlug: string]: string
|
||||||
stripeMetricPlans: Record<string, string>
|
stripeMetricPlans: z.record(z.string())
|
||||||
}
|
})
|
||||||
|
.openapi('PricingPlan')
|
||||||
|
export type PricingPlan = z.infer<typeof pricingPlanSchema>
|
||||||
|
|
||||||
export type Coupon = {
|
export const couponSchema = z
|
||||||
// used to uniquely identify this coupon across deployments
|
.object({
|
||||||
id: string
|
// used to uniquely identify this coupon across deployments
|
||||||
|
id: z.string(),
|
||||||
|
|
||||||
valid: boolean
|
valid: z.boolean(),
|
||||||
stripeCoupon: string
|
stripeCoupon: z.string(),
|
||||||
|
|
||||||
name?: string
|
name: z.string().optional(),
|
||||||
|
|
||||||
currency?: string
|
currency: z.string().optional(),
|
||||||
amount_off?: number
|
amount_off: z.number().optional(),
|
||||||
percent_off?: number
|
percent_off: z.number().optional(),
|
||||||
|
|
||||||
duration: string
|
duration: z.string(),
|
||||||
duration_in_months?: number
|
duration_in_months: z.number().optional(),
|
||||||
|
|
||||||
redeem_by?: Date
|
redeem_by: z.date().optional(),
|
||||||
max_redemptions?: number
|
max_redemptions: z.number().optional()
|
||||||
}
|
})
|
||||||
|
.openapi('Coupon')
|
||||||
|
export type Coupon = z.infer<typeof couponSchema>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { validators } from '@agentic/faas-utils'
|
import { validators } from '@agentic/validators'
|
||||||
import { relations } from 'drizzle-orm'
|
import { relations } from 'drizzle-orm'
|
||||||
import {
|
import {
|
||||||
boolean,
|
boolean,
|
||||||
|
@ -12,8 +12,8 @@ import {
|
||||||
|
|
||||||
import { sha256 } from '@/lib/utils'
|
import { sha256 } from '@/lib/utils'
|
||||||
|
|
||||||
import type { AuthProviders } from './types'
|
|
||||||
import { teams } from './team'
|
import { teams } from './team'
|
||||||
|
import { type AuthProviders, authProvidersSchema } from './types'
|
||||||
import {
|
import {
|
||||||
createInsertSchema,
|
createInsertSchema,
|
||||||
createSelectSchema,
|
createSelectSchema,
|
||||||
|
@ -42,7 +42,7 @@ export const users = pgTable(
|
||||||
image: text(),
|
image: text(),
|
||||||
|
|
||||||
emailConfirmed: boolean().default(false),
|
emailConfirmed: boolean().default(false),
|
||||||
emailConfirmedAt: timestamp(),
|
emailConfirmedAt: timestamp({ mode: 'string' }),
|
||||||
emailConfirmToken: text().unique().default(sha256()),
|
emailConfirmToken: text().unique().default(sha256()),
|
||||||
passwordResetToken: text().unique(),
|
passwordResetToken: text().unique(),
|
||||||
|
|
||||||
|
@ -85,7 +85,9 @@ export const userInsertSchema = createInsertSchema(users, {
|
||||||
{
|
{
|
||||||
message: 'Invalid email'
|
message: 'Invalid email'
|
||||||
}
|
}
|
||||||
)
|
),
|
||||||
|
|
||||||
|
providers: authProvidersSchema.optional()
|
||||||
}).pick({
|
}).pick({
|
||||||
username: true,
|
username: true,
|
||||||
email: true,
|
email: true,
|
||||||
|
@ -95,5 +97,8 @@ export const userInsertSchema = createInsertSchema(users, {
|
||||||
image: true
|
image: true
|
||||||
})
|
})
|
||||||
|
|
||||||
export const userSelectSchema = createSelectSchema(users)
|
export const userSelectSchema = createSelectSchema(users, {
|
||||||
|
providers: authProvidersSchema.optional()
|
||||||
|
}).openapi('User')
|
||||||
|
|
||||||
export const userUpdateSchema = createUpdateSchema(users)
|
export const userUpdateSchema = createUpdateSchema(users)
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
import { createSchemaFactory } from '@fisch0920/drizzle-zod'
|
||||||
|
import { z } from '@hono/zod-openapi'
|
||||||
import { createId } from '@paralleldrive/cuid2'
|
import { createId } from '@paralleldrive/cuid2'
|
||||||
import { sql, type Writable } from 'drizzle-orm'
|
import { sql, type Writable } from 'drizzle-orm'
|
||||||
import {
|
import {
|
||||||
|
@ -7,7 +9,6 @@ import {
|
||||||
timestamp,
|
timestamp,
|
||||||
varchar
|
varchar
|
||||||
} from 'drizzle-orm/pg-core'
|
} from 'drizzle-orm/pg-core'
|
||||||
import { createSchemaFactory } from 'drizzle-zod'
|
|
||||||
|
|
||||||
export function cuid<U extends string, T extends Readonly<[U, ...U[]]>>(
|
export function cuid<U extends string, T extends Readonly<[U, ...U[]]>>(
|
||||||
config?: PgVarcharConfig<T | Writable<T>, never>
|
config?: PgVarcharConfig<T | Writable<T>, never>
|
||||||
|
@ -44,8 +45,8 @@ export const id = varchar('id', { length: 24 })
|
||||||
.$defaultFn(createId)
|
.$defaultFn(createId)
|
||||||
|
|
||||||
export const timestamps = {
|
export const timestamps = {
|
||||||
createdAt: timestamp('createdAt').notNull().defaultNow(),
|
createdAt: timestamp('createdAt', { mode: 'string' }).notNull().defaultNow(),
|
||||||
updatedAt: timestamp('updatedAt')
|
updatedAt: timestamp('updatedAt', { mode: 'string' })
|
||||||
.notNull()
|
.notNull()
|
||||||
.default(sql`now()`)
|
.default(sql`now()`)
|
||||||
}
|
}
|
||||||
|
@ -53,8 +54,31 @@ export const timestamps = {
|
||||||
export const userRoleEnum = pgEnum('UserRole', ['user', 'admin'])
|
export const userRoleEnum = pgEnum('UserRole', ['user', 'admin'])
|
||||||
export const teamMemberRoleEnum = pgEnum('TeamMemberRole', ['user', 'admin'])
|
export const teamMemberRoleEnum = pgEnum('TeamMemberRole', ['user', 'admin'])
|
||||||
|
|
||||||
|
// TODO: Currently unused after forking drizzle-zod.
|
||||||
|
// export function makeNullablePropsOptional<Schema extends z.AnyZodObject>(
|
||||||
|
// schema: Schema
|
||||||
|
// ): z.ZodObject<{
|
||||||
|
// [key in keyof Schema['shape']]: Schema['shape'][key] extends z.ZodNullable<
|
||||||
|
// infer T
|
||||||
|
// >
|
||||||
|
// ? z.ZodOptional<T>
|
||||||
|
// : Schema['shape'][key]
|
||||||
|
// }> {
|
||||||
|
// const entries = Object.entries(schema.shape)
|
||||||
|
// const newProps: any = {}
|
||||||
|
|
||||||
|
// for (const [key, value] of entries) {
|
||||||
|
// newProps[key] =
|
||||||
|
// value instanceof z.ZodNullable ? value.unwrap().optional() : value
|
||||||
|
// return newProps
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return z.object(newProps) as any
|
||||||
|
// }
|
||||||
|
|
||||||
export const { createInsertSchema, createSelectSchema, createUpdateSchema } =
|
export const { createInsertSchema, createSelectSchema, createUpdateSchema } =
|
||||||
createSchemaFactory({
|
createSchemaFactory({
|
||||||
|
zodInstance: z,
|
||||||
coerce: {
|
coerce: {
|
||||||
// Coerce dates / strings to timetamps
|
// Coerce dates / strings to timetamps
|
||||||
date: true
|
date: true
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
import { validators } from '@agentic/validators'
|
||||||
|
import { z } from '@hono/zod-openapi'
|
||||||
|
|
||||||
|
function getCuidSchema(idLabel: string) {
|
||||||
|
return z.string().refine((id) => validators.cuid(id), {
|
||||||
|
message: `Invalid ${idLabel}`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const cuidSchema = getCuidSchema('id')
|
||||||
|
export const userIdSchema = getCuidSchema('user id')
|
||||||
|
|
||||||
|
export const projectIdSchema = z
|
||||||
|
.string()
|
||||||
|
.refine((id) => validators.project(id), {
|
||||||
|
message: 'Invalid project id'
|
||||||
|
})
|
||||||
|
|
||||||
|
export const deploymentIdSchema = z
|
||||||
|
.string()
|
||||||
|
.refine((id) => validators.deployment(id), {
|
||||||
|
message: 'Invalid deployment id'
|
||||||
|
})
|
|
@ -1,19 +1,20 @@
|
||||||
|
import type { z } from '@hono/zod-openapi'
|
||||||
import type { BuildQueryResult, ExtractTablesWithRelations } from 'drizzle-orm'
|
import type { BuildQueryResult, ExtractTablesWithRelations } from 'drizzle-orm'
|
||||||
|
|
||||||
import type * as schema from './schema'
|
import type * as schema from './schema'
|
||||||
|
|
||||||
export type Tables = ExtractTablesWithRelations<typeof schema>
|
export type Tables = ExtractTablesWithRelations<typeof schema>
|
||||||
|
|
||||||
export type User = typeof schema.users.$inferSelect
|
export type User = z.infer<typeof schema.userSelectSchema>
|
||||||
|
|
||||||
export type Team = typeof schema.teams.$inferSelect
|
export type Team = z.infer<typeof schema.teamSelectSchema>
|
||||||
export type TeamWithMembers = BuildQueryResult<
|
export type TeamWithMembers = BuildQueryResult<
|
||||||
Tables,
|
Tables,
|
||||||
Tables['teams'],
|
Tables['teams'],
|
||||||
{ with: { members: true } }
|
{ with: { members: true } }
|
||||||
>
|
>
|
||||||
|
|
||||||
export type TeamMember = typeof schema.teamMembers.$inferSelect
|
export type TeamMember = z.infer<typeof schema.teamMemberSelectSchema>
|
||||||
export type TeamMemberWithTeam = BuildQueryResult<
|
export type TeamMemberWithTeam = BuildQueryResult<
|
||||||
Tables,
|
Tables,
|
||||||
Tables['teamMembers'],
|
Tables['teamMembers'],
|
||||||
|
|
|
@ -20,7 +20,11 @@ export function initExitHooks({
|
||||||
// Gracefully shutdown the HTTP server
|
// Gracefully shutdown the HTTP server
|
||||||
asyncExitHook(
|
asyncExitHook(
|
||||||
async function shutdownServerExitHook() {
|
async function shutdownServerExitHook() {
|
||||||
await promisify(server.close)()
|
try {
|
||||||
|
await promisify(server.close)()
|
||||||
|
} catch {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
wait: timeoutMs
|
wait: timeoutMs
|
||||||
|
@ -30,9 +34,13 @@ export function initExitHooks({
|
||||||
// Gracefully shutdown the postgres database connection
|
// Gracefully shutdown the postgres database connection
|
||||||
asyncExitHook(
|
asyncExitHook(
|
||||||
async function shutdownDbExitHook() {
|
async function shutdownDbExitHook() {
|
||||||
await db.$client.end({
|
try {
|
||||||
timeout: timeoutMs
|
await db.$client.end({
|
||||||
})
|
timeout: timeoutMs
|
||||||
|
})
|
||||||
|
} catch {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
wait: timeoutMs
|
wait: timeoutMs
|
||||||
|
|
|
@ -2,7 +2,7 @@ import '@/lib/instrument'
|
||||||
|
|
||||||
import { serve } from '@hono/node-server'
|
import { serve } from '@hono/node-server'
|
||||||
import { sentry } from '@hono/sentry'
|
import { sentry } from '@hono/sentry'
|
||||||
import { Hono } from 'hono'
|
import { OpenAPIHono } from '@hono/zod-openapi'
|
||||||
import { compress } from 'hono/compress'
|
import { compress } from 'hono/compress'
|
||||||
import { cors } from 'hono/cors'
|
import { cors } from 'hono/cors'
|
||||||
|
|
||||||
|
@ -12,20 +12,26 @@ import * as middleware from '@/lib/middleware'
|
||||||
|
|
||||||
import { initExitHooks } from './lib/exit-hooks'
|
import { initExitHooks } from './lib/exit-hooks'
|
||||||
|
|
||||||
export const app = new Hono()
|
export const app = new OpenAPIHono()
|
||||||
|
|
||||||
app.use(sentry())
|
app.use(sentry())
|
||||||
app.use(compress())
|
app.use(compress())
|
||||||
app.use(middleware.accessLogger)
|
// app.use(middleware.accessLogger)
|
||||||
app.use(middleware.responseTime)
|
app.use(middleware.responseTime)
|
||||||
app.use(middleware.errorHandler)
|
app.use(middleware.errorHandler)
|
||||||
app.use(cors())
|
app.use(cors())
|
||||||
|
|
||||||
app.route('/v1', apiV1)
|
app.route('/v1', apiV1)
|
||||||
|
|
||||||
|
app.doc31('/docs', {
|
||||||
|
openapi: '3.1.0',
|
||||||
|
info: { title: 'Agentic', version: '1.0.0' }
|
||||||
|
})
|
||||||
|
|
||||||
const server = serve({
|
const server = serve({
|
||||||
fetch: app.fetch,
|
fetch: app.fetch,
|
||||||
port: env.PORT
|
port: env.PORT
|
||||||
})
|
})
|
||||||
|
console.log(`Server running on port ${env.PORT}`)
|
||||||
|
|
||||||
initExitHooks({ server })
|
initExitHooks({ server })
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{
|
{
|
||||||
"name": "@agentic/faas-utils",
|
"name": "@agentic/validators",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "Agentic platform FaaS utils.",
|
"description": "Validation utils for the Agentic platform.",
|
||||||
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
"author": "Travis Fischer <travis@transitivebullsh.it>",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git",
|
"url": "git+https://github.com/transitive-bullshit/agentic-platform.git",
|
||||||
"directory": "packages/faas-utils"
|
"directory": "packages/validators"
|
||||||
},
|
},
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"source": "./src/index.ts",
|
"source": "./src/index.ts",
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
"test:unit": "vitest run"
|
"test:unit": "vitest run"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@paralleldrive/cuid2": "^2.2.2",
|
||||||
"email-validator": "^2.0.4",
|
"email-validator": "^2.0.4",
|
||||||
"is-relative-url": "^4.0.0"
|
"is-relative-url": "^4.0.0"
|
||||||
}
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { isCuid } from '@paralleldrive/cuid2'
|
||||||
import emailValidator from 'email-validator'
|
import emailValidator from 'email-validator'
|
||||||
import isRelativeUrl from 'is-relative-url'
|
import isRelativeUrl from 'is-relative-url'
|
||||||
|
|
||||||
|
@ -50,3 +51,7 @@ export function serviceName(value: string): boolean {
|
||||||
export function servicePath(value: string): boolean {
|
export function servicePath(value: string): boolean {
|
||||||
return !!value && servicePathRe.test(value) && isRelativeUrl(value)
|
return !!value && servicePathRe.test(value) && isRelativeUrl(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function cuid(value: string): boolean {
|
||||||
|
return !!value && isCuid(value)
|
||||||
|
}
|
|
@ -125,9 +125,12 @@ importers:
|
||||||
|
|
||||||
apps/api:
|
apps/api:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@agentic/faas-utils':
|
'@agentic/validators':
|
||||||
specifier: workspace:*
|
specifier: workspace:*
|
||||||
version: link:../../packages/faas-utils
|
version: link:../../packages/validators
|
||||||
|
'@fisch0920/drizzle-zod':
|
||||||
|
specifier: ^0.7.2
|
||||||
|
version: 0.7.2(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5))(zod@3.24.3)
|
||||||
'@google-cloud/logging':
|
'@google-cloud/logging':
|
||||||
specifier: ^11.2.0
|
specifier: ^11.2.0
|
||||||
version: 11.2.0
|
version: 11.2.0
|
||||||
|
@ -137,6 +140,9 @@ importers:
|
||||||
'@hono/sentry':
|
'@hono/sentry':
|
||||||
specifier: ^1.2.1
|
specifier: ^1.2.1
|
||||||
version: 1.2.1(hono@4.7.7)
|
version: 1.2.1(hono@4.7.7)
|
||||||
|
'@hono/zod-openapi':
|
||||||
|
specifier: ^0.19.5
|
||||||
|
version: 0.19.5(hono@4.7.7)(zod@3.24.3)
|
||||||
'@hono/zod-validator':
|
'@hono/zod-validator':
|
||||||
specifier: ^0.4.3
|
specifier: ^0.4.3
|
||||||
version: 0.4.3(hono@4.7.7)(zod@3.24.3)
|
version: 0.4.3(hono@4.7.7)(zod@3.24.3)
|
||||||
|
@ -148,13 +154,10 @@ importers:
|
||||||
version: 9.14.0
|
version: 9.14.0
|
||||||
'@workos-inc/node':
|
'@workos-inc/node':
|
||||||
specifier: ^7.47.0
|
specifier: ^7.47.0
|
||||||
version: 7.47.0
|
version: 7.48.0
|
||||||
drizzle-orm:
|
drizzle-orm:
|
||||||
specifier: ^0.43.0
|
specifier: ^0.43.0
|
||||||
version: 0.43.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5)
|
version: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5)
|
||||||
drizzle-zod:
|
|
||||||
specifier: ^0.7.1
|
|
||||||
version: 0.7.1(drizzle-orm@0.43.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5))(zod@3.24.3)
|
|
||||||
eventid:
|
eventid:
|
||||||
specifier: ^2.0.1
|
specifier: ^2.0.1
|
||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
|
@ -196,8 +199,11 @@ importers:
|
||||||
specifier: ^0.31.0
|
specifier: ^0.31.0
|
||||||
version: 0.31.0
|
version: 0.31.0
|
||||||
|
|
||||||
packages/faas-utils:
|
packages/validators:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@paralleldrive/cuid2':
|
||||||
|
specifier: ^2.2.2
|
||||||
|
version: 2.2.2
|
||||||
email-validator:
|
email-validator:
|
||||||
specifier: ^2.0.4
|
specifier: ^2.0.4
|
||||||
version: 2.0.4
|
version: 2.0.4
|
||||||
|
@ -207,6 +213,11 @@ importers:
|
||||||
|
|
||||||
packages:
|
packages:
|
||||||
|
|
||||||
|
'@asteasolutions/zod-to-openapi@7.3.0':
|
||||||
|
resolution: {integrity: sha512-7tE/r1gXwMIvGnXVUdIqUhCU1RevEFC4Jk6Bussa0fk1ecbnnINkZzj1EOAJyE/M3AI25DnHT/zKQL1/FPFi8Q==}
|
||||||
|
peerDependencies:
|
||||||
|
zod: ^3.20.2
|
||||||
|
|
||||||
'@babel/code-frame@7.26.2':
|
'@babel/code-frame@7.26.2':
|
||||||
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
|
resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
|
@ -558,6 +569,12 @@ packages:
|
||||||
prettier: '>= 3'
|
prettier: '>= 3'
|
||||||
typescript: '>= 5'
|
typescript: '>= 5'
|
||||||
|
|
||||||
|
'@fisch0920/drizzle-zod@0.7.2':
|
||||||
|
resolution: {integrity: sha512-f+ltSymKaD14RsZKiSBMoYsbxFrlKYHiybIDkDTPVUMrMQgpjdNleeYvjmyV2h47n73WxCQHF6K/4sTH8/GuTA==}
|
||||||
|
peerDependencies:
|
||||||
|
drizzle-orm: '>=0.36.0'
|
||||||
|
zod: '>=3.0.0'
|
||||||
|
|
||||||
'@google-cloud/common@5.0.2':
|
'@google-cloud/common@5.0.2':
|
||||||
resolution: {integrity: sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==}
|
resolution: {integrity: sha512-V7bmBKYQyu0eVG2BFejuUjlBt+zrya6vtsKdY+JxMM/dNntPF41vZ9+LhOshEUH01zOHEqBSvI7Dad7ZS6aUeA==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
|
@ -598,6 +615,13 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
hono: '>=3.*'
|
hono: '>=3.*'
|
||||||
|
|
||||||
|
'@hono/zod-openapi@0.19.5':
|
||||||
|
resolution: {integrity: sha512-n2RqdZL7XIaWPwBNygctG/1eySyRtSBnS7l+pIsP3f2JW5P2l7Smm6SLluscrGwB5l2C2fxbfvhWoC6Ig+SxXw==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
peerDependencies:
|
||||||
|
hono: '>=4.3.6'
|
||||||
|
zod: 3.*
|
||||||
|
|
||||||
'@hono/zod-validator@0.4.3':
|
'@hono/zod-validator@0.4.3':
|
||||||
resolution: {integrity: sha512-xIgMYXDyJ4Hj6ekm9T9Y27s080Nl9NXHcJkOvkXPhubOLj8hZkOL8pDnnXfvCf5xEE8Q4oMFenQUZZREUY2gqQ==}
|
resolution: {integrity: sha512-xIgMYXDyJ4Hj6ekm9T9Y27s080Nl9NXHcJkOvkXPhubOLj8hZkOL8pDnnXfvCf5xEE8Q4oMFenQUZZREUY2gqQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1257,8 +1281,8 @@ packages:
|
||||||
'@vitest/utils@3.1.2':
|
'@vitest/utils@3.1.2':
|
||||||
resolution: {integrity: sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==}
|
resolution: {integrity: sha512-5GGd0ytZ7BH3H6JTj9Kw7Prn1Nbg0wZVrIvou+UWxm54d+WoXXgAgjFJ8wn3LdagWLFSEfpPeyYrByZaGEZHLg==}
|
||||||
|
|
||||||
'@workos-inc/node@7.47.0':
|
'@workos-inc/node@7.48.0':
|
||||||
resolution: {integrity: sha512-A7K6uIIGmD5qbrLxoXah2BcOzbxyxnrhps+crZ/CFImrkq4CdVAm6BV/wvRaTeAinRF+Ea9cIFFeEAvJ++RaSw==}
|
resolution: {integrity: sha512-dS0wpf8MqezcPsYcEbpW4eLcXZ7wOh0X9Qne4VVumrDTnC60kuxpMWIUAH1iPjXlocd8LKg13aYeP7mHSLVeCw==}
|
||||||
engines: {node: '>=16'}
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
abort-controller@3.0.0:
|
abort-controller@3.0.0:
|
||||||
|
@ -1616,8 +1640,8 @@ packages:
|
||||||
resolution: {integrity: sha512-pcKVT+GbfPA+bUovPIilgVOoq+onNBo/YQBG86sf3/GFHkN6lRJPm1l7dKN0IMAk57RQoIm4GUllRrasLlcaSg==}
|
resolution: {integrity: sha512-pcKVT+GbfPA+bUovPIilgVOoq+onNBo/YQBG86sf3/GFHkN6lRJPm1l7dKN0IMAk57RQoIm4GUllRrasLlcaSg==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
drizzle-orm@0.43.0:
|
drizzle-orm@0.43.1:
|
||||||
resolution: {integrity: sha512-OF6ZOtpGJs3CNXHGwKLfP+mYXEzTnXNL/WRXgAGR+SrtPl6quIBbTPEQZNQ6HhVQchMmJeaezBIcpFBpJD3x+g==}
|
resolution: {integrity: sha512-dUcDaZtE/zN4RV/xqGrVSMpnEczxd5cIaoDeor7Zst9wOe/HzC/7eAaulywWGYXdDEc9oBPMjayVEDg0ziTLJA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@aws-sdk/client-rds-data': '>=3'
|
'@aws-sdk/client-rds-data': '>=3'
|
||||||
'@cloudflare/workers-types': '>=4'
|
'@cloudflare/workers-types': '>=4'
|
||||||
|
@ -1705,12 +1729,6 @@ packages:
|
||||||
sqlite3:
|
sqlite3:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
drizzle-zod@0.7.1:
|
|
||||||
resolution: {integrity: sha512-nZzALOdz44/AL2U005UlmMqaQ1qe5JfanvLujiTHiiT8+vZJTBFhj3pY4Vk+L6UWyKFfNmLhk602Hn4kCTynKQ==}
|
|
||||||
peerDependencies:
|
|
||||||
drizzle-orm: '>=0.36.0'
|
|
||||||
zod: '>=3.0.0'
|
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
@ -2733,6 +2751,9 @@ packages:
|
||||||
resolution: {integrity: sha512-M7CJbmv7UCopc0neRKdzfoGWaVZC+xC1925GitKH9EAqYFzX9//25Q7oX4+jw0tiCCj+t5l6VZh8UPH23NZkMA==}
|
resolution: {integrity: sha512-M7CJbmv7UCopc0neRKdzfoGWaVZC+xC1925GitKH9EAqYFzX9//25Q7oX4+jw0tiCCj+t5l6VZh8UPH23NZkMA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
openapi3-ts@4.4.0:
|
||||||
|
resolution: {integrity: sha512-9asTNB9IkKEzWMcHmVZE7Ts3kC9G7AFHfs8i7caD8HbI76gEjdkId4z/AkP83xdZsH7PLAnnbl47qZkXuxpArw==}
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
|
@ -2808,8 +2829,8 @@ packages:
|
||||||
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
|
resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|
||||||
pg-pool@3.9.5:
|
pg-pool@3.9.6:
|
||||||
resolution: {integrity: sha512-DxyAlOgvUzRFpFAZjbCc8fUfG7BcETDHgepFPf724B0i08k9PAiZV1tkGGgQIL0jbMEuR9jW1YN7eX+WgXxCsQ==}
|
resolution: {integrity: sha512-rFen0G7adh1YmgvrmE5IPIqbb+IgEzENUm+tzm6MLLDSlPRoZVhzU1WdML9PV2W5GOdRA9qBKURlbt1OsXOsPw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
pg: '>=8.0'
|
pg: '>=8.0'
|
||||||
|
|
||||||
|
@ -3703,6 +3724,11 @@ packages:
|
||||||
|
|
||||||
snapshots:
|
snapshots:
|
||||||
|
|
||||||
|
'@asteasolutions/zod-to-openapi@7.3.0(zod@3.24.3)':
|
||||||
|
dependencies:
|
||||||
|
openapi3-ts: 4.4.0
|
||||||
|
zod: 3.24.3
|
||||||
|
|
||||||
'@babel/code-frame@7.26.2':
|
'@babel/code-frame@7.26.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/helper-validator-identifier': 7.25.9
|
'@babel/helper-validator-identifier': 7.25.9
|
||||||
|
@ -3941,6 +3967,11 @@ snapshots:
|
||||||
- supports-color
|
- supports-color
|
||||||
- vitest
|
- vitest
|
||||||
|
|
||||||
|
'@fisch0920/drizzle-zod@0.7.2(drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5))(zod@3.24.3)':
|
||||||
|
dependencies:
|
||||||
|
drizzle-orm: 0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5)
|
||||||
|
zod: 3.24.3
|
||||||
|
|
||||||
'@google-cloud/common@5.0.2':
|
'@google-cloud/common@5.0.2':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@google-cloud/projectify': 4.0.0
|
'@google-cloud/projectify': 4.0.0
|
||||||
|
@ -4008,6 +4039,13 @@ snapshots:
|
||||||
hono: 4.7.7
|
hono: 4.7.7
|
||||||
toucan-js: 4.1.1
|
toucan-js: 4.1.1
|
||||||
|
|
||||||
|
'@hono/zod-openapi@0.19.5(hono@4.7.7)(zod@3.24.3)':
|
||||||
|
dependencies:
|
||||||
|
'@asteasolutions/zod-to-openapi': 7.3.0(zod@3.24.3)
|
||||||
|
'@hono/zod-validator': 0.4.3(hono@4.7.7)(zod@3.24.3)
|
||||||
|
hono: 4.7.7
|
||||||
|
zod: 3.24.3
|
||||||
|
|
||||||
'@hono/zod-validator@0.4.3(hono@4.7.7)(zod@3.24.3)':
|
'@hono/zod-validator@0.4.3(hono@4.7.7)(zod@3.24.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
hono: 4.7.7
|
hono: 4.7.7
|
||||||
|
@ -4769,7 +4807,7 @@ snapshots:
|
||||||
loupe: 3.1.3
|
loupe: 3.1.3
|
||||||
tinyrainbow: 2.0.0
|
tinyrainbow: 2.0.0
|
||||||
|
|
||||||
'@workos-inc/node@7.47.0':
|
'@workos-inc/node@7.48.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
iron-session: 6.3.1
|
iron-session: 6.3.1
|
||||||
jose: 5.6.3
|
jose: 5.6.3
|
||||||
|
@ -5140,18 +5178,13 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
drizzle-orm@0.43.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5):
|
drizzle-orm@0.43.1(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@opentelemetry/api': 1.9.0
|
'@opentelemetry/api': 1.9.0
|
||||||
'@types/pg': 8.11.13
|
'@types/pg': 8.11.13
|
||||||
pg: 8.15.5
|
pg: 8.15.5
|
||||||
postgres: 3.4.5
|
postgres: 3.4.5
|
||||||
|
|
||||||
drizzle-zod@0.7.1(drizzle-orm@0.43.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5))(zod@3.24.3):
|
|
||||||
dependencies:
|
|
||||||
drizzle-orm: 0.43.0(@opentelemetry/api@1.9.0)(@types/pg@8.11.13)(pg@8.15.5)(postgres@3.4.5)
|
|
||||||
zod: 3.24.3
|
|
||||||
|
|
||||||
dunder-proto@1.0.1:
|
dunder-proto@1.0.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
call-bind-apply-helpers: 1.0.2
|
call-bind-apply-helpers: 1.0.2
|
||||||
|
@ -6381,6 +6414,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
which-pm-runs: 1.1.0
|
which-pm-runs: 1.1.0
|
||||||
|
|
||||||
|
openapi3-ts@4.4.0:
|
||||||
|
dependencies:
|
||||||
|
yaml: 2.7.1
|
||||||
|
|
||||||
optionator@0.9.4:
|
optionator@0.9.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
deep-is: 0.1.4
|
deep-is: 0.1.4
|
||||||
|
@ -6447,7 +6484,7 @@ snapshots:
|
||||||
|
|
||||||
pg-numeric@1.0.2: {}
|
pg-numeric@1.0.2: {}
|
||||||
|
|
||||||
pg-pool@3.9.5(pg@8.15.5):
|
pg-pool@3.9.6(pg@8.15.5):
|
||||||
dependencies:
|
dependencies:
|
||||||
pg: 8.15.5
|
pg: 8.15.5
|
||||||
optional: true
|
optional: true
|
||||||
|
@ -6475,7 +6512,7 @@ snapshots:
|
||||||
pg@8.15.5:
|
pg@8.15.5:
|
||||||
dependencies:
|
dependencies:
|
||||||
pg-connection-string: 2.8.5
|
pg-connection-string: 2.8.5
|
||||||
pg-pool: 3.9.5(pg@8.15.5)
|
pg-pool: 3.9.6(pg@8.15.5)
|
||||||
pg-protocol: 1.9.5
|
pg-protocol: 1.9.5
|
||||||
pg-types: 2.2.0
|
pg-types: 2.2.0
|
||||||
pgpass: 1.0.5
|
pgpass: 1.0.5
|
||||||
|
|
Ładowanie…
Reference in New Issue