@lssm/lib.contracts-runtime-core 3.0.1
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 +110 -0
- package/dist/approval.d.ts +13 -0
- package/dist/approval.js +2 -0
- package/dist/approval.test.d.ts +1 -0
- package/dist/browser/approval.js +1 -0
- package/dist/browser/collaboration.js +0 -0
- package/dist/browser/contracts-runtime-core.feature.js +1 -0
- package/dist/browser/engine/backoff.js +1 -0
- package/dist/browser/engine/cache-store.js +1 -0
- package/dist/browser/engine/conflict-registry.js +1 -0
- package/dist/browser/engine/conflict-resolution.js +1 -0
- package/dist/browser/engine/data-engine-host.js +1 -0
- package/dist/browser/engine/data-engine-test-helpers.js +1 -0
- package/dist/browser/engine/data-engine.js +1 -0
- package/dist/browser/engine/dedup.js +1 -0
- package/dist/browser/engine/events.js +1 -0
- package/dist/browser/engine/index.js +1 -0
- package/dist/browser/engine/maintenance.js +1 -0
- package/dist/browser/engine/mutation-engine.js +1 -0
- package/dist/browser/engine/mutation-queue.js +1 -0
- package/dist/browser/engine/mutation-types.js +0 -0
- package/dist/browser/engine/no-cache-store.js +1 -0
- package/dist/browser/engine/online-only-data-engine.js +1 -0
- package/dist/browser/engine/online-only-mutation-engine.js +1 -0
- package/dist/browser/engine/query-engine.js +1 -0
- package/dist/browser/engine/state.js +1 -0
- package/dist/browser/engine/types.js +0 -0
- package/dist/browser/forms-platform-csv-columns.js +1 -0
- package/dist/browser/forms-platform-csv.js +2 -0
- package/dist/browser/forms-platform-management.js +1 -0
- package/dist/browser/forms-platform-navigation.js +1 -0
- package/dist/browser/forms-platform-submission.js +1 -0
- package/dist/browser/forms-platform-types.js +1 -0
- package/dist/browser/forms-platform-utils.js +1 -0
- package/dist/browser/forms-platform.js +2 -0
- package/dist/browser/index.js +3 -0
- package/dist/browser/local-offline.js +1 -0
- package/dist/browser/observability.js +1 -0
- package/dist/browser/reachability.js +1 -0
- package/dist/browser/redaction.js +1 -0
- package/dist/browser/storage/indexeddb.web.js +1 -0
- package/dist/browser/storage/keyed-store.js +1 -0
- package/dist/browser/storage/memory.js +1 -0
- package/dist/browser/storage/port.js +1 -0
- package/dist/browser/transport.js +2 -0
- package/dist/collaboration.d.ts +57 -0
- package/dist/collaboration.js +1 -0
- package/dist/contracts-runtime-core.feature.d.ts +1 -0
- package/dist/contracts-runtime-core.feature.js +2 -0
- package/dist/engine/backoff.d.ts +16 -0
- package/dist/engine/backoff.js +2 -0
- package/dist/engine/backoff.test.d.ts +1 -0
- package/dist/engine/cache-store.d.ts +19 -0
- package/dist/engine/cache-store.js +2 -0
- package/dist/engine/conflict-registry.d.ts +10 -0
- package/dist/engine/conflict-registry.js +2 -0
- package/dist/engine/conflict-registry.test.d.ts +1 -0
- package/dist/engine/conflict-resolution.d.ts +24 -0
- package/dist/engine/conflict-resolution.js +2 -0
- package/dist/engine/conflict-resolution.test.d.ts +1 -0
- package/dist/engine/data-engine-host.d.ts +30 -0
- package/dist/engine/data-engine-host.js +2 -0
- package/dist/engine/data-engine-local-offline.test.d.ts +1 -0
- package/dist/engine/data-engine-test-helpers.d.ts +30 -0
- package/dist/engine/data-engine-test-helpers.js +2 -0
- package/dist/engine/data-engine.d.ts +46 -0
- package/dist/engine/data-engine.js +2 -0
- package/dist/engine/data-engine.test.d.ts +1 -0
- package/dist/engine/dedup.d.ts +9 -0
- package/dist/engine/dedup.js +2 -0
- package/dist/engine/events.d.ts +76 -0
- package/dist/engine/events.economic-evidence.test.d.ts +1 -0
- package/dist/engine/events.js +2 -0
- package/dist/engine/index.d.ts +17 -0
- package/dist/engine/index.js +2 -0
- package/dist/engine/maintenance.d.ts +18 -0
- package/dist/engine/maintenance.js +2 -0
- package/dist/engine/maintenance.test.d.ts +1 -0
- package/dist/engine/mutation-engine.d.ts +2 -0
- package/dist/engine/mutation-engine.js +2 -0
- package/dist/engine/mutation-queue.d.ts +16 -0
- package/dist/engine/mutation-queue.js +2 -0
- package/dist/engine/mutation-queue.test.d.ts +1 -0
- package/dist/engine/mutation-types.d.ts +118 -0
- package/dist/engine/mutation-types.js +1 -0
- package/dist/engine/no-cache-store.d.ts +3 -0
- package/dist/engine/no-cache-store.js +2 -0
- package/dist/engine/online-only-data-engine.d.ts +9 -0
- package/dist/engine/online-only-data-engine.js +2 -0
- package/dist/engine/online-only-mutation-engine.d.ts +3 -0
- package/dist/engine/online-only-mutation-engine.js +2 -0
- package/dist/engine/query-engine.d.ts +7 -0
- package/dist/engine/query-engine.js +2 -0
- package/dist/engine/query-engine.test.d.ts +1 -0
- package/dist/engine/state.d.ts +37 -0
- package/dist/engine/state.js +2 -0
- package/dist/engine/types.d.ts +70 -0
- package/dist/engine/types.js +1 -0
- package/dist/forms-platform-choice.test.d.ts +1 -0
- package/dist/forms-platform-csv-columns.d.ts +7 -0
- package/dist/forms-platform-csv-columns.js +2 -0
- package/dist/forms-platform-csv.d.ts +12 -0
- package/dist/forms-platform-csv.js +3 -0
- package/dist/forms-platform-management.d.ts +12 -0
- package/dist/forms-platform-management.js +2 -0
- package/dist/forms-platform-navigation.d.ts +2 -0
- package/dist/forms-platform-navigation.js +2 -0
- package/dist/forms-platform-submission.d.ts +7 -0
- package/dist/forms-platform-submission.js +2 -0
- package/dist/forms-platform-types.d.ts +51 -0
- package/dist/forms-platform-types.js +2 -0
- package/dist/forms-platform-utils.d.ts +6 -0
- package/dist/forms-platform-utils.js +2 -0
- package/dist/forms-platform-utils.test.d.ts +1 -0
- package/dist/forms-platform.d.ts +19 -0
- package/dist/forms-platform.js +3 -0
- package/dist/forms-platform.test.d.ts +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.js +4 -0
- package/dist/local-offline.d.ts +17 -0
- package/dist/local-offline.js +2 -0
- package/dist/local-offline.test.d.ts +1 -0
- package/dist/native/approval.js +130 -0
- package/dist/native/collaboration.js +0 -0
- package/dist/native/contracts-runtime-core.feature.js +17 -0
- package/dist/native/engine/backoff.js +16 -0
- package/dist/native/engine/cache-store.js +49 -0
- package/dist/native/engine/conflict-registry.js +51 -0
- package/dist/native/engine/conflict-resolution.js +81 -0
- package/dist/native/engine/data-engine-host.js +1119 -0
- package/dist/native/engine/data-engine-test-helpers.js +117 -0
- package/dist/native/engine/data-engine.js +1106 -0
- package/dist/native/engine/dedup.js +22 -0
- package/dist/native/engine/events.js +19 -0
- package/dist/native/engine/index.js +1189 -0
- package/dist/native/engine/maintenance.js +36 -0
- package/dist/native/engine/mutation-engine.js +360 -0
- package/dist/native/engine/mutation-queue.js +26 -0
- package/dist/native/engine/mutation-types.js +0 -0
- package/dist/native/engine/no-cache-store.js +15 -0
- package/dist/native/engine/online-only-data-engine.js +422 -0
- package/dist/native/engine/online-only-mutation-engine.js +70 -0
- package/dist/native/engine/query-engine.js +360 -0
- package/dist/native/engine/state.js +116 -0
- package/dist/native/engine/types.js +0 -0
- package/dist/native/forms-platform-csv-columns.js +31 -0
- package/dist/native/forms-platform-csv.js +150 -0
- package/dist/native/forms-platform-management.js +179 -0
- package/dist/native/forms-platform-navigation.js +27 -0
- package/dist/native/forms-platform-submission.js +207 -0
- package/dist/native/forms-platform-types.js +12 -0
- package/dist/native/forms-platform-utils.js +69 -0
- package/dist/native/forms-platform.js +429 -0
- package/dist/native/index.js +1879 -0
- package/dist/native/local-offline.js +81 -0
- package/dist/native/observability.js +182 -0
- package/dist/native/reachability.js +28 -0
- package/dist/native/redaction.js +44 -0
- package/dist/native/storage/async-storage.native.js +91 -0
- package/dist/native/storage/keyed-store.js +74 -0
- package/dist/native/storage/memory.js +26 -0
- package/dist/native/storage/port.js +16 -0
- package/dist/native/transport.js +27 -0
- package/dist/node/approval.js +1 -0
- package/dist/node/collaboration.js +0 -0
- package/dist/node/contracts-runtime-core.feature.js +1 -0
- package/dist/node/engine/backoff.js +1 -0
- package/dist/node/engine/cache-store.js +1 -0
- package/dist/node/engine/conflict-registry.js +1 -0
- package/dist/node/engine/conflict-resolution.js +1 -0
- package/dist/node/engine/data-engine-host.js +1 -0
- package/dist/node/engine/data-engine-test-helpers.js +1 -0
- package/dist/node/engine/data-engine.js +1 -0
- package/dist/node/engine/dedup.js +1 -0
- package/dist/node/engine/events.js +1 -0
- package/dist/node/engine/index.js +1 -0
- package/dist/node/engine/maintenance.js +1 -0
- package/dist/node/engine/mutation-engine.js +1 -0
- package/dist/node/engine/mutation-queue.js +1 -0
- package/dist/node/engine/mutation-types.js +0 -0
- package/dist/node/engine/no-cache-store.js +1 -0
- package/dist/node/engine/online-only-data-engine.js +1 -0
- package/dist/node/engine/online-only-mutation-engine.js +1 -0
- package/dist/node/engine/query-engine.js +1 -0
- package/dist/node/engine/state.js +1 -0
- package/dist/node/engine/types.js +0 -0
- package/dist/node/forms-platform-csv-columns.js +1 -0
- package/dist/node/forms-platform-csv.js +2 -0
- package/dist/node/forms-platform-management.js +1 -0
- package/dist/node/forms-platform-navigation.js +1 -0
- package/dist/node/forms-platform-submission.js +1 -0
- package/dist/node/forms-platform-types.js +1 -0
- package/dist/node/forms-platform-utils.js +1 -0
- package/dist/node/forms-platform.js +2 -0
- package/dist/node/index.js +3 -0
- package/dist/node/local-offline.js +1 -0
- package/dist/node/observability.js +1 -0
- package/dist/node/reachability.js +1 -0
- package/dist/node/redaction.js +1 -0
- package/dist/node/storage/keyed-store.js +1 -0
- package/dist/node/storage/memory.js +1 -0
- package/dist/node/storage/port.js +1 -0
- package/dist/node/transport.js +2 -0
- package/dist/observability.d.ts +34 -0
- package/dist/observability.js +2 -0
- package/dist/observability.test.d.ts +1 -0
- package/dist/reachability.d.ts +63 -0
- package/dist/reachability.js +2 -0
- package/dist/reachability.test.d.ts +1 -0
- package/dist/redaction.d.ts +34 -0
- package/dist/redaction.js +2 -0
- package/dist/redaction.test.d.ts +1 -0
- package/dist/storage/async-storage.native.d.ts +23 -0
- package/dist/storage/async-storage.native.test.d.ts +1 -0
- package/dist/storage/indexeddb.web.d.ts +13 -0
- package/dist/storage/indexeddb.web.js +2 -0
- package/dist/storage/keyed-store.d.ts +25 -0
- package/dist/storage/keyed-store.js +2 -0
- package/dist/storage/keyed-store.test.d.ts +1 -0
- package/dist/storage/memory.d.ts +11 -0
- package/dist/storage/memory.js +2 -0
- package/dist/storage/memory.test.d.ts +1 -0
- package/dist/storage/port.d.ts +34 -0
- package/dist/storage/port.js +2 -0
- package/dist/transport.d.ts +53 -0
- package/dist/transport.js +3 -0
- package/package.json +219 -0
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{OPERATION_EXECUTION_EFFECTS as Ce}from"@lssm/lib.contracts-spec/operations";function he(e=()=>new Date){let t=new Map;return{async consume(o,n){let r=e().getTime();for(let[a,i]of t)if(i<=r)t.delete(a);if(t.has(o))return!1;return t.set(o,Date.parse(n)),!0}}}function Fe(e={}){let t=e.now??(()=>new Date),o=e.nonceStore??he(t);return{async authorize(n){let r=await Pe(n,t(),e.verifyEvidence);if(r)return r;if(!await o.consume(n.receipt.nonce,n.receipt.expiresAt))return{effect:"deny",reason:"replayed_receipt"};return{effect:"allow",receiptId:n.receipt.id,nonce:n.receipt.nonce}}}}async function we(e){let t=new TextEncoder().encode(Z(e)),o=await globalThis.crypto.subtle.digest("SHA-256",t);return`sha256:${Array.from(new Uint8Array(o),(n)=>n.toString(16).padStart(2,"0")).join("")}`}async function Pe(e,t,o){let{receipt:n}=e,r=Date.parse(n.issuedAt),a=Date.parse(n.expiresAt);if(!n.id||!n.nonce||!n.issuer||!n.evidenceRef||!Me(n.subject)||n.effects.some((s)=>!Ce.includes(s))||!Number.isFinite(r)||!Number.isFinite(a)||a<=r)return F("malformed_receipt");if(a<=t.getTime())return F("expired_receipt");if(r>t.getTime())return F("not_yet_valid");if(!ve(n.subject,e.subject,["actor","userId"]))return F("wrong_subject");if(!ve(n.subject,e.subject,["tenantId","workspaceId","organizationId"]))return F("wrong_tenant");if(n.operation.key!==e.operation.key||n.operation.version!==e.operation.version)return F("wrong_operation");if(n.inputDigest!==await we(e.input))return F("wrong_input");let i=new Set(n.effects);if(e.requiredEffects.some((s)=>!i.has(s)))return F("insufficient_effects");if(!xe(n,e))return F("insufficient_cost_ceiling");if(o&&!await o(n))return F("invalid_evidence");return}function ve(e,t,o){return o.every((n)=>{let r=t[n];return r==null||e[n]===r})}function xe(e,t){if(!t.requestedCost)return!0;if(!Number.isFinite(t.requestedCost.amount)||t.requestedCost.amount<0)return!1;let o=e.costCeiling;if(!o||!Number.isFinite(o.amount)||o.amount<0)return!1;return o.amount>=t.requestedCost.amount&&(!t.requestedCost.currency||o.currency===t.requestedCost.currency)&&(!t.requestedCost.unit||o.unit===t.requestedCost.unit)}function Me(e){return Object.values(e).some((t)=>typeof t==="string"&&t.length>0)}function F(e){return{effect:"deny",reason:e}}function Z(e){if(e===null||typeof e==="string"||typeof e==="boolean")return JSON.stringify(e);if(typeof e==="number"){if(!Number.isFinite(e))throw TypeError("Approval input is not JSON.");return JSON.stringify(e)}if(Array.isArray(e))return`[${e.map(Z).join(",")}]`;if(e instanceof Date)return JSON.stringify(e.toISOString());if(typeof e==="object")return`{${Object.entries(e).filter(([,o])=>o!==void 0).sort(([o],[n])=>o.localeCompare(n)).map(([o,n])=>`${JSON.stringify(o)}:${Z(n)}`).join(",")}}`;throw TypeError("Approval input is not JSON.")}import{defineFeature as Ie}from"@lssm/lib.contracts-spec/features";var Qe=Ie({meta:{key:"libs.contracts-runtime-core",version:"1.0.0",title:"Contracts Runtime Core",description:"Framework-neutral in-house query/cache/offline engine and Transport port for ContractSpec data fetching.",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["package","libs","contracts-runtime-core"],stability:"experimental"}});function ee(e,t={}){let o=t.baseMs??200,n=t.factor??2,r=t.maxMs??30000,a=t.jitter??Math.random,i=Math.max(0,e-1),s=Math.min(r,o*n**i);return Math.round(s*(0.5+a()*0.5))}import{invalidationTagMatches as Le}from"@lssm/lib.contracts-spec/query/invalidation";var be="cache:";function _(e){let t=new Map,o=(n)=>`${be}${n}`;return{read:(n)=>e.get(o(n)),write:async(n)=>{t.set(n.key,n.tags),await e.set(o(n.key),n)},remove:async(n)=>{t.delete(n),await e.remove(o(n))},entries:async()=>{let n=await e.keys(be),r=[];for(let a of n){let i=await e.get(a);if(i!==void 0)r.push(i)}return r},invalidate:async(n,r)=>{let a=[];for(let[i,s]of t.entries()){if(!s.some((d)=>n.some((g)=>Le(d,g))))continue;let p=await e.get(o(i));if(p===void 0)continue;await e.set(o(i),{...p,status:"stale",staleAt:r}),a.push(i)}return a}}}function Ne(e){let{serverData:t,localData:o}=e,n=e.mergeFields??[];if(typeof t!=="object"||t===null||typeof o!=="object"||o===null)return{outcome:"surface"};let r={...t},a=o;for(let i of n)if(i in a)r[i]=a[i];return{outcome:"merged",data:r}}function q(){let e=new Map;return{register(t,o){e.set(t,o)},has(t){return e.has(t)},async resolve(t){switch(t.strategy){case"server-wins":return{outcome:"use-server",data:t.serverData};case"client-wins":return{outcome:"use-client",data:t.localData};case"field-merge":return Ne(t);case"surface":return{outcome:"surface"};default:{let o=t.policy.resolver,n=o===void 0?void 0:e.get(o);if(n===void 0)return{outcome:"surface"};return n(t)}}}}}import{defaultConflictPolicy as _e}from"@lssm/lib.contracts-spec/query/conflict-policy";async function V(e,t){for(let o of e)if(o.previous===void 0)await t.remove(o.key);else await t.write(o.previous)}async function te(e,t,o){let n=e.conflictPolicy??_e(),r=e.rollback[0]?.key,a=r===void 0?void 0:await o.cache.read(r),i=t.metadata?.serverData,s=await o.resolvers.resolve({strategy:n.strategy,policy:n,localData:a?.data,serverData:i,localVersion:e.versionToken,serverVersion:t.versionToken,mergeFields:n.mergeFields});switch(o.events.emit({type:"mutation-conflict",id:e.id,strategy:n.strategy,outcome:s.outcome}),s.outcome){case"use-server":{await V(e.rollback,o.cache),await o.cache.invalidate(e.tags,o.now());for(let u of e.rollback)await o.queryEngine?.refresh(u.key);return"resolved"}case"use-client":return await o.queue.update({...e,versionToken:t.versionToken,status:"pending"}),"re-enqueued";case"merged":{if(r!==void 0&&a!==void 0)await o.cache.write({...a,data:s.data,versionToken:t.versionToken,status:"warm",storedAt:o.now()}),await o.queryEngine?.refresh(r);return await o.queue.update({...e,versionToken:t.versionToken,status:"pending"}),"re-enqueued"}default:{if(r!==void 0)await o.queryEngine?.emitConflict(r,{versionToken:t.versionToken,serverData:i,clientData:a?.data,strategy:n.strategy});return"resolved"}}}var qe=new Set(["online-only","memory-cache","offline-capable"]),Ee={"online-only":"online-only",online:"online-only",disabled:"online-only",false:"online-only",off:"online-only","memory-cache":"memory-cache",memory:"memory-cache",cache:"memory-cache","offline-capable":"offline-capable",offline:"offline-capable",enabled:"offline-capable",true:"offline-capable",on:"offline-capable"};function Ve(e){return typeof e==="string"&&qe.has(e)}function U(e={}){let t=e.defaultMode===void 0?{mode:"online-only",source:"default",reason:"No local/offline policy input was supplied."}:ne(e.defaultMode,"explicit")??{mode:"online-only",source:"explicit"},o=ne(Ue(e.env),"env",{allowUnknown:!0});return ne(e.flag,"feature-flag")??o??t}function Ue(e){if(typeof e!=="object"||e===null)return e;if("CONTRACTSPEC_LOCAL_OFFLINE"in e)return e.CONTRACTSPEC_LOCAL_OFFLINE;if("mode"in e)return e;return}function ne(e,t,o={}){if(e===void 0||e===null||e==="")return;if(typeof e==="boolean")return{mode:e?"offline-capable":"online-only",source:t};if(typeof e==="string"){let n=Ee[e.trim().toLowerCase()];if(n!==void 0)return{mode:n,source:t};if(o.allowUnknown)return}if(typeof e==="object"&&e!==null&&"mode"in e){let n=e,r=typeof n.mode==="string"?Ee[n.mode.trim().toLowerCase()]:void 0;if(r!==void 0)return{mode:r,source:t,...typeof n.reason==="string"?{reason:n.reason}:{}};if(o.allowUnknown)return}throw Error(`Unsupported localOffline policy value: ${String(e)}`)}function A(e){return e!=="offline"}function I(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var je={id:"docs.contracts-runtime-core.reachability",title:"Reachability input port (tri-state)",summary:"Engine-owned reachability input port; integrations feed it, the engine never imports them.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/reachability",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","reachability","offline","port"],body:"Tri-state OnlineSignal ('online' | 'restricted' | 'offline'). 'online' allows normal fetch + queue drain. 'restricted' is online-degraded: the engine fetches and drains the queue as if online but may mark results stale-eligible; it never suspends network nor activates the offline queue. Only 'offline' suspends outbound network and activates the durable offline mutation queue. The engine reads reachability solely through ReachabilitySource and MUST NOT import integrations/* — concrete sources (runtime-local networkReachability) are wired via an integration→port adapter (plan §2.3/§4.2, Architect finding 1). isNetworkAllowed mirrors runtime-local's outboundNetworkAllowed = reachability !== 'offline'. createStaticReachabilitySource('online') is the B1a default and online test fixture."};function j(){let e=new Map;return{get:async(t)=>{let o=e.get(t);return o===void 0?void 0:structuredClone(o)},set:async(t,o)=>{e.set(t,structuredClone(o))},remove:async(t)=>{e.delete(t)},keys:async(t)=>{let o=[...e.keys()];return t===void 0?o:o.filter((n)=>n.startsWith(t))},clear:async()=>{e.clear()}}}function Q(){let e=new Set;return{emit(t){for(let o of e)o(t)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}}function oe(e){return{async collectGarbage(){let t=e.now(),o=0;for(let n of await e.cache.entries())if(n.expiresAt!==void 0&&t>=n.expiresAt)await e.cache.remove(n.key),o+=1;return e.events.emit({type:"gc-collected",evicted:o}),o},async revalidateStale(){let t=e.revalidate;if(t===void 0)return 0;let o=e.now(),n=0;for(let r of await e.cache.entries()){if(!(r.staleAt!==void 0&&o>=r.staleAt)||r.envelope===void 0)continue;await t(r.envelope),e.events.emit({type:"revalidated",key:r.key}),n+=1}return n}}}import{mergeSafeEconomicEvidenceRefIds as Ke,parseSafeEconomicEvidenceRefId as $e}from"@lssm/lib.contracts-spec/operations";import{defaultConflictPolicy as Be}from"@lssm/lib.contracts-spec/query/conflict-policy";var ze=5;function re(...e){return Ke(...e)}function Je(e){let t=$e(e);if(!t)throw Error("EconomicEvidenceInvalidRef: unsafe or invalid economic evidence ref");return t}function ie(e){let{transport:t,cache:o,queue:n,reachability:r,resolvers:a,events:i}=e,s=e.now??(()=>Date.now()),u=e.maxAttempts??ze,p={cache:o,queue:n,resolvers:a,events:i,now:s,queryEngine:e.queryEngine},d=!1,g=0,D=null,k=new Map,W=(c,m=c.economicEvidenceRefs)=>({operation:c.operation,input:c.input,tags:c.tags,economicEvidenceRefs:m});async function S(c){let m=[];for(let y of c){let b=await o.read(y.key);m.push({key:y.key,previous:b}),await o.write({key:y.key,data:y.data,versionToken:y.versionToken??b?.versionToken,status:"warm",tags:b?.tags??[],storedAt:s(),staleAt:b?.staleAt,expiresAt:b?.expiresAt}),await e.queryEngine?.refresh(y.key)}return m}async function T(c,m){for(let y of c.rollback){let b=await o.read(y.key);if(b!==void 0)await o.write({...b,versionToken:m.versionToken??b.versionToken,status:"stale",staleAt:s()});await e.queryEngine?.refresh(y.key)}await o.invalidate(c.tags,s())}async function h(c){let m;try{m=re(c.economicEvidenceRefs)}catch{await V(c.rollback,o);for(let O of c.rollback)await e.queryEngine?.refresh(O.key);return await n.remove(c.id),i.emit({type:"mutation-failed",id:c.id,code:"ECONOMIC_EVIDENCE_INVALID_REF"}),k.set(c.id,{id:c.id,status:"failed",problem:{type:"about:blank",title:"Economic evidence ref rejected",status:422,code:"ECONOMIC_EVIDENCE_INVALID_REF",category:"validation",severity:"error",retryable:!1}}),!0}let y=c.attempts+1;await n.update({...c,status:"in-flight",attempts:y});let b=await t.execute(W(c,m),e.contextProvider?.());if(b.ok){let O=re(m,b.data.economicEvidenceRefs);if(await T(c,b.data),await n.remove(c.id),i.emit({type:"mutation-replayed",id:c.id,queueDepth:await n.depth()}),O.length>0)i.emit({type:"economic-evidence-attached",operationKey:c.operation.key,evidenceRefs:O.map(Je)});return k.set(c.id,{id:c.id,status:"done",data:b.data.data,economicEvidenceRefs:O}),!0}let{problem:R}=b;if(R.category==="auth"){if(d=!0,await n.update({...c,status:"paused-auth",attempts:y}),i.emit({type:"auth-expired",id:c.id}),k.set(c.id,{id:c.id,status:"paused-auth",problem:R}),e.refreshAuth!==void 0&&await e.refreshAuth())return d=!1,await n.update({...c,status:"pending",attempts:y}),!0;return!1}if(R.category==="conflict"){if(await te(c,R,p)==="resolved")await n.remove(c.id),k.set(c.id,{id:c.id,status:"done",problem:R});return!0}if(R.retryable&&y<u){let O=ee(y,e.backoff);return await n.update({...c,status:"failed",attempts:y}),i.emit({type:"mutation-retry",id:c.id,attempt:y,delayMs:O}),k.set(c.id,{id:c.id,status:"failed",problem:R}),e.schedule?.(()=>{v()},O),!0}await V(c.rollback,o);for(let O of c.rollback)await e.queryEngine?.refresh(O.key);return await n.remove(c.id),i.emit({type:"mutation-failed",id:c.id,code:b.code}),k.set(c.id,{id:c.id,status:"failed",problem:R}),!0}async function f(){if(d)return;if(!A(r.getSnapshot().signal))return;for(let c of await n.list()){if(c.status==="paused-auth")continue;if(!await h(c))return}i.emit({type:"queue-drained",remaining:await n.depth()})}function v(){if(D!==null)return D;return D=f().finally(()=>{D=null}),D}async function l(c,m={}){re(c.economicEvidenceRefs);let y=s();g+=1;let b=`${y}-${g}`,R=m.optimistic?await S(m.optimistic):[];if(await n.enqueue({...c,conflictPolicy:c.conflictPolicy??Be(),id:b,enqueuedAt:y,attempts:0,status:"pending",rollback:R}),i.emit({type:"mutation-enqueued",id:b,queueDepth:await n.depth()}),!d&&A(r.getSnapshot().signal))await v();return k.get(b)??{id:b,status:"pending"}}async function w(){d=!1;for(let c of await n.list())if(c.status==="paused-auth")await n.update({...c,status:"pending"});await v()}let C=r.subscribe((c)=>{if(!d&&A(c.signal))v()});return{mutate:l,drain:v,resume:w,dispose:C}}function ae(e){let t=(n)=>`queue:${n}`;async function o(){let n=await e.keys("queue:"),r=[];for(let a of n){let i=await e.get(a);if(i!==void 0)r.push(i)}return r.sort((a,i)=>a.enqueuedAt===i.enqueuedAt?a.id.localeCompare(i.id):a.enqueuedAt-i.enqueuedAt),r}return{enqueue:(n)=>e.set(t(n.id),n),update:(n)=>e.set(t(n.id),n),remove:(n)=>e.remove(t(n)),list:()=>o(),depth:async()=>(await e.keys("queue:")).length}}import{createQueryKey as se}from"@lssm/lib.contracts-spec/query/key";function P(e,t){if(e?.staleAt===void 0)return!1;return t>=e.staleAt}function L(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function K(e,t){if(e===void 0)return{status:"loading",fetchStatus:"fetching",cacheStatus:"empty",isStale:!1,isOffline:!1};let o=P(e,t);return{status:"success",fetchStatus:"fetching",data:e.data,cacheStatus:o?"stale":e.status,isStale:o,isOffline:!1,versionToken:e.versionToken,dataUpdatedAt:e.storedAt,...e.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function M(e,t){let o=P(e,t);return{status:"success",fetchStatus:"idle",data:e.data,cacheStatus:o?"stale":e.status,isStale:o,isOffline:!1,versionToken:e.versionToken,dataUpdatedAt:e.storedAt,...e.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function B(e,t,o){return{status:"error",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:P(t,o),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:o}}function z(e,t,o){return{status:"auth-expired",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:P(t,o),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:o}}function J(e,t,o){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"conflict",isStale:P(e,o),isOffline:!1,conflict:t,versionToken:t.versionToken??e?.versionToken,dataUpdatedAt:e?.storedAt}}function G(e,t){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"offline",isStale:P(e,t),isOffline:!0,versionToken:e?.versionToken,dataUpdatedAt:e?.storedAt,...e?.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function Se(e,t){let{transport:o}=e,n=e.advanced??{},r=n.now??(()=>Date.now()),a=n.reachability??I("online"),i=n.events??Q(),s=n.resolvers??q(),u=t??U({defaultMode:e.localOffline,env:e.env,flag:e.flag}),p=new Map,d=new Map,g=0;function D(S,T){p.set(S,T);for(let h of d.get(S)??[])h(T)}return{...{async query(S,T){let h=se(S),f=a.getSnapshot().signal;if(!A(f)){let m=G(void 0,r());return D(h,m),m}D(h,K(void 0,r()));let v=r(),l=await o.execute(S,T),w=r();if(!l.ok){i.emit({type:"fetch-error",key:h,code:l.code,latencyMs:w-v});let m=l.problem.category==="auth"?z(l.problem,void 0,w):B(l.problem,void 0,w);return D(h,m),m}i.emit({type:"fetch-success",key:h,latencyMs:w-v});let C={key:h,data:l.data.data,versionToken:l.data.versionToken??l.versionToken,status:f==="restricted"?"warm":"fresh",tags:[],storedAt:w,...l.data.pageInfo===void 0?{}:{pageInfo:l.data.pageInfo}},c=M(C,w);return D(h,c),c},getState(S){return p.get(se(S))??L()},subscribe(S,T){let h=se(S),f=d.get(h);if(f===void 0)f=new Set,d.set(h,f);return f.add(T),()=>f?.delete(T)},async invalidateTags(){return[]},async refresh(S){let T=p.get(S)??L();return D(S,T),T},async emitConflict(S,T){let h=J(void 0,T,r());return D(S,h),h}},...{async mutate(S,T={}){g+=1;let h=`${r()}-${g}`;if(!A(a.getSnapshot().signal))return{id:h,status:"disabled-offline",problem:Ge()};let f=await o.execute({operation:S.operation,input:S.input,tags:S.tags},T.ctx??n.contextProvider?.());if(f.ok)return{id:h,status:"done",data:f.data.data};if(f.problem.category==="auth")return{id:h,status:"paused-auth",problem:f.problem};return{id:h,status:"failed",problem:f.problem}},async drain(){},async resume(){},dispose(){}},maintenance:{collectGarbage:async()=>0,revalidateStale:async()=>0},events:i,resolvers:s,localOffline:u}}function Ge(){return{type:"about:blank",title:"Offline mutation queue disabled",status:409,code:"OFFLINE_QUEUE_DISABLED",category:"conflict",severity:"warning",retryable:!1,detail:"localOffline=online-only does not cache local data or queue offline mutations."}}import{createQueryKey as ue}from"@lssm/lib.contracts-spec/query/key";function ce(){let e=new Map;return{run(t,o){let n=e.get(t);if(n!==void 0)return n;let r=o().finally(()=>{e.delete(t)});return e.set(t,r),r},size(){return e.size}}}var He=0;function le(e){let{transport:t}=e,o=e.storage??j(),n=e.reachability??I("online"),r=e.now??(()=>Date.now()),a=e.defaultStaleTimeMs??He,i=e.cacheStore??_(o),s=e.events??Q(),u=ce(),p=new Map,d=new Map;function g(f,v){p.set(f,v);let l=d.get(f);if(l===void 0)return;for(let w of l)w(v)}async function D(f,v){let l=ue(f),w=n.getSnapshot().signal,C=await i.read(l);if(!A(w)){let x=G(C,r());return g(l,x),x}if(C!==void 0&&!P(C,r())){s.emit({type:"cache-hit",key:l});let x=M(C,r());return g(l,x),x}s.emit({type:"cache-miss",key:l}),g(l,K(C,r()));let c=r(),m=await u.run(l,()=>t.execute(f,v)),y=r();if(!m.ok){s.emit({type:"fetch-error",key:l,code:m.code,latencyMs:y-c});let x=m.problem.category==="auth"?z(m.problem,C,y):B(m.problem,C,y);return g(l,x),x}s.emit({type:"fetch-success",key:l,latencyMs:y-c});let b=f.cache?.staleTimeMs??a,R=f.cache?.cacheTimeMs,O={key:l,data:m.data.data,versionToken:m.data.versionToken??m.versionToken,status:w==="restricted"?"warm":"fresh",tags:f.tags??[],storedAt:y,staleAt:y+b,envelope:f,...R===void 0?{}:{expiresAt:y+R},...m.data.pageInfo===void 0?{}:{pageInfo:m.data.pageInfo}};await i.write(O);let ge=M(O,y);return g(l,ge),ge}async function k(f){let v=await i.read(f),l=v===void 0?L():M(v,r());return g(f,l),l}async function W(f,v){let l=await i.read(f),w=J(l,v,r());return g(f,w),w}function S(f){return p.get(ue(f))??L()}function T(f,v){let l=ue(f),w=d.get(l);if(w===void 0)w=new Set,d.set(l,w);return w.add(v),()=>{d.get(l)?.delete(v)}}async function h(f){let v=await i.invalidate(f,r());for(let l of v){let w=await i.read(l);if(w!==void 0)g(l,M(w,r()))}return v}return{query:D,getState:S,subscribe:T,invalidateTags:h,refresh:k,emitConflict:W}}function fe(e){Xe(e);let t=e.advanced??{},o=U({defaultMode:e.localOffline,env:e.env,flag:e.flag});if(We(o.mode,t),o.mode==="online-only")return Se(e,o);let n=t.now??(()=>Date.now()),r=t.storage??j(),a=t.events??Q(),i=t.cacheStore??_(r),s=t.reachability??I("online"),u=t.resolvers??q(),p=ae(r),d=le({...t,transport:e.transport,now:n,storage:r,events:a,cacheStore:i,reachability:s}),g=ie({transport:e.transport,cache:i,queue:p,reachability:s,resolvers:u,events:a,now:n,maxAttempts:t.maxAttempts,queryEngine:d,contextProvider:t.contextProvider,refreshAuth:t.refreshAuth,schedule:t.schedule,backoff:t.backoff}),D=oe({cache:i,events:a,now:n,revalidate:(k)=>d.query(k)});return{...d,...g,maintenance:D,events:a,resolvers:u,localOffline:o}}function We(e,t){if(e==="offline-capable"){if(t.storage===void 0)throw Error("localOffline=offline-capable requires advanced.storage; pass a durable StoragePort or explicit platform storage adapter.");return}if(t.storage!==void 0)throw Error(`localOffline=${e} rejects advanced.storage; this mode must not use durable local persistence.`);if(t.cacheStore!==void 0)throw Error(`localOffline=${e} rejects advanced.cacheStore; this mode owns cache semantics.`)}function Xe(e){let t=e;if(t.storage!==void 0)throw Error("Use advanced.storage instead of top-level storage.");if(t.cacheStore!==void 0)throw Error("Use advanced.cacheStore instead of top-level cacheStore.")}function De(){return globalThis.process?.env}function Ye(e){return fe({...e,env:e.env??De()})}function Ze(){return{read:async(e)=>{return},write:async()=>{},remove:async()=>{},entries:async()=>[],invalidate:async(e,t)=>[]}}function et(){return{type:"https://contractspec.io/problems/offline-queue-disabled",title:"Offline mutation queue disabled",status:503,code:"OFFLINE_QUEUE_DISABLED",category:"policy",severity:"warning",retryable:!0,detail:"localOffline is online-only; offline mutations are not queued or replayed."}}var tt=(e)=>({operation:e.operation,input:e.input,tags:e.tags});function nt(e){let t=e.now??(()=>Date.now()),o=0;async function n(r,a={}){o+=1;let i=`${t()}-${o}`;if(!A(e.reachability.getSnapshot().signal))return{id:i,status:"disabled-offline",problem:et()};let s=await e.transport.execute(tt(r),a.ctx);if(s.ok)return{id:i,status:"done",data:s.data.data};return{id:i,status:"failed",problem:s.problem}}return{mutate:n,drain:async()=>{},resume:async()=>{},dispose:()=>{}}}class E extends Error{code;constructor(e,t){super(t);this.code=e;this.name="FormsPlatformError"}}import{compileFormDocument as ot,validateFormDocument as rt}from"@lssm/lib.contracts-spec/forms";function H(e,t){let o=e?.owner.product===t.product&&e.owner.tenantRef===t.tenantRef&&e.owner.workspaceRef===t.workspaceRef&&e.owner.ownerRef===t.ownerRef;if(!e||!o)throw new E("FORM_NOT_FOUND","Form definition was not found in the caller scope");return e}function X(e){let t=rt(e);if(t.length)throw new E("INVALID_FORM_DOCUMENT",t.map((o)=>o.message).join("; "));ot(e)}function pe(e,t){if(t==="none")return!0;if(t==="either")return e!=="anonymous";if(t==="both")return e==="both";return e===t||e==="both"}function Te(e,t){if(pe(t,e.verification.requirement))return"completed";return e.verification.unverifiedDisposition==="pending-hidden"?"pending-verification":"completed-unverified"}function N(e,t){let o=new Set(e.content.enabledLocales.map((n)=>n.toLowerCase()));for(let n of t){if(!n)continue;let r=n.trim().toLowerCase(),a=e.content.enabledLocales.find((u)=>u.toLowerCase()===r);if(o.has(r)&&a)return a;let i=r.split("-")[0],s=e.content.enabledLocales.find((u)=>u.toLowerCase().split("-")[0]===i);if(s)return s}return e.content.defaultLocale}function ke(e){let t=async(o,n)=>e.emit?.(o,n);return{async create(o,n){X(n);let r=e.now(),a={id:e.id("definition"),owner:o,status:"draft",draft:n,draftVersion:1,createdAt:r,updatedAt:r};return await e.store.insertDefinition(a),await t("forms.definition.created",{definitionId:a.id,occurredAt:r}),a},async saveDraft(o,n,r,a){X(a);let i=H(await e.store.getDefinition(o,n),o);if(i.status==="archived")throw new E("FORM_ARCHIVED","Archived forms cannot be edited");let s={...i,draft:a,draftVersion:r+1,updatedAt:e.now()};if(!await e.store.updateDefinition(s,r))throw new E("DRAFT_CONFLICT","The form draft changed; reload before saving");return await t("forms.draft.saved",{definitionId:n,occurredAt:s.updatedAt}),s},async publish(o,n,r){return e.store.transaction(async(a)=>{let i=H(await a.getDefinition(o,n),o);if(i.draftVersion!==r)throw new E("DRAFT_CONFLICT","The form draft changed before publish");X(i.draft);let s=i.publishedRevisionId?await a.getRevision(i.publishedRevisionId):void 0,u=e.now(),p={id:e.id("revision"),definitionId:n,revision:(s?.revision??0)+1,document:structuredClone(i.draft),digest:await e.digest(JSON.stringify(i.draft)),publishedAt:u},d={...i,status:"published",publishedRevisionId:p.id,publicToken:i.publicToken??e.publicToken(),updatedAt:u};if(await a.insertRevision(p),!await a.updateDefinition(d,r))throw new E("DRAFT_CONFLICT","The form changed during publish");return await t("forms.definition.published",{definitionId:n,revisionId:p.id,occurredAt:u}),{definition:d,revision:p}})},async close(o,n){let r=H(await e.store.getDefinition(o,n),o);if(r.status!=="published")throw new E("FORM_NOT_PUBLISHED","Only a published form can be closed");let a={...r,status:"closed",updatedAt:e.now()};return await e.store.updateDefinition(a),await t("forms.definition.closed",{definitionId:n,occurredAt:a.updatedAt}),a},async archive(o,n){let r=H(await e.store.getDefinition(o,n),o);if(r.status==="archived")return r;let a={...r,status:"archived",updatedAt:e.now()};return await e.store.updateDefinition(a),await t("forms.definition.archived",{definitionId:n,occurredAt:a.updatedAt}),a}}}import{buildZodWithRelations as it,compileFormDocument as at,FORM_AI_FULL_DATA_CONSENT_NOTICE_VERSION as st}from"@lssm/lib.contracts-spec/forms";function ct(e,t){let o=new Map(e.questions.map((s)=>[s.id,s])),n=new Set(Object.keys(e.formSpec?.modelFields??{})),r=Object.keys(t).filter((s)=>!o.has(s)&&!n.has(s));if(r.length)throw new E("INVALID_RESPONSE",`Unknown question IDs: ${r.join(", ")}`);for(let s of e.questions){if(!["single-choice","multi-select","dropdown"].includes(s.kind))continue;let u=t[s.id];if(u==null)continue;let p=new Set(s.options?.map((g)=>g.value));if(!(s.kind==="multi-select"?Array.isArray(u)&&u.every((g)=>typeof g==="string"&&p.has(g)):typeof u==="string"&&p.has(u)))throw new E("INVALID_RESPONSE",`Answer for ${s.id} is not a declared option`)}let a=Object.fromEntries(Object.entries(t).map(([s,u])=>[o.get(s)?.name??s,u])),i=it(at(e)).safeParse(a);if(!i.success)throw new E("INVALID_RESPONSE",`Response does not match the published form: ${String(i.error)}`)}function ut(e){let{kind:t,version:o,id:n,titleKey:r,descriptionKey:a,content:i,questions:s,pages:u,verification:p,formSpec:d,ai:g,beforeDestination:D,afterDestination:k}=e;return structuredClone({kind:t,version:o,id:n,titleKey:r,descriptionKey:a,content:i,questions:s,pages:u,verification:p,formSpec:d,ai:{mode:g.mode,dataMode:g.dataMode,...g.dataMode==="full-with-consent"?{consentNoticeVersion:st}:{}},beforeDestination:D,afterDestination:k})}async function lt(e,t,o,n){if(!t)return"anonymous";if(!e.resolveAssurance)throw new E("VERIFICATION_UNAVAILABLE","Verification evidence cannot be resolved");return e.resolveAssurance(t,o,n)}function Oe(e){return{async getPublic(t,o){let n=await e.store.getDefinitionByPublicToken(t);if(!n||n.status!=="published"||!n.publishedRevisionId)throw new E("FORM_NOT_FOUND","Published form was not found");let r=await e.store.getRevision(n.publishedRevisionId,{publicToken:t});if(!r)throw new E("REVISION_NOT_FOUND","Published form revision was not found");return{definitionId:n.id,revisionId:r.id,revision:r.revision,document:ut(r.document),locale:N(r.document,o)}},async submit(t){let o=await e.store.getDefinitionByPublicToken(t.publicToken);if(!o||o.status!=="published")throw new E("FORM_NOT_FOUND","Form is not accepting responses");let n=await e.store.getRevision(t.revisionId,{publicToken:t.publicToken});if(!n||n.definitionId!==o.id)throw new E("REVISION_MISMATCH","Response revision does not match the public session");ct(n.document,t.answers);let r=await lt(e,t.verificationEvidenceRef,o.id,t.publicToken);if(n.document.verification.timing==="before"&&!pe(r,n.document.verification.requirement))throw new E("VERIFICATION_REQUIRED","Verification is required before submission");let a=e.now(),i={id:e.id("response"),definitionId:o.id,revisionId:n.id,locale:N(n.document,[t.locale]),status:Te(n.document,r),authorAssurance:r,answers:structuredClone(t.answers),createdAt:a,updatedAt:a,submittedAt:a},s=await e.store.insertResponseIdempotently(i,t.publicToken,t.idempotencyKey);if(!s.inserted)return s.response;return await e.emit?.("forms.response.submitted",{definitionId:i.definitionId,revisionId:i.revisionId,responseId:i.id,occurredAt:a}),s.response}}}import{flattenPortableFormFields as ft}from"@lssm/lib.contracts-spec/forms";function de(e){let t=new Set(e.questions.map(({name:i})=>i)),o=e.questions.map(pt),n=new Set(e.formSpec?.policy?.pii??[]),r=ft(e.formSpec?.fields??[]),a=Object.entries(e.formSpec?.modelFields??{}).flatMap(([i,s])=>{if(t.has(i))return[];let u=r.find((p)=>p.name===i);return[{id:i,labelKey:u?.labelI18n??i,sensitive:n.has(i)||[...n].some((p)=>p.startsWith(`${i}.`))||u?.kind==="email"||u?.kind==="phone"||s.type==="email"||s.type==="phone"}]});return[...o,...a]}function pt(e){return{id:e.id,labelKey:e.labelKey,sensitive:e.sensitive===!0||e.kind==="email"||e.kind==="phone"}}var dt="response_extensions:AI follow-ups";function mt(e){let t=Array.isArray(e)?e.every((n)=>typeof n!=="object")?e.join(";"):JSON.stringify(e):e==null?"":typeof e==="object"?JSON.stringify(e):String(e);return`"${(/^[\s]*[=+\-@]/.test(t)?`'${t}`:t).replaceAll('"','""')}"`}function Y(e){return`${e.map(mt).join(",")}\r
|
|
2
|
+
`}function Re(e,t,o){if(t.revisionId!==e.id)throw Error("CSV responses must belong to the exported revision");let n=de(e.document),r=new Set(n.map(({id:i})=>i)),a=Object.fromEntries(Object.entries(t.answers).filter(([i])=>!r.has(i)));return[t.id,t.status,t.locale,t.submittedAt??"",...n.map((i)=>{if(!(o.includeSensitive&&o.sensitiveGrant?.authorized)&&i.sensitive)return"[REDACTED]";return t.answers[i.id]}),o.includeSensitive&&o.sensitiveGrant?.authorized?a:"[REDACTED]"]}function Ae(e,t){let o=N(e.document,[t]),n=e.document.content.catalogs[o]??{};return["response_id","status","locale","submitted_at",...de(e.document).map((r)=>`${r.id}:${n[r.labelKey]??r.labelKey}`),dt]}function yt(e,t,o={}){return`\uFEFF${Y(Ae(e,o.locale))}${t.map((n)=>Y(Re(e,n,o))).join("")}`}async function*gt(e,t,o={}){yield`\uFEFF${Y(Ae(e,o.locale))}`;for await(let n of t)yield Y(Re(e,n,o))}import{evalPredicate as vt}from"@lssm/lib.contracts-spec/forms";function ht(e,t,o){let n=e.pages.findIndex((a)=>a.id===t);if(n<0)throw new E("PAGE_NOT_FOUND","Current form page does not exist");return e.pages[n]?.nextPageRules?.find((a)=>vt(o,a.when))?.nextPageId??e.pages[n]?.defaultNextPageId??e.pages[n+1]?.id}function fo(e){return{...ke(e),...Oe(e)}}var wt=/(?:token|secret|password|passwd|authorization|api[_-]?key|credential|cookie|session)/i;function me(e){if(Array.isArray(e))return e.map(me);if(e===null||typeof e!=="object")return e;let t={};for(let[o,n]of Object.entries(e))t[o]=wt.test(o)?"[REDACTED]":me(n);return t}var ye={encrypt:(e)=>e,decrypt:(e)=>e};function bt(e){let t=2166136261;for(let o=0;o<e.length;o+=1)t^=e.charCodeAt(o),t=Math.imul(t,16777619);return(t>>>0).toString(16).padStart(8,"0")}var Et={id:"docs.contracts-runtime-core.redaction",title:"Persisted-state redaction + encryption-at-rest seam",summary:"Secret redaction for durable state plus an encryption-at-rest seam and a PII-safe key hash for logs.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/redaction",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","security","redaction","encryption"],body:"redactSecrets recursively strips secret-ish keys (token/secret/password/authorization/api-key/credential/cookie/session) so durable cache+queue state never persists secrets (AC12). EncryptionSeam is the encryption-at-rest seam — default identityEncryption (no-op); sensitive entities plug a real cipher transparently to the storage core. hashKey (FNV-1a) lets observability correlate on a stable key id without logging the raw query key (which embeds input/PII)."};function St(e,t={}){let o=t.encryption??ye,n=t.redact,r=t.namespace??"",a=(s)=>`${r}${s}`,i=(s)=>r!==""&&s.startsWith(r)?s.slice(r.length):s;return{async get(s){let u=await e.getItem(a(s));if(u===null)return;let p=await o.decrypt(u);return JSON.parse(p)},async set(s,u){let p=JSON.stringify(n===void 0?u:n(u));await e.setItem(a(s),await o.encrypt(p))},async remove(s){await e.removeItem(a(s))},async keys(s){let u=await e.keys(),p=r===""?u:u.filter((d)=>d.startsWith(r)).map(i);return s===void 0?p:p.filter((d)=>d.startsWith(s))},async clear(){await e.clear()}}}var Dt={id:"docs.contracts-runtime-core.storage-port",title:"Durable storage port (I/O-free contract)",summary:"Value-agnostic durable key/value port; platform impls own serialization, redaction, and encryption-at-rest.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/storage-port",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","storage","persistence","port"],body:"Durable key/value StoragePort: get/set/remove/keys/clear. Intentionally value-agnostic — the engine layers CacheEntry and MutationQueueEntry shapes on top and namespaces by key prefix. keys(prefix?) supports GC and offline-queue scans. Implementations persist JSON-serializable values and own serialization, per-contract secret redaction, and the encryption-at-rest seam (AC12). B1a ships an in-memory default (createMemoryStorage); IndexedDB (web) and SQLite/AsyncStorage (native) platform variants land in B1b/#7 behind this same contract."};var Tt={id:"docs.contracts-runtime-core.transport",title:"Transport port + absorbed data-transmission concept map",summary:"One Transport port carries the canonical protocol; each former data-transmission concept has an explicit home.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/transport",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","transport","data-transmission","port"],body:["The single Transport port: execute(envelope, ctx) → Promise<ContractResult<QueryResultEnvelope>>, plus an optional subscribe(tags, handler) realtime seam. REST/MCP/WebSocket/in-memory are interchangeable adapters behind it.","","Absorbed data-transmission concept map (each former concept has exactly one explicit home — no parallel transport types):","- idempotency → ContractResult success field (ContractResultIdempotency{key,replayed?,firstSeenAt?} on ContractSuccess.idempotency). NOT a transport argument.","- subscriptions → Transport.subscribe(tags, handler) seam (no spec type, by design); the version token doubles as the realtime invalidation token. Full delivery is a follow-on; the realtime worker migrates onto this seam in Phase 7.","- invalidation → the InvalidationTag{entity,id?} vocabulary (contracts-spec/query/invalidation) shared by optimistic updates, offline-queue replay, and realtime broadcast; mutations declare the tags they invalidate.","- consistency levels → the typed QueryEnvelope.consistency request field and ContractSuccess.consistency applied field (QueryConsistency = eventual | read-your-writes | strong, default read-your-writes). Typed, never prose. Subsumes the former DataFetchConsistency incl. read-your-writes.","","TransportContext.principal is opaque and carried end-to-end unchanged (AC14, no-strip); policy.auth enforcement stays in authos/identity-rbac."].join(`
|
|
3
|
+
`)};export{Qe as ContractsRuntimeCoreFeature,E as FormsPlatformError,z as authExpiredState,ee as backoffDelayMs,P as computeIsStale,J as conflictState,_ as createCacheStore,q as createConflictResolverRegistry,fe as createDataEngine,Ye as createDataEngineFromEnv,Q as createEngineEvents,fo as createFormsPlatformService,ce as createInflightRegistry,St as createKeyedStorage,oe as createMaintenance,he as createMemoryApprovalNonceStore,j as createMemoryStorage,ie as createMutationEngine,ae as createMutationQueue,Ze as createNoCacheStore,nt as createOnlineOnlyMutationEngine,Fe as createOperationApprovalPort,le as createQueryEngine,I as createStaticReachabilitySource,B as errorState,bt as hashKey,ye as identityEncryption,L as idleState,Ve as isLocalOfflineMode,A as isNetworkAllowed,K as loadingState,G as offlineState,we as operationApprovalInputDigest,je as reachabilityPortDoc,De as readHostEnv,me as redactSecrets,Et as redactionDoc,N as resolveFormLocale,U as resolveLocalOfflinePolicy,ht as resolveNextFormPage,te as resolveQueuedConflict,V as restoreSnapshots,yt as serializeFormResponsesCsv,Dt as storagePortDoc,gt as streamFormResponsesCsv,M as successState,Tt as transportPortDoc};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var c=new Set(["online-only","memory-cache","offline-capable"]),i={"online-only":"online-only",online:"online-only",disabled:"online-only",false:"online-only",off:"online-only","memory-cache":"memory-cache",memory:"memory-cache",cache:"memory-cache","offline-capable":"offline-capable",offline:"offline-capable",enabled:"offline-capable",true:"offline-capable",on:"offline-capable"};function d(e){return typeof e==="string"&&c.has(e)}function t(e={}){let o=e.defaultMode===void 0?{mode:"online-only",source:"default",reason:"No local/offline policy input was supplied."}:f(e.defaultMode,"explicit")??{mode:"online-only",source:"explicit"},l=f(r(e.env),"env",{allowUnknown:!0});return f(e.flag,"feature-flag")??l??o}function r(e){if(typeof e!=="object"||e===null)return e;if("CONTRACTSPEC_LOCAL_OFFLINE"in e)return e.CONTRACTSPEC_LOCAL_OFFLINE;if("mode"in e)return e;return}function f(e,o,l={}){if(e===void 0||e===null||e==="")return;if(typeof e==="boolean")return{mode:e?"offline-capable":"online-only",source:o};if(typeof e==="string"){let n=i[e.trim().toLowerCase()];if(n!==void 0)return{mode:n,source:o};if(l.allowUnknown)return}if(typeof e==="object"&&e!==null&&"mode"in e){let n=e,a=typeof n.mode==="string"?i[n.mode.trim().toLowerCase()]:void 0;if(a!==void 0)return{mode:a,source:o,...typeof n.reason==="string"?{reason:n.reason}:{}};if(l.allowUnknown)return}throw Error(`Unsupported localOffline policy value: ${String(e)}`)}export{d as isLocalOfflineMode,t as resolveLocalOfflinePolicy};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{isSafeEconomicEvidenceRefId as p}from"@lssm/lib.contracts-spec/operations";import{createCounter as c,createHistogram as f,logger as b}from"@lssm/lib.observability";var g=/(?:token|secret|password|passwd|authorization|api[_-]?key|credential|cookie|session)/i;function d(r){if(Array.isArray(r))return r.map(d);if(r===null||typeof r!=="object")return r;let i={};for(let[t,n]of Object.entries(r))i[t]=g.test(t)?"[REDACTED]":d(n);return i}var R={encrypt:(r)=>r,decrypt:(r)=>r};function u(r){let i=2166136261;for(let t=0;t<r.length;t+=1)i^=r.charCodeAt(t),i=Math.imul(i,16777619);return(i>>>0).toString(16).padStart(8,"0")}var w={id:"docs.contracts-runtime-core.redaction",title:"Persisted-state redaction + encryption-at-rest seam",summary:"Secret redaction for durable state plus an encryption-at-rest seam and a PII-safe key hash for logs.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/redaction",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","security","redaction","encryption"],body:"redactSecrets recursively strips secret-ish keys (token/secret/password/authorization/api-key/credential/cookie/session) so durable cache+queue state never persists secrets (AC12). EncryptionSeam is the encryption-at-rest seam — default identityEncryption (no-op); sensitive entities plug a real cipher transparently to the storage core. hashKey (FNV-1a) lets observability correlate on a stable key id without logging the raw query key (which embeds input/PII)."};function x(r,i={}){let t=i.logger??b,n=i.hashKeys===!1?(e)=>e:u,l=c("contracts_runtime_cache_hits_total","Cache hits"),m=c("contracts_runtime_cache_misses_total","Cache misses"),a=f("contracts_runtime_fetch_latency_ms","Fetch latency (ms)"),y=c("contracts_runtime_conflicts_total","Mutation conflicts"),h=c("contracts_runtime_economic_evidence_refs_total","Safe economic evidence refs attached to runtime operations"),o=i.correlationId===void 0?{}:{correlationId:i.correlationId};return r.subscribe((e)=>{switch(e.type){case"cache-hit":l.add(1),t.debug("cache.hit",{...o,key:n(e.key)});break;case"cache-miss":m.add(1),t.debug("cache.miss",{...o,key:n(e.key)});break;case"fetch-success":a.record(e.latencyMs),t.info("fetch.success",{...o,key:n(e.key),latencyMs:e.latencyMs});break;case"fetch-error":a.record(e.latencyMs),t.warn("fetch.error",{...o,key:n(e.key),code:e.code,latencyMs:e.latencyMs});break;case"mutation-enqueued":t.info("mutation.enqueued",{...o,id:e.id,queueDepth:e.queueDepth});break;case"mutation-replayed":t.info("mutation.replayed",{...o,id:e.id,queueDepth:e.queueDepth});break;case"mutation-conflict":y.add(1,{strategy:e.strategy,outcome:e.outcome}),t.warn("mutation.conflict",{...o,id:e.id,strategy:e.strategy,outcome:e.outcome});break;case"mutation-retry":t.warn("mutation.retry",{...o,id:e.id,attempt:e.attempt,delayMs:e.delayMs});break;case"mutation-failed":t.error("mutation.failed",{...o,id:e.id,code:e.code});break;case"economic-evidence-attached":{let s=E(e.evidenceRefs);h.add(s.length,{operationKey:e.operationKey}),t.info("economic.evidence.attached",{...o,operationKey:e.operationKey,evidenceRefCount:s.length,evidenceRefs:s,latencyMs:e.latencyMs});break}case"auth-expired":t.warn("auth.expired",{...o,id:e.id});break;case"queue-drained":t.info("queue.drained",{...o,remaining:e.remaining});break;case"gc-collected":t.info("cache.gc",{...o,evicted:e.evicted});break;case"revalidated":t.debug("cache.revalidated",{...o,key:n(e.key)});break}})}var k=new Set(["usage","cost","budget","replay","projection"]);function E(r){return r.flatMap((i)=>{if(typeof i.id!=="string"||!p(i.id)||!k.has(i.kind))return[];return[{id:i.id,kind:i.kind,...typeof i.source==="string"?{source:i.source}:{}}]})}var M={id:"docs.contracts-runtime-core.observability",title:"Engine observability binding (no PII)",summary:"Binds the engine EngineEvents stream to the observability lib with hashed keys, safe economic evidence refs, and structured metrics.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/observability",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","observability","metrics","logging"],body:"attachObservability subscribes to the framework-neutral EngineEvents stream (emitted by the engine, zero deps) and forwards it to @lssm/lib.observability: structured logs via Logger (debug/info/warn/error) and metrics (cache hit/miss counters, fetch-latency histogram, conflict counter tagged by strategy+outcome, economic evidence ref counter). Query keys embed input, so they are hashed (FNV-1a via hashKey) before logging — never raw (AC12, no PII). economic-evidence-attached events are serialized through a deny-by-default allowlist: ref id, kind, and optional source only; raw provider receipts, prompts, payloads, and unrecognized fields are dropped before log/replay sinks consume them. Optional correlationId is attached to every entry. Lives behind the ./observability subpath so the OTel-backed lib stays out of the main entry bundle; no console.* — logging is the lib's concern."};export{x as attachObservability,M as observabilityDoc};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function i(e){return e!=="offline"}function n(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var a={id:"docs.contracts-runtime-core.reachability",title:"Reachability input port (tri-state)",summary:"Engine-owned reachability input port; integrations feed it, the engine never imports them.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/reachability",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","reachability","offline","port"],body:"Tri-state OnlineSignal ('online' | 'restricted' | 'offline'). 'online' allows normal fetch + queue drain. 'restricted' is online-degraded: the engine fetches and drains the queue as if online but may mark results stale-eligible; it never suspends network nor activates the offline queue. Only 'offline' suspends outbound network and activates the durable offline mutation queue. The engine reads reachability solely through ReachabilitySource and MUST NOT import integrations/* — concrete sources (runtime-local networkReachability) are wired via an integration→port adapter (plan §2.3/§4.2, Architect finding 1). isNetworkAllowed mirrors runtime-local's outboundNetworkAllowed = reachability !== 'offline'. createStaticReachabilitySource('online') is the B1a default and online test fixture."};export{n as createStaticReachabilitySource,i as isNetworkAllowed,a as reachabilityPortDoc};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var s=/(?:token|secret|password|passwd|authorization|api[_-]?key|credential|cookie|session)/i;function n(t){if(Array.isArray(t))return t.map(n);if(t===null||typeof t!=="object")return t;let e={};for(let[r,o]of Object.entries(t))e[r]=s.test(r)?"[REDACTED]":n(o);return e}var i={encrypt:(t)=>t,decrypt:(t)=>t};function c(t){let e=2166136261;for(let r=0;r<t.length;r+=1)e^=t.charCodeAt(r),e=Math.imul(e,16777619);return(e>>>0).toString(16).padStart(8,"0")}var a={id:"docs.contracts-runtime-core.redaction",title:"Persisted-state redaction + encryption-at-rest seam",summary:"Secret redaction for durable state plus an encryption-at-rest seam and a PII-safe key hash for logs.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/redaction",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","security","redaction","encryption"],body:"redactSecrets recursively strips secret-ish keys (token/secret/password/authorization/api-key/credential/cookie/session) so durable cache+queue state never persists secrets (AC12). EncryptionSeam is the encryption-at-rest seam — default identityEncryption (no-op); sensitive entities plug a real cipher transparently to the storage core. hashKey (FNV-1a) lets observability correlate on a stable key id without logging the raw query key (which embeds input/PII)."};export{c as hashKey,i as identityEncryption,n as redactSecrets,a as redactionDoc};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var f=/(?:token|secret|password|passwd|authorization|api[_-]?key|credential|cookie|session)/i;function d(e){if(Array.isArray(e))return e.map(d);if(e===null||typeof e!=="object")return e;let n={};for(let[t,r]of Object.entries(e))n[t]=f.test(t)?"[REDACTED]":d(r);return n}var u={encrypt:(e)=>e,decrypt:(e)=>e};function h(e){let n=2166136261;for(let t=0;t<e.length;t+=1)n^=e.charCodeAt(t),n=Math.imul(n,16777619);return(n>>>0).toString(16).padStart(8,"0")}var D={id:"docs.contracts-runtime-core.redaction",title:"Persisted-state redaction + encryption-at-rest seam",summary:"Secret redaction for durable state plus an encryption-at-rest seam and a PII-safe key hash for logs.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/redaction",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","security","redaction","encryption"],body:"redactSecrets recursively strips secret-ish keys (token/secret/password/authorization/api-key/credential/cookie/session) so durable cache+queue state never persists secrets (AC12). EncryptionSeam is the encryption-at-rest seam — default identityEncryption (no-op); sensitive entities plug a real cipher transparently to the storage core. hashKey (FNV-1a) lets observability correlate on a stable key id without logging the raw query key (which embeds input/PII)."};function g(e,n={}){let t=n.encryption??u,r=n.redact,i=n.namespace??"",y=(o)=>`${i}${o}`,l=(o)=>i!==""&&o.startsWith(i)?o.slice(i.length):o;return{async get(o){let s=await e.getItem(y(o));if(s===null)return;let a=await t.decrypt(s);return JSON.parse(a)},async set(o,s){let a=JSON.stringify(r===void 0?s:r(s));await e.setItem(y(o),await t.encrypt(a))},async remove(o){await e.removeItem(y(o))},async keys(o){let s=await e.keys(),a=i===""?s:s.filter((p)=>p.startsWith(i)).map(l);return o===void 0?a:a.filter((p)=>p.startsWith(o))},async clear(){await e.clear()}}}var w="contractspec-runtime",m="kv";function c(e){return new Promise((n,t)=>{e.onsuccess=()=>n(e.result),e.onerror=()=>t(e.error)})}function S(e){return new Promise((n,t)=>{let r=indexedDB.open(e,1);r.onupgradeneeded=()=>{r.result.createObjectStore(m)},r.onsuccess=()=>n(r.result),r.onerror=()=>t(r.error)})}function k(e){let n=S(e),t=async(r)=>(await n).transaction(m,r).objectStore(m);return{async getItem(r){return await c((await t("readonly")).get(r))??null},async setItem(r,i){await c((await t("readwrite")).put(i,r))},async removeItem(r){await c((await t("readwrite")).delete(r))},async keys(){return(await c((await t("readonly")).getAllKeys())).map(String)},async clear(){await c((await t("readwrite")).clear())}}}function I(e={}){let{databaseName:n,...t}=e;return g(k(n??w),{redact:d,...t})}export{I as createIndexedDbStorage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var u=/(?:token|secret|password|passwd|authorization|api[_-]?key|credential|cookie|session)/i;function p(t){if(Array.isArray(t))return t.map(p);if(t===null||typeof t!=="object")return t;let r={};for(let[n,c]of Object.entries(t))r[n]=u.test(n)?"[REDACTED]":p(c);return r}var d={encrypt:(t)=>t,decrypt:(t)=>t};function g(t){let r=2166136261;for(let n=0;n<t.length;n+=1)r^=t.charCodeAt(n),r=Math.imul(r,16777619);return(r>>>0).toString(16).padStart(8,"0")}var l={id:"docs.contracts-runtime-core.redaction",title:"Persisted-state redaction + encryption-at-rest seam",summary:"Secret redaction for durable state plus an encryption-at-rest seam and a PII-safe key hash for logs.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/redaction",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","security","redaction","encryption"],body:"redactSecrets recursively strips secret-ish keys (token/secret/password/authorization/api-key/credential/cookie/session) so durable cache+queue state never persists secrets (AC12). EncryptionSeam is the encryption-at-rest seam — default identityEncryption (no-op); sensitive entities plug a real cipher transparently to the storage core. hashKey (FNV-1a) lets observability correlate on a stable key id without logging the raw query key (which embeds input/PII)."};function k(t,r={}){let n=r.encryption??d,c=r.redact,o=r.namespace??"",a=(e)=>`${o}${e}`,m=(e)=>o!==""&&e.startsWith(o)?e.slice(o.length):e;return{async get(e){let i=await t.getItem(a(e));if(i===null)return;let s=await n.decrypt(i);return JSON.parse(s)},async set(e,i){let s=JSON.stringify(c===void 0?i:c(i));await t.setItem(a(e),await n.encrypt(s))},async remove(e){await t.removeItem(a(e))},async keys(e){let i=await t.keys(),s=o===""?i:i.filter((y)=>y.startsWith(o)).map(m);return e===void 0?s:s.filter((y)=>y.startsWith(e))},async clear(){await t.clear()}}}export{k as createKeyedStorage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(){let r=new Map;return{get:async(e)=>{let n=r.get(e);return n===void 0?void 0:structuredClone(n)},set:async(e,n)=>{r.set(e,structuredClone(n))},remove:async(e)=>{r.delete(e)},keys:async(e)=>{let n=[...r.keys()];return e===void 0?n:n.filter((t)=>t.startsWith(e))},clear:async()=>{r.clear()}}}export{o as createMemoryStorage};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e={id:"docs.contracts-runtime-core.storage-port",title:"Durable storage port (I/O-free contract)",summary:"Value-agnostic durable key/value port; platform impls own serialization, redaction, and encryption-at-rest.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/storage-port",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","storage","persistence","port"],body:"Durable key/value StoragePort: get/set/remove/keys/clear. Intentionally value-agnostic — the engine layers CacheEntry and MutationQueueEntry shapes on top and namespaces by key prefix. keys(prefix?) supports GC and offline-queue scans. Implementations persist JSON-serializable values and own serialization, per-contract secret redaction, and the encryption-at-rest seam (AC12). B1a ships an in-memory default (createMemoryStorage); IndexedDB (web) and SQLite/AsyncStorage (native) platform variants land in B1b/#7 behind this same contract."};export{e as storagePortDoc};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e={id:"docs.contracts-runtime-core.transport",title:"Transport port + absorbed data-transmission concept map",summary:"One Transport port carries the canonical protocol; each former data-transmission concept has an explicit home.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/transport",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","transport","data-transmission","port"],body:["The single Transport port: execute(envelope, ctx) → Promise<ContractResult<QueryResultEnvelope>>, plus an optional subscribe(tags, handler) realtime seam. REST/MCP/WebSocket/in-memory are interchangeable adapters behind it.","","Absorbed data-transmission concept map (each former concept has exactly one explicit home — no parallel transport types):","- idempotency → ContractResult success field (ContractResultIdempotency{key,replayed?,firstSeenAt?} on ContractSuccess.idempotency). NOT a transport argument.","- subscriptions → Transport.subscribe(tags, handler) seam (no spec type, by design); the version token doubles as the realtime invalidation token. Full delivery is a follow-on; the realtime worker migrates onto this seam in Phase 7.","- invalidation → the InvalidationTag{entity,id?} vocabulary (contracts-spec/query/invalidation) shared by optimistic updates, offline-queue replay, and realtime broadcast; mutations declare the tags they invalidate.","- consistency levels → the typed QueryEnvelope.consistency request field and ContractSuccess.consistency applied field (QueryConsistency = eventual | read-your-writes | strong, default read-your-writes). Typed, never prose. Subsumes the former DataFetchConsistency incl. read-your-writes.","","TransportContext.principal is opaque and carried end-to-end unchanged (AC14, no-strip); policy.auth enforcement stays in authos/identity-rbac."].join(`
|
|
2
|
+
`)};export{e as transportPortDoc};
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Collaboration envelope types, re-homed from the (to-be-deleted)
|
|
3
|
+
* `data-transmission-spec` into this lib so `crdt-loro` (integration) imports
|
|
4
|
+
* downward (integration → lib) instead of from a sibling spec package.
|
|
5
|
+
*
|
|
6
|
+
* Reconciled to ONE canonical `CollaborationEnvelope` (the CRDT-shaped variant
|
|
7
|
+
* from `data-transmission-spec/contracts.ts`, which matches crdt-loro's actual
|
|
8
|
+
* adapter construction). Type-only — no zod runtime; the validating schemas
|
|
9
|
+
* remain in `data-transmission-spec` until its deletion (worker-5 #16).
|
|
10
|
+
*/
|
|
11
|
+
/** Per-document CRDT version vector (Loro frontier). */
|
|
12
|
+
export interface CollaborationVersionVector {
|
|
13
|
+
documentId: string;
|
|
14
|
+
versionId: string;
|
|
15
|
+
frontier: readonly string[];
|
|
16
|
+
}
|
|
17
|
+
/** A binary CRDT update for a document, attributed to an actor/session. */
|
|
18
|
+
export interface CollaborationUpdateEnvelope {
|
|
19
|
+
documentId: string;
|
|
20
|
+
actorId: string;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
version: CollaborationVersionVector;
|
|
23
|
+
update: Uint8Array;
|
|
24
|
+
}
|
|
25
|
+
/** A binary CRDT snapshot of a document at a version. */
|
|
26
|
+
export interface CollaborationSnapshotEnvelope {
|
|
27
|
+
documentId: string;
|
|
28
|
+
version: CollaborationVersionVector;
|
|
29
|
+
snapshot: Uint8Array;
|
|
30
|
+
}
|
|
31
|
+
/** A self-describing CRDT snapshot payload (protocol-tagged). */
|
|
32
|
+
export interface CollaborationSnapshot {
|
|
33
|
+
protocolVersion: string;
|
|
34
|
+
documentId: string;
|
|
35
|
+
payload: Uint8Array<ArrayBuffer>;
|
|
36
|
+
version: string;
|
|
37
|
+
adapterMetadata?: Record<string, unknown>;
|
|
38
|
+
}
|
|
39
|
+
/** Canonical collaboration message envelope (snapshot/update/merge/version). */
|
|
40
|
+
export interface CollaborationEnvelope<TPayload = unknown> {
|
|
41
|
+
id: string;
|
|
42
|
+
kind: 'snapshot' | 'update' | 'merge' | 'version';
|
|
43
|
+
document: {
|
|
44
|
+
documentId: string;
|
|
45
|
+
collection: string;
|
|
46
|
+
version: string;
|
|
47
|
+
};
|
|
48
|
+
payload: TPayload;
|
|
49
|
+
actorId: string;
|
|
50
|
+
adapter?: {
|
|
51
|
+
adapter: string;
|
|
52
|
+
protocol: 'crdt';
|
|
53
|
+
version: string;
|
|
54
|
+
capabilities?: readonly string[];
|
|
55
|
+
};
|
|
56
|
+
createdAt: string;
|
|
57
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
// @bun
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const ContractsRuntimeCoreFeature: import("@lssm/lib.contracts-spec").FeatureModuleSpec;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{defineFeature as e}from"@lssm/lib.contracts-spec/features";var r=e({meta:{key:"libs.contracts-runtime-core",version:"1.0.0",title:"Contracts Runtime Core",description:"Framework-neutral in-house query/cache/offline engine and Transport port for ContractSpec data fetching.",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["package","libs","contracts-runtime-core"],stability:"experimental"}});export{r as ContractsRuntimeCoreFeature};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exponential backoff with optional jitter for offline-queue replay retries.
|
|
3
|
+
*/
|
|
4
|
+
export interface BackoffOptions {
|
|
5
|
+
baseMs?: number;
|
|
6
|
+
factor?: number;
|
|
7
|
+
maxMs?: number;
|
|
8
|
+
/** Deterministic jitter source in [0, 1); defaults to `Math.random`. */
|
|
9
|
+
jitter?: () => number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Delay before the given 1-based attempt. Attempt 1 uses the base delay;
|
|
13
|
+
* each subsequent attempt multiplies by `factor`, capped at `maxMs`, then a
|
|
14
|
+
* full-jitter factor in [0.5, 1) is applied to spread reconnect storms.
|
|
15
|
+
*/
|
|
16
|
+
export declare function backoffDelayMs(attempt: number, options?: BackoffOptions): number;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { InvalidationTag } from '@lssm/lib.contracts-spec/query/invalidation';
|
|
2
|
+
import type { StoragePort } from '../storage/port.js';
|
|
3
|
+
import type { CacheEntry } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Cache store layered on the {@link StoragePort}. Maintains an in-memory
|
|
6
|
+
* tag→key index (populated on write) so tag invalidation can mark dependent
|
|
7
|
+
* entries stale. The index is session-scoped in B1a (in-memory storage);
|
|
8
|
+
* durable-store index rehydration is a B1b concern.
|
|
9
|
+
*/
|
|
10
|
+
export interface CacheStore {
|
|
11
|
+
read<TData = unknown>(key: string): Promise<CacheEntry<TData> | undefined>;
|
|
12
|
+
write<TData = unknown>(entry: CacheEntry<TData>): Promise<void>;
|
|
13
|
+
remove(key: string): Promise<void>;
|
|
14
|
+
/** Every cached entry (used by GC + background revalidation). */
|
|
15
|
+
entries(): Promise<readonly CacheEntry[]>;
|
|
16
|
+
/** Mark every entry matching any pattern stale; returns affected keys. */
|
|
17
|
+
invalidate(tags: readonly InvalidationTag[], now: number): Promise<readonly string[]>;
|
|
18
|
+
}
|
|
19
|
+
export declare function createCacheStore(storage: StoragePort): CacheStore;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{invalidationTagMatches as h}from"@lssm/lib.contracts-spec/query/invalidation";var c="cache:";function l(e){let i=new Map,a=(t)=>`${c}${t}`;return{read:(t)=>e.get(a(t)),write:async(t)=>{i.set(t.key,t.tags),await e.set(a(t.key),t)},remove:async(t)=>{i.delete(t),await e.remove(a(t))},entries:async()=>{let t=await e.keys(c),r=[];for(let o of t){let n=await e.get(o);if(n!==void 0)r.push(n)}return r},invalidate:async(t,r)=>{let o=[];for(let[n,y]of i.entries()){if(!y.some((d)=>t.some((m)=>h(d,m))))continue;let s=await e.get(a(n));if(s===void 0)continue;await e.set(a(n),{...s,status:"stale",staleAt:r}),o.push(n)}return o}}}export{l as createCacheStore};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ConflictResolverRegistry } from './mutation-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Conflict-resolver registry. Built-in strategies resolve inline; `crdt` and
|
|
4
|
+
* `custom` delegate to a registered resolver keyed by `policy.resolver`. The
|
|
5
|
+
* CRDT slot is filled later by crdt-loro injection.
|
|
6
|
+
*
|
|
7
|
+
* Fail-closed: a `crdt`/`custom` strategy with a missing resolver never drops
|
|
8
|
+
* the write silently — it surfaces the conflict for resolution.
|
|
9
|
+
*/
|
|
10
|
+
export declare function createConflictResolverRegistry(): ConflictResolverRegistry;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
function i(o){let{serverData:e,localData:r}=o,t=o.mergeFields??[];if(typeof e!=="object"||e===null||typeof r!=="object"||r===null)return{outcome:"surface"};let s={...e},c=r;for(let n of t)if(n in c)s[n]=c[n];return{outcome:"merged",data:s}}function l(){let o=new Map;return{register(e,r){o.set(e,r)},has(e){return o.has(e)},async resolve(e){switch(e.strategy){case"server-wins":return{outcome:"use-server",data:e.serverData};case"client-wins":return{outcome:"use-client",data:e.localData};case"field-merge":return i(e);case"surface":return{outcome:"surface"};default:{let r=e.policy.resolver,t=r===void 0?void 0:o.get(r);if(t===void 0)return{outcome:"surface"};return t(e)}}}}}export{l as createConflictResolverRegistry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ContractProblem } from '@lssm/lib.contracts-spec/results';
|
|
2
|
+
import type { CacheStore } from './cache-store.js';
|
|
3
|
+
import type { EngineEvents } from './events.js';
|
|
4
|
+
import type { MutationQueue } from './mutation-queue.js';
|
|
5
|
+
import type { ConflictResolverRegistry, MutationQueueEntry, OptimisticSnapshot } from './mutation-types.js';
|
|
6
|
+
import type { QueryEngine } from './types.js';
|
|
7
|
+
export interface ConflictDeps {
|
|
8
|
+
cache: CacheStore;
|
|
9
|
+
queue: MutationQueue;
|
|
10
|
+
resolvers: ConflictResolverRegistry;
|
|
11
|
+
events: EngineEvents;
|
|
12
|
+
now: () => number;
|
|
13
|
+
queryEngine?: Pick<QueryEngine, 'refresh' | 'emitConflict'>;
|
|
14
|
+
}
|
|
15
|
+
/** Restore cached entries to their pre-optimistic snapshot. */
|
|
16
|
+
export declare function restoreSnapshots(snapshots: readonly OptimisticSnapshot[], cache: CacheStore): Promise<void>;
|
|
17
|
+
/** Resolution disposition: terminal (`resolved`) or retried (`re-enqueued`). */
|
|
18
|
+
export type ConflictDisposition = 'resolved' | 're-enqueued';
|
|
19
|
+
/**
|
|
20
|
+
* Resolve a replay-time version-token conflict via the per-mutation policy.
|
|
21
|
+
* Never drops the write silently; an unresolved `crdt`/`custom` resolver
|
|
22
|
+
* surfaces the conflict for manual resolution.
|
|
23
|
+
*/
|
|
24
|
+
export declare function resolveQueuedConflict(entry: MutationQueueEntry, problem: ContractProblem, deps: ConflictDeps): Promise<ConflictDisposition>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{defaultConflictPolicy as u}from"@lssm/lib.contracts-spec/query/conflict-policy";async function l(t,o){for(let e of t)if(e.previous===void 0)await o.remove(e.key);else await o.write(e.previous)}async function m(t,o,e){let i=t.conflictPolicy??u(),n=t.rollback[0]?.key,r=n===void 0?void 0:await e.cache.read(n),s=o.metadata?.serverData,a=await e.resolvers.resolve({strategy:i.strategy,policy:i,localData:r?.data,serverData:s,localVersion:t.versionToken,serverVersion:o.versionToken,mergeFields:i.mergeFields});switch(e.events.emit({type:"mutation-conflict",id:t.id,strategy:i.strategy,outcome:a.outcome}),a.outcome){case"use-server":{await l(t.rollback,e.cache),await e.cache.invalidate(t.tags,e.now());for(let c of t.rollback)await e.queryEngine?.refresh(c.key);return"resolved"}case"use-client":return await e.queue.update({...t,versionToken:o.versionToken,status:"pending"}),"re-enqueued";case"merged":{if(n!==void 0&&r!==void 0)await e.cache.write({...r,data:a.data,versionToken:o.versionToken,status:"warm",storedAt:e.now()}),await e.queryEngine?.refresh(n);return await e.queue.update({...t,versionToken:o.versionToken,status:"pending"}),"re-enqueued"}default:{if(n!==void 0)await e.queryEngine?.emitConflict(n,{versionToken:o.versionToken,serverData:s,clientData:r?.data,strategy:i.strategy});return"resolved"}}}export{m as resolveQueuedConflict,l as restoreSnapshots};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type DataEngine, type DataEngineOptions } from './data-engine.js';
|
|
2
|
+
/**
|
|
3
|
+
* Reads the host process environment defensively. Returns `undefined` in
|
|
4
|
+
* runtimes without a `process` global (browsers, edge isolates) instead of
|
|
5
|
+
* throwing, so this is safe to call from isomorphic host code.
|
|
6
|
+
*
|
|
7
|
+
* This is the single seam in the runtime that touches the global environment;
|
|
8
|
+
* the pure `resolveLocalOfflinePolicy` deliberately never reads it.
|
|
9
|
+
*/
|
|
10
|
+
export declare function readHostEnv(): unknown;
|
|
11
|
+
/**
|
|
12
|
+
* Host-facing convenience over {@link createDataEngine} that wires the
|
|
13
|
+
* local/offline on/off trigger from the host environment. When `options.env`
|
|
14
|
+
* is omitted it defaults to the host `process.env` (via {@link readHostEnv}),
|
|
15
|
+
* so a host can flip the runtime with `CONTRACTSPEC_LOCAL_OFFLINE` in a single
|
|
16
|
+
* call:
|
|
17
|
+
*
|
|
18
|
+
* ```ts
|
|
19
|
+
* // CONTRACTSPEC_LOCAL_OFFLINE=offline-capable flips this engine on.
|
|
20
|
+
* const engine = createDataEngineFromEnv({
|
|
21
|
+
* transport,
|
|
22
|
+
* advanced: { storage: createIndexedDbStorage() },
|
|
23
|
+
* });
|
|
24
|
+
* ```
|
|
25
|
+
*
|
|
26
|
+
* Precedence is unchanged from the resolver: `flag` > `env` (here, the host
|
|
27
|
+
* env) > `localOffline` default. An explicit `options.env` still wins over the
|
|
28
|
+
* host env, keeping the function testable without mutating globals.
|
|
29
|
+
*/
|
|
30
|
+
export declare function createDataEngineFromEnv(options: DataEngineOptions): DataEngine;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var ue=new Set(["online-only","memory-cache","offline-capable"]),Y={"online-only":"online-only",online:"online-only",disabled:"online-only",false:"online-only",off:"online-only","memory-cache":"memory-cache",memory:"memory-cache",cache:"memory-cache","offline-capable":"offline-capable",offline:"offline-capable",enabled:"offline-capable",true:"offline-capable",on:"offline-capable"};function ke(e){return typeof e==="string"&&ue.has(e)}function I(e={}){let t=e.defaultMode===void 0?{mode:"online-only",source:"default",reason:"No local/offline policy input was supplied."}:z(e.defaultMode,"explicit")??{mode:"online-only",source:"explicit"},n=z(le(e.env),"env",{allowUnknown:!0});return z(e.flag,"feature-flag")??n??t}function le(e){if(typeof e!=="object"||e===null)return e;if("CONTRACTSPEC_LOCAL_OFFLINE"in e)return e.CONTRACTSPEC_LOCAL_OFFLINE;if("mode"in e)return e;return}function z(e,t,n={}){if(e===void 0||e===null||e==="")return;if(typeof e==="boolean")return{mode:e?"offline-capable":"online-only",source:t};if(typeof e==="string"){let a=Y[e.trim().toLowerCase()];if(a!==void 0)return{mode:a,source:t};if(n.allowUnknown)return}if(typeof e==="object"&&e!==null&&"mode"in e){let a=e,i=typeof a.mode==="string"?Y[a.mode.trim().toLowerCase()]:void 0;if(i!==void 0)return{mode:i,source:t,...typeof a.reason==="string"?{reason:a.reason}:{}};if(n.allowUnknown)return}throw Error(`Unsupported localOffline policy value: ${String(e)}`)}function C(e){return e!=="offline"}function R(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var Me={id:"docs.contracts-runtime-core.reachability",title:"Reachability input port (tri-state)",summary:"Engine-owned reachability input port; integrations feed it, the engine never imports them.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/reachability",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","reachability","offline","port"],body:"Tri-state OnlineSignal ('online' | 'restricted' | 'offline'). 'online' allows normal fetch + queue drain. 'restricted' is online-degraded: the engine fetches and drains the queue as if online but may mark results stale-eligible; it never suspends network nor activates the offline queue. Only 'offline' suspends outbound network and activates the durable offline mutation queue. The engine reads reachability solely through ReachabilitySource and MUST NOT import integrations/* \u2014 concrete sources (runtime-local networkReachability) are wired via an integration\u2192port adapter (plan \xA72.3/\xA74.2, Architect finding 1). isNetworkAllowed mirrors runtime-local's outboundNetworkAllowed = reachability !== 'offline'. createStaticReachabilitySource('online') is the B1a default and online test fixture."};function _(){let e=new Map;return{get:async(t)=>{let n=e.get(t);return n===void 0?void 0:structuredClone(n)},set:async(t,n)=>{e.set(t,structuredClone(n))},remove:async(t)=>{e.delete(t)},keys:async(t)=>{let n=[...e.keys()];return t===void 0?n:n.filter((a)=>a.startsWith(t))},clear:async()=>{e.clear()}}}import{invalidationTagMatches as fe}from"@lssm/lib.contracts-spec/query/invalidation";var Z="cache:";function U(e){let t=new Map,n=(a)=>`${Z}${a}`;return{read:(a)=>e.get(n(a)),write:async(a)=>{t.set(a.key,a.tags),await e.set(n(a.key),a)},remove:async(a)=>{t.delete(a),await e.remove(n(a))},entries:async()=>{let a=await e.keys(Z),i=[];for(let u of a){let r=await e.get(u);if(r!==void 0)i.push(r)}return i},invalidate:async(a,i)=>{let u=[];for(let[r,y]of t.entries()){if(!y.some((h)=>a.some((E)=>fe(h,E))))continue;let T=await e.get(n(r));if(T===void 0)continue;await e.set(n(r),{...T,status:"stale",staleAt:i}),u.push(r)}return u}}}function de(e){let{serverData:t,localData:n}=e,a=e.mergeFields??[];if(typeof t!=="object"||t===null||typeof n!=="object"||n===null)return{outcome:"surface"};let i={...t},u=n;for(let r of a)if(r in u)i[r]=u[r];return{outcome:"merged",data:i}}function F(){let e=new Map;return{register(t,n){e.set(t,n)},has(t){return e.has(t)},async resolve(t){switch(t.strategy){case"server-wins":return{outcome:"use-server",data:t.serverData};case"client-wins":return{outcome:"use-client",data:t.localData};case"field-merge":return de(t);case"surface":return{outcome:"surface"};default:{let n=t.policy.resolver,a=n===void 0?void 0:e.get(n);if(a===void 0)return{outcome:"surface"};return a(t)}}}}}function A(){let e=new Set;return{emit(t){for(let n of e)n(t)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}}function ee(e){return{async collectGarbage(){let t=e.now(),n=0;for(let a of await e.cache.entries())if(a.expiresAt!==void 0&&t>=a.expiresAt)await e.cache.remove(a.key),n+=1;return e.events.emit({type:"gc-collected",evicted:n}),n},async revalidateStale(){let t=e.revalidate;if(t===void 0)return 0;let n=e.now(),a=0;for(let i of await e.cache.entries()){if(!(i.staleAt!==void 0&&n>=i.staleAt)||i.envelope===void 0)continue;await t(i.envelope),e.events.emit({type:"revalidated",key:i.key}),a+=1}return a}}}import{mergeSafeEconomicEvidenceRefIds as me,parseSafeEconomicEvidenceRefId as ye}from"@lssm/lib.contracts-spec/operations";import{defaultConflictPolicy as ge}from"@lssm/lib.contracts-spec/query/conflict-policy";function te(e,t={}){let n=t.baseMs??200,a=t.factor??2,i=t.maxMs??30000,u=t.jitter??Math.random,r=Math.max(0,e-1),y=Math.min(i,n*a**r);return Math.round(y*(0.5+u()*0.5))}import{defaultConflictPolicy as pe}from"@lssm/lib.contracts-spec/query/conflict-policy";async function N(e,t){for(let n of e)if(n.previous===void 0)await t.remove(n.key);else await t.write(n.previous)}async function ne(e,t,n){let a=e.conflictPolicy??pe(),i=e.rollback[0]?.key,u=i===void 0?void 0:await n.cache.read(i),r=t.metadata?.serverData,y=await n.resolvers.resolve({strategy:a.strategy,policy:a,localData:u?.data,serverData:r,localVersion:e.versionToken,serverVersion:t.versionToken,mergeFields:a.mergeFields});switch(n.events.emit({type:"mutation-conflict",id:e.id,strategy:a.strategy,outcome:y.outcome}),y.outcome){case"use-server":{await N(e.rollback,n.cache),await n.cache.invalidate(e.tags,n.now());for(let M of e.rollback)await n.queryEngine?.refresh(M.key);return"resolved"}case"use-client":return await n.queue.update({...e,versionToken:t.versionToken,status:"pending"}),"re-enqueued";case"merged":{if(i!==void 0&&u!==void 0)await n.cache.write({...u,data:y.data,versionToken:t.versionToken,status:"warm",storedAt:n.now()}),await n.queryEngine?.refresh(i);return await n.queue.update({...e,versionToken:t.versionToken,status:"pending"}),"re-enqueued"}default:{if(i!==void 0)await n.queryEngine?.emitConflict(i,{versionToken:t.versionToken,serverData:r,clientData:u?.data,strategy:a.strategy});return"resolved"}}}var ve=5;function W(...e){return me(...e)}function he(e){let t=ye(e);if(!t)throw Error("EconomicEvidenceInvalidRef: unsafe or invalid economic evidence ref");return t}function ae(e){let{transport:t,cache:n,queue:a,reachability:i,resolvers:u,events:r}=e,y=e.now??(()=>Date.now()),M=e.maxAttempts??ve,T={cache:n,queue:a,resolvers:u,events:r,now:y,queryEngine:e.queryEngine},h=!1,E=0,b=null,D=new Map,q=(o,l=o.economicEvidenceRefs)=>({operation:o.operation,input:o.input,tags:o.tags,economicEvidenceRefs:l});async function v(o){let l=[];for(let f of o){let g=await n.read(f.key);l.push({key:f.key,previous:g}),await n.write({key:f.key,data:f.data,versionToken:f.versionToken??g?.versionToken,status:"warm",tags:g?.tags??[],storedAt:y(),staleAt:g?.staleAt,expiresAt:g?.expiresAt}),await e.queryEngine?.refresh(f.key)}return l}async function w(o,l){for(let f of o.rollback){let g=await n.read(f.key);if(g!==void 0)await n.write({...g,versionToken:l.versionToken??g.versionToken,status:"stale",staleAt:y()});await e.queryEngine?.refresh(f.key)}await n.invalidate(o.tags,y())}async function p(o){let l;try{l=W(o.economicEvidenceRefs)}catch{await N(o.rollback,n);for(let S of o.rollback)await e.queryEngine?.refresh(S.key);return await a.remove(o.id),r.emit({type:"mutation-failed",id:o.id,code:"ECONOMIC_EVIDENCE_INVALID_REF"}),D.set(o.id,{id:o.id,status:"failed",problem:{type:"about:blank",title:"Economic evidence ref rejected",status:422,code:"ECONOMIC_EVIDENCE_INVALID_REF",category:"validation",severity:"error",retryable:!1}}),!0}let f=o.attempts+1;await a.update({...o,status:"in-flight",attempts:f});let g=await t.execute(q(o,l),e.contextProvider?.());if(g.ok){let S=W(l,g.data.economicEvidenceRefs);if(await w(o,g.data),await a.remove(o.id),r.emit({type:"mutation-replayed",id:o.id,queueDepth:await a.depth()}),S.length>0)r.emit({type:"economic-evidence-attached",operationKey:o.operation.key,evidenceRefs:S.map(he)});return D.set(o.id,{id:o.id,status:"done",data:g.data.data,economicEvidenceRefs:S}),!0}let{problem:k}=g;if(k.category==="auth"){if(h=!0,await a.update({...o,status:"paused-auth",attempts:f}),r.emit({type:"auth-expired",id:o.id}),D.set(o.id,{id:o.id,status:"paused-auth",problem:k}),e.refreshAuth!==void 0&&await e.refreshAuth())return h=!1,await a.update({...o,status:"pending",attempts:f}),!0;return!1}if(k.category==="conflict"){if(await ne(o,k,T)==="resolved")await a.remove(o.id),D.set(o.id,{id:o.id,status:"done",problem:k});return!0}if(k.retryable&&f<M){let S=te(f,e.backoff);return await a.update({...o,status:"failed",attempts:f}),r.emit({type:"mutation-retry",id:o.id,attempt:f,delayMs:S}),D.set(o.id,{id:o.id,status:"failed",problem:k}),e.schedule?.(()=>{d()},S),!0}await N(o.rollback,n);for(let S of o.rollback)await e.queryEngine?.refresh(S.key);return await a.remove(o.id),r.emit({type:"mutation-failed",id:o.id,code:g.code}),D.set(o.id,{id:o.id,status:"failed",problem:k}),!0}async function c(){if(h)return;if(!C(i.getSnapshot().signal))return;for(let o of await a.list()){if(o.status==="paused-auth")continue;if(!await p(o))return}r.emit({type:"queue-drained",remaining:await a.depth()})}function d(){if(b!==null)return b;return b=c().finally(()=>{b=null}),b}async function s(o,l={}){W(o.economicEvidenceRefs);let f=y();E+=1;let g=`${f}-${E}`,k=l.optimistic?await v(l.optimistic):[];if(await a.enqueue({...o,conflictPolicy:o.conflictPolicy??ge(),id:g,enqueuedAt:f,attempts:0,status:"pending",rollback:k}),r.emit({type:"mutation-enqueued",id:g,queueDepth:await a.depth()}),!h&&C(i.getSnapshot().signal))await d();return D.get(g)??{id:g,status:"pending"}}async function m(){h=!1;for(let o of await a.list())if(o.status==="paused-auth")await a.update({...o,status:"pending"});await d()}let O=i.subscribe((o)=>{if(!h&&C(o.signal))d()});return{mutate:s,drain:d,resume:m,dispose:O}}function oe(e){let t=(a)=>`queue:${a}`;async function n(){let a=await e.keys("queue:"),i=[];for(let u of a){let r=await e.get(u);if(r!==void 0)i.push(r)}return i.sort((u,r)=>u.enqueuedAt===r.enqueuedAt?u.id.localeCompare(r.id):u.enqueuedAt-r.enqueuedAt),i}return{enqueue:(a)=>e.set(t(a.id),a),update:(a)=>e.set(t(a.id),a),remove:(a)=>e.remove(t(a)),list:()=>n(),depth:async()=>(await e.keys("queue:")).length}}import{createQueryKey as G}from"@lssm/lib.contracts-spec/query/key";function x(e,t){if(e?.staleAt===void 0)return!1;return t>=e.staleAt}function L(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function V(e,t){if(e===void 0)return{status:"loading",fetchStatus:"fetching",cacheStatus:"empty",isStale:!1,isOffline:!1};let n=x(e,t);return{status:"success",fetchStatus:"fetching",data:e.data,cacheStatus:n?"stale":e.status,isStale:n,isOffline:!1,versionToken:e.versionToken,dataUpdatedAt:e.storedAt,...e.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function Q(e,t){let n=x(e,t);return{status:"success",fetchStatus:"idle",data:e.data,cacheStatus:n?"stale":e.status,isStale:n,isOffline:!1,versionToken:e.versionToken,dataUpdatedAt:e.storedAt,...e.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function j(e,t,n){return{status:"error",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:x(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function B(e,t,n){return{status:"auth-expired",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:x(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function K(e,t,n){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"conflict",isStale:x(e,n),isOffline:!1,conflict:t,versionToken:t.versionToken??e?.versionToken,dataUpdatedAt:e?.storedAt}}function X(e,t){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"offline",isStale:x(e,t),isOffline:!0,versionToken:e?.versionToken,dataUpdatedAt:e?.storedAt,...e?.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function ie(e,t){let{transport:n}=e,a=e.advanced??{},i=a.now??(()=>Date.now()),u=a.reachability??R("online"),r=a.events??A(),y=a.resolvers??F(),M=t??I({defaultMode:e.localOffline,env:e.env,flag:e.flag}),T=new Map,h=new Map,E=0;function b(v,w){T.set(v,w);for(let p of h.get(v)??[])p(w)}return{...{async query(v,w){let p=G(v),c=u.getSnapshot().signal;if(!C(c)){let l=X(void 0,i());return b(p,l),l}b(p,V(void 0,i()));let d=i(),s=await n.execute(v,w),m=i();if(!s.ok){r.emit({type:"fetch-error",key:p,code:s.code,latencyMs:m-d});let l=s.problem.category==="auth"?B(s.problem,void 0,m):j(s.problem,void 0,m);return b(p,l),l}r.emit({type:"fetch-success",key:p,latencyMs:m-d});let O={key:p,data:s.data.data,versionToken:s.data.versionToken??s.versionToken,status:c==="restricted"?"warm":"fresh",tags:[],storedAt:m,...s.data.pageInfo===void 0?{}:{pageInfo:s.data.pageInfo}},o=Q(O,m);return b(p,o),o},getState(v){return T.get(G(v))??L()},subscribe(v,w){let p=G(v),c=h.get(p);if(c===void 0)c=new Set,h.set(p,c);return c.add(w),()=>c?.delete(w)},async invalidateTags(){return[]},async refresh(v){let w=T.get(v)??L();return b(v,w),w},async emitConflict(v,w){let p=K(void 0,w,i());return b(v,p),p}},...{async mutate(v,w={}){E+=1;let p=`${i()}-${E}`;if(!C(u.getSnapshot().signal))return{id:p,status:"disabled-offline",problem:Ee()};let c=await n.execute({operation:v.operation,input:v.input,tags:v.tags},w.ctx??a.contextProvider?.());if(c.ok)return{id:p,status:"done",data:c.data.data};if(c.problem.category==="auth")return{id:p,status:"paused-auth",problem:c.problem};return{id:p,status:"failed",problem:c.problem}},async drain(){},async resume(){},dispose(){}},maintenance:{collectGarbage:async()=>0,revalidateStale:async()=>0},events:r,resolvers:y,localOffline:M}}function Ee(){return{type:"about:blank",title:"Offline mutation queue disabled",status:409,code:"OFFLINE_QUEUE_DISABLED",category:"conflict",severity:"warning",retryable:!1,detail:"localOffline=online-only does not cache local data or queue offline mutations."}}import{createQueryKey as H}from"@lssm/lib.contracts-spec/query/key";function re(){let e=new Map;return{run(t,n){let a=e.get(t);if(a!==void 0)return a;let i=n().finally(()=>{e.delete(t)});return e.set(t,i),i},size(){return e.size}}}var we=0;function se(e){let{transport:t}=e,n=e.storage??_(),a=e.reachability??R("online"),i=e.now??(()=>Date.now()),u=e.defaultStaleTimeMs??we,r=e.cacheStore??U(n),y=e.events??A(),M=re(),T=new Map,h=new Map;function E(c,d){T.set(c,d);let s=h.get(c);if(s===void 0)return;for(let m of s)m(d)}async function b(c,d){let s=H(c),m=a.getSnapshot().signal,O=await r.read(s);if(!C(m)){let P=X(O,i());return E(s,P),P}if(O!==void 0&&!x(O,i())){y.emit({type:"cache-hit",key:s});let P=Q(O,i());return E(s,P),P}y.emit({type:"cache-miss",key:s}),E(s,V(O,i()));let o=i(),l=await M.run(s,()=>t.execute(c,d)),f=i();if(!l.ok){y.emit({type:"fetch-error",key:s,code:l.code,latencyMs:f-o});let P=l.problem.category==="auth"?B(l.problem,O,f):j(l.problem,O,f);return E(s,P),P}y.emit({type:"fetch-success",key:s,latencyMs:f-o});let g=c.cache?.staleTimeMs??u,k=c.cache?.cacheTimeMs,S={key:s,data:l.data.data,versionToken:l.data.versionToken??l.versionToken,status:m==="restricted"?"warm":"fresh",tags:c.tags??[],storedAt:f,staleAt:f+g,envelope:c,...k===void 0?{}:{expiresAt:f+k},...l.data.pageInfo===void 0?{}:{pageInfo:l.data.pageInfo}};await r.write(S);let J=Q(S,f);return E(s,J),J}async function D(c){let d=await r.read(c),s=d===void 0?L():Q(d,i());return E(c,s),s}async function q(c,d){let s=await r.read(c),m=K(s,d,i());return E(c,m),m}function v(c){return T.get(H(c))??L()}function w(c,d){let s=H(c),m=h.get(s);if(m===void 0)m=new Set,h.set(s,m);return m.add(d),()=>{h.get(s)?.delete(d)}}async function p(c){let d=await r.invalidate(c,i());for(let s of d){let m=await r.read(s);if(m!==void 0)E(s,Q(m,i()))}return d}return{query:b,getState:v,subscribe:w,invalidateTags:p,refresh:D,emitConflict:q}}function ce(e){Se(e);let t=e.advanced??{},n=I({defaultMode:e.localOffline,env:e.env,flag:e.flag});if(be(n.mode,t),n.mode==="online-only")return ie(e,n);let a=t.now??(()=>Date.now()),i=t.storage??_(),u=t.events??A(),r=t.cacheStore??U(i),y=t.reachability??R("online"),M=t.resolvers??F(),T=oe(i),h=se({...t,transport:e.transport,now:a,storage:i,events:u,cacheStore:r,reachability:y}),E=ae({transport:e.transport,cache:r,queue:T,reachability:y,resolvers:M,events:u,now:a,maxAttempts:t.maxAttempts,queryEngine:h,contextProvider:t.contextProvider,refreshAuth:t.refreshAuth,schedule:t.schedule,backoff:t.backoff}),b=ee({cache:r,events:u,now:a,revalidate:(D)=>h.query(D)});return{...h,...E,maintenance:b,events:u,resolvers:M,localOffline:n}}function be(e,t){if(e==="offline-capable"){if(t.storage===void 0)throw Error("localOffline=offline-capable requires advanced.storage; pass a durable StoragePort or explicit platform storage adapter.");return}if(t.storage!==void 0)throw Error(`localOffline=${e} rejects advanced.storage; this mode must not use durable local persistence.`);if(t.cacheStore!==void 0)throw Error(`localOffline=${e} rejects advanced.cacheStore; this mode owns cache semantics.`)}function Se(e){let t=e;if(t.storage!==void 0)throw Error("Use advanced.storage instead of top-level storage.");if(t.cacheStore!==void 0)throw Error("Use advanced.cacheStore instead of top-level cacheStore.")}function Te(){return globalThis.process?.env}function wt(e){return ce({...e,env:e.env??Te()})}export{wt as createDataEngineFromEnv,Te as readHostEnv};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { QueryEnvelope } from '@lssm/lib.contracts-spec/query/envelope';
|
|
2
|
+
import type { OnlineSignal, ReachabilitySource } from '../reachability.js';
|
|
3
|
+
import type { Transport } from '../transport.js';
|
|
4
|
+
import type { MutationRequest } from './mutation-types.js';
|
|
5
|
+
export interface User {
|
|
6
|
+
id: string;
|
|
7
|
+
name: string;
|
|
8
|
+
}
|
|
9
|
+
export type UpdateMode = 'ok' | 'conflict' | 'auth' | 'retry';
|
|
10
|
+
export declare const QUERY: QueryEnvelope;
|
|
11
|
+
export declare const MUTATION: MutationRequest;
|
|
12
|
+
export declare const KEY: string;
|
|
13
|
+
export declare const optimisticLocal: {
|
|
14
|
+
key: string;
|
|
15
|
+
data: {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
};
|
|
19
|
+
versionToken: string;
|
|
20
|
+
}[];
|
|
21
|
+
export declare function mutableReachability(initial: OnlineSignal): {
|
|
22
|
+
source: ReachabilitySource;
|
|
23
|
+
set: (signal: OnlineSignal) => void;
|
|
24
|
+
};
|
|
25
|
+
export declare function scriptedTransport(): {
|
|
26
|
+
transport: Transport;
|
|
27
|
+
calls: string[];
|
|
28
|
+
setMode: (m: UpdateMode) => void;
|
|
29
|
+
setRetryFails: (n: number) => void;
|
|
30
|
+
};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{createQueryKey as i}from"@lssm/lib.contracts-spec/query/key";import{contractOk as u}from"@lssm/lib.contracts-spec/results";var l={operation:{key:"user.get",version:"1.0.0"},input:{id:"u1"},tags:[{entity:"user",id:"u1"}],cache:{staleTimeMs:60000}},m={operation:{key:"user.update",version:"1.0.0"},input:{id:"u1",name:"Local"},tags:[{entity:"user",id:"u1"}],versionToken:"v1"},p=i(l),v=[{key:p,data:{id:"u1",name:"Local"},versionToken:"v1"}];function h(t){let e={signal:t,since:0},r=new Set;return{source:{getSnapshot:()=>e,subscribe:(n)=>(r.add(n),()=>r.delete(n))},set:(n)=>{e={signal:n,since:0};for(let s of r)s(e)}}}function c(t,e){return{ok:!1,code:"CONFLICT",status:409,problem:{type:"about:blank",title:"Conflict",status:409,code:"CONFLICT",category:"conflict",severity:"warning",retryable:!1,versionToken:t,metadata:{serverData:e}}}}function a(t,e,r){return{ok:!1,code:t,status:e==="auth"?401:503,problem:{type:"about:blank",title:t,status:e==="auth"?401:503,code:t,category:e,severity:"error",retryable:r}}}function b(){let t={id:"u1",name:"Server"},e="v1",r="ok",n=0,s=[];return{transport:{execute:async(o)=>{if(s.push(o.operation.key),o.operation.key==="user.update"){if(r==="conflict")return c("v2",t);if(r==="auth")return a("UNAUTHENTICATED","auth",!1);if(r==="retry"&&n>0)return n-=1,a("SERVICE_UNAVAILABLE","dependency",!0);e="v2",t={...t,name:o.input.name}}return u({data:t,versionToken:e},{versionToken:e})}},calls:s,setMode:(o)=>{r=o},setRetryFails:(o)=>{n=o}}}export{p as KEY,m as MUTATION,l as QUERY,h as mutableReachability,v as optimisticLocal,b as scriptedTransport};
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { type LocalOfflinePolicy, type LocalOfflinePolicyInput } from '../local-offline.js';
|
|
2
|
+
import type { Transport, TransportContext } from '../transport.js';
|
|
3
|
+
import type { BackoffOptions } from './backoff.js';
|
|
4
|
+
import type { EngineEvents } from './events.js';
|
|
5
|
+
import { type Maintenance } from './maintenance.js';
|
|
6
|
+
import type { ConflictResolverRegistry, MutationEngine } from './mutation-types.js';
|
|
7
|
+
import type { QueryEngine, QueryEngineOptions } from './types.js';
|
|
8
|
+
export interface DataEngineAdvancedOptions extends Omit<QueryEngineOptions, 'transport'> {
|
|
9
|
+
/** Conflict-resolver registry; a fresh one (built-ins only) by default. */
|
|
10
|
+
resolvers?: ConflictResolverRegistry;
|
|
11
|
+
maxAttempts?: number;
|
|
12
|
+
contextProvider?: () => TransportContext | undefined;
|
|
13
|
+
refreshAuth?: () => Promise<boolean>;
|
|
14
|
+
schedule?: (run: () => void, delayMs: number) => void;
|
|
15
|
+
backoff?: BackoffOptions;
|
|
16
|
+
}
|
|
17
|
+
export interface DataEngineOptions {
|
|
18
|
+
transport: Transport;
|
|
19
|
+
/** Semantic local/offline capability mode. Defaults to structurally online-only. */
|
|
20
|
+
localOffline?: LocalOfflinePolicyInput;
|
|
21
|
+
/**
|
|
22
|
+
* Host-supplied env value or env-like object (e.g. `process.env`). When set,
|
|
23
|
+
* the policy resolver reads `CONTRACTSPEC_LOCAL_OFFLINE` from it. This is the
|
|
24
|
+
* env-driven on/off trigger: it wins over `localOffline`/default but loses to
|
|
25
|
+
* `flag`. The core never reads global `process.env` itself — the host injects
|
|
26
|
+
* it (see `createDataEngineFromEnv`).
|
|
27
|
+
*/
|
|
28
|
+
env?: unknown;
|
|
29
|
+
/** Host-supplied feature flag; when set it wins over `env` and `localOffline`. */
|
|
30
|
+
flag?: unknown;
|
|
31
|
+
/** Low-level composition escape hatches used inside the selected mode. */
|
|
32
|
+
advanced?: DataEngineAdvancedOptions;
|
|
33
|
+
}
|
|
34
|
+
/** The unified data engine: queries + mutations + maintenance over one cache. */
|
|
35
|
+
export interface DataEngine extends QueryEngine, MutationEngine {
|
|
36
|
+
maintenance: Maintenance;
|
|
37
|
+
events: EngineEvents;
|
|
38
|
+
resolvers: ConflictResolverRegistry;
|
|
39
|
+
localOffline: LocalOfflinePolicy;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Composes the query engine, mutation engine, and maintenance over one selected
|
|
43
|
+
* local/offline capability mode. `online-only` is structurally no-cache/no-queue;
|
|
44
|
+
* `memory-cache` and `offline-capable` reuse the cache/queue implementation.
|
|
45
|
+
*/
|
|
46
|
+
export declare function createDataEngine(options: DataEngineOptions): DataEngine;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var ce=new Set(["online-only","memory-cache","offline-capable"]),Y={"online-only":"online-only",online:"online-only",disabled:"online-only",false:"online-only",off:"online-only","memory-cache":"memory-cache",memory:"memory-cache",cache:"memory-cache","offline-capable":"offline-capable",offline:"offline-capable",enabled:"offline-capable",true:"offline-capable",on:"offline-capable"};function be(e){return typeof e==="string"&&ce.has(e)}function I(e={}){let t=e.defaultMode===void 0?{mode:"online-only",source:"default",reason:"No local/offline policy input was supplied."}:z(e.defaultMode,"explicit")??{mode:"online-only",source:"explicit"},n=z(ue(e.env),"env",{allowUnknown:!0});return z(e.flag,"feature-flag")??n??t}function ue(e){if(typeof e!=="object"||e===null)return e;if("CONTRACTSPEC_LOCAL_OFFLINE"in e)return e.CONTRACTSPEC_LOCAL_OFFLINE;if("mode"in e)return e;return}function z(e,t,n={}){if(e===void 0||e===null||e==="")return;if(typeof e==="boolean")return{mode:e?"offline-capable":"online-only",source:t};if(typeof e==="string"){let a=Y[e.trim().toLowerCase()];if(a!==void 0)return{mode:a,source:t};if(n.allowUnknown)return}if(typeof e==="object"&&e!==null&&"mode"in e){let a=e,i=typeof a.mode==="string"?Y[a.mode.trim().toLowerCase()]:void 0;if(i!==void 0)return{mode:i,source:t,...typeof a.reason==="string"?{reason:a.reason}:{}};if(n.allowUnknown)return}throw Error(`Unsupported localOffline policy value: ${String(e)}`)}function O(e){return e!=="offline"}function R(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var ke={id:"docs.contracts-runtime-core.reachability",title:"Reachability input port (tri-state)",summary:"Engine-owned reachability input port; integrations feed it, the engine never imports them.",kind:"reference",visibility:"internal",route:"/docs/contracts-runtime-core/reachability",domain:"contracts-runtime-core",owners:["@contractspec-core"],tags:["contracts-runtime-core","reachability","offline","port"],body:"Tri-state OnlineSignal ('online' | 'restricted' | 'offline'). 'online' allows normal fetch + queue drain. 'restricted' is online-degraded: the engine fetches and drains the queue as if online but may mark results stale-eligible; it never suspends network nor activates the offline queue. Only 'offline' suspends outbound network and activates the durable offline mutation queue. The engine reads reachability solely through ReachabilitySource and MUST NOT import integrations/* \u2014 concrete sources (runtime-local networkReachability) are wired via an integration\u2192port adapter (plan \xA72.3/\xA74.2, Architect finding 1). isNetworkAllowed mirrors runtime-local's outboundNetworkAllowed = reachability !== 'offline'. createStaticReachabilitySource('online') is the B1a default and online test fixture."};function _(){let e=new Map;return{get:async(t)=>{let n=e.get(t);return n===void 0?void 0:structuredClone(n)},set:async(t,n)=>{e.set(t,structuredClone(n))},remove:async(t)=>{e.delete(t)},keys:async(t)=>{let n=[...e.keys()];return t===void 0?n:n.filter((a)=>a.startsWith(t))},clear:async()=>{e.clear()}}}import{invalidationTagMatches as le}from"@lssm/lib.contracts-spec/query/invalidation";var Z="cache:";function U(e){let t=new Map,n=(a)=>`${Z}${a}`;return{read:(a)=>e.get(n(a)),write:async(a)=>{t.set(a.key,a.tags),await e.set(n(a.key),a)},remove:async(a)=>{t.delete(a),await e.remove(n(a))},entries:async()=>{let a=await e.keys(Z),i=[];for(let u of a){let r=await e.get(u);if(r!==void 0)i.push(r)}return i},invalidate:async(a,i)=>{let u=[];for(let[r,y]of t.entries()){if(!y.some((h)=>a.some((E)=>le(h,E))))continue;let T=await e.get(n(r));if(T===void 0)continue;await e.set(n(r),{...T,status:"stale",staleAt:i}),u.push(r)}return u}}}function fe(e){let{serverData:t,localData:n}=e,a=e.mergeFields??[];if(typeof t!=="object"||t===null||typeof n!=="object"||n===null)return{outcome:"surface"};let i={...t},u=n;for(let r of a)if(r in u)i[r]=u[r];return{outcome:"merged",data:i}}function F(){let e=new Map;return{register(t,n){e.set(t,n)},has(t){return e.has(t)},async resolve(t){switch(t.strategy){case"server-wins":return{outcome:"use-server",data:t.serverData};case"client-wins":return{outcome:"use-client",data:t.localData};case"field-merge":return fe(t);case"surface":return{outcome:"surface"};default:{let n=t.policy.resolver,a=n===void 0?void 0:e.get(n);if(a===void 0)return{outcome:"surface"};return a(t)}}}}}function A(){let e=new Set;return{emit(t){for(let n of e)n(t)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}}function ee(e){return{async collectGarbage(){let t=e.now(),n=0;for(let a of await e.cache.entries())if(a.expiresAt!==void 0&&t>=a.expiresAt)await e.cache.remove(a.key),n+=1;return e.events.emit({type:"gc-collected",evicted:n}),n},async revalidateStale(){let t=e.revalidate;if(t===void 0)return 0;let n=e.now(),a=0;for(let i of await e.cache.entries()){if(!(i.staleAt!==void 0&&n>=i.staleAt)||i.envelope===void 0)continue;await t(i.envelope),e.events.emit({type:"revalidated",key:i.key}),a+=1}return a}}}import{mergeSafeEconomicEvidenceRefIds as me,parseSafeEconomicEvidenceRefId as pe}from"@lssm/lib.contracts-spec/operations";import{defaultConflictPolicy as ye}from"@lssm/lib.contracts-spec/query/conflict-policy";function te(e,t={}){let n=t.baseMs??200,a=t.factor??2,i=t.maxMs??30000,u=t.jitter??Math.random,r=Math.max(0,e-1),y=Math.min(i,n*a**r);return Math.round(y*(0.5+u()*0.5))}import{defaultConflictPolicy as de}from"@lssm/lib.contracts-spec/query/conflict-policy";async function N(e,t){for(let n of e)if(n.previous===void 0)await t.remove(n.key);else await t.write(n.previous)}async function ne(e,t,n){let a=e.conflictPolicy??de(),i=e.rollback[0]?.key,u=i===void 0?void 0:await n.cache.read(i),r=t.metadata?.serverData,y=await n.resolvers.resolve({strategy:a.strategy,policy:a,localData:u?.data,serverData:r,localVersion:e.versionToken,serverVersion:t.versionToken,mergeFields:a.mergeFields});switch(n.events.emit({type:"mutation-conflict",id:e.id,strategy:a.strategy,outcome:y.outcome}),y.outcome){case"use-server":{await N(e.rollback,n.cache),await n.cache.invalidate(e.tags,n.now());for(let M of e.rollback)await n.queryEngine?.refresh(M.key);return"resolved"}case"use-client":return await n.queue.update({...e,versionToken:t.versionToken,status:"pending"}),"re-enqueued";case"merged":{if(i!==void 0&&u!==void 0)await n.cache.write({...u,data:y.data,versionToken:t.versionToken,status:"warm",storedAt:n.now()}),await n.queryEngine?.refresh(i);return await n.queue.update({...e,versionToken:t.versionToken,status:"pending"}),"re-enqueued"}default:{if(i!==void 0)await n.queryEngine?.emitConflict(i,{versionToken:t.versionToken,serverData:r,clientData:u?.data,strategy:a.strategy});return"resolved"}}}var ge=5;function W(...e){return me(...e)}function ve(e){let t=pe(e);if(!t)throw Error("EconomicEvidenceInvalidRef: unsafe or invalid economic evidence ref");return t}function ae(e){let{transport:t,cache:n,queue:a,reachability:i,resolvers:u,events:r}=e,y=e.now??(()=>Date.now()),M=e.maxAttempts??ge,T={cache:n,queue:a,resolvers:u,events:r,now:y,queryEngine:e.queryEngine},h=!1,E=0,S=null,D=new Map,q=(o,l=o.economicEvidenceRefs)=>({operation:o.operation,input:o.input,tags:o.tags,economicEvidenceRefs:l});async function v(o){let l=[];for(let f of o){let g=await n.read(f.key);l.push({key:f.key,previous:g}),await n.write({key:f.key,data:f.data,versionToken:f.versionToken??g?.versionToken,status:"warm",tags:g?.tags??[],storedAt:y(),staleAt:g?.staleAt,expiresAt:g?.expiresAt}),await e.queryEngine?.refresh(f.key)}return l}async function w(o,l){for(let f of o.rollback){let g=await n.read(f.key);if(g!==void 0)await n.write({...g,versionToken:l.versionToken??g.versionToken,status:"stale",staleAt:y()});await e.queryEngine?.refresh(f.key)}await n.invalidate(o.tags,y())}async function m(o){let l;try{l=W(o.economicEvidenceRefs)}catch{await N(o.rollback,n);for(let b of o.rollback)await e.queryEngine?.refresh(b.key);return await a.remove(o.id),r.emit({type:"mutation-failed",id:o.id,code:"ECONOMIC_EVIDENCE_INVALID_REF"}),D.set(o.id,{id:o.id,status:"failed",problem:{type:"about:blank",title:"Economic evidence ref rejected",status:422,code:"ECONOMIC_EVIDENCE_INVALID_REF",category:"validation",severity:"error",retryable:!1}}),!0}let f=o.attempts+1;await a.update({...o,status:"in-flight",attempts:f});let g=await t.execute(q(o,l),e.contextProvider?.());if(g.ok){let b=W(l,g.data.economicEvidenceRefs);if(await w(o,g.data),await a.remove(o.id),r.emit({type:"mutation-replayed",id:o.id,queueDepth:await a.depth()}),b.length>0)r.emit({type:"economic-evidence-attached",operationKey:o.operation.key,evidenceRefs:b.map(ve)});return D.set(o.id,{id:o.id,status:"done",data:g.data.data,economicEvidenceRefs:b}),!0}let{problem:k}=g;if(k.category==="auth"){if(h=!0,await a.update({...o,status:"paused-auth",attempts:f}),r.emit({type:"auth-expired",id:o.id}),D.set(o.id,{id:o.id,status:"paused-auth",problem:k}),e.refreshAuth!==void 0&&await e.refreshAuth())return h=!1,await a.update({...o,status:"pending",attempts:f}),!0;return!1}if(k.category==="conflict"){if(await ne(o,k,T)==="resolved")await a.remove(o.id),D.set(o.id,{id:o.id,status:"done",problem:k});return!0}if(k.retryable&&f<M){let b=te(f,e.backoff);return await a.update({...o,status:"failed",attempts:f}),r.emit({type:"mutation-retry",id:o.id,attempt:f,delayMs:b}),D.set(o.id,{id:o.id,status:"failed",problem:k}),e.schedule?.(()=>{d()},b),!0}await N(o.rollback,n);for(let b of o.rollback)await e.queryEngine?.refresh(b.key);return await a.remove(o.id),r.emit({type:"mutation-failed",id:o.id,code:g.code}),D.set(o.id,{id:o.id,status:"failed",problem:k}),!0}async function c(){if(h)return;if(!O(i.getSnapshot().signal))return;for(let o of await a.list()){if(o.status==="paused-auth")continue;if(!await m(o))return}r.emit({type:"queue-drained",remaining:await a.depth()})}function d(){if(S!==null)return S;return S=c().finally(()=>{S=null}),S}async function s(o,l={}){W(o.economicEvidenceRefs);let f=y();E+=1;let g=`${f}-${E}`,k=l.optimistic?await v(l.optimistic):[];if(await a.enqueue({...o,conflictPolicy:o.conflictPolicy??ye(),id:g,enqueuedAt:f,attempts:0,status:"pending",rollback:k}),r.emit({type:"mutation-enqueued",id:g,queueDepth:await a.depth()}),!h&&O(i.getSnapshot().signal))await d();return D.get(g)??{id:g,status:"pending"}}async function p(){h=!1;for(let o of await a.list())if(o.status==="paused-auth")await a.update({...o,status:"pending"});await d()}let C=i.subscribe((o)=>{if(!h&&O(o.signal))d()});return{mutate:s,drain:d,resume:p,dispose:C}}function oe(e){let t=(a)=>`queue:${a}`;async function n(){let a=await e.keys("queue:"),i=[];for(let u of a){let r=await e.get(u);if(r!==void 0)i.push(r)}return i.sort((u,r)=>u.enqueuedAt===r.enqueuedAt?u.id.localeCompare(r.id):u.enqueuedAt-r.enqueuedAt),i}return{enqueue:(a)=>e.set(t(a.id),a),update:(a)=>e.set(t(a.id),a),remove:(a)=>e.remove(t(a)),list:()=>n(),depth:async()=>(await e.keys("queue:")).length}}import{createQueryKey as G}from"@lssm/lib.contracts-spec/query/key";function Q(e,t){if(e?.staleAt===void 0)return!1;return t>=e.staleAt}function L(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function V(e,t){if(e===void 0)return{status:"loading",fetchStatus:"fetching",cacheStatus:"empty",isStale:!1,isOffline:!1};let n=Q(e,t);return{status:"success",fetchStatus:"fetching",data:e.data,cacheStatus:n?"stale":e.status,isStale:n,isOffline:!1,versionToken:e.versionToken,dataUpdatedAt:e.storedAt,...e.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function x(e,t){let n=Q(e,t);return{status:"success",fetchStatus:"idle",data:e.data,cacheStatus:n?"stale":e.status,isStale:n,isOffline:!1,versionToken:e.versionToken,dataUpdatedAt:e.storedAt,...e.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function j(e,t,n){return{status:"error",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:Q(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function B(e,t,n){return{status:"auth-expired",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:Q(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function K(e,t,n){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"conflict",isStale:Q(e,n),isOffline:!1,conflict:t,versionToken:t.versionToken??e?.versionToken,dataUpdatedAt:e?.storedAt}}function X(e,t){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"offline",isStale:Q(e,t),isOffline:!0,versionToken:e?.versionToken,dataUpdatedAt:e?.storedAt,...e?.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}function ie(e,t){let{transport:n}=e,a=e.advanced??{},i=a.now??(()=>Date.now()),u=a.reachability??R("online"),r=a.events??A(),y=a.resolvers??F(),M=t??I({defaultMode:e.localOffline,env:e.env,flag:e.flag}),T=new Map,h=new Map,E=0;function S(v,w){T.set(v,w);for(let m of h.get(v)??[])m(w)}return{...{async query(v,w){let m=G(v),c=u.getSnapshot().signal;if(!O(c)){let l=X(void 0,i());return S(m,l),l}S(m,V(void 0,i()));let d=i(),s=await n.execute(v,w),p=i();if(!s.ok){r.emit({type:"fetch-error",key:m,code:s.code,latencyMs:p-d});let l=s.problem.category==="auth"?B(s.problem,void 0,p):j(s.problem,void 0,p);return S(m,l),l}r.emit({type:"fetch-success",key:m,latencyMs:p-d});let C={key:m,data:s.data.data,versionToken:s.data.versionToken??s.versionToken,status:c==="restricted"?"warm":"fresh",tags:[],storedAt:p,...s.data.pageInfo===void 0?{}:{pageInfo:s.data.pageInfo}},o=x(C,p);return S(m,o),o},getState(v){return T.get(G(v))??L()},subscribe(v,w){let m=G(v),c=h.get(m);if(c===void 0)c=new Set,h.set(m,c);return c.add(w),()=>c?.delete(w)},async invalidateTags(){return[]},async refresh(v){let w=T.get(v)??L();return S(v,w),w},async emitConflict(v,w){let m=K(void 0,w,i());return S(v,m),m}},...{async mutate(v,w={}){E+=1;let m=`${i()}-${E}`;if(!O(u.getSnapshot().signal))return{id:m,status:"disabled-offline",problem:he()};let c=await n.execute({operation:v.operation,input:v.input,tags:v.tags},w.ctx??a.contextProvider?.());if(c.ok)return{id:m,status:"done",data:c.data.data};if(c.problem.category==="auth")return{id:m,status:"paused-auth",problem:c.problem};return{id:m,status:"failed",problem:c.problem}},async drain(){},async resume(){},dispose(){}},maintenance:{collectGarbage:async()=>0,revalidateStale:async()=>0},events:r,resolvers:y,localOffline:M}}function he(){return{type:"about:blank",title:"Offline mutation queue disabled",status:409,code:"OFFLINE_QUEUE_DISABLED",category:"conflict",severity:"warning",retryable:!1,detail:"localOffline=online-only does not cache local data or queue offline mutations."}}import{createQueryKey as H}from"@lssm/lib.contracts-spec/query/key";function re(){let e=new Map;return{run(t,n){let a=e.get(t);if(a!==void 0)return a;let i=n().finally(()=>{e.delete(t)});return e.set(t,i),i},size(){return e.size}}}var Ee=0;function se(e){let{transport:t}=e,n=e.storage??_(),a=e.reachability??R("online"),i=e.now??(()=>Date.now()),u=e.defaultStaleTimeMs??Ee,r=e.cacheStore??U(n),y=e.events??A(),M=re(),T=new Map,h=new Map;function E(c,d){T.set(c,d);let s=h.get(c);if(s===void 0)return;for(let p of s)p(d)}async function S(c,d){let s=H(c),p=a.getSnapshot().signal,C=await r.read(s);if(!O(p)){let P=X(C,i());return E(s,P),P}if(C!==void 0&&!Q(C,i())){y.emit({type:"cache-hit",key:s});let P=x(C,i());return E(s,P),P}y.emit({type:"cache-miss",key:s}),E(s,V(C,i()));let o=i(),l=await M.run(s,()=>t.execute(c,d)),f=i();if(!l.ok){y.emit({type:"fetch-error",key:s,code:l.code,latencyMs:f-o});let P=l.problem.category==="auth"?B(l.problem,C,f):j(l.problem,C,f);return E(s,P),P}y.emit({type:"fetch-success",key:s,latencyMs:f-o});let g=c.cache?.staleTimeMs??u,k=c.cache?.cacheTimeMs,b={key:s,data:l.data.data,versionToken:l.data.versionToken??l.versionToken,status:p==="restricted"?"warm":"fresh",tags:c.tags??[],storedAt:f,staleAt:f+g,envelope:c,...k===void 0?{}:{expiresAt:f+k},...l.data.pageInfo===void 0?{}:{pageInfo:l.data.pageInfo}};await r.write(b);let J=x(b,f);return E(s,J),J}async function D(c){let d=await r.read(c),s=d===void 0?L():x(d,i());return E(c,s),s}async function q(c,d){let s=await r.read(c),p=K(s,d,i());return E(c,p),p}function v(c){return T.get(H(c))??L()}function w(c,d){let s=H(c),p=h.get(s);if(p===void 0)p=new Set,h.set(s,p);return p.add(d),()=>{h.get(s)?.delete(d)}}async function m(c){let d=await r.invalidate(c,i());for(let s of d){let p=await r.read(s);if(p!==void 0)E(s,x(p,i()))}return d}return{query:S,getState:v,subscribe:w,invalidateTags:m,refresh:D,emitConflict:q}}function gt(e){Se(e);let t=e.advanced??{},n=I({defaultMode:e.localOffline,env:e.env,flag:e.flag});if(we(n.mode,t),n.mode==="online-only")return ie(e,n);let a=t.now??(()=>Date.now()),i=t.storage??_(),u=t.events??A(),r=t.cacheStore??U(i),y=t.reachability??R("online"),M=t.resolvers??F(),T=oe(i),h=se({...t,transport:e.transport,now:a,storage:i,events:u,cacheStore:r,reachability:y}),E=ae({transport:e.transport,cache:r,queue:T,reachability:y,resolvers:M,events:u,now:a,maxAttempts:t.maxAttempts,queryEngine:h,contextProvider:t.contextProvider,refreshAuth:t.refreshAuth,schedule:t.schedule,backoff:t.backoff}),S=ee({cache:r,events:u,now:a,revalidate:(D)=>h.query(D)});return{...h,...E,maintenance:S,events:u,resolvers:M,localOffline:n}}function we(e,t){if(e==="offline-capable"){if(t.storage===void 0)throw Error("localOffline=offline-capable requires advanced.storage; pass a durable StoragePort or explicit platform storage adapter.");return}if(t.storage!==void 0)throw Error(`localOffline=${e} rejects advanced.storage; this mode must not use durable local persistence.`);if(t.cacheStore!==void 0)throw Error(`localOffline=${e} rejects advanced.cacheStore; this mode owns cache semantics.`)}function Se(e){let t=e;if(t.storage!==void 0)throw Error("Use advanced.storage instead of top-level storage.");if(t.cacheStore!==void 0)throw Error("Use advanced.cacheStore instead of top-level cacheStore.")}export{gt as createDataEngine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-flight request de-duplication. Concurrent calls sharing a key share one
|
|
3
|
+
* underlying promise, so identical reads collapse to a single transport call.
|
|
4
|
+
*/
|
|
5
|
+
export interface InflightRegistry {
|
|
6
|
+
run<TValue>(key: string, factory: () => Promise<TValue>): Promise<TValue>;
|
|
7
|
+
size(): number;
|
|
8
|
+
}
|
|
9
|
+
export declare function createInflightRegistry(): InflightRegistry;
|