// store_versions MUST be first import — its side-effect wipes stale localStorage // before svelte-persisted-store hydrates from it. import { AE_LOC_VERSION } from '$lib/stores/store_versions'; import { auth_loc_defaults } from '$lib/stores/ae_stores__auth_loc_defaults'; import { persisted } from 'svelte-persisted-store'; import { readable, writable } from 'svelte/store'; import type { Readable, Writable } from 'svelte/store'; import { PUBLIC_AE_API_PROTOCOL, PUBLIC_AE_API_SERVER, PUBLIC_AE_API_BAK_SERVER, PUBLIC_AE_API_PORT, PUBLIC_AE_API_PATH, PUBLIC_AE_API_SECRET_KEY, PUBLIC_AE_API_CRUD_SUPER_KEY, PUBLIC_AE_NO_ACCOUNT_ID, // PUBLIC_AE_NO_ACCOUNT_ID_TOKEN, // PUBLIC_AE_ACCOUNT_ID, // PUBLIC_AE_EVENT_ID, // PUBLIC_AE_SPONSORSHIP_CFG_ID } from '$env/static/public'; const api_server_fqdn = PUBLIC_AE_API_SERVER; // 'api.oneskyit.com' const api_base_url = `${PUBLIC_AE_API_PROTOCOL}://${PUBLIC_AE_API_SERVER}:${PUBLIC_AE_API_PORT}${PUBLIC_AE_API_PATH}`; const api_base_url_bak = `${PUBLIC_AE_API_PROTOCOL}://${PUBLIC_AE_API_BAK_SERVER}:${PUBLIC_AE_API_PORT}${PUBLIC_AE_API_PATH}`; const api_secret_key = PUBLIC_AE_API_SECRET_KEY; const api_crud_super_key = PUBLIC_AE_API_CRUD_SUPER_KEY; // const ae_account_id = PUBLIC_AE_ACCOUNT_ID; const ae_account_id: null | string = null; const ae_no_account_id = PUBLIC_AE_NO_ACCOUNT_ID; // const ae_no_account_id_token = PUBLIC_AE_NO_ACCOUNT_ID_TOKEN; // const ae_event_id = PUBLIC_AE_EVENT_ID; // const ae_sponsorship_cfg_id = PUBLIC_AE_SPONSORSHIP_CFG_ID; // Loose bag-of-anything type used throughout the store. Once stores are broken into // typed domain files, individual structs will have proper interfaces instead. export type key_val = { [key: string]: any; }; import { string_snippets } from '$lib/utils/ae_string_snippets'; export const ae_snip = string_snippets; // Deployment version stamp. Compared against ae_sess.ver in +layout.svelte to detect // stale persisted data after a deploy (triggers a reload). Bump this alongside // ae_app_session_data_defaults.ver whenever a deploy changes expected stored shape. // See store_versions.ts for the schema-level localStorage wipe mechanism (separate concern). const ver = '2025-05-01_1445'; // *** BEGIN *** Longer-term app data. This should be stored to local storage. const ae_app_local_data_defaults: key_val = { __version: AE_LOC_VERSION, // Schema version gate — see store_versions.ts last_page_reload: null, last_cache_refresh: Date.now(), cache_expired: false, // Deployment stamp — compared against ae_sess.ver in +layout.svelte to trigger // a reload when the persisted local data predates the current deploy. ver: ver, name: 'Aether - App Hub', theme: 'light', theme_mode: 'light', theme_name: 'AE_Firefly', // OSIT default theme font_size_mode: 'default', // 'default' | 'larger' | 'smaller' iframe: false, browser_type: null, // Safari needs special handling for scrolling in iframes. title: `OSIT's Æ`, debug_mode: false, edit_mode: false, adv_mode: false, sync_local_config: true, // When true, pulls site config from the remote API on load. // Auth and identity section — see ae_stores__auth_loc_defaults.ts. ...auth_loc_defaults, qry__enabled: 'enabled', // all, disabled, enabled qry__hidden: 'not_hidden', // all, hidden, not_hidden qry__limit: 20, qry__offset: 0, qr_scanner_version: 'one', admin: { show_element__sql_qry: false, show_element__sql_qry_results: false }, sys_menu: { hide: false, expand: false, hide_access_type: false, expand_access_type: false, hide_edit_mode: false, expand_edit_mode: false, hide_user: false, expand_user: false, hide_theme: false, expand_theme: false, hide_app_cfg: false, expand_app_cfg: false }, debug_menu: { hide: false, expand: false }, app_cfg: { show_element__header: false, show_element__footer: false, show_element__menu: false, show_element__menu_btn: true, show_element__access_type: true, show_element__passcode_input: true, show_element__cfg: true, show_element__cfg_detail: false, show_element__sign_in_out: true, // Show the sign-in/out button in the UI show_opt__debug: true, show_opt__permissions: true, show_opt__reset: true, show_opt__sync: true, show_opt__theme: true, show_opt__utilities: true }, files: { processed_file_kv: {}, uploaded_file_kv: {}, video_clip_file_kv: {}, add_to_use_files_method: 'upload' // upload, select }, ds: {}, hub: { show_element__cfg: true, show_element__cfg_detail: false, show_element__access_type: true, theme_mode: 'light', theme_name: 'wintry', // wintry classes__form: 'border border-surface-200 p-4 space-y-4 rounded-container', qr: {} }, mod: { archives: {}, events: { event_id: null, show_edit__event_presenter_obj: false, show_list__event_presenter_obj_li: true, show_view__event_presenter_obj: false, submit_status: null, // When set, new presenters are automatically assigned to this session. default_session_id: null }, journals: {}, posts: {}, sponsorships: { cfg_id: null, for_type: null, for_id: null, // Max complimentary guests per sponsorship tier (0 = no tier). // These are fallback defaults; the active event config should override via events_cfg_json. level_guest_max_li: { 0: 0, 1: 4, 2: 8, 3: 8, 4: 8, 5: 8, 6: 16, 7: 16 }, show_edit__sponsorship_obj: false, show_list__sponsorship_obj_li: true, show_view__sponsorship_obj: false, show_question__accommodations: false, submit_status: null // 'saving', 'created', 'updated' } // testing: {}, } }; export const ae_loc: Writable = persisted( 'ae_loc', ae_app_local_data_defaults ); // *** BEGIN *** Temporary app data. This should be stored to session storage. const ae_app_session_data_defaults: key_val = { // Deployment stamp — compared against ae_loc.ver in +layout.svelte. // When these differ, the app reloads to flush stale persisted data. ver: ver, log_lvl: 0, disable_sys_header: false, disable_sys_nav: false, disable_sys_footer: false, sys_menu: { expand: false, focus_passcode_input: false }, debug_menu: { hide_quick_info: true }, app_cfg: { show_element__passcode_input: true }, ds: { submit_status: null }, ds_loaded: { hub__site__root_page_header: false, hub__site__root_page_content: false, hub__site__root_page_footer: false }, files: { disable_submit__hosted_file_obj: false, processed_file_kv: {}, uploaded_file_kv: {}, video_clip_file_kv: {}, processed_file_list: [], status__file_list: null, video_clip_file_list: [], submit_status: null, clip_complete: null }, hub: { show_xyz: null, account_id_qry_status: null, event_badge_id_status_qry__search: null, event_presenter_id_qry_status: null, site_domain_id_qry_status: null, sponsorship_id_qry_status: null, sponsorship_cfg_id_qry_status: null, qr: {} }, mod: { archives: {}, events: {}, journals: {}, posts: {}, sponsorships: { disable_submit__sponsorship_obj: false, slct__level_num: 0, show_question__accommodations: false, submit_status: null // 'saving', 'created', 'updated', 'saved' }, testing: {} }, person: { show_report__person_li: false, qry_limit__people: 100 }, show__modal_change_password: false, download: {}, // Per-file progress keyed by file_id (downloads) or temp_id (uploads). // Shape: { status, endpoint, filename, size_total, size_loaded, percent_completed } api_download_kv: {}, api_upload_kv: {}, test: true }; export const ae_sess = writable(ae_app_session_data_defaults); export const ae_api_data_struct: key_val = { ver: '2024-08-11_11', fqdn: api_server_fqdn, base_url: api_base_url, base_url_bak: api_base_url_bak, api_secret_key: api_secret_key, api_secret_key_bak: api_secret_key, api_crud_super_key: api_crud_super_key, // Used for operations that bypass normal account-scoped CRUD limits. headers: {}, account_id: ae_account_id }; const ae_api_headers: key_val = {}; ae_api_headers['Content-Type'] = 'application/json'; ae_api_headers['x-aether-api-key'] = ae_api_data_struct.api_secret_key; if (ae_account_id) { ae_api_headers['x-account-id'] = ae_account_id; } if (ae_no_account_id) { ae_api_headers['x-no-account-id'] = ae_no_account_id; } ae_api_data_struct['headers'] = ae_api_headers; export const ae_api = writable(ae_api_data_struct); // General-purpose reactive trigger. Set any truthy value to signal consumers to re-query. const ae_trig_template: key_val = {}; export const ae_trig = writable(ae_trig_template); /* *** BEGIN *** Initialize slct and slct_trigger */ /* The slct and slct_trigger variable should not be stored in local storage. Only use session storage because browser tabs can be open to different accounts, events, sponsorships, etc. */ // Intended for temporary session storage. // Updated 2024-03-15 const slct_obj_template: key_val = { account_id: ae_account_id, account_obj: {}, event_id: null, event_obj: {}, event_obj_li: [], event_presentation_id: null, event_presentation_obj: {}, event_presentation_obj_li: [], event_presenter_id: null, event_presenter_obj: {}, event_presenter_obj_li: [], event_session_id: null, event_session_obj: {}, event_session_obj_li: [], sponsorship_id: null, sponsorship_obj: {}, sponsorship_obj_li: [], sponsorship_cfg_id: null, sponsorship_cfg_obj: {}, sponsorship_cfg_obj_li: [], post_id: null, post_obj: {}, post_obj_li: [], post_comment_id: null, post_comment_obj: {}, post_comment_obj_li: [] }; // In-memory only (not persisted) — separate browser tabs can have different selections. export const slct = writable(slct_obj_template); // Increment (or set any truthy value) to tell subscribers that the selection changed // and they should re-query their data. export const slct_trigger: any = writable(null); // Auth error signal — set by API helpers on 401/403 to trigger the session-expired banner in the root layout. // Only set from browser context (never SSR). 'expired' covers both 401 and 403 responses. export const ae_auth_error = writable<{ type: 'expired' | null; ts: number | null; }>({ type: null, ts: null }); // Reactive clock — updates every second. Used for live countdown and elapsed-time displays. export const time = readable(new Date(), function start(set) { const interval = setInterval(() => { set(new Date()); }, 1000); return function stop() { clearInterval(interval); }; });