@tinkerlandai/indiana-sdk 0.0.1 → 0.2.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.
@@ -0,0 +1 @@
1
+ import{Y as E,jc as O}from"./chunk-YOH6DIUN.js";var e=class extends Error{constructor(m,o,n,N){super(o);this.code=m;this.status=n;this.requestId=N;this.name="IndianaError"}code;status;requestId},V=Symbol("IndianaNoContent");function L(d,y){let m=d.webView??"hosted";if(!["hosted","embedded"].includes(m))throw new e("INVALID_REQUEST","Invalid Web View mode");let o=d.scope?E.parse(d.scope):void 0,n=new URL(d.baseUrl);if(n.username||n.password||n.search||n.hash)throw new e("INVALID_URL","baseUrl must not contain credentials, query, or fragment");if(n.protocol!=="https:"&&!(n.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(n.hostname)))throw new e("INVALID_URL","Use HTTPS, or HTTP on localhost during development");let N=d.fetch??globalThis.fetch.bind(globalThis),u=d.timeoutMs??1e4;if(!Number.isFinite(u)||u<=0||u>12e4)throw new e("INVALID_TIMEOUT","timeoutMs must be between 1 and 120000");let h=null,b,c=0,l=null,w=new Set;function S(){c+=1,h=null,l=null;for(let s of w)s.abort();w.clear()}async function _(s,R={},v="POST"){if(!h)throw new e("NOT_IDENTIFIED","Call identify with a server-issued customer token first");let I=c,r=new AbortController;w.add(r);let p=()=>I!==c?new e("IDENTITY_CHANGED","Customer identity changed while the request was in flight"):new e("TIMEOUT","Indiana request timed out"),g,A=new Promise((t,i)=>{g=()=>i(p()),r.signal.addEventListener("abort",g,{once:!0})}),D=setTimeout(()=>r.abort(),u),P=async()=>{let t=await N(`${n.href.replace(/\/$/,"")}/api/v1/${s}`,{method:v,headers:{"Content-Type":"application/json",Authorization:`Bearer ${h}`,...o?{"X-Indiana-Project":o.projectId,"X-Indiana-Environment":o.environment}:{}},body:v==="POST"?JSON.stringify(R):void 0,signal:r.signal,credentials:"omit",cache:"no-store",redirect:"error"});if(r.signal.aborted)throw p();let i=t.status===204?V:await t.json();if(r.signal.aborted)throw p();if(I!==c)throw new e("IDENTITY_CHANGED","Customer identity changed while the request was in flight");if(!t.ok){let a=O.safeParse(i);throw new e(a.success?a.data.error.code:"HTTP_ERROR",a.success?a.data.error.message:"Indiana request failed",t.status,t.headers.get("X-Request-Id")??void 0)}if(o&&i&&typeof i=="object"&&"scope"in i){let a=E.safeParse(i.scope);if(!a.success||a.data.projectId!==o.projectId||a.data.environment!==o.environment)throw new e("SCOPE_MISMATCH","Response scope does not match this SDK instance")}let T=t.headers.get("X-Indiana-Web-View");if(T){if(!/^\/web-view\/renderer\/[a-f0-9]{64}$/.test(T))throw new e("INVALID_RESPONSE","Invalid Web View renderer metadata");b=T}let f=t.headers.get("X-Indiana-Telemetry-Subject");if(f){if(!/^[a-f0-9]{64}$/.test(f))throw new e("INVALID_RESPONSE","Invalid telemetry identity");l!==f&&(l=f,y?.(l))}return i};try{return await Promise.race([P(),A])}catch(t){throw I!==c?new e("IDENTITY_CHANGED","Customer identity changed while the request was in flight"):t instanceof e?t:new e(r.signal.aborted?"TIMEOUT":"NETWORK_ERROR",r.signal.aborted?"Indiana request timed out":"Unable to read the Indiana response")}finally{clearTimeout(D),r.signal.removeEventListener("abort",g),w.delete(r)}}return{subjectKey:()=>l,webViewUrl(){if(m!=="embedded"){if(!b)throw new e("INVALID_RESPONSE","The gateway did not provide a hosted Web View renderer");return new URL(b,n).href}},identify(s){if(!s.trim()||/\s/.test(s)||s.length>4096)throw new e("INVALID_TOKEN","A nonempty server-issued token is required");S(),h=s},reset:S,request:_}}export{e as a,L as b};
@@ -0,0 +1 @@
1
+ import{a as s,b as E}from"./chunk-EZFAY4ZL.js";import{Dj as D,Lh as w,Mj as V,Oe as b,Oj as N,ma as I,na as x,oa as k,pa as h,ra as v,sj as O}from"./chunk-YOH6DIUN.js";async function q(n,t){if(!/^gg_ct_[A-Za-z0-9_-]{43}$/.test(t.customerToken))throw new s("INVALID_TOKEN","A verified Customer credential is required");let a=E(n);a.identify(t.customerToken);let i=w.resolvePaywall.request.parse({selector:{type:"paywall",paywallId:t.paywallId},commerce:t.commerce,locale:t.locale,...t.requirement?{requirement:t.requirement}:{}}),e=null,o=null,u=null,r=null,c=null;async function p(){let l=w.resolvePaywall,f=l.response.parse(await a.request(l.path,l.request.parse({...i,selector:e?{type:"version",versionId:e.id}:i.selector})));if(f.paywall.paywallId!==t.paywallId)throw new s("INVALID_RESPONSE","The requested Paywall changed");if(e&&(f.paywall.id!==e.id||f.paywall.paywallId!==e.paywallId||f.paywall.offering.id!==e.offering.id))throw new s("INVALID_RESPONSE","The pinned Paywall or Offering Version changed");return e=f.paywall,structuredClone(e)}async function d(l){if(!o)throw new s("INVALID_REQUEST","A quoted purchase is required");let f=l==="read"?w.readSubscriptionOperation:w.attach,m=f.response.parse(await a.request(f.path,f.request.parse({operationId:o.operationId,...l==="read"?{}:{intent:l}})));if(m.operation.operationId!==o.operationId||m.operation.currency!==i.commerce.currency)throw new s("INVALID_RESPONSE","The purchase operation changed");return u?.status==="complete"&&m.operation.status!=="complete"||u&&u.status!=="pending"&&m.operation.status==="pending"||(u=m.operation),structuredClone(u)}let y={get view(){return e?structuredClone(e):null},get quote(){return o?structuredClone(o):null},get operation(){return u?structuredClone(u):null},get webViewUrl(){return a.webViewUrl()},refresh:p,async preview(l){if(!e)throw new s("INVALID_REQUEST","Load the Paywall first");let f=e.document.buttons[l.actionId];if(f?.type!=="checkout")throw new s("INVALID_REQUEST","Choose an authored purchase action");let m=b(f.product,l.selection,e.capabilities);if(!m)throw new s("PRODUCT_UNAVAILABLE","Choose an available Product");let C=w.previewAttach.request.parse({requestId:l.requestId,plans:[{productVersionId:m}],provider:i.commerce.provider,currency:i.commerce.currency,...l.offerId?{offerId:l.offerId}:{},...l.nativeOffer?{nativeOffer:l.nativeOffer}:{}}),A=JSON.stringify(C);if(r&&r!==A&&(!u||u.status==="pending"))throw new s("COMMAND_PENDING","Finish or cancel the original purchase before changing it");if(r===A){if(c)return c;if(o)return structuredClone(o)}r=A;let P=(async()=>{let F=w.previewAttach,g=F.response.parse(await a.request(F.path,C));if(g.quote.currency!==i.commerce.currency||g.quote.provider!==i.commerce.provider||g.quote.lines.length!==1||g.quote.lines[0].productVersionId!==m)throw new s("INVALID_RESPONSE","The purchase quote changed its selected Product or presentment");return o=g.quote,u=null,structuredClone(o)})();c=P;try{return await P}finally{c===P&&(c=null)}},confirm:()=>d("confirm"),cancel:()=>d("cancel"),read:()=>d("read"),destroy(){a.reset(),e=null,o=null,u=null,r=null}};return await p(),y}function L(n){let t=null,a=null,i=0,e={attributes:v.parse(n.attributes??{}),onFeatureUsage:n.onFeatureUsage?(r,c)=>n.onFeatureUsage(r,structuredClone(c)):void 0,onExperimentViewed:n.onExperimentViewed?(r,c)=>n.onExperimentViewed(structuredClone(r),structuredClone(c)):void 0,stickyBucketAssignmentDocs:structuredClone(n.stickyBucketAssignmentDocs),saveStickyBucketAssignmentDoc:n.saveStickyBucketAssignmentDoc?r=>n.saveStickyBucketAssignmentDoc(structuredClone(r)):void 0,trackedFeatureUsage:{},trackedExperiments:new Set},o=r=>{if(!t)throw new Error("Initialize the Feature client before evaluation");if(t.expiresAt&&Date.parse(t.expiresAt)<=Date.now())throw new Error("Feature configuration expired; refresh before evaluation");return structuredClone(h(r,e))};async function u(){if(a)return a;let r=i,c=(async()=>{let p=V.parse(await n.load());if(p.scope.projectId!==n.scope.projectId||p.scope.environment!==n.scope.environment)throw new Error("Feature payload scope changed");if(r!==i)throw new Error("Feature client was reset");return t=structuredClone(p),e.features=t.features,e.savedGroups=I(t.savedGroups),e.trackedFeatureUsage={},structuredClone(t)})();a=c;try{return await c}finally{a===c&&(a=null)}}return{init:u,refresh:u,get generation(){return t?.generation??null},setAttributes(r){e.attributes=v.parse(r)},evalFeature:o,getFeatureValue(r,c){let p=o(r).value;return p===null?structuredClone(c):structuredClone(p)},reset(){i++,a=null,t=null,e.features={},e.savedGroups={},e.stickyBucketAssignmentDocs={},e.attributes={},e.trackedFeatureUsage={},e.trackedExperiments?.clear()}}}function Q(n,t,a){let i=N.parse(n);if(i.evaluationMode!=="local")throw new s("EVALUATION_MODE","Remote nodes are evaluated by Grow");if(i.expiresAt&&Date.parse(i.expiresAt)<=Date.now())throw new s("CONFIGURATION_EXPIRED","Refresh local rules at the scheduled transition");let e=i.topology.nodes.find(d=>d.id===t);if(!e)throw new s("NOT_FOUND","Funnel node is unavailable");let o=v.parse(a.attributes??{}),u={attributes:o,saveStickyBucketAssignmentDoc:a.saveStickyBucketAssignmentDoc?d=>a.saveStickyBucketAssignmentDoc(structuredClone(d)):void 0,onFeatureUsage:a.onFeatureUsage?(d,y)=>a.onFeatureUsage(d,structuredClone(y)):void 0,onExperimentViewed:a.onExperimentViewed?(d,y)=>a.onExperimentViewed(structuredClone(d),structuredClone(y)):void 0,features:structuredClone(i.growth.features),savedGroups:I(i.growth.savedGroups),stickyBucketAssignmentDocs:structuredClone(a.stickyBucketAssignmentDocs)},r,c;if(e.type==="condition"){let d=i.conditions[e.id];if(!d)throw new s("INVALID_RESPONSE","Local condition rules are unavailable");r=d.find(y=>x(o,y.condition,u.savedGroups))?.port??"default"}else if(e.type==="feature"){if(!e.resources.featureKey)throw new s("INVALID_RESPONSE","Feature reference is unavailable");c=h(e.resources.featureKey,u).value,r="next"}else if(e.type==="split"){let d=e.resources.experimentId,y=d?i.growth.experiments[d]:null;if(!y||!d)throw new s("INVALID_RESPONSE","Experiment reference is unavailable");let l=k(y,u);c=l.value,r=l.inExperiment&&!l.passthrough&&typeof l.value=="string"?l.value:"notParticipating"}else throw new s("SERVER_REQUIRED","This node is driven by the authoritative Funnel runtime");let p=e.outputs[r];if(!p)throw new s("INVALID_RESPONSE","Evaluated node exit is unavailable");return{nodeId:t,port:r,target:p,...c===void 0?{}:{value:c}}}function K(n,t){return t.type==="id"?n.customerId===t.value:n.externalUserId===t.value}function S(n,t){if(n===t)return!0;if(Array.isArray(n)||Array.isArray(t))return Array.isArray(n)&&Array.isArray(t)&&n.length===t.length&&n.every((e,o)=>S(e,t[o]));if(!n||!t||typeof n!="object"||typeof t!="object")return!1;let a=Object.entries(n),i=Object.entries(t);return a.length===i.length&&a.every(([e,o])=>Object.hasOwn(t,e)&&S(o,Reflect.get(t,e)))}function X(n){let t=/^gg_ct_[A-Za-z0-9_-]{32,128}$/.test(n.apiKey);if(typeof window<"u"&&!t)throw new s("SERVER_ONLY","Funnel starts are issued by your backend");if(!t&&!/^gg_sk_[A-Za-z0-9_-]{32,128}$/.test(n.apiKey))throw new s("INVALID_TOKEN","A customer credential or server API key is required");let a=E({...n,timeoutMs:n.timeoutMs??35e3});return a.identify(n.apiKey),{async start(i){let e=O.safeParse({...i,operation:"start"});if(!e.success||e.data.operation!=="start"||(t?e.data.customer!==void 0:e.data.customer===void 0))throw new s("INVALID_REQUEST","Invalid current Funnel start");let o=e.data,u=D.safeParse(await a.request("funnels",o));if(!u.success||!u.data.runToken)throw new s("INVALID_RESPONSE","Funnel start credentials are missing");let r=u.data;if(r.run.source.id!==o.funnelId||r.run.locale!==(o.locale??"en")||!S(r.run.parameters,o.parameters??{})||(o.customer?!r.customerId||o.customer.type==="id"&&r.customerId!==o.customer.value:r.customerId!==void 0))throw new s("INVALID_RESPONSE","Funnel start contract changed");return{...r,runToken:r.runToken}}}}export{K as a,S as b,q as c,L as d,Q as e,X as f};