blogwright-analytics 0.4.0-beta.2 → 0.4.0-beta.3

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/README.md CHANGED
@@ -7,7 +7,7 @@ through DuckDB. The site's existing CloudWatch log delivery is untouched - one C
7
7
  delivery source carries several deliveries, so this one is added beside it.
8
8
 
9
9
  This package owns the whole pipeline: its own four AWS service clients (S3 Tables,
10
- Firehose, Glue, Lambda), the twelve resource nodes those clients reconcile, the
10
+ Firehose, Glue, Lambda), the fourteen resource nodes those clients reconcile, the
11
11
  record-transform Lambda, the named query set, and the dashboard application. It depends
12
12
  on `blogwright-core` (ports, the SigV4 transport and signer, the S3 and Secrets Manager
13
13
  clients) and on DuckDB, which is reached only through the `AnalyticsQuery` port's one
@@ -0,0 +1 @@
1
+ import{G as e,K as t,dt as n,lt as r,n as i,pt as a,q as o,t as s}from"./D2BnX0Uk.js";import"./B3vZDoek.js";var c=class{constructor(e,t){this.status=e,this.body=typeof t==`string`?{message:t}:t||{message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}},l=class{constructor(e,t){try{new Headers({location:t})}catch{throw Error(`Invalid redirect location ${JSON.stringify(t)}: this string contains characters that cannot be used in HTTP headers`)}this.status=e,this.location=t}},u=class extends Error{constructor(e,t,n){super(n),this.status=e,this.text=t}};new URL(`sveltekit-internal://`);function d(e,t){return e===`/`||t===`ignore`?e:t===`never`?e.endsWith(`/`)?e.slice(0,-1):e:t===`always`&&!e.endsWith(`/`)?e+`/`:e}function f(e){return e.split(`%25`).map(decodeURI).join(`%25`)}function p(e){for(let t in e)e[t]=decodeURIComponent(e[t]);return e}function m({href:e}){return e.split(`#`)[0]}function h(){}function g(...e){let t=5381;for(let n of e)if(typeof n==`string`){let e=n.length;for(;e;)t=t*33^n.charCodeAt(--e)}else if(ArrayBuffer.isView(n)){let e=new Uint8Array(n.buffer,n.byteOffset,n.byteLength),r=e.length;for(;r;)t=t*33^e[--r]}else throw TypeError(`value must be a string or TypedArray`);return(t>>>0).toString(36)}new TextEncoder;function _(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var v=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:t?.method||`GET`)!==`GET`&&y.delete(x(e)),v(e,t));var y=new Map;function ee(e,t){let n=x(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:e,...t}=JSON.parse(r.textContent);r.getAttribute(`data-b64`)!==null&&(e=_(e));let i=r.getAttribute(`data-ttl`);return i&&y.set(n,{body:e,init:t,ttl:1e3*Number(i)}),Promise.resolve(new Response(e,t))}return window.fetch(e,t)}function b(e,t,n){if(y.size>0){let t=x(e,n),r=y.get(t);if(r){if(performance.now()<r.ttl&&[`default`,`force-cache`,`only-if-cached`,void 0].includes(n?.cache))return new Response(r.body,r.init);y.delete(t)}}return window.fetch(t,n)}function x(e,t){let n=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t?.headers||t?.body){let e=[];t.headers&&e.push([...new Headers(t.headers)].join(`,`)),t.body&&(typeof t.body==`string`||ArrayBuffer.isView(t.body))&&e.push(t.body),n+=`[data-hash="${g(...e)}"]`}return n}var te=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/,ne=/^\/\((?:[^)]+)\)$/;function re(e){let t=[];return{pattern:e===`/`||ne.test(e)?/^\/$/:RegExp(`^${ae(e).map(e=>{let n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(e);if(n)return t.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),`(?:/([^]*))?`;let r=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(e);if(r)return t.push({name:r[1],matcher:r[2],optional:!0,rest:!1,chained:!0}),`(?:/([^/]+))?`;if(!e)return;let i=e.split(/\[(.+?)\](?!\])/);return`/`+i.map((e,n)=>{if(n%2){if(e.startsWith(`x+`))return se(String.fromCharCode(parseInt(e.slice(2),16)));if(e.startsWith(`u+`))return se(String.fromCharCode(...e.slice(2).split(`-`).map(e=>parseInt(e,16))));let[,r,a,o,s]=te.exec(e);return t.push({name:o,matcher:s,optional:!!r,rest:!!a,chained:a?n===1&&i[0]===``:!1}),a?`([^]*?)`:r?`([^/]*)?`:`([^/]+?)`}return se(e)}).join(``)}).join(``)}/?$`),params:t}}function ie(e){return e!==``&&!/^\([^)]+\)$/.test(e)}function ae(e){return e.slice(1).split(`/`).filter(ie)}function oe(e,t,n){let r={},i=e.slice(1),a=i.filter(e=>e!==void 0),o=0;for(let e=0;e<t.length;e+=1){let s=t[e],c=i[e-o];if(s.chained&&s.rest&&o&&(c=i.slice(e-o,e+1).filter(e=>e).join(`/`),o=0),c===void 0){if(s.rest)c=``;else continue}if(!s.matcher||n[s.matcher](c)){r[s.name]=c;let n=t[e+1],l=i[e+1];n&&!n.rest&&n.optional&&l&&s.chained&&(o=0),!n&&!l&&Object.keys(r).length===a.length&&(o=0);continue}if(s.optional&&s.chained){o++;continue}return}if(!o)return r}function se(e){return e.normalize().replace(/[[\]]/g,`\\$&`).replace(/%/g,`%25`).replace(/\//g,`%2[Ff]`).replace(/\?/g,`%3[Ff]`).replace(/#/g,`%23`).replace(/[.*+?^${}()|\\]/g,`\\$&`)}function ce({nodes:e,server_loads:t,dictionary:n,matchers:r}){let i=new Set(t);return Object.entries(n).map(([t,[n,i,s]])=>{let{pattern:c,params:l}=re(t),u={id:t,exec:e=>{let t=c.exec(e);if(t)return oe(t,l,r)},errors:[1,...s||[]].map(t=>e[t]),layouts:[0,...i||[]].map(o),leaf:a(n)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function a(t){let n=t<0;return n&&(t=~t),[n,e[t]]}function o(t){return t===void 0?t:[i.has(t),e[t]]}}function le(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function ue(e,t,n=JSON.stringify){let r=n(t);try{sessionStorage[e]=r}catch{}}var S=globalThis.__sveltekit_1447kmy?.base??``,de=globalThis.__sveltekit_1447kmy?.assets??S??``,fe=`1788238916385`,pe=`sveltekit:snapshot`,me=`sveltekit:scroll`,he=`sveltekit:states`,C=`sveltekit:history`,w=`sveltekit:navigation`,T={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},ge=location.origin;function _e(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){let e=document.getElementsByTagName(`base`);t=e.length?e[0].href:document.URL}return new URL(e,t)}function E(){return{x:pageXOffset,y:pageYOffset}}function D(e,t){return e.getAttribute(`data-sveltekit-${t}`)}var ve={...T,"":T.hover};function ye(e){let t=e.assignedSlot??e.parentNode;return t?.nodeType===11&&(t=t.host),t}function be(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()===`A`&&e.hasAttribute(`href`))return e;e=ye(e)}}function xe(e,t,n){let r;try{if(r=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&r.hash.match(/^#[^/]/)){let e=location.hash.split(`#`)[1]||`/`;r.hash=`#${e}${r.hash}`}}catch{}let i=e instanceof SVGAElement?e.target.baseVal:e.target,a=!r||!!i||k(r,t,n)||(e.getAttribute(`rel`)||``).split(/\s+/).includes(`external`),o=r?.origin===ge&&e.hasAttribute(`download`);return{url:r,external:a,target:i,download:o}}function O(e){let t=null,n=null,r=null,i=null,a=null,o=null,s=e;for(;s&&s!==document.documentElement;)r===null&&(r=D(s,`preload-code`)),i===null&&(i=D(s,`preload-data`)),t===null&&(t=D(s,`keepfocus`)),n===null&&(n=D(s,`noscroll`)),a===null&&(a=D(s,`reload`)),o===null&&(o=D(s,`replacestate`)),s=ye(s);function c(e){switch(e){case``:case`true`:return!0;case`off`:case`false`:return!1;default:return}}return{preload_code:ve[r??`off`],preload_data:ve[i??`off`],keepfocus:c(t),noscroll:c(n),reload:c(a),replace_state:c(o)}}function Se(e){let t=a(e),n=!0;function r(){n=!0,t.update(e=>e)}function i(e){n=!1,t.set(e)}function o(e){let r;return t.subscribe(t=>{(r===void 0||n&&t!==r)&&e(r=t)})}return{notify:r,set:i,subscribe:o}}var Ce={v:h};function we(){let{set:e,subscribe:t}=a(!1),n;async function r(){clearTimeout(n);try{let t=await fetch(`${de}/_app/version.json`,{headers:{pragma:`no-cache`,"cache-control":`no-cache`}});if(!t.ok)return!1;let r=(await t.json()).version!==fe;return r&&(e(!0),Ce.v(),clearTimeout(n)),r}catch{return!1}}return{subscribe:t,check:r}}function k(e,t,n){return e.origin!==ge||!e.pathname.startsWith(t)?!0:n?e.pathname!==location.pathname:!1}function Te(e){}var Ee=new Set([`load`,`prerender`,`csr`,`ssr`,`trailingSlash`,`config`]);[...Ee],[...new Set([...Ee])];function De(e){return e.filter(e=>e!=null)}function A(e,t){return e+`/`+t}function Oe(e){return e instanceof c||e instanceof u?e.status:500}function ke(e){return e instanceof u?e.text:`Internal Error`}var j,M,Ae,je=i.toString().includes(`$$`)||/function \w+\(\) \{\}/.test(i.toString()),Me=`a:`;je?(j={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(Me)},M={current:null},Ae={current:!1}):(j=new class{#e=n({});get data(){return e(this.#e)}set data(e){r(this.#e,e)}#t=n(null);get form(){return e(this.#t)}set form(e){r(this.#t,e)}#n=n(null);get error(){return e(this.#n)}set error(e){r(this.#n,e)}#r=n({});get params(){return e(this.#r)}set params(e){r(this.#r,e)}#i=n({id:null});get route(){return e(this.#i)}set route(e){r(this.#i,e)}#a=n({});get state(){return e(this.#a)}set state(e){r(this.#a,e)}#o=n(-1);get status(){return e(this.#o)}set status(e){r(this.#o,e)}#s=n(new URL(Me));get url(){return e(this.#s)}set url(e){r(this.#s,e)}},M=new class{#e=n(null);get current(){return e(this.#e)}set current(e){r(this.#e,e)}},Ae=new class{#e=n(!1);get current(){return e(this.#e)}set current(e){r(this.#e,e)}},Ce.v=()=>Ae.current=!0);function Ne(e){Object.assign(j,e)}var{onMount:Pe,tick:Fe}=s,Ie=new Set([`icon`,`shortcut icon`,`apple-touch-icon`]),N=null,P=le(`sveltekit:scroll`)??{},F=le(`sveltekit:snapshot`)??{},I={url:Se({}),page:Se({}),navigating:a(null),updated:we()};function Le(e){P[e]=E()}function Re(e,t){let n=e+1;for(;P[n];)delete P[n],n+=1;for(n=t+1;F[n];)delete F[n],n+=1}function L(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(h)}async function ze(){if(`serviceWorker`in navigator){let e=await navigator.serviceWorker.getRegistration(S||`/`);e&&await e.update()}}var Be,Ve,R,z,He,B,Ue={},We={},V=[],H=[],U=null;function Ge(){U?.fork?.then(e=>e?.discard()),U=null,Q={element:void 0,href:void 0}}var Ke=new Map,qe=new Set,Je=new Set,W=new Set,G={branch:[],error:null,url:null,nav:null},Ye=!1,Xe=!1,Ze=!0,K=!1,q=!1,Qe=!1,$e=!1,et,J,Y,X,tt=new Set,nt=new Map,rt=new Map;async function it(e,t,n){if(globalThis.__sveltekit_1447kmy.data){let{q:e={},p:t={},l:n={},f:r={}}=globalThis.__sveltekit_1447kmy.data;for(let t in e)Ue[t]=e[t];for(let e in n)Ue[e]=n[e];for(let e in r)Ue[e]=r[e];for(let e in t)We[e]=t[e]}document.URL!==location.href&&(location.href=location.href),B=e,await e.hooks.init?.(),Be=ce(e),z=document.documentElement,He=t,Ve=e.nodes[0],R=e.nodes[1],Ve(),R(),J=history.state?.[C],Y=history.state?.[w],J||(J=Y=Date.now(),history.replaceState({...history.state,[C]:J,[w]:Y},``));let r=P[J];function i(){r&&(history.scrollRestoration=`manual`,scrollTo(r.x,r.y))}n?(i(),await Mt(He,n)):(await Z({type:`enter`,url:_e(B.hash?Rt(new URL(location.href)):location.href),replace_state:!0}),i()),jt()}function at(){V.length=0,$e=!1}function ot(e){H.some(e=>e?.snapshot)&&(F[e]=H.map(e=>e?.snapshot?.capture()))}function st(e){F[e]?.forEach((e,t)=>{H[t]?.snapshot?.restore(e)})}function ct(){Le(J),ue(me,P),ot(Y),ue(pe,F)}async function lt(e,t,n,r){let i,a;t.invalidateAll&&Ge(),await Z({type:`goto`,url:_e(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:r,accept:()=>{if(t.invalidateAll){$e=!0,i=new Set;for(let[e,t]of nt)for(let[n,r]of t)r.resource?.reset(),i.add(A(e,n));a=new Set;for(let[e,t]of rt)for(let n of t.keys())a.add(A(e,n))}t.invalidate&&t.invalidate.forEach(At)}}),t.invalidateAll&&o().then(o).then(()=>{for(let[e,t]of nt)for(let[n,{resource:r}]of t)i?.has(A(e,n))&&r.start();for(let[e,t]of rt)for(let[n,{resource:r}]of t)a?.has(A(e,n))&&r.reconnect()})}async function ut(e){if(e.id!==U?.id){Ge();let t={};tt.add(t),U={id:e.id,token:t,promise:bt({...e,preload:t}).then(e=>(tt.delete(t),e.type===`loaded`&&e.state.error&&Ge(),e)),fork:null}}return U.promise}async function dt(e){let t=(await wt(e,!1))?.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(e=>e[1]()))}async function ft(e,t,n){let r={params:G.params,route:{id:G.route?.id??null},url:new URL(location.href)};if(G={...e.state,nav:r},Ne(e.props.page),et=new B.root({target:t,props:{...e.props,stores:I,components:H},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),n){let e={from:null,to:{...r,scroll:P[J]??E()},willUnload:!1,type:`enter`,complete:Promise.resolve()};W.forEach(t=>t(e))}st(Y),Xe=!0}async function pt({url:e,params:t,branch:n,errors:r,status:i,error:a,route:o,form:s}){let c=`never`;if(S&&(e.pathname===S||e.pathname===S+`/`))c=`always`;else for(let e of n)e?.slash!==void 0&&(c=e.slash);e.pathname=d(e.pathname,c),e.search=e.search;let l={type:`loaded`,state:{url:e,params:t,branch:n,error:a,route:o},props:{constructors:De(n).map(e=>e.node.component),page:Lt(j)}};s!==void 0&&(l.props.form=s);let u={},f=!j,p=0;for(let e=0;e<Math.max(n.length,G.branch.length);e+=1){let t=n[e],r=G.branch[e];t?.data!==r?.data&&(f=!0),t&&(u={...u,...t.data},f&&(l.props[`data_${p}`]=u),p+=1)}return(!G.url||e.href!==G.url.href||G.error!==a||s!==void 0&&s!==j.form||f)&&(l.props.page={error:a,params:t,route:{id:o?.id??null},state:{},status:i,url:new URL(e),form:s??null,data:f?u:j.data}),l}async function mt({loader:e,parent:t,url:n,params:r,route:i,server_data_node:a}){let o={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},s=await e();return{node:s,loader:e,server:a,universal:s.universal?.load?{type:`data`,data:null,uses:o}:null,data:a?.data??null,slash:s.universal?.trailingSlash??a?.slash}}function ht(e,t,n){let r=e instanceof Request?e.url:e,i=new URL(r,n);return i.origin===n.origin&&(r=i.href.slice(n.origin.length)),{resolved:i,promise:Xe?b(r,i.href,t):ee(r,t)}}function gt(e,t,n,r,i,a){if($e)return!0;if(!i)return!1;if(i.parent&&e||i.route&&t||i.url&&n)return!0;for(let e of i.search_params)if(r.has(e))return!0;for(let e of i.params)if(a[e]!==G.params[e])return!0;for(let e of i.dependencies)if(V.some(t=>t(new URL(e))))return!0;return!1}function _t(e,t){return e?.type===`data`?e:e?.type===`skip`?t??null:null}function vt(e,t){if(!e)return new Set(t.searchParams.keys());let n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(let r of n){let i=e.searchParams.getAll(r),a=t.searchParams.getAll(r);i.every(e=>a.includes(e))&&a.every(e=>i.includes(e))&&n.delete(r)}return n}function yt({error:e,url:t,route:n,params:r}){return{type:`loaded`,state:{error:e,url:t,route:n,params:r,branch:[]},props:{page:Lt(j),constructors:[]}}}async function bt({id:e,invalidating:t,url:n,params:r,route:i,preload:a}){if(U?.id===e)return tt.delete(U.token),U.promise;let{errors:o,layouts:s,leaf:u}=i,d=[...s,u];o.forEach(e=>e?.().catch(h)),d.forEach(e=>e?.[1]().catch(h));let f=G.url?e!==Et(G.url):!1,p=G.route?i.id!==G.route.id:!1,m=vt(G.url,n),g=!1,_=d.map(async(e,t)=>{if(!e)return;let a=G.branch[t];return e[1]===a?.loader&&!gt(g,p,f,m,a.universal?.uses,r)?a:(g=!0,mt({loader:e[1],url:n,params:r,route:i,parent:async()=>{let e={};for(let n=0;n<t;n+=1)Object.assign(e,(await _[n])?.data);return e},server_data_node:_t(e[0]?{type:`skip`}:null,e[0]?a?.server:void 0)}))});for(let e of _)e.catch(h);let v=[];for(let e=0;e<d.length;e+=1)if(d[e])try{v.push(await _[e])}catch(t){if(t instanceof l)return{type:`redirect`,location:t.location};if(a&&tt.has(a))return yt({error:await $(t,{params:r,url:n,route:{id:i.id}}),url:n,params:r,route:i});let s=Oe(t),u;if(t instanceof c)u=t.body;else{if(await I.updated.check())return await ze(),await L(n);u=await $(t,{params:r,url:n,route:{id:i.id}})}let d=await xt(e,v,o);return d?pt({url:n,params:r,branch:v.slice(0,d.idx).concat(d.node),errors:o,status:s,error:u,route:i}):await Ot(n,{id:i.id},u,s)}else v.push(void 0);return pt({url:n,params:r,branch:v,errors:o,status:200,error:null,route:i,form:t?void 0:null})}async function xt(e,t,n){for(;e--;)if(n[e]){let r=e;for(;!t[r];)--r;try{return{idx:r+1,node:{node:await n[e](),loader:n[e],data:{},server:null,universal:null}}}catch{continue}}}async function St({status:e,error:t,url:n,route:r}){let i={};try{return pt({url:n,params:i,branch:[await mt({loader:Ve,url:n,params:i,route:r,parent:()=>Promise.resolve({}),server_data_node:_t(null)}),{node:await R(),loader:R,universal:null,server:null,data:null}],status:e,error:t,errors:[],route:null})}catch(t){if(t instanceof l){await lt(new URL(t.location,location.href),{},0);return}let a=await B.get_error_template(),o=await $(t,{url:n,params:i,route:r}),s=a({status:e,message:String(o?.message??``).replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}),c=new DOMParser().parseFromString(s,`text/html`);throw document.documentElement.replaceChild(document.adoptNode(c.head),document.head),document.documentElement.replaceChild(document.adoptNode(c.body),document.body),t}}async function Ct(e){let t=e.href;if(Ke.has(t))return Ke.get(t);let n;try{let r=(async()=>{let t=await B.hooks.reroute({url:new URL(e),fetch:async(t,n)=>ht(t,n,e).promise})??e;if(typeof t==`string`){let n=new URL(e);B.hash?n.hash=t:n.pathname=t,t=n}return t})();Ke.set(t,r),n=await r}catch{Ke.delete(t);return}return n}async function wt(e,t){if(e&&!k(e,S,B.hash)){let n=await Ct(e);if(!n)return;let r=Tt(n);for(let n of Be){let i=n.exec(r);if(i)return{id:Et(e),invalidating:t,route:n,params:p(i),url:e}}}}function Tt(e){return f(B.hash?e.hash.replace(/^#/,``).replace(/[?#].+/,``):e.pathname.slice(S.length))||`/`}function Et(e){return(B.hash?e.hash.replace(/^#/,``):e.pathname)+e.search}function Dt({url:e,type:t,intent:n,delta:r,event:i,scroll:a}){let o=!1,s=It(G,n,e,t,a??null);r!==void 0&&(s.navigation.delta=r),i!==void 0&&(s.navigation.event=i);let c={...s.navigation,cancel:()=>{o=!0,s.reject(Error(`navigation cancelled`))}};return K||qe.forEach(e=>e(c)),o?null:s}async function Z({type:e,url:n,popped:r,keepfocus:i,noscroll:a,replace_state:s,state:c={},redirect_count:l=0,nav_token:d={},accept:f=h,block:p=h,event:m}){let g=X;X=d;let _=await wt(n,!1),v=e===`enter`?It(G,_,n,e):Dt({url:n,type:e,delta:r?.delta,intent:_,scroll:r?.scroll,event:m});if(!v){p(),X===d&&(X=g);return}let y=J,ee=Y;f(),K=!0,Xe&&v.navigation.type!==`enter`&&I.navigating.set(M.current=v.navigation);let b=_&&await bt(_);if(!b){if(k(n,S,B.hash))return await L(n,s);b=await Ot(n,{id:null},await $(new u(404,`Not Found`,`Not found: ${n.pathname}`),{url:n,params:{},route:{id:null}}),404,s)}if(n=_?.url||n,X!==d){v.reject(Error(`navigation aborted`));return}if(!b)return;if(b.type===`redirect`){if(l<20){await Z({type:e,url:new URL(b.location,n),popped:r,keepfocus:i,noscroll:a,replace_state:s,state:c,redirect_count:l+1,nav_token:d}),v.fulfil(void 0);return}if(b=await St({status:500,error:await $(Error(`Redirect loop`),{url:n,params:{},route:{id:null}}),url:n,route:{id:null}}),!b)return}else if(b.props.page.status>=400&&await I.updated.check())return await ze(),await L(n,s);if(at(),Le(y),ot(ee),b.props.page.url.pathname!==n.pathname&&(n.pathname=b.props.page.url.pathname),c=r?r.state:c,!r){let e=+!s,t={[C]:J+=e,[w]:Y+=e,[he]:c};(s?history.replaceState:history.pushState).call(history,t,``,n),s||Re(J,Y)}let x=_&&U?.id===_.id?U.fork:null;U?.fork&&!x?Ge():(U=null,Q={element:void 0,href:void 0}),b.props.page.state=c;let te;if(Xe){let e=(await Promise.all(Array.from(Je,e=>e(v.navigation)))).filter(e=>typeof e==`function`);if(e.length>0){function t(){e.forEach(e=>{W.delete(e)})}e.push(t),e.forEach(e=>{W.add(e)})}let r=v.navigation.to;G={...b.state,nav:{params:r.params,route:r.route,url:r.url}},b.props.page&&(b.props.page.url=n),!i&&document.activeElement instanceof HTMLElement&&document.activeElement!==document.body&&document.activeElement.blur();let a=x&&await x;a?te=a.commit():(N=null,et.$set(b.props),N&&Object.assign(b.props.page,N),Ne(b.props.page),te=t?.()),Qe=!0}else await ft(b,He,!1);let{activeElement:ne}=document;if(await te,await o(),await o(),X!==d){v.reject(Error(`navigation aborted`));return}b.props.page&&N&&Object.assign(b.props.page,N);let re=null;if(Ze){let e=r?r.scroll:a?E():null;e?scrollTo(e.x,e.y):(re=n.hash&&document.getElementById(zt(n)))?re.scrollIntoView():scrollTo(0,0)}let ie=document.activeElement!==ne&&document.activeElement!==document.body;!i&&!ie&&Ft(n,!re),Ze=!0,K=!1,v.fulfil(void 0),v.navigation.to&&(v.navigation.to.scroll=E()),W.forEach(e=>e(v.navigation)),e===`popstate`&&st(Y),I.navigating.set(M.current=null)}async function Ot(e,t,n,r,i){return e.origin===ge&&e.pathname===location.pathname&&!Ye?await St({status:r,error:n,url:e,route:t}):await L(e,i)}var Q={element:void 0,href:void 0};function kt(){let e,t;z.addEventListener(`mousemove`,t=>{let n=t.target;clearTimeout(e),e=setTimeout(()=>{i(n,T.hover)},20)});function n(e){e.defaultPrevented||i(e.composedPath()[0],T.tap)}z.addEventListener(`mousedown`,n),z.addEventListener(`touchstart`,n,{passive:!0});let r=new IntersectionObserver(e=>{for(let t of e)t.isIntersecting&&(dt(new URL(t.target.href)),r.unobserve(t.target))},{threshold:0});async function i(e,n){let r=be(e,z),i=r===Q.element&&r?.href===Q.href&&n>=t;if(!r||i)return;let{url:a,external:o,download:s}=xe(r,S,B.hash);if(o||s)return;let c=O(r),l=a&&Et(G.url)===Et(a);if(!(c.reload||l)){if(n<=c.preload_data){Q={element:r,href:r.href},t=T.tap;let e=await wt(a,!1);if(!e)return;ut(e)}else n<=c.preload_code&&(Q={element:r,href:r.href},t=n,dt(a))}}function a(){r.disconnect();for(let e of z.querySelectorAll(`a`)){let{url:t,external:n,download:i}=xe(e,S,B.hash);if(n||i)continue;let a=O(e);a.reload||(a.preload_code===T.viewport&&r.observe(e),a.preload_code===T.eager&&dt(t))}}W.add(a),a()}function $(e,t){if(e instanceof c)return e.body;let n=Oe(e),r=ke(e);return B.hooks.handleError({error:e,event:t,status:n,message:r})??{message:r}}function At(e){if(typeof e==`function`)V.push(e);else{let{href:t}=new URL(e,location.href);V.push(e=>e.href===t)}}function jt(){history.scrollRestoration=`manual`,addEventListener(`beforeunload`,e=>{let t=!1;if(ct(),!K){let e=It(G,void 0,null,`leave`),n={...e.navigation,cancel:()=>{t=!0,e.reject(Error(`navigation cancelled`))}};qe.forEach(e=>e(n))}t?(e.preventDefault(),e.returnValue=``):history.scrollRestoration=`auto`}),addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&ct()}),navigator.connection?.saveData||kt(),z.addEventListener(`click`,async t=>{if(t.button||t.which!==1||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.defaultPrevented)return;let n=be(t.composedPath()[0],z);if(!n)return;let{url:r,external:i,target:a,download:o}=xe(n,S,B.hash);if(!r)return;if(a===`_parent`||a===`_top`){if(window.parent!==window)return}else if(a&&a!==`_self`)return;let s=O(n);if(!(n instanceof SVGAElement)&&r.protocol!==location.protocol&&r.protocol!==`https:`&&r.protocol!==`http:`||o)return;let[c,l]=(B.hash?r.hash.replace(/^#/,``):r.href).split(`#`),u=c===m(location);if(i||s.reload&&(!u||!l)){Dt({url:r,type:`link`,event:t})?K=!0:t.preventDefault();return}if(l!==void 0&&u){let[,i]=G.url.href.split(`#`);if(i===l){if(t.preventDefault(),l===``||l===`top`&&n.ownerDocument.getElementById(`top`)===null)scrollTo({top:0});else{let e=n.ownerDocument.getElementById(decodeURIComponent(l));e&&(e.scrollIntoView(),e.focus())}return}if(q=!0,Le(J),e(r),!s.replace_state)return;q=!1}t.preventDefault(),await new Promise(e=>{requestAnimationFrame(()=>{setTimeout(e,0)}),setTimeout(e,100)}),await Z({type:`link`,url:r,keepfocus:s.keepfocus,noscroll:s.noscroll,replace_state:s.replace_state??r.href===location.href,event:t})}),z.addEventListener(`submit`,e=>{if(e.defaultPrevented)return;let t=HTMLFormElement.prototype.cloneNode.call(e.target),n=e.submitter;if((n?.formTarget||t.target)===`_blank`||(n?.formMethod||t.method)!==`get`)return;let r=new URL(n?.hasAttribute(`formaction`)&&n?.formAction||t.action);if(k(r,S,!1))return;let i=e.target,a=O(i);if(a.reload)return;e.preventDefault(),e.stopPropagation();let o=new FormData(i,n);r.search=new URLSearchParams(o).toString(),Z({type:`form`,url:r,keepfocus:a.keepfocus,noscroll:a.noscroll,replace_state:a.replace_state??r.href===location.href,event:e})}),addEventListener(`popstate`,async t=>{if(!Pt){if(t.state?.[`sveltekit:history`]){let n=t.state[C];if(X={},n===J)return;let r=P[n],i=t.state[`sveltekit:states`]??{},a=new URL(t.state[`sveltekit:pageurl`]??location.href),o=t.state[w],s=G.url?m(location)===m(G.url):!1;if(o===Y&&(Qe||s)){i!==j.state&&(j.state=i),e(a),P[J]=E(),r&&scrollTo(r.x,r.y),J=n;return}let c=n-J;await Z({type:`popstate`,url:a,popped:{state:i,scroll:r,delta:c},accept:()=>{J=n,Y=o},block:()=>{history.go(-c)},nav_token:X,event:t})}else q||(e(new URL(location.href)),B.hash&&location.reload())}}),addEventListener(`hashchange`,()=>{q&&(q=!1,history.replaceState({...history.state,[C]:++J,[w]:Y},``,location.href))});for(let e of document.querySelectorAll(`link`))Ie.has(e.rel)&&(e.href=e.href);addEventListener(`pageshow`,e=>{e.persisted&&I.navigating.set(M.current=null)});function e(e){G.url=j.url=e,I.page.set(Lt(j)),I.page.notify()}}async function Mt(e,{status:t=200,error:n,node_ids:r,params:i,route:a,server_route:o,data:s,form:c}){Ye=!0;let u=new URL(location.href),d;({params:i={},route:a={id:null}}=await wt(u,!1)||{}),d=Be.find(({id:e})=>e===a.id);let f,p=!0;try{let e=r.map(async(t,n)=>{let r=s[n];return r?.uses&&(r.uses=Nt(r.uses)),mt({loader:B.nodes[t],url:u,params:i,route:a,parent:async()=>{let t={};for(let r=0;r<n;r+=1)Object.assign(t,(await e[r]).data);return t},server_data_node:_t(r)})}),o=await Promise.all(e);if(d){let e=d.layouts;for(let t=0;t<e.length;t++)e[t]||o.splice(t,0,void 0)}f=await pt({url:u,params:i,branch:o,status:t,error:n,errors:d?.errors,form:c,route:d??null})}catch(t){if(t instanceof l)return await L(new URL(t.location,location.href));f=await St({status:Oe(t),error:await $(t,{url:u,params:i,route:a}),url:u,route:a}),e.textContent=``,p=!1}f&&(f.props.page&&(f.props.page.state={}),await ft(f,e,p))}function Nt(e){return{dependencies:new Set(e?.dependencies??[]),params:new Set(e?.params??[]),parent:!!e?.parent,route:!!e?.route,url:!!e?.url,search_params:new Set(e?.search_params??[])}}var Pt=!1;function Ft(e,t=!0){let n=document.querySelector(`[autofocus]`);if(n)n.focus();else{let n=zt(e);if(n&&document.getElementById(n)){let{x:r,y:i}=E();setTimeout(()=>{let a=history.state;Pt=!0,location.replace(new URL(`#${n}`,location.href)),history.replaceState(a,``,e),t&&scrollTo(r,i),Pt=!1})}else{let e=document.body,t=e.getAttribute(`tabindex`);e.tabIndex=-1,e.focus({preventScroll:!0,focusVisible:!1}),t===null?e.removeAttribute(`tabindex`):e.setAttribute(`tabindex`,t)}let r=getSelection();if(r&&r.type!==`None`){let e=[];for(let t=0;t<r.rangeCount;t+=1)e.push(r.getRangeAt(t));setTimeout(()=>{if(r.rangeCount===e.length){for(let t=0;t<r.rangeCount;t+=1){let n=e[t],i=r.getRangeAt(t);if(n.commonAncestorContainer!==i.commonAncestorContainer||n.startContainer!==i.startContainer||n.endContainer!==i.endContainer||n.startOffset!==i.startOffset||n.endOffset!==i.endOffset)return}r.removeAllRanges()}})}}}function It(e,t,n,r,i=null){let a,o,s=new Promise((e,t)=>{a=e,o=t});return s.catch(h),{navigation:{from:{params:e.params,route:{id:e.route?.id??null},url:e.url,scroll:E()},to:n&&{params:t?.params??null,route:{id:t?.route?.id??null},url:n,scroll:i},willUnload:!t,type:r,complete:s},fulfil:a,reject:o}}function Lt(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function Rt(e){let t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function zt(e){let t;if(B.hash){let[,,n]=e.hash.split(`#`,3);t=n??``}else t=e.hash.slice(1);return decodeURIComponent(t)}export{Te as i,M as n,j as r,it as t};
@@ -1,2 +1,2 @@
1
- const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.UTMEigHJ.js","../chunks/D2BnX0Uk.js","../chunks/xihTtKlq.js","../assets/0.BTQrrh5B.css","../nodes/1.CMX4YrAG.js","../chunks/FH_3TS2H.js","../chunks/B3vZDoek.js","../nodes/2.CQMIFodk.js","../chunks/HclGiUj8.js","../chunks/Bd1gDe3Y.js","../chunks/Doz7YX1W.js","../chunks/CyCGwyzf.js","../chunks/ZvRY-WVy.js","../chunks/DKMlMI4a.js","../chunks/Dh958be7.js","../chunks/CvyVA_jC.js","../chunks/caXkbKD3.js","../chunks/C58PZtCD.js","../assets/Group.DaJMILJc.css","../chunks/DJc8C0NK.js","../chunks/Db0q5_zr.js","../chunks/CCch3uox.js","../chunks/V9ZjaxiY.js","../chunks/BID1NNRh.js","../chunks/-aNGNaBT.js","../chunks/RobXhXPM.js","../chunks/Dfvzj6n2.js","../chunks/xIq_fFFM.js","../chunks/BZrG8lR6.js","../assets/Text.j9l35qB0.css","../chunks/DCkHk5n_.js","../chunks/BC0iYSWx.js","../assets/Path.CvpwNZ6g.css","../chunks/BJngAqxk.js","../chunks/B5KRA4hC.js","../chunks/BR2LaRms.js","../chunks/CRTcXoMo.js","../chunks/vDgBJUjM.js","../assets/Rect.CtRaGMmQ.css","../chunks/DMoYijy0.js","../assets/Line.fkmsECm9.css","../chunks/D0Ty6LN0.js","../assets/Circle.C5MKzgk2.css","../chunks/BnKf2MvA.js","../assets/2.DoCaRy3T.css"])))=>i.map(i=>d[i]);
2
- import{$ as e,B as t,Ct as n,E as r,F as i,G as a,I as o,L as s,N as c,_t as l,dt as u,et as d,ft as f,gt as p,i as m,it as h,j as g,lt as _,n as v,ot as y,q as b,r as x,rt as S,s as C,tt as w}from"../chunks/D2BnX0Uk.js";import{t as T}from"../chunks/HclGiUj8.js";import"../chunks/xihTtKlq.js";var E={},D=s(`<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>`),O=s(`<!> <!>`,1);function k(s,x){l(x,!0);let T=m(x,`components`,23,()=>[]),E=m(x,`data_0`,3,null),k=m(x,`data_1`,3,null);w(()=>x.stores.page.set(x.page)),d(()=>{x.stores,x.page,x.constructors,T(),x.form,E(),k(),x.stores.page.notify()});let A=u(!1),j=u(!1),M=u(null);v(()=>{let e=x.stores.page.subscribe(()=>{a(A)&&(_(j,!0),b().then(()=>{_(M,document.title||`untitled page`,!0)}))});return _(A,!0),e});let N=f(()=>x.constructors[1]);var P=O(),F=h(P),I=e=>{let t=f(()=>x.constructors[0]);var n=o(),s=h(n);r(s,()=>a(t),(e,t)=>{C(t(e,{get data(){return E()},get form(){return x.form},get params(){return x.page.params},children:(e,t)=>{var n=o(),s=h(n);r(s,()=>a(N),(e,t)=>{C(t(e,{get data(){return k()},get form(){return x.form},get params(){return x.page.params}}),e=>T()[1]=e,()=>T()?.[1])}),i(e,n)},$$slots:{default:!0}}),e=>T()[0]=e,()=>T()?.[0])}),i(e,n)},L=e=>{let t=f(()=>x.constructors[0]);var n=o(),s=h(n);r(s,()=>a(t),(e,t)=>{C(t(e,{get data(){return E()},get form(){return x.form},get params(){return x.page.params}}),e=>T()[0]=e,()=>T()?.[0])}),i(e,n)};g(F,e=>{x.constructors[1]?e(I):e(L,-1)});var R=y(F,2),z=r=>{var o=D(),s=S(o),l=n=>{var r=t();e(()=>c(r,a(M))),i(n,r)};g(s,e=>{a(j)&&e(l)}),n(o),i(r,o)};g(R,e=>{a(A)&&e(z)}),i(s,P),p()}var A=x(k),j=[()=>T(()=>import(`../nodes/0.UTMEigHJ.js`),__vite__mapDeps([0,1,2,3]),import.meta.url),()=>T(()=>import(`../nodes/1.CMX4YrAG.js`),__vite__mapDeps([4,1,5,6,2]),import.meta.url),()=>T(()=>import(`../nodes/2.CQMIFodk.js`),__vite__mapDeps([7,1,8,2,9,6,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44]),import.meta.url)],M=[],N={"/":[2]},P={handleError:(({error:e})=>{console.error(e)}),reroute:(()=>{}),transport:{}},F=Object.fromEntries(Object.entries(P.transport).map(([e,t])=>[e,t.decode])),I=Object.fromEntries(Object.entries(P.transport).map(([e,t])=>[e,t.encode])),L=!1,R=(e,t)=>F[e](t),z=()=>T(()=>import(`../chunks/Bjy-W4x2.js`).then(e=>e.default),[],import.meta.url);export{R as decode,F as decoders,N as dictionary,I as encoders,z as get_error_template,L as hash,P as hooks,E as matchers,j as nodes,A as root,M as server_loads};
1
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["../nodes/0.UTMEigHJ.js","../chunks/D2BnX0Uk.js","../chunks/xihTtKlq.js","../assets/0.BTQrrh5B.css","../nodes/1.Biso6Rq1.js","../chunks/D1DPCGav.js","../chunks/B3vZDoek.js","../nodes/2.CQMIFodk.js","../chunks/HclGiUj8.js","../chunks/Bd1gDe3Y.js","../chunks/Doz7YX1W.js","../chunks/CyCGwyzf.js","../chunks/ZvRY-WVy.js","../chunks/DKMlMI4a.js","../chunks/Dh958be7.js","../chunks/CvyVA_jC.js","../chunks/caXkbKD3.js","../chunks/C58PZtCD.js","../assets/Group.DaJMILJc.css","../chunks/DJc8C0NK.js","../chunks/Db0q5_zr.js","../chunks/CCch3uox.js","../chunks/V9ZjaxiY.js","../chunks/BID1NNRh.js","../chunks/-aNGNaBT.js","../chunks/RobXhXPM.js","../chunks/Dfvzj6n2.js","../chunks/xIq_fFFM.js","../chunks/BZrG8lR6.js","../assets/Text.j9l35qB0.css","../chunks/DCkHk5n_.js","../chunks/BC0iYSWx.js","../assets/Path.CvpwNZ6g.css","../chunks/BJngAqxk.js","../chunks/B5KRA4hC.js","../chunks/BR2LaRms.js","../chunks/CRTcXoMo.js","../chunks/vDgBJUjM.js","../assets/Rect.CtRaGMmQ.css","../chunks/DMoYijy0.js","../assets/Line.fkmsECm9.css","../chunks/D0Ty6LN0.js","../assets/Circle.C5MKzgk2.css","../chunks/BnKf2MvA.js","../assets/2.DoCaRy3T.css"])))=>i.map(i=>d[i]);
2
+ import{$ as e,B as t,Ct as n,E as r,F as i,G as a,I as o,L as s,N as c,_t as l,dt as u,et as d,ft as f,gt as p,i as m,it as h,j as g,lt as _,n as v,ot as y,q as b,r as x,rt as S,s as C,tt as w}from"../chunks/D2BnX0Uk.js";import{t as T}from"../chunks/HclGiUj8.js";import"../chunks/xihTtKlq.js";var E={},D=s(`<div id="svelte-announcer" aria-live="assertive" aria-atomic="true" style="position: absolute; left: 0; top: 0; clip: rect(0 0 0 0); clip-path: inset(50%); overflow: hidden; white-space: nowrap; width: 1px; height: 1px"><!></div>`),O=s(`<!> <!>`,1);function k(s,x){l(x,!0);let T=m(x,`components`,23,()=>[]),E=m(x,`data_0`,3,null),k=m(x,`data_1`,3,null);w(()=>x.stores.page.set(x.page)),d(()=>{x.stores,x.page,x.constructors,T(),x.form,E(),k(),x.stores.page.notify()});let A=u(!1),j=u(!1),M=u(null);v(()=>{let e=x.stores.page.subscribe(()=>{a(A)&&(_(j,!0),b().then(()=>{_(M,document.title||`untitled page`,!0)}))});return _(A,!0),e});let N=f(()=>x.constructors[1]);var P=O(),F=h(P),I=e=>{let t=f(()=>x.constructors[0]);var n=o(),s=h(n);r(s,()=>a(t),(e,t)=>{C(t(e,{get data(){return E()},get form(){return x.form},get params(){return x.page.params},children:(e,t)=>{var n=o(),s=h(n);r(s,()=>a(N),(e,t)=>{C(t(e,{get data(){return k()},get form(){return x.form},get params(){return x.page.params}}),e=>T()[1]=e,()=>T()?.[1])}),i(e,n)},$$slots:{default:!0}}),e=>T()[0]=e,()=>T()?.[0])}),i(e,n)},L=e=>{let t=f(()=>x.constructors[0]);var n=o(),s=h(n);r(s,()=>a(t),(e,t)=>{C(t(e,{get data(){return E()},get form(){return x.form},get params(){return x.page.params}}),e=>T()[0]=e,()=>T()?.[0])}),i(e,n)};g(F,e=>{x.constructors[1]?e(I):e(L,-1)});var R=y(F,2),z=r=>{var o=D(),s=S(o),l=n=>{var r=t();e(()=>c(r,a(M))),i(n,r)};g(s,e=>{a(j)&&e(l)}),n(o),i(r,o)};g(R,e=>{a(A)&&e(z)}),i(s,P),p()}var A=x(k),j=[()=>T(()=>import(`../nodes/0.UTMEigHJ.js`),__vite__mapDeps([0,1,2,3]),import.meta.url),()=>T(()=>import(`../nodes/1.Biso6Rq1.js`),__vite__mapDeps([4,1,5,6,2]),import.meta.url),()=>T(()=>import(`../nodes/2.CQMIFodk.js`),__vite__mapDeps([7,1,8,2,9,6,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44]),import.meta.url)],M=[],N={"/":[2]},P={handleError:(({error:e})=>{console.error(e)}),reroute:(()=>{}),transport:{}},F=Object.fromEntries(Object.entries(P.transport).map(([e,t])=>[e,t.decode])),I=Object.fromEntries(Object.entries(P.transport).map(([e,t])=>[e,t.encode])),L=!1,R=(e,t)=>F[e](t),z=()=>T(()=>import(`../chunks/Bjy-W4x2.js`).then(e=>e.default),[],import.meta.url);export{R as decode,F as decoders,N as dictionary,I as encoders,z as get_error_template,L as hash,P as hooks,E as matchers,j as nodes,A as root,M as server_loads};
@@ -0,0 +1 @@
1
+ import{i as e,t}from"../chunks/D1DPCGav.js";export{e as load_css,t as start};
@@ -1 +1 @@
1
- import{$ as e,F as t,L as n,N as r,_t as i,at as a,gt as o,it as s,ot as c}from"../chunks/D2BnX0Uk.js";import{r as l}from"../chunks/FH_3TS2H.js";import"../chunks/xihTtKlq.js";var u={get data(){return l.data},get error(){return l.error},get form(){return l.form},get params(){return l.params},get route(){return l.route},get state(){return l.state},get status(){return l.status},get url(){return l.url}},d=n(`<h1> </h1> <p> </p>`,1);function f(n,l){i(l,!0);var f=d(),p=s(f),m=a(p,!0),h=c(p,2),g=a(h,!0);e(()=>{r(m,u.status),r(g,u.error?.message)}),t(n,f),o()}export{f as component};
1
+ import{$ as e,F as t,L as n,N as r,_t as i,at as a,gt as o,it as s,ot as c}from"../chunks/D2BnX0Uk.js";import{r as l}from"../chunks/D1DPCGav.js";import"../chunks/xihTtKlq.js";var u={get data(){return l.data},get error(){return l.error},get form(){return l.form},get params(){return l.params},get route(){return l.route},get state(){return l.state},get status(){return l.status},get url(){return l.url}},d=n(`<h1> </h1> <p> </p>`,1);function f(n,l){i(l,!0);var f=d(),p=s(f),m=a(p,!0),h=c(p,2),g=a(h,!0);e(()=>{r(m,u.status),r(g,u.error?.message)}),t(n,f),o()}export{f as component};
@@ -1 +1 @@
1
- {"version":"1788197150274"}
1
+ {"version":"1788238916385"}
@@ -6,11 +6,11 @@
6
6
  <!-- A dashboard bound to loopback has no icon to serve; an empty data URL
7
7
  answers the browser's request without a round trip that would 404. -->
8
8
  <link rel="icon" href="data:," />
9
- <link href="./_app/immutable/entry/start.B2ycr8Ji.js" rel="modulepreload">
10
- <link href="./_app/immutable/chunks/FH_3TS2H.js" rel="modulepreload">
9
+ <link href="./_app/immutable/entry/start.Dbi2RtrK.js" rel="modulepreload">
10
+ <link href="./_app/immutable/chunks/D1DPCGav.js" rel="modulepreload">
11
11
  <link href="./_app/immutable/chunks/D2BnX0Uk.js" rel="modulepreload">
12
12
  <link href="./_app/immutable/chunks/B3vZDoek.js" rel="modulepreload">
13
- <link href="./_app/immutable/entry/app.cy9eKSTf.js" rel="modulepreload">
13
+ <link href="./_app/immutable/entry/app.bmQ6qGMT.js" rel="modulepreload">
14
14
  <link href="./_app/immutable/chunks/HclGiUj8.js" rel="modulepreload">
15
15
  <link href="./_app/immutable/chunks/xihTtKlq.js" rel="modulepreload">
16
16
  <link href="./_app/immutable/nodes/0.UTMEigHJ.js" rel="modulepreload">
@@ -63,15 +63,15 @@
63
63
 
64
64
  <script>
65
65
  {
66
- __sveltekit_ws17h5 = {
66
+ __sveltekit_1447kmy = {
67
67
  base: new URL(".", location).pathname.slice(0, -1)
68
68
  };
69
69
 
70
70
  const element = document.currentScript.parentElement;
71
71
 
72
72
  Promise.all([
73
- import("./_app/immutable/entry/start.B2ycr8Ji.js"),
74
- import("./_app/immutable/entry/app.cy9eKSTf.js")
73
+ import("./_app/immutable/entry/start.Dbi2RtrK.js"),
74
+ import("./_app/immutable/entry/app.bmQ6qGMT.js")
75
75
  ]).then(([kit, app]) => {
76
76
  kit.start(app, element);
77
77
  });
@@ -72,6 +72,25 @@ export interface IcebergDestinationInput {
72
72
  * without this processor would fail every record with nothing surfacing as an error.
73
73
  */
74
74
  readonly transformLambdaArn: string;
75
+ /**
76
+ * The CloudWatch log group Firehose writes its delivery errors into, sent as
77
+ * `CloudWatchLoggingOptions.LogGroupName`. Firehose creates neither the group nor the
78
+ * stream inside it, so both have to exist before a record can fail into them.
79
+ *
80
+ * Required rather than optional, for {@link STREAM_APPEND_ONLY}'s reason: this client
81
+ * builds exactly one shape of destination and always enables logging, so an optional
82
+ * field would model a call site that does not exist. A destination that quietly
83
+ * omitted the options is the failure this field exists to remove - one where the only
84
+ * account of an undelivered record is the record's absence.
85
+ */
86
+ readonly logGroupName: string;
87
+ /**
88
+ * The log stream inside {@link IcebergDestinationInput.logGroupName}, sent as
89
+ * `CloudWatchLoggingOptions.LogStreamName`. Named explicitly because enabling logging
90
+ * through the API - rather than through the console - creates neither the group nor
91
+ * the stream, so the caller owns both names.
92
+ */
93
+ readonly logStreamName: string;
75
94
  }
76
95
  /**
77
96
  * A delivery stream's lifecycle state in this repo's vocabulary rather than the
@@ -85,7 +104,7 @@ export type DeliveryState = 'creating' | 'create-failed' | 'active' | 'deleting'
85
104
  * The narrow view of `DescribeDeliveryStream` that `analytics status` needs, plus the
86
105
  * three fields {@link FirehoseClient.updateDestination} cannot be called without.
87
106
  *
88
- * The last three are optional and are set only when the response actually carried them.
107
+ * The last four are optional and are set only when the response actually carried them.
89
108
  * `VersionId` and `Destinations` are documented as required members of
90
109
  * `DeliveryStreamDescription`, so on the service's own response model they are always
91
110
  * there - but an absent one must reach the caller as absent rather than as `''` or
@@ -124,6 +143,18 @@ export interface DeliveryStreamStatus {
124
143
  * that omits the flag.
125
144
  */
126
145
  readonly appendOnly?: boolean | undefined;
146
+ /**
147
+ * Whether the live destination has CloudWatch error logging switched on, off
148
+ * `Destinations[0].IcebergDestinationDescription.CloudWatchLoggingOptions.Enabled`.
149
+ *
150
+ * Read back rather than assumed, for {@link DeliveryStreamStatus.appendOnly}'s reason:
151
+ * it is the second flag the stream node compares to decide whether the destination
152
+ * needs reconciling, and assuming a stream this plugin created carries the
153
+ * configuration this plugin sends today is exactly wrong for every stream created
154
+ * before it sent this one. Absent for a destination that is not an Iceberg one, or for
155
+ * a response that reports no logging options at all.
156
+ */
157
+ readonly loggingEnabled?: boolean | undefined;
127
158
  }
128
159
  /**
129
160
  * The current version and destination id `UpdateDestination` conditions on - the two
@@ -139,6 +139,15 @@ function buildIcebergDestination(input) {
139
139
  ErrorOutputPrefix: input.errorOutputPrefix,
140
140
  },
141
141
  AppendOnly: STREAM_APPEND_ONLY,
142
+ // Firehose's only account of a record it could not deliver. Off by default, and
143
+ // `IcebergDestinationConfiguration` and `IcebergDestinationUpdate` both accept the
144
+ // member, so the one builder enables it on the create and on the reconcile alike -
145
+ // an existing stream is switched on in place rather than replaced.
146
+ CloudWatchLoggingOptions: {
147
+ Enabled: true,
148
+ LogGroupName: input.logGroupName,
149
+ LogStreamName: input.logStreamName,
150
+ },
142
151
  BufferingHints: {
143
152
  IntervalInSeconds: input.bufferIntervalSeconds,
144
153
  SizeInMBs: input.bufferSizeMb,
@@ -240,6 +249,7 @@ export class FirehoseClient {
240
249
  const versionId = description?.VersionId;
241
250
  const destinationId = destination?.DestinationId;
242
251
  const appendOnly = destination?.IcebergDestinationDescription?.AppendOnly;
252
+ const loggingEnabled = destination?.IcebergDestinationDescription?.CloudWatchLoggingOptions?.Enabled;
243
253
  return {
244
254
  name: description?.DeliveryStreamName ?? name,
245
255
  arn: description?.DeliveryStreamARN ?? '',
@@ -248,6 +258,7 @@ export class FirehoseClient {
248
258
  ...(versionId !== undefined ? { versionId } : {}),
249
259
  ...(destinationId !== undefined ? { destinationId } : {}),
250
260
  ...(appendOnly !== undefined ? { appendOnly } : {}),
261
+ ...(loggingEnabled !== undefined ? { loggingEnabled } : {}),
251
262
  };
252
263
  }
253
264
  catch (err) {
package/dist/commands.js CHANGED
@@ -172,7 +172,7 @@ function logStreamHealth(ctx, entries) {
172
172
  * A failed read degrades to a warning, which is the whole point of doing it
173
173
  * last: the table is the one part of this listing that needs credentials the
174
174
  * node reads do not (the vendor library attaches the catalog itself), so an
175
- * operator with no session still gets all twelve nodes and the stream's health.
175
+ * operator with no session still gets all fourteen nodes and the stream's health.
176
176
  */
177
177
  async function logRowCount(ctx, query) {
178
178
  const config = resolveAnalyticsConfig(ctx);
package/dist/nodes.d.ts CHANGED
@@ -3,24 +3,29 @@
3
3
  * CloudFront-logs-to-Iceberg pipeline is built from and nothing else: the
4
4
  * site's own bucket, distribution and log group stay in the CLI's graph
5
5
  * (`packages/cli/src/nodes.ts`) and are never touched from here. This module
6
- * carries all twelve of them, in four chains. The table chain - the S3 Tables bucket,
6
+ * carries all fourteen of them, in four chains. The table chain - the S3 Tables bucket,
7
7
  * the namespace inside it, the `page_views` table, and the Glue federation
8
8
  * Firehose reads that table through - runs `analytics-table-bucket` ->
9
9
  * `analytics-namespace` -> `analytics-table` ->
10
10
  * `analytics-catalog-integration`. The transform chain - the long-lived
11
- * `visitor_key` salt, the Lambda execution role whose policy names that
12
- * secret's ARN, and the record-transform function itself - runs
13
- * `analytics-salt-secret` -> `analytics-transform-role` ->
14
- * `analytics-transform-function`. The delivery chain - the bucket every record
15
- * Firehose cannot deliver lands in, the role it assumes, and the stream itself
16
- * - runs `analytics-error-bucket` -> `analytics-firehose-role` ->
17
- * `analytics-firehose-stream`, and joins the other two chains through the
18
- * role's four grants and the stream's destination. The vended-delivery chain -
11
+ * `visitor_key` salt, the log group the function's own output lands in, the
12
+ * Lambda execution role whose policy names that secret's ARN, and the
13
+ * record-transform function itself - runs `analytics-salt-secret` ->
14
+ * `analytics-transform-role` -> `analytics-transform-function`, with
15
+ * `analytics-transform-log-group` a second edge into that function. The
16
+ * delivery chain - the bucket every record Firehose cannot deliver lands in,
17
+ * the log group Firehose's delivery errors land in, the role it assumes, and
18
+ * the stream itself - runs `analytics-error-bucket` ->
19
+ * `analytics-firehose-role` -> `analytics-firehose-stream`, with
20
+ * `analytics-firehose-log-group` a second edge into that stream, and joins the
21
+ * other two chains through the role's five grants and the stream's
22
+ * destination. The vended-delivery chain -
19
23
  * the CloudWatch delivery destination pointing at that stream and the delivery
20
24
  * joining it to the site's log source - runs `analytics-log-destination` ->
21
25
  * `analytics-log-delivery` and hangs off the stream. All four are wired through
22
26
  * `dependsOn`, and a node depends on every node whose recorded ARN it
23
- * interpolates. {@link buildAnalyticsNodes} at the foot of this module returns
27
+ * interpolates - plus the two writers whose log group has to exist before the
28
+ * first line they write, which is an ordering with nothing read back. {@link buildAnalyticsNodes} at the foot of this module returns
24
29
  * the assembled set, and `plugin.ts` hands it to the SPI's `Plugin.nodes`;
25
30
  * assembling an array is all it does - nothing here reconciles anything.
26
31
  *
@@ -41,8 +46,8 @@
41
46
  * over the host's `signingUsEast1` signer; no node here picks a region for a
42
47
  * request. {@link ANALYTICS_REGION} below is the same region as *text*, needed
43
48
  * only because an ARN spells its region out and because every node `title`
44
- * states the pin, so the bootstrap output an operator reads carries it. Ten of
45
- * the twelve titles state it as the region they are created in; the two IAM
49
+ * states the pin, so the bootstrap output an operator reads carries it. Twelve
50
+ * of the fourteen titles state it as the region they are created in; the two IAM
46
51
  * role nodes state it as the pipeline they serve, because IAM is global and
47
52
  * "created in us-east-1" is not a property a role has (§Region pinning says so
48
53
  * in as many words) - a title claiming otherwise would be the pin stated
@@ -80,7 +85,7 @@ type AnalyticsContext = PluginContext<AnalyticsConfig>;
80
85
  /** One node of the analytics graph. */
81
86
  type AnalyticsNode = ResourceNode<AnalyticsContext>;
82
87
  /**
83
- * The `analytics-firehose-stream` node id. Exported, alone among the twelve,
88
+ * The `analytics-firehose-stream` node id. Exported, alone among the fourteen,
84
89
  * because `analytics status` reads this node's recorded outputs back out of
85
90
  * the scoped state its `read` hydrated - the stream's delivery health - and a
86
91
  * second copy of the string in `commands.ts` would be a state key with two
@@ -163,6 +168,22 @@ export declare function analyticsCatalogIntegrationNode(): AnalyticsNode;
163
168
  * anything had changed. The teardown says what it kept and how to remove it.
164
169
  */
165
170
  export declare function analyticsSaltSecretNode(): AnalyticsNode;
171
+ /**
172
+ * The transform Lambda's own log group: the mapping decisions, the drop path,
173
+ * and the cold-start read of the salt secret - the only place this pipeline
174
+ * says *why* a record went where it did.
175
+ *
176
+ * Lambda writes into it under the execution role's existing
177
+ * `logs:CreateLogStream` and `logs:PutLogEvents`, scoped to this group and no
178
+ * other ({@link transformLogGroupArn}). The role is deliberately not granted
179
+ * `logs:CreateLogGroup`, because this node is what creates the group.
180
+ *
181
+ * It is at the head of the chain that writes to it -
182
+ * `analytics-transform-function` declares the edge - and the role does not,
183
+ * because the role's policy *derives* this group's ARN from the function's name
184
+ * rather than reading a recorded one, so there is no output to wait for.
185
+ */
186
+ export declare function analyticsTransformLogGroupNode(): AnalyticsNode;
166
187
  /**
167
188
  * The transform Lambda's execution role: permission to write its own logs and
168
189
  * to read the one secret it needs, and nothing else.
@@ -226,7 +247,11 @@ export declare function transformUpdate(recorded: {
226
247
  * artifact's name, even though the zip travels inline rather than through a
227
248
  * bucket (see {@link MAX_INLINE_ZIP_BYTES} for why inline).
228
249
  *
229
- * It depends on `analytics-transform-role`, whose recorded ARN it runs as.
250
+ * It depends on `analytics-transform-role`, whose recorded ARN it runs as, and
251
+ * on `analytics-transform-log-group`, whose ARN it reads nothing of: a group
252
+ * that does not exist when the function first runs is a log line lost with
253
+ * nothing raised, and on teardown the reverse order the engine walks removes
254
+ * the function before the group holding the evidence of what it did.
230
255
  */
231
256
  export declare function analyticsTransformFunctionNode(): AnalyticsNode;
232
257
  /**
@@ -257,10 +282,32 @@ export declare function analyticsTransformFunctionNode(): AnalyticsNode;
257
282
  * transform did not run on them - inside a bucket the site serves from.
258
283
  */
259
284
  export declare function analyticsErrorBucketNode(): AnalyticsNode;
285
+ /**
286
+ * The log group Firehose writes its delivery errors to, and the log stream
287
+ * inside it that they are written to.
288
+ *
289
+ * **Firehose creates neither.** Enabling error logging through the API rather
290
+ * than the console requires the group *and* the stream to exist in advance, so
291
+ * this node creates both - and re-ensures the stream on every `update` beside
292
+ * the retention, which is why {@link analyticsLogGroupNode} takes a stream at
293
+ * all. Without that, a group left by a run that stopped between
294
+ * `CreateLogGroup` and `CreateLogStream` is permanently one call short, with
295
+ * `read()` reporting it present and `update()` doing nothing about it, and the
296
+ * symptom is a delivery failure with nowhere to be explained.
297
+ *
298
+ * {@link DESTINATION_DELIVERY_STREAM} is the only stream created:
299
+ * `BackupDelivery` belongs to a destination configured with S3 backup, and the
300
+ * Iceberg destination this pipeline builds configures none.
301
+ *
302
+ * It is at the head of the chain that writes to it -
303
+ * `analytics-firehose-stream` declares the edge - while the delivery role does
304
+ * not, for the reason `analytics-transform-log-group`'s counterpart does not.
305
+ */
306
+ export declare function analyticsFirehoseLogGroupNode(): AnalyticsNode;
260
307
  /**
261
308
  * The role Firehose assumes to read the catalog, write the table, invoke the
262
- * transform and store what it could not deliver - four grants, four concrete
263
- * resources, no `*`.
309
+ * transform, store what it could not deliver and say so in one log stream -
310
+ * five grants, five concrete resources, no `*`.
264
311
  *
265
312
  * It declares `dependsOn` on the three nodes whose recorded ARNs those grants
266
313
  * interpolate. `topoSort` drains zero-indegree nodes alphabetically
@@ -366,7 +413,7 @@ export declare function analyticsLogDestinationNode(): AnalyticsNode;
366
413
  */
367
414
  export declare function analyticsLogDeliveryNode(): AnalyticsNode;
368
415
  /**
369
- * The plugin's twelve resource nodes, assembled in the order the change spec's
416
+ * The plugin's fourteen resource nodes, assembled in the order the change spec's
370
417
  * §Analytics pipeline → Resource nodes table lists them. This is what
371
418
  * `Plugin.nodes` (`plugin.ts`) hands the CLI's generic `analytics bootstrap`
372
419
  * and `analytics destroy` verbs, and it is the whole of what this package
@@ -387,7 +434,7 @@ export declare function analyticsLogDeliveryNode(): AnalyticsNode;
387
434
  *
388
435
  * **No `ctx` parameter, deliberately.** The SPI declares `nodes?(ctx)` and the
389
436
  * CLI calls it with one, so this function is assignable to it as written - a
390
- * zero-argument function satisfies a one-argument signature. None of the twelve
437
+ * zero-argument function satisfies a one-argument signature. None of the fourteen
391
438
  * factories needs a context to be *built*: each reads `ctx` inside `read`,
392
439
  * `create`, `update` and `delete`, when the reconcile is actually running. A
393
440
  * parameter accepted and ignored here would be an unused binding and, worse, a
package/dist/nodes.js CHANGED
@@ -3,24 +3,29 @@
3
3
  * CloudFront-logs-to-Iceberg pipeline is built from and nothing else: the
4
4
  * site's own bucket, distribution and log group stay in the CLI's graph
5
5
  * (`packages/cli/src/nodes.ts`) and are never touched from here. This module
6
- * carries all twelve of them, in four chains. The table chain - the S3 Tables bucket,
6
+ * carries all fourteen of them, in four chains. The table chain - the S3 Tables bucket,
7
7
  * the namespace inside it, the `page_views` table, and the Glue federation
8
8
  * Firehose reads that table through - runs `analytics-table-bucket` ->
9
9
  * `analytics-namespace` -> `analytics-table` ->
10
10
  * `analytics-catalog-integration`. The transform chain - the long-lived
11
- * `visitor_key` salt, the Lambda execution role whose policy names that
12
- * secret's ARN, and the record-transform function itself - runs
13
- * `analytics-salt-secret` -> `analytics-transform-role` ->
14
- * `analytics-transform-function`. The delivery chain - the bucket every record
15
- * Firehose cannot deliver lands in, the role it assumes, and the stream itself
16
- * - runs `analytics-error-bucket` -> `analytics-firehose-role` ->
17
- * `analytics-firehose-stream`, and joins the other two chains through the
18
- * role's four grants and the stream's destination. The vended-delivery chain -
11
+ * `visitor_key` salt, the log group the function's own output lands in, the
12
+ * Lambda execution role whose policy names that secret's ARN, and the
13
+ * record-transform function itself - runs `analytics-salt-secret` ->
14
+ * `analytics-transform-role` -> `analytics-transform-function`, with
15
+ * `analytics-transform-log-group` a second edge into that function. The
16
+ * delivery chain - the bucket every record Firehose cannot deliver lands in,
17
+ * the log group Firehose's delivery errors land in, the role it assumes, and
18
+ * the stream itself - runs `analytics-error-bucket` ->
19
+ * `analytics-firehose-role` -> `analytics-firehose-stream`, with
20
+ * `analytics-firehose-log-group` a second edge into that stream, and joins the
21
+ * other two chains through the role's five grants and the stream's
22
+ * destination. The vended-delivery chain -
19
23
  * the CloudWatch delivery destination pointing at that stream and the delivery
20
24
  * joining it to the site's log source - runs `analytics-log-destination` ->
21
25
  * `analytics-log-delivery` and hangs off the stream. All four are wired through
22
26
  * `dependsOn`, and a node depends on every node whose recorded ARN it
23
- * interpolates. {@link buildAnalyticsNodes} at the foot of this module returns
27
+ * interpolates - plus the two writers whose log group has to exist before the
28
+ * first line they write, which is an ordering with nothing read back. {@link buildAnalyticsNodes} at the foot of this module returns
24
29
  * the assembled set, and `plugin.ts` hands it to the SPI's `Plugin.nodes`;
25
30
  * assembling an array is all it does - nothing here reconciles anything.
26
31
  *
@@ -41,8 +46,8 @@
41
46
  * over the host's `signingUsEast1` signer; no node here picks a region for a
42
47
  * request. {@link ANALYTICS_REGION} below is the same region as *text*, needed
43
48
  * only because an ARN spells its region out and because every node `title`
44
- * states the pin, so the bootstrap output an operator reads carries it. Ten of
45
- * the twelve titles state it as the region they are created in; the two IAM
49
+ * states the pin, so the bootstrap output an operator reads carries it. Twelve
50
+ * of the fourteen titles state it as the region they are created in; the two IAM
46
51
  * role nodes state it as the pipeline they serve, because IAM is global and
47
52
  * "created in us-east-1" is not a property a role has (§Region pinning says so
48
53
  * in as many words) - a title claiming otherwise would be the pin stated
@@ -85,16 +90,30 @@ const TABLE_NODE = 'analytics-table';
85
90
  const CATALOG_NODE = 'analytics-catalog-integration';
86
91
  /** The `analytics-salt-secret` node id, shared by its `id`, its state key and the edge into it. */
87
92
  const SALT_SECRET_NODE = 'analytics-salt-secret';
93
+ /**
94
+ * The `analytics-transform-log-group` node id. The group the transform Lambda
95
+ * writes its own output to, owned here rather than left to the implicit
96
+ * creation Lambda would otherwise do on first invocation - which is what makes
97
+ * its retention a property at all.
98
+ */
99
+ const TRANSFORM_LOG_GROUP_NODE = 'analytics-transform-log-group';
88
100
  /** The `analytics-transform-role` node id. */
89
101
  const TRANSFORM_ROLE_NODE = 'analytics-transform-role';
90
102
  /** The `analytics-transform-function` node id. */
91
103
  const TRANSFORM_FUNCTION_NODE = 'analytics-transform-function';
92
104
  /** The `analytics-error-bucket` node id, shared by its `id`, its state key and the edge into it. */
93
105
  const ERROR_BUCKET_NODE = 'analytics-error-bucket';
106
+ /**
107
+ * The `analytics-firehose-log-group` node id. The group Firehose writes its
108
+ * delivery errors to, and the one node in this module that owns a log stream as
109
+ * well as a group: enabling error logging through the API requires both to
110
+ * exist in advance.
111
+ */
112
+ const FIREHOSE_LOG_GROUP_NODE = 'analytics-firehose-log-group';
94
113
  /** The `analytics-firehose-role` node id. */
95
114
  const FIREHOSE_ROLE_NODE = 'analytics-firehose-role';
96
115
  /**
97
- * The `analytics-firehose-stream` node id. Exported, alone among the twelve,
116
+ * The `analytics-firehose-stream` node id. Exported, alone among the fourteen,
98
117
  * because `analytics status` reads this node's recorded outputs back out of
99
118
  * the scoped state its `read` hydrated - the stream's delivery health - and a
100
119
  * second copy of the string in `commands.ts` would be a state key with two
@@ -649,9 +668,12 @@ const ZIP_MTIME = REPRODUCIBLE_ZIP_MTIME;
649
668
  /** The deflate level `packageAndUploadAgent` uses. */
650
669
  const ZIP_LEVEL = 6;
651
670
  /**
652
- * The prefix Lambda derives a function's log group from. The group itself is
653
- * created by the Lambda service on first invocation and by no node in this
654
- * graph - see {@link transformLogGroupArn}.
671
+ * The prefix Lambda derives a function's log group from, and therefore the
672
+ * prefix {@link transformLogGroupName} builds the one group this plugin owns
673
+ * for its function from. `analytics-transform-log-group` creates that group; it
674
+ * is not left to the implicit creation the Lambda service performs on a
675
+ * function's first invocation, because a group created that way is retained
676
+ * forever and carries none of the environment's tags.
655
677
  */
656
678
  const LAMBDA_LOG_GROUP_PREFIX = '/aws/lambda/';
657
679
  /** The name of the inline policy this plugin puts on its own transform role. */
@@ -763,10 +785,25 @@ function transformFunctionName(ctx) {
763
785
  function transformRoleName(ctx) {
764
786
  return boundedName(`${ctx.names.prefix}${TRANSFORM_ROLE_SUFFIX}`, ROLE_NAME_MAX_LENGTH, 'transform role');
765
787
  }
788
+ /**
789
+ * The transform Lambda's log group name - **the one home this string has.**
790
+ *
791
+ * `analytics-transform-log-group` creates the group under this name and
792
+ * {@link transformLogGroupArn} scopes the role's grant to the ARN built from
793
+ * it, so the group that exists and the group the function is allowed to write
794
+ * to are one string by construction rather than two literals that happen to
795
+ * agree. Derived from {@link transformFunctionName} rather than re-derived from
796
+ * `ctx.names.prefix` for the same reason: Lambda's own group name is that
797
+ * function's name under {@link LAMBDA_LOG_GROUP_PREFIX}, so a second derivation
798
+ * could name a group Lambda never writes to.
799
+ */
800
+ function transformLogGroupName(ctx) {
801
+ return `${LAMBDA_LOG_GROUP_PREFIX}${transformFunctionName(ctx)}`;
802
+ }
766
803
  /**
767
804
  * The log group ARN the role's `logs:` grant is scoped to - the function's
768
- * **own** group and nothing else, the scoping
769
- * `packages/cli/src/nodes.ts:212` applies to the site's exec role.
805
+ * **own** group and nothing else, the scoping the site's exec role applies
806
+ * (`packages/cli/src/nodes.ts`).
770
807
  *
771
808
  * The region is {@link ANALYTICS_REGION} and not `ctx.config.region`, which is
772
809
  * the one place this ARN differs from the CLI's `logGroupArn` helper (whose
@@ -775,18 +812,31 @@ function transformRoleName(ctx) {
775
812
  * log group is too, and a grant naming the primary region would be a grant on a
776
813
  * group that never exists.
777
814
  *
778
- * **No node creates this group.** Lambda creates it implicitly on the
779
- * function's first invocation. That is worth stating because the policy below
780
- * grants `logs:CreateLogStream` and `logs:PutLogEvents` and *not*
781
- * `logs:CreateLogGroup`: the transform's own diagnostics therefore depend on
782
- * that implicit creation succeeding, and the pipeline's real failure signal is
783
- * elsewhere - a record the transform cannot map goes to Firehose's error prefix
784
- * (`transform/handler.ts`), and a batch that throws raises Firehose's own error
785
- * metric. Adding the group as a node of its own, with the retention the site's
786
- * log groups carry, is a coherent follow-up and is outside this node set.
815
+ * **`analytics-transform-log-group` creates this group**, which is why the
816
+ * policy below grants `logs:CreateLogStream` and `logs:PutLogEvents` and *not*
817
+ * `logs:CreateLogGroup`: the role has nothing to create, the shape the site's
818
+ * exec role has. An earlier version of this comment said no node creates the
819
+ * group and that Lambda's implicit creation on the function's first invocation
820
+ * was enough; production disproved it - the group never appeared, the transform
821
+ * ran and reported nothing, and the pipeline's record-level signals (a record
822
+ * the transform cannot map goes to Firehose's error prefix,
823
+ * `transform/handler.ts`, and a batch that throws raises Firehose's own error
824
+ * metric) answered *which* and never *why*.
787
825
  */
788
826
  function transformLogGroupArn(ctx) {
789
- const group = `${LAMBDA_LOG_GROUP_PREFIX}${transformFunctionName(ctx)}`;
827
+ return analyticsLogGroupArn(ctx, transformLogGroupName(ctx));
828
+ }
829
+ /**
830
+ * A log group's ARN in {@link ANALYTICS_REGION}, in the `:*` form both an IAM
831
+ * grant and this plugin's recorded outputs use.
832
+ *
833
+ * Takes no region parameter, which is the one way it differs from the CLI's own
834
+ * `logGroupArn` (`packages/cli/src/nodes.ts`) - that helper defaults to
835
+ * `ctx.config.region`, correctly, because the site's groups live there. Every
836
+ * group this plugin names lives in us-east-1 with the rest of the pipeline, so
837
+ * there is no region for a caller to choose and no default for one to forget.
838
+ */
839
+ function analyticsLogGroupArn(ctx, group) {
790
840
  return `arn:aws:logs:${ANALYTICS_REGION}:${ctx.accountId}:log-group:${group}:*`;
791
841
  }
792
842
  /**
@@ -856,8 +906,13 @@ function requireTransformRoleArn(ctx) {
856
906
  * without a teardown.
857
907
  *
858
908
  * **Two statements, two concrete resources, no `*` anywhere.** The `logs`
859
- * statement names the function's own log group ({@link transformLogGroupArn});
860
- * the `secretsmanager` statement names the one secret this pipeline owns and
909
+ * statement names the function's own log group ({@link transformLogGroupArn})
910
+ * and carries two actions rather than three: `logs:CreateLogGroup` is
911
+ * deliberately absent, because `analytics-transform-log-group` creates that
912
+ * group and a role granted the creation of a group it never has to create is a
913
+ * grant with no call behind it - the same shape the site's exec role has. What
914
+ * Lambda needs at runtime is the stream inside that group and the events in it.
915
+ * The `secretsmanager` statement names the one secret this pipeline owns and
861
916
  * nothing else. A `*` in the second would hand every secret in the account -
862
917
  * every other environment's salt, and `blogwright-pds`'s OAuth client key and
863
918
  * live session - to a role whose only job is to read one value, and nothing in
@@ -1024,6 +1079,100 @@ export function analyticsSaltSecretNode() {
1024
1079
  },
1025
1080
  };
1026
1081
  }
1082
+ /**
1083
+ * A CloudWatch log group this plugin owns, on the read/create/update/delete
1084
+ * contract the site's own groups have (`logGroupNode`,
1085
+ * `packages/cli/src/nodes.ts`): `read` reports presence and records the ARN,
1086
+ * `create` ensures the group with the environment's tags and then applies
1087
+ * {@link LOG_RETENTION_DAYS}, `update` re-applies that retention on every
1088
+ * apply, and `delete` removes the group.
1089
+ *
1090
+ * Owning the group is what makes retention a property at all. A group a service
1091
+ * creates for itself is retained **forever** and no reconcile ever notices;
1092
+ * re-applying the policy on every `update` also converts a group that already
1093
+ * exists in that state - an environment provisioned before these two nodes
1094
+ * existed - without a teardown.
1095
+ *
1096
+ * `stream`, when given, is a log stream the group must also hold. It is ensured
1097
+ * on `create` **and re-ensured on every `update`**, which is the one place these
1098
+ * nodes depart from the site's `logGroupNode` and is the
1099
+ * {@link applyErrorBucketConfiguration} reconcile-on-every-apply pattern rather
1100
+ * than the site's narrower update. The reason is concrete: `read()` answers on
1101
+ * the group alone, so a group left by a run that stopped between
1102
+ * `CreateLogGroup` and `CreateLogStream` reports present forever while an
1103
+ * `update` that only re-applied retention would do nothing about the stream it
1104
+ * is missing. Deleting a group deletes the streams inside it, so `delete` needs
1105
+ * no counterpart.
1106
+ *
1107
+ * `dependsOn: []` on both: a group is the head of the chain that writes to it
1108
+ * and reads no other node's output.
1109
+ */
1110
+ function analyticsLogGroupNode(spec) {
1111
+ const { id, title, name, stream } = spec;
1112
+ const record = (ctx, group) => {
1113
+ const out = output(ctx, id);
1114
+ out.name = group;
1115
+ out.arn = analyticsLogGroupArn(ctx, group);
1116
+ };
1117
+ return {
1118
+ id,
1119
+ dependsOn: [],
1120
+ title,
1121
+ async read(ctx) {
1122
+ const group = name(ctx);
1123
+ if (!(await logs(ctx).logGroupExists(group)))
1124
+ return false;
1125
+ record(ctx, group);
1126
+ return true;
1127
+ },
1128
+ async create(ctx) {
1129
+ const group = name(ctx);
1130
+ const client = logs(ctx);
1131
+ await client.ensureLogGroup(group, ctx.tags);
1132
+ // Identity before the secondary calls, the ordering `analytics-error-bucket`
1133
+ // and the site's own `bucketNode` follow: a crash between CreateLogGroup and
1134
+ // the retention or the stream must still leave the group recorded in state.
1135
+ record(ctx, group);
1136
+ await client.putRetentionPolicy(group, LOG_RETENTION_DAYS);
1137
+ if (stream !== undefined)
1138
+ await client.ensureLogStream(group, stream);
1139
+ },
1140
+ async update(ctx) {
1141
+ const group = name(ctx);
1142
+ const client = logs(ctx);
1143
+ await client.putRetentionPolicy(group, LOG_RETENTION_DAYS);
1144
+ if (stream !== undefined)
1145
+ await client.ensureLogStream(group, stream);
1146
+ },
1147
+ async delete(ctx) {
1148
+ // `deleteLogGroup` swallows its own not-found, so a re-run after a
1149
+ // completed teardown is a no-op rather than a failure.
1150
+ await logs(ctx).deleteLogGroup(name(ctx));
1151
+ },
1152
+ };
1153
+ }
1154
+ /**
1155
+ * The transform Lambda's own log group: the mapping decisions, the drop path,
1156
+ * and the cold-start read of the salt secret - the only place this pipeline
1157
+ * says *why* a record went where it did.
1158
+ *
1159
+ * Lambda writes into it under the execution role's existing
1160
+ * `logs:CreateLogStream` and `logs:PutLogEvents`, scoped to this group and no
1161
+ * other ({@link transformLogGroupArn}). The role is deliberately not granted
1162
+ * `logs:CreateLogGroup`, because this node is what creates the group.
1163
+ *
1164
+ * It is at the head of the chain that writes to it -
1165
+ * `analytics-transform-function` declares the edge - and the role does not,
1166
+ * because the role's policy *derives* this group's ARN from the function's name
1167
+ * rather than reading a recorded one, so there is no output to wait for.
1168
+ */
1169
+ export function analyticsTransformLogGroupNode() {
1170
+ return analyticsLogGroupNode({
1171
+ id: TRANSFORM_LOG_GROUP_NODE,
1172
+ title: `Transform Lambda log group (${ANALYTICS_REGION})`,
1173
+ name: transformLogGroupName,
1174
+ });
1175
+ }
1027
1176
  /**
1028
1177
  * The transform Lambda's execution role: permission to write its own logs and
1029
1178
  * to read the one secret it needs, and nothing else.
@@ -1310,12 +1459,16 @@ function whileRoleIsPropagating(call) {
1310
1459
  * artifact's name, even though the zip travels inline rather than through a
1311
1460
  * bucket (see {@link MAX_INLINE_ZIP_BYTES} for why inline).
1312
1461
  *
1313
- * It depends on `analytics-transform-role`, whose recorded ARN it runs as.
1462
+ * It depends on `analytics-transform-role`, whose recorded ARN it runs as, and
1463
+ * on `analytics-transform-log-group`, whose ARN it reads nothing of: a group
1464
+ * that does not exist when the function first runs is a log line lost with
1465
+ * nothing raised, and on teardown the reverse order the engine walks removes
1466
+ * the function before the group holding the evidence of what it did.
1314
1467
  */
1315
1468
  export function analyticsTransformFunctionNode() {
1316
1469
  return {
1317
1470
  id: TRANSFORM_FUNCTION_NODE,
1318
- dependsOn: [TRANSFORM_ROLE_NODE],
1471
+ dependsOn: [TRANSFORM_ROLE_NODE, TRANSFORM_LOG_GROUP_NODE],
1319
1472
  title: `Record-transform Lambda (${ANALYTICS_REGION})`,
1320
1473
  async read(ctx) {
1321
1474
  const name = transformFunctionName(ctx);
@@ -1474,6 +1627,41 @@ const FIREHOSE_TRUST = {
1474
1627
  * that has no second case on this side of it.
1475
1628
  */
1476
1629
  const ERROR_OUTPUT_PREFIX = 'firehose-errors/';
1630
+ /**
1631
+ * Days both of this plugin's log groups retain what is written to them,
1632
+ * re-applied on every `update` the way the site's own groups re-apply theirs.
1633
+ *
1634
+ * 365, matching the site's `retention.microvmDays` default - a year of the
1635
+ * build's own output, and a year of this pipeline's. It is a plugin-owned
1636
+ * constant and **not** read from `ctx.config.retention`, deliberately: that
1637
+ * block holds exactly two keys, `microvmDays` and `cloudfrontDays`, and each
1638
+ * one names one of the site's own two log groups. A third and fourth consumer
1639
+ * reading either would make an operator's setting for a group they named
1640
+ * silently govern two resources it was never named for, so that an environment
1641
+ * shortening its CloudFront retention would shorten the transform's diagnostics
1642
+ * with it and nothing would say so. Making retention configurable per plugin is
1643
+ * a config change with a name of its own, not a key borrowed here.
1644
+ */
1645
+ const LOG_RETENTION_DAYS = 365;
1646
+ /**
1647
+ * The log stream inside {@link firehoseLogGroupName} that Firehose writes its
1648
+ * delivery errors to. Firehose's own name for that stream - enabling error
1649
+ * logging through the API names it explicitly, and the service creates neither
1650
+ * the group nor the stream.
1651
+ *
1652
+ * `BackupDelivery`, the stream Firehose uses for a destination configured with
1653
+ * S3 backup, is deliberately not created: the Iceberg destination this pipeline
1654
+ * builds configures none, so a second stream would be an empty one forever.
1655
+ */
1656
+ const DESTINATION_DELIVERY_STREAM = 'DestinationDelivery';
1657
+ /**
1658
+ * The prefix a Firehose delivery stream's log group is conventionally named
1659
+ * under, the counterpart of {@link LAMBDA_LOG_GROUP_PREFIX}. Firehose derives
1660
+ * nothing from it - it writes to whatever group the stream's
1661
+ * `CloudWatchLoggingOptions` names - so the convention is this plugin's to keep,
1662
+ * and {@link firehoseLogGroupName} is where it is kept.
1663
+ */
1664
+ const FIREHOSE_LOG_GROUP_PREFIX = '/aws/kinesisfirehose/';
1477
1665
  /**
1478
1666
  * Seconds Firehose buffers records before writing a file, and the size in MiB
1479
1667
  * that would flush one sooner. Both are sent, because the service requires the
@@ -1525,6 +1713,39 @@ function firehoseRoleName(ctx) {
1525
1713
  function streamName(ctx) {
1526
1714
  return boundedName(`${ctx.names.prefix}${FIREHOSE_STREAM_SUFFIX}`, STREAM_NAME_MAX_LENGTH, 'delivery stream');
1527
1715
  }
1716
+ /**
1717
+ * The log group Firehose writes its delivery errors to - **the one home this
1718
+ * string has**, for {@link transformLogGroupName}'s reason and with more riding
1719
+ * on it. `analytics-firehose-log-group` creates the group under this name
1720
+ * today; the delivery role's grant on the stream inside it and the stream's own
1721
+ * `CloudWatchLoggingOptions` are the two readers that follow, and each is meant
1722
+ * to reach the name through this helper rather than spell a third literal of
1723
+ * it. Firehose writes to whatever group its logging options name, so three
1724
+ * spellings would fail as an empty group rather than as an error.
1725
+ *
1726
+ * Derived from {@link streamName} rather than from `ctx.names.prefix`, so the
1727
+ * group is named after the stream whose errors it holds even if that stream's
1728
+ * suffix ever changes.
1729
+ */
1730
+ function firehoseLogGroupName(ctx) {
1731
+ return `${FIREHOSE_LOG_GROUP_PREFIX}${streamName(ctx)}`;
1732
+ }
1733
+ /**
1734
+ * The ARN of the one log stream Firehose writes its delivery errors to, which
1735
+ * the delivery role's `logs:PutLogEvents` grant is scoped to.
1736
+ *
1737
+ * Not {@link analyticsLogGroupArn}'s `:*` form, and the difference is the whole
1738
+ * point of a separate helper: the group ARN's trailing wildcard grants every
1739
+ * stream the group will ever hold, while `PutLogEvents` authorises against a
1740
+ * stream ARN and this role writes to exactly one. Both halves come from the
1741
+ * same two helpers the log-group node and the stream's own
1742
+ * `CloudWatchLoggingOptions` reach for, so the grant cannot name a stream that
1743
+ * is not the one being written to.
1744
+ */
1745
+ function firehoseLogStreamArn(ctx) {
1746
+ const group = firehoseLogGroupName(ctx);
1747
+ return `arn:aws:logs:${ANALYTICS_REGION}:${ctx.accountId}:log-group:${group}:log-stream:${DESTINATION_DELIVERY_STREAM}`;
1748
+ }
1528
1749
  /**
1529
1750
  * The error bucket's ARN as `analytics-error-bucket` recorded it. See
1530
1751
  * {@link requireRecordedArn}.
@@ -1637,13 +1858,13 @@ function glueGrantResources(ctx) {
1637
1858
  * and a table recreated under a new generated ARN reaches the policy without a
1638
1859
  * teardown.
1639
1860
  *
1640
- * **Exactly four statements, one per capability the change spec names, every
1861
+ * **Exactly five statements, one per capability the change spec names, every
1641
1862
  * `Resource` a concrete ARN and none of them `*`.** The action lists are AWS's
1642
1863
  * own, from the "Grant Firehose access to Amazon S3 Tables" policy under IAM
1643
1864
  * access control; what is narrowed is the resources, which that policy writes
1644
1865
  * with wildcards over the whole account.
1645
1866
  *
1646
- * Two of the four are easy to get subtly wrong and are worth stating:
1867
+ * Three of the five are easy to get subtly wrong and are worth stating:
1647
1868
  *
1648
1869
  * - the error-bucket statement names the bucket **and** `<bucket>/*`. Bucket
1649
1870
  * actions (`s3:ListBucket`, `s3:GetBucketLocation`) authorise against the
@@ -1658,11 +1879,19 @@ function glueGrantResources(ctx) {
1658
1879
  * unqualified invoke, so copying it would deny every transform call and send
1659
1880
  * every record to the error bucket.
1660
1881
  *
1661
- * There is no fifth statement. AWS's policy carries three more - Kinesis (this
1662
- * stream is `DirectPut`), KMS (no customer-managed key is configured anywhere
1663
- * in this pipeline) and CloudWatch Logs (no `CloudWatchLoggingOptions` is sent,
1664
- * so Firehose writes no log stream to grant on) - and each of the three is
1665
- * conditional on a feature this pipeline does not use.
1882
+ * - the CloudWatch Logs statement names the **one log stream** Firehose writes
1883
+ * its delivery errors to, `<firehose log group>:log-stream:DestinationDelivery`,
1884
+ * and not the group's `:*` form. `logs:PutLogEvents` authorises against the
1885
+ * stream ARN, and this role writes to exactly one stream, so the wildcard the
1886
+ * group ARN carries would grant every stream a future group ever holds. Only
1887
+ * `PutLogEvents`: `analytics-firehose-log-group` creates the group *and* the
1888
+ * stream, so the role has nothing to create - the shape
1889
+ * `analytics-transform-role`'s grant has, one action shorter.
1890
+ *
1891
+ * That fifth statement is the only one of the three AWS's own policy adds that
1892
+ * this pipeline needs. The other two stay out: Kinesis (this stream is
1893
+ * `DirectPut`) and KMS (no customer-managed key is configured anywhere in this
1894
+ * pipeline), each conditional on a feature this pipeline does not use.
1666
1895
  */
1667
1896
  async function applyFirehoseRolePolicy(ctx) {
1668
1897
  const errorBucketArn = requireErrorBucketArn(ctx, FIREHOSE_ROLE_NODE);
@@ -1720,6 +1949,14 @@ async function applyFirehoseRolePolicy(ctx) {
1720
1949
  ],
1721
1950
  Resource: [errorBucketArn, `${errorBucketArn}/*`],
1722
1951
  },
1952
+ {
1953
+ // Without this the destination's `CloudWatchLoggingOptions` are inert:
1954
+ // Firehose is told where to write its delivery errors and is not allowed
1955
+ // to, so the failure it was meant to explain stays as silent as before.
1956
+ Effect: 'Allow',
1957
+ Action: ['logs:PutLogEvents'],
1958
+ Resource: firehoseLogStreamArn(ctx),
1959
+ },
1723
1960
  ],
1724
1961
  });
1725
1962
  }
@@ -1754,6 +1991,12 @@ function firehoseDestination(ctx) {
1754
1991
  bufferIntervalSeconds: STREAM_BUFFER_INTERVAL_SECONDS,
1755
1992
  bufferSizeMb: STREAM_BUFFER_SIZE_MB,
1756
1993
  transformLambdaArn: requireTransformFunctionArn(ctx, FIREHOSE_STREAM_NODE),
1994
+ // Both through the helpers `analytics-firehose-log-group` creates the group and
1995
+ // the stream under, and the delivery role grants on - one string each. Firehose
1996
+ // writes to whatever group its logging options name and creates nothing, so a
1997
+ // second spelling of either would fail as an empty group rather than as an error.
1998
+ logGroupName: firehoseLogGroupName(ctx),
1999
+ logStreamName: DESTINATION_DELIVERY_STREAM,
1757
2000
  };
1758
2001
  }
1759
2002
  /**
@@ -1775,14 +2018,39 @@ function recordOptional(out, key, value) {
1775
2018
  else
1776
2019
  out[key] = value;
1777
2020
  }
2021
+ /**
2022
+ * What the reconcile is about to change, named for the operator reading the log
2023
+ * line, and only what actually differs.
2024
+ *
2025
+ * A single "AppendOnly <recorded> -> <desired>" would report a transition on
2026
+ * every reconcile, including the logging-only one where `AppendOnly` is `true`
2027
+ * on both sides - "AppendOnly true -> true", an operator told the reason for a
2028
+ * call is a field that did not move. The caller reaches this only past a guard
2029
+ * that returned on both flags matching, so at least one clause always fires and
2030
+ * the result is never empty.
2031
+ */
2032
+ function destinationDrift(appendOnly, loggingEnabled) {
2033
+ const parts = [];
2034
+ if (appendOnly !== STREAM_APPEND_ONLY) {
2035
+ parts.push(`AppendOnly ${String(appendOnly)} -> ${String(STREAM_APPEND_ONLY)}`);
2036
+ }
2037
+ if (loggingEnabled !== true) {
2038
+ parts.push(`error logging ${loggingEnabled === false ? 'off' : 'unrecorded'} -> on`);
2039
+ }
2040
+ return parts.join(', ');
2041
+ }
1778
2042
  /**
1779
2043
  * Record the delivery stream's identity and health from a `DescribeDeliveryStream`.
1780
2044
  *
1781
2045
  * `state` and `failure` are what `analytics status` reports (task 55), so the
1782
2046
  * stream's health is hydrated by the same `read` the reconcile runs and there is
1783
2047
  * no second describe path. `versionId` and `destinationId` are what
1784
- * `UpdateDestination` cannot be called without, and `appendOnly` is the live
1785
- * flag the reconcile compares against {@link STREAM_APPEND_ONLY}.
2048
+ * `UpdateDestination` cannot be called without, and `appendOnly` and
2049
+ * `loggingEnabled` are the two live flags the reconcile compares - the first
2050
+ * against {@link STREAM_APPEND_ONLY}, the second against error logging simply
2051
+ * being on. Both go through {@link recordOptional}, so a describe that stops
2052
+ * reporting one clears it rather than leaving a stale `true` that would make the
2053
+ * reconcile skip work it should do.
1786
2054
  *
1787
2055
  * The ARN is guarded on its value, the guard `analytics-table` and
1788
2056
  * `analytics-catalog-integration` both put on theirs: `describeDeliveryStream`
@@ -1797,6 +2065,7 @@ function recordStream(ctx, status) {
1797
2065
  recordOptional(out, 'versionId', status.versionId);
1798
2066
  recordOptional(out, 'destinationId', status.destinationId);
1799
2067
  recordOptional(out, 'appendOnly', status.appendOnly);
2068
+ recordOptional(out, 'loggingEnabled', status.loggingEnabled);
1800
2069
  recordOptional(out, 'failure', status.failure);
1801
2070
  }
1802
2071
  /**
@@ -1945,10 +2214,39 @@ async function applyErrorBucketConfiguration(ctx, name) {
1945
2214
  await client.putBucketTagging(name, ctx.tags ?? {});
1946
2215
  await client.putPublicAccessBlock(name);
1947
2216
  }
2217
+ /**
2218
+ * The log group Firehose writes its delivery errors to, and the log stream
2219
+ * inside it that they are written to.
2220
+ *
2221
+ * **Firehose creates neither.** Enabling error logging through the API rather
2222
+ * than the console requires the group *and* the stream to exist in advance, so
2223
+ * this node creates both - and re-ensures the stream on every `update` beside
2224
+ * the retention, which is why {@link analyticsLogGroupNode} takes a stream at
2225
+ * all. Without that, a group left by a run that stopped between
2226
+ * `CreateLogGroup` and `CreateLogStream` is permanently one call short, with
2227
+ * `read()` reporting it present and `update()` doing nothing about it, and the
2228
+ * symptom is a delivery failure with nowhere to be explained.
2229
+ *
2230
+ * {@link DESTINATION_DELIVERY_STREAM} is the only stream created:
2231
+ * `BackupDelivery` belongs to a destination configured with S3 backup, and the
2232
+ * Iceberg destination this pipeline builds configures none.
2233
+ *
2234
+ * It is at the head of the chain that writes to it -
2235
+ * `analytics-firehose-stream` declares the edge - while the delivery role does
2236
+ * not, for the reason `analytics-transform-log-group`'s counterpart does not.
2237
+ */
2238
+ export function analyticsFirehoseLogGroupNode() {
2239
+ return analyticsLogGroupNode({
2240
+ id: FIREHOSE_LOG_GROUP_NODE,
2241
+ title: `Firehose delivery-error log group (${ANALYTICS_REGION})`,
2242
+ name: firehoseLogGroupName,
2243
+ stream: DESTINATION_DELIVERY_STREAM,
2244
+ });
2245
+ }
1948
2246
  /**
1949
2247
  * The role Firehose assumes to read the catalog, write the table, invoke the
1950
- * transform and store what it could not deliver - four grants, four concrete
1951
- * resources, no `*`.
2248
+ * transform, store what it could not deliver and say so in one log stream -
2249
+ * five grants, five concrete resources, no `*`.
1952
2250
  *
1953
2251
  * It declares `dependsOn` on the three nodes whose recorded ARNs those grants
1954
2252
  * interpolate. `topoSort` drains zero-indegree nodes alphabetically
@@ -2038,7 +2336,16 @@ export function analyticsFirehoseRoleNode() {
2038
2336
  export function analyticsFirehoseStreamNode() {
2039
2337
  return {
2040
2338
  id: FIREHOSE_STREAM_NODE,
2041
- dependsOn: [FIREHOSE_ROLE_NODE, TABLE_NODE, CATALOG_NODE, TRANSFORM_FUNCTION_NODE],
2339
+ dependsOn: [
2340
+ FIREHOSE_ROLE_NODE,
2341
+ TABLE_NODE,
2342
+ CATALOG_NODE,
2343
+ TRANSFORM_FUNCTION_NODE,
2344
+ // Not an ARN this node interpolates: the group has to exist before the
2345
+ // stream can report a delivery failure into it, and on teardown the
2346
+ // stream goes before the group that holds its errors.
2347
+ FIREHOSE_LOG_GROUP_NODE,
2348
+ ],
2042
2349
  title: `Firehose delivery stream (${ANALYTICS_REGION})`,
2043
2350
  async read(ctx) {
2044
2351
  const status = await firehose(ctx).describeDeliveryStream(streamName(ctx));
@@ -2049,10 +2356,11 @@ export function analyticsFirehoseStreamNode() {
2049
2356
  // "already exists", and the reconcile would go green over a stream that
2050
2357
  // accepts nothing.
2051
2358
  //
2052
- // What `update` then does with such a stream is *nothing*: it branches on
2053
- // the recorded `AppendOnly` flag alone, so a `CREATING_FAILED` or
2054
- // `DELETING` stream whose flag already matches is reconciled with zero AWS
2055
- // calls and reported done. That is stated rather than guarded because this
2359
+ // What `update` then does with such a stream may be *nothing*: it branches
2360
+ // on the two recorded flags and nothing else, so a `CREATING_FAILED` or
2361
+ // `DELETING` stream that is already append-only with error logging on is
2362
+ // reconciled with zero AWS calls and reported done - the state is not part
2363
+ // of the comparison. That is stated rather than guarded because this
2056
2364
  // `read` is the hydration path - `recordStream` puts `state` and `failure`
2057
2365
  // into the plugin's scoped state, and reporting an unusable stream from
2058
2366
  // them is `analytics status`' job (task 55). Do not read this comment as a
@@ -2070,12 +2378,21 @@ export function analyticsFirehoseStreamNode() {
2070
2378
  async update(ctx) {
2071
2379
  const recorded = ctx.state.resources[FIREHOSE_STREAM_NODE];
2072
2380
  const appendOnly = typeof recorded?.['appendOnly'] === 'boolean' ? recorded['appendOnly'] : undefined;
2073
- // The live flag already matches what this pipeline wants, so there is
2074
- // nothing to reconcile and no AWS call at all. `undefined` does NOT match:
2075
- // a stream whose destination reported no flag, or a state file that lost
2076
- // it, is a stream this node cannot claim is append-only, and pushing the
2077
- // desired configuration is the safe direction.
2078
- if (appendOnly === STREAM_APPEND_ONLY)
2381
+ const loggingEnabled = typeof recorded?.['loggingEnabled'] === 'boolean' ? recorded['loggingEnabled'] : undefined;
2382
+ // Both live flags already match what this pipeline wants, so there is
2383
+ // nothing to reconcile and no AWS call at all. `undefined` does NOT match
2384
+ // either: a stream whose destination reported no flag, or a state file that
2385
+ // lost one, is a stream this node cannot claim is configured, and pushing
2386
+ // the desired configuration is the safe direction.
2387
+ //
2388
+ // **The logging half is what reaches the installed base.** Every stream
2389
+ // this plugin has created is append-only, so on `appendOnly` alone this
2390
+ // return fires for all of them and no already-deployed stream would ever
2391
+ // be switched from silent delivery failures to logged ones. The second
2392
+ // condition is what makes an existing stream reconcile exactly once: the
2393
+ // update sends `CloudWatchLoggingOptions`, the re-read records
2394
+ // `loggingEnabled: true`, and the next apply returns here.
2395
+ if (appendOnly === STREAM_APPEND_ONLY && loggingEnabled === true)
2079
2396
  return;
2080
2397
  const name = streamName(ctx);
2081
2398
  const client = firehose(ctx);
@@ -2098,7 +2415,7 @@ export function analyticsFirehoseStreamNode() {
2098
2415
  // operator told the update was rejected when it succeeded.
2099
2416
  let refusal;
2100
2417
  try {
2101
- ctx.logger.step(`updating the analytics delivery stream "${name}" in place (AppendOnly ${String(appendOnly)} -> ${STREAM_APPEND_ONLY}) - UpdateDestination keeps the stream's ARN, so the CloudFront log delivery pointed at it is untouched`);
2418
+ ctx.logger.step(`updating the analytics delivery stream "${name}" in place (${destinationDrift(appendOnly, loggingEnabled)}) - UpdateDestination keeps the stream's ARN, so the CloudFront log delivery pointed at it is untouched`);
2102
2419
  await whileRoleIsPropagating(() => client.updateDestination(name, destination, { versionId, destinationId }));
2103
2420
  }
2104
2421
  catch (err) {
@@ -2690,7 +3007,7 @@ export function analyticsLogDeliveryNode() {
2690
3007
  };
2691
3008
  }
2692
3009
  /**
2693
- * The plugin's twelve resource nodes, assembled in the order the change spec's
3010
+ * The plugin's fourteen resource nodes, assembled in the order the change spec's
2694
3011
  * §Analytics pipeline → Resource nodes table lists them. This is what
2695
3012
  * `Plugin.nodes` (`plugin.ts`) hands the CLI's generic `analytics bootstrap`
2696
3013
  * and `analytics destroy` verbs, and it is the whole of what this package
@@ -2711,7 +3028,7 @@ export function analyticsLogDeliveryNode() {
2711
3028
  *
2712
3029
  * **No `ctx` parameter, deliberately.** The SPI declares `nodes?(ctx)` and the
2713
3030
  * CLI calls it with one, so this function is assignable to it as written - a
2714
- * zero-argument function satisfies a one-argument signature. None of the twelve
3031
+ * zero-argument function satisfies a one-argument signature. None of the fourteen
2715
3032
  * factories needs a context to be *built*: each reads `ctx` inside `read`,
2716
3033
  * `create`, `update` and `delete`, when the reconcile is actually running. A
2717
3034
  * parameter accepted and ignored here would be an unused binding and, worse, a
@@ -2733,10 +3050,12 @@ export function buildAnalyticsNodes() {
2733
3050
  analyticsCatalogIntegrationNode(),
2734
3051
  // The transform chain.
2735
3052
  analyticsSaltSecretNode(),
3053
+ analyticsTransformLogGroupNode(),
2736
3054
  analyticsTransformRoleNode(),
2737
3055
  analyticsTransformFunctionNode(),
2738
3056
  // The delivery chain.
2739
3057
  analyticsErrorBucketNode(),
3058
+ analyticsFirehoseLogGroupNode(),
2740
3059
  analyticsFirehoseRoleNode(),
2741
3060
  analyticsFirehoseStreamNode(),
2742
3061
  // The vended-delivery chain.
package/dist/plugin.d.ts CHANGED
@@ -51,11 +51,11 @@
51
51
  * They are both now, and `analytics status` still runs the declared command
52
52
  * above rather than the generic verb, because a declared action wins.
53
53
  *
54
- * The property `nodes` carries is not just "twelve nodes": it is that the
54
+ * The property `nodes` carries is not just "fourteen nodes": it is that the
55
55
  * CLI's engine reconciles them against the store `toPluginContext` scoped to
56
56
  * this plugin's name, `state/<env>.analytics.json` - never the site's
57
57
  * `state/<env>.json`. `blogwright bootstrap` therefore provisions none of
58
- * these twelve (the site's `buildNodes` does not consult discovery), and
58
+ * these fourteen (the site's `buildNodes` does not consult discovery), and
59
59
  * `blogwright destroy --yes` removes none of them, because
60
60
  * `assertNoScopedState` (`packages/cli/src/commands.ts`) refuses the site
61
61
  * teardown for as long as that scoped object exists and names
package/dist/plugin.js CHANGED
@@ -51,11 +51,11 @@
51
51
  * They are both now, and `analytics status` still runs the declared command
52
52
  * above rather than the generic verb, because a declared action wins.
53
53
  *
54
- * The property `nodes` carries is not just "twelve nodes": it is that the
54
+ * The property `nodes` carries is not just "fourteen nodes": it is that the
55
55
  * CLI's engine reconciles them against the store `toPluginContext` scoped to
56
56
  * this plugin's name, `state/<env>.analytics.json` - never the site's
57
57
  * `state/<env>.json`. `blogwright bootstrap` therefore provisions none of
58
- * these twelve (the site's `buildNodes` does not consult discovery), and
58
+ * these fourteen (the site's `buildNodes` does not consult discovery), and
59
59
  * `blogwright destroy --yes` removes none of them, because
60
60
  * `assertNoScopedState` (`packages/cli/src/commands.ts`) refuses the site
61
61
  * teardown for as long as that scoped object exists and names
package/dist/queries.d.ts CHANGED
@@ -130,7 +130,7 @@ interface QueryDefinition {
130
130
  * The name of the row-count query. Not one of the seven the spec's §Local
131
131
  * server lists - those answer the dashboard's panels; this one answers
132
132
  * `analytics status`, which reports the table's current row count beside the
133
- * plugin's twelve nodes. It lives in this set rather than in the command
133
+ * plugin's fourteen nodes. It lives in this set rather than in the command
134
134
  * because the command may not write SQL: every statement this package runs is
135
135
  * one of these definitions, reached through the `AnalyticsQuery` port.
136
136
  */
package/dist/queries.js CHANGED
@@ -89,7 +89,7 @@ const CACHE_HIT_RESULT_TYPES = ['Hit', 'RefreshHit'];
89
89
  * The name of the row-count query. Not one of the seven the spec's §Local
90
90
  * server lists - those answer the dashboard's panels; this one answers
91
91
  * `analytics status`, which reports the table's current row count beside the
92
- * plugin's twelve nodes. It lives in this set rather than in the command
92
+ * plugin's fourteen nodes. It lives in this set rather than in the command
93
93
  * because the command may not write SQL: every statement this package runs is
94
94
  * one of these definitions, reached through the `AnalyticsQuery` port.
95
95
  */
@@ -1,4 +1,4 @@
1
1
  {
2
- "hash": "74fb95ecd190",
3
- "key": "analytics/transform/transform-74fb95ecd190.zip"
2
+ "hash": "e69800712c84",
3
+ "key": "analytics/transform/transform-e69800712c84.zip"
4
4
  }
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "blogwright": {
4
4
  "plugin": "analytics"
5
5
  },
6
- "version": "0.4.0-beta.2",
6
+ "version": "0.4.0-beta.3",
7
7
  "type": "module",
8
8
  "sideEffects": false,
9
9
  "files": [
@@ -18,7 +18,7 @@
18
18
  },
19
19
  "dependencies": {
20
20
  "@duckdb/node-api": "1.5.5-r.4",
21
- "blogwright-core": "0.4.0-beta.2",
21
+ "blogwright-core": "0.4.0-beta.3",
22
22
  "fflate": "^0.8.3"
23
23
  },
24
24
  "devDependencies": {
@@ -1 +0,0 @@
1
- import{G as e,K as t,dt as n,lt as r,n as i,pt as a,q as o,t as s}from"./D2BnX0Uk.js";import"./B3vZDoek.js";var c=class{constructor(e,t){this.status=e,this.body=typeof t==`string`?{message:t}:t||{message:`Error: ${e}`}}toString(){return JSON.stringify(this.body)}},l=class{constructor(e,t){try{new Headers({location:t})}catch{throw Error(`Invalid redirect location ${JSON.stringify(t)}: this string contains characters that cannot be used in HTTP headers`)}this.status=e,this.location=t}},u=class extends Error{constructor(e,t,n){super(n),this.status=e,this.text=t}};new URL(`sveltekit-internal://`);function d(e,t){return e===`/`||t===`ignore`?e:t===`never`?e.endsWith(`/`)?e.slice(0,-1):e:t===`always`&&!e.endsWith(`/`)?e+`/`:e}function f(e){return e.split(`%25`).map(decodeURI).join(`%25`)}function p(e){for(let t in e)e[t]=decodeURIComponent(e[t]);return e}function m({href:e}){return e.split(`#`)[0]}function h(){}function g(...e){let t=5381;for(let n of e)if(typeof n==`string`){let e=n.length;for(;e;)t=t*33^n.charCodeAt(--e)}else if(ArrayBuffer.isView(n)){let e=new Uint8Array(n.buffer,n.byteOffset,n.byteLength),r=e.length;for(;r;)t=t*33^e[--r]}else throw TypeError(`value must be a string or TypedArray`);return(t>>>0).toString(36)}new TextEncoder;function _(e){let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e++)n[e]=t.charCodeAt(e);return n}var v=window.fetch;window.fetch=(e,t)=>((e instanceof Request?e.method:t?.method||`GET`)!==`GET`&&y.delete(x(e)),v(e,t));var y=new Map;function ee(e,t){let n=x(e,t),r=document.querySelector(n);if(r?.textContent){r.remove();let{body:e,...t}=JSON.parse(r.textContent);r.getAttribute(`data-b64`)!==null&&(e=_(e));let i=r.getAttribute(`data-ttl`);return i&&y.set(n,{body:e,init:t,ttl:1e3*Number(i)}),Promise.resolve(new Response(e,t))}return window.fetch(e,t)}function b(e,t,n){if(y.size>0){let t=x(e,n),r=y.get(t);if(r){if(performance.now()<r.ttl&&[`default`,`force-cache`,`only-if-cached`,void 0].includes(n?.cache))return new Response(r.body,r.init);y.delete(t)}}return window.fetch(t,n)}function x(e,t){let n=`script[data-sveltekit-fetched][data-url=${JSON.stringify(e instanceof Request?e.url:e)}]`;if(t?.headers||t?.body){let e=[];t.headers&&e.push([...new Headers(t.headers)].join(`,`)),t.body&&(typeof t.body==`string`||ArrayBuffer.isView(t.body))&&e.push(t.body),n+=`[data-hash="${g(...e)}"]`}return n}var te=/^(\[)?(\.\.\.)?(\w+)(?:=(\w+))?(\])?$/,ne=/^\/\((?:[^)]+)\)$/;function re(e){let t=[];return{pattern:e===`/`||ne.test(e)?/^\/$/:RegExp(`^${ae(e).map(e=>{let n=/^\[\.\.\.(\w+)(?:=(\w+))?\]$/.exec(e);if(n)return t.push({name:n[1],matcher:n[2],optional:!1,rest:!0,chained:!0}),`(?:/([^]*))?`;let r=/^\[\[(\w+)(?:=(\w+))?\]\]$/.exec(e);if(r)return t.push({name:r[1],matcher:r[2],optional:!0,rest:!1,chained:!0}),`(?:/([^/]+))?`;if(!e)return;let i=e.split(/\[(.+?)\](?!\])/);return`/`+i.map((e,n)=>{if(n%2){if(e.startsWith(`x+`))return se(String.fromCharCode(parseInt(e.slice(2),16)));if(e.startsWith(`u+`))return se(String.fromCharCode(...e.slice(2).split(`-`).map(e=>parseInt(e,16))));let[,r,a,o,s]=te.exec(e);return t.push({name:o,matcher:s,optional:!!r,rest:!!a,chained:a?n===1&&i[0]===``:!1}),a?`([^]*?)`:r?`([^/]*)?`:`([^/]+?)`}return se(e)}).join(``)}).join(``)}/?$`),params:t}}function ie(e){return e!==``&&!/^\([^)]+\)$/.test(e)}function ae(e){return e.slice(1).split(`/`).filter(ie)}function oe(e,t,n){let r={},i=e.slice(1),a=i.filter(e=>e!==void 0),o=0;for(let e=0;e<t.length;e+=1){let s=t[e],c=i[e-o];if(s.chained&&s.rest&&o&&(c=i.slice(e-o,e+1).filter(e=>e).join(`/`),o=0),c===void 0){if(s.rest)c=``;else continue}if(!s.matcher||n[s.matcher](c)){r[s.name]=c;let n=t[e+1],l=i[e+1];n&&!n.rest&&n.optional&&l&&s.chained&&(o=0),!n&&!l&&Object.keys(r).length===a.length&&(o=0);continue}if(s.optional&&s.chained){o++;continue}return}if(!o)return r}function se(e){return e.normalize().replace(/[[\]]/g,`\\$&`).replace(/%/g,`%25`).replace(/\//g,`%2[Ff]`).replace(/\?/g,`%3[Ff]`).replace(/#/g,`%23`).replace(/[.*+?^${}()|\\]/g,`\\$&`)}function ce({nodes:e,server_loads:t,dictionary:n,matchers:r}){let i=new Set(t);return Object.entries(n).map(([t,[n,i,s]])=>{let{pattern:c,params:l}=re(t),u={id:t,exec:e=>{let t=c.exec(e);if(t)return oe(t,l,r)},errors:[1,...s||[]].map(t=>e[t]),layouts:[0,...i||[]].map(o),leaf:a(n)};return u.errors.length=u.layouts.length=Math.max(u.errors.length,u.layouts.length),u});function a(t){let n=t<0;return n&&(t=~t),[n,e[t]]}function o(t){return t===void 0?t:[i.has(t),e[t]]}}function le(e,t=JSON.parse){try{return t(sessionStorage[e])}catch{}}function ue(e,t,n=JSON.stringify){let r=n(t);try{sessionStorage[e]=r}catch{}}var S=globalThis.__sveltekit_ws17h5?.base??``,de=globalThis.__sveltekit_ws17h5?.assets??S??``,fe=`1788197150274`,pe=`sveltekit:snapshot`,me=`sveltekit:scroll`,he=`sveltekit:states`,C=`sveltekit:history`,w=`sveltekit:navigation`,T={tap:1,hover:2,viewport:3,eager:4,off:-1,false:-1},ge=location.origin;function _e(e){if(e instanceof URL)return e;let t=document.baseURI;if(!t){let e=document.getElementsByTagName(`base`);t=e.length?e[0].href:document.URL}return new URL(e,t)}function E(){return{x:pageXOffset,y:pageYOffset}}function D(e,t){return e.getAttribute(`data-sveltekit-${t}`)}var ve={...T,"":T.hover};function ye(e){let t=e.assignedSlot??e.parentNode;return t?.nodeType===11&&(t=t.host),t}function be(e,t){for(;e&&e!==t;){if(e.nodeName.toUpperCase()===`A`&&e.hasAttribute(`href`))return e;e=ye(e)}}function xe(e,t,n){let r;try{if(r=new URL(e instanceof SVGAElement?e.href.baseVal:e.href,document.baseURI),n&&r.hash.match(/^#[^/]/)){let e=location.hash.split(`#`)[1]||`/`;r.hash=`#${e}${r.hash}`}}catch{}let i=e instanceof SVGAElement?e.target.baseVal:e.target,a=!r||!!i||k(r,t,n)||(e.getAttribute(`rel`)||``).split(/\s+/).includes(`external`),o=r?.origin===ge&&e.hasAttribute(`download`);return{url:r,external:a,target:i,download:o}}function O(e){let t=null,n=null,r=null,i=null,a=null,o=null,s=e;for(;s&&s!==document.documentElement;)r===null&&(r=D(s,`preload-code`)),i===null&&(i=D(s,`preload-data`)),t===null&&(t=D(s,`keepfocus`)),n===null&&(n=D(s,`noscroll`)),a===null&&(a=D(s,`reload`)),o===null&&(o=D(s,`replacestate`)),s=ye(s);function c(e){switch(e){case``:case`true`:return!0;case`off`:case`false`:return!1;default:return}}return{preload_code:ve[r??`off`],preload_data:ve[i??`off`],keepfocus:c(t),noscroll:c(n),reload:c(a),replace_state:c(o)}}function Se(e){let t=a(e),n=!0;function r(){n=!0,t.update(e=>e)}function i(e){n=!1,t.set(e)}function o(e){let r;return t.subscribe(t=>{(r===void 0||n&&t!==r)&&e(r=t)})}return{notify:r,set:i,subscribe:o}}var Ce={v:h};function we(){let{set:e,subscribe:t}=a(!1),n;async function r(){clearTimeout(n);try{let t=await fetch(`${de}/_app/version.json`,{headers:{pragma:`no-cache`,"cache-control":`no-cache`}});if(!t.ok)return!1;let r=(await t.json()).version!==fe;return r&&(e(!0),Ce.v(),clearTimeout(n)),r}catch{return!1}}return{subscribe:t,check:r}}function k(e,t,n){return e.origin!==ge||!e.pathname.startsWith(t)?!0:n?e.pathname!==location.pathname:!1}function Te(e){}var Ee=new Set([`load`,`prerender`,`csr`,`ssr`,`trailingSlash`,`config`]);[...Ee],[...new Set([...Ee])];function De(e){return e.filter(e=>e!=null)}function A(e,t){return e+`/`+t}function Oe(e){return e instanceof c||e instanceof u?e.status:500}function ke(e){return e instanceof u?e.text:`Internal Error`}var j,M,Ae,je=i.toString().includes(`$$`)||/function \w+\(\) \{\}/.test(i.toString()),Me=`a:`;je?(j={data:{},form:null,error:null,params:{},route:{id:null},state:{},status:-1,url:new URL(Me)},M={current:null},Ae={current:!1}):(j=new class{#e=n({});get data(){return e(this.#e)}set data(e){r(this.#e,e)}#t=n(null);get form(){return e(this.#t)}set form(e){r(this.#t,e)}#n=n(null);get error(){return e(this.#n)}set error(e){r(this.#n,e)}#r=n({});get params(){return e(this.#r)}set params(e){r(this.#r,e)}#i=n({id:null});get route(){return e(this.#i)}set route(e){r(this.#i,e)}#a=n({});get state(){return e(this.#a)}set state(e){r(this.#a,e)}#o=n(-1);get status(){return e(this.#o)}set status(e){r(this.#o,e)}#s=n(new URL(Me));get url(){return e(this.#s)}set url(e){r(this.#s,e)}},M=new class{#e=n(null);get current(){return e(this.#e)}set current(e){r(this.#e,e)}},Ae=new class{#e=n(!1);get current(){return e(this.#e)}set current(e){r(this.#e,e)}},Ce.v=()=>Ae.current=!0);function Ne(e){Object.assign(j,e)}var{onMount:Pe,tick:Fe}=s,Ie=new Set([`icon`,`shortcut icon`,`apple-touch-icon`]),N=null,P=le(`sveltekit:scroll`)??{},F=le(`sveltekit:snapshot`)??{},I={url:Se({}),page:Se({}),navigating:a(null),updated:we()};function Le(e){P[e]=E()}function Re(e,t){let n=e+1;for(;P[n];)delete P[n],n+=1;for(n=t+1;F[n];)delete F[n],n+=1}function L(e,t=!1){return t?location.replace(e.href):location.href=e.href,new Promise(h)}async function ze(){if(`serviceWorker`in navigator){let e=await navigator.serviceWorker.getRegistration(S||`/`);e&&await e.update()}}var Be,Ve,R,z,He,B,Ue={},We={},V=[],H=[],U=null;function Ge(){U?.fork?.then(e=>e?.discard()),U=null,Q={element:void 0,href:void 0}}var Ke=new Map,qe=new Set,Je=new Set,W=new Set,G={branch:[],error:null,url:null,nav:null},Ye=!1,Xe=!1,Ze=!0,K=!1,q=!1,Qe=!1,$e=!1,et,J,Y,X,tt=new Set,nt=new Map,rt=new Map;async function it(e,t,n){if(globalThis.__sveltekit_ws17h5.data){let{q:e={},p:t={},l:n={},f:r={}}=globalThis.__sveltekit_ws17h5.data;for(let t in e)Ue[t]=e[t];for(let e in n)Ue[e]=n[e];for(let e in r)Ue[e]=r[e];for(let e in t)We[e]=t[e]}document.URL!==location.href&&(location.href=location.href),B=e,await e.hooks.init?.(),Be=ce(e),z=document.documentElement,He=t,Ve=e.nodes[0],R=e.nodes[1],Ve(),R(),J=history.state?.[C],Y=history.state?.[w],J||(J=Y=Date.now(),history.replaceState({...history.state,[C]:J,[w]:Y},``));let r=P[J];function i(){r&&(history.scrollRestoration=`manual`,scrollTo(r.x,r.y))}n?(i(),await Mt(He,n)):(await Z({type:`enter`,url:_e(B.hash?Rt(new URL(location.href)):location.href),replace_state:!0}),i()),jt()}function at(){V.length=0,$e=!1}function ot(e){H.some(e=>e?.snapshot)&&(F[e]=H.map(e=>e?.snapshot?.capture()))}function st(e){F[e]?.forEach((e,t)=>{H[t]?.snapshot?.restore(e)})}function ct(){Le(J),ue(me,P),ot(Y),ue(pe,F)}async function lt(e,t,n,r){let i,a;t.invalidateAll&&Ge(),await Z({type:`goto`,url:_e(e),keepfocus:t.keepFocus,noscroll:t.noScroll,replace_state:t.replaceState,state:t.state,redirect_count:n,nav_token:r,accept:()=>{if(t.invalidateAll){$e=!0,i=new Set;for(let[e,t]of nt)for(let[n,r]of t)r.resource?.reset(),i.add(A(e,n));a=new Set;for(let[e,t]of rt)for(let n of t.keys())a.add(A(e,n))}t.invalidate&&t.invalidate.forEach(At)}}),t.invalidateAll&&o().then(o).then(()=>{for(let[e,t]of nt)for(let[n,{resource:r}]of t)i?.has(A(e,n))&&r.start();for(let[e,t]of rt)for(let[n,{resource:r}]of t)a?.has(A(e,n))&&r.reconnect()})}async function ut(e){if(e.id!==U?.id){Ge();let t={};tt.add(t),U={id:e.id,token:t,promise:bt({...e,preload:t}).then(e=>(tt.delete(t),e.type===`loaded`&&e.state.error&&Ge(),e)),fork:null}}return U.promise}async function dt(e){let t=(await wt(e,!1))?.route;t&&await Promise.all([...t.layouts,t.leaf].filter(Boolean).map(e=>e[1]()))}async function ft(e,t,n){let r={params:G.params,route:{id:G.route?.id??null},url:new URL(location.href)};if(G={...e.state,nav:r},Ne(e.props.page),et=new B.root({target:t,props:{...e.props,stores:I,components:H},hydrate:n,sync:!1,transformError:void 0}),await Promise.resolve(),n){let e={from:null,to:{...r,scroll:P[J]??E()},willUnload:!1,type:`enter`,complete:Promise.resolve()};W.forEach(t=>t(e))}st(Y),Xe=!0}async function pt({url:e,params:t,branch:n,errors:r,status:i,error:a,route:o,form:s}){let c=`never`;if(S&&(e.pathname===S||e.pathname===S+`/`))c=`always`;else for(let e of n)e?.slash!==void 0&&(c=e.slash);e.pathname=d(e.pathname,c),e.search=e.search;let l={type:`loaded`,state:{url:e,params:t,branch:n,error:a,route:o},props:{constructors:De(n).map(e=>e.node.component),page:Lt(j)}};s!==void 0&&(l.props.form=s);let u={},f=!j,p=0;for(let e=0;e<Math.max(n.length,G.branch.length);e+=1){let t=n[e],r=G.branch[e];t?.data!==r?.data&&(f=!0),t&&(u={...u,...t.data},f&&(l.props[`data_${p}`]=u),p+=1)}return(!G.url||e.href!==G.url.href||G.error!==a||s!==void 0&&s!==j.form||f)&&(l.props.page={error:a,params:t,route:{id:o?.id??null},state:{},status:i,url:new URL(e),form:s??null,data:f?u:j.data}),l}async function mt({loader:e,parent:t,url:n,params:r,route:i,server_data_node:a}){let o={dependencies:new Set,params:new Set,parent:!1,route:!1,url:!1,search_params:new Set},s=await e();return{node:s,loader:e,server:a,universal:s.universal?.load?{type:`data`,data:null,uses:o}:null,data:a?.data??null,slash:s.universal?.trailingSlash??a?.slash}}function ht(e,t,n){let r=e instanceof Request?e.url:e,i=new URL(r,n);return i.origin===n.origin&&(r=i.href.slice(n.origin.length)),{resolved:i,promise:Xe?b(r,i.href,t):ee(r,t)}}function gt(e,t,n,r,i,a){if($e)return!0;if(!i)return!1;if(i.parent&&e||i.route&&t||i.url&&n)return!0;for(let e of i.search_params)if(r.has(e))return!0;for(let e of i.params)if(a[e]!==G.params[e])return!0;for(let e of i.dependencies)if(V.some(t=>t(new URL(e))))return!0;return!1}function _t(e,t){return e?.type===`data`?e:e?.type===`skip`?t??null:null}function vt(e,t){if(!e)return new Set(t.searchParams.keys());let n=new Set([...e.searchParams.keys(),...t.searchParams.keys()]);for(let r of n){let i=e.searchParams.getAll(r),a=t.searchParams.getAll(r);i.every(e=>a.includes(e))&&a.every(e=>i.includes(e))&&n.delete(r)}return n}function yt({error:e,url:t,route:n,params:r}){return{type:`loaded`,state:{error:e,url:t,route:n,params:r,branch:[]},props:{page:Lt(j),constructors:[]}}}async function bt({id:e,invalidating:t,url:n,params:r,route:i,preload:a}){if(U?.id===e)return tt.delete(U.token),U.promise;let{errors:o,layouts:s,leaf:u}=i,d=[...s,u];o.forEach(e=>e?.().catch(h)),d.forEach(e=>e?.[1]().catch(h));let f=G.url?e!==Et(G.url):!1,p=G.route?i.id!==G.route.id:!1,m=vt(G.url,n),g=!1,_=d.map(async(e,t)=>{if(!e)return;let a=G.branch[t];return e[1]===a?.loader&&!gt(g,p,f,m,a.universal?.uses,r)?a:(g=!0,mt({loader:e[1],url:n,params:r,route:i,parent:async()=>{let e={};for(let n=0;n<t;n+=1)Object.assign(e,(await _[n])?.data);return e},server_data_node:_t(e[0]?{type:`skip`}:null,e[0]?a?.server:void 0)}))});for(let e of _)e.catch(h);let v=[];for(let e=0;e<d.length;e+=1)if(d[e])try{v.push(await _[e])}catch(t){if(t instanceof l)return{type:`redirect`,location:t.location};if(a&&tt.has(a))return yt({error:await $(t,{params:r,url:n,route:{id:i.id}}),url:n,params:r,route:i});let s=Oe(t),u;if(t instanceof c)u=t.body;else{if(await I.updated.check())return await ze(),await L(n);u=await $(t,{params:r,url:n,route:{id:i.id}})}let d=await xt(e,v,o);return d?pt({url:n,params:r,branch:v.slice(0,d.idx).concat(d.node),errors:o,status:s,error:u,route:i}):await Ot(n,{id:i.id},u,s)}else v.push(void 0);return pt({url:n,params:r,branch:v,errors:o,status:200,error:null,route:i,form:t?void 0:null})}async function xt(e,t,n){for(;e--;)if(n[e]){let r=e;for(;!t[r];)--r;try{return{idx:r+1,node:{node:await n[e](),loader:n[e],data:{},server:null,universal:null}}}catch{continue}}}async function St({status:e,error:t,url:n,route:r}){let i={};try{return pt({url:n,params:i,branch:[await mt({loader:Ve,url:n,params:i,route:r,parent:()=>Promise.resolve({}),server_data_node:_t(null)}),{node:await R(),loader:R,universal:null,server:null,data:null}],status:e,error:t,errors:[],route:null})}catch(t){if(t instanceof l){await lt(new URL(t.location,location.href),{},0);return}let a=await B.get_error_template(),o=await $(t,{url:n,params:i,route:r}),s=a({status:e,message:String(o?.message??``).replace(/&/g,`&amp;`).replace(/</g,`&lt;`).replace(/>/g,`&gt;`)}),c=new DOMParser().parseFromString(s,`text/html`);throw document.documentElement.replaceChild(document.adoptNode(c.head),document.head),document.documentElement.replaceChild(document.adoptNode(c.body),document.body),t}}async function Ct(e){let t=e.href;if(Ke.has(t))return Ke.get(t);let n;try{let r=(async()=>{let t=await B.hooks.reroute({url:new URL(e),fetch:async(t,n)=>ht(t,n,e).promise})??e;if(typeof t==`string`){let n=new URL(e);B.hash?n.hash=t:n.pathname=t,t=n}return t})();Ke.set(t,r),n=await r}catch{Ke.delete(t);return}return n}async function wt(e,t){if(e&&!k(e,S,B.hash)){let n=await Ct(e);if(!n)return;let r=Tt(n);for(let n of Be){let i=n.exec(r);if(i)return{id:Et(e),invalidating:t,route:n,params:p(i),url:e}}}}function Tt(e){return f(B.hash?e.hash.replace(/^#/,``).replace(/[?#].+/,``):e.pathname.slice(S.length))||`/`}function Et(e){return(B.hash?e.hash.replace(/^#/,``):e.pathname)+e.search}function Dt({url:e,type:t,intent:n,delta:r,event:i,scroll:a}){let o=!1,s=It(G,n,e,t,a??null);r!==void 0&&(s.navigation.delta=r),i!==void 0&&(s.navigation.event=i);let c={...s.navigation,cancel:()=>{o=!0,s.reject(Error(`navigation cancelled`))}};return K||qe.forEach(e=>e(c)),o?null:s}async function Z({type:e,url:n,popped:r,keepfocus:i,noscroll:a,replace_state:s,state:c={},redirect_count:l=0,nav_token:d={},accept:f=h,block:p=h,event:m}){let g=X;X=d;let _=await wt(n,!1),v=e===`enter`?It(G,_,n,e):Dt({url:n,type:e,delta:r?.delta,intent:_,scroll:r?.scroll,event:m});if(!v){p(),X===d&&(X=g);return}let y=J,ee=Y;f(),K=!0,Xe&&v.navigation.type!==`enter`&&I.navigating.set(M.current=v.navigation);let b=_&&await bt(_);if(!b){if(k(n,S,B.hash))return await L(n,s);b=await Ot(n,{id:null},await $(new u(404,`Not Found`,`Not found: ${n.pathname}`),{url:n,params:{},route:{id:null}}),404,s)}if(n=_?.url||n,X!==d){v.reject(Error(`navigation aborted`));return}if(!b)return;if(b.type===`redirect`){if(l<20){await Z({type:e,url:new URL(b.location,n),popped:r,keepfocus:i,noscroll:a,replace_state:s,state:c,redirect_count:l+1,nav_token:d}),v.fulfil(void 0);return}if(b=await St({status:500,error:await $(Error(`Redirect loop`),{url:n,params:{},route:{id:null}}),url:n,route:{id:null}}),!b)return}else if(b.props.page.status>=400&&await I.updated.check())return await ze(),await L(n,s);if(at(),Le(y),ot(ee),b.props.page.url.pathname!==n.pathname&&(n.pathname=b.props.page.url.pathname),c=r?r.state:c,!r){let e=+!s,t={[C]:J+=e,[w]:Y+=e,[he]:c};(s?history.replaceState:history.pushState).call(history,t,``,n),s||Re(J,Y)}let x=_&&U?.id===_.id?U.fork:null;U?.fork&&!x?Ge():(U=null,Q={element:void 0,href:void 0}),b.props.page.state=c;let te;if(Xe){let e=(await Promise.all(Array.from(Je,e=>e(v.navigation)))).filter(e=>typeof e==`function`);if(e.length>0){function t(){e.forEach(e=>{W.delete(e)})}e.push(t),e.forEach(e=>{W.add(e)})}let r=v.navigation.to;G={...b.state,nav:{params:r.params,route:r.route,url:r.url}},b.props.page&&(b.props.page.url=n),!i&&document.activeElement instanceof HTMLElement&&document.activeElement!==document.body&&document.activeElement.blur();let a=x&&await x;a?te=a.commit():(N=null,et.$set(b.props),N&&Object.assign(b.props.page,N),Ne(b.props.page),te=t?.()),Qe=!0}else await ft(b,He,!1);let{activeElement:ne}=document;if(await te,await o(),await o(),X!==d){v.reject(Error(`navigation aborted`));return}b.props.page&&N&&Object.assign(b.props.page,N);let re=null;if(Ze){let e=r?r.scroll:a?E():null;e?scrollTo(e.x,e.y):(re=n.hash&&document.getElementById(zt(n)))?re.scrollIntoView():scrollTo(0,0)}let ie=document.activeElement!==ne&&document.activeElement!==document.body;!i&&!ie&&Ft(n,!re),Ze=!0,K=!1,v.fulfil(void 0),v.navigation.to&&(v.navigation.to.scroll=E()),W.forEach(e=>e(v.navigation)),e===`popstate`&&st(Y),I.navigating.set(M.current=null)}async function Ot(e,t,n,r,i){return e.origin===ge&&e.pathname===location.pathname&&!Ye?await St({status:r,error:n,url:e,route:t}):await L(e,i)}var Q={element:void 0,href:void 0};function kt(){let e,t;z.addEventListener(`mousemove`,t=>{let n=t.target;clearTimeout(e),e=setTimeout(()=>{i(n,T.hover)},20)});function n(e){e.defaultPrevented||i(e.composedPath()[0],T.tap)}z.addEventListener(`mousedown`,n),z.addEventListener(`touchstart`,n,{passive:!0});let r=new IntersectionObserver(e=>{for(let t of e)t.isIntersecting&&(dt(new URL(t.target.href)),r.unobserve(t.target))},{threshold:0});async function i(e,n){let r=be(e,z),i=r===Q.element&&r?.href===Q.href&&n>=t;if(!r||i)return;let{url:a,external:o,download:s}=xe(r,S,B.hash);if(o||s)return;let c=O(r),l=a&&Et(G.url)===Et(a);if(!(c.reload||l)){if(n<=c.preload_data){Q={element:r,href:r.href},t=T.tap;let e=await wt(a,!1);if(!e)return;ut(e)}else n<=c.preload_code&&(Q={element:r,href:r.href},t=n,dt(a))}}function a(){r.disconnect();for(let e of z.querySelectorAll(`a`)){let{url:t,external:n,download:i}=xe(e,S,B.hash);if(n||i)continue;let a=O(e);a.reload||(a.preload_code===T.viewport&&r.observe(e),a.preload_code===T.eager&&dt(t))}}W.add(a),a()}function $(e,t){if(e instanceof c)return e.body;let n=Oe(e),r=ke(e);return B.hooks.handleError({error:e,event:t,status:n,message:r})??{message:r}}function At(e){if(typeof e==`function`)V.push(e);else{let{href:t}=new URL(e,location.href);V.push(e=>e.href===t)}}function jt(){history.scrollRestoration=`manual`,addEventListener(`beforeunload`,e=>{let t=!1;if(ct(),!K){let e=It(G,void 0,null,`leave`),n={...e.navigation,cancel:()=>{t=!0,e.reject(Error(`navigation cancelled`))}};qe.forEach(e=>e(n))}t?(e.preventDefault(),e.returnValue=``):history.scrollRestoration=`auto`}),addEventListener(`visibilitychange`,()=>{document.visibilityState===`hidden`&&ct()}),navigator.connection?.saveData||kt(),z.addEventListener(`click`,async t=>{if(t.button||t.which!==1||t.metaKey||t.ctrlKey||t.shiftKey||t.altKey||t.defaultPrevented)return;let n=be(t.composedPath()[0],z);if(!n)return;let{url:r,external:i,target:a,download:o}=xe(n,S,B.hash);if(!r)return;if(a===`_parent`||a===`_top`){if(window.parent!==window)return}else if(a&&a!==`_self`)return;let s=O(n);if(!(n instanceof SVGAElement)&&r.protocol!==location.protocol&&r.protocol!==`https:`&&r.protocol!==`http:`||o)return;let[c,l]=(B.hash?r.hash.replace(/^#/,``):r.href).split(`#`),u=c===m(location);if(i||s.reload&&(!u||!l)){Dt({url:r,type:`link`,event:t})?K=!0:t.preventDefault();return}if(l!==void 0&&u){let[,i]=G.url.href.split(`#`);if(i===l){if(t.preventDefault(),l===``||l===`top`&&n.ownerDocument.getElementById(`top`)===null)scrollTo({top:0});else{let e=n.ownerDocument.getElementById(decodeURIComponent(l));e&&(e.scrollIntoView(),e.focus())}return}if(q=!0,Le(J),e(r),!s.replace_state)return;q=!1}t.preventDefault(),await new Promise(e=>{requestAnimationFrame(()=>{setTimeout(e,0)}),setTimeout(e,100)}),await Z({type:`link`,url:r,keepfocus:s.keepfocus,noscroll:s.noscroll,replace_state:s.replace_state??r.href===location.href,event:t})}),z.addEventListener(`submit`,e=>{if(e.defaultPrevented)return;let t=HTMLFormElement.prototype.cloneNode.call(e.target),n=e.submitter;if((n?.formTarget||t.target)===`_blank`||(n?.formMethod||t.method)!==`get`)return;let r=new URL(n?.hasAttribute(`formaction`)&&n?.formAction||t.action);if(k(r,S,!1))return;let i=e.target,a=O(i);if(a.reload)return;e.preventDefault(),e.stopPropagation();let o=new FormData(i,n);r.search=new URLSearchParams(o).toString(),Z({type:`form`,url:r,keepfocus:a.keepfocus,noscroll:a.noscroll,replace_state:a.replace_state??r.href===location.href,event:e})}),addEventListener(`popstate`,async t=>{if(!Pt){if(t.state?.[`sveltekit:history`]){let n=t.state[C];if(X={},n===J)return;let r=P[n],i=t.state[`sveltekit:states`]??{},a=new URL(t.state[`sveltekit:pageurl`]??location.href),o=t.state[w],s=G.url?m(location)===m(G.url):!1;if(o===Y&&(Qe||s)){i!==j.state&&(j.state=i),e(a),P[J]=E(),r&&scrollTo(r.x,r.y),J=n;return}let c=n-J;await Z({type:`popstate`,url:a,popped:{state:i,scroll:r,delta:c},accept:()=>{J=n,Y=o},block:()=>{history.go(-c)},nav_token:X,event:t})}else q||(e(new URL(location.href)),B.hash&&location.reload())}}),addEventListener(`hashchange`,()=>{q&&(q=!1,history.replaceState({...history.state,[C]:++J,[w]:Y},``,location.href))});for(let e of document.querySelectorAll(`link`))Ie.has(e.rel)&&(e.href=e.href);addEventListener(`pageshow`,e=>{e.persisted&&I.navigating.set(M.current=null)});function e(e){G.url=j.url=e,I.page.set(Lt(j)),I.page.notify()}}async function Mt(e,{status:t=200,error:n,node_ids:r,params:i,route:a,server_route:o,data:s,form:c}){Ye=!0;let u=new URL(location.href),d;({params:i={},route:a={id:null}}=await wt(u,!1)||{}),d=Be.find(({id:e})=>e===a.id);let f,p=!0;try{let e=r.map(async(t,n)=>{let r=s[n];return r?.uses&&(r.uses=Nt(r.uses)),mt({loader:B.nodes[t],url:u,params:i,route:a,parent:async()=>{let t={};for(let r=0;r<n;r+=1)Object.assign(t,(await e[r]).data);return t},server_data_node:_t(r)})}),o=await Promise.all(e);if(d){let e=d.layouts;for(let t=0;t<e.length;t++)e[t]||o.splice(t,0,void 0)}f=await pt({url:u,params:i,branch:o,status:t,error:n,errors:d?.errors,form:c,route:d??null})}catch(t){if(t instanceof l)return await L(new URL(t.location,location.href));f=await St({status:Oe(t),error:await $(t,{url:u,params:i,route:a}),url:u,route:a}),e.textContent=``,p=!1}f&&(f.props.page&&(f.props.page.state={}),await ft(f,e,p))}function Nt(e){return{dependencies:new Set(e?.dependencies??[]),params:new Set(e?.params??[]),parent:!!e?.parent,route:!!e?.route,url:!!e?.url,search_params:new Set(e?.search_params??[])}}var Pt=!1;function Ft(e,t=!0){let n=document.querySelector(`[autofocus]`);if(n)n.focus();else{let n=zt(e);if(n&&document.getElementById(n)){let{x:r,y:i}=E();setTimeout(()=>{let a=history.state;Pt=!0,location.replace(new URL(`#${n}`,location.href)),history.replaceState(a,``,e),t&&scrollTo(r,i),Pt=!1})}else{let e=document.body,t=e.getAttribute(`tabindex`);e.tabIndex=-1,e.focus({preventScroll:!0,focusVisible:!1}),t===null?e.removeAttribute(`tabindex`):e.setAttribute(`tabindex`,t)}let r=getSelection();if(r&&r.type!==`None`){let e=[];for(let t=0;t<r.rangeCount;t+=1)e.push(r.getRangeAt(t));setTimeout(()=>{if(r.rangeCount===e.length){for(let t=0;t<r.rangeCount;t+=1){let n=e[t],i=r.getRangeAt(t);if(n.commonAncestorContainer!==i.commonAncestorContainer||n.startContainer!==i.startContainer||n.endContainer!==i.endContainer||n.startOffset!==i.startOffset||n.endOffset!==i.endOffset)return}r.removeAllRanges()}})}}}function It(e,t,n,r,i=null){let a,o,s=new Promise((e,t)=>{a=e,o=t});return s.catch(h),{navigation:{from:{params:e.params,route:{id:e.route?.id??null},url:e.url,scroll:E()},to:n&&{params:t?.params??null,route:{id:t?.route?.id??null},url:n,scroll:i},willUnload:!t,type:r,complete:s},fulfil:a,reject:o}}function Lt(e){return{data:e.data,error:e.error,form:e.form,params:e.params,route:e.route,state:e.state,status:e.status,url:e.url}}function Rt(e){let t=new URL(e);return t.hash=decodeURIComponent(e.hash),t}function zt(e){let t;if(B.hash){let[,,n]=e.hash.split(`#`,3);t=n??``}else t=e.hash.slice(1);return decodeURIComponent(t)}export{Te as i,M as n,j as r,it as t};
@@ -1 +0,0 @@
1
- import{i as e,t}from"../chunks/FH_3TS2H.js";export{e as load_css,t as start};