@stacksjs/orm 0.75.109 → 0.75.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api-routes.js +0 -0
- package/dist/auto-crud.js +1 -0
- package/dist/batch-loader.js +1 -0
- package/dist/builder.d.ts +68 -0
- package/dist/builder.js +0 -0
- package/dist/db.js +1 -0
- package/dist/define-model.js +1 -0
- package/dist/extend-model.js +1 -0
- package/dist/gdpr/engine.js +1 -0
- package/dist/gdpr/index.js +1 -0
- package/dist/gdpr/models.js +1 -0
- package/dist/gdpr/plan.js +3 -0
- package/dist/gdpr/register.js +4 -0
- package/dist/gdpr/types.js +0 -0
- package/dist/generate-database-schema.js +21 -0
- package/dist/generated/index.d.ts +1 -0
- package/dist/generated/index.js +1 -0
- package/dist/generated/table-traits.d.ts +8 -0
- package/dist/generated/table-traits.js +135 -0
- package/dist/generated/types.d.ts +1 -0
- package/dist/generated/types.js +0 -0
- package/dist/index.js +1 -27
- package/dist/migration-tables.js +1 -0
- package/dist/model-registry.js +1 -2
- package/dist/model-types.js +0 -0
- package/dist/ownership.js +1 -0
- package/dist/paginator-request.js +1 -0
- package/dist/paginator.js +1 -0
- package/dist/routes.js +1 -2
- package/dist/subquery.js +1 -0
- package/dist/traits/activity-log.js +1 -0
- package/dist/traits/actor.js +1 -0
- package/dist/traits/audit.js +1 -0
- package/dist/traits/billable.js +1 -0
- package/dist/traits/categorizable.js +1 -0
- package/dist/traits/commentable.js +1 -0
- package/dist/traits/index.d.ts +11 -0
- package/dist/traits/index.js +1 -0
- package/dist/traits/likeable.js +1 -0
- package/dist/traits/soft-deletes.js +1 -0
- package/dist/traits/taggable.js +1 -0
- package/dist/traits/two-factor.js +1 -0
- package/dist/transaction.js +1 -0
- package/dist/types.js +0 -0
- package/dist/utils/compiled-binary.js +1 -0
- package/dist/utils/encrypted.js +1 -0
- package/dist/utils/prunable.js +1 -0
- package/package.json +24 -24
package/dist/subquery.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class SubqueryBuilder{conditions=[];where(column,...args){const[operatorOrValue,value]=args,operator=value===void 0?"=":operatorOrValue,actualValue=value===void 0?operatorOrValue:value;this.addCondition("and","where",column,operator,actualValue)}orWhere(column,...args){const[operatorOrValue,value]=args,operator=value===void 0?"=":operatorOrValue,actualValue=value===void 0?operatorOrValue:value;this.addCondition("or","where",column,operator,actualValue)}whereIn(column,values){this.conditions.push({type:"and",method:"whereIn",column,values})}whereNotIn(column,values){this.conditions.push({type:"and",method:"whereIn",column,values,operator:"not in"})}whereNull(column){this.conditions.push({type:"and",method:"whereNull",column})}whereNotNull(column){this.conditions.push({type:"and",method:"whereNotNull",column})}whereBetween(column,range){this.conditions.push({type:"and",method:"whereBetween",column,range})}whereExists(callback){this.conditions.push({type:"and",method:"whereExists",column:"",callback})}addCondition(type,method,column,operator,value){this.conditions.push({type,method,column,operator,value})}getConditions(){return this.conditions}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{sqlDateTime}from"@stacksjs/database/runtime";import{redactSensitive,reportTrailFailure,resolveTrailActorId,resolveTrailIp}from"./actor";const ACTIVITIES_TABLE="activities";export function resolveActivityLogOptions(declaration){if(!declaration)return null;if(declaration===!0)return{};if(typeof declaration!=="object")return null;const options=declaration;return{logOnly:Array.isArray(options.logOnly)?options.logOnly:void 0,include:Array.isArray(options.include)?options.include:void 0,exclude:Array.isArray(options.exclude)?options.exclude:void 0}}export function selectLoggedProperties(attributes,options,hidden=[]){const safe=redactSensitive(attributes);if(!safe)return null;const hiddenSet=new Set(hidden),wanted=options.logOnly??options.include,excluded=new Set(options.exclude??[]),properties={};for(const[key,value]of Object.entries(safe)){if(hiddenSet.has(key)||excluded.has(key))continue;if(wanted&&!wanted.includes(key))continue;properties[key]=value}return properties}export function describeActivity(modelName,event,id){return id==null?`${modelName} was ${event}`:`${modelName} ${String(id)} was ${event}`}export async function writeActivityRow(payload){try{const{db}=await import("@stacksjs/database/runtime");await db.insertInto(ACTIVITIES_TABLE).values({type:payload.type,description:payload.description,subject_type:payload.subject_type,subject_id:payload.subject_id,causer:payload.causer,properties:payload.properties?JSON.stringify(payload.properties):null,ip_address:payload.ip_address,user_id:payload.user_id,created_at:sqlDateTime()}).executeTakeFirst()}catch(error){reportTrailFailure("activity",`${payload.subject_type}#${String(payload.subject_id)}`,error)}}export async function recordActivity(context){const{attributes,event,modelName,options,primaryKey}=context,id=attributes?.[primaryKey]??null,actorId=await resolveTrailActorId();await writeActivityRow({type:`${modelName.toLowerCase()}.${event}`,description:describeActivity(modelName,event,id),subject_type:modelName,subject_id:id,properties:selectLoggedProperties(attributes,options,context.hidden),causer:actorId==null?null:`User:${String(actorId)}`,user_id:actorId,ip_address:await resolveTrailIp()})}function plainAttributes(value){if(!value||typeof value!=="object")return null;const candidate=value;if(candidate.attributes&&typeof candidate.attributes==="object")return{...candidate.attributes};if(candidate._attributes&&typeof candidate._attributes==="object")return{...candidate._attributes};return{...value}}export function applyActivityLog(baseModel,modelName,primaryKey,options,hidden=[]){if(modelName==="Activity")return;const model=baseModel,readRow=async(id)=>typeof model.find==="function"?plainAttributes(await model.find(id)):null,log=async(event,attributes)=>{await recordActivity({modelName,event,attributes,primaryKey,options,hidden})},originalCreate=baseModel.create;if(typeof originalCreate==="function")baseModel.create=async function(...args){const result=await originalCreate.apply(this,args);await log("created",plainAttributes(result));return result};const originalCreateMany=baseModel.createMany;if(typeof originalCreateMany==="function")baseModel.createMany=async function(...args){const result=await originalCreateMany.apply(this,args);for(const record of Array.isArray(result)?result:[])await log("created",plainAttributes(record));return result};const originalUpdate=baseModel.update;if(typeof originalUpdate==="function")baseModel.update=async function(id,data){const result=await originalUpdate.call(this,id,data);await log("updated",plainAttributes(result)??await readRow(id));return result};const originalDelete=baseModel.delete;if(typeof originalDelete==="function")baseModel.delete=async function(id){const before=await readRow(id),result=await originalDelete.call(this,id);await log("deleted",before??{[primaryKey]:id});return result}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{log}from"@stacksjs/logging";const SENSITIVE_FIELD_PATTERNS=[/^password$/i,/^password_hash$/i,/^api_key$/i,/_token$/i,/_secret$/i,/^remember_token$/i];let explicitActorId=null;export function setTrailActor(id){explicitActorId=id}export function currentTrailActor(){return explicitActorId}export async function resolveTrailActorId(){if(explicitActorId!=null)return explicitActorId;try{const mod=await import("@stacksjs/router").catch(()=>null);if(!mod)return null;const getCurrentRequest=mod.getCurrentRequest;if(typeof getCurrentRequest!=="function")return null;const request=getCurrentRequest();return request?.user?.id??request?._user?.id??null}catch{return null}}export async function resolveTrailIp(){try{const getCurrentRequest=(await import("@stacksjs/router").catch(()=>null))?.getCurrentRequest;if(typeof getCurrentRequest!=="function")return null;const request=getCurrentRequest();return typeof request?.ip==="function"?request.ip()??null:null}catch{return null}}export function redactSensitive(attributes){if(!attributes||typeof attributes!=="object")return null;const safe={};for(const[key,value]of Object.entries(attributes)){if(SENSITIVE_FIELD_PATTERNS.some((pattern)=>pattern.test(key)))continue;safe[key]=value}return safe}export function reportTrailFailure(trail,subject,error){log.warn(`[orm] ${trail} row write failed for ${subject}: ${error?.message??String(error)}`)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{sqlDateTime}from"@stacksjs/database/runtime";import{log}from"@stacksjs/logging";const SENSITIVE_FIELD_PATTERNS=[/^password$/i,/^password_hash$/i,/^api_key$/i,/_token$/i,/_secret$/i,/^remember_token$/i],AUDITS_TABLE="model_audits";let _explicitAuditUserId=null;export function setAuditUser(id){_explicitAuditUserId=id}async function resolveAuditUserId(){if(_explicitAuditUserId!=null)return _explicitAuditUserId;try{const mod=await import("@stacksjs/router").catch(()=>null);if(!mod)return null;const getCurrentRequest=mod.getCurrentRequest;if(typeof getCurrentRequest!=="function")return null;const request=getCurrentRequest();if(!request)return null;return(request.user??request._user)?.id??null}catch{return null}}function redactSensitive(row){if(!row||typeof row!=="object")return null;const out={};for(const[k,v]of Object.entries(row)){if(SENSITIVE_FIELD_PATTERNS.some((rx)=>rx.test(k)))continue;out[k]=v}return out}function plainAttrs(value){if(!value||typeof value!=="object")return null;const v=value;if(v._attributes&&typeof v._attributes==="object")return{...v._attributes};return{...value}}function diffAttrs(before,after){if(!before||!after)return{old:before,next:after};const old={},next={},keys=new Set([...Object.keys(before),...Object.keys(after)]);for(const k of keys)if(before[k]!==after[k]){old[k]=before[k];next[k]=after[k]}return{old,next}}async function writeAuditRow(payload,transactional=!1){try{const{db}=await import("@stacksjs/database/runtime");await db.insertInto(AUDITS_TABLE).values({auditable_type:payload.auditable_type,auditable_id:payload.auditable_id,event:payload.event,old_values:payload.old_values?JSON.stringify(payload.old_values):null,new_values:payload.new_values?JSON.stringify(payload.new_values):null,user_id:payload.user_id,created_at:sqlDateTime()}).executeTakeFirst()}catch(err){log.warn(`[orm] audit row write failed for ${payload.auditable_type}#${String(payload.auditable_id)} (${payload.event})`,{error:err});if(transactional)throw err}}export function createAuditMethods(modelName){return{async audits(id){try{const{db}=await import("@stacksjs/database/runtime");return(await db.selectFrom(AUDITS_TABLE).selectAll().where("auditable_type","=",modelName).where("auditable_id","=",id).orderBy("created_at","desc").execute()??[]).map((r)=>({...r,old_values:typeof r.old_values==="string"?safeJsonParse(r.old_values):r.old_values,new_values:typeof r.new_values==="string"?safeJsonParse(r.new_values):r.new_values}))}catch(err){log.warn(`[orm] audits() lookup failed for ${modelName}#${String(id)}`,{error:err});return[]}}}}function safeJsonParse(value){try{return JSON.parse(value)}catch{return value}}export function resolveAuditOptions(useAudit){if(useAudit&&typeof useAudit==="object"&&"transactional"in useAudit)return{transactional:useAudit.transactional===!0};return{transactional:!1}}export function applyAudit(baseModel,modelName,primaryKey="id",opts={}){const transactional=opts.transactional===!0,model=baseModel;if(typeof baseModel.audits!=="function")baseModel.audits=createAuditMethods(modelName).audits;const runWithOptionalTx=async(fn)=>{if(!transactional)return await fn();const{db}=await import("@stacksjs/database/runtime");return await db.transaction(async()=>fn())},origCreate=baseModel.create;if(typeof origCreate==="function")baseModel.create=async function(...args){return await runWithOptionalTx(async()=>{const result=await origCreate.apply(this,args);try{const newAttrs=plainAttrs(result),id=newAttrs?.[primaryKey];if(id!=null){const userId=await resolveAuditUserId();await writeAuditRow({auditable_type:modelName,auditable_id:id,event:"created",old_values:null,new_values:redactSensitive(newAttrs),user_id:userId},transactional)}}catch(err){log.warn(`[orm] audit(create) failed for ${modelName}`,{error:err});if(transactional)throw err}return result})};const origCreateMany=baseModel.createMany;if(typeof origCreateMany==="function")baseModel.createMany=async function(...args){return await runWithOptionalTx(async()=>{const result=await origCreateMany.apply(this,args);try{const userId=Array.isArray(result)&&result.length?await resolveAuditUserId():null;for(const record of Array.isArray(result)?result:[]){const newAttrs=plainAttrs(record),id=newAttrs?.[primaryKey];if(id==null)continue;await writeAuditRow({auditable_type:modelName,auditable_id:id,event:"created",old_values:null,new_values:redactSensitive(newAttrs),user_id:userId},transactional)}}catch(err){log.warn(`[orm] audit(createMany) failed for ${modelName}`,{error:err});if(transactional)throw err}return result})};const origUpdate=baseModel.update;if(typeof origUpdate==="function")baseModel.update=async function(id,data){return await runWithOptionalTx(async()=>{const find=model.find,before=typeof find==="function"?plainAttrs(await find.call(model,id)):null,result=await origUpdate.call(this,id,data);try{const after=plainAttrs(result)??(typeof find==="function"?plainAttrs(await find.call(model,id)):null),{old,next}=diffAttrs(before,after);if(next&&Object.keys(next).length>0){const userId=await resolveAuditUserId();await writeAuditRow({auditable_type:modelName,auditable_id:id,event:"updated",old_values:redactSensitive(old),new_values:redactSensitive(next),user_id:userId},transactional)}}catch(err){log.warn(`[orm] audit(update) failed for ${modelName}#${String(id)}`,{error:err});if(transactional)throw err}return result})};const wrapDelete=(key)=>{const orig=baseModel[key];if(typeof orig!=="function")return;baseModel[key]=async function(id){return await runWithOptionalTx(async()=>{const find=model.find,before=typeof find==="function"?plainAttrs(await find.call(model,id)):null,result=await orig.call(this,id);try{if(result!==!1&&before){const userId=await resolveAuditUserId();await writeAuditRow({auditable_type:modelName,auditable_id:id,event:"deleted",old_values:redactSensitive(before),new_values:null,user_id:userId},transactional)}}catch(err){log.warn(`[orm] audit(${key}) failed for ${modelName}#${String(id)}`,{error:err});if(transactional)throw err}return result})}};wrapDelete("delete");wrapDelete("softDelete")}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createRequire}from"node:module";import{db as _db}from"@stacksjs/database/runtime";const require=createRequire(import.meta.url);export class SubscriptionNotOwnedError extends Error{constructor(providerId){super(`No subscription ${providerId} belongs to this user`);this.name="SubscriptionNotOwnedError"}}async function driver(){const{paymentDriver}=await import("@stacksjs/payments");return paymentDriver()}async function requireStripe(operation){const{assertStripeDriver}=await import("@stacksjs/payments");assertStripeDriver(operation)}export function createBillableMethods(_tableName){const db=_db;return{async paymentCustomer(model){return(await driver()).customer(model)},async charge(model,amount,paymentMethod,options={}){return(await driver()).charge(model,amount,paymentMethod,options)},async createPayment(model,amount,options={}){return(await driver()).createPayment(model,amount,options)},async checkout(model,request){return(await driver()).checkout(model,request)},async paymentMethods(model){return(await driver()).paymentMethods(model)},async removePaymentMethod(model,paymentMethodId){return(await driver()).removePaymentMethod(model,paymentMethodId)},async newSubscription(model,type,price){return(await driver()).subscribe(model,price,{type})},async cancelSubscription(model,providerId,options={}){if(!await db.selectFrom("subscriptions").where("provider_id","=",providerId).where("user_id","=",model.id).select(["id"]).executeTakeFirst())throw new SubscriptionNotOwnedError(providerId);return(await driver()).cancelSubscription(providerId,options)},async activeSubscription(model){const subscription=await db.selectFrom("subscriptions").where("user_id","=",model.id).where("provider_status","=","active").selectAll().executeTakeFirst();if(!subscription)return;const providerSubscription=(await(await driver()).subscriptions(model)).find((entry)=>entry.id===subscription.provider_id);return providerSubscription?{subscription,providerSubscription}:void 0},async syncStripeCustomerDetails(model,options){await requireStripe("syncStripeCustomerDetails");const{manageCustomer}=await import("@stacksjs/payments");return await manageCustomer.syncStripeCustomerDetails(model,options)},async createStripeUser(model,options){await requireStripe("createStripeUser");const{manageCustomer}=await import("@stacksjs/payments");return await manageCustomer.createStripeCustomer(model,options)},async updateStripeUser(model,options){await requireStripe("updateStripeUser");const{manageCustomer}=await import("@stacksjs/payments");return await manageCustomer.updateStripeCustomer(model,options)},async deleteStripeUser(model){await requireStripe("deleteStripeUser");const{manageCustomer}=await import("@stacksjs/payments");return await manageCustomer.deleteStripeUser(model)},async createOrGetStripeUser(model,options){await requireStripe("createOrGetStripeUser");const{manageCustomer}=await import("@stacksjs/payments");return await manageCustomer.createOrGetStripeUser(model,options)},async retrieveStripeUser(model){await requireStripe("retrieveStripeUser");const{manageCustomer}=await import("@stacksjs/payments");return await manageCustomer.retrieveStripeUser(model)},async defaultPaymentMethod(model){const{managePaymentMethod}=await import("@stacksjs/payments");return await managePaymentMethod.retrieveDefaultPaymentMethod(model)},async setDefaultPaymentMethod(model,paymentMethod){await requireStripe("setDefaultPaymentMethod");const{managePaymentMethod}=await import("@stacksjs/payments");if(typeof paymentMethod==="string")await managePaymentMethod.setUserDefaultPayment(model,paymentMethod);else await managePaymentMethod.setDefaultPaymentMethod(model,paymentMethod)},async addPaymentMethod(model,paymentMethodId){await requireStripe("addPaymentMethod");const{managePaymentMethod,toStoredPaymentMethod}=await import("@stacksjs/payments");return toStoredPaymentMethod(await managePaymentMethod.addPaymentMethod(model,paymentMethodId))},async updateSubscription(model,type,price){await requireStripe("updateSubscription");const{manageSubscription,summarizeSubscription}=await import("@stacksjs/payments");return summarizeSubscription(await manageSubscription.update(model,type,price,{}))},async createSetupIntent(model,options={}){await requireStripe("createSetupIntent");const{manageSetupIntent}=await import("@stacksjs/payments"),intent=await manageSetupIntent.create(model,options);if(!intent.client_secret)throw Error("Stripe returned a SetupIntent without a client secret.");return{id:intent.id,clientConfirmation:{provider:"stripe",clientSecret:intent.client_secret}}},async storeTransaction(model,productId,options){const{manageTransaction}=await import("@stacksjs/payments");return await manageTransaction.store(model,productId,options)},async subscriptionHistory(model){await requireStripe("subscriptionHistory");const{manageInvoice}=await import("@stacksjs/payments");return await manageInvoice.list(model)},async transactionHistory(model){const{manageTransaction}=await import("@stacksjs/payments");return await manageTransaction.list(model)},async connectAccount(model,options={}){await requireStripe("Stripe Connect (connectAccount)");const col=options.accountColumn||"stripe_account_id",accountId=(model._attributes??model)[col];if(!accountId)return null;const stripe=await getStripe();try{return await stripe.accounts.retrieve(accountId)}catch{return null}},async createConnectAccount(model,options={}){await requireStripe("Stripe Connect (createConnectAccount)");const col=options.accountColumn||"stripe_account_id",attrs=model._attributes??model;let accountId=attrs[col];const stripe=await getStripe();if(!accountId){const{stacksIdempotencyKey}=await import("@stacksjs/payments"),tableName=options.modelTable||_tableName,account=await stripe.accounts.create({type:options.type??"express",email:options.email??attrs.email,country:options.country??"US",capabilities:options.capabilities??{card_payments:{requested:!0},transfers:{requested:!0}}},{idempotencyKey:stacksIdempotencyKey("connect.account.create",tableName,attrs.id)});accountId=account.id;await db.updateTable(tableName).set({[col]:accountId}).where("id","=",Number(attrs.id)).execute();return account}return await stripe.accounts.retrieve(accountId)},async connectOnboardLink(model,options){await requireStripe("Stripe Connect (connectOnboardLink)");const col=options.accountColumn||"stripe_account_id",accountId=(model._attributes??model)[col];if(!accountId)throw Error("connectOnboardLink: model has no Stripe Connect account; call createConnectAccount first");return{url:(await(await getStripe()).accountLinks.create({account:accountId,refresh_url:options.refreshUrl,return_url:options.returnUrl,type:options.type??"account_onboarding"})).url,accountId}},async syncConnectStatus(model,options={}){await requireStripe("Stripe Connect (syncConnectStatus)");const accCol=options.accountColumn||"stripe_account_id",chargesCol=options.chargesColumn||"charges_enabled",payoutsCol=options.payoutsColumn||"payouts_enabled",attrs=model._attributes??model,accountId=attrs[accCol];if(!accountId)return{chargesEnabled:!1,payoutsEnabled:!1};const account=await(await getStripe()).accounts.retrieve(accountId),chargesEnabled=!!account.charges_enabled,payoutsEnabled=!!account.payouts_enabled,tableName=options.modelTable||_tableName;await db.updateTable(tableName).set({[chargesCol]:chargesEnabled,[payoutsCol]:payoutsEnabled}).where("id","=",Number(attrs.id)).execute();return{chargesEnabled,payoutsEnabled}},async chargeWithSplit(model,options){await requireStripe("Stripe Connect (chargeWithSplit)");const stripe=await getStripe(),accCol=options.accountColumn||"stripe_account_id",attrs=model._attributes??model,destination=options.connectAccountId||attrs[accCol],params={amount:options.amountCents,currency:options.currency||"usd",automatic_payment_methods:{enabled:!0},metadata:options.metadata};if(destination){params.transfer_data={destination};if(options.platformFeeCents)params.application_fee_amount=options.platformFeeCents}const{freshIdempotencyKey}=await import("@stacksjs/payments");return await stripe.paymentIntents.create(params,{idempotencyKey:freshIdempotencyKey("payment_intent.create",attrs.id,options.amountCents)})}}}const BILLABLE_METHOD_NAMES={syncStripeCustomerDetails:!0,createStripeUser:!0,updateStripeUser:!0,deleteStripeUser:!0,createOrGetStripeUser:!0,retrieveStripeUser:!0,defaultPaymentMethod:!0,setDefaultPaymentMethod:!0,removePaymentMethod:!0,addPaymentMethod:!0,paymentMethods:!0,paymentCustomer:!0,charge:!0,newSubscription:!0,updateSubscription:!0,cancelSubscription:!0,activeSubscription:!0,checkout:!0,createSetupIntent:!0,createPayment:!0,storeTransaction:!0,subscriptionHistory:!0,transactionHistory:!0,connectAccount:!0,createConnectAccount:!0,connectOnboardLink:!0,syncConnectStatus:!0,chargeWithSplit:!0};export const BILLABLE_INSTANCE_METHODS=Object.freeze(Object.keys(BILLABLE_METHOD_NAMES));export function isBillable(model){return BILLABLE_INSTANCE_METHODS.every((name)=>typeof Reflect.get(model,name)==="function")}function getStripe(){const secret=globalThis.process?.env?.STRIPE_SECRET_KEY;if(!secret)throw Error("Stripe Connect: STRIPE_SECRET_KEY not configured");let StripeCtor;try{StripeCtor=require("stripe")}catch{throw Error("Stripe Connect is being used but the `stripe` package is not installed. It is an opt-in dependency - run `bun add stripe` to enable Stripe Connect.")}return new StripeCtor(secret,{apiVersion:StripeCtor.API_VERSION})}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{db as _db,sqlDateTime}from"@stacksjs/database/runtime";export function createCategorizableMethods(tableName){const db=_db;async function getCategoryIds(id){return(await db.selectFrom("categorizable_models").where("categorizable_id","=",id).where("categorizable_type","=",tableName).selectAll().execute()).map((link)=>link.category_id)}return{async categories(id){const categoryIds=await getCategoryIds(id);if(categoryIds.length===0)return[];return await db.selectFrom("categorizables").where("id","in",categoryIds).selectAll().execute()},async categoryCount(id){return(await getCategoryIds(id)).length},async addCategory(id,category){const findCategory=()=>db.selectFrom("categorizables").where("name","=",category.name).where("categorizable_type","=",tableName).selectAll().executeTakeFirst();let categoryRecord=await findCategory();if(!categoryRecord){await db.insertInto("categorizables").values({name:category.name,description:category.description,slug:category.name.toLowerCase().replace(/[^a-z0-9]+/g,"-"),categorizable_type:tableName,is_active:!0,created_at:sqlDateTime(),updated_at:sqlDateTime()}).execute();categoryRecord=await findCategory()}await db.insertInto("categorizable_models").values({categorizable_id:id,categorizable_type:tableName,category_id:categoryRecord?.id,created_at:sqlDateTime(),updated_at:sqlDateTime()}).execute();return categoryRecord},async activeCategories(id){const categoryIds=await getCategoryIds(id);if(categoryIds.length===0)return[];return await db.selectFrom("categorizables").where("id","in",categoryIds).where("is_active","=",!0).selectAll().execute()},async inactiveCategories(id){const categoryIds=await getCategoryIds(id);if(categoryIds.length===0)return[];return await db.selectFrom("categorizables").where("id","in",categoryIds).where("is_active","=",!1).selectAll().execute()},async removeCategory(id,categoryId){await db.deleteFrom("categorizable_models").where("categorizable_id","=",id).where("categorizable_type","=",tableName).where("category_id","=",categoryId).execute()}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{db as _db,sql,sqlDateTime}from"@stacksjs/database/runtime";function assertId(id,method){if(typeof id!=="number"||!Number.isFinite(id)||id<=0)throw Error(`[orm/commentable] ${method} requires a positive numeric id (received ${String(id)})`)}const UPVOTES_TABLE="commentable_upvotes",COMMENT_UPVOTE_TYPE="commentables";function isDuplicateError(err){const e=err;return e.code==="SQLITE_CONSTRAINT_UNIQUE"||e.code==="SQLITE_CONSTRAINT"||e.code==="23505"||e.errno===1062||/unique|duplicate/i.test(e.message??"")}const COMMENT_STATUSES_ON_CREATE=["pending","approved"];export function createCommentableMethods(tableName){const db=_db;return{async comments(id){assertId(id,"comments");return await db.selectFrom("commentables").where("commentables_id","=",id).where("commentables_type","=",tableName).selectAll().orderBy("id","asc").execute()},async commentCount(id){const result=await db.selectFrom("commentables").select(sql`count(*) as count`).where("commentables_id","=",id).where("commentables_type","=",tableName).executeTakeFirst();return Number(result?.count)||0},async addComment(id,comment,options={}){assertId(id,"addComment");if(!comment||typeof comment.body!=="string"||comment.body.trim().length===0)throw Error("[orm/commentable] addComment requires a non-empty comment.body");if(comment.title!=null&&typeof comment.title!=="string")throw Error("[orm/commentable] addComment requires comment.title to be a string when given");const status=options.status??"pending";if(!COMMENT_STATUSES_ON_CREATE.includes(status))throw Error(`[orm/commentable] addComment status must be one of ${COMMENT_STATUSES_ON_CREATE.join(", ")} (received ${String(status)})`);const values={title:comment.title?.trim()??"",body:comment.body,commentables_id:id,commentables_type:tableName,status,approved_at:status==="approved"?Date.now():null,created_at:sqlDateTime(),updated_at:sqlDateTime()};for(const key of["author_name","author_email","user_id"])if(comment[key]!=null)values[key]=comment[key];const written=await db.insertInto("commentables").values(values).returningAll().executeTakeFirst();if(written&&typeof written==="object"&&"id"in written)return written;const insertedId=written?.lastInsertRowid;if(insertedId==null)return written;return await db.selectFrom("commentables").where("id","=",Number(insertedId)).selectAll().executeTakeFirst()},async approvedComments(id){return await db.selectFrom("commentables").where("commentables_id","=",id).where("commentables_type","=",tableName).where("status","=","approved").selectAll().orderBy("id","asc").execute()},async pendingComments(id){return await db.selectFrom("commentables").where("commentables_id","=",id).where("commentables_type","=",tableName).where("status","=","pending").selectAll().orderBy("id","asc").execute()},async rejectedComments(id){return await db.selectFrom("commentables").where("commentables_id","=",id).where("commentables_type","=",tableName).where("status","=","rejected").selectAll().orderBy("id","asc").execute()},async upvoteComment(commentId,userId){assertId(commentId,"upvoteComment");assertId(userId,"upvoteComment");const existing=()=>db.selectFrom(UPVOTES_TABLE).where("upvoteable_id","=",commentId).where("upvoteable_type","=",COMMENT_UPVOTE_TYPE).where("user_id","=",userId).selectAll().executeTakeFirst(),already=await existing();if(already)return already;try{await db.insertInto(UPVOTES_TABLE).values({upvoteable_id:commentId,upvoteable_type:COMMENT_UPVOTE_TYPE,user_id:userId,created_at:sqlDateTime()}).execute()}catch(err){if(!isDuplicateError(err))throw err}return await existing()},async removeCommentUpvote(commentId,userId){assertId(commentId,"removeCommentUpvote");assertId(userId,"removeCommentUpvote");await db.deleteFrom(UPVOTES_TABLE).where("upvoteable_id","=",commentId).where("upvoteable_type","=",COMMENT_UPVOTE_TYPE).where("user_id","=",userId).execute()},async commentUpvoteCount(commentId){assertId(commentId,"commentUpvoteCount");const result=await db.selectFrom(UPVOTES_TABLE).select("count(*) as count").where("upvoteable_id","=",commentId).where("upvoteable_type","=",COMMENT_UPVOTE_TYPE).executeTakeFirst();return Number(result?.count)||0},async hasUpvotedComment(commentId,userId){assertId(commentId,"hasUpvotedComment");assertId(userId,"hasUpvotedComment");return!!await db.selectFrom(UPVOTES_TABLE).where("upvoteable_id","=",commentId).where("upvoteable_type","=",COMMENT_UPVOTE_TYPE).where("user_id","=",userId).selectAll().executeTakeFirst()}}}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type { BillableMethods } from './billable';
|
|
2
|
+
export type { SoftDeleteOptions, SoftDeleteHelpers } from './soft-deletes';
|
|
3
|
+
export type { AuditHelpers } from './audit';
|
|
4
|
+
export { createTaggableMethods } from './taggable';
|
|
5
|
+
export { createCategorizableMethods } from './categorizable';
|
|
6
|
+
export { createCommentableMethods } from './commentable';
|
|
7
|
+
export { BILLABLE_INSTANCE_METHODS, createBillableMethods, isBillable, SubscriptionNotOwnedError } from './billable';
|
|
8
|
+
export { createLikeableMethods } from './likeable';
|
|
9
|
+
export { createTwoFactorMethods } from './two-factor';
|
|
10
|
+
export { createSoftDeleteMethods, resolveSoftDeleteOptions, cascadeSoftDelete } from './soft-deletes';
|
|
11
|
+
export { createAuditMethods, applyAudit, setAuditUser } from './audit';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export{createTaggableMethods}from"./taggable";export{createCategorizableMethods}from"./categorizable";export{createCommentableMethods}from"./commentable";export{BILLABLE_INSTANCE_METHODS,createBillableMethods,isBillable,SubscriptionNotOwnedError}from"./billable";export{createLikeableMethods}from"./likeable";export{createTwoFactorMethods}from"./two-factor";export{createSoftDeleteMethods,resolveSoftDeleteOptions,cascadeSoftDelete}from"./soft-deletes";export{createAuditMethods,applyAudit,setAuditUser}from"./audit";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{db as _db,sqlDateTime}from"@stacksjs/database/runtime";export function createLikeableMethods(tableName,options){const db=_db,likeTable=options?.table||`${tableName}_likes`,foreignKey=options?.foreignKey||`${tableName.replace(/s$/,"")}_id`;return{async likes(id){return await db.selectFrom(likeTable).where(foreignKey,"=",id).selectAll().execute()},async likeCount(id){const result=await db.selectFrom(likeTable).select("count(*) as count").where(foreignKey,"=",id).executeTakeFirst();return Number(result?.count)||0},async like(id,userId){if(typeof id!=="number"||!Number.isFinite(id)||id<=0)throw Error(`[orm/likeable] like requires a positive numeric id (received ${String(id)})`);if(typeof userId!=="number"||!Number.isFinite(userId)||userId<=0)throw Error(`[orm/likeable] like requires a positive numeric userId (received ${String(userId)})`);const now=sqlDateTime();try{return await db.insertInto(likeTable).values({[foreignKey]:id,user_id:userId,created_at:now,updated_at:now}).returningAll().executeTakeFirst()}catch(err){const e=err;if(!(e.code==="SQLITE_CONSTRAINT_UNIQUE"||e.code==="SQLITE_CONSTRAINT"||e.code==="23505"||e.errno===1062||/unique|duplicate/i.test(e.message??"")))throw err;const existing=await db.selectFrom(likeTable).where(foreignKey,"=",id).where("user_id","=",userId).selectAll().executeTakeFirst();if(existing)return existing;throw err}},async unlike(id,userId){await db.deleteFrom(likeTable).where(foreignKey,"=",id).where("user_id","=",userId).execute()},async isLiked(id,userId){return!!await db.selectFrom(likeTable).where(foreignKey,"=",id).where("user_id","=",userId).selectAll().executeTakeFirst()},async likedBy(userId){return(await db.selectFrom(likeTable).select([foreignKey]).where("user_id","=",userId).execute()).map((r)=>Number(r[foreignKey])).filter(Boolean)}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mutationCount,sqlDateTime}from"@stacksjs/database/runtime";import{log}from"@stacksjs/logging";const DELETED_AT_COLUMN="deleted_at";export function createSoftDeleteMethods(model,primaryKey="id"){return{async softDelete(id){const now=sqlDateTime(),q=model.where(primaryKey,id);if(typeof q?.update==="function"){await q.update({[DELETED_AT_COLUMN]:now});return!0}return!1},async restore(id){const q=model.where(primaryKey,id).withTrashed();if(typeof q?.update==="function"){await q.update({[DELETED_AT_COLUMN]:null});return!0}return!1},async forceDelete(id){const q=model.where(primaryKey,id),scoped=typeof q?.withTrashed==="function"?q.withTrashed():q;if(typeof scoped?.forceDelete==="function")return mutationCount(await scoped.forceDelete())>0;return!1},withTrashed(){return typeof model.query==="function"?model.query():model},onlyTrashed(){const q=typeof model.query==="function"?model.query():model;if(typeof q?.whereNotNull==="function")return q.whereNotNull(DELETED_AT_COLUMN);return q?.where?.(DELETED_AT_COLUMN,"!=",null)??q}}}export function resolveSoftDeleteOptions(value){if(value===!0||value==null)return{};if(typeof value==="object"&&!Array.isArray(value)){const v=value;if(Array.isArray(v.cascade)){const cascade=v.cascade.filter((x)=>typeof x==="string");return cascade.length?{cascade}:{}}return{}}return{}}function resolveChildModel(parentDefinition,relationName){const hasMany=parentDefinition.hasMany??[],hasOne=parentDefinition.hasOne??[],isHasMany=hasMany.some((c)=>relationMatches(c,relationName,!0)),isHasOne=hasOne.some((c)=>relationMatches(c,relationName,!1));if(!isHasMany&&!isHasOne){log.warn(`[orm] soft-delete cascade: '${relationName}' is not a declared hasMany/hasOne relation - skipping`);return null}const className=(isHasMany?hasMany:hasOne).find((c)=>relationMatches(c,relationName,isHasMany))??null;if(!className)return null;const model=globalThis[className];if(!model){log.warn(`[orm] soft-delete cascade: model '${className}' is not yet on globalThis - skipping cascade for '${relationName}'`);return null}return{className,model}}function relationMatches(className,relationName,plural){const base=className.toLowerCase();if(relationName===base)return!0;if(plural&&relationName===`${base}s`)return!0;if(plural&&base.endsWith("y")&&relationName===`${base.slice(0,-1)}ies`)return!0;return!1}async function cascadeChildren(parentClassName,parentId,childModel,foreignKey,action){try{const where=childModel.where;if(typeof where!=="function"){log.warn(`[orm] cascade ${action}: child model has no .where()`);return}if(action==="softDelete"){const childSoftDelete=childModel.softDelete;if(typeof childSoftDelete==="function"){const q=where.call(childModel,foreignKey,parentId),rows=typeof q?.get==="function"?await q.get():Array.isArray(q)?q:[],childPk=childModel.primaryKey||"id";for(const r of rows){const id=r?.[childPk];if(id!=null)await childSoftDelete.call(childModel,id)}return}const q=where.call(childModel,foreignKey,parentId);if(typeof q?.delete==="function"){log.debug(`[orm] cascade soft-delete: child has no softDeletes trait, hard-deleting from ${String(childModel.table??"?")}`);await q.delete();return}}else{const childRestore=childModel.restore;if(typeof childRestore!=="function"){log.debug("[orm] cascade restore: child has no restore() - nothing to do");return}const withTrashed=childModel.withTrashed,q=(typeof withTrashed==="function"?withTrashed.call(childModel):childModel).where?.(foreignKey,parentId),rows=typeof q?.get==="function"?await q.get():[],childPk=childModel.primaryKey||"id";for(const r of rows){const id=r?.[childPk];if(id!=null)await childRestore.call(childModel,id)}}}catch(err){log.warn(`[orm] soft-delete cascade ${action} on ${parentClassName}#${String(parentId)} failed`,{error:err});if(process.env.STACKS_ORM_CASCADE_SWALLOW!=="true")throw err}}export async function cascadeSoftDelete(parentDefinition,options,parentId,action){const cascade=options.cascade;if(!cascade||cascade.length===0)return;const parentFk=`${parentDefinition.name.toLowerCase()}_id`;for(const relationName of cascade){const resolved=resolveChildModel(parentDefinition,relationName);if(!resolved)continue;await cascadeChildren(resolved.className,parentId,resolved.model,parentFk,action)}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{db as _db,sql,sqlDateTime}from"@stacksjs/database/runtime";function assertId(id,method){if(typeof id!=="number"||!Number.isFinite(id)||id<=0)throw Error(`[orm/taggable] ${method} requires a positive numeric id (received ${String(id)})`)}export function createTaggableMethods(tableName){const db=_db;return{async tags(id){assertId(id,"tags");return await db.selectFrom("taggables").where("taggable_id","=",id).where("taggable_type","=",tableName).selectAll().execute()},async tagCount(id){assertId(id,"tagCount");const result=await db.selectFrom("taggables").select(sql`count(*) as count`).where("taggable_id","=",id).where("taggable_type","=",tableName).executeTakeFirst();return Number(result?.count)||0},async addTag(id,tag){assertId(id,"addTag");if(!tag||typeof tag.name!=="string"||tag.name.trim().length===0)throw Error("[orm/taggable] addTag requires a non-empty tag.name");await db.insertInto("taggables").values({...tag,taggable_id:id,taggable_type:tableName,slug:tag.name.toLowerCase().replace(/[^a-z0-9]+/g,"-"),is_active:!0,created_at:sqlDateTime(),updated_at:sqlDateTime()}).execute();return await db.selectFrom("taggables").where("taggable_id","=",id).where("taggable_type","=",tableName).where("name","=",tag.name).selectAll().executeTakeFirst()},async activeTags(id){return await db.selectFrom("taggables").where("taggable_id","=",id).where("taggable_type","=",tableName).where("is_active","=",!0).selectAll().execute()},async inactiveTags(id){return await db.selectFrom("taggables").where("taggable_id","=",id).where("taggable_type","=",tableName).where("is_active","=",!1).selectAll().execute()},async removeTag(id,tagId){await db.deleteFrom("taggables").where("taggable_id","=",id).where("taggable_type","=",tableName).where("id","=",tagId).execute()}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const TWO_FACTOR_CODE_VALIDITY_SECONDS=90;export function createTwoFactorMethods(){return{async generateTwoFactorForModel(model){const{generateTwoFactorSecret}=await import("@stacksjs/auth"),secret=generateTwoFactorSecret();await model.update({two_factor_secret:secret})},async verifyTwoFactorCode(model,code){const modelTwoFactorSecret=model.get("two_factor_secret");if(typeof modelTwoFactorSecret!=="string")return!1;const{verifyTwoFactorCode,RateLimiter}=await import("@stacksjs/auth"),identity=String(model.id??model.get?.("id")??model.get?.("email")??modelTwoFactorSecret),rlKey=`2fa:${identity}`;if(await RateLimiter.isRateLimited(rlKey)){const{HttpError}=await import("@stacksjs/error-handling");throw new HttpError(429,"Too many verification attempts. Please try again later.")}if(!await verifyTwoFactorCode(code,modelTwoFactorSecret)){await RateLimiter.recordFailedAttempt(rlKey);return!1}try{const{cache}=await import("@stacksjs/cache"),replayKey=`2fa:used:${identity}:${code}`;if(await cache.has(replayKey)){await RateLimiter.recordFailedAttempt(rlKey);return!1}await cache.set(replayKey,"1",TWO_FACTOR_CODE_VALIDITY_SECONDS)}catch{}await RateLimiter.resetAttempts(rlKey);return!0}}}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{db}from"@stacksjs/database/runtime";export async function transaction(callback,options){return await db.transaction(callback,options)}export async function savepoint(callback){return await db.savepoint(callback)}export function transactional(fn,options){return db.transactional(fn,options)}
|
package/dist/types.js
ADDED
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const BUNDLED_ROOT=/(?:^|[/\\])(?:\$bunfs|~BUN)(?:[/\\]|$)/;export function isCompiledBinary(main=Bun.main){if(!main)return!1;return BUNDLED_ROOT.test(main)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createCipheriv,createDecipheriv,createHash,randomBytes}from"node:crypto";import{log}from"@stacksjs/logging";const PREFIX="enc:",ALGO="aes-256-gcm",IV_LEN=12;let _cachedKey=null;async function getKey(){if(_cachedKey)return _cachedKey;let raw=process.env.APP_KEY;if(!raw)try{raw=(await import("@stacksjs/config").catch(()=>null))?.config?.app?.key}catch{}if(!raw)return null;_cachedKey=createHash("sha256").update(raw).digest();return _cachedKey}export function _clearEncryptedKeyCache(){_cachedKey=null}export function isEncrypted(value){return typeof value==="string"&&value.startsWith(PREFIX)}export async function encryptValue(value){if(value==null)return value;if(isEncrypted(value))return value;if(typeof value!=="string")return value;const key=await getKey();if(!key){log.warn("[orm] encrypted attribute: APP_KEY not set - storing value in plaintext");return value}const iv=randomBytes(IV_LEN),cipher=createCipheriv(ALGO,key,iv),ct=Buffer.concat([cipher.update(value,"utf8"),cipher.final()]),tag=cipher.getAuthTag();return`${PREFIX}${iv.toString("base64")}:${tag.toString("base64")}:${ct.toString("base64")}`}export async function decryptValue(value){if(value==null)return value;if(typeof value!=="string")return value;if(!isEncrypted(value))return value;try{const key=await getKey();if(!key){log.warn("[orm] encrypted attribute: APP_KEY not set - cannot decrypt");return null}const[,ivB64,tagB64,ctB64]=value.split(":");if(!ivB64||!tagB64||!ctB64){log.warn("[orm] encrypted attribute: malformed envelope, returning null");return null}const iv=Buffer.from(ivB64,"base64"),tag=Buffer.from(tagB64,"base64"),ct=Buffer.from(ctB64,"base64"),decipher=createDecipheriv(ALGO,key,iv);decipher.setAuthTag(tag);return Buffer.concat([decipher.update(ct),decipher.final()]).toString("utf8")}catch(err){log.warn("[orm] encrypted attribute: decrypt failed (key rotation? corrupted ciphertext?)",{error:err});return null}}export async function encryptRowForWrite(row,encryptedKeys){if(!row||typeof row!=="object"||encryptedKeys.length===0)return row;const out={...row};for(const key of encryptedKeys)if(key in out)out[key]=await encryptValue(out[key]);return out}export async function decryptRowForRead(row,encryptedKeys){if(!row||typeof row!=="object"||encryptedKeys.length===0)return row;const out={...row};for(const key of encryptedKeys)if(key in out)out[key]=await decryptValue(out[key]);return out}export function collectEncryptedAttributes(definition){const attrs=definition.attributes;if(!attrs||typeof attrs!=="object")return[];const out=[];for(const[name,def]of Object.entries(attrs))if(def&&typeof def==="object"&&def.encrypted===!0)out.push(name);return out}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{log}from"@stacksjs/cli";import{db,mutationCount}from"@stacksjs/database/runtime";export async function prunable(tableName,options={}){const{olderThanDays=30,column="created_at",query}=options;try{if(query){let qb=db.deleteFrom(tableName);qb=query(qb);const count=mutationCount(await qb.execute());log.info(`Pruned ${count} records from ${tableName}`);return count}const cutoffDate=new Date;cutoffDate.setDate(cutoffDate.getDate()-olderThanDays);const result=await db.deleteFrom(tableName).where(column,"<",cutoffDate.toISOString()).execute(),count=mutationCount(result);log.info(`Pruned ${count} records from ${tableName} older than ${olderThanDays} days`);return count}catch(error){log.error(`Failed to prune ${tableName}:`,error);throw error}}export async function massPrunable(tableName,options={}){return prunable(tableName,options)}export function getPrunableConfig(traits){if(!traits?.prunable)return null;if(typeof traits.prunable==="boolean")return{olderThanDays:30,column:"created_at"};return traits.prunable}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/orm",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.75.
|
|
5
|
+
"version": "0.75.110",
|
|
6
6
|
"description": "The Stacks ORM integration",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -59,35 +59,35 @@
|
|
|
59
59
|
"scripts": {
|
|
60
60
|
"build": "bun build.ts",
|
|
61
61
|
"typecheck": "bun tsc --noEmit",
|
|
62
|
-
"typecheck:inference": "
|
|
62
|
+
"typecheck:inference": "(cd ../validation && bun run build) && bun run build && (cd ../faker && bun run build) && (cd ../query-builder && bun run build) && (cd ../types && bun run build) && bun tsc -p tsconfig.type-tests.json",
|
|
63
63
|
"prepublishOnly": "bun run build"
|
|
64
64
|
},
|
|
65
65
|
"dependencies": {
|
|
66
|
-
"@stacksjs/cache": "0.75.
|
|
67
|
-
"@stacksjs/cli": "0.75.
|
|
68
|
-
"@stacksjs/config": "0.75.
|
|
69
|
-
"@stacksjs/database": "0.75.
|
|
70
|
-
"@stacksjs/env": "0.75.
|
|
71
|
-
"@stacksjs/error-handling": "0.75.
|
|
72
|
-
"@stacksjs/events": "0.75.
|
|
73
|
-
"@stacksjs/logging": "0.75.
|
|
74
|
-
"@stacksjs/model-meta": "0.75.
|
|
75
|
-
"@stacksjs/pagination": "0.75.
|
|
76
|
-
"@stacksjs/path": "0.75.
|
|
77
|
-
"@stacksjs/query-builder": "0.75.
|
|
78
|
-
"@stacksjs/realtime": "0.75.
|
|
79
|
-
"@stacksjs/router": "0.75.
|
|
80
|
-
"@stacksjs/storage": "0.75.
|
|
81
|
-
"@stacksjs/strings": "0.75.
|
|
66
|
+
"@stacksjs/cache": "0.75.110",
|
|
67
|
+
"@stacksjs/cli": "0.75.110",
|
|
68
|
+
"@stacksjs/config": "0.75.110",
|
|
69
|
+
"@stacksjs/database": "0.75.110",
|
|
70
|
+
"@stacksjs/env": "0.75.110",
|
|
71
|
+
"@stacksjs/error-handling": "0.75.110",
|
|
72
|
+
"@stacksjs/events": "0.75.110",
|
|
73
|
+
"@stacksjs/logging": "0.75.110",
|
|
74
|
+
"@stacksjs/model-meta": "0.75.110",
|
|
75
|
+
"@stacksjs/pagination": "0.75.110",
|
|
76
|
+
"@stacksjs/path": "0.75.110",
|
|
77
|
+
"@stacksjs/query-builder": "0.75.110",
|
|
78
|
+
"@stacksjs/realtime": "0.75.110",
|
|
79
|
+
"@stacksjs/router": "0.75.110",
|
|
80
|
+
"@stacksjs/storage": "0.75.110",
|
|
81
|
+
"@stacksjs/strings": "0.75.110",
|
|
82
82
|
"@stacksjs/ts-validation": "^0.5.6",
|
|
83
|
-
"@stacksjs/validation": "0.75.
|
|
83
|
+
"@stacksjs/validation": "0.75.110",
|
|
84
84
|
"bun-query-builder": "^0.3.5"
|
|
85
85
|
},
|
|
86
86
|
"peerDependencies": {
|
|
87
|
-
"@stacksjs/auth": "0.75.
|
|
88
|
-
"@stacksjs/payments": "0.75.
|
|
89
|
-
"@stacksjs/queue": "0.75.
|
|
90
|
-
"@stacksjs/search-engine": "0.75.
|
|
87
|
+
"@stacksjs/auth": "0.75.110",
|
|
88
|
+
"@stacksjs/payments": "0.75.110",
|
|
89
|
+
"@stacksjs/queue": "0.75.110",
|
|
90
|
+
"@stacksjs/search-engine": "0.75.110",
|
|
91
91
|
"stripe": "23.0.0"
|
|
92
92
|
},
|
|
93
93
|
"peerDependenciesMeta": {
|
|
@@ -108,7 +108,7 @@
|
|
|
108
108
|
}
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
|
-
"@stacksjs/types": "0.75.
|
|
111
|
+
"@stacksjs/types": "0.75.110",
|
|
112
112
|
"better-dx": "^0.2.24",
|
|
113
113
|
"stripe": "23.0.0"
|
|
114
114
|
}
|