@picked/client 0.0.57-next.1 → 0.0.58-next.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{api-a2yFdVfA.cjs → api-BYtek_yj.cjs} +1 -1
- package/dist/{api-DM5ZJxm0.js → api-lPqwfSkW.js} +1 -1
- package/dist/cjs/index.js +1 -1
- package/dist/cjs/plugins/dynamic-experiences/index.js +1 -1
- package/dist/cjs/plugins/experience-browser-storage/index.js +1 -1
- package/dist/cjs/plugins/history-browser-storage/index.js +1 -1
- package/dist/cjs/plugins/responses-browser-storage/index.js +1 -1
- package/dist/cjs/plugins/responses-indexeddb/index.js +1 -1
- package/dist/docs/pickedql/data-sources.md +61 -2
- package/dist/docs/pickedql/examples.md +46 -0
- package/dist/docs/pickedql/index.md +1 -0
- package/dist/docs/pickedql/schema.md +28 -0
- package/dist/docs/sdk/actions.md +31 -1
- package/dist/docs/sdk/conditions.md +14 -8
- package/dist/docs/sdk/experiences.md +21 -18
- package/dist/docs/sdk/features.md +8 -8
- package/dist/docs/sdk/fields.md +9 -9
- package/dist/docs/sdk/steps.md +12 -12
- package/dist/esm/index.js +1 -1
- package/dist/esm/plugins/dynamic-experiences/index.js +1 -1
- package/dist/esm/plugins/experience-browser-storage/index.js +20 -18
- package/dist/esm/plugins/history-browser-storage/index.js +17 -15
- package/dist/esm/plugins/responses-browser-storage/index.js +21 -19
- package/dist/esm/plugins/responses-indexeddb/index.js +37 -35
- package/dist/types/plugins/experience-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/history-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-browser-storage/plugin.d.ts.map +1 -1
- package/dist/types/plugins/responses-indexeddb/plugin.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
var e=`Picked-Client`,t=`Picked-Version`;function n(n,r){return{[e]:n,[t]:r}}var r=()=>`0.0.
|
|
1
|
+
var e=`Picked-Client`,t=`Picked-Version`;function n(n,r){return{[e]:n,[t]:r}}var r=()=>`0.0.58-next.1`,i=`v1`;function a(e,t){return new URL(`/api/${i}/${t}`,e).toString()}function o(e){return{...n(`sdk`,r()),...e}}async function s(e,t,n){let r=a(e,t);return fetch(r,{headers:o(n)})}async function c(e,t,n,r){let i=a(e,t);return fetch(i,{method:`POST`,headers:{"Content-Type":`application/json`,...o(r)},body:JSON.stringify(n)})}Object.defineProperty(exports,`i`,{enumerable:!0,get:function(){return n}}),Object.defineProperty(exports,`n`,{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,`r`,{enumerable:!0,get:function(){return r}}),Object.defineProperty(exports,`t`,{enumerable:!0,get:function(){return s}});
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../rules-logic-D_dZ52-Q.cjs`),t=require(`../object-CUJOjanb.cjs`),n=require(`../logger-fbvQC40v.cjs`),r=require(`../supports-Dsjg-82e.cjs`),i=require(`../experience-instance-DG9HFNSE.cjs`),a=require(`../storage-B6lQtc4-.cjs`),o=require(`../api-a2yFdVfA.cjs`),s=require(`../id-Bv44ilEo.cjs`);let c=require(`effector`),l=require(`@withease/factories`);var u=(0,c.createDomain)(`experience`),d=(0,l.createFactory)(({client:e,experience:t})=>{let n=u.store(t),r=(0,c.createApi)(n,{merge(e,t){return{...e,...t.experience}}}),a=n.map(e=>({name:e.name,decisionLogic:e.condition,targetSelectors:e.targets,initializeOpenLogic:e.initializeOpen})),o=(0,c.combine)(n,e.components.listStore,(e,t)=>(e.componentName?t.find(t=>t.name===e.componentName):void 0)||t.find(t=>t.name===`experience:${e.id}`)||t.find(e=>e.name===`experience`),{skipVoid:!1}),s=(0,c.combine)(n,e.steps.indexStore,e.conditions.indexStore,(e,t,n)=>{let r=e.nodes.filter(i.i).map(e=>t[e.stepId]).filter(e=>e!==void 0),a=e.nodes.filter(i.r).map(e=>n[e.conditionId]).filter(e=>e!==void 0);return{steps:r,nodes:e.nodes,edges:e.edges,conditions:a}}),l=t.actions??[];return{id:t.id,key:t.key,name:t.name,configStore:a,treeStore:s,componentStore:o,actions:l,merge:r.merge}});function f(e){return(0,l.invoke)(d,e)}var p=(0,l.createFactory)(({client:e})=>{let t=u.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.id]:t}}});function r(r){let i=t.getState()[r.id];if(i){i.merge({experience:r,strategy:`override`});return}let a=f({client:e,experience:r});n.register(a)}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e],{skipVoid:!1})}function o(e){return t.map(t=>Object.values(t).find(t=>t.key===e)??t[e],{skipVoid:!1})}let s=(0,c.attach)({source:t,effect:async(t,n)=>{let r=`key`in n?Object.values(t).find(e=>e.key===n.key):t[n.id];if(!r){`key`in n?`${n.key}`:`${n.id}`;return}let{options:i}=n;if(i?.bypassCondition){h(e,r,i);return}await _(e,r)&&h(e,r,i)}});return{register:r,get:a,getById:a,getByKey:o,launch:s,open:(0,c.attach)({source:{experiences:t,instances:e.experienceInstances.listStore},effect:({experiences:e,instances:t},n)=>{let r=`key`in n?Object.values(e).find(e=>e.key===n.key):e[n.id],i=t.filter(e=>r&&e.experienceId===r.id);if(i.length>0)for(let e of i)e.open();else s(`key`in n?{key:n.key,options:{...n.options,initialiseOpen:!0}}:{id:n.id,options:{...n.options,initialiseOpen:!0}})}}),listStore:i}});function m(e){return(0,l.invoke)(p,e)}var h=(e,t,n)=>{let{targetSelectors:r}=t.configStore.getState(),a=i.s(r,t.id);a.forEach(({elements:e})=>e.forEach(e=>i.l(e,t.id))),i.a(a,t.id,t.key).forEach(r=>{n?e.experienceInstances.spawn(t,r,n):e.experienceInstances.spawn(t,r)})},g=async e=>{await Promise.all(e.experiences.listStore.getState().map(async t=>{await _(e,t)&&h(e,t)}))},_=async(t,n)=>{if(!t.preconditionPassed||!t.session?.isUser)return!1;let{decisionLogic:r,targetSelectors:a}=n.configStore.getState();return i.s(a,n.id).length<1||a.length===0?!1:r===void 0?!0:e.n(r)?await e.t(t,r)===!0:!0},v=(e,t,n)=>{let r=t.name,i=t.setup(e);if(i){let e=n[r]??{};n[r]=Object.freeze({...e,...i})}},ee=(0,c.createDomain)(`component`),y=(0,l.createFactory)(({client:e,component:t,name:n})=>({name:n,component:t,frameworkStore:e.frameworks.getByName(t.frameworkName)}));function b(e){return(0,l.invoke)(y,e)}var x=(0,l.createFactory)(({client:e})=>{let t=ee.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.name]:t}}});function r(r,i){t.getState()[r]&&`${r}`;let a=b({client:e,name:r,component:i});n.register(a)}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e],{skipVoid:!1})}return{register:r,getByName:a,listStore:i}});function S(e){return(0,l.invoke)(x,e)}var C=(e,t,n)=>{n.register(e,t)},te=(0,c.createDomain)(`framework`),ne=(0,l.createFactory)(({client:e})=>{let t=te.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.name]:t.framework}}});function r(e,r){t.getState()[e]&&`${e}`,n.register({name:e,framework:r})}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e],{skipVoid:!1})}return{register:r,getByName:a,listStore:i}});function re(e){return(0,l.invoke)(ne,e)}var ie=(e,t)=>{t.register(e.name,e)},ae=6e3,oe=a.t(`last_ping`,`global`),se=async e=>{try{let t=Date.now(),n=a.n.get(oe),r=0;if(n.isSet()&&(r=n.json()??0),r&&t-r<ae)return!0;let i={projectId:e.projectId,path:window.location.pathname},s={},c=document.getElementById(`picked_script`)?.dataset.verceltoken;c&&(s[`x-vercel-protection-bypass`]=c);let l=(await o.n(e.apiUrl,`ping`,i,s)).ok;return l&&n.set(t),l}catch{return!1}};function w(e){let t=typeof window<`u`,n={client:e,responses:e.responses.listStore.getState(),fields:e.fields.listStore.getState(),location:t?window.location:void 0,document:t?window.document:void 0,sessionStorage:t?window.sessionStorage:void 0,localStorage:t?window.localStorage:void 0};return Object.freeze(n)}var T=(0,c.createDomain)(`predicate`),E=(0,l.createFactory)(({client:e})=>{let t=T.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.name]:{name:t.name,predicate:t.predicate}}}});function r(e,r){t.getState()[e]&&`${e}`,n.register(Object.freeze({name:e,predicate:r}))}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e]?.predicate,{skipVoid:!1})}function o(t){let n=(0,c.createStore)(()=>void 0,{skipVoid:!1}),r=(0,c.createEffect)(async()=>{let n=a(t).getState();if(!n)return;let r=w(e);return(...e)=>n({...r,args:e})});return(0,c.sample)({source:{responses:e.responses.listStore,fields:e.fields.listStore},target:r}),(0,c.sample)({clock:r.doneData,filter:e=>e!==void 0,target:n}),r(),n}return{register:r,getByName:a,listStore:i,getEvokablePredicate:o}});function D(e){return(0,l.invoke)(E,e)}var O=(e,t,n)=>{n.register(e,t)},k=(0,c.createDomain)(`feature`),A=(0,l.createFactory)(({client:e})=>{let t=k.store({}),n=k.store([]),r=(0,c.createApi)(t,{register(e,t){return{...e,[t.id]:t.feature}}});function i(e,n){t.getState()[e]&&`${e}`;let i={id:e,feature:{...n,id:e}};r.register(Object.freeze({id:e,feature:i}))}let o=(0,c.createEffect)(async({id:e,key:t,storage:n=`local`})=>{let r=a.t(`feature:${e}:${t}`,`global`),i=(n===`session`?a.r:a.n).get(r).boolean();return i===null?null:{id:e,key:t,bucket:i,storage:n}}),s=(0,c.createEffect)(e=>{if(!e)return null;let t=a.t(`feature:${e.id}:${e.key}`,`global`);(e.storage===`session`?a.r:a.n).set(t,e.bucket)}),l=new Map,u=(0,c.attach)({source:{allFeaturesStore:t,resolvedFeaturesStore:n},effect:async({allFeaturesStore:e,resolvedFeaturesStore:t},{id:n,decide:r,key:i})=>{let a=t.find(e=>e.id===n&&e.key===i);if(a)return a;let o=e[n];if(!o)throw Error(`Feature with id '${n}' not found`);let s=l.get(`${n}:${i}`);if(s!==void 0){let e=await s;return{id:o.id,bucket:e,key:i,storage:o.feature.storage}}let c=r();l.set(`${n}:${i}`,c);let u=await c;return{id:o.id,bucket:u,key:i,storage:o.feature.storage}}});(0,c.sample)({clock:r.register,fn:e=>{let{id:t,feature:n}=e;return{id:t,key:n.feature.key,storage:n.feature.storage}},target:o}),(0,c.sample)({source:t,clock:u.doneData,fn:(e,t)=>{let{id:n,bucket:r,storage:i}=t,a=e[n]?.feature.key;return a?{key:a,id:n,bucket:r,storage:i}:null},target:s});let d=(0,c.combine)({allFeaturesStore:t,resolvedFeaturesStore:n}).map(({allFeaturesStore:e,resolvedFeaturesStore:t})=>Object.values(e).map(e=>{let n=t.find(t=>t.id===e.id&&t.key===e.feature.key);return n?{id:e.id,name:e.feature.name,key:e.feature.key,getBucket:()=>({id:e.id,bucket:n.bucket}),isResolved:!0}:{id:e.id,name:e.feature.name,key:e.feature.key,getBucket:async()=>await u(e.feature),isResolved:!1}}));(0,c.sample)({clock:u.doneData,source:{resolvedFeatures:n},filter:({resolvedFeatures:e},{id:t,key:n})=>!e.some(e=>e.id===t&&e.key===n),fn:(e,t)=>t,target:e.eventBus.prepend(e=>({type:`feature_decide`,feature:e}))}),(0,c.sample)({clock:[o.doneData,u.doneData],source:n,fn:(e,t)=>!t||e.some(e=>e.id===t.id&&e.key===t.key)?e:[...e,t],target:n});function f(e){return d.map(t=>t.find(t=>t.id===e))}function p(e){return t.map(t=>t[e],{skipVoid:!1})}function m(e){let t=(0,c.createStore)(async()=>void 0,{skipVoid:!1}),r=(0,c.createEffect)(async()=>{let t=f(e).getState();if(t)return async()=>(await t.getBucket()).bucket});return(0,c.sample)({source:n,filter:t=>t.some(t=>t.id===e),target:r}),(0,c.sample)({clock:r.doneData,filter:e=>e!==void 0,target:t}),r(),t}return{register:i,getById:f,getByName:p,listStore:d,getEvokableFeature:m}});function j(e){return(0,l.invoke)(A,e)}var M=(e,t,n)=>{n.register(e,t)},N=(0,c.createDomain)(`history`);function P(e,t){return{id:s.t(`history-entry-${Date.now()}-${Math.random()}}`),node:t,at:Date.now(),experienceId:e}}var F=(0,l.createFactory)(()=>{let e=N.store({entries:[]}),t=N.event(),n=N.event(),r=N.event(),a=N.event();e.on(t,(e,t)=>({entries:[...e.entries,P(t.experienceId,t.node)]})),e.on(n,(e,t)=>({entries:[...e.entries.slice(0,-1),P(t.experienceId,t.node)]})),e.on(a,(e,t)=>t);function o(e,t){let n={lastStepIndex:-1,secondLastStepIndex:-1};for(let r=e.length-1;r>=0;r--){let a=e[r],o=a?.node;if(!(!o||!i.i(o))&&!(t&&a.experienceId!==t))if(n.lastStepIndex===-1)n.lastStepIndex=r;else{n.secondLastStepIndex=r;break}}return n}return e.on(r,(e,t)=>{let{secondLastStepIndex:n}=o(e.entries,t.experienceId),r=n===-1?e.entries:e.entries.slice(0,n+1);return{...e,entries:r}}),{push:t,replace:n,back:r,restore:a,entriesStore:e.map(e=>e.entries)}});function I(){return(0,l.invoke)(F)}var L=0,R=e=>{let t=(0,c.createStore)([]);return(0,c.sample)({clock:e,source:t,fn:(e,t)=>[...e,{id:L++,at:new Date,payload:t}],target:t}),t},z=(0,c.createDomain)(`field`),B=(0,l.createFactory)(()=>{let e=z.store({}),t=(0,c.createApi)(e,{register(e,t){return{...e,[t.field.id]:t.field}},updateByKey(e,t){let n=Object.values(e).find(e=>e.key===t.fieldKey);return n?{...e,[n.id]:Object.assign({},n,t.updates)}:e}});function n(e){t.register({field:e})}function r(n,r){let i=e.getState(),a=Object.values(i).find(e=>e.key===n)?.key;a&&t.updateByKey({fieldKey:a,updates:r})}let i=e.map(e=>Object.values(e));function a(t){return e.map(e=>e[t]??null,{skipVoid:!1})}function o(t){return e.map(e=>Object.values(e).find(e=>e.key===t)??null,{skipVoid:!1})}function s(t){return e.map(e=>Object.values(e).find(e=>e.name===t)??null,{skipVoid:!1})}return{register:n,update:r,getById:a,getByKey:o,listStore:i,getByName:s}});function V(){return(0,l.invoke)(B)}var H=(0,c.createDomain)(`step`),U=(0,l.createFactory)(()=>{let e=H.store({}),t=(0,c.createApi)(e,{register(e,t){return{...e,[t.step.id]:t.step}}});function n(e){t.register({step:e})}let r=e.map(e=>Object.values(e));function i(t){return e.map(e=>e[t]??null,{skipVoid:!1})}function a(t){return e.map(e=>Object.values(e).find(e=>e.key===t)??null,{skipVoid:!1})}return{register:n,getById:i,getByKey:a,listStore:r,indexStore:e}});function W(){return(0,l.invoke)(U)}function G(e,t){t.register(e)}var K=(0,c.createDomain)(`action`),ce=(0,l.createFactory)(({client:e})=>{let t=K.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.key]:{key:t.key,action:t.action}}}});function r(e,r){t.getState()[e]&&`${e}`,n.register(Object.freeze({key:e,action:r}))}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e]?.action,{skipVoid:!1})}function o(t){let n=(0,c.createStore)(()=>void 0,{skipVoid:!1}),r=(0,c.createEffect)(async()=>{let n=a(t).getState();if(!n)return;let r=w(e);return(...e)=>n({...r,args:e})});return(0,c.sample)({source:{responses:e.responses.listStore,fields:e.fields.listStore},target:r}),(0,c.sample)({clock:r.doneData,filter:e=>e!==void 0,target:n}),r(),n}return{register:r,getByKey:a,listStore:i,getEvokableAction:o}});function le(e){return(0,l.invoke)(ce,e)}var ue=(e,t,n)=>{n.register(e,t)};function q(e,t,n,r){return{fieldKey:e.key,userMessage:n,systemMessage:r,value:t}}function J(e){return e==null||e===``||Array.isArray(e)&&!e.length}function de(e){return typeof e==`string`||typeof e==`number`||typeof e==`boolean`||e===null}function Y(e){try{return JSON.stringify(e),!0}catch{return!1}}function fe(e,t){return t.find(t=>t.key===e)??null}function pe(e,t){return e.multiple&&(typeof t==`string`||typeof t==`number`||typeof t==`boolean`)?[t]:t}function me(e,t){if(e.type!==`object`||J(t))return null;if(e.multiple){if(!Array.isArray(t))return q(e,t,`This field expects multiple values`,`Field expects an array of objects.`);for(let n of t){if(typeof n!=`object`||!n)return q(e,t,`This field expects object values`,`Field expects an array of objects, but received non-object value.`);if(!Y(n))return q(e,t,`This field contains non-serializable data`,`Field value contains non-JSON-serializable data (functions, circular references, etc.).`)}return null}return Array.isArray(t)?q(e,t,`This field expects a single value`,`Field expects a single object, not an array.`):typeof t!=`object`||!t?q(e,t,`This field expects an object value`,`Field expects an object value.`):Y(t)?null:q(e,t,`This field contains non-serializable data`,`Field value contains non-JSON-serializable data (functions, circular references, etc.).`)}function he(e,t){if(!Array.isArray(e.options)||e.options.length===0)return null;let n=Array.isArray(t)?t:[t];if(e.type===`object`){let r=e.options.map(e=>JSON.stringify(e.value));return n.every(e=>r.includes(JSON.stringify(e)))?null:q(e,t,`This field expects a valid option`,`Field expects a valid option from the defined choices.`)}let r=e.options.map(e=>e.value);return n.every(e=>!de(e)||e===null?!1:r.includes(e))?null:q(e,t,`This field expects a valid option`,`Field expects a valid option. Valid options are: ${r.join(`, `)}`)}async function ge(e,n,r,i){if(n.validation&&!t.t(n.validation)&&!J(r)){let t=i?{$input:r,responses:i}:{$input:r},a=await e(n.validation,t);if(typeof a==`string`)return q(n,r,a,`Field validation resulted in an error: ${a}.`);if(a===!1)return q(n,r,`This field is invalid`,`Field validation resulted in an unspecified error.`)}return n.required&&J(r)?q(n,r,`This field is required`,`Field is required.`):!n.multiple&&Array.isArray(r)&&r.length>1?q(n,r,`This field expects a single response`,`Field expects a single response.`):null}var _e=(0,c.createDomain)(`response`),ve=(0,l.createFactory)(({client:t})=>{let n=_e.store({}),r=(0,c.createEvent)(),i=(0,c.createEvent)(),a=(0,c.createApi)(n,{register(e,t){return{...e,[t.fieldKey]:t.response}},registerBatch(e,t){return{...e,...t}},clear(e,t){return Object.fromEntries(Object.entries(e).filter(([e])=>e!==t))},clearAll(){return{}}});function o(e,t){if(typeof e==`string`){if(t===void 0)throw Error(`Response is required when registering a single field`);a.register({fieldKey:e,response:t})}else a.registerBatch(e)}let s=(n,r)=>e.t(t,n,r),l=(0,c.attach)({source:{allFields:t.fields.listStore,existingResponses:n},effect:async({allFields:e,existingResponses:t},n)=>{let r=[],i={},a={...t,...n},o=Object.entries(n),c=await Promise.allSettled(o.map(async([t,n])=>{let o=fe(t,e);if(!o){r.push({fieldKey:t,userMessage:`Field '${t}' not found`,systemMessage:`No field with key '${t}' exists. Available field keys are: ${e.map(e=>e.key).join(`,`)}.`,value:n});return}let c=pe(o,n),l=await ge(s,o,c,a);if(l){r.push(l);return}let u=me(o,c);if(u){r.push(u);return}let d=he(o,c);if(d){r.push(d);return}i[o.key]=n}));for(let[e,t]of c.entries()){if(t.status!==`rejected`)continue;let n=o[e];if(!n)continue;let[i,a]=n,s=t.reason instanceof Error?t.reason.message:String(t.reason);r.push({fieldKey:i,userMessage:`An unexpected error occurred validating this field`,systemMessage:`Validation threw for field '${i}': ${s}`,value:a})}return{responsesToCommit:i,validationErrors:r}}});async function u(e){return l(e)}let d=(0,c.attach)({source:n,effect:async(e,t)=>{let{responsesToCommit:n,validationErrors:r}=await l(t);return r.length?{errors:r}:(a.registerBatch(n),{errors:[]})}});async function f(e){return d(e)}let p=n.map(e=>e),m=n.map(e=>Object.entries(e).map(([e,t])=>({fieldKey:e,value:t})));function h(e){return(0,c.combine)({f:e,s:n},({f:e,s:t})=>{let n=e?t[e.key]??null:null;return n===null?e?.defaultValue!==void 0&&e.defaultValue!==null?e.defaultValue:null:n})}function g(e){return h(t.fields.getById(e))}function _(e){return h(t.fields.getByKey(e))}function v(e){return h(t.fields.getByName(e))}return(0,c.sample)({clock:r,target:a.clearAll}),(0,c.sample)({clock:i,target:a.clear}),{register:o,clearAll:r,clear:i,commit:f,getResponsesToCommit:u,listStore:m,getAllStore:p,getByFieldId:g,getByFieldKey:_,getByFieldName:v}});function ye(e){return(0,l.invoke)(ve,e)}var be=(0,c.createDomain)(`condition`),xe=(0,l.createFactory)(()=>{let e=be.store({}),t=(0,c.createApi)(e,{register(e,t){return{...e,[t.condition.id]:t.condition}}});function n(e){t.register({condition:e})}let r=e.map(e=>Object.values(e));function i(t){return e.map(e=>e[t]??null,{skipVoid:!1})}function a(t){return e.map(e=>Object.values(e).find(e=>e.key===t)??null,{skipVoid:!1})}return{register:n,getById:i,getByKey:a,listStore:r,indexStore:e}});function Se(){return(0,l.invoke)(xe)}function Ce(){if(r.r()){let e=[document.currentScript,document.getElementById(`picked_script`),document.getElementById(`picked`),document.getElementById(`cvly`),document.getElementById(`cvly-x`)];for(let t of e)if(t?.dataset.api)return t.dataset.api}return`https://api.picked.app`}function we(){if(!r.d())return{};let e=new URLSearchParams(window.location.search),t={},n=new Set;for(let[t]of e.entries())t.startsWith(`picked-`)&&n.add(t);for(let r of n){let n=r.replace(`picked-`,``);t[n]=e.getAll(r)}let i=a.t(`s`);if(n.size>0)a.r.set(i,t);else{let e=a.r.get(i);if(e)return e.json()||{}}return t}function Te(){return r.n()?crypto.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).substring(2,15)}-${Math.random().toString(36).substring(2,15)}-${Math.random().toString(36).substring(2,15)}`}var Ee=a.t(`session`,`global`),De=()=>{let e=a.n.get(Ee),t;e.isSet()&&(t=e.json());let n=t=>e.set({id:t.id,isUser:t.isUser,identifiers:t.identifiers}),r={id:t?.id??Te(),isUser:t?.isUser??void 0,identifiers:t?.identifiers??void 0,setIsUser(e){this.isUser=e,n(this)},setIdentifier(e,t){this.identifiers={...this.identifiers,[e]:t},n(this)},removeIdentifier(e){this.identifiers&&(this.identifiers=Object.fromEntries(Object.entries(this.identifiers).filter(([t])=>t!==e)),this.identifiers=Object.keys(this.identifiers).length?this.identifiers:void 0,n(this))}};return n(r),r},Oe=()=>r.o()?De():null;function X(){if(r.d())return globalThis.window.pickedConfig}function ke(){if(!r.d())return;let e=X(),t=e?.organizationId,n=e?.projectId;return t&&n?{organizationId:t,projectId:n}:void 0}function Z(){if(!r.r())return;let e=document.getElementById(`picked_script`)||document.querySelector(`script#picked`)||document.getElementById(`cvly`);if(!e)return;let t=e.dataset.organizationid||e.getAttribute(`organization-id`),n=e.dataset.projectid||e.getAttribute(`project-id`);return t&&n?{organizationId:t,projectId:n}:void 0}function Ae(e){let{organizationId:t,projectId:n}=e;if(t&&n)return{organizationId:t,projectId:n};let r=ke();if(r)return r;let i=Z();if(i)return i;throw Error(`organizationId and projectId are required. You can provide them via one of:
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../rules-logic-D_dZ52-Q.cjs`),t=require(`../object-CUJOjanb.cjs`),n=require(`../logger-fbvQC40v.cjs`),r=require(`../supports-Dsjg-82e.cjs`),i=require(`../experience-instance-DG9HFNSE.cjs`),a=require(`../storage-B6lQtc4-.cjs`),o=require(`../api-BYtek_yj.cjs`),s=require(`../id-Bv44ilEo.cjs`);let c=require(`effector`),l=require(`@withease/factories`);var u=(0,c.createDomain)(`experience`),d=(0,l.createFactory)(({client:e,experience:t})=>{let n=u.store(t),r=(0,c.createApi)(n,{merge(e,t){return{...e,...t.experience}}}),a=n.map(e=>({name:e.name,decisionLogic:e.condition,targetSelectors:e.targets,initializeOpenLogic:e.initializeOpen})),o=(0,c.combine)(n,e.components.listStore,(e,t)=>(e.componentName?t.find(t=>t.name===e.componentName):void 0)||t.find(t=>t.name===`experience:${e.id}`)||t.find(e=>e.name===`experience`),{skipVoid:!1}),s=(0,c.combine)(n,e.steps.indexStore,e.conditions.indexStore,(e,t,n)=>{let r=e.nodes.filter(i.i).map(e=>t[e.stepId]).filter(e=>e!==void 0),a=e.nodes.filter(i.r).map(e=>n[e.conditionId]).filter(e=>e!==void 0);return{steps:r,nodes:e.nodes,edges:e.edges,conditions:a}}),l=t.actions??[];return{id:t.id,key:t.key,name:t.name,configStore:a,treeStore:s,componentStore:o,actions:l,merge:r.merge}});function f(e){return(0,l.invoke)(d,e)}var p=(0,l.createFactory)(({client:e})=>{let t=u.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.id]:t}}});function r(r){let i=t.getState()[r.id];if(i){i.merge({experience:r,strategy:`override`});return}let a=f({client:e,experience:r});n.register(a)}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e],{skipVoid:!1})}function o(e){return t.map(t=>Object.values(t).find(t=>t.key===e)??t[e],{skipVoid:!1})}let s=(0,c.attach)({source:t,effect:async(t,n)=>{let r=`key`in n?Object.values(t).find(e=>e.key===n.key):t[n.id];if(!r){`key`in n?`${n.key}`:`${n.id}`;return}let{options:i}=n;if(i?.bypassCondition){h(e,r,i);return}await _(e,r)&&h(e,r,i)}});return{register:r,get:a,getById:a,getByKey:o,launch:s,open:(0,c.attach)({source:{experiences:t,instances:e.experienceInstances.listStore},effect:({experiences:e,instances:t},n)=>{let r=`key`in n?Object.values(e).find(e=>e.key===n.key):e[n.id],i=t.filter(e=>r&&e.experienceId===r.id);if(i.length>0)for(let e of i)e.open();else s(`key`in n?{key:n.key,options:{...n.options,initialiseOpen:!0}}:{id:n.id,options:{...n.options,initialiseOpen:!0}})}}),listStore:i}});function m(e){return(0,l.invoke)(p,e)}var h=(e,t,n)=>{let{targetSelectors:r}=t.configStore.getState(),a=i.s(r,t.id);a.forEach(({elements:e})=>e.forEach(e=>i.l(e,t.id))),i.a(a,t.id,t.key).forEach(r=>{n?e.experienceInstances.spawn(t,r,n):e.experienceInstances.spawn(t,r)})},g=async e=>{await Promise.all(e.experiences.listStore.getState().map(async t=>{await _(e,t)&&h(e,t)}))},_=async(t,n)=>{if(!t.preconditionPassed||!t.session?.isUser)return!1;let{decisionLogic:r,targetSelectors:a}=n.configStore.getState();return i.s(a,n.id).length<1||a.length===0?!1:r===void 0?!0:e.n(r)?await e.t(t,r)===!0:!0},v=(e,t,n)=>{let r=t.name,i=t.setup(e);if(i){let e=n[r]??{};n[r]=Object.freeze({...e,...i})}},ee=(0,c.createDomain)(`component`),y=(0,l.createFactory)(({client:e,component:t,name:n})=>({name:n,component:t,frameworkStore:e.frameworks.getByName(t.frameworkName)}));function b(e){return(0,l.invoke)(y,e)}var x=(0,l.createFactory)(({client:e})=>{let t=ee.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.name]:t}}});function r(r,i){t.getState()[r]&&`${r}`;let a=b({client:e,name:r,component:i});n.register(a)}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e],{skipVoid:!1})}return{register:r,getByName:a,listStore:i}});function S(e){return(0,l.invoke)(x,e)}var C=(e,t,n)=>{n.register(e,t)},te=(0,c.createDomain)(`framework`),ne=(0,l.createFactory)(({client:e})=>{let t=te.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.name]:t.framework}}});function r(e,r){t.getState()[e]&&`${e}`,n.register({name:e,framework:r})}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e],{skipVoid:!1})}return{register:r,getByName:a,listStore:i}});function re(e){return(0,l.invoke)(ne,e)}var ie=(e,t)=>{t.register(e.name,e)},ae=6e3,oe=a.t(`last_ping`,`global`),se=async e=>{try{let t=Date.now(),n=a.n.get(oe),r=0;if(n.isSet()&&(r=n.json()??0),r&&t-r<ae)return!0;let i={projectId:e.projectId,path:window.location.pathname},s={},c=document.getElementById(`picked_script`)?.dataset.verceltoken;c&&(s[`x-vercel-protection-bypass`]=c);let l=(await o.n(e.apiUrl,`ping`,i,s)).ok;return l&&n.set(t),l}catch{return!1}};function w(e){let t=typeof window<`u`,n={client:e,responses:e.responses.listStore.getState(),fields:e.fields.listStore.getState(),location:t?window.location:void 0,document:t?window.document:void 0,sessionStorage:t?window.sessionStorage:void 0,localStorage:t?window.localStorage:void 0};return Object.freeze(n)}var T=(0,c.createDomain)(`predicate`),E=(0,l.createFactory)(({client:e})=>{let t=T.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.name]:{name:t.name,predicate:t.predicate}}}});function r(e,r){t.getState()[e]&&`${e}`,n.register(Object.freeze({name:e,predicate:r}))}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e]?.predicate,{skipVoid:!1})}function o(t){let n=(0,c.createStore)(()=>void 0,{skipVoid:!1}),r=(0,c.createEffect)(async()=>{let n=a(t).getState();if(!n)return;let r=w(e);return(...e)=>n({...r,args:e})});return(0,c.sample)({source:{responses:e.responses.listStore,fields:e.fields.listStore},target:r}),(0,c.sample)({clock:r.doneData,filter:e=>e!==void 0,target:n}),r(),n}return{register:r,getByName:a,listStore:i,getEvokablePredicate:o}});function D(e){return(0,l.invoke)(E,e)}var O=(e,t,n)=>{n.register(e,t)},k=(0,c.createDomain)(`feature`),A=(0,l.createFactory)(({client:e})=>{let t=k.store({}),n=k.store([]),r=(0,c.createApi)(t,{register(e,t){return{...e,[t.id]:t.feature}}});function i(e,n){t.getState()[e]&&`${e}`;let i={id:e,feature:{...n,id:e}};r.register(Object.freeze({id:e,feature:i}))}let o=(0,c.createEffect)(async({id:e,key:t,storage:n=`local`})=>{let r=a.t(`feature:${e}:${t}`,`global`),i=(n===`session`?a.r:a.n).get(r).boolean();return i===null?null:{id:e,key:t,bucket:i,storage:n}}),s=(0,c.createEffect)(e=>{if(!e)return null;let t=a.t(`feature:${e.id}:${e.key}`,`global`);(e.storage===`session`?a.r:a.n).set(t,e.bucket)}),l=new Map,u=(0,c.attach)({source:{allFeaturesStore:t,resolvedFeaturesStore:n},effect:async({allFeaturesStore:e,resolvedFeaturesStore:t},{id:n,decide:r,key:i})=>{let a=t.find(e=>e.id===n&&e.key===i);if(a)return a;let o=e[n];if(!o)throw Error(`Feature with id '${n}' not found`);let s=l.get(`${n}:${i}`);if(s!==void 0){let e=await s;return{id:o.id,bucket:e,key:i,storage:o.feature.storage}}let c=r();l.set(`${n}:${i}`,c);let u=await c;return{id:o.id,bucket:u,key:i,storage:o.feature.storage}}});(0,c.sample)({clock:r.register,fn:e=>{let{id:t,feature:n}=e;return{id:t,key:n.feature.key,storage:n.feature.storage}},target:o}),(0,c.sample)({source:t,clock:u.doneData,fn:(e,t)=>{let{id:n,bucket:r,storage:i}=t,a=e[n]?.feature.key;return a?{key:a,id:n,bucket:r,storage:i}:null},target:s});let d=(0,c.combine)({allFeaturesStore:t,resolvedFeaturesStore:n}).map(({allFeaturesStore:e,resolvedFeaturesStore:t})=>Object.values(e).map(e=>{let n=t.find(t=>t.id===e.id&&t.key===e.feature.key);return n?{id:e.id,name:e.feature.name,key:e.feature.key,getBucket:()=>({id:e.id,bucket:n.bucket}),isResolved:!0}:{id:e.id,name:e.feature.name,key:e.feature.key,getBucket:async()=>await u(e.feature),isResolved:!1}}));(0,c.sample)({clock:u.doneData,source:{resolvedFeatures:n},filter:({resolvedFeatures:e},{id:t,key:n})=>!e.some(e=>e.id===t&&e.key===n),fn:(e,t)=>t,target:e.eventBus.prepend(e=>({type:`feature_decide`,feature:e}))}),(0,c.sample)({clock:[o.doneData,u.doneData],source:n,fn:(e,t)=>!t||e.some(e=>e.id===t.id&&e.key===t.key)?e:[...e,t],target:n});function f(e){return d.map(t=>t.find(t=>t.id===e))}function p(e){return t.map(t=>t[e],{skipVoid:!1})}function m(e){let t=(0,c.createStore)(async()=>void 0,{skipVoid:!1}),r=(0,c.createEffect)(async()=>{let t=f(e).getState();if(t)return async()=>(await t.getBucket()).bucket});return(0,c.sample)({source:n,filter:t=>t.some(t=>t.id===e),target:r}),(0,c.sample)({clock:r.doneData,filter:e=>e!==void 0,target:t}),r(),t}return{register:i,getById:f,getByName:p,listStore:d,getEvokableFeature:m}});function j(e){return(0,l.invoke)(A,e)}var M=(e,t,n)=>{n.register(e,t)},N=(0,c.createDomain)(`history`);function P(e,t){return{id:s.t(`history-entry-${Date.now()}-${Math.random()}}`),node:t,at:Date.now(),experienceId:e}}var F=(0,l.createFactory)(()=>{let e=N.store({entries:[]}),t=N.event(),n=N.event(),r=N.event(),a=N.event();e.on(t,(e,t)=>({entries:[...e.entries,P(t.experienceId,t.node)]})),e.on(n,(e,t)=>({entries:[...e.entries.slice(0,-1),P(t.experienceId,t.node)]})),e.on(a,(e,t)=>t);function o(e,t){let n={lastStepIndex:-1,secondLastStepIndex:-1};for(let r=e.length-1;r>=0;r--){let a=e[r],o=a?.node;if(!(!o||!i.i(o))&&!(t&&a.experienceId!==t))if(n.lastStepIndex===-1)n.lastStepIndex=r;else{n.secondLastStepIndex=r;break}}return n}return e.on(r,(e,t)=>{let{secondLastStepIndex:n}=o(e.entries,t.experienceId),r=n===-1?e.entries:e.entries.slice(0,n+1);return{...e,entries:r}}),{push:t,replace:n,back:r,restore:a,entriesStore:e.map(e=>e.entries)}});function I(){return(0,l.invoke)(F)}var L=0,R=e=>{let t=(0,c.createStore)([]);return(0,c.sample)({clock:e,source:t,fn:(e,t)=>[...e,{id:L++,at:new Date,payload:t}],target:t}),t},z=(0,c.createDomain)(`field`),B=(0,l.createFactory)(()=>{let e=z.store({}),t=(0,c.createApi)(e,{register(e,t){return{...e,[t.field.id]:t.field}},updateByKey(e,t){let n=Object.values(e).find(e=>e.key===t.fieldKey);return n?{...e,[n.id]:Object.assign({},n,t.updates)}:e}});function n(e){t.register({field:e})}function r(n,r){let i=e.getState(),a=Object.values(i).find(e=>e.key===n)?.key;a&&t.updateByKey({fieldKey:a,updates:r})}let i=e.map(e=>Object.values(e));function a(t){return e.map(e=>e[t]??null,{skipVoid:!1})}function o(t){return e.map(e=>Object.values(e).find(e=>e.key===t)??null,{skipVoid:!1})}function s(t){return e.map(e=>Object.values(e).find(e=>e.name===t)??null,{skipVoid:!1})}return{register:n,update:r,getById:a,getByKey:o,listStore:i,getByName:s}});function V(){return(0,l.invoke)(B)}var H=(0,c.createDomain)(`step`),U=(0,l.createFactory)(()=>{let e=H.store({}),t=(0,c.createApi)(e,{register(e,t){return{...e,[t.step.id]:t.step}}});function n(e){t.register({step:e})}let r=e.map(e=>Object.values(e));function i(t){return e.map(e=>e[t]??null,{skipVoid:!1})}function a(t){return e.map(e=>Object.values(e).find(e=>e.key===t)??null,{skipVoid:!1})}return{register:n,getById:i,getByKey:a,listStore:r,indexStore:e}});function W(){return(0,l.invoke)(U)}function G(e,t){t.register(e)}var K=(0,c.createDomain)(`action`),ce=(0,l.createFactory)(({client:e})=>{let t=K.store({}),n=(0,c.createApi)(t,{register(e,t){return{...e,[t.key]:{key:t.key,action:t.action}}}});function r(e,r){t.getState()[e]&&`${e}`,n.register(Object.freeze({key:e,action:r}))}let i=t.map(e=>Object.values(e));function a(e){return t.map(t=>t[e]?.action,{skipVoid:!1})}function o(t){let n=(0,c.createStore)(()=>void 0,{skipVoid:!1}),r=(0,c.createEffect)(async()=>{let n=a(t).getState();if(!n)return;let r=w(e);return(...e)=>n({...r,args:e})});return(0,c.sample)({source:{responses:e.responses.listStore,fields:e.fields.listStore},target:r}),(0,c.sample)({clock:r.doneData,filter:e=>e!==void 0,target:n}),r(),n}return{register:r,getByKey:a,listStore:i,getEvokableAction:o}});function le(e){return(0,l.invoke)(ce,e)}var ue=(e,t,n)=>{n.register(e,t)};function q(e,t,n,r){return{fieldKey:e.key,userMessage:n,systemMessage:r,value:t}}function J(e){return e==null||e===``||Array.isArray(e)&&!e.length}function de(e){return typeof e==`string`||typeof e==`number`||typeof e==`boolean`||e===null}function Y(e){try{return JSON.stringify(e),!0}catch{return!1}}function fe(e,t){return t.find(t=>t.key===e)??null}function pe(e,t){return e.multiple&&(typeof t==`string`||typeof t==`number`||typeof t==`boolean`)?[t]:t}function me(e,t){if(e.type!==`object`||J(t))return null;if(e.multiple){if(!Array.isArray(t))return q(e,t,`This field expects multiple values`,`Field expects an array of objects.`);for(let n of t){if(typeof n!=`object`||!n)return q(e,t,`This field expects object values`,`Field expects an array of objects, but received non-object value.`);if(!Y(n))return q(e,t,`This field contains non-serializable data`,`Field value contains non-JSON-serializable data (functions, circular references, etc.).`)}return null}return Array.isArray(t)?q(e,t,`This field expects a single value`,`Field expects a single object, not an array.`):typeof t!=`object`||!t?q(e,t,`This field expects an object value`,`Field expects an object value.`):Y(t)?null:q(e,t,`This field contains non-serializable data`,`Field value contains non-JSON-serializable data (functions, circular references, etc.).`)}function he(e,t){if(!Array.isArray(e.options)||e.options.length===0)return null;let n=Array.isArray(t)?t:[t];if(e.type===`object`){let r=e.options.map(e=>JSON.stringify(e.value));return n.every(e=>r.includes(JSON.stringify(e)))?null:q(e,t,`This field expects a valid option`,`Field expects a valid option from the defined choices.`)}let r=e.options.map(e=>e.value);return n.every(e=>!de(e)||e===null?!1:r.includes(e))?null:q(e,t,`This field expects a valid option`,`Field expects a valid option. Valid options are: ${r.join(`, `)}`)}async function ge(e,n,r,i){if(n.validation&&!t.t(n.validation)&&!J(r)){let t=i?{$input:r,responses:i}:{$input:r},a=await e(n.validation,t);if(typeof a==`string`)return q(n,r,a,`Field validation resulted in an error: ${a}.`);if(a===!1)return q(n,r,`This field is invalid`,`Field validation resulted in an unspecified error.`)}return n.required&&J(r)?q(n,r,`This field is required`,`Field is required.`):!n.multiple&&Array.isArray(r)&&r.length>1?q(n,r,`This field expects a single response`,`Field expects a single response.`):null}var _e=(0,c.createDomain)(`response`),ve=(0,l.createFactory)(({client:t})=>{let n=_e.store({}),r=(0,c.createEvent)(),i=(0,c.createEvent)(),a=(0,c.createApi)(n,{register(e,t){return{...e,[t.fieldKey]:t.response}},registerBatch(e,t){return{...e,...t}},clear(e,t){return Object.fromEntries(Object.entries(e).filter(([e])=>e!==t))},clearAll(){return{}}});function o(e,t){if(typeof e==`string`){if(t===void 0)throw Error(`Response is required when registering a single field`);a.register({fieldKey:e,response:t})}else a.registerBatch(e)}let s=(n,r)=>e.t(t,n,r),l=(0,c.attach)({source:{allFields:t.fields.listStore,existingResponses:n},effect:async({allFields:e,existingResponses:t},n)=>{let r=[],i={},a={...t,...n},o=Object.entries(n),c=await Promise.allSettled(o.map(async([t,n])=>{let o=fe(t,e);if(!o){r.push({fieldKey:t,userMessage:`Field '${t}' not found`,systemMessage:`No field with key '${t}' exists. Available field keys are: ${e.map(e=>e.key).join(`,`)}.`,value:n});return}let c=pe(o,n),l=await ge(s,o,c,a);if(l){r.push(l);return}let u=me(o,c);if(u){r.push(u);return}let d=he(o,c);if(d){r.push(d);return}i[o.key]=n}));for(let[e,t]of c.entries()){if(t.status!==`rejected`)continue;let n=o[e];if(!n)continue;let[i,a]=n,s=t.reason instanceof Error?t.reason.message:String(t.reason);r.push({fieldKey:i,userMessage:`An unexpected error occurred validating this field`,systemMessage:`Validation threw for field '${i}': ${s}`,value:a})}return{responsesToCommit:i,validationErrors:r}}});async function u(e){return l(e)}let d=(0,c.attach)({source:n,effect:async(e,t)=>{let{responsesToCommit:n,validationErrors:r}=await l(t);return r.length?{errors:r}:(a.registerBatch(n),{errors:[]})}});async function f(e){return d(e)}let p=n.map(e=>e),m=n.map(e=>Object.entries(e).map(([e,t])=>({fieldKey:e,value:t})));function h(e){return(0,c.combine)({f:e,s:n},({f:e,s:t})=>{let n=e?t[e.key]??null:null;return n===null?e?.defaultValue!==void 0&&e.defaultValue!==null?e.defaultValue:null:n})}function g(e){return h(t.fields.getById(e))}function _(e){return h(t.fields.getByKey(e))}function v(e){return h(t.fields.getByName(e))}return(0,c.sample)({clock:r,target:a.clearAll}),(0,c.sample)({clock:i,target:a.clear}),{register:o,clearAll:r,clear:i,commit:f,getResponsesToCommit:u,listStore:m,getAllStore:p,getByFieldId:g,getByFieldKey:_,getByFieldName:v}});function ye(e){return(0,l.invoke)(ve,e)}var be=(0,c.createDomain)(`condition`),xe=(0,l.createFactory)(()=>{let e=be.store({}),t=(0,c.createApi)(e,{register(e,t){return{...e,[t.condition.id]:t.condition}}});function n(e){t.register({condition:e})}let r=e.map(e=>Object.values(e));function i(t){return e.map(e=>e[t]??null,{skipVoid:!1})}function a(t){return e.map(e=>Object.values(e).find(e=>e.key===t)??null,{skipVoid:!1})}return{register:n,getById:i,getByKey:a,listStore:r,indexStore:e}});function Se(){return(0,l.invoke)(xe)}function Ce(){if(r.r()){let e=[document.currentScript,document.getElementById(`picked_script`),document.getElementById(`picked`),document.getElementById(`cvly`),document.getElementById(`cvly-x`)];for(let t of e)if(t?.dataset.api)return t.dataset.api}return`https://api.picked.app`}function we(){if(!r.d())return{};let e=new URLSearchParams(window.location.search),t={},n=new Set;for(let[t]of e.entries())t.startsWith(`picked-`)&&n.add(t);for(let r of n){let n=r.replace(`picked-`,``);t[n]=e.getAll(r)}let i=a.t(`s`);if(n.size>0)a.r.set(i,t);else{let e=a.r.get(i);if(e)return e.json()||{}}return t}function Te(){return r.n()?crypto.randomUUID():`${Date.now().toString(36)}-${Math.random().toString(36).substring(2,15)}-${Math.random().toString(36).substring(2,15)}-${Math.random().toString(36).substring(2,15)}`}var Ee=a.t(`session`,`global`),De=()=>{let e=a.n.get(Ee),t;e.isSet()&&(t=e.json());let n=t=>e.set({id:t.id,isUser:t.isUser,identifiers:t.identifiers}),r={id:t?.id??Te(),isUser:t?.isUser??void 0,identifiers:t?.identifiers??void 0,setIsUser(e){this.isUser=e,n(this)},setIdentifier(e,t){this.identifiers={...this.identifiers,[e]:t},n(this)},removeIdentifier(e){this.identifiers&&(this.identifiers=Object.fromEntries(Object.entries(this.identifiers).filter(([t])=>t!==e)),this.identifiers=Object.keys(this.identifiers).length?this.identifiers:void 0,n(this))}};return n(r),r},Oe=()=>r.o()?De():null;function X(){if(r.d())return globalThis.window.pickedConfig}function ke(){if(!r.d())return;let e=X(),t=e?.organizationId,n=e?.projectId;return t&&n?{organizationId:t,projectId:n}:void 0}function Z(){if(!r.r())return;let e=document.getElementById(`picked_script`)||document.querySelector(`script#picked`)||document.getElementById(`cvly`);if(!e)return;let t=e.dataset.organizationid||e.getAttribute(`organization-id`),n=e.dataset.projectid||e.getAttribute(`project-id`);return t&&n?{organizationId:t,projectId:n}:void 0}function Ae(e){let{organizationId:t,projectId:n}=e;if(t&&n)return{organizationId:t,projectId:n};let r=ke();if(r)return r;let i=Z();if(i)return i;throw Error(`organizationId and projectId are required. You can provide them via one of:
|
|
2
2
|
(1) createClient({ organizationId, projectId }) options;
|
|
3
3
|
(2) window.pickedConfig = { organizationId, projectId };
|
|
4
4
|
(3) a <script> tag with id 'picked_script', 'picked', or 'cvly' that includes data-organizationid/data-projectid (or organization-id/project-id) attributes.`)}function Q(e){return e==null||e===``||Number.isNaN(e)}var je={default:(e,t)=>Q(e)?String(t??``):String(e),append:(e,t)=>t&&!Q(e)?`${e} ${t}`:e,prepend:(e,t)=>t&&!Q(e)?`${t} ${e}`:e,upper:e=>typeof e==`string`?e.toUpperCase():e,lower:e=>typeof e==`string`?e.toLowerCase():e,capitalize:e=>typeof e==`string`?e.charAt(0).toUpperCase()+e.slice(1):e,list:e=>{if(!e||typeof e!=`object`)return e;let t=new Intl.ListFormat(`en`,{style:`long`,type:`conjunction`});if(Array.isArray(e))return t.format(e.map(String));if(e instanceof Map){let n=Array.from(e.entries()).map(([e,t])=>`${e}: ${t}`);return t.format(n)}return e instanceof Set?t.format(Array.from(e).map(String)):e}};function Me(e){return typeof e==`object`&&!!e}function Ne(e,t){let n=t;for(let t of e.split(`.`))if(Me(n)&&t in n)n=n[t];else return;return typeof n==`function`?n():n}function Pe(e,t){let n=e.split(`|`).map(e=>e.trim()),r=Ne(n.shift()??``,t);for(let e of n){let[t,...n]=e.split(`:`),i=t?.trim()??``,a=n.join(`:`).trim().replaceAll(/(?:^['"]|['"]$)/g,``),o=je[i];o&&(r=o(r,a))}return Q(r)?``:typeof r==`object`?JSON.stringify(r):String(r)}function Fe(e,t){let n=``,r=0;for(;r<e.length;)if(e[r]===`{`&&e[r+1]===`{`){r+=2;let i=``;for(;r<e.length;){if(e[r]===`}`&&e[r+1]===`}`){r+=2;break}i+=e[r++]}n+=Pe(i.trim(),t)}else n+=e[r++];return n}var $=class{static logger=n.t;options;isReady=!1;plugins={};pluginStartHooks=[];eventBus=(0,c.createEvent)();events;organizationId;projectId;apiUrl;dashboardUrl;documentationSiteUrl;session;precondition;condition;preconditionPassed;searchParams=we();history=I();components=S({client:this});experienceInstances=i.n({client:this});experiences=m({client:this});frameworks=re({client:this});predicates=D({client:this});features=j({client:this});conditions=Se();fields=V();responses=ye({client:this});steps=W();actions=le({client:this});constructor(e){this.events=R(this.eventBus);let{organizationId:t,projectId:n}=Ae(e);this.organizationId=t,this.projectId=n,this.options=Object.freeze(e),this.apiUrl=e.apiUrl||Ce(),this.dashboardUrl=e.dashboardUrl??`https://dashboard.picked.app`,this.documentationSiteUrl=e.documentationSiteUrl??`https://docs.picked.app`,this.session=Oe(),this.setupPlugins(),this.setupFeatures(),this.setupPredicates(),this.setupActions(),this.setupComponents(),this.precondition=e.precondition,this.condition=e.condition,this.eventBus({type:`load`,version:o.r()})}start=()=>{se(this),(async()=>{if(!r.r())return;await this.runPluginStartHooks();let e=await this.evaluateGate(this.precondition);this.preconditionPassed=e;let t;e&&(t=await this.evaluateGate(this.condition));let n=e&&t===!0;if(typeof t==`boolean`&&this.session){let e=t;this.session.setIsUser(e),this.eventBus({type:`user_decide`,value:e})}if(this.eventBus({type:`initialize`}),n&&g(this),await i.o(),n&&g(this),this.eventBus({type:`ready`}),this.isReady=!0,this.session){let{id:e,isUser:t,identifiers:n}=this.session;this.eventBus({type:`session`,id:e,isUser:t,identifiers:n})}})()};registerFramework=e=>{ie(e,this.frameworks)};registerComponent=(e,t)=>{C(e,t,this.components)};registerPlugin=e=>{v(this,e,this.plugins);let t=this.plugins[e.name];t?.onStart&&this.pluginStartHooks.push(t.onStart)};registerFeature=(e,t)=>{M(e,t,this.features)};registerPredicate=(e,t)=>{O(e,t,this.predicates)};registerCondition=e=>{this.conditions.register(e)};registerField=e=>{this.fields.register(e)};registerStep=e=>{G(e,this.steps)};registerAction=(e,t)=>{ue(e,t,this.actions)};registerExperience=e=>{e.component&&C(`experience:${e.id}`,e.component,this.components),this.experiences.register(e)};getStateSnapshot=()=>w(this);launchExperienceById=async(e,t)=>{this.experiences.launch({id:e,options:t})};launchExperienceByKey=async(e,t)=>{this.experiences.launch({key:e,options:t})};openExperienceById=async e=>{this.experiences.open({id:e})};openExperienceByKey=async e=>{this.experiences.open({key:e})};async runPluginStartHooks(){for(let e of this.pluginStartHooks)await e()}async evaluateGate(t){return t===void 0?!0:typeof t==`boolean`?t:await e.t(this,t)===!0}setupPlugins(){this.options.plugins?.forEach(e=>{if(Array.isArray(e)){e.forEach(e=>{this.registerPlugin(e)});return}let t=typeof e==`function`?e():e;if(Array.isArray(t)){t.forEach(e=>this.registerPlugin(e));return}this.registerPlugin(t)})}setupFeatures(){let e=this.options.features;e&&Object.entries(e).forEach(([e,t])=>{let n=typeof t==`function`?t():t;this.registerFeature(e,n)}),this.searchParams.feature&&this.searchParams.feature.forEach(e=>{try{let[t,n]=e.split(`:`);if(!t||!n)return;let r=n;r===`true`&&(r=!0),r===`false`&&(r=!1),this.registerFeature(t,{key:`${r}`,name:t,decide:()=>r,storage:`session`})}catch{}})}setupPredicates(){let e=this.options.predicates;e&&Object.entries(e).forEach(([e,t])=>{if(typeof t!=`function`){`${e}`;return}this.registerPredicate(e,t)})}setupActions(){let e=this.options.actions;e&&Object.entries(e).forEach(([e,t])=>{if(typeof t!=`function`){`${e}`;return}this.registerAction(e,t)})}setupComponents(){let e=this.options.components;e&&Object.entries(e).forEach(([e,t])=>{this.registerComponent(e,t)})}getPluginConfig=e=>this.plugins[e]??{};renderString=(e,t)=>{let n=this.responses.listStore.getState().reduce((e,t)=>(e[t.fieldKey]=t.value,e),{});return Fe(e,{...t,responses:n})}};function Ie(e){return{$$type:`lazy-component`,load:e}}function Le(e){return typeof e==`object`&&!!e&&`$$type`in e&&e.$$type===`lazy-component`}var Re=e=>{let t=new $(e);return r.p(t),t};exports.Picked=$,exports.createClient=Re,exports.createStorageKey=a.t,exports.evaluateRuleLogic=e.t,exports.getPickedConfig=X,exports.isLazyLoader=Le,exports.isPickedRoot=i.c,exports.lazyComponent=Ie,exports.localStore=a.n,exports.sessionStore=a.r,exports.subscribe=a.i,exports.supportsCookies=r.t,exports.supportsCryptoRandomUUID=r.n,exports.supportsDocument=r.r,exports.supportsHistory=r.i,exports.supportsIntersectionObserver=r.a,exports.supportsLocalStorage=r.o,exports.supportsLocation=r.s,exports.supportsSessionStorage=r.c,exports.supportsShadowRoot=r.l,exports.supportsWebComponents=r.u,exports.supportsWindow=r.d;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../../../logger-fbvQC40v.cjs`);const e=require(`../../../api-
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../../../logger-fbvQC40v.cjs`);const e=require(`../../../api-BYtek_yj.cjs`);function t(e){let t=2166136261;for(let n=0;n<e.length;n++)t^=e.charCodeAt(n),t=Math.imul(t,16777619);return t>>>0}function n(e){return t(e)%100}function r(e,t){return t<=0?!1:t>=100?!0:e<t}function i(e){return typeof e==`object`&&!!e&&`features`in e&&Array.isArray(e.features)}function a(e,t){if(Array.isArray(t.conditions))for(let n of t.conditions)e.registerCondition(n)}function o(e,t){e.registerExperience({id:t.id,key:t.key,name:t.name,targets:t.targets,nodes:t.nodes,edges:t.edges,condition:t.condition,initializeOpen:t.initializeOpen,componentName:t.componentName})}function s(e,t){if(Array.isArray(t.fields))for(let n of t.fields)e.registerField(n)}function c(e,t){if(!Array.isArray(t.steps))return;let n=new Map(t.fields.map(e=>[e.id,e.key]));for(let r of t.steps){let t=[];for(let e of r.fieldIds??[]){let i=n.get(e);if(i===void 0){`${r.key}${e}`;continue}t.push(i)}e.registerStep({id:r.id,key:r.key,name:r.name,fieldKeys:t,layoutName:r.layoutName})}}function l(e,t){if(Array.isArray(t.experiences))for(let n of t.experiences)o(e,n)}function u(e){let t=e.searchParams[`preview-token`];if(t&&t.length>0)return t[0]}function d(e){let t=new Set,n=e.searchParams.feature;if(!n)return t;for(let e of n){let n=e.indexOf(`:`);n>0&&t.add(e.substring(0,n))}return t}function f(e,t,r){let i=d(e),a=e.session?.id;if(!a)throw Error(`Session ID is required for feature registration`);let o=n(a);for(let n of t.features)i.has(n.id)||p(e,n,r,o)}function p(e,t,n,i){e.registerFeature(t.id,{key:`v${t.version}`,name:t.name,storage:n?`session`:`local`,decide:()=>t.enabled?r(i,t.weight):!1})}function m(){let e;try{e=__PICKED_PROJECT_CONFIG__}catch{return}if(e===void 0)return;let t;if(typeof e==`string`)try{t=JSON.parse(e)}catch{return}else t=e;if(i(t))return t}function h(e){try{let t=new URL(e).hostname;return t===`localhost`||t===`127.0.0.1`||t===`::1`}catch{return!1}}function g(e){if(typeof e!=`object`||!e||!(`cause`in e))return!1;let{cause:t}=e;return typeof t!=`object`||!t||!(`code`in t)?!1:t.code===`ECONNREFUSED`}async function _(t,n,r){let a=r?.previewToken?`projects/${n}?preview_token=${encodeURIComponent(r.previewToken)}`:`projects/${n}`,o;try{o=await e.t(t,a,r?.headers)}catch(e){return h(t)&&g(e),null}if(!o.ok)return`${o.status.toString()}`,null;let s;try{s=await o.json()}catch{return null}return i(s)?s:null}function v(e){return{name:`dynamic-experiences`,setup:t=>{let n=e?.apiUrl??t.apiUrl;return{onStart:async()=>{let r=u(t);if(r){let e=await _(n,t.projectId,{previewToken:r});e&&(f(t,e,!0),a(t,e),s(t,e),c(t,e),l(t,e));return}let i=e?.projectConfig??m();if(i){f(t,i,!1),a(t,i),s(t,i),c(t,i),l(t,i);return}let o=await _(n,t.projectId);o&&(f(t,o,!1),a(t,o),s(t,o),c(t,o),l(t,o))}}}}}exports.createClientHeaders=e.i,exports.dynamicExperiencesPlugin=v,exports.fetchProject=_;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../../../logger-fbvQC40v.cjs`);const e=require(`../../../storage-B6lQtc4-.cjs`);let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`../../../logger-fbvQC40v.cjs`);const e=require(`../../../supports-Dsjg-82e.cjs`),t=require(`../../../storage-B6lQtc4-.cjs`);let n=require(`effector`);var r=({storage:r=`local`}={})=>({name:`experience-browser-storage-plugin`,setup:i=>{if(!e.d())return;let a=r===`local`?t.n:t.r,o=(0,n.createEffect)(async e=>{if(!e)return;let n=t.t(`open-close`,`experience`,e),r=a.get(n).boolean();e.setOpen(r??e.isOpenStore.getState())});(0,n.sample)({source:i.eventBus,filter:e=>e.type===`experience_initialize`&&`experience`in e,fn:e=>e.experience,target:o});let s=(0,n.createEffect)(async e=>{let n=t.t(`open-close`,`experience`,e),r=e.isOpenStore.getState();a.set(n,r)}),c=e=>(0,n.sample)({source:e.isOpenStore,fn:()=>e,target:s});i.experienceInstances.listStore.watch(e=>{e.forEach(e=>c(e))})}});exports.experienceBrowserStoragePlugin=r;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../../storage-B6lQtc4-.cjs`);let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../../supports-Dsjg-82e.cjs`),t=require(`../../../storage-B6lQtc4-.cjs`);let n=require(`effector`);var r=t.t(`history`,`global`),i=({storage:i=`local`}={})=>({name:`history-browser-storage-plugin`,setup:a=>{if(!e.d())return;let o=i===`local`?t.n:t.r,s=(0,n.createEffect)(async()=>{let e=o.get(r).json();e&&a.history.restore({entries:e})}),c=(0,n.createEffect)(async e=>{o.set(r,e)});(0,n.sample)({source:a.eventBus,filter:e=>e.type===`load`,target:s}),(0,n.sample)({source:a.history.entriesStore,target:c})}});exports.historyBrowserStoragePlugin=i;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../../storage-B6lQtc4-.cjs`);let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../../supports-Dsjg-82e.cjs`),t=require(`../../../storage-B6lQtc4-.cjs`);let n=require(`effector`);var r=t.t(`responses`,`global`);function i(e,t){return Object.fromEntries(Object.entries(e).flatMap(([e,n])=>t.find(t=>t.key===e)?.policies?.doNotStoreValue?[]:[[e,n]]))}var a=({storage:a=`local`}={})=>({name:`responses-browser-storage-plugin`,setup:o=>{if(!e.d())return;let s=a===`local`?t.n:t.r,c=(0,n.createEffect)(async()=>{let e=s.get(r).json();if(!e)return null;o.responses.register(e)}),l=(0,n.attach)({source:{responses:o.responses.getAllStore,fields:o.fields.listStore},effect:({responses:e,fields:t})=>{s.set(r,i(e,t))}});(0,n.sample)({source:o.eventBus,filter:e=>e.type===`load`,target:c}),(0,n.sample)({source:o.responses.getAllStore,target:l})}});exports.responsesBrowserStoragePlugin=a;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../../../supports-Dsjg-82e.cjs`);let t=require(`effector`);var n=`picked`;async function r(e){let{storeName:t,version:r=1,keyPath:i=`id`}=e;return new Promise((e,a)=>{let o=indexedDB.open(n,r);o.onupgradeneeded=()=>{let e=o.result;e.objectStoreNames.contains(t)||e.createObjectStore(t,i?{keyPath:i}:void 0)},o.onsuccess=()=>e(o.result),o.onerror=()=>a(Error(o.error?.message))})}async function i(e,t){return new Promise((n,r)=>{let i=e.transaction(t,`readonly`).objectStore(t).getAll();i.onsuccess=()=>n(i.result),i.onerror=()=>r(Error(i.error?.message))})}async function a(e,t,n){return new Promise((r,i)=>{let a=e.transaction(t,`readwrite`).objectStore(t).put(n);a.onsuccess=()=>r(),a.onerror=()=>i(Error(a.error?.message))})}async function o(e,t,n){return new Promise((r,i)=>{let a=e.transaction(t,`readwrite`).objectStore(t).delete(n);a.onsuccess=()=>r(),a.onerror=()=>i(Error(a.error?.message))})}async function s(e,t){return new Promise((n,r)=>{let i=e.transaction(t,`readwrite`).objectStore(t).clear();i.onsuccess=()=>n(),i.onerror=()=>r(Error(i.error?.message))})}var c=`responses`,l={storeName:c,keyPath:`fieldKey`,version:2},u=()=>({name:`responses-indexdb-plugin`,setup:n=>{if(!e.d())return;let u=(0,t.createEffect)(async()=>{let e=(await i(await r(l),c)).reduce((e,{fieldKey:t,value:n})=>(e[t]=n,e),{});await n.responses.commit(e)}),d=(0,t.createEffect)(async e=>{await Promise.allSettled(e.map(async e=>{await a(await r(l),c,e)}))}),f=(0,t.createEffect)(async()=>{await s(await r(l),c)}),p=(0,t.createEffect)(async e=>{await o(await r(l),c,e)});(0,t.sample)({source:n.eventBus,filter:e=>e.type===`load`,target:u}),(0,t.sample)({clock:n.responses.listStore,source:{responses:n.responses.listStore,fields:n.fields.listStore},fn:({responses:e,fields:t})=>{let n=new Set(t.filter(e=>!e.policies?.doNotStoreValue).map(e=>e.key));return e.filter(e=>n.has(e.fieldKey)).map(e=>({fieldKey:e.fieldKey,value:e.value}))},target:d}),(0,t.sample)({clock:n.responses.clearAll,target:f}),(0,t.sample)({clock:n.responses.clear,target:p})}});exports.responsesIndexedDBPlugin=u;
|
|
@@ -4,7 +4,7 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Data sources
|
|
6
6
|
|
|
7
|
-
PickedQL provides
|
|
7
|
+
PickedQL provides four virtual tables, each optimized for different query patterns.
|
|
8
8
|
|
|
9
9
|
## `metrics` — Pre-aggregated daily data
|
|
10
10
|
|
|
@@ -36,4 +36,63 @@ Best for querying user, session, and event counts per feature flag and bucket. U
|
|
|
36
36
|
SELECT feature_users('homepage_test', 'control')
|
|
37
37
|
FROM features
|
|
38
38
|
WHERE project_id = 'your-project-id'
|
|
39
|
-
```
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## `experience_journey` — Sankey-ready step transitions
|
|
42
|
+
|
|
43
|
+
Returns per-edge `source, target, users` rows ready to feed a sankey or funnel chart. Wraps the 8-CTE step-transition scaffold (lagInFrame, consecutive-step dedup, top-N path ranking) that cards used to carry inline.
|
|
44
|
+
|
|
45
|
+
All filters pass through the **outer `WHERE`** — PickedQL extracts them as parameters and substitutes them into the underlying scaffold. Only `project_id` is required; the rest are optional and default sensibly.
|
|
46
|
+
|
|
47
|
+
| Param (WHERE key) | Purpose |
|
|
48
|
+
|---|---|
|
|
49
|
+
| `project_id` | Required tenant filter. |
|
|
50
|
+
| `experience_id` | Restrict to one `fact_events.experience_id`. |
|
|
51
|
+
| `starting_step` | Anchor edges at each user's first occurrence of this `step_id`. Absent → anchor at user's first step_enter event. |
|
|
52
|
+
| `targeting_step` | Trim each user's window at their first occurrence of this `step_id`. Users who never reach it still contribute their journey up to `num_nodes_after_start`. |
|
|
53
|
+
| `num_nodes_after_start` | Forward window size from the starting step (card 4129 semantics). |
|
|
54
|
+
| `num_nodes_before_target` | Backward window size from the targeting step (card 2740 semantics). Requires `targeting_step`. |
|
|
55
|
+
| `top_n_path` | Keep only the top-N distinct paths by user count. |
|
|
56
|
+
|
|
57
|
+
When `num_nodes_after_start` and `num_nodes_before_target` are both set, both predicates apply (intersection).
|
|
58
|
+
|
|
59
|
+
```sql
|
|
60
|
+
-- "Overall user journey" sankey (card 4129 pattern) — forward window, target optional
|
|
61
|
+
SELECT source, target, users FROM experience_journey
|
|
62
|
+
WHERE project_id = 'your-project-id'
|
|
63
|
+
AND starting_step = 'step-start'
|
|
64
|
+
AND targeting_step = 'step-target'
|
|
65
|
+
AND num_nodes_after_start = 10
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
```sql
|
|
69
|
+
-- "Paths leading to target" sankey (card 2740 pattern) — backward window + top-N ranking
|
|
70
|
+
SELECT source, target, users FROM experience_journey
|
|
71
|
+
WHERE project_id = 'your-project-id'
|
|
72
|
+
AND experience_id = 'exp-123'
|
|
73
|
+
AND starting_step = 'step-start'
|
|
74
|
+
AND targeting_step = 'step-target'
|
|
75
|
+
AND num_nodes_before_target = 5
|
|
76
|
+
AND top_n_path = 10
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
```sql
|
|
80
|
+
-- "Drop-off" sankey (card 2744 pattern) — omit targeting_step
|
|
81
|
+
SELECT source, target, users FROM experience_journey
|
|
82
|
+
WHERE project_id = 'your-project-id'
|
|
83
|
+
AND experience_id = 'exp-123'
|
|
84
|
+
AND starting_step = 'step-start'
|
|
85
|
+
AND num_nodes_after_start = 10
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Columns returned:
|
|
89
|
+
|
|
90
|
+
| Column | Type | Description |
|
|
91
|
+
|---|---|---|
|
|
92
|
+
| `source` | String | Edge start, formatted as `NN. step_id` (e.g., `01. welcome`). |
|
|
93
|
+
| `target` | String | Edge end, formatted as `NN. step_id`. |
|
|
94
|
+
| `users` | UInt64 | Distinct `user_canonical_id` count that traversed this edge. |
|
|
95
|
+
|
|
96
|
+
:::note
|
|
97
|
+
`experience_journey` is aggregated output. Dimension filters (`variant_type`, `device_category`, `country_code`, …) are **not** supported — if you need those, query [`events`](#events--user-level-event-data) directly and build the transitions yourself.
|
|
98
|
+
:::
|
|
@@ -180,6 +180,52 @@ ORDER BY variant DESC
|
|
|
180
180
|
| lead-capture-step | 398 |
|
|
181
181
|
|
|
182
182
|
|
|
183
|
+
## Sankey — overall user journey
|
|
184
|
+
|
|
185
|
+
Forward window from the starting step. Non-reachers of `targeting_step` still contribute edges up to `num_nodes_after_start`. Mirrors dashboard 1156 card 4129's semantics.
|
|
186
|
+
|
|
187
|
+
```sql
|
|
188
|
+
SELECT source, target, users FROM experience_journey
|
|
189
|
+
WHERE project_id = '{{ your project ID }}'
|
|
190
|
+
AND starting_step = '{{ starting step ID }}'
|
|
191
|
+
AND targeting_step = '{{ target step ID }}'
|
|
192
|
+
AND num_nodes_after_start = 10
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
**Result:**
|
|
196
|
+
|
|
197
|
+
| source | target | users |
|
|
198
|
+
| ------------------------- | ------------------------- | ----- |
|
|
199
|
+
| 001. step-start | 002. step-questionnaire-1 | 182 |
|
|
200
|
+
| 002. step-questionnaire-1 | 003. step-quote | 144 |
|
|
201
|
+
| 003. step-quote | 004. step-target | 121 |
|
|
202
|
+
|
|
203
|
+
## Sankey — top paths leading to a target step
|
|
204
|
+
|
|
205
|
+
Backward window anchored at the target, plus top-N path ranking. Mirrors dashboard 1156 card 3961 / dashboard 991 card 2740 semantics.
|
|
206
|
+
|
|
207
|
+
```sql
|
|
208
|
+
SELECT source, target, users FROM experience_journey
|
|
209
|
+
WHERE project_id = '{{ your project ID }}'
|
|
210
|
+
AND experience_id = '{{ experience ID }}'
|
|
211
|
+
AND starting_step = '{{ starting step ID }}'
|
|
212
|
+
AND targeting_step = '{{ target step ID }}'
|
|
213
|
+
AND num_nodes_before_target = 5
|
|
214
|
+
AND top_n_path = 10
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
## Sankey — drop-off view
|
|
218
|
+
|
|
219
|
+
Starts at `starting_step` and emits every consecutive transition until `num_nodes_after_start` steps later (no target). Mirrors card 2744.
|
|
220
|
+
|
|
221
|
+
```sql
|
|
222
|
+
SELECT source, target, users FROM experience_journey
|
|
223
|
+
WHERE project_id = '{{ your project ID }}'
|
|
224
|
+
AND experience_id = '{{ experience ID }}'
|
|
225
|
+
AND starting_step = '{{ starting step ID }}'
|
|
226
|
+
AND num_nodes_after_start = 10
|
|
227
|
+
```
|
|
228
|
+
|
|
183
229
|
## List all control users for mobile (user-level)
|
|
184
230
|
|
|
185
231
|
Use `events` when you need data at the individual user level:
|
|
@@ -29,6 +29,7 @@ PickedQL handles table mapping and dimension joins automatically. You write conc
|
|
|
29
29
|
|
|
30
30
|
- **Use `metrics` for dashboards**: Pre-aggregated data is significantly faster than raw events for standard reporting
|
|
31
31
|
- **Use `events` for deep dives**: Raw event-level data gives full flexibility for custom breakdowns and funnels
|
|
32
|
+
- **Use `experience_journey` for sankey / funnel charts**: One line of SQL returns `source, target, users` — the 8-CTE scaffold (dedup, top-N path ranking, lagInFrame) is handled internally; pass parameters via `WHERE`
|
|
32
33
|
- **Don't write JOINs**: PickedQL injects them automatically when you reference dimension columns
|
|
33
34
|
- **Group by any dimension**: Add dimension columns to `SELECT` and `GROUP BY` freely — required joins are handled for you
|
|
34
35
|
|
|
@@ -93,3 +93,31 @@ The `features` table provides [parameterized virtual functions](./virtual-functi
|
|
|
93
93
|
| `feature_users('name', 'bucket' [, 'key'])` | Unique users in a feature bucket |
|
|
94
94
|
| `feature_sessions('name', 'bucket' [, 'key'])` | Unique sessions in a feature bucket |
|
|
95
95
|
| `feature_events('name', 'bucket' [, 'key'])` | Unique events in a feature bucket |
|
|
96
|
+
|
|
97
|
+
## `experience_journey`
|
|
98
|
+
|
|
99
|
+
Aggregated step-transition edges for sankey / funnel charts. Parameters are supplied via the outer `WHERE` clause (PickedQL extracts them and substitutes into the underlying scaffold).
|
|
100
|
+
|
|
101
|
+
### Columns
|
|
102
|
+
|
|
103
|
+
| Column | Type | Description |
|
|
104
|
+
|---|---|---|
|
|
105
|
+
| `source` | String | Edge start, formatted as `NN. step_id` (e.g., `01. welcome`). |
|
|
106
|
+
| `target` | String | Edge end, formatted as `NN. step_id`. |
|
|
107
|
+
| `users` | UInt64 | Distinct `user_canonical_id` count that traversed this edge. |
|
|
108
|
+
|
|
109
|
+
### Parameters (via `WHERE`)
|
|
110
|
+
|
|
111
|
+
| Key | Type | Required | Purpose |
|
|
112
|
+
|---|---|---|---|
|
|
113
|
+
| `project_id` | String | ✓ | Tenant filter. |
|
|
114
|
+
| `experience_id` | String | | Restrict to one `fact_events.experience_id`. |
|
|
115
|
+
| `starting_step` | String | | Anchor edges at first occurrence of this `step_id`. Defaults to each user's first step. |
|
|
116
|
+
| `targeting_step` | String | | Trim each user's window at their first occurrence of this `step_id`. Non-reachers still contribute their pre-cut journey. |
|
|
117
|
+
| `num_nodes_after_start` | Integer | | Forward window from start: keep steps where `seq_pos < start_seq + N` (card 4129 semantics). |
|
|
118
|
+
| `num_nodes_before_target` | Integer | | Backward window from target: keep steps where `seq_pos >= target_seq - N` (card 2740 semantics). Requires `targeting_step`. |
|
|
119
|
+
| `top_n_path` | Integer | | Keep only the top-N most-walked paths by user count. Defaults to no limit. |
|
|
120
|
+
|
|
121
|
+
When both window params are set, both predicates apply (intersection).
|
|
122
|
+
|
|
123
|
+
See [data sources → experience_journey](./data-sources#experience_journey--sankey-ready-step-transitions) for usage examples.
|
package/dist/docs/sdk/actions.md
CHANGED
|
@@ -4,4 +4,34 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Actions
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Register action handlers on the Picked client so the SDK can invoke them when an experience flow reaches an action node.
|
|
8
|
+
|
|
9
|
+
## Registering actions
|
|
10
|
+
|
|
11
|
+
Register actions when creating the Picked client. Each key in the `actions` object must match an action key defined in the dashboard:
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createClient } from '@picked/client'
|
|
15
|
+
|
|
16
|
+
const picked = createClient({
|
|
17
|
+
organizationId: 'your-org-id',
|
|
18
|
+
projectId: 'your-project-id',
|
|
19
|
+
actions: {
|
|
20
|
+
'track-signup': () => {
|
|
21
|
+
analytics.track('signup_completed')
|
|
22
|
+
},
|
|
23
|
+
'notify': (params) => {
|
|
24
|
+
sendNotification(params)
|
|
25
|
+
},
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each action is a function that receives optional parameters and returns nothing. When the flow reaches an action node, the SDK calls the matching function.
|
|
31
|
+
|
|
32
|
+
## How actions work
|
|
33
|
+
|
|
34
|
+
1. **Registration**: Actions are registered to the Picked client during initialization
|
|
35
|
+
2. **Flow traversal**: When the SDK traverses the experience flow and reaches an action node, it looks up the action by its key
|
|
36
|
+
3. **Execution**: The matching function is called. Actions execute synchronously within the flow — the SDK continues to the next node after the function returns
|
|
37
|
+
4. **Non-blocking**: Actions don't interrupt the visitor's experience. The flow continues to the next connected node automatically
|
|
@@ -4,13 +4,15 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Conditions
|
|
6
6
|
|
|
7
|
-
Conditions
|
|
7
|
+
Conditions use JSON Logic to evaluate client state. Use them to gate experience activation, branch step navigation, and control field visibility.
|
|
8
8
|
|
|
9
|
-
## Conditions in
|
|
9
|
+
## Conditions in experiences
|
|
10
10
|
|
|
11
|
-
A common
|
|
11
|
+
A common pattern is using conditions between steps to branch the flow based on form responses:
|
|
12
12
|
|
|
13
13
|
```ts
|
|
14
|
+
import { createExperience } from '@picked/client/plugins/create-experience'
|
|
15
|
+
|
|
14
16
|
const questionaireFunnel = createExperience({
|
|
15
17
|
id: 'mini-funnel',
|
|
16
18
|
// experience config
|
|
@@ -30,7 +32,7 @@ const isSigningUpForNewsletter = funnel.addConditional({
|
|
|
30
32
|
condition: {
|
|
31
33
|
'==': [
|
|
32
34
|
{
|
|
33
|
-
var: 'responses.sign-up-for-newsletter-field',
|
|
35
|
+
var: 'responses.sign-up-for-newsletter-field',
|
|
34
36
|
},
|
|
35
37
|
true,
|
|
36
38
|
],
|
|
@@ -43,11 +45,13 @@ isSigningUpForNewsletter.addTrue(signUserUpToNewsletter)
|
|
|
43
45
|
isSigningUpForNewsletter.addFalse(userDoesNotWantNewsletter)
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
## Conditional
|
|
48
|
+
## Conditional fields
|
|
47
49
|
|
|
48
|
-
Fields can be shown or hidden based on previous responses or predicates using JSON Logic conditions:
|
|
50
|
+
Fields can be shown or hidden based on previous responses or [predicates](./predicates) using JSON Logic conditions:
|
|
49
51
|
|
|
50
52
|
```ts
|
|
53
|
+
import { createField } from '@picked/client/plugins/create-experience'
|
|
54
|
+
|
|
51
55
|
// Show email field only if user selected "email" as a contact method
|
|
52
56
|
export const emailField = createField({
|
|
53
57
|
id: 'email-input',
|
|
@@ -60,7 +64,7 @@ export const emailField = createField({
|
|
|
60
64
|
in: [
|
|
61
65
|
'email',
|
|
62
66
|
{
|
|
63
|
-
var: 'responses.communication-methods',
|
|
67
|
+
var: 'responses.communication-methods',
|
|
64
68
|
},
|
|
65
69
|
],
|
|
66
70
|
},
|
|
@@ -87,11 +91,13 @@ export const mailingAddressField = createField({
|
|
|
87
91
|
})
|
|
88
92
|
```
|
|
89
93
|
|
|
90
|
-
## Field
|
|
94
|
+
## Field options with conditions
|
|
91
95
|
|
|
92
96
|
Individual options within a field can also have conditional visibility:
|
|
93
97
|
|
|
94
98
|
```ts
|
|
99
|
+
import { createField } from '@picked/client/plugins/create-experience'
|
|
100
|
+
|
|
95
101
|
export const contactMethodsField = createField({
|
|
96
102
|
id: 'contact-methods',
|
|
97
103
|
name: 'contact-methods',
|
|
@@ -4,38 +4,41 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Experiences
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Define experiences in code with `createExperience()`, or load them from the dashboard at runtime with the [Dynamic Experiences Plugin](./plugins/dynamic-experiences).
|
|
8
8
|
|
|
9
|
-
## Creating
|
|
9
|
+
## Creating experiences
|
|
10
10
|
|
|
11
11
|
```ts
|
|
12
12
|
import { createExperience } from '@picked/client/plugins/create-experience'
|
|
13
|
+
import { adaptSvelteComponent } from '@picked/svelte'
|
|
14
|
+
import appCss from '../app.css?inline'
|
|
13
15
|
|
|
14
16
|
export const productPageExperience = createExperience({
|
|
15
17
|
id: 'product-page-experience',
|
|
16
|
-
name: 'Product Page Experience,
|
|
18
|
+
name: 'Product Page Experience',
|
|
17
19
|
condition: {
|
|
18
20
|
'==': [
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
]
|
|
24
|
-
}
|
|
21
|
+
{ var: 'location.pathname' },
|
|
22
|
+
'/product/',
|
|
23
|
+
],
|
|
24
|
+
},
|
|
25
25
|
targets: [{ selector: 'body', placement: 'after' }],
|
|
26
|
-
component: adaptSvelteComponent(
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
component: adaptSvelteComponent(
|
|
27
|
+
import('../ui/ProductPageExperience.svelte'),
|
|
28
|
+
{ css: [appCss] },
|
|
29
|
+
),
|
|
29
30
|
})
|
|
30
31
|
```
|
|
31
32
|
|
|
32
|
-
|
|
33
|
+
Register experiences using the [Create Experience Plugin](./plugins/create-experience) for code-defined experiences, or the [Dynamic Experiences Plugin](./plugins/dynamic-experiences) to fetch and manage them from the dashboard at runtime.
|
|
34
|
+
|
|
35
|
+
## How experiences work
|
|
33
36
|
|
|
34
37
|
When an experience is registered, the SDK follows this execution flow:
|
|
35
38
|
|
|
36
|
-
1. **Registration**: Experiences are registered to the Picked client using [createExperiencePlugin](./plugins/create-experience
|
|
37
|
-
2. **Condition
|
|
38
|
-
3. **Target
|
|
39
|
+
1. **Registration**: Experiences are registered to the Picked client using [createExperiencePlugin](./plugins/create-experience) for code-defined experiences, or [dynamicExperiencesPlugin](./plugins/dynamic-experiences) for dashboard-managed experiences
|
|
40
|
+
2. **Condition evaluation**: The SDK evaluates the experience's `condition` using the logic engine to determine if it should be shown
|
|
41
|
+
3. **Target resolution**: The SDK searches the DOM for elements matching the `targets` selectors (e.g., `body`, `.product-page`, `#checkout`)
|
|
39
42
|
4. **Initialization**: If the condition evaluates to `true` and at least one target element exists, the experience is initialized
|
|
40
|
-
5. **Component
|
|
41
|
-
6. **Flow
|
|
43
|
+
5. **Component rendering**: The experience's component is rendered and injected into the DOM at the specified target location and placement (before, after, append, prepend, or replace)
|
|
44
|
+
6. **Flow execution**: If the experience contains steps, fields, and conditionals, users can progress through the multi-step flow with validation and branching logic
|
|
@@ -4,9 +4,9 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Features
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Define feature flags with a `decide` function that resolves to a bucket value, access results through framework hooks, and invalidate cached decisions by bumping the version.
|
|
8
8
|
|
|
9
|
-
## Defining
|
|
9
|
+
## Defining features
|
|
10
10
|
|
|
11
11
|
Define features when creating the Picked client by passing a `features` object. Each feature requires a unique identifier, a versioning key, a human-readable name, and a `decide` function:
|
|
12
12
|
|
|
@@ -51,7 +51,7 @@ features: {
|
|
|
51
51
|
}
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
## How
|
|
54
|
+
## How features work
|
|
55
55
|
|
|
56
56
|
When a feature is registered, the SDK follows this execution order:
|
|
57
57
|
|
|
@@ -61,7 +61,7 @@ When a feature is registered, the SDK follows this execution order:
|
|
|
61
61
|
4. **Storage**: Once resolved, the bucket value is persisted to browser storage for consistency across page loads
|
|
62
62
|
5. **Event Emission**: A `feature_decide` event is fired on the event bus when a feature is resolved for the first time
|
|
63
63
|
|
|
64
|
-
## URL-
|
|
64
|
+
## URL-based overrides
|
|
65
65
|
|
|
66
66
|
You can override feature decisions via URL search parameters for testing purposes:
|
|
67
67
|
|
|
@@ -79,7 +79,7 @@ The format is `?picked-feature=<feature-id>:<bucket-value>`.
|
|
|
79
79
|
|
|
80
80
|
This is useful for QA testing specific feature variants without modifying code. The override is persisted in session storage and will be cleared when the browser tab is closed.
|
|
81
81
|
|
|
82
|
-
## Accessing
|
|
82
|
+
## Accessing features
|
|
83
83
|
|
|
84
84
|
Once features are defined, you can access their resolved bucket values in your application code. The SDK provides framework-specific hooks and a framework-agnostic event bus for subscribing to feature decisions.
|
|
85
85
|
|
|
@@ -132,7 +132,7 @@ function MyComponent() {
|
|
|
132
132
|
|
|
133
133
|
:::
|
|
134
134
|
|
|
135
|
-
## Using
|
|
135
|
+
## Using features in experience conditions
|
|
136
136
|
|
|
137
137
|
Features can be used with experience conditions via JSON logic:
|
|
138
138
|
|
|
@@ -156,7 +156,7 @@ const myExperience = createExperience({
|
|
|
156
156
|
})
|
|
157
157
|
```
|
|
158
158
|
|
|
159
|
-
## Listening to
|
|
159
|
+
## Listening to feature decisions
|
|
160
160
|
|
|
161
161
|
Subscribe to the `feature_decide` event to track when features are resolved:
|
|
162
162
|
|
|
@@ -206,7 +206,7 @@ features: {
|
|
|
206
206
|
|
|
207
207
|
For features managed through the dashboard, versioning is handled automatically — the dashboard appends the version number to the cache key used by the SDK.
|
|
208
208
|
|
|
209
|
-
## Best
|
|
209
|
+
## Best practices
|
|
210
210
|
|
|
211
211
|
- **Use `session` storage for temporary tests**: Set `storage: 'session'` for short-lived experiments that shouldn't persist across browser sessions
|
|
212
212
|
- **Keep `decide` functions fast**: The function blocks experience rendering, so avoid heavy computations or slow network calls
|
package/dist/docs/sdk/fields.md
CHANGED
|
@@ -4,9 +4,9 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Fields
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Define fields in code with `createField()`, validate input with JSON Logic, and read submitted values through step context.
|
|
8
8
|
|
|
9
|
-
## Defining
|
|
9
|
+
## Defining fields
|
|
10
10
|
|
|
11
11
|
Define fields using `createField()` from the `@picked/client/plugins/create-experience` package. Each field requires a unique identifier, name, type, label, and cardinality:
|
|
12
12
|
|
|
@@ -52,7 +52,7 @@ export const communicationMethods = createField({
|
|
|
52
52
|
})
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
## How
|
|
55
|
+
## How fields work
|
|
56
56
|
|
|
57
57
|
When a field is registered and used in an experience:
|
|
58
58
|
|
|
@@ -63,7 +63,7 @@ When a field is registered and used in an experience:
|
|
|
63
63
|
5. **Storage**: Valid responses are stored in the experience state under `responses[field.name]`
|
|
64
64
|
6. **Reference**: Field values can be referenced in subsequent field conditions, step conditions, or experience logic via `{ var: 'responses.field-name' }`
|
|
65
65
|
|
|
66
|
-
## Field
|
|
66
|
+
## Field types
|
|
67
67
|
|
|
68
68
|
Fields support four primitive types, `text`, `number`, `boolean` and `object`:
|
|
69
69
|
|
|
@@ -129,7 +129,7 @@ const conditionalField = createField({
|
|
|
129
129
|
})
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
## Field
|
|
132
|
+
## Field validation
|
|
133
133
|
|
|
134
134
|
Validate user input using JSON Logic rules. The `$input` variable contains the current field value:
|
|
135
135
|
|
|
@@ -179,7 +179,7 @@ const quantityField = createField({
|
|
|
179
179
|
The `catch`/`throw` pattern in JSON Logic validates that the condition evaluates to `true`. If it doesn't, the `throw` message is displayed to the user.
|
|
180
180
|
:::
|
|
181
181
|
|
|
182
|
-
## Field
|
|
182
|
+
## Field policies
|
|
183
183
|
|
|
184
184
|
Control how field data is tracked and stored using privacy policies:
|
|
185
185
|
|
|
@@ -205,7 +205,7 @@ const sensitiveField = createField({
|
|
|
205
205
|
|
|
206
206
|
Use these policies for sensitive data like SSNs, credit cards, or passwords that should only exist client-side.
|
|
207
207
|
|
|
208
|
-
## Field
|
|
208
|
+
## Field metadata
|
|
209
209
|
|
|
210
210
|
Enhance user experience with additional descriptive text:
|
|
211
211
|
|
|
@@ -229,7 +229,7 @@ const passwordField = createField({
|
|
|
229
229
|
- `description`: Brief explanation shown below the label
|
|
230
230
|
- `helpText`: Additional guidance or requirements displayed near the field
|
|
231
231
|
|
|
232
|
-
## Accessing
|
|
232
|
+
## Accessing field values
|
|
233
233
|
|
|
234
234
|
Field responses are stored in the experience state and can be accessed via the `responses` object:
|
|
235
235
|
|
|
@@ -251,7 +251,7 @@ const sensitiveField = createField({
|
|
|
251
251
|
|
|
252
252
|
:::
|
|
253
253
|
|
|
254
|
-
## Best
|
|
254
|
+
## Best practices
|
|
255
255
|
|
|
256
256
|
- **Use semantic IDs and names**: Name fields descriptively (e.g., `email-address` not `field1`) for easier debugging and analytics
|
|
257
257
|
- **Match type to data**: Use `number` for numeric values, not `text`, to enable proper validation and comparison
|
package/dist/docs/sdk/steps.md
CHANGED
|
@@ -4,9 +4,9 @@ layout: docs
|
|
|
4
4
|
|
|
5
5
|
# Steps
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Render steps with `<StepRouter>` and `<Step>` from `@picked/react` or `@picked/svelte`, and access the active step through `useStep()` / `getStep()`.
|
|
8
8
|
|
|
9
|
-
## How
|
|
9
|
+
## How steps work
|
|
10
10
|
|
|
11
11
|
When an experience starts, its steps are connected in a directed node graph. The SDK traverses this graph to determine which step to display:
|
|
12
12
|
|
|
@@ -18,7 +18,7 @@ When an experience starts, its steps are connected in a directed node graph. The
|
|
|
18
18
|
6. **Navigation**: The SDK follows the edge from the current step node to the next, traversing any conditionals or actions before arriving at the next step
|
|
19
19
|
7. **Completion**: When no further steps exist, the experience flow is complete
|
|
20
20
|
|
|
21
|
-
## Step
|
|
21
|
+
## Step routing
|
|
22
22
|
|
|
23
23
|
`StepRouter` listens to the experience's current step state and activates the matching `Step` by its `id`. Each `Step` registers itself on mount and unregisters on unmount, so lazy-rendered tabs or drawers work automatically.
|
|
24
24
|
|
|
@@ -72,7 +72,7 @@ function MyExperience() {
|
|
|
72
72
|
All Steps must be wrapped in a router, even if it is a single Step
|
|
73
73
|
:::
|
|
74
74
|
|
|
75
|
-
### Lazy-
|
|
75
|
+
### Lazy-loaded steps
|
|
76
76
|
|
|
77
77
|
For large experiences, steps can be lazily loaded using dynamic imports:
|
|
78
78
|
|
|
@@ -100,7 +100,7 @@ const WelcomeScreen = lazy(() => import('./WelcomeScreen'))
|
|
|
100
100
|
|
|
101
101
|
The `component` prop accepts a `Promise<Component>` — the step's content is not loaded until it becomes active.
|
|
102
102
|
|
|
103
|
-
## Accessing
|
|
103
|
+
## Accessing step context
|
|
104
104
|
|
|
105
105
|
Inside a `Step` component (or any component rendered within one), call `getStep()` / `useStep()` to access the step's reactive context:
|
|
106
106
|
|
|
@@ -123,7 +123,7 @@ Inside a `Step` component (or any component rendered within one), call `getStep(
|
|
|
123
123
|
onchange={field.onChange}
|
|
124
124
|
onblur={field.onBlur}
|
|
125
125
|
/>
|
|
126
|
-
{#if field.error && field.touched
|
|
126
|
+
{#if field.error && field.touched}
|
|
127
127
|
<p class="error">{field.error.userMessage}</p>
|
|
128
128
|
{/if}
|
|
129
129
|
{/each}
|
|
@@ -168,13 +168,13 @@ function ContactForm() {
|
|
|
168
168
|
| `step` | `Store<ExperienceStep>` | Reactive store containing the step's `id` and `label` |
|
|
169
169
|
| `fields` | `Store<FormField[]>` | Reactive store of fields with their current values, errors, and event handlers |
|
|
170
170
|
| `isActive` | `Store<boolean>` (Svelte) / `boolean` (React) | Whether this step is currently active in the router |
|
|
171
|
-
| `form` | Object | Form submission handlers (see Forms
|
|
171
|
+
| `form` | Object | Form submission handlers (see [Forms](#forms)) |
|
|
172
172
|
|
|
173
173
|
## Forms
|
|
174
174
|
|
|
175
175
|
When a step has fields, `getStep()` / `useStep()` provides a `form` object for handling submission, validation, and navigation.
|
|
176
176
|
|
|
177
|
-
### Handling
|
|
177
|
+
### Handling submission
|
|
178
178
|
|
|
179
179
|
Bind `form.props` to your `<form>` element's `onsubmit` handler:
|
|
180
180
|
|
|
@@ -249,7 +249,7 @@ Submitting the form:
|
|
|
249
249
|
5. Fires `form_submit_success` or `form_submit_error` on the event bus
|
|
250
250
|
6. Navigates to the next step (unless `stopNavigation` is called)
|
|
251
251
|
|
|
252
|
-
### Transforming
|
|
252
|
+
### Transforming data before submission
|
|
253
253
|
|
|
254
254
|
Use `form.onBeforeSubmit` to intercept and transform field data before it is committed. Callbacks run in series, each receiving the output of the previous:
|
|
255
255
|
|
|
@@ -293,7 +293,7 @@ function ContactStep() {
|
|
|
293
293
|
|
|
294
294
|
:::
|
|
295
295
|
|
|
296
|
-
### Success and
|
|
296
|
+
### Success and error callbacks
|
|
297
297
|
|
|
298
298
|
Register callbacks for submission outcomes using `form.onSuccess` and `form.onError`:
|
|
299
299
|
|
|
@@ -347,7 +347,7 @@ function ContactStep() {
|
|
|
347
347
|
|
|
348
348
|
Callbacks registered via `onSuccess` and `onError` are automatically cleaned up when the component unmounts — no manual teardown is needed.
|
|
349
349
|
|
|
350
|
-
### Preventing
|
|
350
|
+
### Preventing navigation on success
|
|
351
351
|
|
|
352
352
|
By default, the experience advances to the next step after a successful submission. Call `stopNavigation()` inside `onSuccess` to prevent this, for example when you need to handle navigation manually:
|
|
353
353
|
|
|
@@ -401,7 +401,7 @@ function PlanStep() {
|
|
|
401
401
|
|
|
402
402
|
:::
|
|
403
403
|
|
|
404
|
-
## Best
|
|
404
|
+
## Best practices
|
|
405
405
|
|
|
406
406
|
- **Give steps meaningful labels**: The `label` is used in analytics events and the dashboard — prefer descriptive names like `"Contact Details"` over `"Step 2"`
|
|
407
407
|
- **Keep steps focused**: Each step should collect a small, related set of information. Prefer more steps over longer forms to reduce abandonment
|
package/dist/esm/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { t as r } from "../logger-6z_gb4rA.js";
|
|
|
4
4
|
import { a as i, c as a, d as o, i as s, l as c, n as l, o as u, p as d, r as f, s as p, t as m, u as h } from "../supports-0XIfd4-R.js";
|
|
5
5
|
import { a as g, c as _, i as v, l as ee, n as te, o as ne, r as re, s as y } from "../experience-instance-DffMstSs.js";
|
|
6
6
|
import { i as ie, n as b, r as x, t as S } from "../storage-6Xk0uAxB.js";
|
|
7
|
-
import { n as ae, r as oe } from "../api-
|
|
7
|
+
import { n as ae, r as oe } from "../api-lPqwfSkW.js";
|
|
8
8
|
import { t as se } from "../id-CRqy981z.js";
|
|
9
9
|
import { attach as C, combine as w, createApi as T, createDomain as E, createEffect as D, createEvent as O, createStore as k, sample as A } from "effector";
|
|
10
10
|
import { createFactory as j, invoke as M } from "@withease/factories";
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
import "../../../logger-6z_gb4rA.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { d as e } from "../../../supports-0XIfd4-R.js";
|
|
3
|
+
import { n as t, r as n, t as r } from "../../../storage-6Xk0uAxB.js";
|
|
4
|
+
import { createEffect as i, sample as a } from "effector";
|
|
4
5
|
//#region src/plugins/experience-browser-storage/plugin.ts
|
|
5
|
-
var
|
|
6
|
+
var o = ({ storage: o = "local" } = {}) => ({
|
|
6
7
|
name: "experience-browser-storage-plugin",
|
|
7
|
-
setup: (
|
|
8
|
-
|
|
8
|
+
setup: (s) => {
|
|
9
|
+
if (!e()) return;
|
|
10
|
+
let c = o === "local" ? t : n, l = i(async (e) => {
|
|
9
11
|
if (!e) return;
|
|
10
|
-
let t =
|
|
11
|
-
e.setOpen(
|
|
12
|
+
let t = r("open-close", "experience", e), n = c.get(t).boolean();
|
|
13
|
+
e.setOpen(n ?? e.isOpenStore.getState());
|
|
12
14
|
});
|
|
13
|
-
|
|
14
|
-
source:
|
|
15
|
+
a({
|
|
16
|
+
source: s.eventBus,
|
|
15
17
|
filter: (e) => e.type === "experience_initialize" && "experience" in e,
|
|
16
18
|
fn: (e) => e.experience,
|
|
17
|
-
target:
|
|
19
|
+
target: l
|
|
18
20
|
});
|
|
19
|
-
let
|
|
20
|
-
let t =
|
|
21
|
-
|
|
22
|
-
}),
|
|
21
|
+
let u = i(async (e) => {
|
|
22
|
+
let t = r("open-close", "experience", e), n = e.isOpenStore.getState();
|
|
23
|
+
c.set(t, n);
|
|
24
|
+
}), d = (e) => a({
|
|
23
25
|
source: e.isOpenStore,
|
|
24
26
|
fn: () => e,
|
|
25
|
-
target:
|
|
27
|
+
target: u
|
|
26
28
|
});
|
|
27
|
-
|
|
28
|
-
e.forEach((e) =>
|
|
29
|
+
s.experienceInstances.listStore.watch((e) => {
|
|
30
|
+
e.forEach((e) => d(e));
|
|
29
31
|
});
|
|
30
32
|
}
|
|
31
33
|
});
|
|
32
34
|
//#endregion
|
|
33
|
-
export {
|
|
35
|
+
export { o as experienceBrowserStoragePlugin };
|
|
@@ -1,24 +1,26 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { d as e } from "../../../supports-0XIfd4-R.js";
|
|
2
|
+
import { n as t, r as n, t as r } from "../../../storage-6Xk0uAxB.js";
|
|
3
|
+
import { createEffect as i, sample as a } from "effector";
|
|
3
4
|
//#region src/plugins/history-browser-storage/plugin.ts
|
|
4
|
-
var
|
|
5
|
+
var o = r("history", "global"), s = ({ storage: r = "local" } = {}) => ({
|
|
5
6
|
name: "history-browser-storage-plugin",
|
|
6
|
-
setup: (
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
e
|
|
10
|
-
|
|
11
|
-
|
|
7
|
+
setup: (s) => {
|
|
8
|
+
if (!e()) return;
|
|
9
|
+
let c = r === "local" ? t : n, l = i(async () => {
|
|
10
|
+
let e = c.get(o).json();
|
|
11
|
+
e && s.history.restore({ entries: e });
|
|
12
|
+
}), u = i(async (e) => {
|
|
13
|
+
c.set(o, e);
|
|
12
14
|
});
|
|
13
|
-
|
|
14
|
-
source:
|
|
15
|
+
a({
|
|
16
|
+
source: s.eventBus,
|
|
15
17
|
filter: (e) => e.type === "load",
|
|
16
|
-
target: c
|
|
17
|
-
}), i({
|
|
18
|
-
source: o.history.entriesStore,
|
|
19
18
|
target: l
|
|
19
|
+
}), a({
|
|
20
|
+
source: s.history.entriesStore,
|
|
21
|
+
target: u
|
|
20
22
|
});
|
|
21
23
|
}
|
|
22
24
|
});
|
|
23
25
|
//#endregion
|
|
24
|
-
export {
|
|
26
|
+
export { s as historyBrowserStoragePlugin };
|
|
@@ -1,35 +1,37 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { d as e } from "../../../supports-0XIfd4-R.js";
|
|
2
|
+
import { n as t, r as n, t as r } from "../../../storage-6Xk0uAxB.js";
|
|
3
|
+
import { attach as i, createEffect as a, sample as o } from "effector";
|
|
3
4
|
//#region src/plugins/responses-browser-storage/plugin.ts
|
|
4
|
-
var
|
|
5
|
-
function
|
|
5
|
+
var s = r("responses", "global");
|
|
6
|
+
function c(e, t) {
|
|
6
7
|
return Object.fromEntries(Object.entries(e).flatMap(([e, n]) => t.find((t) => t.key === e)?.policies?.doNotStoreValue ? [] : [[e, n]]));
|
|
7
8
|
}
|
|
8
|
-
var
|
|
9
|
+
var l = ({ storage: r = "local" } = {}) => ({
|
|
9
10
|
name: "responses-browser-storage-plugin",
|
|
10
|
-
setup: (
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
setup: (l) => {
|
|
12
|
+
if (!e()) return;
|
|
13
|
+
let u = r === "local" ? t : n, d = a(async () => {
|
|
14
|
+
let e = u.get(s).json();
|
|
13
15
|
if (!e) return null;
|
|
14
|
-
|
|
15
|
-
}),
|
|
16
|
+
l.responses.register(e);
|
|
17
|
+
}), f = i({
|
|
16
18
|
source: {
|
|
17
|
-
responses:
|
|
18
|
-
fields:
|
|
19
|
+
responses: l.responses.getAllStore,
|
|
20
|
+
fields: l.fields.listStore
|
|
19
21
|
},
|
|
20
22
|
effect: ({ responses: e, fields: t }) => {
|
|
21
|
-
|
|
23
|
+
u.set(s, c(e, t));
|
|
22
24
|
}
|
|
23
25
|
});
|
|
24
|
-
|
|
25
|
-
source:
|
|
26
|
+
o({
|
|
27
|
+
source: l.eventBus,
|
|
26
28
|
filter: (e) => e.type === "load",
|
|
27
|
-
target: u
|
|
28
|
-
}), a({
|
|
29
|
-
source: c.responses.getAllStore,
|
|
30
29
|
target: d
|
|
30
|
+
}), o({
|
|
31
|
+
source: l.responses.getAllStore,
|
|
32
|
+
target: f
|
|
31
33
|
});
|
|
32
34
|
}
|
|
33
35
|
});
|
|
34
36
|
//#endregion
|
|
35
|
-
export {
|
|
37
|
+
export { l as responsesBrowserStoragePlugin };
|
|
@@ -1,35 +1,36 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { d as e } from "../../../supports-0XIfd4-R.js";
|
|
2
|
+
import { createEffect as t, sample as n } from "effector";
|
|
2
3
|
//#region src/utils/indexed-db.ts
|
|
3
|
-
var
|
|
4
|
-
async function
|
|
5
|
-
let { storeName: t, version:
|
|
4
|
+
var r = "picked";
|
|
5
|
+
async function i(e) {
|
|
6
|
+
let { storeName: t, version: n = 1, keyPath: i = "id" } = e;
|
|
6
7
|
return new Promise((e, a) => {
|
|
7
|
-
let o = indexedDB.open(
|
|
8
|
+
let o = indexedDB.open(r, n);
|
|
8
9
|
o.onupgradeneeded = () => {
|
|
9
10
|
let e = o.result;
|
|
10
11
|
e.objectStoreNames.contains(t) || e.createObjectStore(t, i ? { keyPath: i } : void 0);
|
|
11
12
|
}, o.onsuccess = () => e(o.result), o.onerror = () => a(Error(o.error?.message));
|
|
12
13
|
});
|
|
13
14
|
}
|
|
14
|
-
async function
|
|
15
|
+
async function a(e, t) {
|
|
15
16
|
return new Promise((n, r) => {
|
|
16
17
|
let i = e.transaction(t, "readonly").objectStore(t).getAll();
|
|
17
18
|
i.onsuccess = () => n(i.result), i.onerror = () => r(Error(i.error?.message));
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
|
-
async function
|
|
21
|
+
async function o(e, t, n) {
|
|
21
22
|
return new Promise((r, i) => {
|
|
22
23
|
let a = e.transaction(t, "readwrite").objectStore(t).put(n);
|
|
23
24
|
a.onsuccess = () => r(), a.onerror = () => i(Error(a.error?.message));
|
|
24
25
|
});
|
|
25
26
|
}
|
|
26
|
-
async function
|
|
27
|
+
async function s(e, t, n) {
|
|
27
28
|
return new Promise((r, i) => {
|
|
28
29
|
let a = e.transaction(t, "readwrite").objectStore(t).delete(n);
|
|
29
30
|
a.onsuccess = () => r(), a.onerror = () => i(Error(a.error?.message));
|
|
30
31
|
});
|
|
31
32
|
}
|
|
32
|
-
async function
|
|
33
|
+
async function c(e, t) {
|
|
33
34
|
return new Promise((n, r) => {
|
|
34
35
|
let i = e.transaction(t, "readwrite").objectStore(t).clear();
|
|
35
36
|
i.onsuccess = () => n(), i.onerror = () => r(Error(i.error?.message));
|
|
@@ -37,34 +38,35 @@ async function s(e, t) {
|
|
|
37
38
|
}
|
|
38
39
|
//#endregion
|
|
39
40
|
//#region src/plugins/responses-indexeddb/plugin.ts
|
|
40
|
-
var
|
|
41
|
-
storeName:
|
|
41
|
+
var l = "responses", u = {
|
|
42
|
+
storeName: l,
|
|
42
43
|
keyPath: "fieldKey",
|
|
43
44
|
version: 2
|
|
44
|
-
},
|
|
45
|
+
}, d = () => ({
|
|
45
46
|
name: "responses-indexdb-plugin",
|
|
46
|
-
setup: (
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
await
|
|
50
|
-
|
|
47
|
+
setup: (r) => {
|
|
48
|
+
if (!e()) return;
|
|
49
|
+
let d = t(async () => {
|
|
50
|
+
let e = (await a(await i(u), l)).reduce((e, { fieldKey: t, value: n }) => (e[t] = n, e), {});
|
|
51
|
+
await r.responses.commit(e);
|
|
52
|
+
}), f = t(async (e) => {
|
|
51
53
|
await Promise.allSettled(e.map(async (e) => {
|
|
52
|
-
await
|
|
54
|
+
await o(await i(u), l, e);
|
|
53
55
|
}));
|
|
54
|
-
}),
|
|
55
|
-
await
|
|
56
|
-
}),
|
|
57
|
-
await
|
|
56
|
+
}), p = t(async () => {
|
|
57
|
+
await c(await i(u), l);
|
|
58
|
+
}), m = t(async (e) => {
|
|
59
|
+
await s(await i(u), l, e);
|
|
58
60
|
});
|
|
59
|
-
|
|
60
|
-
source:
|
|
61
|
+
n({
|
|
62
|
+
source: r.eventBus,
|
|
61
63
|
filter: (e) => e.type === "load",
|
|
62
|
-
target:
|
|
63
|
-
}),
|
|
64
|
-
clock:
|
|
64
|
+
target: d
|
|
65
|
+
}), n({
|
|
66
|
+
clock: r.responses.listStore,
|
|
65
67
|
source: {
|
|
66
|
-
responses:
|
|
67
|
-
fields:
|
|
68
|
+
responses: r.responses.listStore,
|
|
69
|
+
fields: r.fields.listStore
|
|
68
70
|
},
|
|
69
71
|
fn: ({ responses: e, fields: t }) => {
|
|
70
72
|
let n = new Set(t.filter((e) => !e.policies?.doNotStoreValue).map((e) => e.key));
|
|
@@ -73,15 +75,15 @@ var c = "responses", l = {
|
|
|
73
75
|
value: e.value
|
|
74
76
|
}));
|
|
75
77
|
},
|
|
76
|
-
target: d
|
|
77
|
-
}), t({
|
|
78
|
-
clock: n.responses.clearAll,
|
|
79
78
|
target: f
|
|
80
|
-
}),
|
|
81
|
-
clock:
|
|
79
|
+
}), n({
|
|
80
|
+
clock: r.responses.clearAll,
|
|
82
81
|
target: p
|
|
82
|
+
}), n({
|
|
83
|
+
clock: r.responses.clear,
|
|
84
|
+
target: m
|
|
83
85
|
});
|
|
84
86
|
}
|
|
85
87
|
});
|
|
86
88
|
//#endregion
|
|
87
|
-
export {
|
|
89
|
+
export { d as responsesIndexedDBPlugin };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/experience-browser-storage/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/experience-browser-storage/plugin.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAM1D,UAAU,8BAA8B;IACtC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC9B;AAED,eAAO,MAAM,8BAA8B,GAAI,eAE5C,8BAAmC,KAAG,MA+CxC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/history-browser-storage/plugin.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/history-browser-storage/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAM1D,cAAc;AACd,MAAM,WAAW,2BAA2B;IAC1C,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC9B;AAED,eAAO,MAAM,2BAA2B,GAAI,eAEzC,2BAAgC,KAAG,MA8BrC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/responses-browser-storage/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/responses-browser-storage/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAM1D,UAAU,6BAA6B;IACrC,OAAO,CAAC,EAAE,OAAO,GAAG,SAAS,CAAA;CAC9B;AAiCD,eAAO,MAAM,6BAA6B,GAAI,eAE3C,6BAAkC,KAAG,MAuCvC,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/responses-indexeddb/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../../src/plugins/responses-indexeddb/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,8BAA8B,CAAA;AAO1D,eAAO,MAAM,wBAAwB,QAAO,MAuE3C,CAAA"}
|