From dcf77988c62cd663f68af837b3db5ee0dc907cca Mon Sep 17 00:00:00 2001 From: Seth Trowbridge Date: Tue, 10 Mar 2026 13:58:00 -0400 Subject: [PATCH] idk --- app.html | 10 ++ app.js | 167 ++++++++++++++++++++ copilot.html | 428 +++++++++++++++++++++++++++++++++++++++++++++++++++ deno.json | 15 ++ index.html | 2 +- types.d.ts | 48 ++++++ 6 files changed, 669 insertions(+), 1 deletion(-) create mode 100644 app.html create mode 100644 app.js create mode 100644 copilot.html create mode 100644 deno.json create mode 100644 types.d.ts diff --git a/app.html b/app.html new file mode 100644 index 0000000..6c1c4d7 --- /dev/null +++ b/app.html @@ -0,0 +1,10 @@ + + + + + Document + + + + + \ No newline at end of file diff --git a/app.js b/app.js new file mode 100644 index 0000000..843b3e8 --- /dev/null +++ b/app.js @@ -0,0 +1,167 @@ +/// burn vanjs into codebase +{let e,t,r,o,n,s,l,i,f,h,w,a,u,d,c,_,S,g,y,b,m,v,j,x,O;l=Object.getPrototypeOf,f={},h=l(i={isConnected:1}),w=l(l),a=(e,t,r,o)=>(e??(o?setTimeout(r,o):queueMicrotask(r),new Set)).add(t),u=(e,t,o)=>{let n=r;r=t;try{return e(o)}catch(e){return console.error(e),o}finally{r=n}},d=e=>e.filter(e=>e.t?.isConnected),c=e=>n=a(n,e,()=>{for(let e of n)e.o=d(e.o),e.l=d(e.l);n=s},1e3),_={get val(){return r?.i?.add(this),this.rawVal},get oldVal(){return r?.i?.add(this),this.h},set val(o){r?.u?.add(this),o!==this.rawVal&&(this.rawVal=o,this.o.length+this.l.length?(t?.add(this),e=a(e,this,x)):this.h=o)}},S=e=>({__proto__:_,rawVal:e,h:e,o:[],l:[]}),g=(e,t)=>{let r={i:new Set,u:new Set},n={f:e},s=o;o=[];let l=u(e,r,t);l=(l??document).nodeType?l:new Text(l);for(let e of r.i)r.u.has(e)||(c(e),e.o.push(n));for(let e of o)e.t=l;return o=s,n.t=l},y=(e,t=S(),r)=>{let n={i:new Set,u:new Set},s={f:e,s:t};s.t=r??o?.push(s)??i,t.val=u(e,n,t.rawVal);for(let e of n.i)n.u.has(e)||(c(e),e.l.push(s));return t},b=(e,...t)=>{for(let r of t.flat(1/0)){let t=l(r??0),o=t===_?g(()=>r.val):t===w?g(r):r;o!=s&&e.append(o)}return e},m=(e,t,...r)=>{let[{is:o,...n},...i]=l(r[0]??0)===h?r:[{},...r],a=e?document.createElementNS(e,t,{is:o}):document.createElement(t,{is:o});for(let[e,r]of Object.entries(n)){let o=t=>t?Object.getOwnPropertyDescriptor(t,e)??o(l(t)):s,n=t+","+e,i=f[n]??=o(l(a))?.set??0,h=e.startsWith("on")?(t,r)=>{let o=e.slice(2);a.removeEventListener(o,r),a.addEventListener(o,t)}:i?i.bind(a):a.setAttribute.bind(a,e),u=l(r??0);e.startsWith("on")||u===w&&(r=y(r),u=_),u===_?g(()=>(h(r.val,r.h),a)):h(r)}return b(a,i)},v=e=>({get:(t,r)=>m.bind(s,e,r)}),j=(e,t)=>t?t!==e&&e.replaceWith(t):e.remove(),x=()=>{let r=0,o=[...e].filter(e=>e.rawVal!==e.h);do{t=new Set;for(let e of new Set(o.flatMap(e=>e.l=d(e.l))))y(e.f,e.s,e.t),e.t=s}while(++r<100&&(o=[...t]).length);let n=[...e].filter(e=>e.rawVal!==e.h);e=s;for(let e of new Set(n.flatMap(e=>e.o=d(e.o))))j(e.t,g(e.f,e.t)),e.t=s;for(let e of n)e.h=e.rawVal},O={tags:new Proxy(e=>new Proxy(m,v(e)),v()),hydrate:(e,t)=>j(e,g(t,e)),add:b,state:S,derive:y},window.van=O;} +/// + +const $ = van.tags; +const writeKey = "vault"; + +const password = van.state(""); + +const encrypt =(secret)=> +{ + const serialized = JSON.stringify(vault.val); + + // todo add actual encryption here using secret + const encrypted = serialized+"|"+secret; + // + + localStorage.setItem(writeKey, encrypted); + vaultSerialized.val = encrypted; +}; +const decrypt =(secret)=> +{ + try + { + const encrypted = localStorage.getItem(writeKey)||""; + vaultSerialized.val = encrypted; + + // todo add actual decryption here using secret + const [decrypted, actual] = encrypted.split("|"); + if(actual !== secret){return false;} + // + + vault.val = JSON.parse(decrypted); + return true; + } + catch(e) + { + console.log("decrypt error", e); + return false; + } +}; + +/** @typedef {[domain:string, username:string, password:string, codegen:string, timestamp:number, custom_kvp:Record]} CredSet */ +const vault = van.state(/**@type{CredSet[]}*/([])); +const vaultSerialized = van.state(""); + +let attempts = 0; +const preexisting = van.state(localStorage.getItem(writeKey).length>1 ? true : false); +const accessError = van.state(false); + + +van.derive(()=>{ + if(preexisting.val) + { + const success = decrypt(password.val); + if(success) + { + preexisting.val = false; + accessError.val = false; + } + else + { + if(attempts > 0) + { + accessError.val = true; + } + return; + } + } + encrypt(password.val); + console.log("password change"); +}); + +/** @type {(credSet:CredSet)=>void} */ +const vaultAdd =(credSet)=>{ + const clone = [...vault.rawVal]; + let i; + for(i=0; iHTMLFieldSetElement} */ + const block =(label, control)=> + { + control.id = label; + return $.fieldset( + $.label({for:label}, label), + control + ) + }; + + const inputSite = $.input(); + const inputUser = $.input(); + const inputPass = $.input(); + const inputCode = $.input(); + return $.form( + + { + onsubmit(e){ + e.preventDefault(); + vaultAdd([inputSite.value, inputUser.value, inputPass.value, inputCode.value, new Date().getTime(), {}]) + } + }, + + block("Site", inputSite), + block("User", inputUser), + block("Pass", inputPass), + block("Code", inputCode), + + $.button("Add") + ) + }, + + Root() + { + const input = $.input({id:"passwordField", value:password.val}); + return $.div( + $.label({for:input.id}, "Password"), + ()=>input, + $.strong(password.val), + $.button({onclick(){ + attempts++; + password.val = input.value; + }}, "Set Password"), + $.hr(), + $.em(preexisting.val), + accessError.val ? $.strong("Password did not decrypt existing session.") : null, + preexisting.val ? $.div( + $.p("Existing session found. Unlock it with the right password, or destroy data."), + $.button({onclick(){attempts = 0; accessError.val = false; preexisting.val = false;}}, "Destroy Data") + ) : null, + (!preexisting.val && password.val) ? Components.Vault() : null, + $.hr(), + $.pre(vaultSerialized.val) + ); + + }, + + Vault() + { + return $.div( + $.h2("Vault"), + Components.Form, + vault.val.map((cred)=>{ + return $.details( + $.summary(cred[0]), + $.p( + $.strong("User Name:"), + cred[1] + ) + ) + }) + ); + } +} + +van.add(document.body, Components.Root); \ No newline at end of file diff --git a/copilot.html b/copilot.html new file mode 100644 index 0000000..e291595 --- /dev/null +++ b/copilot.html @@ -0,0 +1,428 @@ + + + + + CredVault (VanJS, in-memory, no storage) + + + + + + + + + \ No newline at end of file diff --git a/deno.json b/deno.json new file mode 100644 index 0000000..cfc84e9 --- /dev/null +++ b/deno.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "checkJs": true, + "lib": [ + "dom", + "dom.iterable", + "dom.asynciterable", + "deno.ns" + ], + "types": ["./types.d.ts"] + }, + "imports": { + "entry": "./app.js" + } +} \ No newline at end of file diff --git a/index.html b/index.html index 61ec1a6..546371a 100644 --- a/index.html +++ b/index.html @@ -95,6 +95,7 @@
+

Your Credentials

Add or Update Credential

@@ -129,7 +130,6 @@

Vault

-

Each entry follows: CredSet = [username, password, codegen, custom_kvp]

diff --git a/types.d.ts b/types.d.ts new file mode 100644 index 0000000..b3c58df --- /dev/null +++ b/types.d.ts @@ -0,0 +1,48 @@ +export {} +declare global +{ + namespace Van { + + interface State { + val: T + readonly oldVal: T + readonly rawVal: T + } + + // Defining readonly view of State for covariance. + // Basically we want StateView to implement StateView + type StateView = Readonly> + + type Val = State | T + + type Primitive = string | number | boolean | bigint + + // deno-lint-ignore no-explicit-any + type PropValue = Primitive | ((e: any) => void) | null + + type PropValueOrDerived = PropValue | StateView | (() => PropValue) + + type Props = Record & { class?: PropValueOrDerived; is?: string } + + type PropsWithKnownKeys = Partial<{[K in keyof ElementType]: PropValueOrDerived}> + + type ValidChildDomValue = Primitive | Node | null | undefined + + type BindingFunc = ((dom?: Node) => ValidChildDomValue) | ((dom?: Element) => Element) + + type ChildDom = ValidChildDomValue | StateView | BindingFunc | readonly ChildDom[] + + type TagFunc = (first?: Props & PropsWithKnownKeys | ChildDom, ...rest: readonly ChildDom[]) => Result + + type Tags = Readonly>> & { + [K in keyof HTMLElementTagNameMap]: TagFunc + } + } + const van:{ + readonly state: (initVal: T, HMRKey?:string)=> Van.State + readonly derive: (f: () => T) => Van.State + readonly add: (dom: Element, ...children: readonly Van.ChildDom[]) => Element + readonly tags: Van.Tags & ((namespaceURI: string) => Readonly>>) + readonly hydrate: (dom: T, f: (dom: T) => T | null | undefined) => T + }; +} \ No newline at end of file