@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.
Files changed (227) hide show
  1. package/README.md +110 -0
  2. package/dist/approval.d.ts +13 -0
  3. package/dist/approval.js +2 -0
  4. package/dist/approval.test.d.ts +1 -0
  5. package/dist/browser/approval.js +1 -0
  6. package/dist/browser/collaboration.js +0 -0
  7. package/dist/browser/contracts-runtime-core.feature.js +1 -0
  8. package/dist/browser/engine/backoff.js +1 -0
  9. package/dist/browser/engine/cache-store.js +1 -0
  10. package/dist/browser/engine/conflict-registry.js +1 -0
  11. package/dist/browser/engine/conflict-resolution.js +1 -0
  12. package/dist/browser/engine/data-engine-host.js +1 -0
  13. package/dist/browser/engine/data-engine-test-helpers.js +1 -0
  14. package/dist/browser/engine/data-engine.js +1 -0
  15. package/dist/browser/engine/dedup.js +1 -0
  16. package/dist/browser/engine/events.js +1 -0
  17. package/dist/browser/engine/index.js +1 -0
  18. package/dist/browser/engine/maintenance.js +1 -0
  19. package/dist/browser/engine/mutation-engine.js +1 -0
  20. package/dist/browser/engine/mutation-queue.js +1 -0
  21. package/dist/browser/engine/mutation-types.js +0 -0
  22. package/dist/browser/engine/no-cache-store.js +1 -0
  23. package/dist/browser/engine/online-only-data-engine.js +1 -0
  24. package/dist/browser/engine/online-only-mutation-engine.js +1 -0
  25. package/dist/browser/engine/query-engine.js +1 -0
  26. package/dist/browser/engine/state.js +1 -0
  27. package/dist/browser/engine/types.js +0 -0
  28. package/dist/browser/forms-platform-csv-columns.js +1 -0
  29. package/dist/browser/forms-platform-csv.js +2 -0
  30. package/dist/browser/forms-platform-management.js +1 -0
  31. package/dist/browser/forms-platform-navigation.js +1 -0
  32. package/dist/browser/forms-platform-submission.js +1 -0
  33. package/dist/browser/forms-platform-types.js +1 -0
  34. package/dist/browser/forms-platform-utils.js +1 -0
  35. package/dist/browser/forms-platform.js +2 -0
  36. package/dist/browser/index.js +3 -0
  37. package/dist/browser/local-offline.js +1 -0
  38. package/dist/browser/observability.js +1 -0
  39. package/dist/browser/reachability.js +1 -0
  40. package/dist/browser/redaction.js +1 -0
  41. package/dist/browser/storage/indexeddb.web.js +1 -0
  42. package/dist/browser/storage/keyed-store.js +1 -0
  43. package/dist/browser/storage/memory.js +1 -0
  44. package/dist/browser/storage/port.js +1 -0
  45. package/dist/browser/transport.js +2 -0
  46. package/dist/collaboration.d.ts +57 -0
  47. package/dist/collaboration.js +1 -0
  48. package/dist/contracts-runtime-core.feature.d.ts +1 -0
  49. package/dist/contracts-runtime-core.feature.js +2 -0
  50. package/dist/engine/backoff.d.ts +16 -0
  51. package/dist/engine/backoff.js +2 -0
  52. package/dist/engine/backoff.test.d.ts +1 -0
  53. package/dist/engine/cache-store.d.ts +19 -0
  54. package/dist/engine/cache-store.js +2 -0
  55. package/dist/engine/conflict-registry.d.ts +10 -0
  56. package/dist/engine/conflict-registry.js +2 -0
  57. package/dist/engine/conflict-registry.test.d.ts +1 -0
  58. package/dist/engine/conflict-resolution.d.ts +24 -0
  59. package/dist/engine/conflict-resolution.js +2 -0
  60. package/dist/engine/conflict-resolution.test.d.ts +1 -0
  61. package/dist/engine/data-engine-host.d.ts +30 -0
  62. package/dist/engine/data-engine-host.js +2 -0
  63. package/dist/engine/data-engine-local-offline.test.d.ts +1 -0
  64. package/dist/engine/data-engine-test-helpers.d.ts +30 -0
  65. package/dist/engine/data-engine-test-helpers.js +2 -0
  66. package/dist/engine/data-engine.d.ts +46 -0
  67. package/dist/engine/data-engine.js +2 -0
  68. package/dist/engine/data-engine.test.d.ts +1 -0
  69. package/dist/engine/dedup.d.ts +9 -0
  70. package/dist/engine/dedup.js +2 -0
  71. package/dist/engine/events.d.ts +76 -0
  72. package/dist/engine/events.economic-evidence.test.d.ts +1 -0
  73. package/dist/engine/events.js +2 -0
  74. package/dist/engine/index.d.ts +17 -0
  75. package/dist/engine/index.js +2 -0
  76. package/dist/engine/maintenance.d.ts +18 -0
  77. package/dist/engine/maintenance.js +2 -0
  78. package/dist/engine/maintenance.test.d.ts +1 -0
  79. package/dist/engine/mutation-engine.d.ts +2 -0
  80. package/dist/engine/mutation-engine.js +2 -0
  81. package/dist/engine/mutation-queue.d.ts +16 -0
  82. package/dist/engine/mutation-queue.js +2 -0
  83. package/dist/engine/mutation-queue.test.d.ts +1 -0
  84. package/dist/engine/mutation-types.d.ts +118 -0
  85. package/dist/engine/mutation-types.js +1 -0
  86. package/dist/engine/no-cache-store.d.ts +3 -0
  87. package/dist/engine/no-cache-store.js +2 -0
  88. package/dist/engine/online-only-data-engine.d.ts +9 -0
  89. package/dist/engine/online-only-data-engine.js +2 -0
  90. package/dist/engine/online-only-mutation-engine.d.ts +3 -0
  91. package/dist/engine/online-only-mutation-engine.js +2 -0
  92. package/dist/engine/query-engine.d.ts +7 -0
  93. package/dist/engine/query-engine.js +2 -0
  94. package/dist/engine/query-engine.test.d.ts +1 -0
  95. package/dist/engine/state.d.ts +37 -0
  96. package/dist/engine/state.js +2 -0
  97. package/dist/engine/types.d.ts +70 -0
  98. package/dist/engine/types.js +1 -0
  99. package/dist/forms-platform-choice.test.d.ts +1 -0
  100. package/dist/forms-platform-csv-columns.d.ts +7 -0
  101. package/dist/forms-platform-csv-columns.js +2 -0
  102. package/dist/forms-platform-csv.d.ts +12 -0
  103. package/dist/forms-platform-csv.js +3 -0
  104. package/dist/forms-platform-management.d.ts +12 -0
  105. package/dist/forms-platform-management.js +2 -0
  106. package/dist/forms-platform-navigation.d.ts +2 -0
  107. package/dist/forms-platform-navigation.js +2 -0
  108. package/dist/forms-platform-submission.d.ts +7 -0
  109. package/dist/forms-platform-submission.js +2 -0
  110. package/dist/forms-platform-types.d.ts +51 -0
  111. package/dist/forms-platform-types.js +2 -0
  112. package/dist/forms-platform-utils.d.ts +6 -0
  113. package/dist/forms-platform-utils.js +2 -0
  114. package/dist/forms-platform-utils.test.d.ts +1 -0
  115. package/dist/forms-platform.d.ts +19 -0
  116. package/dist/forms-platform.js +3 -0
  117. package/dist/forms-platform.test.d.ts +1 -0
  118. package/dist/index.d.ts +25 -0
  119. package/dist/index.js +4 -0
  120. package/dist/local-offline.d.ts +17 -0
  121. package/dist/local-offline.js +2 -0
  122. package/dist/local-offline.test.d.ts +1 -0
  123. package/dist/native/approval.js +130 -0
  124. package/dist/native/collaboration.js +0 -0
  125. package/dist/native/contracts-runtime-core.feature.js +17 -0
  126. package/dist/native/engine/backoff.js +16 -0
  127. package/dist/native/engine/cache-store.js +49 -0
  128. package/dist/native/engine/conflict-registry.js +51 -0
  129. package/dist/native/engine/conflict-resolution.js +81 -0
  130. package/dist/native/engine/data-engine-host.js +1119 -0
  131. package/dist/native/engine/data-engine-test-helpers.js +117 -0
  132. package/dist/native/engine/data-engine.js +1106 -0
  133. package/dist/native/engine/dedup.js +22 -0
  134. package/dist/native/engine/events.js +19 -0
  135. package/dist/native/engine/index.js +1189 -0
  136. package/dist/native/engine/maintenance.js +36 -0
  137. package/dist/native/engine/mutation-engine.js +360 -0
  138. package/dist/native/engine/mutation-queue.js +26 -0
  139. package/dist/native/engine/mutation-types.js +0 -0
  140. package/dist/native/engine/no-cache-store.js +15 -0
  141. package/dist/native/engine/online-only-data-engine.js +422 -0
  142. package/dist/native/engine/online-only-mutation-engine.js +70 -0
  143. package/dist/native/engine/query-engine.js +360 -0
  144. package/dist/native/engine/state.js +116 -0
  145. package/dist/native/engine/types.js +0 -0
  146. package/dist/native/forms-platform-csv-columns.js +31 -0
  147. package/dist/native/forms-platform-csv.js +150 -0
  148. package/dist/native/forms-platform-management.js +179 -0
  149. package/dist/native/forms-platform-navigation.js +27 -0
  150. package/dist/native/forms-platform-submission.js +207 -0
  151. package/dist/native/forms-platform-types.js +12 -0
  152. package/dist/native/forms-platform-utils.js +69 -0
  153. package/dist/native/forms-platform.js +429 -0
  154. package/dist/native/index.js +1879 -0
  155. package/dist/native/local-offline.js +81 -0
  156. package/dist/native/observability.js +182 -0
  157. package/dist/native/reachability.js +28 -0
  158. package/dist/native/redaction.js +44 -0
  159. package/dist/native/storage/async-storage.native.js +91 -0
  160. package/dist/native/storage/keyed-store.js +74 -0
  161. package/dist/native/storage/memory.js +26 -0
  162. package/dist/native/storage/port.js +16 -0
  163. package/dist/native/transport.js +27 -0
  164. package/dist/node/approval.js +1 -0
  165. package/dist/node/collaboration.js +0 -0
  166. package/dist/node/contracts-runtime-core.feature.js +1 -0
  167. package/dist/node/engine/backoff.js +1 -0
  168. package/dist/node/engine/cache-store.js +1 -0
  169. package/dist/node/engine/conflict-registry.js +1 -0
  170. package/dist/node/engine/conflict-resolution.js +1 -0
  171. package/dist/node/engine/data-engine-host.js +1 -0
  172. package/dist/node/engine/data-engine-test-helpers.js +1 -0
  173. package/dist/node/engine/data-engine.js +1 -0
  174. package/dist/node/engine/dedup.js +1 -0
  175. package/dist/node/engine/events.js +1 -0
  176. package/dist/node/engine/index.js +1 -0
  177. package/dist/node/engine/maintenance.js +1 -0
  178. package/dist/node/engine/mutation-engine.js +1 -0
  179. package/dist/node/engine/mutation-queue.js +1 -0
  180. package/dist/node/engine/mutation-types.js +0 -0
  181. package/dist/node/engine/no-cache-store.js +1 -0
  182. package/dist/node/engine/online-only-data-engine.js +1 -0
  183. package/dist/node/engine/online-only-mutation-engine.js +1 -0
  184. package/dist/node/engine/query-engine.js +1 -0
  185. package/dist/node/engine/state.js +1 -0
  186. package/dist/node/engine/types.js +0 -0
  187. package/dist/node/forms-platform-csv-columns.js +1 -0
  188. package/dist/node/forms-platform-csv.js +2 -0
  189. package/dist/node/forms-platform-management.js +1 -0
  190. package/dist/node/forms-platform-navigation.js +1 -0
  191. package/dist/node/forms-platform-submission.js +1 -0
  192. package/dist/node/forms-platform-types.js +1 -0
  193. package/dist/node/forms-platform-utils.js +1 -0
  194. package/dist/node/forms-platform.js +2 -0
  195. package/dist/node/index.js +3 -0
  196. package/dist/node/local-offline.js +1 -0
  197. package/dist/node/observability.js +1 -0
  198. package/dist/node/reachability.js +1 -0
  199. package/dist/node/redaction.js +1 -0
  200. package/dist/node/storage/keyed-store.js +1 -0
  201. package/dist/node/storage/memory.js +1 -0
  202. package/dist/node/storage/port.js +1 -0
  203. package/dist/node/transport.js +2 -0
  204. package/dist/observability.d.ts +34 -0
  205. package/dist/observability.js +2 -0
  206. package/dist/observability.test.d.ts +1 -0
  207. package/dist/reachability.d.ts +63 -0
  208. package/dist/reachability.js +2 -0
  209. package/dist/reachability.test.d.ts +1 -0
  210. package/dist/redaction.d.ts +34 -0
  211. package/dist/redaction.js +2 -0
  212. package/dist/redaction.test.d.ts +1 -0
  213. package/dist/storage/async-storage.native.d.ts +23 -0
  214. package/dist/storage/async-storage.native.test.d.ts +1 -0
  215. package/dist/storage/indexeddb.web.d.ts +13 -0
  216. package/dist/storage/indexeddb.web.js +2 -0
  217. package/dist/storage/keyed-store.d.ts +25 -0
  218. package/dist/storage/keyed-store.js +2 -0
  219. package/dist/storage/keyed-store.test.d.ts +1 -0
  220. package/dist/storage/memory.d.ts +11 -0
  221. package/dist/storage/memory.js +2 -0
  222. package/dist/storage/memory.test.d.ts +1 -0
  223. package/dist/storage/port.d.ts +34 -0
  224. package/dist/storage/port.js +2 -0
  225. package/dist/transport.d.ts +53 -0
  226. package/dist/transport.js +3 -0
  227. package/package.json +219 -0
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ class a extends Error{code;constructor(o,e){super(e);this.code=o;this.name="FormsPlatformError"}}import{compileFormDocument as l,validateFormDocument as p}from"@lssm/lib.contracts-spec/forms";function f(o,e){let t=o?.owner.product===e.product&&o.owner.tenantRef===e.tenantRef&&o.owner.workspaceRef===e.workspaceRef&&o.owner.ownerRef===e.ownerRef;if(!o||!t)throw new a("FORM_NOT_FOUND","Form definition was not found in the caller scope");return o}function d(o){let e=p(o);if(e.length)throw new a("INVALID_FORM_DOCUMENT",e.map((t)=>t.message).join("; "));l(o)}function w(o,e){if(e==="none")return!0;if(e==="either")return o!=="anonymous";if(e==="both")return o==="both";return o===e||o==="both"}function R(o,e){if(w(e,o.verification.requirement))return"completed";return o.verification.unverifiedDisposition==="pending-hidden"?"pending-verification":"completed-unverified"}function h(o,e){let t=new Set(o.content.enabledLocales.map((r)=>r.toLowerCase()));for(let r of e){if(!r)continue;let n=r.trim().toLowerCase(),i=o.content.enabledLocales.find((c)=>c.toLowerCase()===n);if(t.has(n)&&i)return i;let s=n.split("-")[0],m=o.content.enabledLocales.find((c)=>c.toLowerCase().split("-")[0]===s);if(m)return m}return o.content.defaultLocale}function P(o){let e=async(t,r)=>o.emit?.(t,r);return{async create(t,r){d(r);let n=o.now(),i={id:o.id("definition"),owner:t,status:"draft",draft:r,draftVersion:1,createdAt:n,updatedAt:n};return await o.store.insertDefinition(i),await e("forms.definition.created",{definitionId:i.id,occurredAt:n}),i},async saveDraft(t,r,n,i){d(i);let s=f(await o.store.getDefinition(t,r),t);if(s.status==="archived")throw new a("FORM_ARCHIVED","Archived forms cannot be edited");let m={...s,draft:i,draftVersion:n+1,updatedAt:o.now()};if(!await o.store.updateDefinition(m,n))throw new a("DRAFT_CONFLICT","The form draft changed; reload before saving");return await e("forms.draft.saved",{definitionId:r,occurredAt:m.updatedAt}),m},async publish(t,r,n){return o.store.transaction(async(i)=>{let s=f(await i.getDefinition(t,r),t);if(s.draftVersion!==n)throw new a("DRAFT_CONFLICT","The form draft changed before publish");d(s.draft);let m=s.publishedRevisionId?await i.getRevision(s.publishedRevisionId):void 0,c=o.now(),u={id:o.id("revision"),definitionId:r,revision:(m?.revision??0)+1,document:structuredClone(s.draft),digest:await o.digest(JSON.stringify(s.draft)),publishedAt:c},F={...s,status:"published",publishedRevisionId:u.id,publicToken:s.publicToken??o.publicToken(),updatedAt:c};if(await i.insertRevision(u),!await i.updateDefinition(F,n))throw new a("DRAFT_CONFLICT","The form changed during publish");return await e("forms.definition.published",{definitionId:r,revisionId:u.id,occurredAt:c}),{definition:F,revision:u}})},async close(t,r){let n=f(await o.store.getDefinition(t,r),t);if(n.status!=="published")throw new a("FORM_NOT_PUBLISHED","Only a published form can be closed");let i={...n,status:"closed",updatedAt:o.now()};return await o.store.updateDefinition(i),await e("forms.definition.closed",{definitionId:r,occurredAt:i.updatedAt}),i},async archive(t,r){let n=f(await o.store.getDefinition(t,r),t);if(n.status==="archived")return n;let i={...n,status:"archived",updatedAt:o.now()};return await o.store.updateDefinition(i),await e("forms.definition.archived",{definitionId:r,occurredAt:i.updatedAt}),i}}}export{P as createFormsManagementService};
@@ -0,0 +1,2 @@
1
+ import { type FormAnswerValue, type FormDocument } from '@lssm/lib.contracts-spec/forms';
2
+ export declare function resolveNextFormPage(document: FormDocument, currentPageId: string, answers: Readonly<Record<string, FormAnswerValue>>): string | undefined;
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{evalPredicate as s}from"@lssm/lib.contracts-spec/forms";class n extends Error{code;constructor(e,i){super(i);this.code=e;this.name="FormsPlatformError"}}function p(e,i,t){let o=e.pages.findIndex((r)=>r.id===i);if(o<0)throw new n("PAGE_NOT_FOUND","Current form page does not exist");return e.pages[o]?.nextPageRules?.find((r)=>s(t,r.when))?.nextPageId??e.pages[o]?.defaultNextPageId??e.pages[o+1]?.id}export{p as resolveNextFormPage};
@@ -0,0 +1,7 @@
1
+ import { type FormAnswerValue, type FormDocument, type FormResponse } from '@lssm/lib.contracts-spec/forms';
2
+ import { type FormsPlatformDependencies, type PublicFormProjection, type SubmitFormResponseInput } from './forms-platform-types.js';
3
+ export declare function validateCanonicalFormAnswers(document: FormDocument, answers: Readonly<Record<string, FormAnswerValue>>): void;
4
+ export declare function createFormsSubmissionService(dependencies: FormsPlatformDependencies): {
5
+ getPublic(publicToken: string, localeCandidates: readonly (string | undefined)[]): Promise<PublicFormProjection>;
6
+ submit(input: SubmitFormResponseInput): Promise<FormResponse>;
7
+ };
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{buildZodWithRelations as b,compileFormDocument as y,FORM_AI_FULL_DATA_CONSENT_NOTICE_VERSION as I}from"@lssm/lib.contracts-spec/forms";class s extends Error{code;constructor(e,o){super(o);this.code=e;this.name="FormsPlatformError"}}import{compileFormDocument as R,validateFormDocument as g}from"@lssm/lib.contracts-spec/forms";function N(e,o){let r=e?.owner.product===o.product&&e.owner.tenantRef===o.tenantRef&&e.owner.workspaceRef===o.workspaceRef&&e.owner.ownerRef===o.ownerRef;if(!e||!r)throw new s("FORM_NOT_FOUND","Form definition was not found in the caller scope");return e}function _(e){let o=g(e);if(o.length)throw new s("INVALID_FORM_DOCUMENT",o.map((r)=>r.message).join("; "));R(e)}function f(e,o){if(o==="none")return!0;if(o==="either")return e!=="anonymous";if(o==="both")return e==="both";return e===o||e==="both"}function F(e,o){if(f(o,e.verification.requirement))return"completed";return e.verification.unverifiedDisposition==="pending-hidden"?"pending-verification":"completed-unverified"}function l(e,o){let r=new Set(e.content.enabledLocales.map((n)=>n.toLowerCase()));for(let n of o){if(!n)continue;let t=n.trim().toLowerCase(),m=e.content.enabledLocales.find((c)=>c.toLowerCase()===t);if(r.has(t)&&m)return m;let a=t.split("-")[0],i=e.content.enabledLocales.find((c)=>c.toLowerCase().split("-")[0]===a);if(i)return i}return e.content.defaultLocale}function D(e,o){let r=new Map(e.questions.map((i)=>[i.id,i])),n=new Set(Object.keys(e.formSpec?.modelFields??{})),t=Object.keys(o).filter((i)=>!r.has(i)&&!n.has(i));if(t.length)throw new s("INVALID_RESPONSE",`Unknown question IDs: ${t.join(", ")}`);for(let i of e.questions){if(!["single-choice","multi-select","dropdown"].includes(i.kind))continue;let c=o[i.id];if(c==null)continue;let d=new Set(i.options?.map((u)=>u.value));if(!(i.kind==="multi-select"?Array.isArray(c)&&c.every((u)=>typeof u==="string"&&d.has(u)):typeof c==="string"&&d.has(c)))throw new s("INVALID_RESPONSE",`Answer for ${i.id} is not a declared option`)}let m=Object.fromEntries(Object.entries(o).map(([i,c])=>[r.get(i)?.name??i,c])),a=b(y(e)).safeParse(m);if(!a.success)throw new s("INVALID_RESPONSE",`Response does not match the published form: ${String(a.error)}`)}function A(e){let{kind:o,version:r,id:n,titleKey:t,descriptionKey:m,content:a,questions:i,pages:c,verification:d,formSpec:p,ai:u,beforeDestination:w,afterDestination:v}=e;return structuredClone({kind:o,version:r,id:n,titleKey:t,descriptionKey:m,content:a,questions:i,pages:c,verification:d,formSpec:p,ai:{mode:u.mode,dataMode:u.dataMode,...u.dataMode==="full-with-consent"?{consentNoticeVersion:I}:{}},beforeDestination:w,afterDestination:v})}async function h(e,o,r,n){if(!o)return"anonymous";if(!e.resolveAssurance)throw new s("VERIFICATION_UNAVAILABLE","Verification evidence cannot be resolved");return e.resolveAssurance(o,r,n)}function V(e){return{async getPublic(o,r){let n=await e.store.getDefinitionByPublicToken(o);if(!n||n.status!=="published"||!n.publishedRevisionId)throw new s("FORM_NOT_FOUND","Published form was not found");let t=await e.store.getRevision(n.publishedRevisionId,{publicToken:o});if(!t)throw new s("REVISION_NOT_FOUND","Published form revision was not found");return{definitionId:n.id,revisionId:t.id,revision:t.revision,document:A(t.document),locale:l(t.document,r)}},async submit(o){let r=await e.store.getDefinitionByPublicToken(o.publicToken);if(!r||r.status!=="published")throw new s("FORM_NOT_FOUND","Form is not accepting responses");let n=await e.store.getRevision(o.revisionId,{publicToken:o.publicToken});if(!n||n.definitionId!==r.id)throw new s("REVISION_MISMATCH","Response revision does not match the public session");D(n.document,o.answers);let t=await h(e,o.verificationEvidenceRef,r.id,o.publicToken);if(n.document.verification.timing==="before"&&!f(t,n.document.verification.requirement))throw new s("VERIFICATION_REQUIRED","Verification is required before submission");let m=e.now(),a={id:e.id("response"),definitionId:r.id,revisionId:n.id,locale:l(n.document,[o.locale]),status:F(n.document,t),authorAssurance:t,answers:structuredClone(o.answers),createdAt:m,updatedAt:m,submittedAt:m},i=await e.store.insertResponseIdempotently(a,o.publicToken,o.idempotencyKey);if(!i.inserted)return i.response;return await e.emit?.("forms.response.submitted",{definitionId:a.definitionId,revisionId:a.revisionId,responseId:a.id,occurredAt:m}),i.response}}}export{V as createFormsSubmissionService,D as validateCanonicalFormAnswers};
@@ -0,0 +1,51 @@
1
+ import type { FormAiPolicy, FormAnswerValue, FormAuthorAssurance, FormDefinition, FormDocument, FormOwnerScope, FormResponse, FormRevision } from '@lssm/lib.contracts-spec/forms';
2
+ import { FORM_AI_FULL_DATA_CONSENT_NOTICE_VERSION } from '@lssm/lib.contracts-spec/forms';
3
+ export declare class FormsPlatformError extends Error {
4
+ readonly code: string;
5
+ constructor(code: string, message: string);
6
+ }
7
+ export interface FormsPlatformStore {
8
+ transaction<T>(run: (store: FormsPlatformStore) => Promise<T>): Promise<T>;
9
+ insertDefinition(definition: FormDefinition): Promise<void>;
10
+ updateDefinition(definition: FormDefinition, expectedDraftVersion?: number): Promise<boolean>;
11
+ getDefinition(scope: FormOwnerScope, definitionId: string): Promise<FormDefinition | undefined>;
12
+ getDefinitionByPublicToken(publicToken: string): Promise<FormDefinition | undefined>;
13
+ insertRevision(revision: FormRevision): Promise<void>;
14
+ getRevision(revisionId: string, access?: {
15
+ readonly scope?: FormOwnerScope;
16
+ readonly publicToken?: string;
17
+ }): Promise<FormRevision | undefined>;
18
+ insertResponseIdempotently(response: FormResponse, publicToken: string, idempotencyKey: string): Promise<{
19
+ response: FormResponse;
20
+ inserted: boolean;
21
+ }>;
22
+ }
23
+ export interface FormsPlatformDependencies {
24
+ store: FormsPlatformStore;
25
+ now: () => string;
26
+ id: (kind: 'definition' | 'revision' | 'response') => string;
27
+ publicToken: () => string;
28
+ digest: (value: string) => Promise<string> | string;
29
+ resolveAssurance?: (verificationEvidenceRef: string, definitionId: string, publicToken: string) => Promise<FormAuthorAssurance>;
30
+ emit?: (key: string, payload: Readonly<Record<string, string>>) => Promise<void> | void;
31
+ }
32
+ export type PublicFormDocument = Omit<Pick<FormDocument, 'kind' | 'version' | 'id' | 'titleKey' | 'descriptionKey' | 'content' | 'questions' | 'pages' | 'verification' | 'formSpec' | 'beforeDestination' | 'afterDestination'>, 'ai'> & {
33
+ readonly ai: Pick<FormAiPolicy, 'mode' | 'dataMode'> & {
34
+ readonly consentNoticeVersion?: typeof FORM_AI_FULL_DATA_CONSENT_NOTICE_VERSION;
35
+ };
36
+ };
37
+ export interface PublicFormProjection {
38
+ definitionId: string;
39
+ revisionId: string;
40
+ revision: number;
41
+ document: PublicFormDocument;
42
+ locale: string;
43
+ }
44
+ export interface SubmitFormResponseInput {
45
+ publicToken: string;
46
+ revisionId: string;
47
+ locale: string;
48
+ answers: Readonly<Record<string, FormAnswerValue>>;
49
+ idempotencyKey: string;
50
+ verificationEvidenceRef?: string;
51
+ }
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ class i extends Error{code;constructor(o,e){super(e);this.code=o;this.name="FormsPlatformError"}}export{i as FormsPlatformError};
@@ -0,0 +1,6 @@
1
+ import { type FormAuthorAssurance, type FormDefinition, type FormDocument, type FormOwnerScope, type FormResponseStatus } from '@lssm/lib.contracts-spec/forms';
2
+ export declare function requireFormDefinition(definition: FormDefinition | undefined, scope: FormOwnerScope): FormDefinition;
3
+ export declare function validateRuntimeFormDocument(document: FormDocument): void;
4
+ export declare function formAssuranceMeetsRequirement(assurance: FormAuthorAssurance, requirement: FormDocument['verification']['requirement']): boolean;
5
+ export declare function resolveFormResponseStatus(document: FormDocument, assurance: FormAuthorAssurance): FormResponseStatus;
6
+ export declare function resolveFormLocale(document: FormDocument, candidates: readonly (string | undefined)[]): string;
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ import{compileFormDocument as f,validateFormDocument as u}from"@lssm/lib.contracts-spec/forms";class i extends Error{code;constructor(e,o){super(o);this.code=e;this.name="FormsPlatformError"}}function g(e,o){let r=e?.owner.product===o.product&&e.owner.tenantRef===o.tenantRef&&e.owner.workspaceRef===o.workspaceRef&&e.owner.ownerRef===o.ownerRef;if(!e||!r)throw new i("FORM_NOT_FOUND","Form definition was not found in the caller scope");return e}function D(e){let o=u(e);if(o.length)throw new i("INVALID_FORM_DOCUMENT",o.map((r)=>r.message).join("; "));f(e)}function p(e,o){if(o==="none")return!0;if(o==="either")return e!=="anonymous";if(o==="both")return e==="both";return e===o||e==="both"}function v(e,o){if(p(o,e.verification.requirement))return"completed";return e.verification.unverifiedDisposition==="pending-hidden"?"pending-verification":"completed-unverified"}function R(e,o){let r=new Set(e.content.enabledLocales.map((n)=>n.toLowerCase()));for(let n of o){if(!n)continue;let t=n.trim().toLowerCase(),m=e.content.enabledLocales.find((s)=>s.toLowerCase()===t);if(r.has(t)&&m)return m;let a=t.split("-")[0],c=e.content.enabledLocales.find((s)=>s.toLowerCase().split("-")[0]===a);if(c)return c}return e.content.defaultLocale}export{p as formAssuranceMeetsRequirement,g as requireFormDefinition,R as resolveFormLocale,v as resolveFormResponseStatus,D as validateRuntimeFormDocument};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,19 @@
1
+ import type { FormsPlatformDependencies } from './forms-platform-types.js';
2
+ export type { FormCsvExportOptions } from './forms-platform-csv.js';
3
+ export { serializeFormResponsesCsv, streamFormResponsesCsv, } from './forms-platform-csv.js';
4
+ export { resolveNextFormPage } from './forms-platform-navigation.js';
5
+ export type { FormsPlatformDependencies, FormsPlatformStore, PublicFormDocument, PublicFormProjection, SubmitFormResponseInput, } from './forms-platform-types.js';
6
+ export { FormsPlatformError } from './forms-platform-types.js';
7
+ export { resolveFormLocale } from './forms-platform-utils.js';
8
+ export declare function createFormsPlatformService(dependencies: FormsPlatformDependencies): {
9
+ getPublic(publicToken: string, localeCandidates: readonly (string | undefined)[]): Promise<import("./forms-platform-types").PublicFormProjection>;
10
+ submit(input: import("./forms-platform-types").SubmitFormResponseInput): Promise<import("@lssm/lib.contracts-spec").FormResponse>;
11
+ create(scope: import("@lssm/lib.contracts-spec").FormOwnerScope, document: import("@lssm/lib.contracts-spec").FormDocument): Promise<import("@lssm/lib.contracts-spec").FormDefinition>;
12
+ saveDraft(scope: import("@lssm/lib.contracts-spec").FormOwnerScope, definitionId: string, expectedVersion: number, document: import("@lssm/lib.contracts-spec").FormDocument): Promise<import("@lssm/lib.contracts-spec").FormDefinition>;
13
+ publish(scope: import("@lssm/lib.contracts-spec").FormOwnerScope, definitionId: string, expectedVersion: number): Promise<{
14
+ definition: import("@lssm/lib.contracts-spec").FormDefinition;
15
+ revision: import("@lssm/lib.contracts-spec").FormRevision;
16
+ }>;
17
+ close(scope: import("@lssm/lib.contracts-spec").FormOwnerScope, definitionId: string): Promise<import("@lssm/lib.contracts-spec").FormDefinition>;
18
+ archive(scope: import("@lssm/lib.contracts-spec").FormOwnerScope, definitionId: string): Promise<import("@lssm/lib.contracts-spec").FormDefinition>;
19
+ };
@@ -0,0 +1,3 @@
1
+ // @bun
2
+ class m extends Error{code;constructor(e,o){super(o);this.code=e;this.name="FormsPlatformError"}}import{compileFormDocument as P,validateFormDocument as S}from"@lssm/lib.contracts-spec/forms";function p(e,o){let t=e?.owner.product===o.product&&e.owner.tenantRef===o.tenantRef&&e.owner.workspaceRef===o.workspaceRef&&e.owner.ownerRef===o.ownerRef;if(!e||!t)throw new m("FORM_NOT_FOUND","Form definition was not found in the caller scope");return e}function F(e){let o=S(e);if(o.length)throw new m("INVALID_FORM_DOCUMENT",o.map((t)=>t.message).join("; "));P(e)}function w(e,o){if(o==="none")return!0;if(o==="either")return e!=="anonymous";if(o==="both")return e==="both";return e===o||e==="both"}function b(e,o){if(w(o,e.verification.requirement))return"completed";return e.verification.unverifiedDisposition==="pending-hidden"?"pending-verification":"completed-unverified"}function l(e,o){let t=new Set(e.content.enabledLocales.map((r)=>r.toLowerCase()));for(let r of o){if(!r)continue;let n=r.trim().toLowerCase(),i=e.content.enabledLocales.find((c)=>c.toLowerCase()===n);if(t.has(n)&&i)return i;let s=n.split("-")[0],a=e.content.enabledLocales.find((c)=>c.toLowerCase().split("-")[0]===s);if(a)return a}return e.content.defaultLocale}function y(e){let o=async(t,r)=>e.emit?.(t,r);return{async create(t,r){F(r);let n=e.now(),i={id:e.id("definition"),owner:t,status:"draft",draft:r,draftVersion:1,createdAt:n,updatedAt:n};return await e.store.insertDefinition(i),await o("forms.definition.created",{definitionId:i.id,occurredAt:n}),i},async saveDraft(t,r,n,i){F(i);let s=p(await e.store.getDefinition(t,r),t);if(s.status==="archived")throw new m("FORM_ARCHIVED","Archived forms cannot be edited");let a={...s,draft:i,draftVersion:n+1,updatedAt:e.now()};if(!await e.store.updateDefinition(a,n))throw new m("DRAFT_CONFLICT","The form draft changed; reload before saving");return await o("forms.draft.saved",{definitionId:r,occurredAt:a.updatedAt}),a},async publish(t,r,n){return e.store.transaction(async(i)=>{let s=p(await i.getDefinition(t,r),t);if(s.draftVersion!==n)throw new m("DRAFT_CONFLICT","The form draft changed before publish");F(s.draft);let a=s.publishedRevisionId?await i.getRevision(s.publishedRevisionId):void 0,c=e.now(),u={id:e.id("revision"),definitionId:r,revision:(a?.revision??0)+1,document:structuredClone(s.draft),digest:await e.digest(JSON.stringify(s.draft)),publishedAt:c},d={...s,status:"published",publishedRevisionId:u.id,publicToken:s.publicToken??e.publicToken(),updatedAt:c};if(await i.insertRevision(u),!await i.updateDefinition(d,n))throw new m("DRAFT_CONFLICT","The form changed during publish");return await o("forms.definition.published",{definitionId:r,revisionId:u.id,occurredAt:c}),{definition:d,revision:u}})},async close(t,r){let n=p(await e.store.getDefinition(t,r),t);if(n.status!=="published")throw new m("FORM_NOT_PUBLISHED","Only a published form can be closed");let i={...n,status:"closed",updatedAt:e.now()};return await e.store.updateDefinition(i),await o("forms.definition.closed",{definitionId:r,occurredAt:i.updatedAt}),i},async archive(t,r){let n=p(await e.store.getDefinition(t,r),t);if(n.status==="archived")return n;let i={...n,status:"archived",updatedAt:e.now()};return await e.store.updateDefinition(i),await o("forms.definition.archived",{definitionId:r,occurredAt:i.updatedAt}),i}}}import{buildZodWithRelations as O,compileFormDocument as E,FORM_AI_FULL_DATA_CONSENT_NOTICE_VERSION as N}from"@lssm/lib.contracts-spec/forms";function C(e,o){let t=new Map(e.questions.map((a)=>[a.id,a])),r=new Set(Object.keys(e.formSpec?.modelFields??{})),n=Object.keys(o).filter((a)=>!t.has(a)&&!r.has(a));if(n.length)throw new m("INVALID_RESPONSE",`Unknown question IDs: ${n.join(", ")}`);for(let a of e.questions){if(!["single-choice","multi-select","dropdown"].includes(a.kind))continue;let c=o[a.id];if(c==null)continue;let u=new Set(a.options?.map((f)=>f.value));if(!(a.kind==="multi-select"?Array.isArray(c)&&c.every((f)=>typeof f==="string"&&u.has(f)):typeof c==="string"&&u.has(c)))throw new m("INVALID_RESPONSE",`Answer for ${a.id} is not a declared option`)}let i=Object.fromEntries(Object.entries(o).map(([a,c])=>[t.get(a)?.name??a,c])),s=O(E(e)).safeParse(i);if(!s.success)throw new m("INVALID_RESPONSE",`Response does not match the published form: ${String(s.error)}`)}function x(e){let{kind:o,version:t,id:r,titleKey:n,descriptionKey:i,content:s,questions:a,pages:c,verification:u,formSpec:d,ai:f,beforeDestination:A,afterDestination:I}=e;return structuredClone({kind:o,version:t,id:r,titleKey:n,descriptionKey:i,content:s,questions:a,pages:c,verification:u,formSpec:d,ai:{mode:f.mode,dataMode:f.dataMode,...f.dataMode==="full-with-consent"?{consentNoticeVersion:N}:{}},beforeDestination:A,afterDestination:I})}async function T(e,o,t,r){if(!o)return"anonymous";if(!e.resolveAssurance)throw new m("VERIFICATION_UNAVAILABLE","Verification evidence cannot be resolved");return e.resolveAssurance(o,t,r)}function R(e){return{async getPublic(o,t){let r=await e.store.getDefinitionByPublicToken(o);if(!r||r.status!=="published"||!r.publishedRevisionId)throw new m("FORM_NOT_FOUND","Published form was not found");let n=await e.store.getRevision(r.publishedRevisionId,{publicToken:o});if(!n)throw new m("REVISION_NOT_FOUND","Published form revision was not found");return{definitionId:r.id,revisionId:n.id,revision:n.revision,document:x(n.document),locale:l(n.document,t)}},async submit(o){let t=await e.store.getDefinitionByPublicToken(o.publicToken);if(!t||t.status!=="published")throw new m("FORM_NOT_FOUND","Form is not accepting responses");let r=await e.store.getRevision(o.revisionId,{publicToken:o.publicToken});if(!r||r.definitionId!==t.id)throw new m("REVISION_MISMATCH","Response revision does not match the public session");C(r.document,o.answers);let n=await T(e,o.verificationEvidenceRef,t.id,o.publicToken);if(r.document.verification.timing==="before"&&!w(n,r.document.verification.requirement))throw new m("VERIFICATION_REQUIRED","Verification is required before submission");let i=e.now(),s={id:e.id("response"),definitionId:t.id,revisionId:r.id,locale:l(r.document,[o.locale]),status:b(r.document,n),authorAssurance:n,answers:structuredClone(o.answers),createdAt:i,updatedAt:i,submittedAt:i},a=await e.store.insertResponseIdempotently(s,o.publicToken,o.idempotencyKey);if(!a.inserted)return a.response;return await e.emit?.("forms.response.submitted",{definitionId:s.definitionId,revisionId:s.revisionId,responseId:s.id,occurredAt:i}),a.response}}}import{flattenPortableFormFields as _}from"@lssm/lib.contracts-spec/forms";function g(e){let o=new Set(e.questions.map(({name:s})=>s)),t=e.questions.map(k),r=new Set(e.formSpec?.policy?.pii??[]),n=_(e.formSpec?.fields??[]),i=Object.entries(e.formSpec?.modelFields??{}).flatMap(([s,a])=>{if(o.has(s))return[];let c=n.find((u)=>u.name===s);return[{id:s,labelKey:c?.labelI18n??s,sensitive:r.has(s)||[...r].some((u)=>u.startsWith(`${s}.`))||c?.kind==="email"||c?.kind==="phone"||a.type==="email"||a.type==="phone"}]});return[...t,...i]}function k(e){return{id:e.id,labelKey:e.labelKey,sensitive:e.sensitive===!0||e.kind==="email"||e.kind==="phone"}}var L="response_extensions:AI follow-ups";function V(e){let o=Array.isArray(e)?e.every((r)=>typeof r!=="object")?e.join(";"):JSON.stringify(e):e==null?"":typeof e==="object"?JSON.stringify(e):String(e);return`"${(/^[\s]*[=+\-@]/.test(o)?`'${o}`:o).replaceAll('"','""')}"`}function v(e){return`${e.map(V).join(",")}\r
3
+ `}function D(e,o,t){if(o.revisionId!==e.id)throw Error("CSV responses must belong to the exported revision");let r=g(e.document),n=new Set(r.map(({id:s})=>s)),i=Object.fromEntries(Object.entries(o.answers).filter(([s])=>!n.has(s)));return[o.id,o.status,o.locale,o.submittedAt??"",...r.map((s)=>{if(!(t.includeSensitive&&t.sensitiveGrant?.authorized)&&s.sensitive)return"[REDACTED]";return o.answers[s.id]}),t.includeSensitive&&t.sensitiveGrant?.authorized?i:"[REDACTED]"]}function h(e,o){let t=l(e.document,[o]),r=e.document.content.catalogs[t]??{};return["response_id","status","locale","submitted_at",...g(e.document).map((n)=>`${n.id}:${r[n.labelKey]??n.labelKey}`),L]}function M(e,o,t={}){return`\uFEFF${v(h(e,t.locale))}${o.map((r)=>v(D(e,r,t))).join("")}`}async function*j(e,o,t={}){yield`\uFEFF${v(h(e,t.locale))}`;for await(let r of o)yield v(D(e,r,t))}import{evalPredicate as U}from"@lssm/lib.contracts-spec/forms";function K(e,o,t){let r=e.pages.findIndex((i)=>i.id===o);if(r<0)throw new m("PAGE_NOT_FOUND","Current form page does not exist");return e.pages[r]?.nextPageRules?.find((i)=>U(t,i.when))?.nextPageId??e.pages[r]?.defaultNextPageId??e.pages[r+1]?.id}function ue(e){return{...y(e),...R(e)}}export{m as FormsPlatformError,ue as createFormsPlatformService,l as resolveFormLocale,K as resolveNextFormPage,M as serializeFormResponsesCsv,j as streamFormResponsesCsv};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,25 @@
1
+ /**
2
+ * `@lssm/lib.contracts-runtime-core`
3
+ *
4
+ * Framework-neutral runtime for the canonical ContractSpec data-fetching
5
+ * protocol. Depends only on `@lssm/lib.contracts-spec` (the frozen
6
+ * protocol primitives). It will host the `Transport` port, the in-house
7
+ * query/cache engine, the offline mutation queue, the conflict-resolver
8
+ * registry, the storage port, the reachability input port, and
9
+ * tag-invalidation runtime.
10
+ *
11
+ * Phase 2.2–2.6: the Transport / reachability / storage ports, the in-house
12
+ * query engine (B1a), and the offline-queue + conflict engine (B1b) live here.
13
+ */
14
+ export { type ApprovalNonceStore, createMemoryApprovalNonceStore, createOperationApprovalPort, type OperationApprovalPortOptions, operationApprovalInputDigest, } from './approval.js';
15
+ export type { CollaborationEnvelope, CollaborationSnapshot, CollaborationSnapshotEnvelope, CollaborationUpdateEnvelope, CollaborationVersionVector, } from './collaboration.js';
16
+ export { ContractsRuntimeCoreFeature } from './contracts-runtime-core.feature.js';
17
+ export * from './engine/index.js';
18
+ export * from './forms-platform.js';
19
+ export { isLocalOfflineMode, type LocalOfflineMode, type LocalOfflinePolicy, type LocalOfflinePolicyInput, type ResolveLocalOfflinePolicyOptions, resolveLocalOfflinePolicy, } from './local-offline.js';
20
+ export { createStaticReachabilitySource, isNetworkAllowed, type OnlineSignal, type ReachabilityListener, type ReachabilitySnapshot, type ReachabilitySource, reachabilityPortDoc, } from './reachability.js';
21
+ export { type EncryptionSeam, hashKey, identityEncryption, type RedactionPolicy, redactionDoc, redactSecrets, } from './redaction.js';
22
+ export { createKeyedStorage, type KeyedStorageOptions, type StringKvDriver, } from './storage/keyed-store.js';
23
+ export { createMemoryStorage } from './storage/memory.js';
24
+ export { type StoragePort, storagePortDoc } from './storage/port.js';
25
+ export { type InvalidationEvent, type InvalidationHandler, type Transport, type TransportContext, transportPortDoc, } from './transport.js';
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ // @bun
2
+ 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/* \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 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
3
+ `}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 \u2014 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 \u2014 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) \u2192 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 \u2014 no parallel transport types):","- idempotency \u2192 ContractResult success field (ContractResultIdempotency{key,replayed?,firstSeenAt?} on ContractSuccess.idempotency). NOT a transport argument.","- subscriptions \u2192 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 \u2192 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 \u2192 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(`
4
+ `)};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,17 @@
1
+ export type LocalOfflineMode = 'online-only' | 'memory-cache' | 'offline-capable';
2
+ export interface LocalOfflinePolicy {
3
+ mode: LocalOfflineMode;
4
+ source?: 'default' | 'env' | 'feature-flag' | 'explicit';
5
+ reason?: string;
6
+ }
7
+ export type LocalOfflinePolicyInput = LocalOfflineMode | LocalOfflinePolicy;
8
+ export interface ResolveLocalOfflinePolicyOptions {
9
+ /** Explicit fallback/default when env and flag are unset. Defaults to online-only. */
10
+ defaultMode?: LocalOfflinePolicyInput;
11
+ /** Host-supplied env value or env-like object; the resolver never reads global process.env. */
12
+ env?: unknown;
13
+ /** Host-supplied feature flag; when set it wins over env/default. */
14
+ flag?: unknown;
15
+ }
16
+ export declare function isLocalOfflineMode(value: unknown): value is LocalOfflineMode;
17
+ export declare function resolveLocalOfflinePolicy(options?: ResolveLocalOfflinePolicyOptions): LocalOfflinePolicy;
@@ -0,0 +1,2 @@
1
+ // @bun
2
+ 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
+ export {};
@@ -0,0 +1,130 @@
1
+ // src/approval.ts
2
+ import { OPERATION_EXECUTION_EFFECTS } from "@lssm/lib.contracts-spec/operations";
3
+ function createMemoryApprovalNonceStore(now = () => new Date) {
4
+ const consumed = new Map;
5
+ return {
6
+ async consume(nonce, expiresAt) {
7
+ const currentTime = now().getTime();
8
+ for (const [key, expiry] of consumed) {
9
+ if (expiry <= currentTime)
10
+ consumed.delete(key);
11
+ }
12
+ if (consumed.has(nonce))
13
+ return false;
14
+ consumed.set(nonce, Date.parse(expiresAt));
15
+ return true;
16
+ }
17
+ };
18
+ }
19
+ function createOperationApprovalPort(options = {}) {
20
+ const now = options.now ?? (() => new Date);
21
+ const nonceStore = options.nonceStore ?? createMemoryApprovalNonceStore(now);
22
+ return {
23
+ async authorize(request) {
24
+ const denial = await validateApprovalRequest(request, now(), options.verifyEvidence);
25
+ if (denial)
26
+ return denial;
27
+ if (!await nonceStore.consume(request.receipt.nonce, request.receipt.expiresAt)) {
28
+ return { effect: "deny", reason: "replayed_receipt" };
29
+ }
30
+ return {
31
+ effect: "allow",
32
+ receiptId: request.receipt.id,
33
+ nonce: request.receipt.nonce
34
+ };
35
+ }
36
+ };
37
+ }
38
+ async function operationApprovalInputDigest(input) {
39
+ const bytes = new TextEncoder().encode(canonicalJson(input));
40
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", bytes);
41
+ return `sha256:${Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")}`;
42
+ }
43
+ async function validateApprovalRequest(request, now, verifyEvidence) {
44
+ const { receipt } = request;
45
+ const issuedAt = Date.parse(receipt.issuedAt);
46
+ const expiresAt = Date.parse(receipt.expiresAt);
47
+ if (!receipt.id || !receipt.nonce || !receipt.issuer || !receipt.evidenceRef || !hasSubjectScope(receipt.subject) || receipt.effects.some((effect) => !OPERATION_EXECUTION_EFFECTS.includes(effect)) || !Number.isFinite(issuedAt) || !Number.isFinite(expiresAt) || expiresAt <= issuedAt) {
48
+ return deny("malformed_receipt");
49
+ }
50
+ if (expiresAt <= now.getTime())
51
+ return deny("expired_receipt");
52
+ if (issuedAt > now.getTime())
53
+ return deny("not_yet_valid");
54
+ if (!matchesSubject(receipt.subject, request.subject, ["actor", "userId"])) {
55
+ return deny("wrong_subject");
56
+ }
57
+ if (!matchesSubject(receipt.subject, request.subject, [
58
+ "tenantId",
59
+ "workspaceId",
60
+ "organizationId"
61
+ ])) {
62
+ return deny("wrong_tenant");
63
+ }
64
+ if (receipt.operation.key !== request.operation.key || receipt.operation.version !== request.operation.version) {
65
+ return deny("wrong_operation");
66
+ }
67
+ if (receipt.inputDigest !== await operationApprovalInputDigest(request.input)) {
68
+ return deny("wrong_input");
69
+ }
70
+ const approvedEffects = new Set(receipt.effects);
71
+ if (request.requiredEffects.some((effect) => !approvedEffects.has(effect))) {
72
+ return deny("insufficient_effects");
73
+ }
74
+ if (!coversCost(receipt, request)) {
75
+ return deny("insufficient_cost_ceiling");
76
+ }
77
+ if (verifyEvidence && !await verifyEvidence(receipt)) {
78
+ return deny("invalid_evidence");
79
+ }
80
+ return;
81
+ }
82
+ function matchesSubject(receipt, expected, keys) {
83
+ return keys.every((key) => {
84
+ const expectedValue = expected[key];
85
+ return expectedValue == null || receipt[key] === expectedValue;
86
+ });
87
+ }
88
+ function coversCost(receipt, request) {
89
+ if (!request.requestedCost)
90
+ return true;
91
+ if (!Number.isFinite(request.requestedCost.amount) || request.requestedCost.amount < 0) {
92
+ return false;
93
+ }
94
+ const ceiling = receipt.costCeiling;
95
+ if (!ceiling || !Number.isFinite(ceiling.amount) || ceiling.amount < 0) {
96
+ return false;
97
+ }
98
+ return ceiling.amount >= request.requestedCost.amount && (!request.requestedCost.currency || ceiling.currency === request.requestedCost.currency) && (!request.requestedCost.unit || ceiling.unit === request.requestedCost.unit);
99
+ }
100
+ function hasSubjectScope(subject) {
101
+ return Object.values(subject).some((value) => typeof value === "string" && value.length > 0);
102
+ }
103
+ function deny(reason) {
104
+ return { effect: "deny", reason };
105
+ }
106
+ function canonicalJson(value) {
107
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
108
+ return JSON.stringify(value);
109
+ }
110
+ if (typeof value === "number") {
111
+ if (!Number.isFinite(value))
112
+ throw new TypeError("Approval input is not JSON.");
113
+ return JSON.stringify(value);
114
+ }
115
+ if (Array.isArray(value)) {
116
+ return `[${value.map(canonicalJson).join(",")}]`;
117
+ }
118
+ if (value instanceof Date)
119
+ return JSON.stringify(value.toISOString());
120
+ if (typeof value === "object") {
121
+ const entries = Object.entries(value).filter(([, item]) => item !== undefined).sort(([left], [right]) => left.localeCompare(right));
122
+ return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${canonicalJson(item)}`).join(",")}}`;
123
+ }
124
+ throw new TypeError("Approval input is not JSON.");
125
+ }
126
+ export {
127
+ createMemoryApprovalNonceStore,
128
+ createOperationApprovalPort,
129
+ operationApprovalInputDigest
130
+ };
File without changes
@@ -0,0 +1,17 @@
1
+ // src/contracts-runtime-core.feature.ts
2
+ import { defineFeature } from "@lssm/lib.contracts-spec/features";
3
+ var ContractsRuntimeCoreFeature = defineFeature({
4
+ meta: {
5
+ key: "libs.contracts-runtime-core",
6
+ version: "1.0.0",
7
+ title: "Contracts Runtime Core",
8
+ description: "Framework-neutral in-house query/cache/offline engine and Transport port for ContractSpec data fetching.",
9
+ domain: "contracts-runtime-core",
10
+ owners: ["@contractspec-core"],
11
+ tags: ["package", "libs", "contracts-runtime-core"],
12
+ stability: "experimental"
13
+ }
14
+ });
15
+ export {
16
+ ContractsRuntimeCoreFeature
17
+ };
@@ -0,0 +1,16 @@
1
+ // src/engine/backoff.ts
2
+ var DEFAULT_BASE_MS = 200;
3
+ var DEFAULT_FACTOR = 2;
4
+ var DEFAULT_MAX_MS = 30000;
5
+ function backoffDelayMs(attempt, options = {}) {
6
+ const base = options.baseMs ?? DEFAULT_BASE_MS;
7
+ const factor = options.factor ?? DEFAULT_FACTOR;
8
+ const max = options.maxMs ?? DEFAULT_MAX_MS;
9
+ const jitter = options.jitter ?? Math.random;
10
+ const exponent = Math.max(0, attempt - 1);
11
+ const raw = Math.min(max, base * factor ** exponent);
12
+ return Math.round(raw * (0.5 + jitter() * 0.5));
13
+ }
14
+ export {
15
+ backoffDelayMs
16
+ };
@@ -0,0 +1,49 @@
1
+ // src/engine/cache-store.ts
2
+ import { invalidationTagMatches } from "@lssm/lib.contracts-spec/query/invalidation";
3
+ var CACHE_PREFIX = "cache:";
4
+ function createCacheStore(storage) {
5
+ const tagIndex = new Map;
6
+ const storageKey = (key) => `${CACHE_PREFIX}${key}`;
7
+ return {
8
+ read: (key) => storage.get(storageKey(key)),
9
+ write: async (entry) => {
10
+ tagIndex.set(entry.key, entry.tags);
11
+ await storage.set(storageKey(entry.key), entry);
12
+ },
13
+ remove: async (key) => {
14
+ tagIndex.delete(key);
15
+ await storage.remove(storageKey(key));
16
+ },
17
+ entries: async () => {
18
+ const keys = await storage.keys(CACHE_PREFIX);
19
+ const all = [];
20
+ for (const key of keys) {
21
+ const entry = await storage.get(key);
22
+ if (entry !== undefined)
23
+ all.push(entry);
24
+ }
25
+ return all;
26
+ },
27
+ invalidate: async (tags, now) => {
28
+ const affected = [];
29
+ for (const [cacheKey, entryTags] of tagIndex.entries()) {
30
+ const matched = entryTags.some((subject) => tags.some((pattern) => invalidationTagMatches(subject, pattern)));
31
+ if (!matched)
32
+ continue;
33
+ const stored = await storage.get(storageKey(cacheKey));
34
+ if (stored === undefined)
35
+ continue;
36
+ await storage.set(storageKey(cacheKey), {
37
+ ...stored,
38
+ status: "stale",
39
+ staleAt: now
40
+ });
41
+ affected.push(cacheKey);
42
+ }
43
+ return affected;
44
+ }
45
+ };
46
+ }
47
+ export {
48
+ createCacheStore
49
+ };
@@ -0,0 +1,51 @@
1
+ // src/engine/conflict-registry.ts
2
+ function fieldMerge(ctx) {
3
+ const server = ctx.serverData;
4
+ const local = ctx.localData;
5
+ const fields = ctx.mergeFields ?? [];
6
+ if (typeof server !== "object" || server === null || typeof local !== "object" || local === null) {
7
+ return { outcome: "surface" };
8
+ }
9
+ const merged = {
10
+ ...server
11
+ };
12
+ const localRecord = local;
13
+ for (const field of fields) {
14
+ if (field in localRecord)
15
+ merged[field] = localRecord[field];
16
+ }
17
+ return { outcome: "merged", data: merged };
18
+ }
19
+ function createConflictResolverRegistry() {
20
+ const resolvers = new Map;
21
+ return {
22
+ register(key, resolver) {
23
+ resolvers.set(key, resolver);
24
+ },
25
+ has(key) {
26
+ return resolvers.has(key);
27
+ },
28
+ async resolve(ctx) {
29
+ switch (ctx.strategy) {
30
+ case "server-wins":
31
+ return { outcome: "use-server", data: ctx.serverData };
32
+ case "client-wins":
33
+ return { outcome: "use-client", data: ctx.localData };
34
+ case "field-merge":
35
+ return fieldMerge(ctx);
36
+ case "surface":
37
+ return { outcome: "surface" };
38
+ default: {
39
+ const key = ctx.policy.resolver;
40
+ const resolver = key === undefined ? undefined : resolvers.get(key);
41
+ if (resolver === undefined)
42
+ return { outcome: "surface" };
43
+ return resolver(ctx);
44
+ }
45
+ }
46
+ }
47
+ };
48
+ }
49
+ export {
50
+ createConflictResolverRegistry
51
+ };