@realtek/core-theme 0.0.310 → 0.0.312

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.
@@ -0,0 +1,47 @@
1
+ import { b as e, i as t } from "./authApi-cJpMRgNq.js";
2
+ import { n, t as r } from "./formGroupsCache-CdBtIpe0.js";
3
+ //#region src/services/addFormApi.js
4
+ function i(e) {
5
+ let t = e?.data ?? e;
6
+ return Array.isArray(t) ? t : Array.isArray(t?.groups) ? t.groups : Array.isArray(e?.groups) ? e.groups : [];
7
+ }
8
+ function a({ module: t, id: n, action: r, clientId: i, region: a, group: o, view: s } = {}) {
9
+ let c = new URLSearchParams({ module: t });
10
+ return n && c.set("id", n), r && c.set("action", r), i && c.set("clientId", i), a && c.set("region", a), o && c.set("group", o), s && c.set("view", s), `${e}/admin/form-groups?${c.toString()}`;
11
+ }
12
+ async function o(e = {}) {
13
+ if (!e.module) throw Error("module is required to fetch form groups");
14
+ let n = a(e);
15
+ return r(n, async () => {
16
+ let e = await t(), r = await fetch(n, {
17
+ method: "GET",
18
+ headers: {
19
+ "Content-Type": "application/json",
20
+ Authorization: `Bearer ${e}`
21
+ }
22
+ });
23
+ if (!r.ok) throw Error(`API ${r.status}: ${r.statusText}`);
24
+ return r.json();
25
+ });
26
+ }
27
+ async function s(e = {}) {
28
+ return i(await o(e));
29
+ }
30
+ async function c(r, i) {
31
+ let a = await t(), o = await fetch(`${e}/module/create?module=${encodeURIComponent(r)}`, {
32
+ method: "POST",
33
+ headers: { Authorization: `Bearer ${a}` },
34
+ body: i
35
+ }), { logout: s } = await import("./authApi-f5UkjakT.js");
36
+ o.status === 401 && s(), n();
37
+ let c = (o.headers.get("content-type") || "").includes("application/json") ? await o.json() : await o.text();
38
+ if (!o.ok) {
39
+ let e = Error(c?.error || c?.message || `API ${o.status}: ${o.statusText}`);
40
+ throw e.status = o.status, e.data = c, e;
41
+ }
42
+ return c;
43
+ }
44
+ //#endregion
45
+ export { o as n, s as r, c as t };
46
+
47
+ //# sourceMappingURL=addFormApi-CuFRO2TI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addFormApi-CuFRO2TI.js","names":[],"sources":["../src/services/addFormApi.js"],"sourcesContent":["import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, JOBS_URL } from './apiConfig';\nimport { fetchFormGroupsCached, invalidateFormGroupsCache } from './formGroupsCache';\n\n// ── Form Groups ───────────────────────────────────────────────────────────────\n\nfunction extractFormGroups(json) {\n const payload = json?.data ?? json;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(payload?.groups)) return payload.groups;\n if (Array.isArray(json?.groups)) return json.groups;\n return [];\n}\n\n/**\n * formGroupsUrl — the canonical URL for one form-groups query. Built in a fixed\n * parameter order so two callers asking the same question produce the same\n * string, which is what makes them shareable (see formGroupsCache).\n */\nfunction formGroupsUrl({ module, id, action, clientId, region, group, view } = {}) {\n const params = new URLSearchParams({ module });\n if (id) params.set('id', id);\n if (action) params.set('action', action);\n if (clientId) params.set('clientId', clientId);\n if (region) params.set('region', region);\n if (group) params.set('group', group);\n if (view) params.set('view', view);\n return `${AUTH_URL}/admin/form-groups?${params.toString()}`;\n}\n\n/**\n * fetchFormGroupsPayload — the RAW payload for one query, shared between every\n * caller that asks for it (see formGroupsCache for why). Returns the whole\n * envelope, so a caller that needs `recordMeta` and one that needs only the\n * groups array still cost ONE request between them.\n */\nexport async function fetchFormGroupsPayload(options = {}) {\n if (!options.module) throw new Error('module is required to fetch form groups');\n const url = formGroupsUrl(options);\n return fetchFormGroupsCached(url, async () => {\n const token = await ensureToken();\n const res = await fetch(url, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n return res.json();\n });\n}\n\nexport async function getFormGroups(options = {}) {\n return extractFormGroups(await fetchFormGroupsPayload(options));\n}\n\nexport async function getCandidateFormGroups(options = {}) {\n return getFormGroups({ module: 'candidates', ...options });\n}\n\nexport async function getMentionUsers(search = '') {\n const { apiGetWithAuth } = await import('./authApi');\n const params = search ? `?search=${encodeURIComponent(search)}` : '';\n return apiGetWithAuth(JOBS_URL, `/users/any${params}`);\n}\n\n// ── Create Records ────────────────────────────────────────────────────────────\n// Routes through the Auth gateway which injects tenant context from JWT\n// and proxies to the correct downstream service based on module name.\n\nexport async function createModuleRecord(moduleName, formData) {\n // ensureToken (not getStoredToken) so a missing/expired session throws a\n // clear \"Authentication required\" error instead of silently sending the\n // request with no Authorization header (which the gateway rejects as 401).\n // NOTE: do NOT set Content-Type here — the browser must add the multipart\n // boundary itself. Only the Authorization header is set manually.\n const token = await ensureToken();\n\n const res = await fetch(\n `${AUTH_URL}/module/create?module=${encodeURIComponent(moduleName)}`,\n { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n // A save is exactly the moment a cached form-groups payload stops describing\n // the record — invalidate before the caller can navigate and read one back.\n invalidateFormGroupsCache();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data;\n}\n"],"mappings":";;;AAMA,SAAS,EAAkB,GAAM;CAC/B,IAAM,IAAU,GAAM,QAAQ;CAI9B,OAHI,MAAM,QAAQ,CAAO,IAAU,IAC/B,MAAM,QAAQ,GAAS,MAAM,IAAU,EAAQ,SAC/C,MAAM,QAAQ,GAAM,MAAM,IAAU,EAAK,SACtC,CAAC;AACV;AAOA,SAAS,EAAc,EAAE,WAAQ,OAAI,WAAQ,aAAU,WAAQ,UAAO,YAAS,CAAC,GAAG;CACjF,IAAM,IAAS,IAAI,gBAAgB,EAAE,UAAO,CAAC;CAO7C,OANI,KAAI,EAAO,IAAI,MAAM,CAAE,GACvB,KAAQ,EAAO,IAAI,UAAU,CAAM,GACnC,KAAU,EAAO,IAAI,YAAY,CAAQ,GACzC,KAAQ,EAAO,IAAI,UAAU,CAAM,GACnC,KAAO,EAAO,IAAI,SAAS,CAAK,GAChC,KAAM,EAAO,IAAI,QAAQ,CAAI,GAC1B,GAAG,EAAS,qBAAqB,EAAO,SAAS;AAC1D;AAQA,eAAsB,EAAuB,IAAU,CAAC,GAAG;CACzD,IAAI,CAAC,EAAQ,QAAQ,MAAU,MAAM,yCAAyC;CAC9E,IAAM,IAAM,EAAc,CAAO;CACjC,OAAO,EAAsB,GAAK,YAAY;EAC5C,IAAM,IAAQ,MAAM,EAAY,GAC1B,IAAM,MAAM,MAAM,GAAK;GAC3B,QAAQ;GACR,SAAS;IAAE,gBAAgB;IAAoB,eAAe,UAAU;GAAQ;EAClF,CAAC;EACD,IAAI,CAAC,EAAI,IAAI,MAAU,MAAM,OAAO,EAAI,OAAO,IAAI,EAAI,YAAY;EACnE,OAAO,EAAI,KAAK;CAClB,CAAC;AACH;AAEA,eAAsB,EAAc,IAAU,CAAC,GAAG;CAChD,OAAO,EAAkB,MAAM,EAAuB,CAAO,CAAC;AAChE;AAgBA,eAAsB,EAAmB,GAAY,GAAU;CAM7D,IAAM,IAAQ,MAAM,EAAY,GAE1B,IAAM,MAAM,MAChB,GAAG,EAAS,wBAAwB,mBAAmB,CAAU,KACjE;EAAE,QAAQ;EAAQ,SAAS,EAAE,eAAe,UAAU,IAAQ;EAAG,MAAM;CAAS,CAClF,GAEM,EAAE,cAAW,MAAM,OAAO;CAKhC,AAJI,EAAI,WAAW,OAAK,EAAO,GAI/B,EAA0B;CAG1B,IAAM,KADc,EAAI,QAAQ,IAAI,cAAc,KAAK,IAC9B,SAAS,kBAAkB,IAAI,MAAM,EAAI,KAAK,IAAI,MAAM,EAAI,KAAK;CAE1F,IAAI,CAAC,EAAI,IAAI;EACX,IAAM,IAAY,MAAM,GAAM,SAAS,GAAM,WAAW,OAAO,EAAI,OAAO,IAAI,EAAI,YAAY;EAG9F,MAFA,EAAM,SAAS,EAAI,QACnB,EAAM,OAAO,GACP;CACR;CAEA,OAAO;AACT"}
@@ -0,0 +1,2 @@
1
+ const e=require("./Breadcrumb-CnVqsNfZ.cjs"),t=require("./authApi-qTqKoZ1I.cjs"),n=require("./formGroupsCache-Bw0aY136.cjs");var r=e.gi({createModuleRecord:()=>c,fetchFormGroupsPayload:()=>o,getFormGroups:()=>s});function i(e){let t=e?.data??e;return Array.isArray(t)?t:Array.isArray(t?.groups)?t.groups:Array.isArray(e?.groups)?e.groups:[]}function a({module:e,id:n,action:r,clientId:i,region:a,group:o,view:s}={}){let c=new URLSearchParams({module:e});return n&&c.set(`id`,n),r&&c.set(`action`,r),i&&c.set(`clientId`,i),a&&c.set(`region`,a),o&&c.set(`group`,o),s&&c.set(`view`,s),`${t.b}/admin/form-groups?${c.toString()}`}async function o(e={}){if(!e.module)throw Error(`module is required to fetch form groups`);let r=a(e);return n.t(r,async()=>{let e=await t.i(),n=await fetch(r,{method:`GET`,headers:{"Content-Type":`application/json`,Authorization:`Bearer ${e}`}});if(!n.ok)throw Error(`API ${n.status}: ${n.statusText}`);return n.json()})}async function s(e={}){return i(await o(e))}async function c(e,r){let i=await t.i(),a=await fetch(`${t.b}/module/create?module=${encodeURIComponent(e)}`,{method:`POST`,headers:{Authorization:`Bearer ${i}`},body:r}),{logout:o}=await Promise.resolve().then(()=>require("./authApi-bAKRYcVy.cjs"));a.status===401&&o(),n.r();let s=(a.headers.get(`content-type`)||``).includes(`application/json`)?await a.json():await a.text();if(!a.ok){let e=Error(s?.error||s?.message||`API ${a.status}: ${a.statusText}`);throw e.status=a.status,e.data=s,e}return s}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return r}});
2
+ //# sourceMappingURL=addFormApi-DBFdfv0-.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"addFormApi-DBFdfv0-.cjs","names":[],"sources":["../src/services/addFormApi.js"],"sourcesContent":["import { ensureToken, fetchJsonWithAuth } from './authApi';\nimport { AUTH_URL, JOBS_URL } from './apiConfig';\nimport { fetchFormGroupsCached, invalidateFormGroupsCache } from './formGroupsCache';\n\n// ── Form Groups ───────────────────────────────────────────────────────────────\n\nfunction extractFormGroups(json) {\n const payload = json?.data ?? json;\n if (Array.isArray(payload)) return payload;\n if (Array.isArray(payload?.groups)) return payload.groups;\n if (Array.isArray(json?.groups)) return json.groups;\n return [];\n}\n\n/**\n * formGroupsUrl — the canonical URL for one form-groups query. Built in a fixed\n * parameter order so two callers asking the same question produce the same\n * string, which is what makes them shareable (see formGroupsCache).\n */\nfunction formGroupsUrl({ module, id, action, clientId, region, group, view } = {}) {\n const params = new URLSearchParams({ module });\n if (id) params.set('id', id);\n if (action) params.set('action', action);\n if (clientId) params.set('clientId', clientId);\n if (region) params.set('region', region);\n if (group) params.set('group', group);\n if (view) params.set('view', view);\n return `${AUTH_URL}/admin/form-groups?${params.toString()}`;\n}\n\n/**\n * fetchFormGroupsPayload — the RAW payload for one query, shared between every\n * caller that asks for it (see formGroupsCache for why). Returns the whole\n * envelope, so a caller that needs `recordMeta` and one that needs only the\n * groups array still cost ONE request between them.\n */\nexport async function fetchFormGroupsPayload(options = {}) {\n if (!options.module) throw new Error('module is required to fetch form groups');\n const url = formGroupsUrl(options);\n return fetchFormGroupsCached(url, async () => {\n const token = await ensureToken();\n const res = await fetch(url, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },\n });\n if (!res.ok) throw new Error(`API ${res.status}: ${res.statusText}`);\n return res.json();\n });\n}\n\nexport async function getFormGroups(options = {}) {\n return extractFormGroups(await fetchFormGroupsPayload(options));\n}\n\nexport async function getCandidateFormGroups(options = {}) {\n return getFormGroups({ module: 'candidates', ...options });\n}\n\nexport async function getMentionUsers(search = '') {\n const { apiGetWithAuth } = await import('./authApi');\n const params = search ? `?search=${encodeURIComponent(search)}` : '';\n return apiGetWithAuth(JOBS_URL, `/users/any${params}`);\n}\n\n// ── Create Records ────────────────────────────────────────────────────────────\n// Routes through the Auth gateway which injects tenant context from JWT\n// and proxies to the correct downstream service based on module name.\n\nexport async function createModuleRecord(moduleName, formData) {\n // ensureToken (not getStoredToken) so a missing/expired session throws a\n // clear \"Authentication required\" error instead of silently sending the\n // request with no Authorization header (which the gateway rejects as 401).\n // NOTE: do NOT set Content-Type here — the browser must add the multipart\n // boundary itself. Only the Authorization header is set manually.\n const token = await ensureToken();\n\n const res = await fetch(\n `${AUTH_URL}/module/create?module=${encodeURIComponent(moduleName)}`,\n { method: 'POST', headers: { Authorization: `Bearer ${token}` }, body: formData },\n );\n\n const { logout } = await import('./authApi');\n if (res.status === 401) logout();\n\n // A save is exactly the moment a cached form-groups payload stops describing\n // the record — invalidate before the caller can navigate and read one back.\n invalidateFormGroupsCache();\n\n const contentType = res.headers.get('content-type') || '';\n const data = contentType.includes('application/json') ? await res.json() : await res.text();\n\n if (!res.ok) {\n const error = new Error(data?.error || data?.message || `API ${res.status}: ${res.statusText}`);\n error.status = res.status;\n error.data = data;\n throw error;\n }\n\n return data;\n}\n"],"mappings":"qNAMA,SAAS,EAAkB,EAAM,CAC/B,IAAM,EAAU,GAAM,MAAQ,EAI9B,OAHI,MAAM,QAAQ,CAAO,EAAU,EAC/B,MAAM,QAAQ,GAAS,MAAM,EAAU,EAAQ,OAC/C,MAAM,QAAQ,GAAM,MAAM,EAAU,EAAK,OACtC,CAAC,CACV,CAOA,SAAS,EAAc,CAAE,SAAQ,KAAI,SAAQ,WAAU,SAAQ,QAAO,QAAS,CAAC,EAAG,CACjF,IAAM,EAAS,IAAI,gBAAgB,CAAE,QAAO,CAAC,EAO7C,OANI,GAAI,EAAO,IAAI,KAAM,CAAE,EACvB,GAAQ,EAAO,IAAI,SAAU,CAAM,EACnC,GAAU,EAAO,IAAI,WAAY,CAAQ,EACzC,GAAQ,EAAO,IAAI,SAAU,CAAM,EACnC,GAAO,EAAO,IAAI,QAAS,CAAK,EAChC,GAAM,EAAO,IAAI,OAAQ,CAAI,EAC1B,GAAG,EAAA,EAAS,qBAAqB,EAAO,SAAS,GAC1D,CAQA,eAAsB,EAAuB,EAAU,CAAC,EAAG,CACzD,GAAI,CAAC,EAAQ,OAAQ,MAAU,MAAM,yCAAyC,EAC9E,IAAM,EAAM,EAAc,CAAO,EACjC,OAAO,EAAA,EAAsB,EAAK,SAAY,CAC5C,IAAM,EAAQ,MAAM,EAAA,EAAY,EAC1B,EAAM,MAAM,MAAM,EAAK,CAC3B,OAAQ,MACR,QAAS,CAAE,eAAgB,mBAAoB,cAAe,UAAU,GAAQ,CAClF,CAAC,EACD,GAAI,CAAC,EAAI,GAAI,MAAU,MAAM,OAAO,EAAI,OAAO,IAAI,EAAI,YAAY,EACnE,OAAO,EAAI,KAAK,CAClB,CAAC,CACH,CAEA,eAAsB,EAAc,EAAU,CAAC,EAAG,CAChD,OAAO,EAAkB,MAAM,EAAuB,CAAO,CAAC,CAChE,CAgBA,eAAsB,EAAmB,EAAY,EAAU,CAM7D,IAAM,EAAQ,MAAM,EAAA,EAAY,EAE1B,EAAM,MAAM,MAChB,GAAG,EAAA,EAAS,wBAAwB,mBAAmB,CAAU,IACjE,CAAE,OAAQ,OAAQ,QAAS,CAAE,cAAe,UAAU,GAAQ,EAAG,KAAM,CAAS,CAClF,EAEM,CAAE,UAAW,MAAA,QAAA,QAAA,EAAA,SAAA,QAAM,wBAAA,CAAA,EACrB,EAAI,SAAW,KAAK,EAAO,EAI/B,EAAA,EAA0B,EAG1B,IAAM,GADc,EAAI,QAAQ,IAAI,cAAc,GAAK,IAC9B,SAAS,kBAAkB,EAAI,MAAM,EAAI,KAAK,EAAI,MAAM,EAAI,KAAK,EAE1F,GAAI,CAAC,EAAI,GAAI,CACX,IAAM,EAAY,MAAM,GAAM,OAAS,GAAM,SAAW,OAAO,EAAI,OAAO,IAAI,EAAI,YAAY,EAG9F,KAFA,GAAM,OAAS,EAAI,OACnB,EAAM,KAAO,EACP,CACR,CAEA,OAAO,CACT"}
@@ -0,0 +1,2 @@
1
+ import { n as e } from "./addFormApi-CuFRO2TI.js";
2
+ export { e as fetchFormGroupsPayload };
@@ -0,0 +1,2 @@
1
+ var e=require("./Breadcrumb-CnVqsNfZ.cjs").gi({fetchFormGroupsCached:()=>a,invalidateFormGroupsCache:()=>i}),t=4e3,n=new Map,r=t;function i(){n.clear()}function a(e,t){let i=Date.now(),a=n.get(e);if(a&&(a.pending||i-a.at<r))return a.promise;let o={at:i,pending:!0};return o.promise=Promise.resolve().then(t).then(e=>(o.pending=!1,o.at=Date.now(),e)).catch(t=>{throw n.get(e)===o&&n.delete(e),t}),n.set(e,o),o.promise}Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return e}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return i}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return a}});
2
+ //# sourceMappingURL=formGroupsCache-Bw0aY136.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formGroupsCache-Bw0aY136.cjs","names":[],"sources":["../src/services/formGroupsCache.js"],"sourcesContent":["// formGroupsCache — one network request per distinct form-groups query.\n//\n// WHY\n// A single page routinely asks for the SAME form-groups payload several times\n// over, because the parts that need it are independent of each other and none\n// of them can know the others exist: on Quick Submit the job header card, the\n// form's cross-module prefill and a job-source lookup each fetch the same job,\n// and the submission config is fetched once unscoped and again client-scoped.\n// Those payloads are large (60 kB for a submission config), so the duplicates\n// are the expensive kind — a measured 245-request, ~4.8 s load with several\n// identical 60 kB and 15 kB responses in it.\n//\n// The honest fix is not to make each caller aware of the others — that couples\n// components that have no business knowing about each other, and breaks the\n// moment a new one is added. It is to make the REQUEST idempotent: identical\n// queries fired close together share one response.\n//\n// TWO MECHANISMS, deliberately:\n//\n// • in-flight coalescing — a query that is already running is joined rather\n// than reissued. Always safe: the answer cannot be staler than the request\n// that is still in the air.\n// • a SHORT time-to-live — covers the common case where the duplicates are\n// sequential rather than overlapping (a child mounts after its parent's\n// fetch has already resolved). This is the part that can serve stale data,\n// so it is kept to a few seconds and any write invalidates the whole cache.\n//\n// Errors are never cached: a failed fetch removes its entry so the next caller\n// retries for real instead of inheriting the failure.\n//\n// Nothing here knows a module or a field. The cache key is the request URL, so\n// scopes that differ in any way (id, clientId, view, group, action) are\n// different entries and can never be served each other's data.\n\n// Long enough to cover one page's mount burst, short enough that no realistic\n// user action fits inside it. Writes invalidate regardless — see below.\nconst DEFAULT_TTL_MS = 4000;\n\nconst entries = new Map(); // url -> { at, promise }\n\nlet ttlMs = DEFAULT_TTL_MS;\n\n/** Test/host hook: change the window, or set 0 to keep only in-flight coalescing. */\nexport function setFormGroupsCacheTtl(ms) {\n ttlMs = Math.max(0, Number(ms) || 0);\n}\n\n/**\n * Drop everything. Called after any module write: a save is exactly the moment\n * a cached payload stops describing the record, and a stale read here would\n * show the user their own change missing.\n */\nexport function invalidateFormGroupsCache() {\n entries.clear();\n}\n\n/**\n * fetchFormGroupsCached — run `loader()` for `url`, or join/replay a recent\n * identical run.\n *\n * `loader` returns the RAW payload, so every caller shares one request no\n * matter which shape it goes on to read out of it (the groups array, or the\n * whole envelope with recordMeta).\n */\nexport function fetchFormGroupsCached(url, loader) {\n const now = Date.now();\n const hit = entries.get(url);\n // A pending entry is always joinable; a settled one only within the window.\n if (hit && (hit.pending || now - hit.at < ttlMs)) return hit.promise;\n\n const entry = { at: now, pending: true };\n entry.promise = Promise.resolve()\n .then(loader)\n .then((value) => {\n entry.pending = false;\n // Stamp on COMPLETION, not on start: a slow request must not begin life\n // already half-expired, or a burst behind it would reissue immediately.\n entry.at = Date.now();\n return value;\n })\n .catch((err) => {\n // Never cache a failure — the next caller must get a real attempt.\n if (entries.get(url) === entry) entries.delete(url);\n throw err;\n });\n entries.set(url, entry);\n return entry.promise;\n}\n\nexport default { fetchFormGroupsCached, invalidateFormGroupsCache, setFormGroupsCacheTtl };\n"],"mappings":"6GAoCM,EAAiB,IAEjB,EAAU,IAAI,IAEhB,EAAQ,EAYZ,SAAgB,GAA4B,CAC1C,EAAQ,MAAM,CAChB,CAUA,SAAgB,EAAsB,EAAK,EAAQ,CACjD,IAAM,EAAM,KAAK,IAAI,EACf,EAAM,EAAQ,IAAI,CAAG,EAE3B,GAAI,IAAQ,EAAI,SAAW,EAAM,EAAI,GAAK,GAAQ,OAAO,EAAI,QAE7D,IAAM,EAAQ,CAAE,GAAI,EAAK,QAAS,EAAK,EAgBvC,MAfA,GAAM,QAAU,QAAQ,QAAQ,EAC7B,KAAK,CAAM,EACX,KAAM,IACL,EAAM,QAAU,GAGhB,EAAM,GAAK,KAAK,IAAI,EACb,EACR,EACA,MAAO,GAAQ,CAGd,MADI,EAAQ,IAAI,CAAG,IAAM,GAAO,EAAQ,OAAO,CAAG,EAC5C,CACR,CAAC,EACH,EAAQ,IAAI,EAAK,CAAK,EACf,EAAM,OACf"}
@@ -0,0 +1,20 @@
1
+ //#region src/services/formGroupsCache.js
2
+ var e = 4e3, t = /* @__PURE__ */ new Map(), n = e;
3
+ function r() {
4
+ t.clear();
5
+ }
6
+ function i(e, r) {
7
+ let i = Date.now(), a = t.get(e);
8
+ if (a && (a.pending || i - a.at < n)) return a.promise;
9
+ let o = {
10
+ at: i,
11
+ pending: !0
12
+ };
13
+ return o.promise = Promise.resolve().then(r).then((e) => (o.pending = !1, o.at = Date.now(), e)).catch((n) => {
14
+ throw t.get(e) === o && t.delete(e), n;
15
+ }), t.set(e, o), o.promise;
16
+ }
17
+ //#endregion
18
+ export { r as n, i as t };
19
+
20
+ //# sourceMappingURL=formGroupsCache-CdBtIpe0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formGroupsCache-CdBtIpe0.js","names":[],"sources":["../src/services/formGroupsCache.js"],"sourcesContent":["// formGroupsCache — one network request per distinct form-groups query.\n//\n// WHY\n// A single page routinely asks for the SAME form-groups payload several times\n// over, because the parts that need it are independent of each other and none\n// of them can know the others exist: on Quick Submit the job header card, the\n// form's cross-module prefill and a job-source lookup each fetch the same job,\n// and the submission config is fetched once unscoped and again client-scoped.\n// Those payloads are large (60 kB for a submission config), so the duplicates\n// are the expensive kind — a measured 245-request, ~4.8 s load with several\n// identical 60 kB and 15 kB responses in it.\n//\n// The honest fix is not to make each caller aware of the others — that couples\n// components that have no business knowing about each other, and breaks the\n// moment a new one is added. It is to make the REQUEST idempotent: identical\n// queries fired close together share one response.\n//\n// TWO MECHANISMS, deliberately:\n//\n// • in-flight coalescing — a query that is already running is joined rather\n// than reissued. Always safe: the answer cannot be staler than the request\n// that is still in the air.\n// • a SHORT time-to-live — covers the common case where the duplicates are\n// sequential rather than overlapping (a child mounts after its parent's\n// fetch has already resolved). This is the part that can serve stale data,\n// so it is kept to a few seconds and any write invalidates the whole cache.\n//\n// Errors are never cached: a failed fetch removes its entry so the next caller\n// retries for real instead of inheriting the failure.\n//\n// Nothing here knows a module or a field. The cache key is the request URL, so\n// scopes that differ in any way (id, clientId, view, group, action) are\n// different entries and can never be served each other's data.\n\n// Long enough to cover one page's mount burst, short enough that no realistic\n// user action fits inside it. Writes invalidate regardless — see below.\nconst DEFAULT_TTL_MS = 4000;\n\nconst entries = new Map(); // url -> { at, promise }\n\nlet ttlMs = DEFAULT_TTL_MS;\n\n/** Test/host hook: change the window, or set 0 to keep only in-flight coalescing. */\nexport function setFormGroupsCacheTtl(ms) {\n ttlMs = Math.max(0, Number(ms) || 0);\n}\n\n/**\n * Drop everything. Called after any module write: a save is exactly the moment\n * a cached payload stops describing the record, and a stale read here would\n * show the user their own change missing.\n */\nexport function invalidateFormGroupsCache() {\n entries.clear();\n}\n\n/**\n * fetchFormGroupsCached — run `loader()` for `url`, or join/replay a recent\n * identical run.\n *\n * `loader` returns the RAW payload, so every caller shares one request no\n * matter which shape it goes on to read out of it (the groups array, or the\n * whole envelope with recordMeta).\n */\nexport function fetchFormGroupsCached(url, loader) {\n const now = Date.now();\n const hit = entries.get(url);\n // A pending entry is always joinable; a settled one only within the window.\n if (hit && (hit.pending || now - hit.at < ttlMs)) return hit.promise;\n\n const entry = { at: now, pending: true };\n entry.promise = Promise.resolve()\n .then(loader)\n .then((value) => {\n entry.pending = false;\n // Stamp on COMPLETION, not on start: a slow request must not begin life\n // already half-expired, or a burst behind it would reissue immediately.\n entry.at = Date.now();\n return value;\n })\n .catch((err) => {\n // Never cache a failure — the next caller must get a real attempt.\n if (entries.get(url) === entry) entries.delete(url);\n throw err;\n });\n entries.set(url, entry);\n return entry.promise;\n}\n\nexport default { fetchFormGroupsCached, invalidateFormGroupsCache, setFormGroupsCacheTtl };\n"],"mappings":";AAoCA,IAAM,IAAiB,KAEjB,oBAAU,IAAI,IAAI,GAEpB,IAAQ;AAYZ,SAAgB,IAA4B;CAC1C,EAAQ,MAAM;AAChB;AAUA,SAAgB,EAAsB,GAAK,GAAQ;CACjD,IAAM,IAAM,KAAK,IAAI,GACf,IAAM,EAAQ,IAAI,CAAG;CAE3B,IAAI,MAAQ,EAAI,WAAW,IAAM,EAAI,KAAK,IAAQ,OAAO,EAAI;CAE7D,IAAM,IAAQ;EAAE,IAAI;EAAK,SAAS;CAAK;CAgBvC,OAfA,EAAM,UAAU,QAAQ,QAAQ,EAC7B,KAAK,CAAM,EACX,MAAM,OACL,EAAM,UAAU,IAGhB,EAAM,KAAK,KAAK,IAAI,GACb,EACR,EACA,OAAO,MAAQ;EAGd,MADI,EAAQ,IAAI,CAAG,MAAM,KAAO,EAAQ,OAAO,CAAG,GAC5C;CACR,CAAC,GACH,EAAQ,IAAI,GAAK,CAAK,GACf,EAAM;AACf"}
@@ -0,0 +1,2 @@
1
+ import { n as e } from "./formGroupsCache-CdBtIpe0.js";
2
+ export { e as invalidateFormGroupsCache };