@stacksjs/newsletter 0.72.4 → 0.72.6
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/index.d.ts +1 -0
- package/dist/index.js +1 -1
- package/dist/usage.d.ts +23 -0
- package/package.json +8 -8
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
var J=import.meta.require;import{db as x,sqlDateTime as Y}from"@stacksjs/database";function T(f){return f.toLowerCase().trim().replace(/[^\w\s-]+/g,"").replace(/\s+/g,"-").replace(/-+/g,"-")}function j(f){return{name:f.name,slug:f.slug??T(f.name),description:f.description??null,status:"active",is_public:f.isPublic===!1?0:1,double_opt_in:f.doubleOptIn===!1?0:1,subscriber_count:0,active_count:0,unsubscribed_count:0,bounced_count:0}}var U={async create(f){let{EmailList:F}=await import("@stacksjs/orm");return F.create(j(f))},async find(f){let{EmailList:F}=await import("@stacksjs/orm");if(typeof f==="number")return F.find(f);return F.where("slug",f).first()},async all(){let{EmailList:f}=await import("@stacksjs/orm");return f.where("status","active").get()},async archive(f){let F=await U.find(f);if(!F)throw Error(`[newsletter] List '${String(f)}' not found`);return F.update({status:"archived"})}};async function V(f){if(f.emailListId)return f.emailListId;if(f.emailListSlug){let F=await U.find(f.emailListSlug);if(!F)throw Error(`[newsletter] List '${f.emailListSlug}' not found`);return F.id}throw Error("[newsletter] Campaign requires emailListId or emailListSlug")}class D extends Error{constructor(f){super(`[newsletter] Campaign ${f} changed before delivery could be queued`);this.name="CampaignStateConflictError"}}function Z(f,F,z){if(typeof f?.get==="function"){let H=f.get(F);if(H!==void 0)return H;return f.get(z)}return f?.[F]??f?.[z]}function B(f){let F=Z(f,"scheduled_at","scheduledAt"),z=Z(f,"updated_at","updatedAt");return{status:String(Z(f,"status","status")||""),scheduledAt:F===null||F===void 0||F===""?null:String(F),updatedAt:z===null||z===void 0||z===""?null:String(z)}}function O(f,F,z,H){return`newsletter:campaign:${f}:${F}:${H||"now"}:${z}`}function M(f){return["draft","scheduled","paused","failed"].includes(String(f||"").toLowerCase())}function w(f){if(Array.isArray(f))return f.reduce((H,N)=>H+w(N),0);if(typeof f==="number"||typeof f==="bigint")return Number(f);let F=f,z=F?.changes??F?.affectedRows??F?.numAffectedRows??F?.numUpdatedRows;if(typeof z==="object"&&z!==null)return Number(z.changes||0);return Number(z||0)}function q(f,F){return F===null?f.whereNull("scheduled_at"):f.where("scheduled_at","=",F)}function v(f,F){return F===null?f.whereNull("updated_at"):f.where("updated_at","=",F)}async function P(f,F,z){let N=await v(q(x.updateTable("campaigns").set({status:z.status,scheduled_at:z.scheduledAt,updated_at:z.updatedAt}).where("id","=",f).where("status","=",F.status),F.scheduledAt),F.updatedAt).execute();if(w(N)!==1)throw new D(f)}async function I(f,F,z){await q(x.updateTable("campaigns").set({status:z.status,scheduled_at:z.scheduledAt,updated_at:Y()}).where("id","=",f).where("status","=",F.status).where("updated_at","=",F.updatedAt),F.scheduledAt).execute()}async function Q(f,F,z,H,N,_=0){let{job:$}=await import("@stacksjs/queue"),G=N?"scheduled":"immediate";try{await $("SendCampaign",{campaignId:f,chunkSize:H.chunkSize??50,dryRun:H.dryRun??!1,...N?{scheduledAt:N}:{}}).onQueue("campaigns").delay(_).withIdempotencyKey(O(f,G,F.attemptId,N)).dispatch()}catch(W){try{await I(f,F,z)}catch(R){throw AggregateError([W,R],`[newsletter] Campaign ${f} could not be dispatched or restored`)}throw W}}function g(f,F){if(!F)return!0;if(String(f.status||"")!=="scheduled")return!1;let z=f.scheduled_at??f.scheduledAt,H=new Date(String(z||"")).getTime(),N=new Date(F).getTime();return Number.isFinite(H)&&Number.isFinite(N)&&H===N}function L(f,F){return{name:f.name,description:f.description??null,type:"email",status:f.scheduledAt?"scheduled":"draft",subject:f.subject,template:f.template,text:f.text??null,from_name:f.fromName??null,from_address:f.fromAddress??null,email_list_id:F,scheduled_at:f.scheduledAt??null,sent_count:0}}function y(f,F){let z={};if(f.name!==void 0)z.name=f.name;if(f.description!==void 0)z.description=f.description;if(f.subject!==void 0)z.subject=f.subject;if(f.template!==void 0)z.template=f.template;if(f.text!==void 0)z.text=f.text;if(f.fromName!==void 0)z.from_name=f.fromName;if(f.fromAddress!==void 0)z.from_address=f.fromAddress;if(F!==void 0)z.email_list_id=F;if(f.scheduledAt!==void 0)z.scheduled_at=f.scheduledAt||null,z.status=f.scheduledAt?"scheduled":"draft";return z}var X={async create(f){let{Campaign:F}=await import("@stacksjs/orm"),z=await V(f);return F.create(L(f,z))},async find(f){let{Campaign:F}=await import("@stacksjs/orm");return F.find(f)},async update(f,F){let z=await X.find(f);if(!z)throw Error(`[newsletter] Campaign ${f} not found`);if(z.status!=="draft"&&z.status!=="scheduled")throw Error(`[newsletter] Cannot edit campaign in status '${z.status}'`);let N=F.emailListId!==void 0||F.emailListSlug!==void 0?await V(F):void 0;return z.update(y(F,N))},async sendNow(f,F={}){let z=await X.find(f);if(!z)throw Error(`[newsletter] Campaign ${f} not found`);if(!M(z.status))throw Error(`[newsletter] Campaign ${f} in status '${z.status}' cannot be queued`);let H=B(z),N={status:"sending",scheduledAt:H.scheduledAt,updatedAt:Y(),attemptId:crypto.randomUUID()};return await P(f,H,N),await Q(f,N,H,F),{ok:!0,campaignId:f}},async schedule(f,F,z={}){let H=await X.find(f);if(!H)throw Error(`[newsletter] Campaign ${f} not found`);if(!M(H.status))throw Error(`[newsletter] Campaign ${f} in status '${H.status}' cannot be scheduled`);let N=F instanceof Date?F:new Date(F);if(!Number.isFinite(N.getTime()))throw Error("[newsletter] scheduledAt must be a valid date");let _=Math.max(0,Math.floor((N.getTime()-Date.now())/1000)),$=N.toISOString(),G=B(H),W={status:_===0?"sending":"scheduled",scheduledAt:$,updatedAt:Y(),attemptId:crypto.randomUUID()};return await P(f,G,W),await Q(f,W,G,z,_>0?$:void 0,_),{ok:!0,campaignId:f,scheduledAt:$}},async cancel(f){let F=await X.find(f);if(!F)throw Error(`[newsletter] Campaign ${f} not found`);if(F.status==="sent")throw Error(`[newsletter] Campaign ${f} already sent. It cannot be cancelled`);let z=B(F),H={status:"cancelled",scheduledAt:z.scheduledAt,updatedAt:Y(),attemptId:crypto.randomUUID()};return await P(f,z,H),X.find(f)}};function k(f){let F=2166136261;for(let z=0;z<f.length;z++)F^=f.charCodeAt(z),F=Math.imul(F,16777619);return F>>>0}function m(f,F){if(F.length===0)throw Error("At least one delivery variant is required");let z=F.reduce((_,$)=>_+$.allocation,0);if(z<=0)throw Error("Delivery variant allocation must be greater than zero");let H=k(f)%z,N=0;for(let _ of F){if(!Number.isFinite(_.allocation)||_.allocation<0)throw Error("Delivery variant allocation must be a non-negative number");if(N+=_.allocation,H<N)return _}return F[F.length-1]}function h(f){return["delivery",f.teamId,f.campaignId,f.channel,f.recipient.trim().toLowerCase(),f.occurrence||"once"].join(":")}function l(f){let F=f.find((z)=>{if(z.limit===null)return!1;return z.used+(z.requested??1)>z.limit});if(F)throw Error(`Usage limit exceeded for ${F.meter}`)}function u(f,F=""){let z=F.toLowerCase();if(["suppressed","unsubscribe","complaint","stop"].some((H)=>z.includes(H)))return"suppressed";if(Number(f)===429||z.includes("rate"))return"rate_limited";if(Number(f)>=500||["timeout","temporary","unavailable"].some((H)=>z.includes(H)))return"transient";return"permanent"}function e(f){if(!f.url||!/^https?:\/\//i.test(f.url))throw Error("[newsletter] List-Unsubscribe URL must be absolute (http/https)");let F=[];if(f.mailto)F.push(`<mailto:${f.mailto}>`);return F.push(`<${f.url}>`),{"List-Unsubscribe":F.join(", "),"List-Unsubscribe-Post":"List-Unsubscribe=One-Click"}}async function r(f){if(f==null){let z=await U.find("default");if(!z)z=await U.create({name:"Default",slug:"default"});return z.id}let F=await U.find(f);if(!F)throw Error(`[newsletter] List '${String(f)}' not found`);return F.id}async function C(f,F={}){if(!f||!f.includes("@"))throw Error("[newsletter] subscribe() requires a valid email address");let{Subscriber:z,EmailListSubscriber:H}=await import("@stacksjs/orm"),N=await r(F.list),_=F.source??"api",$=await z.where("email",f).first();if(!$)$=await z.create({email:f,status:"subscribed",source:_});let G=await H.where("subscriber_id",$.id).where("email_list_id",N).first();if(G){if(G.status==="unsubscribed")await G.update({status:"subscribed",unsubscribed_at:null});return{created:!1,email:f,listId:N,token:G.uuid}}let W=await H.create({subscriber_id:$.id,email_list_id:N,status:"subscribed",source:_});return{created:!0,email:f,listId:N,token:W.uuid}}async function E(f){if(!f)return{ok:!1};let{EmailListSubscriber:F,Subscriber:z}=await import("@stacksjs/orm"),H=await F.where("uuid",f).first();if(!H)return{ok:!1};if(H.status==="unsubscribed")return{ok:!0,alreadyUnsubscribed:!0,email:(await z.find(H.subscriber_id))?.email,listId:H.email_list_id};return await H.update({status:"unsubscribed",unsubscribed_at:new Date().toISOString()}),{ok:!0,email:(await z.find(H.subscriber_id))?.email,listId:H.email_list_id}}async function K(f){let{Subscriber:F,EmailListSubscriber:z}=await import("@stacksjs/orm"),H=await F.where("email",f).first();if(!H)return 0;let N=await z.where("subscriber_id",H.id).where("status","subscribed").get();for(let _ of N)await _.update({status:"unsubscribed",unsubscribed_at:new Date().toISOString()});return N.length}var ff={lists:U,campaigns:X,subscribe:C,unsubscribe:E,unsubscribeAll:K};export{K as unsubscribeAll,E as unsubscribe,C as subscribe,g as shouldRunScheduledCampaign,ff as newsletter,U as lists,j as emailListCreateData,h as deliveryIdempotencyKey,u as classifyDeliveryFailure,M as canQueueCampaignStatus,X as campaigns,y as campaignUpdateData,B as campaignDeliverySnapshot,O as campaignDeliveryDispatchKey,L as campaignCreateData,e as buildUnsubscribeHeaders,l as assertUsageAvailable,m as allocateDeliveryVariant,D as CampaignStateConflictError};
|
|
2
|
+
var $=import.meta.require;import{db as Z,sqlDateTime as H}from"@stacksjs/database";function j(f){return f.toLowerCase().trim().replace(/[^\w\s-]+/g,"").replace(/\s+/g,"-").replace(/-+/g,"-")}function O(f){return{name:f.name,slug:f.slug??j(f.name),description:f.description??null,status:"active",is_public:f.isPublic===!1?0:1,double_opt_in:f.doubleOptIn===!1?0:1,subscriber_count:0,active_count:0,unsubscribed_count:0,bounced_count:0}}var P={async create(f){let{EmailList:r}=await import("@stacksjs/orm");return r.create(O(f))},async find(f){let{EmailList:r}=await import("@stacksjs/orm");if(typeof f==="number")return r.find(f);return r.where("slug",f).first()},async all(){let{EmailList:f}=await import("@stacksjs/orm");return f.where("status","active").get()},async archive(f){let r=await P.find(f);if(!r)throw Error(`[newsletter] List '${String(f)}' not found`);return r.update({status:"archived"})}};async function W(f){if(f.emailListId)return f.emailListId;if(f.emailListSlug){let r=await P.find(f.emailListSlug);if(!r)throw Error(`[newsletter] List '${f.emailListSlug}' not found`);return r.id}throw Error("[newsletter] Campaign requires emailListId or emailListSlug")}class B extends Error{constructor(f){super(`[newsletter] Campaign ${f} changed before delivery could be queued`);this.name="CampaignStateConflictError"}}function _(f,r,o){if(typeof f?.get==="function"){let n=f.get(r);if(n!==void 0)return n;return f.get(o)}return f?.[r]??f?.[o]}function G(f){let r=_(f,"scheduled_at","scheduledAt"),o=_(f,"updated_at","updatedAt");return{status:String(_(f,"status","status")||""),scheduledAt:r===null||r===void 0||r===""?null:String(r),updatedAt:o===null||o===void 0||o===""?null:String(o)}}function E(f,r,o,n){return`newsletter:campaign:${f}:${r}:${n||"now"}:${o}`}function X(f){return["draft","scheduled","paused","failed"].includes(String(f||"").toLowerCase())}function V(f){if(Array.isArray(f))return f.reduce((n,w)=>n+V(w),0);if(typeof f==="number"||typeof f==="bigint")return Number(f);let r=f,o=r?.changes??r?.affectedRows??r?.numAffectedRows??r?.numUpdatedRows;if(typeof o==="object"&&o!==null)return Number(o.changes||0);return Number(o||0)}function v(f,r){return r===null?f.whereNull("scheduled_at"):f.where("scheduled_at","=",r)}function y(f,r){return r===null?f.whereNull("updated_at"):f.where("updated_at","=",r)}async function J(f,r,o){let w=await y(v(Z.updateTable("campaigns").set({status:o.status,scheduled_at:o.scheduledAt,updated_at:o.updatedAt}).where("id","=",f).where("status","=",r.status),r.scheduledAt),r.updatedAt).execute();if(V(w)!==1)throw new B(f)}async function I(f,r,o){await v(Z.updateTable("campaigns").set({status:o.status,scheduled_at:o.scheduledAt,updated_at:H()}).where("id","=",f).where("status","=",r.status).where("updated_at","=",r.updatedAt),r.scheduledAt).execute()}async function Y(f,r,o,n,w,U=0){let{job:x}=await import("@stacksjs/queue"),N=w?"scheduled":"immediate";try{await x("SendCampaign",{campaignId:f,chunkSize:n.chunkSize??50,dryRun:n.dryRun??!1,...w?{scheduledAt:w}:{}}).onQueue("campaigns").delay(U).withIdempotencyKey(E(f,N,r.attemptId,w)).dispatch()}catch(z){try{await I(f,r,o)}catch(T){throw AggregateError([z,T],`[newsletter] Campaign ${f} could not be dispatched or restored`)}throw z}}function g(f,r){if(!r)return!0;if(String(f.status||"")!=="scheduled")return!1;let o=f.scheduled_at??f.scheduledAt,n=new Date(String(o||"")).getTime(),w=new Date(r).getTime();return Number.isFinite(n)&&Number.isFinite(w)&&n===w}function b(f,r){return{name:f.name,description:f.description??null,type:"email",status:f.scheduledAt?"scheduled":"draft",subject:f.subject,template:f.template,text:f.text??null,from_name:f.fromName??null,from_address:f.fromAddress??null,email_list_id:r,scheduled_at:f.scheduledAt??null,sent_count:0}}function L(f,r){let o={};if(f.name!==void 0)o.name=f.name;if(f.description!==void 0)o.description=f.description;if(f.subject!==void 0)o.subject=f.subject;if(f.template!==void 0)o.template=f.template;if(f.text!==void 0)o.text=f.text;if(f.fromName!==void 0)o.from_name=f.fromName;if(f.fromAddress!==void 0)o.from_address=f.fromAddress;if(r!==void 0)o.email_list_id=r;if(f.scheduledAt!==void 0)o.scheduled_at=f.scheduledAt||null,o.status=f.scheduledAt?"scheduled":"draft";return o}var Q={async create(f){let{Campaign:r}=await import("@stacksjs/orm"),o=await W(f);return r.create(b(f,o))},async find(f){let{Campaign:r}=await import("@stacksjs/orm");return r.find(f)},async update(f,r){let o=await Q.find(f);if(!o)throw Error(`[newsletter] Campaign ${f} not found`);if(o.status!=="draft"&&o.status!=="scheduled")throw Error(`[newsletter] Cannot edit campaign in status '${o.status}'`);let w=r.emailListId!==void 0||r.emailListSlug!==void 0?await W(r):void 0;return o.update(L(r,w))},async sendNow(f,r={}){let o=await Q.find(f);if(!o)throw Error(`[newsletter] Campaign ${f} not found`);if(!X(o.status))throw Error(`[newsletter] Campaign ${f} in status '${o.status}' cannot be queued`);let n=G(o),w={status:"sending",scheduledAt:n.scheduledAt,updatedAt:H(),attemptId:crypto.randomUUID()};return await J(f,n,w),await Y(f,w,n,r),{ok:!0,campaignId:f}},async schedule(f,r,o={}){let n=await Q.find(f);if(!n)throw Error(`[newsletter] Campaign ${f} not found`);if(!X(n.status))throw Error(`[newsletter] Campaign ${f} in status '${n.status}' cannot be scheduled`);let w=r instanceof Date?r:new Date(r);if(!Number.isFinite(w.getTime()))throw Error("[newsletter] scheduledAt must be a valid date");let U=Math.max(0,Math.floor((w.getTime()-Date.now())/1000)),x=w.toISOString(),N=G(n),z={status:U===0?"sending":"scheduled",scheduledAt:x,updatedAt:H(),attemptId:crypto.randomUUID()};return await J(f,N,z),await Y(f,z,N,o,U>0?x:void 0,U),{ok:!0,campaignId:f,scheduledAt:x}},async cancel(f){let r=await Q.find(f);if(!r)throw Error(`[newsletter] Campaign ${f} not found`);if(r.status==="sent")throw Error(`[newsletter] Campaign ${f} already sent. It cannot be cancelled`);let o=G(r),n={status:"cancelled",scheduledAt:o.scheduledAt,updatedAt:H(),attemptId:crypto.randomUUID()};return await J(f,o,n),Q.find(f)}};function k(f){let r=2166136261;for(let o=0;o<f.length;o++)r^=f.charCodeAt(o),r=Math.imul(r,16777619);return r>>>0}function u(f,r){if(r.length===0)throw Error("At least one delivery variant is required");let o=r.reduce((U,x)=>U+x.allocation,0);if(o<=0)throw Error("Delivery variant allocation must be greater than zero");let n=k(f)%o,w=0;for(let U of r){if(!Number.isFinite(U.allocation)||U.allocation<0)throw Error("Delivery variant allocation must be a non-negative number");if(w+=U.allocation,n<w)return U}return r[r.length-1]}function c(f){return["delivery",f.teamId,f.campaignId,f.channel,f.recipient.trim().toLowerCase(),f.occurrence||"once"].join(":")}function d(f){let r=f.find((o)=>{if(o.limit===null)return!1;return o.used+(o.requested??1)>o.limit});if(r)throw Error(`Usage limit exceeded for ${r.meter}`)}function s(f,r=""){let o=r.toLowerCase();if(["suppressed","unsubscribe","complaint","stop"].some((n)=>o.includes(n)))return"suppressed";if(Number(f)===429||o.includes("rate"))return"rate_limited";if(Number(f)>=500||["timeout","temporary","unavailable"].some((n)=>o.includes(n)))return"transient";return"permanent"}function p(f){if(!f.url||!/^https?:\/\//i.test(f.url))throw Error("[newsletter] List-Unsubscribe URL must be absolute (http/https)");let r=[];if(f.mailto)r.push(`<mailto:${f.mailto}>`);return r.push(`<${f.url}>`),{"List-Unsubscribe":r.join(", "),"List-Unsubscribe-Post":"List-Unsubscribe=One-Click"}}async function S(f){if(f==null){let o=await P.find("default");if(!o)o=await P.create({name:"Default",slug:"default"});return o.id}let r=await P.find(f);if(!r)throw Error(`[newsletter] List '${String(f)}' not found`);return r.id}async function M(f,r={}){if(!f||!f.includes("@"))throw Error("[newsletter] subscribe() requires a valid email address");let{Subscriber:o,EmailListSubscriber:n}=await import("@stacksjs/orm"),w=await S(r.list),U=r.source??"api",x=await o.where("email",f).first();if(!x)x=await o.create({email:f,status:"subscribed",source:U});let N=await n.where("subscriber_id",x.id).where("email_list_id",w).first();if(N){if(N.status==="unsubscribed")await N.update({status:"subscribed",unsubscribed_at:null});return{created:!1,email:f,listId:w,token:N.uuid}}let z=await n.create({subscriber_id:x.id,email_list_id:w,status:"subscribed",source:U});return{created:!0,email:f,listId:w,token:z.uuid}}async function D(f){if(!f)return{ok:!1};let{EmailListSubscriber:r,Subscriber:o}=await import("@stacksjs/orm"),n=await r.where("uuid",f).first();if(!n)return{ok:!1};if(n.status==="unsubscribed")return{ok:!0,alreadyUnsubscribed:!0,email:(await o.find(n.subscriber_id))?.email,listId:n.email_list_id};return await n.update({status:"unsubscribed",unsubscribed_at:new Date().toISOString()}),{ok:!0,email:(await o.find(n.subscriber_id))?.email,listId:n.email_list_id}}async function R(f){let{Subscriber:r,EmailListSubscriber:o}=await import("@stacksjs/orm"),n=await r.where("email",f).first();if(!n)return 0;let w=await o.where("subscriber_id",n.id).where("status","subscribed").get();for(let U of w)await U.update({status:"unsubscribed",unsubscribed_at:new Date().toISOString()});return w.length}var wf={lists:P,campaigns:Q,subscribe:M,unsubscribe:D,unsubscribeAll:R};class F extends Error{code;constructor(f,r){let o={unavailable:`Usage meter unavailable for ${r}`,invalid:`Usage meter state is invalid for ${r}`,exceeded:`Usage limit exceeded for ${r}`,contention:`Usage reservation contention for ${r}`};super(o[f]);this.code=f;this.name="UsageQuotaError"}}function C(f){return Number.isFinite(f)&&f>0}function q(f,r){return f.meter===r&&Number.isFinite(f.used)&&f.used>=0&&(f.limit===null||Number.isFinite(f.limit)&&f.limit>=0)}function K(f){return Number.isInteger(f)&&f>0?f:1}async function xf(f,r,o=1,n=5){if(!r||!C(o))throw new F("invalid",r||"unknown");for(let w=0;w<K(n);w++){let U=await f.read(r);if(!U)throw new F("unavailable",r);if(!q(U,r))throw new F("invalid",r);let x=U.used+o;if(U.limit!==null&&x>U.limit)throw new F("exceeded",r);if(await f.compareAndSet(U,x))return{meter:r,quantity:o,previousUsed:U.used,used:x,limit:U.limit}}throw new F("contention",r)}async function Ff(f,r,o=5){if(!r.meter||!C(r.quantity))throw new F("invalid",r.meter||"unknown");for(let n=0;n<K(o);n++){let w=await f.read(r.meter);if(!w)throw new F("unavailable",r.meter);if(!q(w,r.meter)||w.used<r.quantity)throw new F("invalid",r.meter);if(await f.compareAndSet(w,w.used-r.quantity))return}throw new F("contention",r.meter)}export{R as unsubscribeAll,D as unsubscribe,M as subscribe,g as shouldRunScheduledCampaign,xf as reserveUsage,Ff as releaseUsage,wf as newsletter,P as lists,O as emailListCreateData,c as deliveryIdempotencyKey,s as classifyDeliveryFailure,X as canQueueCampaignStatus,Q as campaigns,L as campaignUpdateData,G as campaignDeliverySnapshot,E as campaignDeliveryDispatchKey,b as campaignCreateData,p as buildUnsubscribeHeaders,d as assertUsageAvailable,u as allocateDeliveryVariant,F as UsageQuotaError,B as CampaignStateConflictError};
|
package/dist/usage.d.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export declare function reserveUsage(store: UsageQuotaStore, meter: string, quantity?: number, maxAttempts?: number): Promise<UsageReservation>;
|
|
2
|
+
export declare function releaseUsage(store: UsageQuotaStore, reservation: UsageReservation, maxAttempts?: number): Promise<void>;
|
|
3
|
+
export declare interface UsageQuotaSnapshot {
|
|
4
|
+
meter: string
|
|
5
|
+
used: number
|
|
6
|
+
limit: number | null
|
|
7
|
+
}
|
|
8
|
+
export declare interface UsageQuotaStore {
|
|
9
|
+
read: (meter: string) => Promise<UsageQuotaSnapshot | null>
|
|
10
|
+
compareAndSet: (snapshot: UsageQuotaSnapshot, nextUsed: number) => Promise<boolean>
|
|
11
|
+
}
|
|
12
|
+
export declare interface UsageReservation {
|
|
13
|
+
meter: string
|
|
14
|
+
quantity: number
|
|
15
|
+
previousUsed: number
|
|
16
|
+
used: number
|
|
17
|
+
limit: number | null
|
|
18
|
+
}
|
|
19
|
+
export type UsageQuotaErrorCode = 'unavailable' | 'invalid' | 'exceeded' | 'contention';
|
|
20
|
+
export declare class UsageQuotaError extends Error {
|
|
21
|
+
public readonly code: UsageQuotaErrorCode;
|
|
22
|
+
constructor(code: UsageQuotaErrorCode, meter: string);
|
|
23
|
+
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@stacksjs/newsletter",
|
|
3
3
|
"type": "module",
|
|
4
4
|
"sideEffects": false,
|
|
5
|
-
"version": "0.72.
|
|
5
|
+
"version": "0.72.6",
|
|
6
6
|
"description": "Promotional email & newsletter primitives for Stacks. Lists, campaigns, subscribers, and one-click unsubscribe — sent through @stacksjs/email.",
|
|
7
7
|
"author": "Chris Breuer",
|
|
8
8
|
"contributors": [
|
|
@@ -61,15 +61,15 @@
|
|
|
61
61
|
"prepublishOnly": "bun run build"
|
|
62
62
|
},
|
|
63
63
|
"dependencies": {
|
|
64
|
-
"@stacksjs/database": "0.72.
|
|
65
|
-
"@stacksjs/orm": "0.72.
|
|
66
|
-
"@stacksjs/queue": "0.72.
|
|
64
|
+
"@stacksjs/database": "0.72.6",
|
|
65
|
+
"@stacksjs/orm": "0.72.6",
|
|
66
|
+
"@stacksjs/queue": "0.72.6"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"@stacksjs/cli": "0.72.
|
|
70
|
-
"@stacksjs/config": "0.72.
|
|
69
|
+
"@stacksjs/cli": "0.72.6",
|
|
70
|
+
"@stacksjs/config": "0.72.6",
|
|
71
71
|
"better-dx": "^0.2.23",
|
|
72
|
-
"@stacksjs/error-handling": "0.72.
|
|
73
|
-
"@stacksjs/types": "0.72.
|
|
72
|
+
"@stacksjs/error-handling": "0.72.6",
|
|
73
|
+
"@stacksjs/types": "0.72.6"
|
|
74
74
|
}
|
|
75
75
|
}
|