kopia lustrzana https://github.com/transitive-bullshit/chatgpt-api
feat: WIP stripe billing refactor update for 2025
rodzic
f55ba7f060
commit
6ac5b3d589
|
@ -48,6 +48,7 @@
|
|||
"bcryptjs": "^3.0.2",
|
||||
"eventid": "^2.0.1",
|
||||
"exit-hook": "catalog:",
|
||||
"hash-object": "^5.0.1",
|
||||
"hono": "^4.7.9",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"p-all": "^5.0.0",
|
||||
|
|
|
@ -15,7 +15,7 @@ const route = createRoute({
|
|||
description: 'Updates a project.',
|
||||
tags: ['projects'],
|
||||
operationId: 'updateProject',
|
||||
method: 'put',
|
||||
method: 'post',
|
||||
path: 'projects/{projectId}',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
|
|
|
@ -17,7 +17,7 @@ const route = createRoute({
|
|||
description: 'Updates a team member.',
|
||||
tags: ['teams'],
|
||||
operationId: 'updateTeamMember',
|
||||
method: 'put',
|
||||
method: 'post',
|
||||
path: 'teams/{team}/members/{userId}',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
|
|
|
@ -16,7 +16,7 @@ const route = createRoute({
|
|||
description: 'Updates a team.',
|
||||
tags: ['teams'],
|
||||
operationId: 'updateTeam',
|
||||
method: 'put',
|
||||
method: 'post',
|
||||
path: 'teams/{team}',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
|
|
|
@ -16,7 +16,7 @@ const route = createRoute({
|
|||
description: 'Updates a user',
|
||||
tags: ['users'],
|
||||
operationId: 'updateUser',
|
||||
method: 'put',
|
||||
method: 'post',
|
||||
path: 'users/{userId}',
|
||||
security: openapiAuthenticatedSecuritySchemas,
|
||||
request: {
|
||||
|
|
|
@ -40,8 +40,8 @@ export function registerV1StripeWebhook(app: OpenAPIHono) {
|
|||
})
|
||||
}
|
||||
|
||||
// Shouldn't ever happen because the signatures should be different, but it's
|
||||
// a useful sanity check just in case.
|
||||
// Shouldn't ever happen because the signatures _should_ be different, but
|
||||
// it's a useful sanity check just in case.
|
||||
assert(
|
||||
event.livemode === isStripeLive,
|
||||
400,
|
||||
|
@ -89,7 +89,8 @@ export function registerV1StripeWebhook(app: OpenAPIHono) {
|
|||
await db
|
||||
.update(schema.consumers)
|
||||
.set({
|
||||
stripeStatus: consumer.stripeStatus
|
||||
stripeStatus: consumer.stripeStatus,
|
||||
enabled: consumer.enabled
|
||||
})
|
||||
.where(eq(schema.consumers.id, consumer.id))
|
||||
|
||||
|
|
|
@ -31,10 +31,10 @@ import {
|
|||
*
|
||||
* Consumers are used to track usage and billing for a project.
|
||||
*
|
||||
* Consumers are linked to a corresponding Stripe Customer. The Stripe customer
|
||||
* will either be the user's default Stripe Customer for the platform account,
|
||||
* or a customer on the project's connected Stripe account if the project has
|
||||
* Stripe Connect enabled.
|
||||
* Consumers are linked to a corresponding Stripe Customer and Subscription.
|
||||
* The Stripe customer will either be the user's default Stripe Customer for
|
||||
* the platform account, or a customer on the project's connected Stripe
|
||||
* account if the project has Stripe Connect enabled.
|
||||
*/
|
||||
export const consumers = pgTable(
|
||||
'consumers',
|
||||
|
|
|
@ -12,10 +12,10 @@ import { z } from '@hono/zod-openapi'
|
|||
import { projects } from './project'
|
||||
import { teams, teamSelectSchema } from './team'
|
||||
import {
|
||||
type Coupon,
|
||||
couponSchema,
|
||||
type PricingPlan,
|
||||
pricingPlanSchema
|
||||
// type Coupon,
|
||||
// couponSchema,
|
||||
type PricingPlanMapByInterval,
|
||||
pricingPlanMapByIntervalSchema
|
||||
} from './types'
|
||||
import { users, userSelectSchema } from './user'
|
||||
import {
|
||||
|
@ -54,13 +54,9 @@ export const deployments = pgTable(
|
|||
onDelete: 'cascade'
|
||||
}),
|
||||
|
||||
// TODO: tools?
|
||||
// TODO: Tool definitions or OpenAPI spec
|
||||
// services: jsonb().$type<Service[]>().default([]),
|
||||
|
||||
// TODO: Environment variables & secrets
|
||||
// build: jsonb().$type<object>(),
|
||||
// env: jsonb().$type<object>(),
|
||||
|
||||
// TODO: metadata config (logo, keywords, etc)
|
||||
// TODO: webhooks
|
||||
// TODO: third-party auth provider config?
|
||||
|
@ -68,8 +64,13 @@ export const deployments = pgTable(
|
|||
// Backend API URL
|
||||
_url: text().notNull(),
|
||||
|
||||
pricingPlans: jsonb().$type<PricingPlan[]>().notNull(),
|
||||
coupons: jsonb().$type<Coupon[]>().default([]).notNull()
|
||||
// NOTE: this does not have a default value and must be given a value at creation.
|
||||
// Record<PricingInterval, Record<string, PricingPlan>>
|
||||
pricingPlanMapByInterval: jsonb()
|
||||
.$type<PricingPlanMapByInterval>()
|
||||
.notNull()
|
||||
|
||||
// coupons: jsonb().$type<Coupon[]>().default([]).notNull()
|
||||
},
|
||||
(table) => [
|
||||
index('deployment_userId_idx').on(table.userId),
|
||||
|
@ -108,8 +109,9 @@ export const deploymentsRelations = relations(deployments, ({ one }) => ({
|
|||
export const deploymentSelectSchema = createSelectSchema(deployments, {
|
||||
// build: z.object({}),
|
||||
// env: z.object({}),
|
||||
pricingPlans: z.array(pricingPlanSchema),
|
||||
coupons: z.array(couponSchema)
|
||||
|
||||
pricingPlanMapByInterval: pricingPlanMapByIntervalSchema
|
||||
// coupons: z.array(couponSchema)
|
||||
})
|
||||
.omit({
|
||||
_url: true
|
||||
|
@ -141,10 +143,12 @@ export const deploymentInsertSchema = createInsertSchema(deployments, {
|
|||
|
||||
_url: (schema) => schema.url(),
|
||||
|
||||
// build: z.object({}),
|
||||
// env: z.object({}),
|
||||
pricingPlans: z.array(pricingPlanSchema),
|
||||
coupons: z.array(couponSchema).optional()
|
||||
// TODO: should this public resource be decoupled from the internal pricing
|
||||
// plan structure?
|
||||
pricingPlanMapByInterval: pricingPlanMapByIntervalSchema
|
||||
|
||||
// TODO
|
||||
// coupons: z.array(couponSchema).optional()
|
||||
})
|
||||
.omit({ id: true, createdAt: true, updatedAt: true })
|
||||
.strict()
|
||||
|
|
|
@ -12,7 +12,16 @@ import { z } from '@hono/zod-openapi'
|
|||
|
||||
import { deployments, deploymentSelectSchema } from './deployment'
|
||||
import { teams, teamSelectSchema } from './team'
|
||||
import { type Webhook } from './types'
|
||||
import {
|
||||
pricingIntervalSchema,
|
||||
type StripeMeterIdMap,
|
||||
stripeMeterIdMapSchema,
|
||||
type StripePriceIdMap,
|
||||
stripePriceIdMapSchema,
|
||||
type StripeProductIdMap,
|
||||
stripeProductIdMapSchema,
|
||||
type Webhook
|
||||
} from './types'
|
||||
import { users, userSelectSchema } from './user'
|
||||
import {
|
||||
createInsertSchema,
|
||||
|
@ -20,6 +29,8 @@ import {
|
|||
createUpdateSchema,
|
||||
cuid,
|
||||
deploymentId,
|
||||
pricingCurrencyEnum,
|
||||
pricingIntervalEnum,
|
||||
projectId,
|
||||
stripeId,
|
||||
timestamps
|
||||
|
@ -51,6 +62,18 @@ export const projects = pgTable(
|
|||
// TODO: This is going to need to vary from dev to prod
|
||||
isStripeConnectEnabled: boolean().default(false).notNull(),
|
||||
|
||||
// Which pricing intervals are supported for subscriptions to this project
|
||||
pricingIntervals: pricingIntervalEnum()
|
||||
.array()
|
||||
.default(['month'])
|
||||
.notNull(),
|
||||
|
||||
// Default pricing interval for subscriptions to this project
|
||||
defaultPricingInterval: pricingIntervalEnum().default('month').notNull(),
|
||||
|
||||
// Pricing currency used across all prices and subscriptions to this project
|
||||
pricingCurrency: pricingCurrencyEnum().default('usd').notNull(),
|
||||
|
||||
// All deployments share the same underlying proxy secret
|
||||
_secret: text().notNull(),
|
||||
|
||||
|
@ -62,41 +85,45 @@ export const projects = pgTable(
|
|||
|
||||
_webhooks: jsonb().$type<Webhook[]>().default([]).notNull(),
|
||||
|
||||
// Stripe products corresponding to the stripe plans across deployments
|
||||
stripeBaseProductId: stripeId(),
|
||||
stripeRequestProductId: stripeId(),
|
||||
|
||||
// Map between metric slugs and stripe product ids
|
||||
// [metricSlug: string]: string
|
||||
stripeMetricProductIds: jsonb()
|
||||
.$type<Record<string, string>>()
|
||||
.default({})
|
||||
.notNull(),
|
||||
// TODO: currency?
|
||||
|
||||
// Stripe coupons associated with this project, mapping from unique coupon
|
||||
// hash to stripe coupon id.
|
||||
// object hash to stripe coupon id.
|
||||
// `[hash: string]: string`
|
||||
_stripeCouponIds: jsonb()
|
||||
.$type<Record<string, string>>()
|
||||
// _stripeCouponsMap: jsonb()
|
||||
// .$type<Record<string, string>>()
|
||||
// .default({})
|
||||
// .notNull(),
|
||||
|
||||
// Stripe billing Products associated with this project across deployments,
|
||||
// mapping from PricingPlanMetric **slug** to Stripe Product id.
|
||||
// NOTE: This map uses slugs as keys, unlike `_stripePriceIdMap`, because
|
||||
// Stripe Products are agnostic to the PricingPlanMetric config. This is
|
||||
// important for them to be shared across deployments even if the pricing
|
||||
// details change.
|
||||
_stripeProductIdMap: jsonb()
|
||||
.$type<StripeProductIdMap>()
|
||||
.default({})
|
||||
.notNull(),
|
||||
|
||||
// Stripe billing plans associated with this project (created lazily),
|
||||
// mapping from unique plan hash to stripe plan ids for base and request
|
||||
// respectively.
|
||||
// `[hash: string]: { basePlanId: string, requestPlanId: string }`
|
||||
_stripePlanIds: jsonb()
|
||||
.$type<Record<string, { basePlanId: string; requestPlanId: string }>>()
|
||||
.default({})
|
||||
.notNull(),
|
||||
// Stripe billing Prices associated with this project, mapping from unique
|
||||
// PricingPlanMetric **hash** to Stripe Price id.
|
||||
// NOTE: This map uses hashes as keys, because Stripe Prices are dependent
|
||||
// on the PricingPlanMetric config. This is important for them to be shared
|
||||
// across deployments even if the pricing details change.
|
||||
_stripePriceIdMap: jsonb().$type<StripePriceIdMap>().default({}).notNull(),
|
||||
|
||||
// Stripe billing Metrics associated with this project, mapping from unique
|
||||
// PricingPlanMetric **slug** to Stripe Meter id.
|
||||
// NOTE: This map uses slugs as keys, unlike `_stripePriceIdMap`, because
|
||||
// Stripe Products are agnostic to the PricingPlanMetric config. This is
|
||||
// important for them to be shared across deployments even if the pricing
|
||||
// details change.
|
||||
_stripeMeterIdMap: jsonb().$type<StripeMeterIdMap>().default({}).notNull(),
|
||||
|
||||
// Connected Stripe account (standard or express).
|
||||
// If not defined, then subscriptions for this project route through our
|
||||
// main Stripe account.
|
||||
// NOTE: the connected account is shared between dev and prod, so we're not using
|
||||
// the stripeID utility.
|
||||
// TODO: is it wise to share this between dev and prod?
|
||||
// TODO: is it okay for this to be public?
|
||||
_stripeAccountId: stripeId()
|
||||
},
|
||||
(table) => [
|
||||
|
@ -149,26 +176,21 @@ export const projectRelationsSchema: z.ZodType<ProjectRelationFields> = z.enum([
|
|||
export const projectSelectSchema = createSelectSchema(projects, {
|
||||
applicationFeePercent: (schema) => schema.nonnegative(),
|
||||
|
||||
stripeMetricProductIds: z.record(z.string(), z.string()).optional()
|
||||
// _webhooks: z.array(webhookSchema),
|
||||
// _stripeCouponIds: z.record(z.string(), z.string()).optional(),
|
||||
// _stripePlanIds: z
|
||||
// .record(
|
||||
// z.string(),
|
||||
// z.object({
|
||||
// basePlanId: z.string(),
|
||||
// requestPlanId: z.string()
|
||||
// })
|
||||
// )
|
||||
// .optional()
|
||||
_stripeProductIdMap: stripeProductIdMapSchema,
|
||||
_stripePriceIdMap: stripePriceIdMapSchema,
|
||||
_stripeMeterIdMap: stripeMeterIdMapSchema,
|
||||
|
||||
pricingIntervals: z.array(pricingIntervalSchema).optional(),
|
||||
defaultPricingInterval: pricingIntervalSchema.optional()
|
||||
})
|
||||
.omit({
|
||||
_secret: true,
|
||||
_providerToken: true,
|
||||
_text: true,
|
||||
_webhooks: true,
|
||||
_stripeCouponIds: true,
|
||||
_stripePlanIds: true,
|
||||
_stripeProductIdMap: true,
|
||||
_stripePriceIdMap: true,
|
||||
_stripeMeterIdMap: true,
|
||||
_stripeAccountId: true
|
||||
})
|
||||
.extend({
|
||||
|
@ -229,7 +251,9 @@ export const projectDebugSelectSchema = createSelectSchema(projects).pick({
|
|||
updatedAt: true,
|
||||
isStripeConnectEnabled: true,
|
||||
lastPublishedDeploymentId: true,
|
||||
lastDeploymentId: true
|
||||
lastDeploymentId: true,
|
||||
pricingIntervals: true,
|
||||
defaultPricingInterval: true
|
||||
})
|
||||
|
||||
// TODO: virtual saasUrl
|
||||
|
|
|
@ -61,11 +61,11 @@ export const rateLimitSchema = z
|
|||
.object({
|
||||
enabled: z.boolean(),
|
||||
|
||||
// informal description that overrides any other properties
|
||||
desc: z.string().optional(),
|
||||
|
||||
interval: z.number(), // seconds
|
||||
maxPerInterval: z.number() // unitless
|
||||
maxPerInterval: z.number(), // unitless
|
||||
|
||||
// informal description that overrides any other properties
|
||||
desc: z.string().optional()
|
||||
})
|
||||
.openapi('RateLimit')
|
||||
export type RateLimit = z.infer<typeof rateLimitSchema>
|
||||
|
@ -86,91 +86,152 @@ export const pricingPlanTierSchema = z
|
|||
.openapi('PricingPlanTier')
|
||||
export type PricingPlanTier = z.infer<typeof pricingPlanTierSchema>
|
||||
|
||||
export const pricingIntervalSchema = z
|
||||
.enum(['day', 'week', 'month', 'year'])
|
||||
.openapi('PricingInterval')
|
||||
export type PricingInterval = z.infer<typeof pricingIntervalSchema>
|
||||
|
||||
export const pricingPlanMetricHashSchema = z
|
||||
.string()
|
||||
.nonempty()
|
||||
.describe('Internal PricingPlanMetric hash')
|
||||
|
||||
export const pricingPlanMetricSlugSchema = z
|
||||
.string()
|
||||
.nonempty()
|
||||
.describe('PricingPlanMetric slug')
|
||||
|
||||
export const stripePriceIdMapSchema = z
|
||||
.record(pricingPlanMetricHashSchema, z.string().describe('Stripe Price id'))
|
||||
.describe('Map from internal PricingPlanMetric **hash** to Stripe Price id')
|
||||
.openapi('StripePriceIdMap')
|
||||
export type StripePriceIdMap = z.infer<typeof stripePriceIdMapSchema>
|
||||
|
||||
export const stripeMeterIdMapSchema = z
|
||||
.record(pricingPlanMetricHashSchema, z.string().describe('Stripe Meter id'))
|
||||
.describe('Map from internal PricingPlanMetric **slug** to Stripe Meter id')
|
||||
.openapi('StripeMeterIdMap')
|
||||
export type StripeMeterIdMap = z.infer<typeof stripeMeterIdMapSchema>
|
||||
|
||||
const commonPricingPlanMetricSchema = z.object({
|
||||
/**
|
||||
* Slugs act as the primary key for metrics. They should be lower and
|
||||
* kebab-cased ("base", "requests", "image-transformations").
|
||||
*/
|
||||
slug: z.union([z.string(), z.literal('base'), z.literal('requests')]),
|
||||
|
||||
interval: pricingIntervalSchema,
|
||||
|
||||
label: z.string().optional().openapi('label', { example: 'API calls' }),
|
||||
|
||||
rateLimit: rateLimitSchema.optional(),
|
||||
|
||||
stripePriceId: z.string().optional()
|
||||
})
|
||||
|
||||
export const pricingPlanMetricSchema = z
|
||||
.object({
|
||||
// slug acts as a primary key for metrics
|
||||
slug: z.string(),
|
||||
.discriminatedUnion('usageType', [
|
||||
commonPricingPlanMetricSchema.merge(
|
||||
z.object({
|
||||
usageType: z.literal('licensed'),
|
||||
amount: z.number()
|
||||
})
|
||||
),
|
||||
|
||||
amount: z.number(),
|
||||
commonPricingPlanMetricSchema.merge(
|
||||
z.object({
|
||||
usageType: z.literal('metered'),
|
||||
unitLabel: z.string().optional(),
|
||||
|
||||
label: z.string(),
|
||||
unitLabel: z.string(),
|
||||
billingScheme: z.enum(['per_unit', 'tiered']),
|
||||
|
||||
// TODO: should this default be 'licensed' or 'metered'?
|
||||
// methinks licensed for "sites", "jobs", etc...
|
||||
// TODO: this should probably be explicit since its easy to confuse
|
||||
usageType: z.enum(['licensed', 'metered']),
|
||||
// Only applicable for `per_unit` billing schemes
|
||||
amount: z.number().optional(),
|
||||
|
||||
billingScheme: z.enum(['per_unit', 'tiered']),
|
||||
// Only applicable for `tiered` billing schemes
|
||||
tiersMode: z.enum(['graduated', 'volume']).optional(),
|
||||
tiers: z.array(pricingPlanTierSchema).optional(),
|
||||
|
||||
tiersMode: z.enum(['graduated', 'volume']),
|
||||
tiers: z.array(pricingPlanTierSchema),
|
||||
// TODO: add support for tiered rate limits?
|
||||
|
||||
// TODO (low priority): add aggregateUsage
|
||||
defaultAggregation: z
|
||||
.object({
|
||||
formula: z.enum(['sum', 'count', 'last'])
|
||||
})
|
||||
.optional(),
|
||||
|
||||
rateLimit: rateLimitSchema.optional()
|
||||
})
|
||||
// Stripe metric id, which is created lazily upon first use.
|
||||
stripeMetricId: z.string().optional()
|
||||
})
|
||||
)
|
||||
])
|
||||
.describe('Stripe billing Price and possibly corresponding Metric')
|
||||
.openapi('PricingPlanMetric')
|
||||
export type PricingPlanMetric = z.infer<typeof pricingPlanMetricSchema>
|
||||
|
||||
export const pricingPlanSchema = z
|
||||
.object({
|
||||
name: z.string(),
|
||||
slug: z.string(),
|
||||
name: z.string().openapi('name', { example: 'Starter Monthly' }),
|
||||
slug: z.string().openapi('slug', { example: 'starter-monthly' }),
|
||||
|
||||
desc: z.string().optional(),
|
||||
features: z.array(z.string()),
|
||||
|
||||
auth: z.boolean(),
|
||||
amount: z.number(),
|
||||
interval: pricingIntervalSchema,
|
||||
|
||||
trialPeriodDays: z.number().optional(),
|
||||
|
||||
requests: pricingPlanMetricSchema,
|
||||
metrics: z.array(pricingPlanMetricSchema),
|
||||
|
||||
rateLimit: rateLimitSchema.optional(),
|
||||
|
||||
// used to uniquely identify this pricing plan across deployments
|
||||
baseId: z.string(),
|
||||
|
||||
// used to uniquely identify this pricing plan across deployments
|
||||
requestsId: z.string(),
|
||||
|
||||
// [metricSlug: string]: string
|
||||
metricIds: z.record(z.string()),
|
||||
|
||||
// NOTE: the stripe billing plan id(s) for this PricingPlan are referenced
|
||||
// in the Project._stripePlans mapping via the plan's hash.
|
||||
// NOTE: all metered billing usage is stored in stripe
|
||||
stripeBasePlanId: z.string(),
|
||||
stripeRequestPlanId: z.string(),
|
||||
|
||||
// Record mapping metric slugs to stripe plan IDs
|
||||
// [metricSlug: string]: string
|
||||
stripeMetricPlans: z.record(z.string())
|
||||
metricsMap: z
|
||||
.record(pricingPlanMetricSlugSchema, pricingPlanMetricSchema)
|
||||
.refine((metricsMap) => {
|
||||
// Stripe Checkout currently supports a max of 20 line items per
|
||||
// subscription.
|
||||
return Object.keys(metricsMap).length <= 20
|
||||
})
|
||||
.default({})
|
||||
})
|
||||
.openapi('PricingPlan')
|
||||
export type PricingPlan = z.infer<typeof pricingPlanSchema>
|
||||
|
||||
export const couponSchema = z
|
||||
.object({
|
||||
// used to uniquely identify this coupon across deployments
|
||||
id: z.string(),
|
||||
export const stripeProductIdMapSchema = z
|
||||
.record(pricingPlanMetricSlugSchema, z.string().describe('Stripe Product id'))
|
||||
.describe('Map from PricingPlanMetric **slug** to Stripe Product id')
|
||||
.openapi('StripeProductIdMap')
|
||||
export type StripeProductIdMap = z.infer<typeof stripeProductIdMapSchema>
|
||||
|
||||
valid: z.boolean(),
|
||||
stripeCoupon: z.string(),
|
||||
export const pricingPlanMapBySlugSchema = z
|
||||
.record(z.string().describe('PricingPlan slug'), pricingPlanSchema)
|
||||
.describe('Map from PricingPlan slug to PricingPlan')
|
||||
export type PricingPlanMapBySlug = z.infer<typeof pricingPlanMapBySlugSchema>
|
||||
|
||||
name: z.string().optional(),
|
||||
export const pricingPlanMapByIntervalSchema = z
|
||||
.record(pricingIntervalSchema, pricingPlanMapBySlugSchema)
|
||||
.describe(
|
||||
'Map from PricingInterval to a map from PricingPlan slug to PricingPlan'
|
||||
)
|
||||
export type PricingPlanMapByInterval = z.infer<
|
||||
typeof pricingPlanMapByIntervalSchema
|
||||
>
|
||||
|
||||
currency: z.string().optional(),
|
||||
amount_off: z.number().optional(),
|
||||
percent_off: z.number().optional(),
|
||||
// export const couponSchema = z
|
||||
// .object({
|
||||
// // used to uniquely identify this coupon across deployments
|
||||
// id: z.string(),
|
||||
|
||||
duration: z.string(),
|
||||
duration_in_months: z.number().optional(),
|
||||
// valid: z.boolean(),
|
||||
// stripeCoupon: z.string(),
|
||||
|
||||
redeem_by: z.date().optional(),
|
||||
max_redemptions: z.number().optional()
|
||||
})
|
||||
.openapi('Coupon')
|
||||
export type Coupon = z.infer<typeof couponSchema>
|
||||
// name: z.string().optional(),
|
||||
|
||||
// currency: z.string().optional(),
|
||||
// amount_off: z.number().optional(),
|
||||
// percent_off: z.number().optional(),
|
||||
|
||||
// duration: z.string(),
|
||||
// duration_in_months: z.number().optional(),
|
||||
|
||||
// redeem_by: z.date().optional(),
|
||||
// max_redemptions: z.number().optional()
|
||||
// })
|
||||
// .openapi('Coupon')
|
||||
// export type Coupon = z.infer<typeof couponSchema>
|
||||
|
|
|
@ -13,6 +13,11 @@ import { createSchemaFactory } from '@fisch0920/drizzle-zod'
|
|||
import { z } from '@hono/zod-openapi'
|
||||
import { createId } from '@paralleldrive/cuid2'
|
||||
|
||||
import { hashObject, omit } from '@/lib/utils'
|
||||
|
||||
import type { RawProject } from '../types'
|
||||
import type { PricingPlanMetric } from './types'
|
||||
|
||||
const usernameAndTeamSlugLength = 64 as const
|
||||
|
||||
/**
|
||||
|
@ -100,6 +105,13 @@ export const logEntryLevelEnum = pgEnum('LogEntryLevel', [
|
|||
'warn',
|
||||
'error'
|
||||
])
|
||||
export const pricingIntervalEnum = pgEnum('PricingInterval', [
|
||||
'day',
|
||||
'week',
|
||||
'month',
|
||||
'year'
|
||||
])
|
||||
export const pricingCurrencyEnum = pgEnum('PricingCurrency', ['usd'])
|
||||
|
||||
export const { createInsertSchema, createSelectSchema, createUpdateSchema } =
|
||||
createSchemaFactory({
|
||||
|
@ -109,3 +121,47 @@ export const { createInsertSchema, createSelectSchema, createUpdateSchema } =
|
|||
date: true
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Gets the hash used to uniquely map a PricingPlanMetric to its corresponding
|
||||
* Stripe Price in a stable way across deployments within a project.
|
||||
*
|
||||
* This hash is used as the key for the `Project._stripePriceIdMap`.
|
||||
*/
|
||||
export function getPricingPlanMetricHashForStripePrice({
|
||||
pricingPlanMetric,
|
||||
project
|
||||
}: {
|
||||
pricingPlanMetric: PricingPlanMetric
|
||||
project: RawProject
|
||||
}) {
|
||||
const hash = hashObject({
|
||||
...omit(pricingPlanMetric, 'stripePriceId', 'stripeMetricId'),
|
||||
projectId: project.id,
|
||||
stripeAccountId: project._stripeAccountId
|
||||
})
|
||||
|
||||
return `price:${pricingPlanMetric.slug}:${hash}`
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the hash used to uniquely map a PricingPlanMetric to its corresponding
|
||||
* Stripe Meter in a stable way across deployments within a project.
|
||||
*
|
||||
* This hash is used as the key for the `Project._stripePriceIdMap`.
|
||||
*/
|
||||
export function getPricingPlanMetricHashForStripeMeter({
|
||||
pricingPlanMetric,
|
||||
project
|
||||
}: {
|
||||
pricingPlanMetric: PricingPlanMetric
|
||||
project: RawProject
|
||||
}) {
|
||||
const hash = hashObject({
|
||||
...omit(pricingPlanMetric, 'stripePriceId', 'stripeMetricId'),
|
||||
projectId: project.id,
|
||||
stripeAccountId: project._stripeAccountId
|
||||
})
|
||||
|
||||
return `price:${pricingPlanMetric.slug}:${hash}`
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import { db, eq, type RawConsumer, type RawProject, schema } from '@/db'
|
|||
import { stripe } from '@/lib/stripe'
|
||||
import { assert } from '@/lib/utils'
|
||||
|
||||
// TODO: Update this for the new / updated Stripe Connect API
|
||||
|
||||
export async function upsertStripeConnectCustomer({
|
||||
stripeCustomer,
|
||||
consumer,
|
||||
|
|
|
@ -1,254 +0,0 @@
|
|||
import type Stripe from 'stripe'
|
||||
import pAll from 'p-all'
|
||||
|
||||
import type { PricingPlan, PricingPlanMetric } from '@/db/schema'
|
||||
import { db, eq, type RawDeployment, type RawProject, schema } from '@/db'
|
||||
import { stripe } from '@/lib/stripe'
|
||||
import { assert } from '@/lib/utils'
|
||||
|
||||
// TODO: move these to config
|
||||
const currency = 'usd'
|
||||
const interval = 'month'
|
||||
|
||||
export async function upsertStripePricingPlans({
|
||||
deployment,
|
||||
project
|
||||
}: {
|
||||
deployment: RawDeployment
|
||||
project: RawProject
|
||||
}): Promise<void> {
|
||||
const stripeConnectParams = project._stripeAccountId
|
||||
? [
|
||||
{
|
||||
stripeAccount: project._stripeAccountId
|
||||
}
|
||||
]
|
||||
: []
|
||||
let dirty = false
|
||||
|
||||
async function upsertStripeBaseProduct() {
|
||||
if (!project.stripeBaseProductId) {
|
||||
const product = await stripe.products.create(
|
||||
{
|
||||
name: `${project.id} base`,
|
||||
type: 'service'
|
||||
},
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
project.stripeBaseProductId = product.id
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
async function upsertStripeRequestProduct() {
|
||||
if (!project.stripeRequestProductId) {
|
||||
const product = await stripe.products.create(
|
||||
{
|
||||
name: `${project.id} requests`,
|
||||
type: 'service',
|
||||
unit_label: 'request'
|
||||
},
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
project.stripeRequestProductId = product.id
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
async function upsertStripeMetricProduct(metric: PricingPlanMetric) {
|
||||
const { slug: metricSlug } = metric
|
||||
|
||||
if (!project.stripeMetricProductIds[metricSlug]) {
|
||||
const product = await stripe.products.create(
|
||||
{
|
||||
name: `${project.id} ${metricSlug}`,
|
||||
type: 'service',
|
||||
unit_label: metric.unitLabel
|
||||
},
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
project.stripeMetricProductIds[metricSlug] = product.id
|
||||
dirty = true
|
||||
}
|
||||
}
|
||||
|
||||
async function upsertStripeBasePlan(pricingPlan: PricingPlan) {
|
||||
if (!pricingPlan.stripeBasePlanId) {
|
||||
const hash = pricingPlan.baseId
|
||||
const stripePlan = project._stripePlanIds[hash]
|
||||
assert(stripePlan, 400, 'Missing stripe base plan')
|
||||
|
||||
pricingPlan.stripeBasePlanId = stripePlan.basePlanId
|
||||
dirty = true
|
||||
|
||||
if (!pricingPlan.stripeBasePlanId) {
|
||||
const stripePlan = await stripe.plans.create(
|
||||
{
|
||||
product: project.stripeBaseProductId,
|
||||
currency,
|
||||
interval,
|
||||
amount_decimal: pricingPlan.amount.toFixed(12),
|
||||
nickname: `${project.id}-${pricingPlan.slug}-base`
|
||||
},
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
pricingPlan.stripeBasePlanId = stripePlan.id
|
||||
project._stripePlanIds[hash]!.basePlanId = stripePlan.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function upsertStripeRequestPlan(pricingPlan: PricingPlan) {
|
||||
const { requests } = pricingPlan
|
||||
|
||||
if (!pricingPlan.stripeRequestPlanId) {
|
||||
const hash = pricingPlan.requestsId
|
||||
const projectStripePlan = project._stripePlanIds[hash]
|
||||
assert(projectStripePlan, 400, 'Missing stripe request plan')
|
||||
|
||||
pricingPlan.stripeRequestPlanId = projectStripePlan.requestPlanId
|
||||
dirty = true
|
||||
|
||||
if (!pricingPlan.stripeRequestPlanId) {
|
||||
const planParams: Stripe.PlanCreateParams = {
|
||||
product: project.stripeRequestProductId,
|
||||
currency,
|
||||
interval,
|
||||
usage_type: 'metered',
|
||||
billing_scheme: requests.billingScheme,
|
||||
nickname: `${project.id}-${pricingPlan.slug}-requests`
|
||||
}
|
||||
|
||||
if (requests.billingScheme === 'tiered') {
|
||||
planParams.tiers_mode = requests.tiersMode
|
||||
planParams.tiers = requests.tiers.map((tier) => {
|
||||
const result: Stripe.PlanCreateParams.Tier = {
|
||||
up_to: tier.upTo
|
||||
}
|
||||
|
||||
if (tier.unitAmount !== undefined) {
|
||||
result.unit_amount_decimal = tier.unitAmount.toFixed(12)
|
||||
}
|
||||
|
||||
if (tier.flatAmount !== undefined) {
|
||||
result.flat_amount_decimal = tier.flatAmount.toFixed(12)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
} else {
|
||||
planParams.amount_decimal = requests.amount.toFixed(12)
|
||||
}
|
||||
|
||||
const stripePlan = await stripe.plans.create(
|
||||
planParams,
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
pricingPlan.stripeRequestPlanId = stripePlan.id
|
||||
projectStripePlan.requestPlanId = stripePlan.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function upsertStripeMetricPlan(
|
||||
pricingPlan: PricingPlan,
|
||||
metric: PricingPlanMetric
|
||||
) {
|
||||
const { slug: metricSlug } = metric
|
||||
|
||||
if (!pricingPlan.stripeMetricPlans[metricSlug]) {
|
||||
const hash = pricingPlan.metricIds[metricSlug]
|
||||
assert(hash, 500, `Missing stripe metric "${metricSlug}"`)
|
||||
|
||||
const projectStripePlan = project._stripePlanIds[hash]
|
||||
assert(projectStripePlan, 500, 'Missing stripe request plan')
|
||||
|
||||
// TODO: is this right? differs from original source
|
||||
pricingPlan.stripeMetricPlans[metricSlug] = projectStripePlan.basePlanId
|
||||
dirty = true
|
||||
|
||||
if (!pricingPlan.stripeMetricPlans[metricSlug]) {
|
||||
const stripeProductId = project.stripeMetricProductIds[metricSlug]
|
||||
assert(
|
||||
stripeProductId,
|
||||
500,
|
||||
`Missing stripe product ID for metric "${metricSlug}"`
|
||||
)
|
||||
|
||||
const planParams: Stripe.PlanCreateParams = {
|
||||
product: stripeProductId,
|
||||
currency,
|
||||
interval,
|
||||
usage_type: metric.usageType,
|
||||
billing_scheme: metric.billingScheme,
|
||||
nickname: `${project.id}-${pricingPlan.slug}-${metricSlug}`
|
||||
}
|
||||
|
||||
if (metric.billingScheme === 'tiered') {
|
||||
planParams.tiers_mode = metric.tiersMode
|
||||
planParams.tiers = metric.tiers.map((tier) => {
|
||||
const result: Stripe.PlanCreateParams.Tier = {
|
||||
up_to: tier.upTo
|
||||
}
|
||||
|
||||
if (tier.unitAmount !== undefined) {
|
||||
result.unit_amount_decimal = tier.unitAmount.toFixed(12)
|
||||
}
|
||||
|
||||
if (tier.flatAmount !== undefined) {
|
||||
result.flat_amount_decimal = tier.flatAmount.toFixed(12)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
} else {
|
||||
planParams.amount_decimal = metric.amount.toFixed(12)
|
||||
}
|
||||
|
||||
const stripePlan = await stripe.plans.create(
|
||||
planParams,
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
pricingPlan.stripeMetricPlans[metricSlug] = stripePlan.id
|
||||
projectStripePlan.basePlanId = stripePlan.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Promise.all([upsertStripeBaseProduct(), upsertStripeRequestProduct()])
|
||||
|
||||
const upserts = []
|
||||
for (const pricingPlan of deployment.pricingPlans) {
|
||||
upserts.push(() => upsertStripeBasePlan(pricingPlan))
|
||||
upserts.push(() => upsertStripeRequestPlan(pricingPlan))
|
||||
|
||||
for (const metric of pricingPlan.metrics) {
|
||||
upserts.push(async () => {
|
||||
await upsertStripeMetricProduct(metric)
|
||||
return upsertStripeMetricPlan(pricingPlan, metric)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
await pAll(upserts, { concurrency: 4 })
|
||||
|
||||
if (dirty) {
|
||||
await Promise.all([
|
||||
db
|
||||
.update(schema.projects)
|
||||
.set(project)
|
||||
.where(eq(schema.projects.id, project.id)),
|
||||
|
||||
db
|
||||
.update(schema.deployments)
|
||||
.set(deployment)
|
||||
.where(eq(schema.deployments.id, deployment.id))
|
||||
])
|
||||
}
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
import type Stripe from 'stripe'
|
||||
import pAll from 'p-all'
|
||||
|
||||
import { db, eq, type RawDeployment, type RawProject, schema } from '@/db'
|
||||
import {
|
||||
getPricingPlanMetricHash,
|
||||
type PricingPlan,
|
||||
type PricingPlanMetric
|
||||
} from '@/db/schema'
|
||||
import { stripe } from '@/lib/stripe'
|
||||
import { assert } from '@/lib/utils'
|
||||
|
||||
export async function upsertStripeProductsAndPricing({
|
||||
deployment,
|
||||
project
|
||||
}: {
|
||||
deployment: RawDeployment
|
||||
project: RawProject
|
||||
}): Promise<void> {
|
||||
const stripeConnectParams = project._stripeAccountId
|
||||
? [
|
||||
{
|
||||
stripeAccount: project._stripeAccountId
|
||||
}
|
||||
]
|
||||
: []
|
||||
let dirty = false
|
||||
|
||||
async function upsertStripeProductAndPricingForMetric({
|
||||
pricingPlan,
|
||||
pricingPlanMetric
|
||||
}: {
|
||||
pricingPlan: PricingPlan
|
||||
pricingPlanMetric: PricingPlanMetric
|
||||
}) {
|
||||
const { slug: pricingPlanSlug } = pricingPlan // TODO
|
||||
const { slug: pricingPlanMetricSlug } = pricingPlanMetric
|
||||
|
||||
const pricingPlanMetricHash = getPricingPlanMetricHash({
|
||||
pricingPlanMetric,
|
||||
project
|
||||
})
|
||||
|
||||
// Upsert the Stripe Product
|
||||
if (!project._stripeProductIdMap[pricingPlanMetricSlug]) {
|
||||
const productParams: Stripe.ProductCreateParams = {
|
||||
name: `${project.id} ${pricingPlanMetricSlug}`,
|
||||
type: 'service',
|
||||
metadata: {
|
||||
projectId: project.id,
|
||||
pricingPlanMetricSlug
|
||||
}
|
||||
}
|
||||
|
||||
if (pricingPlanMetric.usageType === 'licensed') {
|
||||
productParams.unit_label = pricingPlanMetric.label
|
||||
} else {
|
||||
productParams.unit_label = pricingPlanMetric.unitLabel
|
||||
}
|
||||
|
||||
const product = await stripe.products.create(
|
||||
productParams,
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
project._stripeProductIdMap[pricingPlanMetricSlug] = product.id
|
||||
dirty = true
|
||||
}
|
||||
|
||||
assert(project._stripeProductIdMap[pricingPlanMetricSlug])
|
||||
|
||||
// Upsert the Stripe Meter
|
||||
if (
|
||||
pricingPlanMetric.usageType === 'metered' &&
|
||||
!project._stripeMeterIdMap[pricingPlanMetricSlug]
|
||||
) {
|
||||
const meter = await stripe.billing.meters.create(
|
||||
{
|
||||
display_name: `${project.id} ${pricingPlanMetric.label || pricingPlanMetricSlug}`,
|
||||
event_name: `meter-${project.id}-${pricingPlanMetricSlug}`,
|
||||
default_aggregation: {
|
||||
formula: 'sum'
|
||||
},
|
||||
customer_mapping: {
|
||||
event_payload_key: 'stripe_customer_id',
|
||||
type: 'by_id'
|
||||
},
|
||||
value_settings: {
|
||||
event_payload_key: 'value'
|
||||
}
|
||||
},
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
project._stripeMeterIdMap[pricingPlanMetricSlug] = meter.id
|
||||
dirty = true
|
||||
}
|
||||
|
||||
assert(
|
||||
pricingPlanMetric.usageType === 'licensed' ||
|
||||
project._stripeMeterIdMap[pricingPlanMetricSlug]
|
||||
)
|
||||
|
||||
// Upsert the Stripe Price
|
||||
if (!project._stripePriceIdMap[pricingPlanMetricHash]) {
|
||||
const priceParams: Stripe.PriceCreateParams = {
|
||||
nickname: `price-${project.id}-${pricingPlan.slug}-${pricingPlanMetricSlug}`,
|
||||
product: project._stripeProductIdMap[pricingPlanMetricSlug],
|
||||
currency: project.pricingCurrency,
|
||||
recurring: {
|
||||
interval: pricingPlanMetric.interval,
|
||||
|
||||
// TODO: support this
|
||||
interval_count: 1,
|
||||
|
||||
usage_type: pricingPlanMetric.usageType
|
||||
}
|
||||
}
|
||||
|
||||
if (pricingPlanMetric.usageType === 'licensed') {
|
||||
priceParams.unit_amount_decimal = pricingPlanMetric.amount.toFixed(12)
|
||||
} else {
|
||||
priceParams.billing_scheme = pricingPlanMetric.billingScheme
|
||||
|
||||
if (pricingPlanMetric.billingScheme === 'tiered') {
|
||||
assert(
|
||||
pricingPlanMetric.tiers,
|
||||
`Invalid pricing plan metric: ${pricingPlanMetricSlug}`
|
||||
)
|
||||
|
||||
priceParams.tiers_mode = pricingPlanMetric.tiersMode
|
||||
priceParams.tiers = pricingPlanMetric.tiers!.map((tier) => {
|
||||
const result: Stripe.PriceCreateParams.Tier = {
|
||||
up_to: tier.upTo
|
||||
}
|
||||
|
||||
if (tier.unitAmount !== undefined) {
|
||||
result.unit_amount_decimal = tier.unitAmount.toFixed(12)
|
||||
}
|
||||
|
||||
if (tier.flatAmount !== undefined) {
|
||||
result.flat_amount_decimal = tier.flatAmount.toFixed(12)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const stripePrice = await stripe.prices.create(
|
||||
priceParams,
|
||||
...stripeConnectParams
|
||||
)
|
||||
|
||||
project._stripePriceIdMap[pricingPlanMetricHash] = stripePrice.id
|
||||
dirty = true
|
||||
}
|
||||
|
||||
assert(project._stripePriceIdMap[pricingPlanMetricHash])
|
||||
}
|
||||
|
||||
const upserts: Array<() => Promise<void>> = []
|
||||
|
||||
for (const pricingInterval of project.pricingIntervals) {
|
||||
const pricingPlanMap = deployment.pricingPlanMapByInterval[pricingInterval]
|
||||
assert(
|
||||
pricingPlanMap,
|
||||
`Invalid pricing config for deployment "${deployment.id}": missing pricing plan map for interval "${pricingInterval}"`
|
||||
)
|
||||
|
||||
for (const pricingPlan of Object.values(pricingPlanMap)) {
|
||||
for (const pricingPlanMetric of Object.values(pricingPlan.metricsMap)) {
|
||||
upserts.push(() =>
|
||||
upsertStripeProductAndPricingForMetric({
|
||||
pricingPlan,
|
||||
pricingPlanMetric
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await pAll(upserts, { concurrency: 4 })
|
||||
|
||||
if (dirty) {
|
||||
await Promise.all([
|
||||
db
|
||||
.update(schema.projects)
|
||||
.set(project)
|
||||
.where(eq(schema.projects.id, project.id)),
|
||||
|
||||
db
|
||||
.update(schema.deployments)
|
||||
.set(deployment)
|
||||
.where(eq(schema.deployments.id, deployment.id))
|
||||
])
|
||||
}
|
||||
}
|
|
@ -2,11 +2,50 @@ import { createHash, randomUUID } from 'node:crypto'
|
|||
|
||||
import type { ContentfulStatusCode } from 'hono/utils/http-status'
|
||||
import type { ZodSchema, ZodTypeDef } from 'zod'
|
||||
import hashObjectImpl, { type Options as HashObjectOptions } from 'hash-object'
|
||||
|
||||
import { HttpError, ZodValidationError } from './errors'
|
||||
|
||||
export function sha256(input: string = randomUUID()) {
|
||||
return createHash('sha256').update(input).digest('hex')
|
||||
/**
|
||||
* From `inputObj`, create a new object that does not include `keys`.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* omit({ a: 1, b: 2, c: 3 }, 'a', 'c') // { b: 2 }
|
||||
* ```
|
||||
*/
|
||||
export const omit = <
|
||||
T extends Record<string, unknown> | object,
|
||||
K extends keyof any
|
||||
>(
|
||||
inputObj: T,
|
||||
...keys: K[]
|
||||
): Omit<T, K> => {
|
||||
const keysSet = new Set(keys)
|
||||
return Object.fromEntries(
|
||||
Object.entries(inputObj).filter(([k]) => !keysSet.has(k as any))
|
||||
) as any
|
||||
}
|
||||
|
||||
/**
|
||||
* From `inputObj`, create a new object that only includes `keys`.
|
||||
*
|
||||
* @example
|
||||
* ```js
|
||||
* pick({ a: 1, b: 2, c: 3 }, 'a', 'c') // { a: 1, c: 3 }
|
||||
* ```
|
||||
*/
|
||||
export const pick = <
|
||||
T extends Record<string, unknown> | object,
|
||||
K extends keyof T
|
||||
>(
|
||||
inputObj: T,
|
||||
...keys: K[]
|
||||
): Pick<T, K> => {
|
||||
const keysSet = new Set(keys)
|
||||
return Object.fromEntries(
|
||||
Object.entries(inputObj).filter(([k]) => keysSet.has(k as any))
|
||||
) as any
|
||||
}
|
||||
|
||||
export function assert(expr: unknown, message?: string): asserts expr
|
||||
|
@ -31,6 +70,10 @@ export function assert(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given input against the given Zod schema, throwing a
|
||||
* `ZodValidationError` if the input is invalid.
|
||||
*/
|
||||
export function parseZodSchema<
|
||||
Output,
|
||||
Def extends ZodTypeDef = ZodTypeDef,
|
||||
|
@ -53,3 +96,22 @@ export function parseZodSchema<
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function sha256(input: string = randomUUID()) {
|
||||
return createHash('sha256').update(input).digest('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stable, deterministic hash of the given object, defaulting to
|
||||
* using `sha256` as the hashing algorithm and `hex` as the encoding.
|
||||
*/
|
||||
export function hashObject(
|
||||
object: Record<string, any>,
|
||||
options?: HashObjectOptions
|
||||
): string {
|
||||
return hashObjectImpl(object, {
|
||||
algorithm: 'sha256',
|
||||
encoding: 'hex',
|
||||
...options
|
||||
})
|
||||
}
|
||||
|
|
412
pnpm-lock.yaml
412
pnpm-lock.yaml
|
@ -6,15 +6,60 @@ settings:
|
|||
|
||||
catalogs:
|
||||
default:
|
||||
'@fisch0920/config':
|
||||
specifier: ^1.1.0
|
||||
version: 1.1.0
|
||||
'@types/node':
|
||||
specifier: ^22.15.18
|
||||
version: 22.15.18
|
||||
del-cli:
|
||||
specifier: ^6.0.0
|
||||
version: 6.0.0
|
||||
dotenv:
|
||||
specifier: ^16.5.0
|
||||
version: 16.5.0
|
||||
eslint:
|
||||
specifier: ^9.26.0
|
||||
version: 9.26.0
|
||||
exit-hook:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
lint-staged:
|
||||
specifier: ^16.0.0
|
||||
version: 16.0.0
|
||||
npm-run-all2:
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.1
|
||||
only-allow:
|
||||
specifier: ^1.2.1
|
||||
version: 1.2.1
|
||||
prettier:
|
||||
specifier: ^3.5.3
|
||||
version: 3.5.3
|
||||
restore-cursor:
|
||||
specifier: ^5.1.0
|
||||
version: 5.1.0
|
||||
simple-git-hooks:
|
||||
specifier: ^2.13.0
|
||||
version: 2.13.0
|
||||
tsup:
|
||||
specifier: ^8.4.0
|
||||
version: 8.4.0
|
||||
tsx:
|
||||
specifier: ^4.19.4
|
||||
version: 4.19.4
|
||||
turbo:
|
||||
specifier: ^2.5.3
|
||||
version: 2.5.3
|
||||
type-fest:
|
||||
specifier: ^4.41.0
|
||||
version: 4.41.0
|
||||
typescript:
|
||||
specifier: ^5.8.3
|
||||
version: 5.8.3
|
||||
vitest:
|
||||
specifier: ^3.1.3
|
||||
version: 3.1.3
|
||||
zod:
|
||||
specifier: ^3.24.4
|
||||
version: 3.24.4
|
||||
|
@ -116,6 +161,9 @@ importers:
|
|||
exit-hook:
|
||||
specifier: 'catalog:'
|
||||
version: 4.0.0
|
||||
hash-object:
|
||||
specifier: ^5.0.1
|
||||
version: 5.0.1
|
||||
hono:
|
||||
specifier: ^4.7.9
|
||||
version: 4.7.9
|
||||
|
@ -196,12 +244,6 @@ packages:
|
|||
resolution: {integrity: sha512-FxEMIkJKnodyA1OaCUoEvbYRkoZlLZ4d/eXFu9Fh8CbBBgP5EmZxrfTRyN0qpXZ4vOvqnE5YdRdcrmUUXuU+dA==}
|
||||
deprecated: 'Merged into tsx: https://tsx.is'
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.3':
|
||||
resolution: {integrity: sha512-W8bFfPA8DowP8l//sxjJLSLkD8iEjMc7cBVyP+u4cEv9sM7mdUCkgsj+t0n/BWPFtv7WWCN5Yzj0N6FJNUUqBQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.4':
|
||||
resolution: {integrity: sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -214,12 +256,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-XelR6MzjlZuBM4f5z2IQHK6LkK34Cvv6Rj2EntER3lwCBFdg6h2lKbtRjpTTsdEjD/WSe1q8UyPBXP1x3i/wYQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -232,12 +268,6 @@ packages:
|
|||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.25.3':
|
||||
resolution: {integrity: sha512-PuwVXbnP87Tcff5I9ngV0lmiSu40xw1At6i3GsU77U7cjDDB4s0X2cyFuBiDa1SBk9DnvWwnGvVaGBqoFWPb7A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.25.4':
|
||||
resolution: {integrity: sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -250,12 +280,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.25.3':
|
||||
resolution: {integrity: sha512-ogtTpYHT/g1GWS/zKM0cc/tIebFjm1F9Aw1boQ2Y0eUQ+J89d0jFY//s9ei9jVIlkYi8AfOjiixcLJSGNSOAdQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.25.4':
|
||||
resolution: {integrity: sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -268,12 +292,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-eESK5yfPNTqpAmDfFWNsOhmIOaQA59tAcF/EfYvo5/QWQCzXn5iUSOnqt3ra3UdzBv073ykTtmeLJZGt3HhA+w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -286,12 +304,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.25.3':
|
||||
resolution: {integrity: sha512-Kd8glo7sIZtwOLcPbW0yLpKmBNWMANZhrC1r6K++uDR2zyzb6AeOYtI6udbtabmQpFaxJ8uduXMAo1gs5ozz8A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.25.4':
|
||||
resolution: {integrity: sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -304,12 +316,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-EJiyS70BYybOBpJth3M0KLOus0n+RRMKTYzhYhFeMwp7e/RaajXvP+BWlmEXNk6uk+KAu46j/kaQzr6au+JcIw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -322,12 +328,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.3':
|
||||
resolution: {integrity: sha512-Q+wSjaLpGxYf7zC0kL0nDlhsfuFkoN+EXrx2KSB33RhinWzejOd6AvgmP5JbkgXKmjhmpfgKZq24pneodYqE8Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.4':
|
||||
resolution: {integrity: sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -340,12 +340,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-xCUgnNYhRD5bb1C1nqrDV1PfkwgbswTTBRbAd8aH5PhYzikdf/ddtsYyMXFfGSsb/6t6QaPSzxtbfAZr9uox4A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -358,12 +352,6 @@ packages:
|
|||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.25.3':
|
||||
resolution: {integrity: sha512-dUOVmAUzuHy2ZOKIHIKHCm58HKzFqd+puLaS424h6I85GlSDRZIA5ycBixb3mFgM0Jdh+ZOSB6KptX30DD8YOQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.25.4':
|
||||
resolution: {integrity: sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -376,12 +364,6 @@ packages:
|
|||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.25.3':
|
||||
resolution: {integrity: sha512-yplPOpczHOO4jTYKmuYuANI3WhvIPSVANGcNUeMlxH4twz/TeXuzEP41tGKNGWJjuMhotpGabeFYGAOU2ummBw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.25.4':
|
||||
resolution: {integrity: sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -394,12 +376,6 @@ packages:
|
|||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.25.3':
|
||||
resolution: {integrity: sha512-P4BLP5/fjyihmXCELRGrLd793q/lBtKMQl8ARGpDxgzgIKJDRJ/u4r1A/HgpBpKpKZelGct2PGI4T+axcedf6g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.25.4':
|
||||
resolution: {integrity: sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -412,12 +388,6 @@ packages:
|
|||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.3':
|
||||
resolution: {integrity: sha512-eRAOV2ODpu6P5divMEMa26RRqb2yUoYsuQQOuFUexUoQndm4MdpXXDBbUoKIc0iPa4aCO7gIhtnYomkn2x+bag==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.4':
|
||||
resolution: {integrity: sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -430,12 +400,6 @@ packages:
|
|||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.3':
|
||||
resolution: {integrity: sha512-ZC4jV2p7VbzTlnl8nZKLcBkfzIf4Yad1SJM4ZMKYnJqZFD4rTI+pBG65u8ev4jk3/MPwY9DvGn50wi3uhdaghg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.4':
|
||||
resolution: {integrity: sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -448,12 +412,6 @@ packages:
|
|||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.3':
|
||||
resolution: {integrity: sha512-LDDODcFzNtECTrUUbVCs6j9/bDVqy7DDRsuIXJg6so+mFksgwG7ZVnTruYi5V+z3eE5y+BJZw7VvUadkbfg7QA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.4':
|
||||
resolution: {integrity: sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -466,12 +424,6 @@ packages:
|
|||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.25.3':
|
||||
resolution: {integrity: sha512-s+w/NOY2k0yC2p9SLen+ymflgcpRkvwwa02fqmAwhBRI3SC12uiS10edHHXlVWwfAagYSY5UpmT/zISXPMW3tQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.25.4':
|
||||
resolution: {integrity: sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -484,24 +436,12 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.25.3':
|
||||
resolution: {integrity: sha512-nQHDz4pXjSDC6UfOE1Fw9Q8d6GCAd9KdvMZpfVGWSJztYCarRgSDfOVBY5xwhQXseiyxapkiSJi/5/ja8mRFFA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.25.4':
|
||||
resolution: {integrity: sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-1QaLtOWq0mzK6tzzp0jRN3eccmN3hezey7mhLnzC6oNlJoUJz4nym5ZD7mDnS/LZQgkrhEbEiTn515lPeLpgWA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -514,24 +454,12 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.3':
|
||||
resolution: {integrity: sha512-i5Hm68HXHdgv8wkrt+10Bc50zM0/eonPb/a/OFVfB6Qvpiirco5gBA5bz7S2SHuU+Y4LWn/zehzNX14Sp4r27g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.4':
|
||||
resolution: {integrity: sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-zGAVApJEYTbOC6H/3QBr2mq3upG/LBEXr85/pTtKiv2IXcgKV0RT0QA/hSXZqSvLEpXeIxah7LczB4lkiYhTAQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -544,12 +472,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.3':
|
||||
resolution: {integrity: sha512-fpqctI45NnCIDKBH5AXQBsD0NDPbEFczK98hk/aa6HJxbl+UtLkJV2+Bvy5hLSLk3LHmqt0NTkKNso1A9y1a4w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.4':
|
||||
resolution: {integrity: sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -562,12 +484,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/sunos-x64@0.25.3':
|
||||
resolution: {integrity: sha512-ROJhm7d8bk9dMCUZjkS8fgzsPAZEjtRJqCAmVgB0gMrvG7hfmPmz9k1rwO4jSiblFjYmNvbECL9uhaPzONMfgA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/sunos-x64@0.25.4':
|
||||
resolution: {integrity: sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -580,12 +496,6 @@ packages:
|
|||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-arm64@0.25.3':
|
||||
resolution: {integrity: sha512-YWcow8peiHpNBiIXHwaswPnAXLsLVygFwCB3A7Bh5jRkIBFWHGmNQ48AlX4xDvQNoMZlPYzjVOQDYEzWCqufMQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-arm64@0.25.4':
|
||||
resolution: {integrity: sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -598,12 +508,6 @@ packages:
|
|||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.25.3':
|
||||
resolution: {integrity: sha512-qspTZOIGoXVS4DpNqUYUs9UxVb04khS1Degaw/MnfMe7goQ3lTfQ13Vw4qY/Nj0979BGvMRpAYbs/BAxEvU8ew==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.25.4':
|
||||
resolution: {integrity: sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -616,12 +520,6 @@ packages:
|
|||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.25.3':
|
||||
resolution: {integrity: sha512-ICgUR+kPimx0vvRzf+N/7L7tVSQeE3BYY+NhHRHXS1kBuPO7z2+7ea2HbhDyZdTephgvNvKrlDDKUexuCVBVvg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.25.4':
|
||||
resolution: {integrity: sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -654,10 +552,6 @@ packages:
|
|||
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@9.25.1':
|
||||
resolution: {integrity: sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@9.26.0':
|
||||
resolution: {integrity: sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
@ -1642,6 +1536,10 @@ packages:
|
|||
supports-color:
|
||||
optional: true
|
||||
|
||||
decircular@0.1.1:
|
||||
resolution: {integrity: sha512-V2Vy+QYSXdgxRPmOZKQWCDf1KQNTUP/Eqswv/3W20gz7+6GB1HTosNrWqK3PqstVpFw/Dd/cGTmXSTKPeOiGVg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
deep-eql@5.0.2:
|
||||
resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==}
|
||||
engines: {node: '>=6'}
|
||||
|
@ -1853,11 +1751,6 @@ packages:
|
|||
engines: {node: '>=12'}
|
||||
hasBin: true
|
||||
|
||||
esbuild@0.25.3:
|
||||
resolution: {integrity: sha512-qKA6Pvai73+M2FtftpNKRxJ78GIjmFXFxd/1DVBqGo/qNhLSfv+G12n9pNoWdytJC8U00TrViOwpjT0zgqQS8Q==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
esbuild@0.25.4:
|
||||
resolution: {integrity: sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q==}
|
||||
engines: {node: '>=18'}
|
||||
|
@ -2219,6 +2112,10 @@ packages:
|
|||
resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
hash-object@5.0.1:
|
||||
resolution: {integrity: sha512-iaRY4jYOow1caHkXW7wotYRjZDQk2nq4U7904anGJj8l4x1SLId+vuR8RpGoywZz9puD769hNFVFLFH9t+baJw==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
hasown@2.0.2:
|
||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
@ -2357,6 +2254,10 @@ packages:
|
|||
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
|
||||
engines: {node: '>=0.12.0'}
|
||||
|
||||
is-obj@3.0.0:
|
||||
resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-path-cwd@3.0.0:
|
||||
resolution: {integrity: sha512-kyiNFFLU0Ampr6SDZitD/DwUo4Zs1nSdnygUBqsu3LooL00Qvb5j+UnvApUn/TTj1J3OuE6BTdQ5rudKmU2ZaA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
|
@ -2365,6 +2266,10 @@ packages:
|
|||
resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-plain-obj@4.1.0:
|
||||
resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
is-promise@4.0.0:
|
||||
resolution: {integrity: sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==}
|
||||
|
||||
|
@ -3019,11 +2924,6 @@ packages:
|
|||
resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.1:
|
||||
resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.2:
|
||||
resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -3106,6 +3006,10 @@ packages:
|
|||
resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
sort-keys@5.1.0:
|
||||
resolution: {integrity: sha512-aSbHV0DaBcr7u0PVHXzM6NbZNAtrr9sF6+Qfs9UUVG7Ll3jQ6hHi8F/xqIIcn2rvIVbr0v/2zyjSdwSV47AgLQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
source-map-js@1.2.1:
|
||||
resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -3355,10 +3259,6 @@ packages:
|
|||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
type-fest@4.40.1:
|
||||
resolution: {integrity: sha512-9YvLNnORDpI+vghLU/Nf+zSv0kL47KbVJ1o3sKgoTefl6i+zebxbiDQWoe/oWWqPhIgQdRZRT1KA9sCPL810SA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
type-fest@4.41.0:
|
||||
resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
|
||||
engines: {node: '>=16'}
|
||||
|
@ -3633,219 +3533,144 @@ snapshots:
|
|||
'@esbuild-kit/core-utils': 3.3.2
|
||||
get-tsconfig: 4.10.0
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.18.20':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.25.3':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
|
@ -3859,7 +3684,7 @@ snapshots:
|
|||
'@eslint/config-array@0.20.0':
|
||||
dependencies:
|
||||
'@eslint/object-schema': 2.1.6
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
minimatch: 3.1.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
@ -3873,7 +3698,7 @@ snapshots:
|
|||
'@eslint/eslintrc@3.3.1':
|
||||
dependencies:
|
||||
ajv: 6.12.6
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
espree: 10.3.0
|
||||
globals: 14.0.0
|
||||
ignore: 5.3.2
|
||||
|
@ -3884,8 +3709,6 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint/js@9.25.1': {}
|
||||
|
||||
'@eslint/js@9.26.0': {}
|
||||
|
||||
'@eslint/object-schema@2.1.6': {}
|
||||
|
@ -3907,7 +3730,7 @@ snapshots:
|
|||
|
||||
'@fisch0920/config@1.1.0(@typescript-eslint/parser@8.31.0(eslint@9.26.0)(typescript@5.8.3))(@typescript-eslint/utils@8.31.0(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(prettier@3.5.3)(typescript@5.8.3)(vitest@3.1.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1))':
|
||||
dependencies:
|
||||
'@eslint/js': 9.25.1
|
||||
'@eslint/js': 9.26.0
|
||||
'@total-typescript/ts-reset': 0.6.1
|
||||
'@vitest/eslint-plugin': 1.1.43(@typescript-eslint/utils@8.31.0(eslint@9.26.0)(typescript@5.8.3))(eslint@9.26.0)(typescript@5.8.3)(vitest@3.1.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1))
|
||||
eslint: 9.26.0
|
||||
|
@ -4484,7 +4307,7 @@ snapshots:
|
|||
'@typescript-eslint/types': 8.31.0
|
||||
'@typescript-eslint/typescript-estree': 8.31.0(typescript@5.8.3)
|
||||
'@typescript-eslint/visitor-keys': 8.31.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
eslint: 9.26.0
|
||||
typescript: 5.8.3
|
||||
transitivePeerDependencies:
|
||||
|
@ -4715,7 +4538,7 @@ snapshots:
|
|||
dependencies:
|
||||
bytes: 3.1.2
|
||||
content-type: 1.0.5
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
http-errors: 2.0.0
|
||||
iconv-lite: 0.6.3
|
||||
on-finished: 2.4.1
|
||||
|
@ -4751,9 +4574,9 @@ snapshots:
|
|||
|
||||
builtin-modules@5.0.0: {}
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.25.3):
|
||||
bundle-require@5.1.0(esbuild@0.25.4):
|
||||
dependencies:
|
||||
esbuild: 0.25.3
|
||||
esbuild: 0.25.4
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
bytes@3.1.2: {}
|
||||
|
@ -4892,6 +4715,8 @@ snapshots:
|
|||
dependencies:
|
||||
ms: 2.1.3
|
||||
|
||||
decircular@0.1.1: {}
|
||||
|
||||
deep-eql@5.0.2: {}
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
@ -5105,34 +4930,6 @@ snapshots:
|
|||
'@esbuild/win32-ia32': 0.18.20
|
||||
'@esbuild/win32-x64': 0.18.20
|
||||
|
||||
esbuild@0.25.3:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.25.3
|
||||
'@esbuild/android-arm': 0.25.3
|
||||
'@esbuild/android-arm64': 0.25.3
|
||||
'@esbuild/android-x64': 0.25.3
|
||||
'@esbuild/darwin-arm64': 0.25.3
|
||||
'@esbuild/darwin-x64': 0.25.3
|
||||
'@esbuild/freebsd-arm64': 0.25.3
|
||||
'@esbuild/freebsd-x64': 0.25.3
|
||||
'@esbuild/linux-arm': 0.25.3
|
||||
'@esbuild/linux-arm64': 0.25.3
|
||||
'@esbuild/linux-ia32': 0.25.3
|
||||
'@esbuild/linux-loong64': 0.25.3
|
||||
'@esbuild/linux-mips64el': 0.25.3
|
||||
'@esbuild/linux-ppc64': 0.25.3
|
||||
'@esbuild/linux-riscv64': 0.25.3
|
||||
'@esbuild/linux-s390x': 0.25.3
|
||||
'@esbuild/linux-x64': 0.25.3
|
||||
'@esbuild/netbsd-arm64': 0.25.3
|
||||
'@esbuild/netbsd-x64': 0.25.3
|
||||
'@esbuild/openbsd-arm64': 0.25.3
|
||||
'@esbuild/openbsd-x64': 0.25.3
|
||||
'@esbuild/sunos-x64': 0.25.3
|
||||
'@esbuild/win32-arm64': 0.25.3
|
||||
'@esbuild/win32-ia32': 0.25.3
|
||||
'@esbuild/win32-x64': 0.25.3
|
||||
|
||||
esbuild@0.25.4:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.25.4
|
||||
|
@ -5310,7 +5107,7 @@ snapshots:
|
|||
read-package-up: 11.0.0
|
||||
regexp-tree: 0.1.27
|
||||
regjsparser: 0.12.0
|
||||
semver: 7.7.1
|
||||
semver: 7.7.2
|
||||
strip-indent: 4.0.0
|
||||
|
||||
eslint-scope@8.3.0:
|
||||
|
@ -5341,7 +5138,7 @@ snapshots:
|
|||
ajv: 6.12.6
|
||||
chalk: 4.1.2
|
||||
cross-spawn: 7.0.6
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint-scope: 8.3.0
|
||||
eslint-visitor-keys: 4.2.0
|
||||
|
@ -5416,7 +5213,7 @@ snapshots:
|
|||
content-type: 1.0.5
|
||||
cookie: 0.7.2
|
||||
cookie-signature: 1.2.2
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
|
@ -5472,7 +5269,7 @@ snapshots:
|
|||
|
||||
finalhandler@2.1.0:
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
on-finished: 2.4.1
|
||||
|
@ -5615,6 +5412,13 @@ snapshots:
|
|||
dependencies:
|
||||
has-symbols: 1.1.0
|
||||
|
||||
hash-object@5.0.1:
|
||||
dependencies:
|
||||
decircular: 0.1.1
|
||||
is-obj: 3.0.0
|
||||
sort-keys: 5.1.0
|
||||
type-fest: 4.41.0
|
||||
|
||||
hasown@2.0.2:
|
||||
dependencies:
|
||||
function-bind: 1.1.2
|
||||
|
@ -5749,10 +5553,14 @@ snapshots:
|
|||
|
||||
is-number@7.0.0: {}
|
||||
|
||||
is-obj@3.0.0: {}
|
||||
|
||||
is-path-cwd@3.0.0: {}
|
||||
|
||||
is-path-inside@4.0.0: {}
|
||||
|
||||
is-plain-obj@4.1.0: {}
|
||||
|
||||
is-promise@4.0.0: {}
|
||||
|
||||
is-regex@1.2.1:
|
||||
|
@ -5897,7 +5705,7 @@ snapshots:
|
|||
dependencies:
|
||||
chalk: 5.4.1
|
||||
commander: 13.1.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
lilconfig: 3.1.3
|
||||
listr2: 8.3.3
|
||||
micromatch: 4.0.8
|
||||
|
@ -6027,7 +5835,7 @@ snapshots:
|
|||
normalize-package-data@6.0.2:
|
||||
dependencies:
|
||||
hosted-git-info: 7.0.2
|
||||
semver: 7.7.1
|
||||
semver: 7.7.2
|
||||
validate-npm-package-license: 3.0.4
|
||||
|
||||
npm-normalize-package-bin@4.0.0: {}
|
||||
|
@ -6146,7 +5954,7 @@ snapshots:
|
|||
dependencies:
|
||||
'@babel/code-frame': 7.26.2
|
||||
index-to-position: 1.1.0
|
||||
type-fest: 4.40.1
|
||||
type-fest: 4.41.0
|
||||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
|
@ -6266,14 +6074,14 @@ snapshots:
|
|||
dependencies:
|
||||
find-up-simple: 1.0.1
|
||||
read-pkg: 9.0.1
|
||||
type-fest: 4.40.1
|
||||
type-fest: 4.41.0
|
||||
|
||||
read-pkg@9.0.1:
|
||||
dependencies:
|
||||
'@types/normalize-package-data': 2.4.4
|
||||
normalize-package-data: 6.0.2
|
||||
parse-json: 8.3.0
|
||||
type-fest: 4.40.1
|
||||
type-fest: 4.41.0
|
||||
unicorn-magic: 0.1.0
|
||||
|
||||
readdirp@4.1.2: {}
|
||||
|
@ -6371,7 +6179,7 @@ snapshots:
|
|||
|
||||
router@2.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
depd: 2.0.0
|
||||
is-promise: 4.0.0
|
||||
parseurl: 1.3.3
|
||||
|
@ -6412,13 +6220,11 @@ snapshots:
|
|||
|
||||
semver@6.3.1: {}
|
||||
|
||||
semver@7.7.1: {}
|
||||
|
||||
semver@7.7.2: {}
|
||||
|
||||
send@1.2.0:
|
||||
dependencies:
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
encodeurl: 2.0.0
|
||||
escape-html: 1.0.3
|
||||
etag: 1.8.1
|
||||
|
@ -6521,6 +6327,10 @@ snapshots:
|
|||
ansi-styles: 6.2.1
|
||||
is-fullwidth-code-point: 5.0.0
|
||||
|
||||
sort-keys@5.1.0:
|
||||
dependencies:
|
||||
is-plain-obj: 4.1.0
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
source-map-support@0.5.21:
|
||||
|
@ -6722,12 +6532,12 @@ snapshots:
|
|||
|
||||
tsup@8.4.0(postcss@8.5.3)(tsx@4.19.4)(typescript@5.8.3)(yaml@2.7.1):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.25.3)
|
||||
bundle-require: 5.1.0(esbuild@0.25.4)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.0
|
||||
esbuild: 0.25.3
|
||||
debug: 4.4.1
|
||||
esbuild: 0.25.4
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
postcss-load-config: 6.0.1(postcss@8.5.3)(tsx@4.19.4)(yaml@2.7.1)
|
||||
|
@ -6749,7 +6559,7 @@ snapshots:
|
|||
|
||||
tsx@4.19.4:
|
||||
dependencies:
|
||||
esbuild: 0.25.3
|
||||
esbuild: 0.25.4
|
||||
get-tsconfig: 4.10.0
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
@ -6785,8 +6595,6 @@ snapshots:
|
|||
dependencies:
|
||||
prelude-ls: 1.2.1
|
||||
|
||||
type-fest@4.40.1: {}
|
||||
|
||||
type-fest@4.41.0: {}
|
||||
|
||||
type-is@2.0.1:
|
||||
|
@ -6877,7 +6685,7 @@ snapshots:
|
|||
vite-node@3.1.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1):
|
||||
dependencies:
|
||||
cac: 6.7.14
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
es-module-lexer: 1.7.0
|
||||
pathe: 2.0.3
|
||||
vite: 6.3.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1)
|
||||
|
@ -6908,7 +6716,7 @@ snapshots:
|
|||
|
||||
vite@6.3.3(@types/node@22.15.18)(tsx@4.19.4)(yaml@2.7.1):
|
||||
dependencies:
|
||||
esbuild: 0.25.3
|
||||
esbuild: 0.25.4
|
||||
fdir: 6.4.4(picomatch@4.0.2)
|
||||
picomatch: 4.0.2
|
||||
postcss: 8.5.3
|
||||
|
@ -6930,7 +6738,7 @@ snapshots:
|
|||
'@vitest/spy': 3.1.3
|
||||
'@vitest/utils': 3.1.3
|
||||
chai: 5.2.0
|
||||
debug: 4.4.0
|
||||
debug: 4.4.1
|
||||
expect-type: 1.2.1
|
||||
magic-string: 0.30.17
|
||||
pathe: 2.0.3
|
||||
|
|
20
readme.md
20
readme.md
|
@ -5,6 +5,26 @@
|
|||
|
||||
# Agentic <!-- omit from toc -->
|
||||
|
||||
## TODO
|
||||
|
||||
- stripe
|
||||
- plans => prices
|
||||
- monthly vs yearly prices
|
||||
- re-add custom metrics
|
||||
- re-add coupons
|
||||
- declarative json-based pricing
|
||||
- like https://github.com/tierrun/tier and Saasify
|
||||
- https://github.com/tierrun/tier/blob/main/pricing/schema.json
|
||||
- https://blog.tier.run/tier-hello-world-demo
|
||||
- auth
|
||||
- decide on approach for auth
|
||||
- built-in, first-party, tight coupling
|
||||
- https://github.com/toolbeam/openauth
|
||||
- https://github.com/aipotheosis-labs/aci/tree/main/backend/apps
|
||||
- https://github.com/NangoHQ/nango
|
||||
- https://github.com/transitive-bullshit?submit=Search&q=oauth&tab=stars&type=&sort=&direction=&submit=Search
|
||||
- clerk / workos / auth0
|
||||
|
||||
## License
|
||||
|
||||
UNLICENSED PROPRIETARY © [Agentic](https://x.com/transitive_bs)
|
||||
|
|
Ładowanie…
Reference in New Issue