@picked/vite-plugin-picked 0.0.1-dev.1778458140037 → 0.0.1-dev.1778468342672
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 +54 -9
- package/dist/esm/index.js +306 -98
- package/dist/types/gitignore.d.ts +16 -0
- package/dist/types/gitignore.d.ts.map +1 -0
- package/dist/types/index.d.ts +8 -4
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/prefetch.d.ts +72 -0
- package/dist/types/prefetch.d.ts.map +1 -0
- package/dist/types/util.d.ts +18 -0
- package/dist/types/util.d.ts.map +1 -0
- package/dist/types/virtual-module/build.d.ts +33 -0
- package/dist/types/virtual-module/build.d.ts.map +1 -0
- package/dist/types/virtual-module/dev.d.ts +26 -0
- package/dist/types/virtual-module/dev.d.ts.map +1 -0
- package/package.json +2 -2
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
|
|
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="${
|
|
5
|
-
<span class="feature-name">${
|
|
6
|
-
<span class="feature-id">${
|
|
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="${
|
|
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(
|
|
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>`]),
|
|
858
|
+
</html>`]),n,t)}function s(e){return e.replaceAll(`&`,`&`).replaceAll(`<`,`<`).replaceAll(`>`,`>`).replaceAll(`"`,`"`)}function c(e){return e.replaceAll(`&`,`&`).replaceAll(`"`,`"`).replaceAll(`<`,`<`).replaceAll(`>`,`>`)}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 ee(e,t){return`${new Date().toLocaleTimeString()} [${t?`${l}:${t}`:l}] ${e}`}function h(e,t,n,r){if(!e.enabled)return;let i=ee(t,r);e.log(e.color?e.color(i):i),n!==void 0&&e.log(n)}function g(e){let t=m[e],n=new Set;return Object.assign((e,n,r)=>{h(t,e,n,r)},{once(e,r,i){!t.enabled||n.has(e)||(n.add(e),h(t,e,r,i))}})}function _(e){let t=p.indexOf(e);if(t===-1){h(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
|
|
861
|
-
`)
|
|
860
|
+
`))}var x={banner:b,debug:g(`debug`),info:g(`info`),warn:g(`warn`),error:g(`error`),setLevel:_};function S(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 C=`.picked/`;async function w(t,i){if(i)return;let a=(0,r.join)(t,`.gitignore`);if(!(0,e.existsSync)(a)){await(0,n.writeFile)(a,`${C}
|
|
861
|
+
`);return}let o=await(0,n.readFile)(a,`utf8`);T(o)||await(0,n.appendFile)(a,`${o.length>0&&!o.endsWith(`
|
|
862
|
+
`)?`
|
|
863
|
+
`:``}${C}
|
|
864
|
+
`)}function T(e){return e.split(/\r?\n/).map(e=>e.trim()).some(e=>e===C||e===`.picked`)}function E(e){return Array.isArray(e)?e:[]}var D=1;function O(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 te(t){if(!(0,e.existsSync)(t))return null;try{let e=await(0,n.readFile)(t,`utf8`),r=JSON.parse(e);return ne(r)?r:null}catch{return null}}function ne(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 re(e){let t=new Set;for(let n of E(e.steps))n.layoutContentId&&t.add(n.id);if(t.size===0)return[];let n=[];for(let r of E(e.experiences))E(r.nodes).some(e=>e.type===`step`&&t.has(e.stepId))&&n.push(r.id);return n}async function k(e){let{apiUrl:t,projectId:i,projectRoot:a,headers:o,reuseWhenUnchanged:s=!0}=e,c=O(a),l=await j(t,i,o),u=re(l);if(s){let e=await te(c.manifestPath);if(e&&e.projectVersionId===l.versionId&&e.workspaceId===l.workspaceId)return{config:l,manifest:e,experienceContentPaths:A(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 M(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:D,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:A(c,u)}}function A(e,t){let n={};for(let i of t)n[i]=(0,r.join)(e.experiencesDir,`${i}.json`);return n}async function j(e,n,r){let i;try{i=await(0,t.fetchProject)(e,n,{headers:r})}catch(t){throw Error(`[picked] Build-time fetch of project config failed for "${n}" at ${e}: ${N(t)}`)}if(!i)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 i}async function M(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}: ${N(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 N(e){if(e instanceof Error)return e.message;if(typeof e==`string`)return e;try{return JSON.stringify(e)}catch{return`unknown error`}}function P(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 F(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 I=5;function L(e,t,n){if(t.length===0)return;if(t.length>I){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 R(e,t){t.length!==0&&console.log(`\n ${e}: ${t.length.toString()} loaded\n`)}var z=[{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()}`}],B=[{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()}],V=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key},{header:`Fields`,value:e=>(e.fieldIds?.length??0).toString()}],H=[{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`}],U=[{header:`Name`,value:e=>e.name},{header:`Key`,value:e=>e.key}];function W(e){try{let t=new URL(e).hostname;return t===`localhost`||t===`127.0.0.1`||t===`::1`}catch{return!1}}function G(e){return new Promise(t=>{setTimeout(t,e)})}async function K(e,n,r){let i=await(0,t.fetchProject)(e,n,{headers:r});if(i||!W(e))return i;for(let i=1;i<=Z;i++){x.info(`local API not ready, retrying in ${(Q/1e3).toString()}s (${i.toString()}/${Z.toString()})`),await G(Q);let a=await(0,t.fetchProject)(e,n,{headers:r});if(a)return a}return null}function q(){return`0.0.1-dev.1778468342672`}var J=`https://api.picked.app`,Y=`virtual:picked/data`,X=`\0${Y}`,Z=5,Q=2e3;function ie(n){let r=n.apiUrl??J,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!==J&&x.warn(`using custom API URL: ${r}`);let d=(0,t.createClientHeaders)(`vite-plugin`,q());if(u.command===`build`){if(l)return;l=!0,x.info(`prefetching project config + content for "${n.projectId}"`);let t=O(a),o=(0,e.existsSync)(t.pickedDir),s=performance.now(),u=await k({apiUrl:r,projectId:n.projectId,projectRoot:a,headers:d}),f=(performance.now()-s).toFixed(0);await w(a,o);let p=u.config;i=E(p.features),$(p,f),c=P({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(),t=await K(r,n.projectId,d),a=(performance.now()-e).toFixed(0);t?(i=E(t.features),$(t,a)):x.warn(`failed to fetch project config for "${n.projectId}" (${a}ms)`)}},resolveId(e){if(e===Y)return X},load(e){if(e===X)return s===`build`?c:F({apiUrl:r,projectId:n.projectId})},configureServer(e){if(n.disableLauncher)return;e.middlewares.use(S(()=>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=E(e.features),r=E(e.experiences),i=E(e.steps),a=E(e.fields),o=E(e.conditions),s=E(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})`),L(`Features`,n,z),L(`Experiences`,r,B),L(`Steps`,i,V),L(`Fields`,a,H),L(`Conditions`,o,U),R(`Actions`,s),x.debug(`config size: ${c.toString()} bytes`)}exports.picked=ie;
|
package/dist/esm/index.js
CHANGED
|
@@ -1,25 +1,28 @@
|
|
|
1
|
-
import {
|
|
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
|
|
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
|
|
9
|
-
function
|
|
10
|
-
let t = JSON.stringify(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="${
|
|
14
|
-
<span class="feature-name">${
|
|
15
|
-
<span class="feature-id">${
|
|
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="${
|
|
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(
|
|
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 \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, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\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 \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, '&').replace(/\"/g, '"').replace(/</g, '<').replace(/>/g, '>');\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
|
-
]),
|
|
33
|
+
]), n, t);
|
|
31
34
|
}
|
|
32
|
-
function
|
|
35
|
+
function f(e) {
|
|
33
36
|
return e.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """);
|
|
34
37
|
}
|
|
35
|
-
function
|
|
38
|
+
function p(e) {
|
|
36
39
|
return e.replaceAll("&", "&").replaceAll("\"", """).replaceAll("<", "<").replaceAll(">", ">");
|
|
37
40
|
}
|
|
38
41
|
//#endregion
|
|
39
42
|
//#region src/log.ts
|
|
40
|
-
var
|
|
41
|
-
function
|
|
43
|
+
var m = "picked", h = "\x1B[0m";
|
|
44
|
+
function ee() {
|
|
42
45
|
let e = process.env.NO_COLOR;
|
|
43
46
|
return e === "1" || e === "true";
|
|
44
47
|
}
|
|
45
|
-
function
|
|
46
|
-
return (t) =>
|
|
48
|
+
function g(e) {
|
|
49
|
+
return (t) => ee() ? t : `${e}${t}${h}`;
|
|
47
50
|
}
|
|
48
|
-
var
|
|
51
|
+
var _ = [
|
|
49
52
|
"debug",
|
|
50
53
|
"info",
|
|
51
54
|
"warn",
|
|
52
55
|
"error",
|
|
53
56
|
"silent"
|
|
54
|
-
],
|
|
57
|
+
], v = {
|
|
55
58
|
debug: {
|
|
56
|
-
color:
|
|
59
|
+
color: g("\x1B[2m"),
|
|
57
60
|
log: console.debug,
|
|
58
61
|
enabled: !1
|
|
59
62
|
},
|
|
60
63
|
info: {
|
|
61
|
-
color:
|
|
64
|
+
color: g("\x1B[36m"),
|
|
62
65
|
log: console.log,
|
|
63
66
|
enabled: !0
|
|
64
67
|
},
|
|
65
68
|
warn: {
|
|
66
|
-
color:
|
|
69
|
+
color: g("\x1B[33m"),
|
|
67
70
|
log: console.warn,
|
|
68
71
|
enabled: !0
|
|
69
72
|
},
|
|
70
73
|
error: {
|
|
71
|
-
color:
|
|
74
|
+
color: g("\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
|
|
81
|
-
return `${(/* @__PURE__ */ new Date()).toLocaleTimeString()} [${t ? `${
|
|
83
|
+
function te(e, t) {
|
|
84
|
+
return `${(/* @__PURE__ */ new Date()).toLocaleTimeString()} [${t ? `${m}:${t}` : m}] ${e}`;
|
|
82
85
|
}
|
|
83
|
-
function
|
|
86
|
+
function y(e, t, n, r) {
|
|
84
87
|
if (!e.enabled) return;
|
|
85
|
-
let i =
|
|
88
|
+
let i = te(t, r);
|
|
86
89
|
e.log(e.color ? e.color(i) : i), n !== void 0 && e.log(n);
|
|
87
90
|
}
|
|
88
|
-
function
|
|
89
|
-
let t =
|
|
91
|
+
function b(e) {
|
|
92
|
+
let t = v[e], n = /* @__PURE__ */ new Set();
|
|
90
93
|
return Object.assign((e, n, r) => {
|
|
91
|
-
|
|
94
|
+
y(t, e, n, r);
|
|
92
95
|
}, { once(e, r, i) {
|
|
93
|
-
!t.enabled || n.has(e) || (n.add(e),
|
|
96
|
+
!t.enabled || n.has(e) || (n.add(e), y(t, e, r, i));
|
|
94
97
|
} });
|
|
95
98
|
}
|
|
96
|
-
function
|
|
97
|
-
let t =
|
|
99
|
+
function x(e) {
|
|
100
|
+
let t = _.indexOf(e);
|
|
98
101
|
if (t === -1) {
|
|
99
|
-
|
|
102
|
+
y(v.error, `invalid log level: ${e}`);
|
|
100
103
|
return;
|
|
101
104
|
}
|
|
102
|
-
for (let [e, n] of
|
|
105
|
+
for (let [e, n] of _.entries()) v[n].enabled = e >= t;
|
|
103
106
|
}
|
|
104
|
-
var
|
|
105
|
-
function
|
|
107
|
+
var S = g("\x1B[34m"), C = g("\x1B[36m");
|
|
108
|
+
function w() {
|
|
106
109
|
let e = [
|
|
107
|
-
` ${
|
|
108
|
-
`${
|
|
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
|
|
116
|
-
banner:
|
|
117
|
-
debug:
|
|
118
|
-
info:
|
|
119
|
-
warn:
|
|
120
|
-
error:
|
|
121
|
-
setLevel:
|
|
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
|
|
128
|
+
function ne(e) {
|
|
126
129
|
return (t, n, r) => {
|
|
127
130
|
if (t.method !== "GET") {
|
|
128
131
|
r();
|
|
@@ -138,14 +141,185 @@ function x(e) {
|
|
|
138
141
|
};
|
|
139
142
|
}
|
|
140
143
|
//#endregion
|
|
141
|
-
//#region src/
|
|
142
|
-
var
|
|
143
|
-
function
|
|
144
|
+
//#region src/gitignore.ts
|
|
145
|
+
var E = ".picked/";
|
|
146
|
+
async function D(t, n) {
|
|
147
|
+
if (n) return;
|
|
148
|
+
let r = c(t, ".gitignore");
|
|
149
|
+
if (!e(r)) {
|
|
150
|
+
await s(r, `${E}
|
|
151
|
+
`);
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
let a = await o(r, "utf8");
|
|
155
|
+
O(a) || await i(r, `${a.length > 0 && !a.endsWith("\n") ? "\n" : ""}${E}
|
|
156
|
+
`);
|
|
157
|
+
}
|
|
158
|
+
function O(e) {
|
|
159
|
+
return e.split(/\r?\n/).map((e) => e.trim()).some((e) => e === E || e === ".picked");
|
|
160
|
+
}
|
|
161
|
+
//#endregion
|
|
162
|
+
//#region src/util.ts
|
|
163
|
+
function k(e) {
|
|
144
164
|
return Array.isArray(e) ? e : [];
|
|
145
165
|
}
|
|
146
|
-
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/prefetch.ts
|
|
168
|
+
var re = 1;
|
|
169
|
+
function A(e) {
|
|
170
|
+
let t = c(e, ".picked");
|
|
171
|
+
return {
|
|
172
|
+
pickedDir: t,
|
|
173
|
+
manifestPath: c(t, "manifest.json"),
|
|
174
|
+
projectConfigPath: c(t, "project-config.json"),
|
|
175
|
+
experiencesDir: c(t, "experiences")
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
async function ie(t) {
|
|
179
|
+
if (!e(t)) return null;
|
|
180
|
+
try {
|
|
181
|
+
let e = await o(t, "utf8"), n = JSON.parse(e);
|
|
182
|
+
return ae(n) ? n : null;
|
|
183
|
+
} catch {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
function ae(e) {
|
|
188
|
+
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));
|
|
189
|
+
}
|
|
190
|
+
function j(e) {
|
|
191
|
+
let t = /* @__PURE__ */ new Set();
|
|
192
|
+
for (let n of k(e.steps)) n.layoutContentId && t.add(n.id);
|
|
193
|
+
if (t.size === 0) return [];
|
|
194
|
+
let n = [];
|
|
195
|
+
for (let r of k(e.experiences)) k(r.nodes).some((e) => e.type === "step" && t.has(e.stepId)) && n.push(r.id);
|
|
196
|
+
return n;
|
|
197
|
+
}
|
|
198
|
+
async function M(e) {
|
|
199
|
+
let { apiUrl: t, projectId: n, projectRoot: r, headers: i, reuseWhenUnchanged: o = !0 } = e, l = A(r), u = await P(t, n, i), d = j(u);
|
|
200
|
+
if (o) {
|
|
201
|
+
let e = await ie(l.manifestPath);
|
|
202
|
+
if (e && e.projectVersionId === u.versionId && e.workspaceId === u.workspaceId) return {
|
|
203
|
+
config: u,
|
|
204
|
+
manifest: e,
|
|
205
|
+
experienceContentPaths: N(l, d)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
await a(l.experiencesDir, { recursive: !0 }), await s(l.projectConfigPath, JSON.stringify(u, null, 2));
|
|
209
|
+
let f = (/* @__PURE__ */ new Date()).toISOString(), p = [];
|
|
210
|
+
for (let e of d) {
|
|
211
|
+
let r = await F(t, n, e, i);
|
|
212
|
+
await s(c(l.experiencesDir, `${e}.json`), JSON.stringify(r, null, 2)), p.push({
|
|
213
|
+
id: e,
|
|
214
|
+
fetchedAt: f
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
let m = {
|
|
218
|
+
version: re,
|
|
219
|
+
fetchedAt: f,
|
|
220
|
+
projectVersionId: u.versionId,
|
|
221
|
+
workspaceId: u.workspaceId,
|
|
222
|
+
projectId: u.id,
|
|
223
|
+
experiences: p
|
|
224
|
+
};
|
|
225
|
+
return await s(l.manifestPath, JSON.stringify(m, null, 2)), {
|
|
226
|
+
config: u,
|
|
227
|
+
manifest: m,
|
|
228
|
+
experienceContentPaths: N(l, d)
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
function N(e, t) {
|
|
232
|
+
let n = {};
|
|
233
|
+
for (let r of t) n[r] = c(e.experiencesDir, `${r}.json`);
|
|
234
|
+
return n;
|
|
235
|
+
}
|
|
236
|
+
async function P(e, t, n) {
|
|
237
|
+
let i;
|
|
238
|
+
try {
|
|
239
|
+
i = await r(e, t, { headers: n });
|
|
240
|
+
} catch (n) {
|
|
241
|
+
throw Error(`[picked] Build-time fetch of project config failed for "${t}" at ${e}: ${I(n)}`);
|
|
242
|
+
}
|
|
243
|
+
if (!i) 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.`);
|
|
244
|
+
return i;
|
|
245
|
+
}
|
|
246
|
+
async function F(e, t, r, i) {
|
|
247
|
+
let a;
|
|
248
|
+
try {
|
|
249
|
+
a = await n(e, t, r, { headers: i });
|
|
250
|
+
} catch (t) {
|
|
251
|
+
throw Error(`[picked] Build-time fetch of content for experience "${r}" failed at ${e}: ${I(t)}`);
|
|
252
|
+
}
|
|
253
|
+
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.`);
|
|
254
|
+
return a;
|
|
255
|
+
}
|
|
256
|
+
function I(e) {
|
|
257
|
+
if (e instanceof Error) return e.message;
|
|
258
|
+
if (typeof e == "string") return e;
|
|
259
|
+
try {
|
|
260
|
+
return JSON.stringify(e);
|
|
261
|
+
} catch {
|
|
262
|
+
return "unknown error";
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
//#endregion
|
|
266
|
+
//#region src/virtual-module/build.ts
|
|
267
|
+
function L(e) {
|
|
268
|
+
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");
|
|
269
|
+
return `import projectConfigJson from ${i(t)}
|
|
270
|
+
|
|
271
|
+
const experienceImporters = {
|
|
272
|
+
${a}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export const projectConfig = projectConfigJson
|
|
276
|
+
|
|
277
|
+
export async function experienceContent(experience, _options) {
|
|
278
|
+
const id = typeof experience === 'string' ? experience : experience?.id
|
|
279
|
+
if (!id) return null
|
|
280
|
+
const importer = experienceImporters[id]
|
|
281
|
+
if (!importer) return null
|
|
282
|
+
try {
|
|
283
|
+
return await importer()
|
|
284
|
+
} catch {
|
|
285
|
+
return null
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
export const manifestMetadata = ${JSON.stringify(r)}
|
|
290
|
+
`;
|
|
291
|
+
}
|
|
292
|
+
//#endregion
|
|
293
|
+
//#region src/virtual-module/dev.ts
|
|
294
|
+
function R(e) {
|
|
295
|
+
let { apiUrl: t, projectId: n } = e;
|
|
296
|
+
return `import { fetchProject, fetchExperienceContent } from '@picked/client/plugins/dynamic-experiences'
|
|
297
|
+
|
|
298
|
+
const apiUrl = ${JSON.stringify(t)}
|
|
299
|
+
const projectId = ${JSON.stringify(n)}
|
|
300
|
+
|
|
301
|
+
export const projectConfig = await fetchProject(apiUrl, projectId)
|
|
302
|
+
|
|
303
|
+
export async function experienceContent(experience, options) {
|
|
304
|
+
const id = typeof experience === 'string' ? experience : experience?.id
|
|
305
|
+
if (!id) return null
|
|
306
|
+
return await fetchExperienceContent(apiUrl, projectId, id, options)
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export const manifestMetadata = {
|
|
310
|
+
mode: 'dev',
|
|
311
|
+
fetchedAt: null,
|
|
312
|
+
projectVersionId: projectConfig?.versionId ?? null,
|
|
313
|
+
workspaceId: projectConfig?.workspaceId ?? null,
|
|
314
|
+
}
|
|
315
|
+
`;
|
|
316
|
+
}
|
|
317
|
+
//#endregion
|
|
318
|
+
//#region src/index.ts
|
|
319
|
+
var z = 5;
|
|
320
|
+
function B(e, t, n) {
|
|
147
321
|
if (t.length === 0) return;
|
|
148
|
-
if (t.length >
|
|
322
|
+
if (t.length > z) {
|
|
149
323
|
console.log(`\n ${e}: ${t.length.toString()} loaded\n`);
|
|
150
324
|
return;
|
|
151
325
|
}
|
|
@@ -160,10 +334,10 @@ function w(e, t, n) {
|
|
|
160
334
|
""
|
|
161
335
|
].join("\n"));
|
|
162
336
|
}
|
|
163
|
-
function
|
|
337
|
+
function V(e, t) {
|
|
164
338
|
t.length !== 0 && console.log(`\n ${e}: ${t.length.toString()} loaded\n`);
|
|
165
339
|
}
|
|
166
|
-
var
|
|
340
|
+
var H = [
|
|
167
341
|
{
|
|
168
342
|
header: "Name",
|
|
169
343
|
value: (e) => e.name
|
|
@@ -184,7 +358,7 @@ var E = [
|
|
|
184
358
|
header: "Ver",
|
|
185
359
|
value: (e) => `v${e.version.toString()}`
|
|
186
360
|
}
|
|
187
|
-
],
|
|
361
|
+
], U = [
|
|
188
362
|
{
|
|
189
363
|
header: "Name",
|
|
190
364
|
value: (e) => e.name
|
|
@@ -205,7 +379,7 @@ var E = [
|
|
|
205
379
|
header: "Targets",
|
|
206
380
|
value: (e) => e.targets.length.toString()
|
|
207
381
|
}
|
|
208
|
-
],
|
|
382
|
+
], W = [
|
|
209
383
|
{
|
|
210
384
|
header: "Name",
|
|
211
385
|
value: (e) => e.name
|
|
@@ -218,7 +392,7 @@ var E = [
|
|
|
218
392
|
header: "Fields",
|
|
219
393
|
value: (e) => (e.fieldIds?.length ?? 0).toString()
|
|
220
394
|
}
|
|
221
|
-
],
|
|
395
|
+
], G = [
|
|
222
396
|
{
|
|
223
397
|
header: "Name",
|
|
224
398
|
value: (e) => e.name
|
|
@@ -235,14 +409,14 @@ var E = [
|
|
|
235
409
|
header: "Required",
|
|
236
410
|
value: (e) => e.required ? "yes" : "no"
|
|
237
411
|
}
|
|
238
|
-
],
|
|
412
|
+
], K = [{
|
|
239
413
|
header: "Name",
|
|
240
414
|
value: (e) => e.name
|
|
241
415
|
}, {
|
|
242
416
|
header: "Key",
|
|
243
417
|
value: (e) => e.key
|
|
244
418
|
}];
|
|
245
|
-
function
|
|
419
|
+
function q(e) {
|
|
246
420
|
try {
|
|
247
421
|
let t = new URL(e).hostname;
|
|
248
422
|
return t === "localhost" || t === "127.0.0.1" || t === "::1";
|
|
@@ -250,66 +424,100 @@ function j(e) {
|
|
|
250
424
|
return !1;
|
|
251
425
|
}
|
|
252
426
|
}
|
|
253
|
-
function
|
|
427
|
+
function oe(e) {
|
|
254
428
|
return new Promise((t) => {
|
|
255
429
|
setTimeout(t, e);
|
|
256
430
|
});
|
|
257
431
|
}
|
|
258
|
-
async function
|
|
259
|
-
let i = await
|
|
260
|
-
if (i || !
|
|
261
|
-
for (let i = 1; i <=
|
|
262
|
-
|
|
263
|
-
let a = await
|
|
432
|
+
async function se(e, t, n) {
|
|
433
|
+
let i = await r(e, t, { headers: n });
|
|
434
|
+
if (i || !q(e)) return i;
|
|
435
|
+
for (let i = 1; i <= Z; i++) {
|
|
436
|
+
T.info(`local API not ready, retrying in ${(Q / 1e3).toString()}s (${i.toString()}/${Z.toString()})`), await oe(Q);
|
|
437
|
+
let a = await r(e, t, { headers: n });
|
|
264
438
|
if (a) return a;
|
|
265
439
|
}
|
|
266
440
|
return null;
|
|
267
441
|
}
|
|
268
|
-
function
|
|
269
|
-
return "0.0.1-dev.
|
|
442
|
+
function ce() {
|
|
443
|
+
return "0.0.1-dev.1778468342672";
|
|
270
444
|
}
|
|
271
|
-
var
|
|
272
|
-
function
|
|
273
|
-
let
|
|
445
|
+
var J = "https://api.picked.app", Y = "virtual:picked/data", X = `\0${Y}`, Z = 5, Q = 2e3;
|
|
446
|
+
function le(n) {
|
|
447
|
+
let r = n.apiUrl ?? J, i = [], a = process.cwd(), o = "serve", s, c = !1;
|
|
274
448
|
return {
|
|
275
449
|
name: "picked",
|
|
276
|
-
config: async () => {
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
450
|
+
config: async (l, u) => {
|
|
451
|
+
o = u.command, a = process.cwd(), T.banner(), r !== J && T.warn(`using custom API URL: ${r}`);
|
|
452
|
+
let d = t("vite-plugin", ce());
|
|
453
|
+
if (u.command === "build") {
|
|
454
|
+
if (c) return;
|
|
455
|
+
c = !0, T.info(`prefetching project config + content for "${n.projectId}"`);
|
|
456
|
+
let t = A(a), o = e(t.pickedDir), l = performance.now(), u = await M({
|
|
457
|
+
apiUrl: r,
|
|
458
|
+
projectId: n.projectId,
|
|
459
|
+
projectRoot: a,
|
|
460
|
+
headers: d
|
|
461
|
+
}), f = (performance.now() - l).toFixed(0);
|
|
462
|
+
await D(a, o);
|
|
463
|
+
let p = u.config;
|
|
464
|
+
i = k(p.features), $(p, f), s = L({
|
|
465
|
+
projectConfigPath: t.projectConfigPath,
|
|
466
|
+
experienceContentPaths: u.experienceContentPaths,
|
|
467
|
+
manifestMetadata: {
|
|
468
|
+
mode: "build",
|
|
469
|
+
fetchedAt: u.manifest.fetchedAt,
|
|
470
|
+
projectVersionId: u.manifest.projectVersionId,
|
|
471
|
+
workspaceId: u.manifest.workspaceId
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
} else {
|
|
475
|
+
T.info(`fetching project config for "${n.projectId}" (dev)`);
|
|
476
|
+
let e = performance.now(), t = await se(r, n.projectId, d), a = (performance.now() - e).toFixed(0);
|
|
477
|
+
t ? (i = k(t.features), $(t, a)) : T.warn(`failed to fetch project config for "${n.projectId}" (${a}ms)`);
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
resolveId(e) {
|
|
481
|
+
if (e === Y) return X;
|
|
482
|
+
},
|
|
483
|
+
load(e) {
|
|
484
|
+
if (e === X) return o === "build" ? s : R({
|
|
485
|
+
apiUrl: r,
|
|
486
|
+
projectId: n.projectId
|
|
487
|
+
});
|
|
291
488
|
},
|
|
292
489
|
configureServer(e) {
|
|
293
|
-
if (
|
|
294
|
-
e.middlewares.use(
|
|
295
|
-
let
|
|
490
|
+
if (n.disableLauncher) return;
|
|
491
|
+
e.middlewares.use(ne(() => d(i)));
|
|
492
|
+
let t = e.printUrls.bind(e);
|
|
296
493
|
e.printUrls = () => {
|
|
297
|
-
let
|
|
298
|
-
if (
|
|
299
|
-
let e =
|
|
494
|
+
let n = e.resolvedUrls?.local[0];
|
|
495
|
+
if (n) {
|
|
496
|
+
let e = n.replace(/:(?<port>\d+)\//, (e, t) => `:\x1b[1m${t}\x1b[22m/`);
|
|
300
497
|
console.log(` \x1b[32m➜\x1b[0m \x1b[1mPicked\x1b[22m: \x1b[36m${e}\x1b[0m`);
|
|
301
498
|
}
|
|
302
|
-
|
|
499
|
+
t();
|
|
303
500
|
};
|
|
304
501
|
},
|
|
305
502
|
generateBundle() {
|
|
306
|
-
|
|
503
|
+
n.disableLauncher || this.emitFile({
|
|
307
504
|
type: "asset",
|
|
308
505
|
fileName: "index.html",
|
|
309
|
-
source: i
|
|
506
|
+
source: d(i)
|
|
310
507
|
});
|
|
311
508
|
}
|
|
312
509
|
};
|
|
313
510
|
}
|
|
511
|
+
function $(e, t) {
|
|
512
|
+
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 = [
|
|
513
|
+
`${n.length.toString()} features`,
|
|
514
|
+
`${r.length.toString()} experiences`,
|
|
515
|
+
`${i.length.toString()} steps`,
|
|
516
|
+
`${a.length.toString()} fields`,
|
|
517
|
+
`${o.length.toString()} conditions`,
|
|
518
|
+
`${s.length.toString()} actions`
|
|
519
|
+
].join(", ");
|
|
520
|
+
T.info(`fetched config in ${t}ms (${l})`), B("Features", n, H), B("Experiences", r, U), B("Steps", i, W), B("Fields", a, G), B("Conditions", o, K), V("Actions", s), T.debug(`config size: ${c.toString()} bytes`);
|
|
521
|
+
}
|
|
314
522
|
//#endregion
|
|
315
|
-
export {
|
|
523
|
+
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"}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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
|
|
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
|
-
*
|
|
15
|
-
*
|
|
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":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAA;AA2MlC,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,CAmI3D"}
|
|
@@ -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;AAMnD,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,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.
|
|
4
|
+
"version": "0.0.1-dev.1778468342672",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@picked/client": "0.0.1-dev.
|
|
6
|
+
"@picked/client": "0.0.1-dev.1778468342672"
|
|
7
7
|
},
|
|
8
8
|
"devDependencies": {
|
|
9
9
|
"@repo/eslint-config": "workspace:*",
|