From 69bfa20d453f288f114cc3f91405e72708dc0bf0 Mon Sep 17 00:00:00 2001 From: pavlov346346-source Date: Tue, 8 Sep 2026 12:12:04 +0300 Subject: [PATCH] Add v17.7.0 data layer foundation --- public/core/data-layer-v1770.js | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 public/core/data-layer-v1770.js diff --git a/public/core/data-layer-v1770.js b/public/core/data-layer-v1770.js new file mode 100644 index 0000000..c6854b7 --- /dev/null +++ b/public/core/data-layer-v1770.js @@ -0,0 +1,68 @@ +(()=>{ + 'use strict'; + if(window.CateriumDataV1770)return; + + const VERSION='17.7.0'; + const RELEASE='20260908-v17-7-0-architecture-foundation'; + const listeners=new Map(); + const clone=value=>{try{return structuredClone(value)}catch(_){return JSON.parse(JSON.stringify(value))}}; + const safeJson=(raw,fallback)=>{try{const value=JSON.parse(raw);return value==null?fallback:value}catch(_){return fallback}}; + const emit=(topic,payload)=>{for(const fn of listeners.get(topic)||[]){try{fn(payload)}catch(error){console.error('[Caterium Data]',error)}}}; + const subscribe=(topic,fn)=>{if(typeof fn!=='function')return()=>{};if(!listeners.has(topic))listeners.set(topic,new Set());listeners.get(topic).add(fn);return()=>listeners.get(topic)?.delete(fn)}; + + function runtimeOrders(){try{if(typeof orders!=='undefined'&&Array.isArray(orders))return orders}catch(_){}return null} + function runtimePersist(){try{return typeof persist==='function'?persist:null}catch(_){return null}} + function cloud(){return window.SunCloudV2||null} + function session(){try{return cloud()?.getSession?.()||null}catch(_){return null}} + function workspace(){try{return cloud()?.getWorkspace?.()||null}catch(_){return null}} + function supportMode(){try{return cloud()?.getSupportMode?.()||null}catch(_){return null}} + function isSignedIn(){return Boolean(session()?.user)} + function isSupportReadOnly(){return Boolean(supportMode()||cloud()?.isSupportMode?.())} + function canWrite(permission='app.write'){ + if(isSupportReadOnly())return false; + if(!isSignedIn())return true; + try{const c=cloud();if(typeof c?.hasPermission==='function')return c.hasPermission(permission)||c.hasPermission('app.write')}catch(_){} + return true; + } + + const storage={ + read(key,fallback=null){const raw=localStorage.getItem(String(key));if(raw==null)return fallback;return safeJson(raw,fallback)}, + write(key,value,{silent=false}={}){localStorage.setItem(String(key),JSON.stringify(value));if(!silent)emit(`storage:${key}`,clone(value));return value}, + remove(key){localStorage.removeItem(String(key));emit(`storage:${key}`,null)}, + raw(key){return localStorage.getItem(String(key))} + }; + + function getOrders(){const live=runtimeOrders();return clone(live||storage.read('sunOrders',[])||[])} + function replaceOrders(next,{persistLocal=true,reason='replace'}={}){ + const list=Array.isArray(next)?clone(next):[]; + const live=runtimeOrders(); + if(live){live.length=0;live.push(...clone(list))} + if(persistLocal){const p=runtimePersist();if(p){try{p()}catch(error){console.warn('[Caterium Data] persist fallback',error);storage.write('sunOrders',list,{silent:true})}}else storage.write('sunOrders',list,{silent:true})} + else storage.write('sunOrders',list,{silent:true}); + emit('orders',{reason,orders:clone(list)});return list; + } + function getOrder(id){return getOrders().find(o=>String(o?.id)===String(id))||null} + function updateOrder(id,updater,{persistLocal=true,reason='update'}={}){ + const list=getOrders();const index=list.findIndex(o=>String(o?.id)===String(id));if(index<0)return null; + const current=clone(list[index]);const next=typeof updater==='function'?updater(current):{...current,...clone(updater||{})};if(!next)return null;list[index]=next;replaceOrders(list,{persistLocal,reason});return clone(next) + } + function applyServerOrders(changed,{reason='server'}={}){ + const patches=Array.isArray(changed)?changed:[];if(!patches.length)return {changed:0,ids:[]}; + const list=getOrders(),map=new Map(list.map((o,i)=>[String(o?.id),i])),ids=[]; + for(const patch of patches){const id=String(patch?.id??'');if(!id)continue;const index=map.get(id);if(index==null){list.push(clone(patch));map.set(id,list.length-1)}else list[index]=clone(patch);ids.push(id)} + replaceOrders(list,{persistLocal:false,reason}); + try{window.render?.();window.renderOrders?.();window.renderStats?.();window.renderClients?.()}catch(_){} + try{window.dispatchEvent(new CustomEvent('caterium:data-orders-applied',{detail:{ids,reason}}))}catch(_){} + return {changed:ids.length,ids} + } + + const ordersRepo={list:getOrders,get:getOrder,replace:replaceOrders,update:updateOrder,applyServerChanges:applyServerOrders}; + const diagnostics={ + snapshot(){return {version:VERSION,workspaceId:workspace()?.id||null,signedIn:isSignedIn(),supportReadOnly:isSupportReadOnly(),orderCount:getOrders().length,listenerTopics:listeners.size}}, + measure(name,fn){const started=performance.now();return Promise.resolve().then(fn).finally(()=>emit('metric',{name,durationMs:Math.round((performance.now()-started)*10)/10,at:new Date().toISOString()}))} + }; + + window.CateriumDataV1770={VERSION,RELEASE,storage,orders:ordersRepo,subscribe,emit,cloud,session,workspace,isSignedIn,isSupportReadOnly,canWrite,diagnostics}; + window.CateriumData=window.CateriumDataV1770; + try{window.dispatchEvent(new CustomEvent('caterium:data-ready',{detail:{version:VERSION}}))}catch(_){} +})();