palbase 0.10.1 → 0.11.0

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.
Files changed (63) hide show
  1. package/dist/ai/index.js +2 -2
  2. package/dist/backend/engine.js +2 -2
  3. package/dist/backend/env.js +1 -1
  4. package/dist/backend/index.js +11 -11
  5. package/dist/backend/stack.js +1 -1
  6. package/dist/backend/test.js +2 -2
  7. package/dist/chunks/{PG7BO4MR.js → 2CQIOY2Z.js} +1 -1
  8. package/dist/chunks/{QC2SDBJD.js → 2MS22NJC.js} +2 -2
  9. package/dist/chunks/476DZPIV.js +2 -0
  10. package/dist/chunks/{NBI5LPMS.js → 52DZTUF7.js} +2 -2
  11. package/dist/chunks/{FHXGE2MX.js → 5D7BAVO5.js} +1 -1
  12. package/dist/chunks/{R4OLJRMH.js → 63TWL4SB.js} +2 -2
  13. package/dist/chunks/{RWGEPDRQ.js → 7JE4KOPN.js} +2 -2
  14. package/dist/chunks/{QW3V2NBT.js → AEAFASN2.js} +2 -2
  15. package/dist/chunks/{RO7ERCT3.js → BAOQMZPI.js} +2 -2
  16. package/dist/chunks/CQBT2Z2T.js +20 -0
  17. package/dist/chunks/{APTIATSC.js → D6RL5OCI.js} +2 -2
  18. package/dist/chunks/{MFMA46HN.js → DAYQLOBB.js} +2 -2
  19. package/dist/chunks/GCVVMTWI.js +2 -0
  20. package/dist/chunks/{Z4A62VQR.js → GUFHH4DP.js} +2 -2
  21. package/dist/chunks/KXY2SF4N.js +2 -0
  22. package/dist/chunks/{QD3SPM7D.js → LJ3VOTHH.js} +2 -2
  23. package/dist/chunks/{XYXBDSXW.js → NHNXW6EK.js} +1 -1
  24. package/dist/chunks/{NGS4ALOE.js → QX7E6TNL.js} +1 -1
  25. package/dist/chunks/{6N3E2XHZ.js → S4GBHZOR.js} +1 -1
  26. package/dist/chunks/WYUC5N2T.js +2 -0
  27. package/dist/chunks/{IVJCGBYG.js → XBT3QJRS.js} +2 -2
  28. package/dist/cli.js +172 -128
  29. package/dist/refuse/ai.js +1 -1
  30. package/dist/refuse/backend.js +1 -1
  31. package/dist/refuse/server.js +1 -1
  32. package/dist/server/index.js +2 -2
  33. package/dist/server/testing.js +2 -2
  34. package/dist/web/index.js +2 -2
  35. package/dist/web/message.js +2 -2
  36. package/dist/web/palbe.js +2 -2
  37. package/dist/web/paywall.js +2 -2
  38. package/dist/web/push-sw.js +1 -1
  39. package/dist/web/ssr.js +2 -2
  40. package/dist/web/vite.js +2 -2
  41. package/package.json +2 -2
  42. package/types/backend/src/container.d.ts +2 -0
  43. package/types/backend/src/database.d.ts +8 -1
  44. package/types/backend/src/decorators.d.ts +1 -0
  45. package/types/backend/src/engine-db.d.ts +3 -1
  46. package/types/backend/src/engine.d.ts +8 -1
  47. package/types/backend/src/idempotency.d.ts +136 -0
  48. package/types/backend/src/index.d.ts +1 -0
  49. package/types/backend/src/test-user-templates.d.ts +58 -0
  50. package/types/internal/src/modules/auth.d.ts +44 -13
  51. package/types/internal/src/modules/events.d.ts +2 -2
  52. package/types/internal/src/modules/machines.d.ts +74 -2
  53. package/types/internal/src/modules/purchases.d.ts +39 -3
  54. package/types/internal/src/modules/secrets.d.ts +138 -3
  55. package/types/internal/src/modules/stores.d.ts +714 -8
  56. package/types/server/src/fields.d.ts +62 -0
  57. package/types/server/src/index.d.ts +1 -0
  58. package/types/server/src/server.d.ts +8 -0
  59. package/types/server/src/testing.d.ts +8 -0
  60. package/dist/chunks/EUI3Y3LO.js +0 -20
  61. package/dist/chunks/INRWHOEW.js +0 -2
  62. package/dist/chunks/UDAWSJMT.js +0 -2
  63. package/dist/chunks/YQEM4H7U.js +0 -2
package/dist/refuse/ai.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
2
  throw new Error("palbase/ai is for servers: it speaks with the project secret key, which never goes into a browser or a mobile app \u2014 call your backend, and let it ask the model");
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
2
  throw new Error("palbase/backend is a server framework: it runs on your backend, never in a browser or a mobile app \u2014 apps use palbase/web (or the native SDKs)");
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
2
  throw new Error("palbase/server is for servers: its secret key never goes into a browser or a mobile app \u2014 apps use palbase/web (or the native SDKs) with the publishable key");
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{a as c,b as d,c as e,d as f,e as g,f as h,g as i,h as j,i as k,j as l,k as m,l as n,m as o,n as p,o as q,p as r,q as s,r as t}from"../chunks/INRWHOEW.js";import"../chunks/6N3E2XHZ.js";import{m as b}from"../chunks/YQEM4H7U.js";import"../chunks/RO7ERCT3.js";import{b as a}from"../chunks/RWGEPDRQ.js";import"../chunks/PG7BO4MR.js";import"../chunks/XYXBDSXW.js";export{j as AuthUnavailable,b as EVENT_TYPES,a as PalbaseError,o as ReservationSettled,i as TokenError,l as TokenVerifier,d as WebhookSignatureError,c as createServer,n as isEntitled,t as loadSecrets,q as pages,s as platformOwn,r as processOwn,m as requestAddress,k as sessionStanding,g as signatureHeader,f as verifySignature,e as verifyWebhook,h as verifyWebhookRequest,p as withReservation};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{a as e,b as f,c as g,d as h,e as i,f as j,g as k,h as l,i as m,j as n,k as o,l as p,m as q,n as r,o as s,p as t,q as u,r as v}from"../chunks/GCVVMTWI.js";import{a as c,b as d}from"../chunks/WYUC5N2T.js";import"../chunks/S4GBHZOR.js";import{m as b}from"../chunks/KXY2SF4N.js";import"../chunks/BAOQMZPI.js";import{b as a}from"../chunks/7JE4KOPN.js";import"../chunks/2CQIOY2Z.js";import"../chunks/NHNXW6EK.js";export{l as AuthUnavailable,b as EVENT_TYPES,c as FieldError,d as Fields,a as PalbaseError,q as ReservationSettled,k as TokenError,n as TokenVerifier,f as WebhookSignatureError,e as createServer,p as isEntitled,v as loadSecrets,s as pages,u as platformOwn,t as processOwn,o as requestAddress,m as sessionStanding,i as signatureHeader,h as verifySignature,g as verifyWebhook,j as verifyWebhookRequest,r as withReservation};
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{a,b,c,d}from"../chunks/UDAWSJMT.js";import"../chunks/PG7BO4MR.js";import"../chunks/XYXBDSXW.js";export{a as TestApiError,d as api,b as createTestApi,c as testRun};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{a,b,c,d,e}from"../chunks/476DZPIV.js";import"../chunks/WYUC5N2T.js";import"../chunks/2CQIOY2Z.js";import"../chunks/NHNXW6EK.js";export{b as TestApiError,e as api,c as createTestApi,a as testFields,d as testRun};
package/dist/web/index.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{a as h,b as g,c as C,d as w,e as v,f as b,g as E,h as T,i as P,j as S,k as x,l as k,m as R,n as I,o as L,p as M,q as G}from"../chunks/QD3SPM7D.js";import{a as V,b as O,f as A}from"../chunks/R4OLJRMH.js";import"../chunks/MFMA46HN.js";import"../chunks/NBI5LPMS.js";import"../chunks/FHXGE2MX.js";import"../chunks/IVJCGBYG.js";import"../chunks/YQEM4H7U.js";import"../chunks/RO7ERCT3.js";import{b as u}from"../chunks/RWGEPDRQ.js";import"../chunks/PG7BO4MR.js";import"../chunks/XYXBDSXW.js";var D="oai-events";async function N(e){if(typeof e?.start!="function")throw new TypeError("connectVoice: start(sdp) is the round trip to the app's backend, which starts the voice (chats.voice.start) and answers { id, sdp }");let o=e.rtc??B(),t=e.signal;t?.throwIfAborted();let s=e.audio??F(),c=e.microphone===void 0,a=e.microphone??await o.getUserMedia({audio:!0});if(t?.aborted){if(c)for(let r of a.getTracks())r.stop();throw t.reason}let n=new o.RTCPeerConnection,p=!1,d=()=>{if(!p){if(p=!0,f.close(),n.close(),c)for(let r of a.getTracks())r.stop();s.srcObject!==null&&(s.srcObject=null)}};n.addEventListener("track",r=>{s.srcObject=r.streams[0]??new MediaStream([r.track]),s.play?.()?.catch?.(()=>{})}),n.addEventListener("connectionstatechange",()=>e.onState?.(n.connectionState));for(let r of a.getAudioTracks())n.addTrack(r,a);let f=n.createDataChannel(D);f.addEventListener("message",r=>{let i=U(r.data);i!==void 0&&e.onError?.(i)});let y=()=>d();t?.addEventListener("abort",y,{once:!0});try{await n.setLocalDescription(await n.createOffer()),t?.throwIfAborted(),await H(n,e.iceTimeoutMs??1e4,t);let r=n.localDescription?.sdp;if(!r)throw new Error("connectVoice: the peer connection made no offer");let i=await e.start(r);if(t?.throwIfAborted(),typeof i?.id!="string"||i.id===""||typeof i.sdp!="string"||i.sdp==="")throw new TypeError("connectVoice: start answered no voice \u2014 it is { id, sdp }, chats.voice.start's answer as the backend got it");return await n.setRemoteDescription({type:"answer",sdp:i.sdp}),t?.removeEventListener("abort",y),{id:i.id,peer:n,audio:s,microphone:a,get muted(){return a.getAudioTracks().every(l=>!l.enabled)},mute(l){for(let m of a.getAudioTracks())m.enabled=!l},close:d}}catch(r){throw t?.removeEventListener("abort",y),d(),t?.aborted?t.reason:r}}function H(e,o,t){return e.iceGatheringState==="complete"?Promise.resolve():t?.aborted?Promise.reject(t.reason):new Promise((s,c)=>{let a=()=>{clearTimeout(d),e.removeEventListener("icegatheringstatechange",n),t?.removeEventListener("abort",p)},n=()=>{e.iceGatheringState==="complete"&&(a(),s())},p=()=>{a(),c(t?.reason)},d=setTimeout(()=>{a(),c(new Error(`connectVoice: the network candidates were not gathered in ${o} ms`))},o);e.addEventListener("icegatheringstatechange",n),t?.addEventListener("abort",p,{once:!0})})}function U(e){if(typeof e=="string")try{let o=JSON.parse(e);return o.type!=="error"?void 0:typeof o.error?.message=="string"?o.error.message:typeof o.error?.code=="string"?o.error.code:"the voice failed"}catch{return}}function B(){let e=globalThis;if(typeof e.RTCPeerConnection!="function"||typeof e.navigator?.mediaDevices?.getUserMedia!="function")throw new TypeError("connectVoice: this runtime has no WebRTC (RTCPeerConnection, navigator.mediaDevices): a chat's voice is a browser's");let o=e.navigator.mediaDevices;return{RTCPeerConnection:e.RTCPeerConnection,getUserMedia:t=>o.getUserMedia(t)}}function F(){let e=globalThis.document;if(!e)throw new TypeError("connectVoice: give audio \u2014 the element the voice plays in \u2014 where there is no document");let o=e.createElement("audio");return o.autoplay=!0,o}export{E as Analytics,V as BackendError,w as Channel,T as Consent,M as Flags,A as GatewayError,x as InApp,P as Inbox,b as Installation,R as Notify,u as PalbaseError,I as Purchases,v as RealtimeSocket,S as SESSION_START,h as SessionChangedError,k as WebPush,N as connectVoice,G as createClient,O as isBackendError,C as localStore,g as memoryStore,L as redemptionToken};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{a as h,b as g,c as C,d as w,e as v,f as b,g as E,h as T,i as P,j as S,k as x,l as k,m as R,n as I,o as L,p as M,q as G}from"../chunks/LJ3VOTHH.js";import{a as V,b as O,f as A}from"../chunks/63TWL4SB.js";import"../chunks/DAYQLOBB.js";import"../chunks/52DZTUF7.js";import"../chunks/5D7BAVO5.js";import"../chunks/XBT3QJRS.js";import"../chunks/KXY2SF4N.js";import"../chunks/BAOQMZPI.js";import{b as u}from"../chunks/7JE4KOPN.js";import"../chunks/2CQIOY2Z.js";import"../chunks/NHNXW6EK.js";var D="oai-events";async function N(e){if(typeof e?.start!="function")throw new TypeError("connectVoice: start(sdp) is the round trip to the app's backend, which starts the voice (chats.voice.start) and answers { id, sdp }");let o=e.rtc??B(),t=e.signal;t?.throwIfAborted();let s=e.audio??F(),c=e.microphone===void 0,a=e.microphone??await o.getUserMedia({audio:!0});if(t?.aborted){if(c)for(let r of a.getTracks())r.stop();throw t.reason}let n=new o.RTCPeerConnection,p=!1,d=()=>{if(!p){if(p=!0,f.close(),n.close(),c)for(let r of a.getTracks())r.stop();s.srcObject!==null&&(s.srcObject=null)}};n.addEventListener("track",r=>{s.srcObject=r.streams[0]??new MediaStream([r.track]),s.play?.()?.catch?.(()=>{})}),n.addEventListener("connectionstatechange",()=>e.onState?.(n.connectionState));for(let r of a.getAudioTracks())n.addTrack(r,a);let f=n.createDataChannel(D);f.addEventListener("message",r=>{let i=U(r.data);i!==void 0&&e.onError?.(i)});let y=()=>d();t?.addEventListener("abort",y,{once:!0});try{await n.setLocalDescription(await n.createOffer()),t?.throwIfAborted(),await H(n,e.iceTimeoutMs??1e4,t);let r=n.localDescription?.sdp;if(!r)throw new Error("connectVoice: the peer connection made no offer");let i=await e.start(r);if(t?.throwIfAborted(),typeof i?.id!="string"||i.id===""||typeof i.sdp!="string"||i.sdp==="")throw new TypeError("connectVoice: start answered no voice \u2014 it is { id, sdp }, chats.voice.start's answer as the backend got it");return await n.setRemoteDescription({type:"answer",sdp:i.sdp}),t?.removeEventListener("abort",y),{id:i.id,peer:n,audio:s,microphone:a,get muted(){return a.getAudioTracks().every(l=>!l.enabled)},mute(l){for(let m of a.getAudioTracks())m.enabled=!l},close:d}}catch(r){throw t?.removeEventListener("abort",y),d(),t?.aborted?t.reason:r}}function H(e,o,t){return e.iceGatheringState==="complete"?Promise.resolve():t?.aborted?Promise.reject(t.reason):new Promise((s,c)=>{let a=()=>{clearTimeout(d),e.removeEventListener("icegatheringstatechange",n),t?.removeEventListener("abort",p)},n=()=>{e.iceGatheringState==="complete"&&(a(),s())},p=()=>{a(),c(t?.reason)},d=setTimeout(()=>{a(),c(new Error(`connectVoice: the network candidates were not gathered in ${o} ms`))},o);e.addEventListener("icegatheringstatechange",n),t?.addEventListener("abort",p,{once:!0})})}function U(e){if(typeof e=="string")try{let o=JSON.parse(e);return o.type!=="error"?void 0:typeof o.error?.message=="string"?o.error.message:typeof o.error?.code=="string"?o.error.code:"the voice failed"}catch{return}}function B(){let e=globalThis;if(typeof e.RTCPeerConnection!="function"||typeof e.navigator?.mediaDevices?.getUserMedia!="function")throw new TypeError("connectVoice: this runtime has no WebRTC (RTCPeerConnection, navigator.mediaDevices): a chat's voice is a browser's");let o=e.navigator.mediaDevices;return{RTCPeerConnection:e.RTCPeerConnection,getUserMedia:t=>o.getUserMedia(t)}}function F(){let e=globalThis.document;if(!e)throw new TypeError("connectVoice: give audio \u2014 the element the voice plays in \u2014 where there is no document");let o=e.createElement("audio");return o.autoplay=!0,o}export{E as Analytics,V as BackendError,w as Channel,T as Consent,M as Flags,A as GatewayError,x as InApp,P as Inbox,b as Installation,R as Notify,u as PalbaseError,I as Purchases,v as RealtimeSocket,S as SESSION_START,h as SessionChangedError,k as WebPush,N as connectVoice,G as createClient,O as isBackendError,C as localStore,g as memoryStore,L as redemptionToken};
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{a as i}from"../chunks/NBI5LPMS.js";import{a as e,b as s,c as t,d as p,e as o,f as a,h as r,j as n,k as y,p as M,q as l,t as g}from"../chunks/FHXGE2MX.js";import"../chunks/XYXBDSXW.js";export{n as MESSAGE_COMPONENTS,a as MESSAGE_ICONS,r as MESSAGE_SCHEMA,s as assetId,y as countdownValues,e as fontFamily,o as htmlDocument,t as prepareAssets,i as presentMessage,g as renderMessage,M as resolveMessage,l as resolveMessageWithPaths,p as unzip};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{a as i}from"../chunks/52DZTUF7.js";import{a as e,b as s,c as t,d as p,e as o,f as a,h as r,j as n,k as y,p as M,q as l,t as g}from"../chunks/5D7BAVO5.js";import"../chunks/NHNXW6EK.js";export{n as MESSAGE_COMPONENTS,a as MESSAGE_ICONS,r as MESSAGE_SCHEMA,s as assetId,y as countdownValues,e as fontFamily,o as htmlDocument,t as prepareAssets,i as presentMessage,g as renderMessage,M as resolveMessage,l as resolveMessageWithPaths,p as unzip};
package/dist/web/palbe.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{c as e,d as r,i as t}from"../chunks/R4OLJRMH.js";import"../chunks/IVJCGBYG.js";import"../chunks/RO7ERCT3.js";import"../chunks/RWGEPDRQ.js";import"../chunks/PG7BO4MR.js";import"../chunks/XYXBDSXW.js";export{e as RESERVED_CLIENT_NAMES,t as __registerBackend,r as isClientName};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{c as e,d as r,i as t}from"../chunks/63TWL4SB.js";import"../chunks/XBT3QJRS.js";import"../chunks/BAOQMZPI.js";import"../chunks/7JE4KOPN.js";import"../chunks/2CQIOY2Z.js";import"../chunks/NHNXW6EK.js";export{e as RESERVED_CLIENT_NAMES,t as __registerBackend,r as isClientName};
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{a as f,b as d,c as m,d as u,e as A,f as c,g as R}from"../chunks/MFMA46HN.js";import{a as e,b as a,c as t,d as l,e as r,f as o,g as p,i as y,k as s,l as P,n,o as i,s as w}from"../chunks/FHXGE2MX.js";import"../chunks/RWGEPDRQ.js";import"../chunks/PG7BO4MR.js";import"../chunks/XYXBDSXW.js";export{y as PAYWALL_COMPONENTS,o as PAYWALL_ICONS,p as PAYWALL_SCHEMA,c as Paywall,a as assetId,f as attributesHeader,s as countdownValues,R as defaultPaywall,e as fontFamily,u as formatPeriod,m as formatPrice,r as htmlDocument,A as packageValues,P as packagesOf,d as parseDuration,t as prepareAssets,w as renderPaywall,n as resolvePaywall,i as resolvePaywallWithPaths,l as unzip};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{a as f,b as d,c as m,d as u,e as A,f as c,g as R}from"../chunks/DAYQLOBB.js";import{a as e,b as a,c as t,d as l,e as r,f as o,g as p,i as y,k as s,l as P,n,o as i,s as w}from"../chunks/5D7BAVO5.js";import"../chunks/7JE4KOPN.js";import"../chunks/2CQIOY2Z.js";import"../chunks/NHNXW6EK.js";export{y as PAYWALL_COMPONENTS,o as PAYWALL_ICONS,p as PAYWALL_SCHEMA,c as Paywall,a as assetId,f as attributesHeader,s as countdownValues,R as defaultPaywall,e as fontFamily,u as formatPeriod,m as formatPrice,r as htmlDocument,A as packageValues,P as packagesOf,d as parseDuration,t as prepareAssets,w as renderPaywall,n as resolvePaywall,i as resolvePaywallWithPaths,l as unzip};
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
2
  "use strict";(()=>{function f(e,s){if(e)try{let i=new URL(e,s);if(i.protocol==="https:"||i.protocol==="http:"&&(i.hostname==="localhost"||i.hostname==="127.0.0.1"))return i.href}catch{}}function g(e){let s=new URL(e.location.href).searchParams,i=s.get("url")??"",l=s.get("key")??"",r=(n,t)=>!n||!i?Promise.resolve():e.fetch(`${i.replace(/\/$/,"")}/notify/receipts`,{method:"POST",headers:{"content-type":"application/json",apikey:l},body:JSON.stringify({receipts:[{receipt:n,status:t}]}),keepalive:!0}).catch(()=>{}),a=async(n,t)=>{for(let o of await e.clients.matchAll({type:"window",includeUncontrolled:!0}))o.postMessage({type:"palbase.push",event:n,url:t.url,data:t.data})};e.addEventListener("push",(n=>{let t={};try{t=n.data?.json()??{}}catch{}let o={url:t.url,receipt:t.receipt,data:t.data};n.waitUntil(e.registration.showNotification(t.title??"",{body:t.body,image:t.image,tag:t.tag,data:o}).then(()=>Promise.all([r(t.receipt,"delivered"),a("received",o)])))})),e.addEventListener("notificationclick",(n=>{n.notification.close();let t=n.notification.data??{},o=f(t.url,e.location.origin),c=async()=>{if(!o)return;let d=(await e.clients.matchAll({type:"window",includeUncontrolled:!0})).find(u=>u.url===o);d?await d.focus():await e.clients.openWindow(o)};n.waitUntil(Promise.all([r(t.receipt,"opened"),c(),a("opened",t)]))})),e.addEventListener("notificationclose",(n=>{n.waitUntil(r(n.notification.data?.receipt,"dismissed"))}))}typeof self<"u"&&typeof self.registration=="object"&&!("document"in self)&&g(self);})();
package/dist/web/ssr.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import{q as f}from"../chunks/QD3SPM7D.js";import"../chunks/R4OLJRMH.js";import"../chunks/MFMA46HN.js";import"../chunks/NBI5LPMS.js";import"../chunks/FHXGE2MX.js";import"../chunks/IVJCGBYG.js";import"../chunks/YQEM4H7U.js";import"../chunks/RO7ERCT3.js";import"../chunks/RWGEPDRQ.js";import"../chunks/PG7BO4MR.js";import"../chunks/XYXBDSXW.js";var C=Object.freeze({"Cache-Control":"private, no-cache, no-store, must-revalidate, max-age=0",Expires:"0",Pragma:"no-cache"}),x={path:"/",sameSite:"lax",secure:!0,httpOnly:!1,maxAge:400*24*60*60},u=3180,k=5*6e4;function d(t){let e=/^pb_([a-z0-9]+)_c/.exec(t)?.[1];if(e===void 0)throw new TypeError("publishableKey is the project publishable key (pb_<ref>_c\u2026)");return`pb-${e}-session`}var y=t=>{let e=new TextEncoder().encode(JSON.stringify(t)),o="";for(let s of e)o+=String.fromCharCode(s);return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")},b=t=>{try{let e=atob(t.replace(/-/g,"+").replace(/_/g,"/")),o=JSON.parse(new TextDecoder().decode(Uint8Array.from(e,s=>s.charCodeAt(0))));return typeof o.accessToken!="string"||typeof o.refreshToken!="string"||typeof o.expiresAt!="number"||typeof o.userId!="string"?null:o}catch{return null}};function m(t,e,o={},s=!1){let c={...x,...o},g=async()=>new Map((await e.getAll()).map(n=>[n.name,n.value])),S=n=>n===t||n.startsWith(`${t}.`)&&/^\d+$/.test(n.slice(t.length+1)),h=async n=>{if(!e.setAll)return;let i=n===null?[]:n.length<=u?[[t,n]]:Array.from({length:Math.ceil(n.length/u)},(r,l)=>[`${t}.${l}`,n.slice(l*u,(l+1)*u)]),p=new Set(i.map(([r])=>r)),a=i.map(([r,l])=>({name:r,value:l,options:c}));for(let r of(await g()).keys())S(r)&&!p.has(r)&&a.push({name:r,value:"",options:{...c,maxAge:0}});a.length>0&&await e.setAll(a,{...C})};return{async load(){let n=await g(),i=n.get(t);if(i===void 0){let p="";for(let a=0;n.has(`${t}.${a}`);a++)p+=n.get(`${t}.${a}`);i=p}return i?b(i):null},save:n=>h(y(n)),clear:()=>h(null),shared:s}}function A(t){let{cookies:e,cookieOptions:o,...s}=t,c=t.refresh??(e.setAll?"before":"never");return f({...s,refresh:c,storage:m(d(t.publishableKey),e,o)})}function O(){let t=()=>{let e=globalThis.document;if(!e)throw new Error("documentCookies: there is no document here \u2014 a server gives its request's cookies to createServerClient");return e};return{getAll:()=>t().cookie.split(";").map(e=>e.trim()).filter(e=>e.includes("=")).map(e=>{let o=e.indexOf("=");return{name:e.slice(0,o),value:e.slice(o+1)}}),setAll(e){for(let o of e){let s=o.options,c=[`Path=${s.path??"/"}`,s.domain?`Domain=${s.domain}`:"",s.maxAge!==void 0?`Max-Age=${s.maxAge}`:"",s.expires?`Expires=${s.expires.toUTCString()}`:"",s.sameSite?`SameSite=${s.sameSite.charAt(0).toUpperCase()}${s.sameSite.slice(1)}`:"",s.secure?"Secure":""].filter(Boolean);t().cookie=[`${o.name}=${o.value}`,...c].join("; ")}}}}function $(t){let{cookieOptions:e,...o}=t,s=globalThis.location?.protocol==="https:";return f({...o,storage:m(d(t.publishableKey),O(),{secure:s,...e},!0)})}async function v(t){let e=await t.auth.currentSession();if(!e||(e.expiresAt-Date.now()<=k&&await t.auth.refreshSession().catch(()=>{}),!await t.auth.currentSession()))return null;try{return(await t.auth.api.verifyTokenUser()).data}catch(o){if(o.status===401)return null;throw o}}export{C as NO_STORE_HEADERS,x as SESSION_COOKIE_OPTIONS,m as cookieSessionStore,$ as createBrowserClient,A as createServerClient,O as documentCookies,v as keepSession,d as sessionCookieName};
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import{q as f}from"../chunks/LJ3VOTHH.js";import"../chunks/63TWL4SB.js";import"../chunks/DAYQLOBB.js";import"../chunks/52DZTUF7.js";import"../chunks/5D7BAVO5.js";import"../chunks/XBT3QJRS.js";import"../chunks/KXY2SF4N.js";import"../chunks/BAOQMZPI.js";import"../chunks/7JE4KOPN.js";import"../chunks/2CQIOY2Z.js";import"../chunks/NHNXW6EK.js";var C=Object.freeze({"Cache-Control":"private, no-cache, no-store, must-revalidate, max-age=0",Expires:"0",Pragma:"no-cache"}),x={path:"/",sameSite:"lax",secure:!0,httpOnly:!1,maxAge:400*24*60*60},u=3180,k=5*6e4;function d(t){let e=/^pb_([a-z0-9]+)_c/.exec(t)?.[1];if(e===void 0)throw new TypeError("publishableKey is the project publishable key (pb_<ref>_c\u2026)");return`pb-${e}-session`}var y=t=>{let e=new TextEncoder().encode(JSON.stringify(t)),o="";for(let s of e)o+=String.fromCharCode(s);return btoa(o).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+$/,"")},b=t=>{try{let e=atob(t.replace(/-/g,"+").replace(/_/g,"/")),o=JSON.parse(new TextDecoder().decode(Uint8Array.from(e,s=>s.charCodeAt(0))));return typeof o.accessToken!="string"||typeof o.refreshToken!="string"||typeof o.expiresAt!="number"||typeof o.userId!="string"?null:o}catch{return null}};function m(t,e,o={},s=!1){let c={...x,...o},g=async()=>new Map((await e.getAll()).map(n=>[n.name,n.value])),S=n=>n===t||n.startsWith(`${t}.`)&&/^\d+$/.test(n.slice(t.length+1)),h=async n=>{if(!e.setAll)return;let i=n===null?[]:n.length<=u?[[t,n]]:Array.from({length:Math.ceil(n.length/u)},(r,l)=>[`${t}.${l}`,n.slice(l*u,(l+1)*u)]),p=new Set(i.map(([r])=>r)),a=i.map(([r,l])=>({name:r,value:l,options:c}));for(let r of(await g()).keys())S(r)&&!p.has(r)&&a.push({name:r,value:"",options:{...c,maxAge:0}});a.length>0&&await e.setAll(a,{...C})};return{async load(){let n=await g(),i=n.get(t);if(i===void 0){let p="";for(let a=0;n.has(`${t}.${a}`);a++)p+=n.get(`${t}.${a}`);i=p}return i?b(i):null},save:n=>h(y(n)),clear:()=>h(null),shared:s}}function A(t){let{cookies:e,cookieOptions:o,...s}=t,c=t.refresh??(e.setAll?"before":"never");return f({...s,refresh:c,storage:m(d(t.publishableKey),e,o)})}function O(){let t=()=>{let e=globalThis.document;if(!e)throw new Error("documentCookies: there is no document here \u2014 a server gives its request's cookies to createServerClient");return e};return{getAll:()=>t().cookie.split(";").map(e=>e.trim()).filter(e=>e.includes("=")).map(e=>{let o=e.indexOf("=");return{name:e.slice(0,o),value:e.slice(o+1)}}),setAll(e){for(let o of e){let s=o.options,c=[`Path=${s.path??"/"}`,s.domain?`Domain=${s.domain}`:"",s.maxAge!==void 0?`Max-Age=${s.maxAge}`:"",s.expires?`Expires=${s.expires.toUTCString()}`:"",s.sameSite?`SameSite=${s.sameSite.charAt(0).toUpperCase()}${s.sameSite.slice(1)}`:"",s.secure?"Secure":""].filter(Boolean);t().cookie=[`${o.name}=${o.value}`,...c].join("; ")}}}}function $(t){let{cookieOptions:e,...o}=t,s=globalThis.location?.protocol==="https:";return f({...o,storage:m(d(t.publishableKey),O(),{secure:s,...e},!0)})}async function v(t){let e=await t.auth.currentSession();if(!e||(e.expiresAt-Date.now()<=k&&await t.auth.refreshSession().catch(()=>{}),!await t.auth.currentSession()))return null;try{return(await t.auth.api.verifyTokenUser()).data}catch(o){if(o.status===401)return null;throw o}}export{C as NO_STORE_HEADERS,x as SESSION_COOKIE_OPTIONS,m as cookieSessionStore,$ as createBrowserClient,A as createServerClient,O as documentCookies,v as keepSession,d as sessionCookieName};
package/dist/web/vite.js CHANGED
@@ -1,3 +1,3 @@
1
- /*! palbase 0.10.1 — Palbase SDK License (see LICENSE) */
2
- import"../chunks/XYXBDSXW.js";import{execFile as $}from"node:child_process";import{existsSync as y,mkdtempSync as k,readFileSync as S,rmSync as x,writeFileSync as P}from"node:fs";import{createRequire as O}from"node:module";import{tmpdir as W}from"node:os";import{dirname as g,join as o,relative as u,resolve as v,sep as d}from"node:path";var E=/\.(?:ts|js|mts|mjs)$/;function F(n){if(!/^[a-z][a-z0-9-]*$/.test(n.app))throw new TypeError(`palbase({ app }): ${JSON.stringify(n.app)} is an app's slug (palbase apps:list)`);let c=process.cwd(),p="",i="",f=()=>v(c,n.dir??"palbase"),m=()=>{let e=n.environments[p];if(!e)throw new Error(`palbase: the mode "${p}" is not built against any environment \u2014 say which in vite.config: palbase({ environments: { ${JSON.stringify(p)}: '<environment slug>' } })`);return e};return{name:"palbase",enforce:"pre",config(e,s){p=s.mode,i=s.command??"";let r=e.build?.rollupOptions?.onwarn;return{build:{rollupOptions:{onwarn(t,a){let l=String(t.exporter??t.id??"");if(t.code==="MISSING_EXPORT"&&l.includes(`${d}environments${d}`))throw new Error(`palbase: ${t.message} \u2014 ${m()}'s backend has no such route (palbase apps:sync brings what it has)`);r?r(t,a):a(t)}}}}},configResolved(e){c=e.root,p=e.mode,i=e.command??i},async buildStart(){i!=="build"||n.typecheck===!1||await j(c,m(),n.tsconfig)},resolveId(e,s){if(!s)return null;let r=f();if(e==="palbase"||e.startsWith("palbase/")){let w=u(r,s);return w.startsWith("..")||w===""?null:this.resolve(e,o(c,"index.html"),{skipSelf:!0})}if(!(e.startsWith(".")||e.startsWith("/")))return null;let t=v(g(s),e);if(E.test(t)||(t+=".ts"),g(t)!==r&&g(t)!==o(r,"palbe"))return null;let a=u(r,t),l=`${n.app}.ts`;if(a!==l&&!a.startsWith(`palbe${d}`))return null;let h=m(),b=o(r,"environments",h,a===l?l:o("palbe",a.slice(`palbe${d}`.length)));if(!y(b))throw new Error(`palbase: ${u(c,b)} is not there \u2014 palbase apps:sync writes it (is ${n.app} set in ${h}, and has ${h} that machine?)`);return b}}}async function j(n,c,p){let i=p?v(n,p):["tsconfig.app.json","tsconfig.json"].map(e=>o(n,e)).find(y);if(!i)return;if(!y(i))throw new Error(`palbase({ tsconfig }): ${u(n,i)} is not there`);let f;try{let e=O(o(n,"package.json")).resolve("typescript/package.json"),s=JSON.parse(S(e,"utf8")).bin?.tsc;if(!s)throw new Error("no tsc");f=o(g(e),s)}catch{throw new Error("palbase: the build type-checks the app against its environment with the app's TypeScript, and the app has none (npm i -D typescript) \u2014 or say palbase({ typecheck: false })")}let m=k(o(W(),"palbase-typecheck-"));try{let e=o(m,"tsconfig.json");P(e,JSON.stringify({extends:i,compilerOptions:{noEmit:!0,composite:!1,incremental:!1,moduleSuffixes:[`.${c}`,""]}}));let s=await new Promise(r=>$(process.execPath,[f,"-p",e,"--pretty","false"],{cwd:n,maxBuffer:16<<20},(t,a,l)=>r({code:t?typeof t.code=="number"?t.code:1:0,text:`${a}${l}`.trim()})));if(s.code!==0)throw new Error(`palbase: the app does not type-check against ${c}, the environment this build is made for \u2014 a route its backend does not have, or a call that does not fit it:
1
+ /*! palbase 0.11.0 — Palbase SDK License (see LICENSE) */
2
+ import"../chunks/NHNXW6EK.js";import{execFile as $}from"node:child_process";import{existsSync as y,mkdtempSync as k,readFileSync as S,rmSync as x,writeFileSync as P}from"node:fs";import{createRequire as O}from"node:module";import{tmpdir as W}from"node:os";import{dirname as g,join as o,relative as u,resolve as v,sep as d}from"node:path";var E=/\.(?:ts|js|mts|mjs)$/;function F(n){if(!/^[a-z][a-z0-9-]*$/.test(n.app))throw new TypeError(`palbase({ app }): ${JSON.stringify(n.app)} is an app's slug (palbase apps:list)`);let c=process.cwd(),p="",i="",f=()=>v(c,n.dir??"palbase"),m=()=>{let e=n.environments[p];if(!e)throw new Error(`palbase: the mode "${p}" is not built against any environment \u2014 say which in vite.config: palbase({ environments: { ${JSON.stringify(p)}: '<environment slug>' } })`);return e};return{name:"palbase",enforce:"pre",config(e,s){p=s.mode,i=s.command??"";let r=e.build?.rollupOptions?.onwarn;return{build:{rollupOptions:{onwarn(t,a){let l=String(t.exporter??t.id??"");if(t.code==="MISSING_EXPORT"&&l.includes(`${d}environments${d}`))throw new Error(`palbase: ${t.message} \u2014 ${m()}'s backend has no such route (palbase apps:sync brings what it has)`);r?r(t,a):a(t)}}}}},configResolved(e){c=e.root,p=e.mode,i=e.command??i},async buildStart(){i!=="build"||n.typecheck===!1||await j(c,m(),n.tsconfig)},resolveId(e,s){if(!s)return null;let r=f();if(e==="palbase"||e.startsWith("palbase/")){let w=u(r,s);return w.startsWith("..")||w===""?null:this.resolve(e,o(c,"index.html"),{skipSelf:!0})}if(!(e.startsWith(".")||e.startsWith("/")))return null;let t=v(g(s),e);if(E.test(t)||(t+=".ts"),g(t)!==r&&g(t)!==o(r,"palbe"))return null;let a=u(r,t),l=`${n.app}.ts`;if(a!==l&&!a.startsWith(`palbe${d}`))return null;let h=m(),b=o(r,"environments",h,a===l?l:o("palbe",a.slice(`palbe${d}`.length)));if(!y(b))throw new Error(`palbase: ${u(c,b)} is not there \u2014 palbase apps:sync writes it (is ${n.app} set in ${h}, and has ${h} that machine?)`);return b}}}async function j(n,c,p){let i=p?v(n,p):["tsconfig.app.json","tsconfig.json"].map(e=>o(n,e)).find(y);if(!i)return;if(!y(i))throw new Error(`palbase({ tsconfig }): ${u(n,i)} is not there`);let f;try{let e=O(o(n,"package.json")).resolve("typescript/package.json"),s=JSON.parse(S(e,"utf8")).bin?.tsc;if(!s)throw new Error("no tsc");f=o(g(e),s)}catch{throw new Error("palbase: the build type-checks the app against its environment with the app's TypeScript, and the app has none (npm i -D typescript) \u2014 or say palbase({ typecheck: false })")}let m=k(o(W(),"palbase-typecheck-"));try{let e=o(m,"tsconfig.json");P(e,JSON.stringify({extends:i,compilerOptions:{noEmit:!0,composite:!1,incremental:!1,moduleSuffixes:[`.${c}`,""]}}));let s=await new Promise(r=>$(process.execPath,[f,"-p",e,"--pretty","false"],{cwd:n,maxBuffer:16<<20},(t,a,l)=>r({code:t?typeof t.code=="number"?t.code:1:0,text:`${a}${l}`.trim()})));if(s.code!==0)throw new Error(`palbase: the app does not type-check against ${c}, the environment this build is made for \u2014 a route its backend does not have, or a call that does not fit it:
3
3
  ${s.text}`)}finally{x(m,{recursive:!0,force:!0})}}export{F as palbase};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "palbase",
3
- "version": "0.10.1",
3
+ "version": "0.11.0",
4
4
  "description": "Palbase for JavaScript: the web client (palbase/web), the server SDK (palbase/server), the backend framework (palbase/backend), AI (palbase/ai) and the CLI (npx palbase).",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://palbase.studio",
@@ -135,7 +135,7 @@
135
135
  "dependencies": {
136
136
  "@types/node": "26.6.4",
137
137
  "bun-types": "1.4.2",
138
- "palgres": "0.10.1",
138
+ "palgres": "0.11.0",
139
139
  "reflect-metadata": "^0.2.2",
140
140
  "typescript": "~6.0.3",
141
141
  "zod": "^4.6.5"
@@ -30,6 +30,8 @@ export declare class Container {
30
30
  readonly cronProviders: Ctor[];
31
31
  /** The providers that are @Task classes: the platform runs them as their tasks come due. */
32
32
  readonly taskProviders: Ctor[];
33
+ /** The providers that are @TestUserTemplate classes: the platform calls them for the test users made with their names. */
34
+ readonly templateProviders: Ctor[];
33
35
  /** The providers that are @Gateway classes. */
34
36
  readonly gateways: Ctor[];
35
37
  constructor(modules: Function[]);
@@ -1,5 +1,7 @@
1
1
  import { type AppDatabase, type Binding, type Embed, type Plan, type Pool, type Schema, type SearchWorker, type SqlDriver } from 'palgres';
2
2
  import { type SchemaDef } from 'palgres/dsl';
3
+ import { type Fields } from '../../server/src/index.js';
4
+ import type { KeptAnswers } from './idempotency.js';
3
5
  import type { Schemas, Tables } from './db-env.js';
4
6
  /** A connection pool: Bun's SQL and postgres.js both are one. */
5
7
  export interface SqlPool extends Pool {
@@ -137,6 +139,8 @@ export interface OpenDatabase {
137
139
  }>;
138
140
  /** What embeds the rows of the tables that declare search, when the schema has one; not started. */
139
141
  search?: SearchWorker;
142
+ /** How an Idempotency-Key's answer is kept: sealed when the app encrypts a column (idempotency.ts). */
143
+ keptAnswers: KeptAnswers;
140
144
  close(): Promise<void>;
141
145
  }
142
146
  /**
@@ -158,6 +162,9 @@ export declare function planLines(p: Plan): string[];
158
162
  * schema with no version is the developer's to push (SchemaNotPushed).
159
163
  */
160
164
  export declare function openDatabase(config: DatabaseConfig, env: Record<string, string | undefined>, secretKey: string, where: {
161
- onPlatform: boolean; /** What embeds when the app names no embedder: the project's ai module. */
165
+ onPlatform: boolean;
166
+ /** What embeds when the app names no embedder: the project's ai module. */
162
167
  embed: Embed;
168
+ /** The project's field keys (palbase/server's fields): loaded only when the schema encrypts a column, or a migration takes one's encryption on or off. */
169
+ fields: Fields;
163
170
  }): Promise<OpenDatabase>;
@@ -88,6 +88,7 @@ export declare const EVENTS: unique symbol;
88
88
  export declare const WEBHOOK: unique symbol;
89
89
  export declare const CRONS: unique symbol;
90
90
  export declare const TASKS: unique symbol;
91
+ export declare const TEST_USER_TEMPLATES: unique symbol;
91
92
  export declare function declared(): Set<Function>;
92
93
  export declare const RESERVED: Set<string>;
93
94
  export declare function Controller(basePath: string, options?: ControllerOptions): ClassDecorator;
@@ -1,4 +1,4 @@
1
- import { type DatabaseDiagnosticsOptions, type DBClient, type SqlDriver } from 'palgres';
1
+ import { type DatabaseDiagnosticsOptions, type DBClient, type FieldsSource, type SqlDriver } from 'palgres';
2
2
  import type { SchemaDef } from 'palgres/dsl';
3
3
  export type { SqlDriver };
4
4
  export interface RequestDatabaseIdentity {
@@ -14,6 +14,8 @@ export interface RequestDatabaseOptions {
14
14
  schemas: readonly SchemaDef[];
15
15
  diagnostics?: DatabaseDiagnosticsOptions;
16
16
  signal?: AbortSignal;
17
+ /** What seals and opens the schemas' .encrypted() columns: palbase/server/testing's testFields() in a test. */
18
+ fields?: FieldsSource;
17
19
  }
18
20
  export interface RequestDatabaseHandle {
19
21
  /** The string-keyed client: what withServices({ Database }) takes. */
@@ -1,5 +1,6 @@
1
1
  import { type PalbaseServer, type TokenUser, type TokenVerifier, type WebhookEvent } from '../../server/src/index.js';
2
2
  import { type AuthSpec, type RateLimitConfig } from './decorators.js';
3
+ import { type KeptAnswers } from './idempotency.js';
3
4
  import { type ContractOptions } from './openapi.js';
4
5
  import { type StringsTable } from './i18n.js';
5
6
  import type { RequestTelemetry } from './requests.js';
@@ -66,6 +67,7 @@ export interface AppOptions {
66
67
  database?: {
67
68
  app: RequestDatabases;
68
69
  requests: 'caller' | 'service';
70
+ keptAnswers?: KeptAnswers;
69
71
  };
70
72
  /**
71
73
  * The app's strings (palbase/strings/, loadStringsTable): t() answers each request in the first of its
@@ -106,7 +108,12 @@ export interface AppOptions {
106
108
  * - its ai tools and agents (@palbase/ai's ai.tool and ai.agent), from the
107
109
  * ai module: POST /_palbase/ai/tools runs the tool an agent called (its
108
110
  * body's tool), POST /_palbase/ai/agents answers the definition of the
109
- * agent a turn is of (its body's agent) — ai-calls.ts.
111
+ * agent a turn is of (its body's agent) — ai-calls.ts;
112
+ * - its @TestUserTemplate classes, from the machines module, for a test
113
+ * user it made through auth: POST /_palbase/test-user-templates/<name>
114
+ * writes the template for the user its body names — a test user, auth
115
+ * says, or 403 — and answers {rows: {table: n}}; …/<name>/undo takes it
116
+ * away (404: the template has none) — test-user-templates.ts.
110
117
  */
111
118
  platform?: {
112
119
  secret: string;
@@ -0,0 +1,136 @@
1
+ import { type EnvServiceDatabase, type TablesOf } from 'palgres';
2
+ import type { StandardSchemaV1 } from './decorators.js';
3
+ import type { RequestDatabases } from './engine.js';
4
+ /** The writes a key is taken on: what may change something, and be sent again. */
5
+ export declare const KEYED_METHODS: ReadonlySet<string>;
6
+ /** The header parameter every keyed write of the contract carries — the platform's modules' own words. */
7
+ export declare const IDEMPOTENCY_KEY_PARAMETER: {
8
+ readonly description: 'Makes the write safe to send again (at most 255 characters). A retry with the same key and the same request gets the first answer back, marked `Idempotent-Replayed: true`, and changes nothing; the same key with a different request is refused with 422, and while the first request still runs a retry gets 409 with Retry-After. Answers are kept for 24 hours.';
9
+ readonly in: 'header';
10
+ readonly name: 'Idempotency-Key';
11
+ readonly required: false;
12
+ readonly schema: {
13
+ readonly maxLength: 255;
14
+ readonly type: 'string';
15
+ };
16
+ };
17
+ /**
18
+ * The framework's own tables in the app's database: migrated with the app's
19
+ * schemas, by the deploy's migration step, as the tables' owner.
20
+ */
21
+ export declare const FRAMEWORK_SCHEMA: import("palgres/dsl").SchemaDef<{
22
+ idempotency_keys: import("palgres/dsl").TableDef<{
23
+ /** The caller's key, as theirs: a digest of who sent it and the key. */
24
+ key: import("palgres/dsl").ColumnBuilder<"text", false, false, never, unknown, unknown, import("palgres/dsl").ColumnWrite, "primary">;
25
+ /** What made the request the one it is: a digest of its method, path and query, caller and body. */
26
+ fingerprint: import("palgres/dsl").ColumnBuilder<"text", false, false, never, unknown, unknown, import("palgres/dsl").ColumnWrite, import("palgres/dsl").ColumnKey>;
27
+ started_at: import("palgres/dsl").ColumnBuilder<"timestamp", false, true, never, unknown, unknown, import("palgres/dsl").ColumnWrite, import("palgres/dsl").ColumnKey>;
28
+ completed_at: import("palgres/dsl").ColumnBuilder<"timestamp", true, false, never, unknown, unknown, import("palgres/dsl").ColumnWrite, import("palgres/dsl").ColumnKey>;
29
+ status_code: import("palgres/dsl").ColumnBuilder<"integer", true, false, never, unknown, unknown, import("palgres/dsl").ColumnWrite, import("palgres/dsl").ColumnKey>;
30
+ /** The answer's headers, as [name, value] pairs. */
31
+ response_headers: import("palgres/dsl").ColumnBuilder<"jsonb", true, false, never, [string, string][], unknown, import("palgres/dsl").ColumnWrite, import("palgres/dsl").ColumnKey>;
32
+ /** The answer's body, base64; null when it had none, was over 64 KiB or was still being produced. */
33
+ response_body: import("palgres/dsl").ColumnBuilder<"text", true, false, never, unknown, unknown, import("palgres/dsl").ColumnWrite, import("palgres/dsl").ColumnKey>;
34
+ }> & {
35
+ name: "idempotency_keys";
36
+ } & import("palgres/dsl").TableKeys<readonly (readonly "key"[])[]>;
37
+ }>;
38
+ type KeysTable = TablesOf<typeof FRAMEWORK_SCHEMA>['idempotency_keys'];
39
+ type KeysDatabase = EnvServiceDatabase<Record<never, never>, {
40
+ palbase: {
41
+ idempotency_keys: KeysTable;
42
+ };
43
+ }>;
44
+ type Row = Awaited<ReturnType<KeysDatabase['palbase']['idempotency_keys']['findMany']>>[number];
45
+ /** Who sent a request, as its key is theirs: the role it runs as, and its user's id — for nobody signed in, the address the edge names (a:<address>). */
46
+ export interface Caller {
47
+ role: string;
48
+ uid: string;
49
+ }
50
+ /** A keyed write, as it arrived. */
51
+ export interface KeyedRequest {
52
+ key: string;
53
+ caller: Caller;
54
+ method: string;
55
+ /** Its path and query, as sent. */
56
+ uri: string;
57
+ body: Uint8Array;
58
+ }
59
+ /** How the engine answers: its refusals' envelope, a failure's answer, and whether the handler had begun. */
60
+ export interface Answers {
61
+ requestId: string;
62
+ refusal(r: {
63
+ status: number;
64
+ code: string;
65
+ message: string;
66
+ retryAfter?: number;
67
+ }): Response;
68
+ failure(e: unknown): Response;
69
+ ran(): boolean;
70
+ }
71
+ /**
72
+ * Whether a route's writes take a key: a write the client itself sends the
73
+ * app. An @Upload route's is sent by storage, which calls it once the file is
74
+ * there (its route says so: meta.upload).
75
+ */
76
+ export declare function takesKey(route: {
77
+ method: string;
78
+ meta: object;
79
+ }): boolean;
80
+ /** The key a write carries, undefined for a request it does not apply to. */
81
+ export declare function keyOf(method: string, headers: Headers): string | undefined;
82
+ /** The key as its caller's: two callers' same key are two rows, and neither can take — or block for a day — the other's. */
83
+ export declare function recordKey(caller: Caller, key: string): string;
84
+ /** What makes two requests the same one: what they do, to what, who asks, and with what. */
85
+ export declare function fingerprint(r: KeyedRequest): string;
86
+ /**
87
+ * What a key's row says of a request that finds it: another request's
88
+ * (reused), its answer to send again (replay), or a claim still running —
89
+ * taken over if it went stale.
90
+ */
91
+ export declare function verdictOf(row: Pick<Row, 'fingerprint' | 'completed_at'>, fp: string): 'reused' | 'replay' | 'running';
92
+ /**
93
+ * Whether an answer is kept against its key once the request failed: one
94
+ * that settles it (not a 5xx, a 429, a 499), given by the handler — what was
95
+ * answered before it ran is not, and the retry runs afresh.
96
+ */
97
+ export declare function keeps(status: number, ran: boolean): boolean;
98
+ /** The Idempotency-Key a route's @Headers schema declares, if it does: the framework reads that header on every keyed write. */
99
+ export declare function declaresKey(schema: StandardSchemaV1): boolean;
100
+ /**
101
+ * How a kept answer's body is kept: sealed with the project's field keys when
102
+ * the app encrypts a column — an answer may hold a field's value, and the
103
+ * table is the app's database, which encryption keeps fields out of — and
104
+ * opened when it is replayed.
105
+ */
106
+ export interface KeptAnswers {
107
+ seal(body: string): string;
108
+ open(kept: string): Promise<string>;
109
+ }
110
+ export declare class IdempotencyKeys {
111
+ private readonly db;
112
+ private readonly answers;
113
+ constructor(db: RequestDatabases, answers?: KeptAnswers);
114
+ /** A keyed write: its first answer again, a refusal, or `run` under its claim — the answer written by `record` in the request's transaction. */
115
+ serve(r: KeyedRequest, run: (record: (built: Response) => Promise<Response>) => Promise<Response>, answers: Answers): Promise<Response>;
116
+ /**
117
+ * The rows a day old, taken away: now and then before a claim, in a
118
+ * transaction of its own — a sweep that fails, or waits on a row, costs no
119
+ * write its key.
120
+ */
121
+ sweep(): Promise<number>;
122
+ private begin;
123
+ private record;
124
+ private keep;
125
+ /**
126
+ * Frees the key of a request that failed, so its retry runs — at once,
127
+ * before the failure is answered. When the database is what failed it may
128
+ * refuse this too, and a claim left standing makes every retry wait out
129
+ * StaleAfter on top of the outage: tried again in the background until the
130
+ * database takes it, or the claim is stale anyway.
131
+ */
132
+ private release;
133
+ private releaseOnce;
134
+ private asService;
135
+ }
136
+ export {};
@@ -1,6 +1,7 @@
1
1
  import 'reflect-metadata';
2
2
  export { Body, Client, Controller, Delete, Get, getRegisteredControllers, getRoutes, Headers, Cron, Injectable, Module, On, OptionalUser, Param, Patch, Post, Put, Query, QueryParams, Req, RequestId, Resolve, TraceId, User, Webhook, type AuthConfig, type AuthSpec, type ControllerOptions, type EventHandler, type CronMeta, type CronOptions, type ModuleDef, type OnOptions, type ParamResolver, type RateLimitConfig, type ResolveContext, type RouteInfo, type RouteOptions, type SignatureSpec, type StandardSchemaV1, type Token, type WebhookOptions, type WebhookProvider, } from './decorators.js';
3
3
  export { Task, TaskFailure, Tasks, type EnqueueOptions, type PayloadOf, type QueuedTask, type TaskClass, type TaskMeta, type TaskOptions, type TasksService } from './tasks.js';
4
+ export { TestUserTemplate, type TemplateMeta, type TemplateUser, type TestUserTemplateClass, type TestUserTemplateOptions } from './test-user-templates.js';
4
5
  export { Upload, UploadedObject, type UploadConfig } from './upload.js';
5
6
  export { Authorize, ChatPart, Gateway, GatewayRefusal, OnConnect, OnDisconnect, OnEmpty, OnFirst, Subscribe, type GatewayAuthorizeContext, type GatewayContext, type GatewayEvents, type GatewayGrant, type GatewayMessageContext, type GatewayOptions, type GatewayUser, type PipeSource, } from './gateways.js';
6
7
  export type { GatewaysApi, GatewayTarget } from './runtime.js';
@@ -0,0 +1,58 @@
1
+ import type { PalbaseServer } from '../../server/src/index.js';
2
+ export declare const TEST_USER_TEMPLATES_PATH = "/_palbase/test-user-templates";
3
+ /** A template's name, as the platform takes it: lowercase letters, digits, '_' and '-', a letter first, at most 64. */
4
+ export declare const TEMPLATE_NAME: RegExp;
5
+ export interface TestUserTemplateOptions {
6
+ /** What `palbase auth:test-users:create --template <name>` names it by. */
7
+ name: string;
8
+ }
9
+ export interface TestUserTemplateDeclaration {
10
+ name: string;
11
+ }
12
+ /** The test user a template writes for, as the platform names it: auth made it, and says it is a test user. */
13
+ export interface TemplateUser {
14
+ id: string;
15
+ email: string;
16
+ }
17
+ /** What seed() and undo() are given beside the user. */
18
+ export interface TemplateMeta {
19
+ /** The app's environment variables, as it started (its keys left out). */
20
+ env: Record<string, string>;
21
+ /** The project the app belongs to (its id). */
22
+ environmentId: string;
23
+ /** The template's name. */
24
+ template: string;
25
+ }
26
+ /** A @TestUserTemplate class: seed(user, meta) writes what the screen shows, undo(user, meta) — when it has one — takes it away. */
27
+ export type TestUserTemplateClass = abstract new (...args: never[]) => {
28
+ seed(user: TemplateUser, meta: TemplateMeta): unknown;
29
+ undo?(user: TemplateUser, meta: TemplateMeta): unknown;
30
+ };
31
+ /**
32
+ * A provider whose seed(user, meta) writes the data a test account needs —
33
+ * the rows behind the screen a person or an agent signs in to see — for test
34
+ * users alone: the platform calls it for each test user made with its name
35
+ * (palbase auth:test-users:create --template <name>), and the framework asks
36
+ * auth first that the user is a test user. It runs in one transaction, as the
37
+ * service role: all of it written, or none. An undo(user, meta) takes away
38
+ * what seed wrote that the user's deletion does not (user.deleted forgets
39
+ * what ownedByUser() owns). Its module lists it in its providers, as any other.
40
+ */
41
+ export declare function TestUserTemplate(options: TestUserTemplateOptions): ClassDecorator;
42
+ /** What the platform's call names, or why it is not one: the template the path names, and its test user. */
43
+ export declare function templateCallOf(name: string, body: unknown): {
44
+ user: TemplateUser;
45
+ } | string;
46
+ /**
47
+ * Whether auth says the user is a test user: false for one it does not say
48
+ * is, or does not know (404). Anything else — auth not answering — throws: a
49
+ * template is never run, nor refused, on a guess.
50
+ */
51
+ export declare function isTestUser(server: PalbaseServer, id: string): Promise<boolean>;
52
+ /**
53
+ * The rows the transaction wrote, by table — public's by name, any other
54
+ * schema's as <schema>.<table> — as Postgres counts them for it
55
+ * (pg_stat_xact_user_tables: its inserts and updates). An app with no
56
+ * database wrote none.
57
+ */
58
+ export declare function rowsWritten(database: object | undefined): Promise<Record<string, number>>;
@@ -627,7 +627,7 @@ export type AdminUserImportError = {
627
627
  */
628
628
  export type AdminUserImportHash = {
629
629
  /**
630
- * palbase-argon2id: Argon2id (PHC string) over HMAC-SHA256(pepper, secret) — Palbase's own scheme, before and now.
630
+ * palbase-argon2id: Argon2id (PHC string) over HMAC-SHA256(pepper, secret) — Palbase's own scheme.
631
631
  */
632
632
  algorithm: "palbase-argon2id";
633
633
  /**
@@ -635,7 +635,7 @@ export type AdminUserImportHash = {
635
635
  */
636
636
  pepper: string;
637
637
  /**
638
- * What the source put before a recovery code it hashed (the platform before used palauth-rc:). This platform's own, palbase-rc:, when absent.
638
+ * What the source put before a recovery code it hashed. This platform's own, palbase-rc:, when absent.
639
639
  */
640
640
  recovery_code_prefix?: string;
641
641
  };
@@ -1014,13 +1014,17 @@ export type AuthSettings = {
1014
1014
  */
1015
1015
  risk_strict_step_up: number;
1016
1016
  /**
1017
- * The changes to how an account signs in that its user is mailed about, each with its own template (auth.security_<kind>): a password changed, the email address changed (told to the address the account had), a provider linked or unlinked, a second factor added or removed, a passkey added or removed. None by default, as Supabase's. Mail goes to a confirmed address only, in the user's locale.
1017
+ * The changes to how an account signs in that its user is mailed about, each with its own template (auth.security_<kind>): a password changed, the email address changed (told to the address the account had), a provider linked or unlinked, a second factor added or removed, a passkey added or removed. By default a second factor or a passkey added or removed (mfa_factor_enrolled, mfa_factor_removed, passkey_added, passkey_removed); the others as the project chooses. Mail goes to a confirmed address only, in the user's locale.
1018
1018
  */
1019
1019
  security_notifications?: ("password_changed" | "email_changed" | "identity_linked" | "identity_unlinked" | "mfa_factor_enrolled" | "mfa_factor_removed" | "passkey_added" | "passkey_removed")[];
1020
1020
  /**
1021
1021
  * Bind each session to the device it was opened on: a refresh from another device fingerprint, browser or operating system ends the session; a network change is recorded in the audit log. Default false.
1022
1022
  */
1023
1023
  session_device_binding?: boolean;
1024
+ /**
1025
+ * Let clients create new users — the project's sign-up switch. Default true; off, every way a client opens an account answers 403 `signups_closed`: `POST /auth/signup`, a passkey signup, a guest, a phone's first code and a provider's first sign-in. Accounts sign in as before, and the admin API (`POST /admin/users`, imports, test users) still creates users.
1026
+ */
1027
+ signups_enabled?: boolean;
1024
1028
  /**
1025
1029
  * Base URL for the links in auth emails (reset, verify, magic link). Empty: the Palbase-hosted default.
1026
1030
  */
@@ -1093,13 +1097,17 @@ export type AuthSettingsUpdateRequest = {
1093
1097
  */
1094
1098
  risk_strict_step_up?: number;
1095
1099
  /**
1096
- * The changes to how an account signs in that its user is mailed about, each with its own template (auth.security_<kind>): a password changed, the email address changed (told to the address the account had), a provider linked or unlinked, a second factor added or removed, a passkey added or removed. None by default, as Supabase's. Mail goes to a confirmed address only, in the user's locale. Present (even []) replaces the kinds; absent keeps them.
1100
+ * The changes to how an account signs in that its user is mailed about, each with its own template (auth.security_<kind>): a password changed, the email address changed (told to the address the account had), a provider linked or unlinked, a second factor added or removed, a passkey added or removed. By default a second factor or a passkey added or removed (mfa_factor_enrolled, mfa_factor_removed, passkey_added, passkey_removed); the others as the project chooses. Mail goes to a confirmed address only, in the user's locale. Present (even []) replaces the kinds; absent keeps them.
1097
1101
  */
1098
1102
  security_notifications?: ("password_changed" | "email_changed" | "identity_linked" | "identity_unlinked" | "mfa_factor_enrolled" | "mfa_factor_removed" | "passkey_added" | "passkey_removed")[];
1099
1103
  /**
1100
1104
  * Bind each session to the device it was opened on: a refresh from another device fingerprint, browser or operating system ends the session; a network change is recorded in the audit log. Default false.
1101
1105
  */
1102
1106
  session_device_binding?: boolean;
1107
+ /**
1108
+ * Let clients create new users — the project's sign-up switch. Default true; off, every way a client opens an account answers 403 `signups_closed`: `POST /auth/signup`, a passkey signup, a guest, a phone's first code and a provider's first sign-in. Accounts sign in as before, and the admin API (`POST /admin/users`, imports, test users) still creates users.
1109
+ */
1110
+ signups_enabled?: boolean;
1103
1111
  /**
1104
1112
  * Absolute http(s) URL without a fragment, at most 2048 characters; empty clears it.
1105
1113
  */
@@ -3160,12 +3168,29 @@ export type StatusSent = {
3160
3168
  export type StatusVerified = {
3161
3169
  status: "verified";
3162
3170
  };
3163
- export type StepUpRequest = {
3164
- /**
3165
- * Required when `method=totp`.
3166
- */
3167
- code?: string;
3168
- method: "totp" | "webauthn";
3171
+ /**
3172
+ * A TOTP step-up sends its `code`; a WebAuthn step-up sends the assertion of `POST /auth/step-up/webauthn/begin`'s ceremony at the top level of the same body.
3173
+ *
3174
+ */
3175
+ export type StepUpRequest = StepUpTOTPRequest | StepUpWebAuthnRequest;
3176
+ export type StepUpTOTPRequest = {
3177
+ code: string;
3178
+ method: "totp";
3179
+ };
3180
+ /**
3181
+ * `method` beside the PublicKeyCredential JSON a browser makes in `navigator.credentials.get()` (as `WebAuthnAssertionResponse`); all binary fields are base64url-encoded.
3182
+ *
3183
+ */
3184
+ export type StepUpWebAuthnRequest = {
3185
+ authenticatorAttachment?: string;
3186
+ clientExtensionResults?: {
3187
+ [key: string]: unknown;
3188
+ };
3189
+ id: string;
3190
+ method: "webauthn";
3191
+ rawId: string;
3192
+ response: WebAuthnAuthenticatorAssertionResponse;
3193
+ type: "public-key";
3169
3194
  };
3170
3195
  export type StepUpResult = {
3171
3196
  access_token: string;
@@ -4521,7 +4546,7 @@ export declare function client(t: Transport): {
4521
4546
  *
4522
4547
  * Opens a guest account and its session: an account with no address, password or credential, whose id stays when it becomes an account — by `POST /auth/anonymous/convert` (an address and a password), by adding a passkey (`POST /auth/webauthn/register/begin`, with `user_name`), or by linking a provider (`POST /auth/oauth/transactions` with the guest's session). What the visitor does as a guest is kept under that id.
4523
4548
  *
4524
- * The project must have turned guests on (`anonymous_sign_ins`; 403 `anonymous_sign_ins_disabled` otherwise). Held to what holds signup: the project's user ceiling (403 `signups_closed`), proof of work (403 `pow_required`, with the challenge), the risk engine and the project's `before.user.create` hook (told `is_anonymous`) — and to 30 guests an hour from one address (429). The access token carries `is_anonymous: true`; a native client that sends a DPoP proof gets the session and its tokens bound to that key, as a sign-in does. Send an `Idempotency-Key`: a retry then gets the same guest back, not a second one.
4549
+ * The project must have turned guests on (`anonymous_sign_ins`; 403 `anonymous_sign_ins_disabled` otherwise). Held to what holds signup: the project's sign-up switch and user ceiling (`signups_enabled`, 403 `signups_closed`), proof of work (403 `pow_required`, with the challenge), the risk engine and the project's `before.user.create` hook (told `is_anonymous`) — and to 30 guests an hour from one address (429). The access token carries `is_anonymous: true`; a native client that sends a DPoP proof gets the session and its tokens bound to that key, as a sign-in does. Send an `Idempotency-Key`: a retry then gets the same guest back, not a second one.
4525
4550
  */
4526
4551
  signInAnonymously(input?: {
4527
4552
  headers?: {
@@ -5098,7 +5123,7 @@ export declare function client(t: Transport): {
5098
5123
  /**
5099
5124
  * Complete a social sign-in
5100
5125
  *
5101
- * Finishes a sign-in with the provider's answer: signs the user in (or asks for a second factor), or links the identity. Retrying with the same body answers the same result.
5126
+ * Finishes a sign-in with the provider's answer: signs the user in (or asks for a second factor), or links the identity. Retrying with the same body answers the same result. A first sign-in that would open an account answers 403 `signups_closed` while the project takes no new users.
5102
5127
  */
5103
5128
  socialComplete(input: {
5104
5129
  path: {
@@ -5119,7 +5144,7 @@ export declare function client(t: Transport): {
5119
5144
  /**
5120
5145
  * Sign in with an SMS code
5121
5146
  *
5122
- * Checks the code sent by `POST /auth/otp`. A correct code signs the phone's owner in; an unknown number gets a new account when the project allows phone sign-ups (403 `signup_disabled` otherwise). A user with a second factor enrolled gets the `mfa_required` envelope instead of tokens and finishes with `mfa_token` at `/auth/mfa/*`. Codes are single-use; too many wrong attempts (`otp_max_attempts`) or an expired code (`otp_expired`) means requesting a new one.
5147
+ * Checks the code sent by `POST /auth/otp`. A correct code signs the phone's owner in; an unknown number gets a new account when the project allows phone sign-ups (403 `signup_disabled` otherwise) and takes new users at all (403 `signups_closed` otherwise). A user with a second factor enrolled gets the `mfa_required` envelope instead of tokens and finishes with `mfa_token` at `/auth/mfa/*`. Codes are single-use; too many wrong attempts (`otp_max_attempts`) or an expired code (`otp_expired`) means requesting a new one.
5123
5148
  */
5124
5149
  verifyPhoneOtp(input: {
5125
5150
  body: PhoneOTPVerifyRequest; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
@@ -5411,6 +5436,8 @@ export declare function client(t: Transport): {
5411
5436
  * IP-based rate limiting. Pepper + Argon2id is applied server-side;
5412
5437
  * the plaintext password never reaches storage.
5413
5438
  *
5439
+ * 403 `signups_closed` while the project takes no new users (its `signups_enabled` is off, or its user ceiling is reached).
5440
+ *
5414
5441
  */
5415
5442
  signup(input: {
5416
5443
  headers?: {
@@ -5762,6 +5789,8 @@ export declare function client(t: Transport): {
5762
5789
  * Finish the ceremony at `/auth/webauthn/signup/finish` carrying
5763
5790
  * the returned `user_id` as a query parameter.
5764
5791
  *
5792
+ * 403 `signups_closed` while the project takes no new users.
5793
+ *
5765
5794
  */
5766
5795
  webauthnSignupBegin(input: {
5767
5796
  body: WebAuthnSignupBeginRequest; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
@@ -5774,6 +5803,8 @@ export declare function client(t: Transport): {
5774
5803
  * attestation for the user returned by
5775
5804
  * `/auth/webauthn/signup/begin`.
5776
5805
  *
5806
+ * 403 `signups_closed` while the project takes no new users.
5807
+ *
5777
5808
  */
5778
5809
  webauthnSignupFinish(input: {
5779
5810
  query: {