(function (global) { const REMOTE_ENDPOINT = "https://training20251112.azurewebsites.net/api/message"; const runtimeCredentials = { create: "", update: "", search: "" }; function normalizeCredentialType(type) { return ["create", "update", "search"].includes(type) ? type : "create"; } function configureCredential(type, value) { if (value === undefined) { const sharedValue = String(type || "").trim(); Object.keys(runtimeCredentials).forEach((key) => { runtimeCredentials[key] = sharedValue; }); return Boolean(sharedValue); } const credentialType = normalizeCredentialType(type); runtimeCredentials[credentialType] = String(value || "").trim(); return Boolean(runtimeCredentials[credentialType]); } function clearCredential(type) { if (!type) { Object.keys(runtimeCredentials).forEach((key) => { runtimeCredentials[key] = ""; }); return; } runtimeCredentials[normalizeCredentialType(type)] = ""; } function hasCredential(type = "create") { return Boolean(runtimeCredentials[normalizeCredentialType(type)]); } function requireCredential(type) { const credentialType = normalizeCredentialType(type); if (!runtimeCredentials[credentialType]) { const credentialError = new Error(`A session-only ${credentialType} credential is required. Configure it in Profile > Synchronization.`); credentialError.code = "CREDENTIAL_REQUIRED"; credentialError.credentialType = credentialType; throw credentialError; } return runtimeCredentials[credentialType]; } function hashValue(value) { let hash = 2166136261; for (let index = 0; index < value.length; index += 1) { hash ^= value.charCodeAt(index); hash = Math.imul(hash, 16777619); } return (hash >>> 0).toString(16).padStart(8, "0"); } async function getJson(url, options = {}) { const response = await fetch(url, { method: "GET", cache: options.forceRefresh ? "no-store" : "force-cache", credentials: "omit", referrerPolicy: "no-referrer" }); if (!response.ok) { throw new Error(`Published data request failed with status ${response.status}.`); } return response.json(); } async function submitOperation(operationType, payload, context = {}) { const credentialType = context.credentialType || (/update|retry|upsert|inventory|placement|match|approve|monitor/i.test(operationType) ? "update" : "create"); const credential = requireCredential(credentialType); if (/^intake-record-(create|update)$/.test(operationType)) { const primaryKey = payload?.primaryKey; if (!primaryKey?.field || !String(primaryKey.value || "").trim()) { throw new Error("Intake submission requires a generated primary key."); } if (String(payload?.record?.[primaryKey.field] || "") !== String(primaryKey.value)) { throw new Error("Intake primary key does not match the serialized record."); } } const controller = new AbortController(); const timeoutId = global.setTimeout(() => controller.abort(), 20000); const operationId = context.operationId || global.crypto?.randomUUID?.() || `OP-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; const correlationId = context.correlationId || global.crypto?.randomUUID?.() || `COR-${Date.now()}-${Math.random().toString(36).slice(2, 10)}`; const idempotencyKey = context.idempotencyKey || `IDEM-${hashValue(`${operationType}|${context.organizationId || "unassigned"}|${JSON.stringify(payload)}`)}`; const organization = context.organization || ""; const body = { schemaVersion: "2.0", source: "BeaReponseCare portal", operationId, operationType, correlationId, idempotencyKey, sentAt: new Date().toISOString(), profileId: context.profileId || "", organization, tenantId: context.organizationId || `ORG-${organization.toUpperCase().replace(/[^A-Z0-9]+/g, "-").replace(/^-|-$/g, "") || "UNASSIGNED"}`, role: context.role || "", purposeOfUse: context.purposeOfUse || "", expectedVersion: context.expectedVersion ?? payload?.expectedPreviousVersion ?? null, governance: { dataClassification: context.dataClassification || "Sensitive care coordination demonstration data", minimumNecessary: context.minimumNecessary !== false, consentChecked: Boolean(context.consentChecked), authoritativeSubmission: false, trustBoundary: "Browser-originated prototype operation; server authorization is required for production" }, payload }; try { const response = await fetch(REMOTE_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": credential }, body: JSON.stringify(body), signal: controller.signal, cache: "no-store", credentials: "omit", referrerPolicy: "no-referrer" }); const responseText = await response.text(); let responseBody = responseText; try { responseBody = responseText ? JSON.parse(responseText) : null; } catch (error) { // The endpoint may return an acknowledgement as plain text. } if (!response.ok) { const detail = response.status === 401 || response.status === 403 ? "Remote authentication was rejected. Verify the configured endpoint credential." : `Remote submission failed with status ${response.status}.`; const requestError = new Error(detail); requestError.status = response.status; requestError.response = responseBody; throw requestError; } return { ok: true, status: response.status, response: responseBody, rowKey: responseBody && typeof responseBody === "object" ? responseBody.RowKey || responseBody.rowKey || "" : "", operationId, correlationId, idempotencyKey, submittedAt: body.sentAt }; } catch (error) { if (error.name === "AbortError") { const timeoutError = new Error("Remote submission timed out after 20 seconds."); timeoutError.code = "TIMEOUT"; timeoutError.operationId = operationId; timeoutError.correlationId = correlationId; timeoutError.idempotencyKey = idempotencyKey; throw timeoutError; } error.operationId = error.operationId || operationId; error.correlationId = error.correlationId || correlationId; error.idempotencyKey = error.idempotencyKey || idempotencyKey; throw error; } finally { global.clearTimeout(timeoutId); } } async function findRecordByReceipt(rowKey) { const normalizedRowKey = String(rowKey || "").trim(); if (!normalizedRowKey) { throw new Error("A remote receipt RowKey is required."); } const credential = requireCredential("search"); const controller = new AbortController(); const timeoutId = global.setTimeout(() => controller.abort(), 20000); try { const response = await fetch(REMOTE_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": credential }, body: JSON.stringify({ RowKey: normalizedRowKey }), signal: controller.signal, cache: "no-store", credentials: "omit", referrerPolicy: "no-referrer" }); const responseText = await response.text(); let responseBody = responseText; try { responseBody = responseText ? JSON.parse(responseText) : null; } catch (error) { // Preserve plain-text endpoint responses for inspection. } if (!response.ok) { const requestError = new Error(`Remote receipt lookup failed with status ${response.status}.`); requestError.status = response.status; requestError.response = responseBody; throw requestError; } return { ok: true, status: response.status, rowKey: normalizedRowKey, response: responseBody, fetchedAt: new Date().toISOString() }; } catch (error) { if (error.name === "AbortError") throw new Error("Remote receipt lookup timed out after 20 seconds."); throw error; } finally { global.clearTimeout(timeoutId); } } async function updateRecord(record) { const credential = requireCredential("update"); const controller = new AbortController(); const timeoutId = global.setTimeout(() => controller.abort(), 20000); try { const response = await fetch(REMOTE_ENDPOINT, { method: "POST", headers: { "Content-Type": "application/json", "x-api-key": credential }, body: JSON.stringify(record), signal: controller.signal, cache: "no-store", credentials: "omit", referrerPolicy: "no-referrer" }); const responseText = await response.text(); let responseBody = responseText; try { responseBody = responseText ? JSON.parse(responseText) : null; } catch (error) { // Preserve plain-text acknowledgements. } if (!response.ok) { const requestError = new Error(`Remote record update failed with status ${response.status}.`); requestError.status = response.status; requestError.response = responseBody; throw requestError; } return { ok: true, status: response.status, response: responseBody, submittedAt: new Date().toISOString() }; } catch (error) { if (error.name === "AbortError") throw new Error("Remote record update timed out after 20 seconds."); throw error; } finally { global.clearTimeout(timeoutId); } } global.BeaApiService = Object.freeze({ endpoint: REMOTE_ENDPOINT, configureCredential, clearCredential, hasCredential, credentialStatus: () => Object.fromEntries(Object.entries(runtimeCredentials).map(([key, value]) => [key, Boolean(value)])), getJson, findRecordByReceipt, updateRecord, submitOperation }); })(window);