@picked/vite-plugin-picked 0.0.1-dev.1778561735098 → 0.0.1-dev.1780446300503

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/cjs/index.js CHANGED
@@ -1,15 +1,15 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`@picked/client/plugins/dynamic-experiences`);function t(e,t){return t||=e.slice(0),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}var n;function r(e){let r=JSON.stringify(e),o=e.length>0?e.map(e=>`
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require(`node:fs`),t=require(`@picked/client/plugins/dynamic-experiences`),n=require(`node:fs/promises`),r=require(`node:path`);function i(e,t){return t||=e.slice(0),Object.freeze(Object.defineProperties(e,{raw:{value:Object.freeze(t)}}))}var a;function o(e){let t=JSON.stringify(e),n=e.length>0?e.map(e=>`
2
2
  <div class="feature-row">
3
3
  <label>
4
- <input type="checkbox" data-feature-id="${a(e.id)}" checked>
5
- <span class="feature-name">${i(e.name)}</span>
6
- <span class="feature-id">${i(e.id)}</span>
4
+ <input type="checkbox" data-feature-id="${c(e.id)}" checked>
5
+ <span class="feature-name">${s(e.name)}</span>
6
+ <span class="feature-id">${s(e.id)}</span>
7
7
  </label>
8
- <select data-value-for="${a(e.id)}">
8
+ <select data-value-for="${c(e.id)}">
9
9
  <option value="true" selected>true</option>
10
10
  <option value="false">false</option>
11
11
  </select>
12
- </div>`).join(``):`<p class="text no-features">No features configured for this project.</p>`;return String.raw(n||=t([`<!DOCTYPE html>
12
+ </div>`).join(``):`<p class="text no-features">No features configured for this project.</p>`;return String.raw(a||=i([`<!DOCTYPE html>
13
13
  <html lang="en">
14
14
  <head>
15
15
  <meta charset="UTF-8">
@@ -855,7 +855,52 @@ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require
855
855
  <\/script>
856
856
  </main>
857
857
  </body>
858
- </html>`]),o,r)}function i(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`).replaceAll(`"`,`&quot;`)}function a(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`"`,`&quot;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`)}var o=`picked`,s=`\x1B[0m`;function c(){let e=process.env.NO_COLOR;return e===`1`||e===`true`}function l(e){return t=>c()?t:`${e}${t}${s}`}var u=[`debug`,`info`,`warn`,`error`,`silent`],d={debug:{color:l(`\x1B[2m`),log:console.debug,enabled:!1},info:{color:l(`\x1B[36m`),log:console.log,enabled:!0},warn:{color:l(`\x1B[33m`),log:console.warn,enabled:!0},error:{color:l(`\x1B[31m`),log:console.error,enabled:!0},silent:{log:()=>{},enabled:!1}};function f(e,t){return`${new Date().toLocaleTimeString()} [${t?`${o}:${t}`:o}] ${e}`}function p(e,t,n,r){if(!e.enabled)return;let i=f(t,r);e.log(e.color?e.color(i):i),n!==void 0&&e.log(n)}function m(e){let t=d[e],n=new Set;return Object.assign((e,n,r)=>{p(t,e,n,r)},{once(e,r,i){!t.enabled||n.has(e)||(n.add(e),p(t,e,r,i))}})}function h(e){let t=u.indexOf(e);if(t===-1){p(d.error,`invalid log level: ${e}`);return}for(let[e,n]of u.entries())d[n].enabled=e>=t}var g=l(`\x1B[34m`),_=l(`\x1B[36m`);function v(){let e=[` ${g(`_`)} ${_(`_`)} _ _ _`,`${g(`(_)`)} ${_(`(_)`)} _ __(_)__| |_____ __| |`,` ${g(`_`)} ${g(`_`)} | '_ \\ / _| / / -_) _\` |`,`${g(`(_)`)} ${g(`(_)`)} | .__/_\\__|_\\_\\___\\__,_|`,` |_|
858
+ </html>`]),n,t)}function s(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`).replaceAll(`"`,`&quot;`)}function c(e){return e.replaceAll(`&`,`&amp;`).replaceAll(`"`,`&quot;`).replaceAll(`<`,`&lt;`).replaceAll(`>`,`&gt;`)}var l=`picked`,u=`\x1B[0m`;function d(){let e=process.env.NO_COLOR;return e===`1`||e===`true`}function f(e){return t=>d()?t:`${e}${t}${u}`}var p=[`debug`,`info`,`warn`,`error`,`silent`],m={debug:{color:f(`\x1B[2m`),log:console.debug,enabled:!1},info:{color:f(`\x1B[36m`),log:console.log,enabled:!0},warn:{color:f(`\x1B[33m`),log:console.warn,enabled:!0},error:{color:f(`\x1B[31m`),log:console.error,enabled:!0},silent:{log:()=>{},enabled:!1}};function h(e,t){return`${new Date().toLocaleTimeString()} [${t?`${l}:${t}`:l}] ${e}`}function g(e,t,n,r){if(!e.enabled)return;let i=h(t,r);e.log(e.color?e.color(i):i),n!==void 0&&e.log(n)}function _(e){let t=m[e],n=new Set;return Object.assign((e,n,r)=>{g(t,e,n,r)},{once(e,r,i){!t.enabled||n.has(e)||(n.add(e),g(t,e,r,i))}})}function ee(e){let t=p.indexOf(e);if(t===-1){g(m.error,`invalid log level: ${e}`);return}for(let[e,n]of p.entries())m[n].enabled=e>=t}var v=f(`\x1B[34m`),y=f(`\x1B[36m`);function b(){let e=[` ${v(`_`)} ${y(`_`)} _ _ _`,`${v(`(_)`)} ${y(`(_)`)} _ __(_)__| |_____ __| |`,` ${v(`_`)} ${v(`_`)} | '_ \\ / _| / / -_) _\` |`,`${v(`(_)`)} ${v(`(_)`)} | .__/_\\__|_\\_\\___\\__,_|`,` |_|
859
859
  `];console.log(e.join(`
860
- `))}var y={banner:v,debug:m(`debug`),info:m(`info`),warn:m(`warn`),error:m(`error`),setLevel:h};function b(e){return(t,n,r)=>{if(t.method!==`GET`){r();return}let i=(t.url??``).split(`?`)[0];if(i!==`/`&&i!==`/index.html`){r();return}let a=e();n.setHeader(`Content-Type`,`text/html`),n.statusCode=200,n.end(a)}}var x=5;function S(e){return Array.isArray(e)?e:[]}function C(e,t,n){if(t.length===0)return;if(t.length>x){console.log(`\n ${e}: ${t.length.toString()} loaded\n`);return}let r=n.map(e=>Math.max(e.header.length,...t.map(t=>e.value(t).length))),i=(e,t)=>e.padEnd(r[t]??e.length),a=` ${n.map((e,t)=>i(e.header,t)).join(` `)}`,o=` ${r.map(e=>`─`.repeat(e)).join(` `)}`,s=t.map(e=>` ${n.map((t,n)=>i(t.value(e),n)).join(` `)}`);console.log([``,` ${e}:`,``,a,o,...s,``].join(`
861
- `))}function w(e,t){t.length!==0&&console.log(`\n ${e}: ${t.length.toString()} loaded\n`)}var T=[{header:`Name`,value:e=>e.name},{header:`ID`,value:e=>e.id},{header:`Status`,value:e=>e.enabled?`\x1B[32m enabled\x1B[0m`:`\x1B[2mdisabled\x1B[0m`},{header:`Weight`,value:e=>`${e.weight.toString()}%`.padStart(6)},{header:`Ver`,value:e=>`v${e.version.toString()}`}],E=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Component`,value:e=>e.componentName},{header:`Nodes`,value:e=>e.nodes.length.toString()},{header:`Targets`,value:e=>e.targets.length.toString()}],D=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Fields`,value:e=>(e.fieldIds?.length??0).toString()}],O=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Type`,value:e=>e.type},{header:`Required`,value:e=>e.required?`yes`:`no`}],k=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key}];function A(e){try{let t=new URL(e).hostname;return t===`localhost`||t===`127.0.0.1`||t===`::1`}catch{return!1}}function j(e){return new Promise(t=>{setTimeout(t,e)})}async function M(t,n,r){let i=await(0,e.fetchProject)(t,n,{headers:r});if(i||!A(t))return i;for(let i=1;i<=I;i++){y.info(`local API not ready, retrying in ${(L/1e3).toString()}s (${i.toString()}/${I.toString()})`),await j(L);let a=await(0,e.fetchProject)(t,n,{headers:r});if(a)return a}return null}function N(){return`0.0.1-dev.1778561735098`}var P=`https://api.picked.app`,F=`__PICKED_PROJECT_CONFIG__`,I=5,L=2e3;function R(t){let n=t.apiUrl??P,i=[],a,o=!1;return{name:`picked`,config:async()=>{if(o)return a;o=!0,y.banner(),y.info(`fetching project config for "${t.projectId}"`),n!==P&&y.warn(`using custom API URL: ${n}`);let r=(0,e.createClientHeaders)(`vite-plugin`,N()),s=performance.now(),c=await M(n,t.projectId,r),l=(performance.now()-s).toFixed(0);if(!c)return y.warn(`failed to fetch project config for "${t.projectId}" (${l}ms)`),a=void 0,a;i=S(c.features);let u=S(c.experiences),d=S(c.steps),f=S(c.fields),p=S(c.conditions),m=S(c.actions),h=JSON.stringify(c).length,g=[`${i.length.toString()} features`,`${u.length.toString()} experiences`,`${d.length.toString()} steps`,`${f.length.toString()} fields`,`${p.length.toString()} conditions`,`${m.length.toString()} actions`].join(`, `);return y.info(`fetched config for "${t.projectId}" in ${l}ms (${g})`),C(`Features`,i,T),C(`Experiences`,u,E),C(`Steps`,d,D),C(`Fields`,f,O),C(`Conditions`,p,k),w(`Actions`,m),y.debug(`config size: ${h.toString()} bytes`),a={define:{[F]:JSON.stringify(c)}},a},configureServer(e){if(t.disableLauncher)return;e.middlewares.use(b(()=>r(i)));let n=e.printUrls.bind(e);e.printUrls=()=>{let t=e.resolvedUrls?.local[0];if(t){let e=t.replace(/:(?<port>\d+)\//,(e,t)=>`:\x1b[1m${t}\x1b[22m/`);console.log(` \x1b[32m➜\x1b[0m \x1b[1mPicked\x1b[22m: \x1b[36m${e}\x1b[0m`)}n()}},generateBundle(){t.disableLauncher||this.emitFile({type:`asset`,fileName:`index.html`,source:r(i)})}}}exports.picked=R;
860
+ `))}var x={banner:b,debug:_(`debug`),info:_(`info`),warn:_(`warn`),error:_(`error`),setLevel:ee};function te(e){return(t,n,r)=>{if(t.method!==`GET`){r();return}let i=(t.url??``).split(`?`)[0];if(i!==`/`&&i!==`/index.html`){r();return}let a=e();n.setHeader(`Content-Type`,`text/html`),n.statusCode=200,n.end(a)}}var S=`.picked/`;async function C(t,i){if(i)return;let a=(0,r.join)(t,`.gitignore`);if(!(0,e.existsSync)(a)){await(0,n.writeFile)(a,`${S}
861
+ `);return}let o=await(0,n.readFile)(a,`utf8`);w(o)||await(0,n.appendFile)(a,`${o.length>0&&!o.endsWith(`
862
+ `)?`
863
+ `:``}${S}
864
+ `)}function w(e){return e.split(/\r?\n/).map(e=>e.trim()).some(e=>e===S||e===`.picked`)}var T=6,E=5e3;function D(e){let t;try{t=new URL(e).hostname}catch{return!1}return t===`localhost`||t===`127.0.0.1`||t===`::1`||t===`[::1]`?!0:t===`api.picked.app`?!1:t.endsWith(`.picked.app`)}function ne(e){return new Promise(t=>{setTimeout(t,e)})}async function O(e,t){let n=null,r=null;try{n=await e()}catch(e){r=e}if(n!==null)return n;for(let i=1;i<=T;i++){let a=E*2**(i-1);t(i,T,a),await ne(a),r=null;try{n=await e()}catch(e){r=e}if(n!==null)return n}if(r!==null)throw r;return null}function k(e){return Array.isArray(e)?e:[]}var re=1;function A(e){let t=(0,r.join)(e,`.picked`);return{pickedDir:t,manifestPath:(0,r.join)(t,`manifest.json`),projectConfigPath:(0,r.join)(t,`project-config.json`),experiencesDir:(0,r.join)(t,`experiences`)}}async function j(t){if(!(0,e.existsSync)(t))return null;try{let e=await(0,n.readFile)(t,`utf8`),r=JSON.parse(e);return M(r)?r:null}catch{return null}}function M(e){return!(typeof e!=`object`||!e||!(`version`in e)||typeof e.version!=`number`||!(`projectVersionId`in e)||typeof e.projectVersionId!=`string`||!(`workspaceId`in e)||typeof e.workspaceId!=`string`||!(`experiences`in e)||!Array.isArray(e.experiences))}function N(e){let t=new Set;for(let n of k(e.steps))n.layoutContentId&&t.add(n.id);if(t.size===0)return[];let n=[];for(let r of k(e.experiences))k(r.nodes).some(e=>e.type===`step`&&t.has(e.stepId))&&n.push(r.id);return n}async function P(e){let{apiUrl:t,projectId:i,projectRoot:a,headers:o,reuseWhenUnchanged:s=!0}=e,c=A(a),l=await I(t,i,o),u=N(l);if(s){let e=await j(c.manifestPath);if(e&&e.projectVersionId===l.versionId&&e.workspaceId===l.workspaceId)return{config:l,manifest:e,experienceContentPaths:F(c,u)}}await(0,n.mkdir)(c.experiencesDir,{recursive:!0}),await(0,n.writeFile)(c.projectConfigPath,JSON.stringify(l,null,2));let d=new Date().toISOString(),f=[];for(let e of u){let a=await L(t,i,e,o);await(0,n.writeFile)((0,r.join)(c.experiencesDir,`${e}.json`),JSON.stringify(a,null,2)),f.push({id:e,fetchedAt:d})}let p={version:re,fetchedAt:d,projectVersionId:l.versionId,workspaceId:l.workspaceId,projectId:l.id,experiences:f};return await(0,n.writeFile)(c.manifestPath,JSON.stringify(p,null,2)),{config:l,manifest:p,experienceContentPaths:F(c,u)}}function F(e,t){let n={};for(let i of t)n[i]=(0,r.join)(e.experiencesDir,`${i}.json`);return n}async function I(e,n,r){let i=()=>(0,t.fetchProject)(e,n,{headers:r}),a;try{a=D(e)?await O(i,(e,t,n)=>{x.info(`project config not ready, retrying in ${(n/1e3).toString()}s (${e.toString()}/${t.toString()})`)}):await i()}catch(t){throw Error(`[picked] Build-time fetch of project config failed for "${n}" at ${e}: ${R(t)}`)}if(!a)throw Error(`[picked] Build-time fetch of project config returned no usable response for "${n}" at ${e}. Confirm the API is reachable and the project ID is correct.`);return a}async function L(e,n,r,i){let a;try{a=await(0,t.fetchExperienceContent)(e,n,r,{headers:i})}catch(t){throw Error(`[picked] Build-time fetch of content for experience "${r}" failed at ${e}: ${R(t)}`)}if(!a)throw Error(`[picked] Build-time fetch of content for experience "${r}" returned no usable response. Confirm the experience id exists and the API is reachable.`);return a}function R(e){if(e instanceof Error)return e.message;if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return`unknown error`}}function z(e){let{projectConfigPath:t,experienceContentPaths:n,manifestMetadata:r}=e,i=e=>JSON.stringify(e),a=Object.entries(n).map(([e,t])=>` ${JSON.stringify(e)}: () => import(${i(t)}).then((m) => m.default ?? m),`).join(`
865
+ `);return`import projectConfigJson from ${i(t)}
866
+
867
+ const experienceImporters = {
868
+ ${a}
869
+ }
870
+
871
+ export const projectConfig = projectConfigJson
872
+
873
+ export async function experienceContent(experience, _options) {
874
+ const id = typeof experience === 'string' ? experience : experience?.id
875
+ if (!id) return null
876
+ const importer = experienceImporters[id]
877
+ if (!importer) return null
878
+ try {
879
+ return await importer()
880
+ } catch {
881
+ return null
882
+ }
883
+ }
884
+
885
+ export const manifestMetadata = ${JSON.stringify(r)}
886
+ `}function B(e){let{apiUrl:t,projectId:n}=e;return`import { fetchProject, fetchExperienceContent } from '@picked/client/plugins/dynamic-experiences'
887
+
888
+ const apiUrl = ${JSON.stringify(t)}
889
+ const projectId = ${JSON.stringify(n)}
890
+
891
+ export const projectConfig = await fetchProject(apiUrl, projectId)
892
+
893
+ export async function experienceContent(experience, options) {
894
+ const id = typeof experience === 'string' ? experience : experience?.id
895
+ if (!id) return null
896
+ return await fetchExperienceContent(apiUrl, projectId, id, options)
897
+ }
898
+
899
+ export const manifestMetadata = {
900
+ mode: 'dev',
901
+ fetchedAt: null,
902
+ projectVersionId: projectConfig?.versionId ?? null,
903
+ workspaceId: projectConfig?.workspaceId ?? null,
904
+ }
905
+ `}var V=5;function H(e,t,n){if(t.length===0)return;if(t.length>V){console.log(`\n ${e}: ${t.length.toString()} loaded\n`);return}let r=n.map(e=>Math.max(e.header.length,...t.map(t=>e.value(t).length))),i=(e,t)=>e.padEnd(r[t]??e.length),a=` ${n.map((e,t)=>i(e.header,t)).join(` `)}`,o=` ${r.map(e=>`─`.repeat(e)).join(` `)}`,s=t.map(e=>` ${n.map((t,n)=>i(t.value(e),n)).join(` `)}`);console.log([``,` ${e}:`,``,a,o,...s,``].join(`
906
+ `))}function U(e,t){t.length!==0&&console.log(`\n ${e}: ${t.length.toString()} loaded\n`)}var W=[{header:`Name`,value:e=>e.name},{header:`ID`,value:e=>e.id},{header:`Status`,value:e=>e.enabled?`\x1B[32m enabled\x1B[0m`:`\x1B[2mdisabled\x1B[0m`},{header:`Weight`,value:e=>`${e.weight.toString()}%`.padStart(6)},{header:`Ver`,value:e=>`v${e.version.toString()}`}],G=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Component`,value:e=>e.componentName},{header:`Nodes`,value:e=>e.nodes.length.toString()},{header:`Targets`,value:e=>e.targets.length.toString()}],K=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Fields`,value:e=>(e.fieldIds?.length??0).toString()}],q=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Type`,value:e=>e.type},{header:`Required`,value:e=>e.required?`yes`:`no`}],J=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key}];function Y(){return`0.0.1-dev.1780446300503`}var X=`https://api.picked.app`,Z=`virtual:picked/data`,Q=`\0${Z}`;function ie(n){let r=n.apiUrl??X,i=[],a=process.cwd(),s=`serve`,c,l=!1;return{name:`picked`,config:async(o,u)=>{s=u.command,a=process.cwd(),x.banner(),r!==X&&x.warn(`using custom API URL: ${r}`);let d=(0,t.createClientHeaders)(`vite-plugin`,Y());if(u.command===`build`){if(l)return;l=!0,x.info(`prefetching project config + content for "${n.projectId}"`);let t=A(a),o=(0,e.existsSync)(t.pickedDir),s=performance.now(),u=await P({apiUrl:r,projectId:n.projectId,projectRoot:a,headers:d}),f=(performance.now()-s).toFixed(0);await C(a,o);let p=u.config;i=k(p.features),$(p,f),c=z({projectConfigPath:t.projectConfigPath,experienceContentPaths:u.experienceContentPaths,manifestMetadata:{mode:`build`,fetchedAt:u.manifest.fetchedAt,projectVersionId:u.manifest.projectVersionId,workspaceId:u.manifest.workspaceId}})}else{x.info(`fetching project config for "${n.projectId}" (dev)`);let e=performance.now(),a=()=>(0,t.fetchProject)(r,n.projectId,{headers:d}),o=D(r)?await O(a,(e,t,n)=>{x.info(`local API not ready, retrying in ${(n/1e3).toString()}s (${e.toString()}/${t.toString()})`)}):await a(),s=(performance.now()-e).toFixed(0);o?(i=k(o.features),$(o,s)):x.warn(`failed to fetch project config for "${n.projectId}" (${s}ms)`)}},resolveId(e){if(e===Z)return Q},load(e){if(e===Q)return s===`build`?c:B({apiUrl:r,projectId:n.projectId})},configureServer(e){if(n.disableLauncher)return;e.middlewares.use(te(()=>o(i)));let t=e.printUrls.bind(e);e.printUrls=()=>{let n=e.resolvedUrls?.local[0];if(n){let e=n.replace(/:(?<port>\d+)\//,(e,t)=>`:\x1b[1m${t}\x1b[22m/`);console.log(` \x1b[32m➜\x1b[0m \x1b[1mPicked\x1b[22m: \x1b[36m${e}\x1b[0m`)}t()}},generateBundle(){n.disableLauncher||this.emitFile({type:`asset`,fileName:`index.html`,source:o(i)})}}}function $(e,t){let n=k(e.features),r=k(e.experiences),i=k(e.steps),a=k(e.fields),o=k(e.conditions),s=k(e.actions),c=JSON.stringify(e).length,l=[`${n.length.toString()} features`,`${r.length.toString()} experiences`,`${i.length.toString()} steps`,`${a.length.toString()} fields`,`${o.length.toString()} conditions`,`${s.length.toString()} actions`].join(`, `);x.info(`fetched config in ${t}ms (${l})`),H(`Features`,n,W),H(`Experiences`,r,G),H(`Steps`,i,K),H(`Fields`,a,q),H(`Conditions`,o,J),U(`Actions`,s),x.debug(`config size: ${c.toString()} bytes`)}exports.picked=ie;
package/dist/esm/index.js CHANGED
@@ -1,25 +1,28 @@
1
- import { createClientHeaders as e, fetchProject as t } from "@picked/client/plugins/dynamic-experiences";
1
+ import { existsSync as e } from "node:fs";
2
+ import { createClientHeaders as t, fetchExperienceContent as n, fetchProject as r } from "@picked/client/plugins/dynamic-experiences";
3
+ import { appendFile as i, mkdir as a, readFile as o, writeFile as s } from "node:fs/promises";
4
+ import { join as c } from "node:path";
2
5
  //#region \0@oxc-project+runtime@0.124.0/helpers/taggedTemplateLiteral.js
3
- function n(e, t) {
6
+ function l(e, t) {
4
7
  return t ||= e.slice(0), Object.freeze(Object.defineProperties(e, { raw: { value: Object.freeze(t) } }));
5
8
  }
6
9
  //#endregion
7
10
  //#region src/launcher.ts
8
- var r;
9
- function i(e) {
10
- let t = JSON.stringify(e), i = e.length > 0 ? e.map((e) => `
11
+ var u;
12
+ function d(e) {
13
+ let t = JSON.stringify(e), n = e.length > 0 ? e.map((e) => `
11
14
  <div class="feature-row">
12
15
  <label>
13
- <input type="checkbox" data-feature-id="${o(e.id)}" checked>
14
- <span class="feature-name">${a(e.name)}</span>
15
- <span class="feature-id">${a(e.id)}</span>
16
+ <input type="checkbox" data-feature-id="${p(e.id)}" checked>
17
+ <span class="feature-name">${f(e.name)}</span>
18
+ <span class="feature-id">${f(e.id)}</span>
16
19
  </label>
17
- <select data-value-for="${o(e.id)}">
20
+ <select data-value-for="${p(e.id)}">
18
21
  <option value="true" selected>true</option>
19
22
  <option value="false">false</option>
20
23
  </select>
21
24
  </div>`).join("") : "<p class=\"text no-features\">No features configured for this project.</p>";
22
- return String.raw(r ||= n([
25
+ return String.raw(u ||= l([
23
26
  "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"robots\" content=\"noindex, nofollow\">\n <title>Picked</title>\n <style>\n main {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n display: grid;\n align-items: center;\n justify-content: center;\n font-family: monospace;\n color: #4A4A4A\n }\n\n .wrap {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n max-width: 480px;\n width: 100%;\n }\n\n .title {\n font-size: 1.125rem;\n font-weight: 700;\n margin-bottom: 0;\n }\n\n .text {\n margin-top: .5rem;\n font-size: .875rem\n }\n\n .text a {\n color: #4A4A4A;\n text-decoration: none;\n }\n\n .text a:hover {\n cursor: pointer;\n text-decoration: underline\n }\n\n .preview-form {\n margin-top: 2rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n }\n\n .preview-input {\n padding: 0.5rem;\n border: 1px solid #4A4A4A;\n border-radius: 0;\n font-family: monospace;\n }\n\n .preview-button {\n padding: 0.5rem;\n background: #4A4A4A;\n color: white;\n border: none;\n border-radius: 0;\n cursor: pointer;\n font-family: monospace;\n }\n\n .preview-button:hover {\n opacity: 0.9;\n }\n\n .buttons {\n display: grid;\n gap: 0.5em;\n grid-template-columns: 1fr 1fr;\n }\n\n #copy-status {\n font-size: 1em;\n text-align: right;\n margin-top: 0.5em;\n }\n\n .features-section {\n margin-top: 1.5rem;\n }\n\n .features-label {\n font-weight: 700;\n font-size: .875rem;\n margin-bottom: 0.5rem;\n }\n\n .feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n padding: 0.25rem 0;\n }\n\n .feature-row label {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n cursor: pointer;\n min-width: 0;\n flex: 1;\n }\n\n .feature-name {\n font-size: .875rem;\n }\n\n .feature-id {\n font-size: .75rem;\n color: #888;\n }\n\n .feature-row select {\n font-family: monospace;\n font-size: .75rem;\n padding: 0.125rem 0.25rem;\n border: 1px solid #4A4A4A;\n border-radius: 0;\n background: white;\n flex-shrink: 0;\n }\n\n .no-features {\n color: #888;\n font-style: italic;\n }\n\n .url-history {\n margin-top: 0.5rem;\n font-size: .75rem;\n }\n\n .url-history-item {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.125rem 0;\n }\n\n .url-history-item button {\n background: none;\n border: none;\n cursor: pointer;\n font-family: monospace;\n font-size: .75rem;\n color: #888;\n padding: 0;\n }\n\n .url-history-item button.url-link {\n color: #4A4A4A;\n text-decoration: none;\n text-align: left;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n min-width: 0;\n }\n\n .url-history-item button.url-link:hover {\n text-decoration: underline;\n }\n\n .url-history-item button.url-delete:hover {\n color: #b71c1c;\n }\n\n @media (min-width: 640px) {\n .title {\n font-size: 1.25rem\n }\n\n .text {\n font-size: 1rem\n }\n }\n </style>\n</head>\n<body>\n\n<main>\n <div class=\"wrap\">\n <p class=\"title\">Picked</p>\n <p class=\"text\">\n Learn more at\n <a href=\"https://picked.app\">\n https://picked.app\n </a>\n </p>\n <div class=\"preview-form\">\n <input type=\"text\" id=\"url-input\" pattern=\"^(https?://)?.+\" placeholder=\"Enter preview URL\" required\n class=\"preview-input\">\n <div id=\"url-history\" class=\"url-history\"></div>\n <div class=\"features-section\">\n <p class=\"features-label\">Features</p>\n ",
24
27
  "\n </div>\n <div class=\"buttons\">\n <button type=\"button\" class=\"preview-button\" id=\"copy-btn\">Copy</button>\n <button type=\"button\" class=\"preview-button\" id=\"preview-btn\">Preview</button>\n </div>\n <p class=\"text\" id=\"copy-status\" aria-live=\"polite\" style=\"visibility:hidden;\">\n &nbsp;\n </p>\n </div>\n </div>\n\n <script>\n var FEATURES = ",
25
28
  ";\n var STORAGE_KEY_URL = 'picked-launcher-url';\n var STORAGE_KEY_HISTORY = 'picked-launcher-history';\n var STORAGE_KEY_SELECTIONS = 'picked-launcher-selections';\n\n function getUrlInput() {\n return document.getElementById('url-input');\n }\n\n // URL history\n function getHistory() {\n try {\n var raw = localStorage.getItem(STORAGE_KEY_HISTORY);\n if (!raw) return [];\n var parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch (e) {\n return [];\n }\n }\n\n function saveHistory(history) {\n localStorage.setItem(STORAGE_KEY_HISTORY, JSON.stringify(history));\n }\n\n function addToHistory(url) {\n var history = getHistory().filter(function(h) { return h !== url; });\n history.unshift(url);\n if (history.length > 10) history = history.slice(0, 10);\n saveHistory(history);\n renderHistory();\n }\n\n function removeFromHistory(url) {\n var history = getHistory().filter(function(h) { return h !== url; });\n saveHistory(history);\n renderHistory();\n }\n\n function renderHistory() {\n var container = document.getElementById('url-history');\n if (!container) return;\n var history = getHistory();\n if (history.length === 0) {\n container.innerHTML = '';\n return;\n }\n container.innerHTML = history.map(function(url) {\n return '<div class=\"url-history-item\">' +\n '<button type=\"button\" class=\"url-link\" data-history-url=\"' + escapeAttr(url) + '\">' + escapeHtml(url) + '</button>' +\n '<button type=\"button\" class=\"url-delete\" data-history-delete=\"' + escapeAttr(url) + '\">x</button>' +\n '</div>';\n }).join('');\n }\n\n // Feature selection persistence\n function getSelections() {\n try {\n var raw = localStorage.getItem(STORAGE_KEY_SELECTIONS);\n if (!raw) return {};\n var parsed = JSON.parse(raw);\n return typeof parsed === 'object' && parsed !== null ? parsed : {};\n } catch (e) {\n return {};\n }\n }\n\n function saveSelections() {\n var selections = {};\n FEATURES.forEach(function(f) {\n var checkbox = document.querySelector('input[data-feature-id=\"' + f.id + '\"]');\n var select = document.querySelector('select[data-value-for=\"' + f.id + '\"]');\n if (checkbox && select) {\n selections[f.id] = { checked: checkbox.checked, value: select.value };\n }\n });\n localStorage.setItem(STORAGE_KEY_SELECTIONS, JSON.stringify(selections));\n }\n\n function restoreSelections() {\n var selections = getSelections();\n FEATURES.forEach(function(f) {\n var saved = selections[f.id];\n if (!saved) return;\n var checkbox = document.querySelector('input[data-feature-id=\"' + f.id + '\"]');\n var select = document.querySelector('select[data-value-for=\"' + f.id + '\"]');\n if (checkbox) checkbox.checked = saved.checked;\n if (select) select.value = saved.value;\n });\n }\n\n function escapeHtml(str) {\n var div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n function escapeAttr(str) {\n return str.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n }\n\n function buildTargetUrl(inputValue) {\n var value = inputValue;\n if (!value.startsWith('http://') && !value.startsWith('https://')) {\n value = 'https://' + value;\n }\n var hostname = window.location.hostname;\n var subdomain = hostname.split('.').length > 2 ? hostname.split('.')[0] : '';\n var target = value;\n var sep = target.includes('?') ? '&' : '?';\n if (subdomain) {\n target = target + sep + 'cvly_env=' + encodeURIComponent(subdomain);\n sep = '&';\n }\n FEATURES.forEach(function(f) {\n var checkbox = document.querySelector('input[data-feature-id=\"' + f.id + '\"]');\n var select = document.querySelector('select[data-value-for=\"' + f.id + '\"]');\n if (checkbox && checkbox.checked && select) {\n target = target + sep + 'picked-feature=' + encodeURIComponent(f.id + ':' + select.value);\n sep = '&';\n }\n });\n return target;\n }\n\n function showCopyStatus(message, success) {\n var el = document.getElementById('copy-status');\n if (!el) return;\n el.textContent = message;\n el.style.visibility = 'visible';\n el.style.color = success ? '#2e7d32' : '#b71c1c';\n setTimeout(function() {\n el.style.visibility = 'hidden';\n }, 2000);\n }\n\n // Init\n window.addEventListener('load', function() {\n var input = getUrlInput();\n var savedUrl = localStorage.getItem(STORAGE_KEY_URL);\n if (savedUrl && input) {\n input.value = savedUrl;\n }\n renderHistory();\n restoreSelections();\n });\n\n // Delegate click events\n document.addEventListener('click', function(e) {\n var target = e.target;\n\n // History URL click\n var historyUrl = target.getAttribute('data-history-url');\n if (historyUrl) {\n var input = getUrlInput();\n if (input) input.value = historyUrl;\n return;\n }\n\n // History delete click\n var deleteUrl = target.getAttribute('data-history-delete');\n if (deleteUrl) {\n removeFromHistory(deleteUrl);\n return;\n }\n });\n\n // Feature change persistence\n document.addEventListener('change', function(e) {\n var target = e.target;\n if (target.getAttribute('data-feature-id') || target.getAttribute('data-value-for')) {\n saveSelections();\n }\n });\n\n // Copy button\n document.getElementById('copy-btn').addEventListener('click', function() {\n var input = getUrlInput();\n if (!input || !input.value) return;\n localStorage.setItem(STORAGE_KEY_URL, input.value);\n addToHistory(input.value);\n saveSelections();\n var target = buildTargetUrl(input.value);\n navigator.clipboard.writeText(target).then(function() {\n showCopyStatus('Preview URL copied to clipboard', true);\n });\n });\n\n // Preview button\n document.getElementById('preview-btn').addEventListener('click', function() {\n var input = getUrlInput();\n if (!input || !input.value) return;\n localStorage.setItem(STORAGE_KEY_URL, input.value);\n addToHistory(input.value);\n saveSelections();\n var target = buildTargetUrl(input.value);\n window.location.href = target;\n });\n <\/script>\n</main>\n</body>\n</html>"
@@ -27,48 +30,48 @@ function i(e) {
27
30
  "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n <meta charset=\"UTF-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n <meta name=\"robots\" content=\"noindex, nofollow\">\n <title>Picked</title>\n <style>\n main {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n left: 0;\n display: grid;\n align-items: center;\n justify-content: center;\n font-family: monospace;\n color: #4A4A4A\n }\n\n .wrap {\n padding-left: 1.5rem;\n padding-right: 1.5rem;\n max-width: 480px;\n width: 100%;\n }\n\n .title {\n font-size: 1.125rem;\n font-weight: 700;\n margin-bottom: 0;\n }\n\n .text {\n margin-top: .5rem;\n font-size: .875rem\n }\n\n .text a {\n color: #4A4A4A;\n text-decoration: none;\n }\n\n .text a:hover {\n cursor: pointer;\n text-decoration: underline\n }\n\n .preview-form {\n margin-top: 2rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n }\n\n .preview-input {\n padding: 0.5rem;\n border: 1px solid #4A4A4A;\n border-radius: 0;\n font-family: monospace;\n }\n\n .preview-button {\n padding: 0.5rem;\n background: #4A4A4A;\n color: white;\n border: none;\n border-radius: 0;\n cursor: pointer;\n font-family: monospace;\n }\n\n .preview-button:hover {\n opacity: 0.9;\n }\n\n .buttons {\n display: grid;\n gap: 0.5em;\n grid-template-columns: 1fr 1fr;\n }\n\n #copy-status {\n font-size: 1em;\n text-align: right;\n margin-top: 0.5em;\n }\n\n .features-section {\n margin-top: 1.5rem;\n }\n\n .features-label {\n font-weight: 700;\n font-size: .875rem;\n margin-bottom: 0.5rem;\n }\n\n .feature-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 0.5rem;\n padding: 0.25rem 0;\n }\n\n .feature-row label {\n display: flex;\n align-items: center;\n gap: 0.5rem;\n cursor: pointer;\n min-width: 0;\n flex: 1;\n }\n\n .feature-name {\n font-size: .875rem;\n }\n\n .feature-id {\n font-size: .75rem;\n color: #888;\n }\n\n .feature-row select {\n font-family: monospace;\n font-size: .75rem;\n padding: 0.125rem 0.25rem;\n border: 1px solid #4A4A4A;\n border-radius: 0;\n background: white;\n flex-shrink: 0;\n }\n\n .no-features {\n color: #888;\n font-style: italic;\n }\n\n .url-history {\n margin-top: 0.5rem;\n font-size: .75rem;\n }\n\n .url-history-item {\n display: flex;\n align-items: center;\n gap: 0.25rem;\n padding: 0.125rem 0;\n }\n\n .url-history-item button {\n background: none;\n border: none;\n cursor: pointer;\n font-family: monospace;\n font-size: .75rem;\n color: #888;\n padding: 0;\n }\n\n .url-history-item button.url-link {\n color: #4A4A4A;\n text-decoration: none;\n text-align: left;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n min-width: 0;\n }\n\n .url-history-item button.url-link:hover {\n text-decoration: underline;\n }\n\n .url-history-item button.url-delete:hover {\n color: #b71c1c;\n }\n\n @media (min-width: 640px) {\n .title {\n font-size: 1.25rem\n }\n\n .text {\n font-size: 1rem\n }\n }\n </style>\n</head>\n<body>\n\n<main>\n <div class=\"wrap\">\n <p class=\"title\">Picked</p>\n <p class=\"text\">\n Learn more at\n <a href=\"https://picked.app\">\n https://picked.app\n </a>\n </p>\n <div class=\"preview-form\">\n <input type=\"text\" id=\"url-input\" pattern=\"^(https?:\\/\\/)?.+\" placeholder=\"Enter preview URL\" required\n class=\"preview-input\">\n <div id=\"url-history\" class=\"url-history\"></div>\n <div class=\"features-section\">\n <p class=\"features-label\">Features</p>\n ",
28
31
  "\n </div>\n <div class=\"buttons\">\n <button type=\"button\" class=\"preview-button\" id=\"copy-btn\">Copy</button>\n <button type=\"button\" class=\"preview-button\" id=\"preview-btn\">Preview</button>\n </div>\n <p class=\"text\" id=\"copy-status\" aria-live=\"polite\" style=\"visibility:hidden;\">\n &nbsp;\n </p>\n </div>\n </div>\n\n <script>\n var FEATURES = ",
29
32
  ";\n var STORAGE_KEY_URL = 'picked-launcher-url';\n var STORAGE_KEY_HISTORY = 'picked-launcher-history';\n var STORAGE_KEY_SELECTIONS = 'picked-launcher-selections';\n\n function getUrlInput() {\n return document.getElementById('url-input');\n }\n\n // URL history\n function getHistory() {\n try {\n var raw = localStorage.getItem(STORAGE_KEY_HISTORY);\n if (!raw) return [];\n var parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch (e) {\n return [];\n }\n }\n\n function saveHistory(history) {\n localStorage.setItem(STORAGE_KEY_HISTORY, JSON.stringify(history));\n }\n\n function addToHistory(url) {\n var history = getHistory().filter(function(h) { return h !== url; });\n history.unshift(url);\n if (history.length > 10) history = history.slice(0, 10);\n saveHistory(history);\n renderHistory();\n }\n\n function removeFromHistory(url) {\n var history = getHistory().filter(function(h) { return h !== url; });\n saveHistory(history);\n renderHistory();\n }\n\n function renderHistory() {\n var container = document.getElementById('url-history');\n if (!container) return;\n var history = getHistory();\n if (history.length === 0) {\n container.innerHTML = '';\n return;\n }\n container.innerHTML = history.map(function(url) {\n return '<div class=\"url-history-item\">' +\n '<button type=\"button\" class=\"url-link\" data-history-url=\"' + escapeAttr(url) + '\">' + escapeHtml(url) + '</button>' +\n '<button type=\"button\" class=\"url-delete\" data-history-delete=\"' + escapeAttr(url) + '\">x</button>' +\n '</div>';\n }).join('');\n }\n\n // Feature selection persistence\n function getSelections() {\n try {\n var raw = localStorage.getItem(STORAGE_KEY_SELECTIONS);\n if (!raw) return {};\n var parsed = JSON.parse(raw);\n return typeof parsed === 'object' && parsed !== null ? parsed : {};\n } catch (e) {\n return {};\n }\n }\n\n function saveSelections() {\n var selections = {};\n FEATURES.forEach(function(f) {\n var checkbox = document.querySelector('input[data-feature-id=\"' + f.id + '\"]');\n var select = document.querySelector('select[data-value-for=\"' + f.id + '\"]');\n if (checkbox && select) {\n selections[f.id] = { checked: checkbox.checked, value: select.value };\n }\n });\n localStorage.setItem(STORAGE_KEY_SELECTIONS, JSON.stringify(selections));\n }\n\n function restoreSelections() {\n var selections = getSelections();\n FEATURES.forEach(function(f) {\n var saved = selections[f.id];\n if (!saved) return;\n var checkbox = document.querySelector('input[data-feature-id=\"' + f.id + '\"]');\n var select = document.querySelector('select[data-value-for=\"' + f.id + '\"]');\n if (checkbox) checkbox.checked = saved.checked;\n if (select) select.value = saved.value;\n });\n }\n\n function escapeHtml(str) {\n var div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n function escapeAttr(str) {\n return str.replace(/&/g, '&amp;').replace(/\"/g, '&quot;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n }\n\n function buildTargetUrl(inputValue) {\n var value = inputValue;\n if (!value.startsWith('http://') && !value.startsWith('https://')) {\n value = 'https://' + value;\n }\n var hostname = window.location.hostname;\n var subdomain = hostname.split('.').length > 2 ? hostname.split('.')[0] : '';\n var target = value;\n var sep = target.includes('?') ? '&' : '?';\n if (subdomain) {\n target = target + sep + 'cvly_env=' + encodeURIComponent(subdomain);\n sep = '&';\n }\n FEATURES.forEach(function(f) {\n var checkbox = document.querySelector('input[data-feature-id=\"' + f.id + '\"]');\n var select = document.querySelector('select[data-value-for=\"' + f.id + '\"]');\n if (checkbox && checkbox.checked && select) {\n target = target + sep + 'picked-feature=' + encodeURIComponent(f.id + ':' + select.value);\n sep = '&';\n }\n });\n return target;\n }\n\n function showCopyStatus(message, success) {\n var el = document.getElementById('copy-status');\n if (!el) return;\n el.textContent = message;\n el.style.visibility = 'visible';\n el.style.color = success ? '#2e7d32' : '#b71c1c';\n setTimeout(function() {\n el.style.visibility = 'hidden';\n }, 2000);\n }\n\n // Init\n window.addEventListener('load', function() {\n var input = getUrlInput();\n var savedUrl = localStorage.getItem(STORAGE_KEY_URL);\n if (savedUrl && input) {\n input.value = savedUrl;\n }\n renderHistory();\n restoreSelections();\n });\n\n // Delegate click events\n document.addEventListener('click', function(e) {\n var target = e.target;\n\n // History URL click\n var historyUrl = target.getAttribute('data-history-url');\n if (historyUrl) {\n var input = getUrlInput();\n if (input) input.value = historyUrl;\n return;\n }\n\n // History delete click\n var deleteUrl = target.getAttribute('data-history-delete');\n if (deleteUrl) {\n removeFromHistory(deleteUrl);\n return;\n }\n });\n\n // Feature change persistence\n document.addEventListener('change', function(e) {\n var target = e.target;\n if (target.getAttribute('data-feature-id') || target.getAttribute('data-value-for')) {\n saveSelections();\n }\n });\n\n // Copy button\n document.getElementById('copy-btn').addEventListener('click', function() {\n var input = getUrlInput();\n if (!input || !input.value) return;\n localStorage.setItem(STORAGE_KEY_URL, input.value);\n addToHistory(input.value);\n saveSelections();\n var target = buildTargetUrl(input.value);\n navigator.clipboard.writeText(target).then(function() {\n showCopyStatus('Preview URL copied to clipboard', true);\n });\n });\n\n // Preview button\n document.getElementById('preview-btn').addEventListener('click', function() {\n var input = getUrlInput();\n if (!input || !input.value) return;\n localStorage.setItem(STORAGE_KEY_URL, input.value);\n addToHistory(input.value);\n saveSelections();\n var target = buildTargetUrl(input.value);\n window.location.href = target;\n });\n <\/script>\n</main>\n</body>\n</html>"
30
- ]), i, t);
33
+ ]), n, t);
31
34
  }
32
- function a(e) {
35
+ function f(e) {
33
36
  return e.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
34
37
  }
35
- function o(e) {
38
+ function p(e) {
36
39
  return e.replaceAll("&", "&amp;").replaceAll("\"", "&quot;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
37
40
  }
38
41
  //#endregion
39
42
  //#region src/log.ts
40
- var s = "picked", c = "\x1B[0m";
41
- function l() {
43
+ var m = "picked", ee = "\x1B[0m";
44
+ function te() {
42
45
  let e = process.env.NO_COLOR;
43
46
  return e === "1" || e === "true";
44
47
  }
45
- function u(e) {
46
- return (t) => l() ? t : `${e}${t}${c}`;
48
+ function h(e) {
49
+ return (t) => te() ? t : `${e}${t}${ee}`;
47
50
  }
48
- var d = [
51
+ var g = [
49
52
  "debug",
50
53
  "info",
51
54
  "warn",
52
55
  "error",
53
56
  "silent"
54
- ], f = {
57
+ ], _ = {
55
58
  debug: {
56
- color: u("\x1B[2m"),
59
+ color: h("\x1B[2m"),
57
60
  log: console.debug,
58
61
  enabled: !1
59
62
  },
60
63
  info: {
61
- color: u("\x1B[36m"),
64
+ color: h("\x1B[36m"),
62
65
  log: console.log,
63
66
  enabled: !0
64
67
  },
65
68
  warn: {
66
- color: u("\x1B[33m"),
69
+ color: h("\x1B[33m"),
67
70
  log: console.warn,
68
71
  enabled: !0
69
72
  },
70
73
  error: {
71
- color: u("\x1B[31m"),
74
+ color: h("\x1B[31m"),
72
75
  log: console.error,
73
76
  enabled: !0
74
77
  },
@@ -77,52 +80,52 @@ var d = [
77
80
  enabled: !1
78
81
  }
79
82
  };
80
- function p(e, t) {
81
- return `${(/* @__PURE__ */ new Date()).toLocaleTimeString()} [${t ? `${s}:${t}` : s}] ${e}`;
83
+ function v(e, t) {
84
+ return `${(/* @__PURE__ */ new Date()).toLocaleTimeString()} [${t ? `${m}:${t}` : m}] ${e}`;
82
85
  }
83
- function m(e, t, n, r) {
86
+ function y(e, t, n, r) {
84
87
  if (!e.enabled) return;
85
- let i = p(t, r);
88
+ let i = v(t, r);
86
89
  e.log(e.color ? e.color(i) : i), n !== void 0 && e.log(n);
87
90
  }
88
- function h(e) {
89
- let t = f[e], n = /* @__PURE__ */ new Set();
91
+ function b(e) {
92
+ let t = _[e], n = /* @__PURE__ */ new Set();
90
93
  return Object.assign((e, n, r) => {
91
- m(t, e, n, r);
94
+ y(t, e, n, r);
92
95
  }, { once(e, r, i) {
93
- !t.enabled || n.has(e) || (n.add(e), m(t, e, r, i));
96
+ !t.enabled || n.has(e) || (n.add(e), y(t, e, r, i));
94
97
  } });
95
98
  }
96
- function g(e) {
97
- let t = d.indexOf(e);
99
+ function x(e) {
100
+ let t = g.indexOf(e);
98
101
  if (t === -1) {
99
- m(f.error, `invalid log level: ${e}`);
102
+ y(_.error, `invalid log level: ${e}`);
100
103
  return;
101
104
  }
102
- for (let [e, n] of d.entries()) f[n].enabled = e >= t;
105
+ for (let [e, n] of g.entries()) _[n].enabled = e >= t;
103
106
  }
104
- var _ = u("\x1B[34m"), v = u("\x1B[36m");
105
- function y() {
107
+ var S = h("\x1B[34m"), C = h("\x1B[36m");
108
+ function w() {
106
109
  let e = [
107
- ` ${_("_")} ${v("_")} _ _ _`,
108
- `${_("(_)")} ${v("(_)")} _ __(_)__| |_____ __| |`,
109
- ` ${_("_")} ${_("_")} | '_ \\ / _| / / -_) _\` |`,
110
- `${_("(_)")} ${_("(_)")} | .__/_\\__|_\\_\\___\\__,_|`,
110
+ ` ${S("_")} ${C("_")} _ _ _`,
111
+ `${S("(_)")} ${C("(_)")} _ __(_)__| |_____ __| |`,
112
+ ` ${S("_")} ${S("_")} | '_ \\ / _| / / -_) _\` |`,
113
+ `${S("(_)")} ${S("(_)")} | .__/_\\__|_\\_\\___\\__,_|`,
111
114
  " |_|\n "
112
115
  ];
113
116
  console.log(e.join("\n"));
114
117
  }
115
- var b = {
116
- banner: y,
117
- debug: h("debug"),
118
- info: h("info"),
119
- warn: h("warn"),
120
- error: h("error"),
121
- setLevel: g
118
+ var T = {
119
+ banner: w,
120
+ debug: b("debug"),
121
+ info: b("info"),
122
+ warn: b("warn"),
123
+ error: b("error"),
124
+ setLevel: x
122
125
  };
123
126
  //#endregion
124
127
  //#region src/middleware.ts
125
- function x(e) {
128
+ function E(e) {
126
129
  return (t, n, r) => {
127
130
  if (t.method !== "GET") {
128
131
  r();
@@ -138,14 +141,225 @@ function x(e) {
138
141
  };
139
142
  }
140
143
  //#endregion
141
- //#region src/index.ts
142
- var S = 5;
143
- function C(e) {
144
+ //#region src/gitignore.ts
145
+ var D = ".picked/";
146
+ async function ne(t, n) {
147
+ if (n) return;
148
+ let r = c(t, ".gitignore");
149
+ if (!e(r)) {
150
+ await s(r, `${D}
151
+ `);
152
+ return;
153
+ }
154
+ let a = await o(r, "utf8");
155
+ O(a) || await i(r, `${a.length > 0 && !a.endsWith("\n") ? "\n" : ""}${D}
156
+ `);
157
+ }
158
+ function O(e) {
159
+ return e.split(/\r?\n/).map((e) => e.trim()).some((e) => e === D || e === ".picked");
160
+ }
161
+ //#endregion
162
+ //#region src/retry.ts
163
+ var k = 6, A = 5e3;
164
+ function j(e) {
165
+ let t;
166
+ try {
167
+ t = new URL(e).hostname;
168
+ } catch {
169
+ return !1;
170
+ }
171
+ return t === "localhost" || t === "127.0.0.1" || t === "::1" || t === "[::1]" ? !0 : t === "api.picked.app" ? !1 : t.endsWith(".picked.app");
172
+ }
173
+ function re(e) {
174
+ return new Promise((t) => {
175
+ setTimeout(t, e);
176
+ });
177
+ }
178
+ async function M(e, t) {
179
+ let n = null, r = null;
180
+ try {
181
+ n = await e();
182
+ } catch (e) {
183
+ r = e;
184
+ }
185
+ if (n !== null) return n;
186
+ for (let i = 1; i <= k; i++) {
187
+ let a = A * 2 ** (i - 1);
188
+ t(i, k, a), await re(a), r = null;
189
+ try {
190
+ n = await e();
191
+ } catch (e) {
192
+ r = e;
193
+ }
194
+ if (n !== null) return n;
195
+ }
196
+ if (r !== null) throw r;
197
+ return null;
198
+ }
199
+ //#endregion
200
+ //#region src/util.ts
201
+ function N(e) {
144
202
  return Array.isArray(e) ? e : [];
145
203
  }
146
- function w(e, t, n) {
204
+ //#endregion
205
+ //#region src/prefetch.ts
206
+ var P = 1;
207
+ function F(e) {
208
+ let t = c(e, ".picked");
209
+ return {
210
+ pickedDir: t,
211
+ manifestPath: c(t, "manifest.json"),
212
+ projectConfigPath: c(t, "project-config.json"),
213
+ experiencesDir: c(t, "experiences")
214
+ };
215
+ }
216
+ async function I(t) {
217
+ if (!e(t)) return null;
218
+ try {
219
+ let e = await o(t, "utf8"), n = JSON.parse(e);
220
+ return L(n) ? n : null;
221
+ } catch {
222
+ return null;
223
+ }
224
+ }
225
+ function L(e) {
226
+ return !(typeof e != "object" || !e || !("version" in e) || typeof e.version != "number" || !("projectVersionId" in e) || typeof e.projectVersionId != "string" || !("workspaceId" in e) || typeof e.workspaceId != "string" || !("experiences" in e) || !Array.isArray(e.experiences));
227
+ }
228
+ function R(e) {
229
+ let t = /* @__PURE__ */ new Set();
230
+ for (let n of N(e.steps)) n.layoutContentId && t.add(n.id);
231
+ if (t.size === 0) return [];
232
+ let n = [];
233
+ for (let r of N(e.experiences)) N(r.nodes).some((e) => e.type === "step" && t.has(e.stepId)) && n.push(r.id);
234
+ return n;
235
+ }
236
+ async function z(e) {
237
+ let { apiUrl: t, projectId: n, projectRoot: r, headers: i, reuseWhenUnchanged: o = !0 } = e, l = F(r), u = await V(t, n, i), d = R(u);
238
+ if (o) {
239
+ let e = await I(l.manifestPath);
240
+ if (e && e.projectVersionId === u.versionId && e.workspaceId === u.workspaceId) return {
241
+ config: u,
242
+ manifest: e,
243
+ experienceContentPaths: B(l, d)
244
+ };
245
+ }
246
+ await a(l.experiencesDir, { recursive: !0 }), await s(l.projectConfigPath, JSON.stringify(u, null, 2));
247
+ let f = (/* @__PURE__ */ new Date()).toISOString(), p = [];
248
+ for (let e of d) {
249
+ let r = await H(t, n, e, i);
250
+ await s(c(l.experiencesDir, `${e}.json`), JSON.stringify(r, null, 2)), p.push({
251
+ id: e,
252
+ fetchedAt: f
253
+ });
254
+ }
255
+ let m = {
256
+ version: P,
257
+ fetchedAt: f,
258
+ projectVersionId: u.versionId,
259
+ workspaceId: u.workspaceId,
260
+ projectId: u.id,
261
+ experiences: p
262
+ };
263
+ return await s(l.manifestPath, JSON.stringify(m, null, 2)), {
264
+ config: u,
265
+ manifest: m,
266
+ experienceContentPaths: B(l, d)
267
+ };
268
+ }
269
+ function B(e, t) {
270
+ let n = {};
271
+ for (let r of t) n[r] = c(e.experiencesDir, `${r}.json`);
272
+ return n;
273
+ }
274
+ async function V(e, t, n) {
275
+ let i = () => r(e, t, { headers: n }), a;
276
+ try {
277
+ a = j(e) ? await M(i, (e, t, n) => {
278
+ T.info(`project config not ready, retrying in ${(n / 1e3).toString()}s (${e.toString()}/${t.toString()})`);
279
+ }) : await i();
280
+ } catch (n) {
281
+ throw Error(`[picked] Build-time fetch of project config failed for "${t}" at ${e}: ${U(n)}`);
282
+ }
283
+ if (!a) throw Error(`[picked] Build-time fetch of project config returned no usable response for "${t}" at ${e}. Confirm the API is reachable and the project ID is correct.`);
284
+ return a;
285
+ }
286
+ async function H(e, t, r, i) {
287
+ let a;
288
+ try {
289
+ a = await n(e, t, r, { headers: i });
290
+ } catch (t) {
291
+ throw Error(`[picked] Build-time fetch of content for experience "${r}" failed at ${e}: ${U(t)}`);
292
+ }
293
+ if (!a) throw Error(`[picked] Build-time fetch of content for experience "${r}" returned no usable response. Confirm the experience id exists and the API is reachable.`);
294
+ return a;
295
+ }
296
+ function U(e) {
297
+ if (e instanceof Error) return e.message;
298
+ if (typeof e == "string") return e;
299
+ try {
300
+ return JSON.stringify(e);
301
+ } catch {
302
+ return "unknown error";
303
+ }
304
+ }
305
+ //#endregion
306
+ //#region src/virtual-module/build.ts
307
+ function W(e) {
308
+ let { projectConfigPath: t, experienceContentPaths: n, manifestMetadata: r } = e, i = (e) => JSON.stringify(e), a = Object.entries(n).map(([e, t]) => ` ${JSON.stringify(e)}: () => import(${i(t)}).then((m) => m.default ?? m),`).join("\n");
309
+ return `import projectConfigJson from ${i(t)}
310
+
311
+ const experienceImporters = {
312
+ ${a}
313
+ }
314
+
315
+ export const projectConfig = projectConfigJson
316
+
317
+ export async function experienceContent(experience, _options) {
318
+ const id = typeof experience === 'string' ? experience : experience?.id
319
+ if (!id) return null
320
+ const importer = experienceImporters[id]
321
+ if (!importer) return null
322
+ try {
323
+ return await importer()
324
+ } catch {
325
+ return null
326
+ }
327
+ }
328
+
329
+ export const manifestMetadata = ${JSON.stringify(r)}
330
+ `;
331
+ }
332
+ //#endregion
333
+ //#region src/virtual-module/dev.ts
334
+ function G(e) {
335
+ let { apiUrl: t, projectId: n } = e;
336
+ return `import { fetchProject, fetchExperienceContent } from '@picked/client/plugins/dynamic-experiences'
337
+
338
+ const apiUrl = ${JSON.stringify(t)}
339
+ const projectId = ${JSON.stringify(n)}
340
+
341
+ export const projectConfig = await fetchProject(apiUrl, projectId)
342
+
343
+ export async function experienceContent(experience, options) {
344
+ const id = typeof experience === 'string' ? experience : experience?.id
345
+ if (!id) return null
346
+ return await fetchExperienceContent(apiUrl, projectId, id, options)
347
+ }
348
+
349
+ export const manifestMetadata = {
350
+ mode: 'dev',
351
+ fetchedAt: null,
352
+ projectVersionId: projectConfig?.versionId ?? null,
353
+ workspaceId: projectConfig?.workspaceId ?? null,
354
+ }
355
+ `;
356
+ }
357
+ //#endregion
358
+ //#region src/index.ts
359
+ var K = 5;
360
+ function q(e, t, n) {
147
361
  if (t.length === 0) return;
148
- if (t.length > S) {
362
+ if (t.length > K) {
149
363
  console.log(`\n ${e}: ${t.length.toString()} loaded\n`);
150
364
  return;
151
365
  }
@@ -160,10 +374,10 @@ function w(e, t, n) {
160
374
  ""
161
375
  ].join("\n"));
162
376
  }
163
- function T(e, t) {
377
+ function J(e, t) {
164
378
  t.length !== 0 && console.log(`\n ${e}: ${t.length.toString()} loaded\n`);
165
379
  }
166
- var E = [
380
+ var Y = [
167
381
  {
168
382
  header: "Name",
169
383
  value: (e) => e.name
@@ -184,7 +398,7 @@ var E = [
184
398
  header: "Ver",
185
399
  value: (e) => `v${e.version.toString()}`
186
400
  }
187
- ], D = [
401
+ ], ie = [
188
402
  {
189
403
  header: "Name",
190
404
  value: (e) => e.name
@@ -205,7 +419,7 @@ var E = [
205
419
  header: "Targets",
206
420
  value: (e) => e.targets.length.toString()
207
421
  }
208
- ], O = [
422
+ ], ae = [
209
423
  {
210
424
  header: "Name",
211
425
  value: (e) => e.name
@@ -218,7 +432,7 @@ var E = [
218
432
  header: "Fields",
219
433
  value: (e) => (e.fieldIds?.length ?? 0).toString()
220
434
  }
221
- ], k = [
435
+ ], oe = [
222
436
  {
223
437
  header: "Name",
224
438
  value: (e) => e.name
@@ -235,81 +449,94 @@ var E = [
235
449
  header: "Required",
236
450
  value: (e) => e.required ? "yes" : "no"
237
451
  }
238
- ], A = [{
452
+ ], se = [{
239
453
  header: "Name",
240
454
  value: (e) => e.name
241
455
  }, {
242
456
  header: "Key",
243
457
  value: (e) => e.key
244
458
  }];
245
- function j(e) {
246
- try {
247
- let t = new URL(e).hostname;
248
- return t === "localhost" || t === "127.0.0.1" || t === "::1";
249
- } catch {
250
- return !1;
251
- }
252
- }
253
- function M(e) {
254
- return new Promise((t) => {
255
- setTimeout(t, e);
256
- });
257
- }
258
- async function N(e, n, r) {
259
- let i = await t(e, n, { headers: r });
260
- if (i || !j(e)) return i;
261
- for (let i = 1; i <= L; i++) {
262
- b.info(`local API not ready, retrying in ${(R / 1e3).toString()}s (${i.toString()}/${L.toString()})`), await M(R);
263
- let a = await t(e, n, { headers: r });
264
- if (a) return a;
265
- }
266
- return null;
459
+ function ce() {
460
+ return "0.0.1-dev.1780446300503";
267
461
  }
268
- function P() {
269
- return "0.0.1-dev.1778561735098";
270
- }
271
- var F = "https://api.picked.app", I = "__PICKED_PROJECT_CONFIG__", L = 5, R = 2e3;
272
- function z(t) {
273
- let n = t.apiUrl ?? F, r = [], a, o = !1;
462
+ var X = "https://api.picked.app", Z = "virtual:picked/data", Q = `\0${Z}`;
463
+ function le(n) {
464
+ let i = n.apiUrl ?? X, a = [], o = process.cwd(), s = "serve", c, l = !1;
274
465
  return {
275
466
  name: "picked",
276
- config: async () => {
277
- if (o) return a;
278
- o = !0, b.banner(), b.info(`fetching project config for "${t.projectId}"`), n !== F && b.warn(`using custom API URL: ${n}`);
279
- let i = e("vite-plugin", P()), s = performance.now(), c = await N(n, t.projectId, i), l = (performance.now() - s).toFixed(0);
280
- if (!c) return b.warn(`failed to fetch project config for "${t.projectId}" (${l}ms)`), a = void 0, a;
281
- r = C(c.features);
282
- let u = C(c.experiences), d = C(c.steps), f = C(c.fields), p = C(c.conditions), m = C(c.actions), h = JSON.stringify(c).length, g = [
283
- `${r.length.toString()} features`,
284
- `${u.length.toString()} experiences`,
285
- `${d.length.toString()} steps`,
286
- `${f.length.toString()} fields`,
287
- `${p.length.toString()} conditions`,
288
- `${m.length.toString()} actions`
289
- ].join(", ");
290
- return b.info(`fetched config for "${t.projectId}" in ${l}ms (${g})`), w("Features", r, E), w("Experiences", u, D), w("Steps", d, O), w("Fields", f, k), w("Conditions", p, A), T("Actions", m), b.debug(`config size: ${h.toString()} bytes`), a = { define: { [I]: JSON.stringify(c) } }, a;
467
+ config: async (u, d) => {
468
+ s = d.command, o = process.cwd(), T.banner(), i !== X && T.warn(`using custom API URL: ${i}`);
469
+ let f = t("vite-plugin", ce());
470
+ if (d.command === "build") {
471
+ if (l) return;
472
+ l = !0, T.info(`prefetching project config + content for "${n.projectId}"`);
473
+ let t = F(o), r = e(t.pickedDir), s = performance.now(), u = await z({
474
+ apiUrl: i,
475
+ projectId: n.projectId,
476
+ projectRoot: o,
477
+ headers: f
478
+ }), d = (performance.now() - s).toFixed(0);
479
+ await ne(o, r);
480
+ let p = u.config;
481
+ a = N(p.features), $(p, d), c = W({
482
+ projectConfigPath: t.projectConfigPath,
483
+ experienceContentPaths: u.experienceContentPaths,
484
+ manifestMetadata: {
485
+ mode: "build",
486
+ fetchedAt: u.manifest.fetchedAt,
487
+ projectVersionId: u.manifest.projectVersionId,
488
+ workspaceId: u.manifest.workspaceId
489
+ }
490
+ });
491
+ } else {
492
+ T.info(`fetching project config for "${n.projectId}" (dev)`);
493
+ let e = performance.now(), t = () => r(i, n.projectId, { headers: f }), o = j(i) ? await M(t, (e, t, n) => {
494
+ T.info(`local API not ready, retrying in ${(n / 1e3).toString()}s (${e.toString()}/${t.toString()})`);
495
+ }) : await t(), s = (performance.now() - e).toFixed(0);
496
+ o ? (a = N(o.features), $(o, s)) : T.warn(`failed to fetch project config for "${n.projectId}" (${s}ms)`);
497
+ }
498
+ },
499
+ resolveId(e) {
500
+ if (e === Z) return Q;
501
+ },
502
+ load(e) {
503
+ if (e === Q) return s === "build" ? c : G({
504
+ apiUrl: i,
505
+ projectId: n.projectId
506
+ });
291
507
  },
292
508
  configureServer(e) {
293
- if (t.disableLauncher) return;
294
- e.middlewares.use(x(() => i(r)));
295
- let n = e.printUrls.bind(e);
509
+ if (n.disableLauncher) return;
510
+ e.middlewares.use(E(() => d(a)));
511
+ let t = e.printUrls.bind(e);
296
512
  e.printUrls = () => {
297
- let t = e.resolvedUrls?.local[0];
298
- if (t) {
299
- let e = t.replace(/:(?<port>\d+)\//, (e, t) => `:\x1b[1m${t}\x1b[22m/`);
513
+ let n = e.resolvedUrls?.local[0];
514
+ if (n) {
515
+ let e = n.replace(/:(?<port>\d+)\//, (e, t) => `:\x1b[1m${t}\x1b[22m/`);
300
516
  console.log(` \x1b[32m➜\x1b[0m \x1b[1mPicked\x1b[22m: \x1b[36m${e}\x1b[0m`);
301
517
  }
302
- n();
518
+ t();
303
519
  };
304
520
  },
305
521
  generateBundle() {
306
- t.disableLauncher || this.emitFile({
522
+ n.disableLauncher || this.emitFile({
307
523
  type: "asset",
308
524
  fileName: "index.html",
309
- source: i(r)
525
+ source: d(a)
310
526
  });
311
527
  }
312
528
  };
313
529
  }
530
+ function $(e, t) {
531
+ let n = N(e.features), r = N(e.experiences), i = N(e.steps), a = N(e.fields), o = N(e.conditions), s = N(e.actions), c = JSON.stringify(e).length, l = [
532
+ `${n.length.toString()} features`,
533
+ `${r.length.toString()} experiences`,
534
+ `${i.length.toString()} steps`,
535
+ `${a.length.toString()} fields`,
536
+ `${o.length.toString()} conditions`,
537
+ `${s.length.toString()} actions`
538
+ ].join(", ");
539
+ T.info(`fetched config in ${t}ms (${l})`), q("Features", n, Y), q("Experiences", r, ie), q("Steps", i, ae), q("Fields", a, oe), q("Conditions", o, se), J("Actions", s), T.debug(`config size: ${c.toString()} bytes`);
540
+ }
314
541
  //#endregion
315
- export { z as picked };
542
+ export { le as picked };
@@ -0,0 +1,16 @@
1
+ /**
2
+ * If `<root>/.picked/` did not exist before this build, append `.picked/` to the
3
+ * project's `.gitignore` (creating the file if absent and not already containing the
4
+ * line). On subsequent runs, leaves the gitignore alone.
5
+ *
6
+ * @param {string} projectRoot - Absolute path to the Vite project root.
7
+ * @param {boolean} pickedDirAlreadyExists - Whether `<root>/.picked/` existed before the prefetch
8
+ * ran. Pass the result of `existsSync(pickedDir)` evaluated BEFORE any writes.
9
+ * @returns {Promise<void>} Resolves once the gitignore has been updated (or skipped).
10
+ * @example
11
+ * ```typescript
12
+ * await appendGitignoreOnFirstRun(process.cwd(), existsSync(pickedDir))
13
+ * ```
14
+ */
15
+ export declare function appendGitignoreOnFirstRun(projectRoot: string, pickedDirAlreadyExists: boolean): Promise<void>;
16
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../src/gitignore.ts"],"names":[],"mappings":"AAYA;;;;;;;;;;;;;GAaG;AACH,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,EACnB,sBAAsB,EAAE,OAAO,GAC9B,OAAO,CAAC,IAAI,CAAC,CAqBf"}
@@ -8,11 +8,15 @@ export interface PickedPluginOptions {
8
8
  disableLauncher?: boolean;
9
9
  }
10
10
  /**
11
- * Vite plugin that fetches Picked project config at build time
12
- * and injects it via `define`, eliminating the runtime API request.
11
+ * Vite plugin that fetches Picked project config + per-experience CMS content at build time
12
+ * and exposes both via the `virtual:picked/data` virtual module.
13
13
  *
14
- * When a preview token is present at runtime, the SDK will still
15
- * fetch fresh config from the API, ignoring the build-time value.
14
+ * Build mode (`vite build`) prefetches every CMS-using experience, writes JSON artefacts
15
+ * to `<root>/.picked/`, and synthesises a virtual module that statically imports the
16
+ * project config (so it's bundled) and lazily imports each experience's content on demand.
17
+ *
18
+ * Dev mode (`vite serve`) writes nothing to disk; the synthesised virtual module performs
19
+ * runtime API fetches via top-level `await` for project config and per-call for content.
16
20
  *
17
21
  * @param {PickedPluginOptions} options - Plugin configuration.
18
22
  * @returns {Plugin} A Vite plugin instance.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAmNlC,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CAwG3D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AAsIlC,MAAM,WAAW,mBAAmB;IAClC,6BAA6B;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,MAAM,CAAC,OAAO,EAAE,mBAAmB,GAAG,MAAM,CA2I3D"}
@@ -0,0 +1,72 @@
1
+ import { ProjectConfig } from '@picked/client/plugins/dynamic-experiences';
2
+ interface ManifestExperienceEntry {
3
+ id: string;
4
+ fetchedAt: string;
5
+ }
6
+ interface PickedManifest {
7
+ version: number;
8
+ fetchedAt: string;
9
+ projectVersionId: string;
10
+ workspaceId: string;
11
+ projectId: string;
12
+ experiences: ManifestExperienceEntry[];
13
+ }
14
+ interface PrefetchPaths {
15
+ /** Absolute path to `<root>/.picked/`. */
16
+ pickedDir: string;
17
+ /** Absolute path to `<root>/.picked/manifest.json`. */
18
+ manifestPath: string;
19
+ /** Absolute path to `<root>/.picked/project-config.json`. */
20
+ projectConfigPath: string;
21
+ /** Absolute path to `<root>/.picked/experiences/`. */
22
+ experiencesDir: string;
23
+ }
24
+ interface PrefetchResult {
25
+ config: ProjectConfig;
26
+ manifest: PickedManifest;
27
+ /** Map of experience id → absolute path to its `<id>.json`. */
28
+ experienceContentPaths: Record<string, string>;
29
+ }
30
+ /**
31
+ * Resolves the artefact paths under `<root>/.picked/`.
32
+ *
33
+ * @param {string} projectRoot - Vite project root (typically `process.cwd()`).
34
+ * @returns {PrefetchPaths} Absolute paths under `.picked/`.
35
+ * @example
36
+ * ```typescript
37
+ * const paths = resolvePrefetchPaths(process.cwd())
38
+ * ```
39
+ */
40
+ export declare function resolvePrefetchPaths(projectRoot: string): PrefetchPaths;
41
+ interface RunPrefetchOptions {
42
+ apiUrl: string;
43
+ projectId: string;
44
+ projectRoot: string;
45
+ /** HTTP headers to forward with every fetch (e.g. `Picked-Client`, `Picked-Version`). */
46
+ headers: Record<string, string>;
47
+ /** When the existing manifest matches the API's versionId+workspaceId, skip per-experience fetches. */
48
+ reuseWhenUnchanged?: boolean;
49
+ }
50
+ /**
51
+ * Runs the full build-mode prefetch: fetches project config, walks CMS-using experiences,
52
+ * fetches each one's content, writes all artefacts under `<root>/.picked/`, and replaces
53
+ * `manifest.json` in full so stale entries cannot accumulate.
54
+ *
55
+ * Hard-fails on any API failure (non-2xx response or network error). Customers shipping
56
+ * with the Vite plugin must ship from a known-good snapshot or not at all.
57
+ *
58
+ * When `reuseWhenUnchanged` is true and the existing manifest's `versionId` + `workspaceId`
59
+ * match the freshly-fetched project config, per-experience fetches are skipped and the
60
+ * existing JSON files are left in place. The manifest is left unchanged.
61
+ *
62
+ * @param {RunPrefetchOptions} options - Prefetch configuration.
63
+ * @returns {Promise<PrefetchResult>} The fetched config, manifest, and content-path map.
64
+ * @throws {Error} When any API request fails or returns malformed data.
65
+ * @example
66
+ * ```typescript
67
+ * const result = await runPrefetch({ apiUrl, projectId, projectRoot: cwd, headers })
68
+ * ```
69
+ */
70
+ export declare function runPrefetch(options: RunPrefetchOptions): Promise<PrefetchResult>;
71
+ export {};
72
+ //# sourceMappingURL=prefetch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prefetch.d.ts","sourceRoot":"","sources":["../../src/prefetch.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,aAAa,EACd,MAAM,4CAA4C,CAAA;AAQnD,UAAU,uBAAuB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,UAAU,cAAc;IACtB,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,CAAA;IACjB,gBAAgB,EAAE,MAAM,CAAA;IACxB,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,uBAAuB,EAAE,CAAA;CACvC;AAED,UAAU,aAAa;IACrB,0CAA0C;IAC1C,SAAS,EAAE,MAAM,CAAA;IACjB,uDAAuD;IACvD,YAAY,EAAE,MAAM,CAAA;IACpB,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAA;IACzB,sDAAsD;IACtD,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,aAAa,CAAA;IACrB,QAAQ,EAAE,cAAc,CAAA;IACxB,+DAA+D;IAC/D,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/C;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,aAAa,CAQvE;AAsFD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,yFAAyF;IACzF,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC/B,uGAAuG;IACvG,kBAAkB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,cAAc,CAAC,CAiDtF"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * @file URL-driven retry helper for the build- and dev-mode project-config fetch.
3
+ *
4
+ * Retries are auto-enabled when the configured `apiUrl` points at a host where
5
+ * deploys can race ahead of the API: `localhost`/`127.0.0.1`/`::1` for local
6
+ * development, or any `*.picked.app` host EXCEPT the canonical production host
7
+ * `api.picked.app`. Production and external customer hosts continue to fail fast.
8
+ *
9
+ * The retry schedule is hardcoded — there is no public option. The behavior is
10
+ * an internal deploy-ordering workaround for the Picked monorepo and is not
11
+ * intended for tuning by external SDK consumers.
12
+ */
13
+ /**
14
+ * Whether build- and dev-mode project-config fetches against `apiUrl` should be
15
+ * retried with exponential backoff. True for hosts where the API may briefly
16
+ * be unreachable while a deploy is propagating; false for the canonical
17
+ * production host and every external host.
18
+ *
19
+ * @param {string} apiUrl - The configured API base URL.
20
+ * @returns {boolean} `true` when retries should be attempted, `false` otherwise.
21
+ * @example
22
+ * shouldRetryUrl('http://localhost:54321') // true
23
+ * shouldRetryUrl('https://picked-api-git-foo.picked.app') // true
24
+ * shouldRetryUrl('https://api.picked.app') // false
25
+ */
26
+ export declare function shouldRetryUrl(apiUrl: string): boolean;
27
+ /**
28
+ * Run `fetchFn` once. If it returns `null` OR throws, retry up to 6 times with
29
+ * exponential backoff (5s → 10s → 20s → 40s → 80s → 160s, ~315 s worst-case wall time).
30
+ *
31
+ * Both thrown errors AND `null` returns are eligible for retry — the SDK's
32
+ * `fetchProject` and `fetchExperienceContent` swallow network errors and return
33
+ * `null`, so retrying only on throws would do nothing useful for the typical
34
+ * "API not yet up" failure mode. If every attempt fails, the most recent thrown
35
+ * error is rethrown; if every attempt simply returned `null`, `null` is returned
36
+ * so the caller can produce its own diagnostic.
37
+ *
38
+ * `onRetry` is invoked once per retry, before the corresponding backoff sleep,
39
+ * with the 1-indexed attempt number, the total retry budget, and the upcoming
40
+ * delay in ms. The callback is the only place log lines are produced — callers
41
+ * own the phrasing and any contextual prefix.
42
+ *
43
+ * @template T - Type of the value resolved by the fetch operation.
44
+ * @param {() => Promise<T | null>} fetchFn - The async fetch operation to run.
45
+ * @param {(attempt: number, totalAttempts: number, nextDelayMs: number) => void} onRetry -
46
+ * Hook invoked before each backoff sleep.
47
+ * @returns {Promise<T | null>} The first non-null result, or `null` after all retries are exhausted.
48
+ * @throws {Error} The last thrown error when every attempt threw.
49
+ * @example
50
+ * const config = await withRetry(
51
+ * () => fetchProject(apiUrl, projectId, { headers }),
52
+ * (attempt, total, ms) => log.info(`retrying in ${ms}ms (${attempt}/${total})`),
53
+ * )
54
+ */
55
+ export declare function withRetry<T>(fetchFn: () => Promise<T | null>, onRetry: (attempt: number, totalAttempts: number, nextDelayMs: number) => void): Promise<T | null>;
56
+ //# sourceMappingURL=retry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retry.d.ts","sourceRoot":"","sources":["../../src/retry.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAsBtD;AAgBD;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAsB,SAAS,CAAC,CAAC,EAC/B,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,EAChC,OAAO,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,GAC7E,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CA+BnB"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * @file Shared utilities for the Picked Vite plugin. Currently exposes a single
3
+ * defensive helper for handling API entity arrays that may be omitted at runtime
4
+ * despite being typed as required.
5
+ */
6
+ /**
7
+ * Safely coerce a value to an array, returning an empty array for nullish inputs.
8
+ * Needed because the API response may omit entity arrays at runtime even though
9
+ * the TypeScript type marks them as required.
10
+ *
11
+ * @template T - The element type of the array.
12
+ * @param {ReadonlyArray<T> | null | undefined} value - The value to coerce.
13
+ * @returns {ReadonlyArray<T>} The original array, or an empty array if the input is nullish.
14
+ * @example
15
+ * toArray(undefined) // []
16
+ */
17
+ export declare function toArray<T>(value: ReadonlyArray<T> | null | undefined): ReadonlyArray<T>;
18
+ //# sourceMappingURL=util.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../../src/util.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAAC,CAAC,CAAC,CAEvF"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * @file Build-mode synthesis for `virtual:picked/data`.
3
+ *
4
+ * Generates a JavaScript module that statically imports the prefetched project config
5
+ * (so it's bundled into the customer's main chunk) and lazily imports each experience's
6
+ * content JSON on demand.
7
+ */
8
+ interface BuildVirtualModuleOptions {
9
+ /** Absolute path to `<root>/.picked/project-config.json`. */
10
+ projectConfigPath: string;
11
+ /** Map of experience id → absolute path to its content JSON file. */
12
+ experienceContentPaths: Record<string, string>;
13
+ /** Manifest metadata that the synthesised module re-exports as `manifestMetadata`. */
14
+ manifestMetadata: {
15
+ mode: 'build';
16
+ fetchedAt: string | null;
17
+ projectVersionId: string | null;
18
+ workspaceId: string | null;
19
+ };
20
+ }
21
+ /**
22
+ * Synthesises the source code for the build-mode `virtual:picked/data` module.
23
+ *
24
+ * @param {BuildVirtualModuleOptions} options - Build-mode synthesis inputs.
25
+ * @returns {string} JavaScript source as a string for Vite's `load` hook.
26
+ * @example
27
+ * ```typescript
28
+ * const src = buildVirtualModuleSource({ projectConfigPath, experienceContentPaths, manifestMetadata })
29
+ * ```
30
+ */
31
+ export declare function buildVirtualModuleSource(options: BuildVirtualModuleOptions): string;
32
+ export {};
33
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../../src/virtual-module/build.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,UAAU,yBAAyB;IACjC,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAA;IACzB,qEAAqE;IACrE,sBAAsB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC9C,sFAAsF;IACtF,gBAAgB,EAAE;QAChB,IAAI,EAAE,OAAO,CAAA;QACb,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;QACxB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAA;QAC/B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;KAC3B,CAAA;CACF;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,yBAAyB,GAAG,MAAM,CAiCnF"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @file Dev-mode synthesis for `virtual:picked/data`.
3
+ *
4
+ * Generates a JavaScript module that fetches the project config at module-evaluation time
5
+ * via top-level `await`, and resolves per-experience content with per-call API requests.
6
+ * No filesystem writes; matches the customer's runtime API behaviour.
7
+ */
8
+ interface DevVirtualModuleOptions {
9
+ /** Absolute API base URL (e.g. `https://api.picked.app`). */
10
+ apiUrl: string;
11
+ /** Picked project ID to fetch. */
12
+ projectId: string;
13
+ }
14
+ /**
15
+ * Synthesises the source code for the dev-mode `virtual:picked/data` module.
16
+ *
17
+ * @param {DevVirtualModuleOptions} options - Dev-mode synthesis inputs.
18
+ * @returns {string} JavaScript source as a string for Vite's `load` hook.
19
+ * @example
20
+ * ```typescript
21
+ * const src = devVirtualModuleSource({ apiUrl: 'https://api.picked.app', projectId: 'p_1' })
22
+ * ```
23
+ */
24
+ export declare function devVirtualModuleSource(options: DevVirtualModuleOptions): string;
25
+ export {};
26
+ //# sourceMappingURL=dev.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/virtual-module/dev.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,UAAU,uBAAuB;IAC/B,6DAA6D;IAC7D,MAAM,EAAE,MAAM,CAAA;IACd,kCAAkC;IAClC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,EAAE,uBAAuB,GAAG,MAAM,CAuB/E"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@picked/vite-plugin-picked",
3
3
  "description": "Vite plugin for build-time config injection",
4
- "version": "0.0.1-dev.1778561735098",
4
+ "version": "0.0.1-dev.1780446300503",
5
5
  "dependencies": {
6
- "@picked/client": "0.0.1-dev.1778561735098"
6
+ "@picked/client": "0.0.1-dev.1780446300503"
7
7
  },
8
8
  "devDependencies": {
9
9
  "@repo/eslint-config": "workspace:*",