@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
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# @lssm/lib.contracts-runtime-core
|
|
2
|
+
|
|
3
|
+
Framework-neutral, in-house runtime for the canonical ContractSpec
|
|
4
|
+
data-fetching protocol. It depends only on
|
|
5
|
+
[`@lssm/lib.contracts-spec`](../contracts-spec) — the frozen protocol
|
|
6
|
+
primitives (`QueryEnvelope`, `QueryResultEnvelope`, `ContractResult`,
|
|
7
|
+
`CacheStatus`, `InvalidationTag`, `ConflictPolicy`, `VersionToken`,
|
|
8
|
+
`QueryConsistency`, `QueryState`).
|
|
9
|
+
|
|
10
|
+
## Scope
|
|
11
|
+
|
|
12
|
+
This package hosts:
|
|
13
|
+
|
|
14
|
+
- the `Transport` port (`execute()` + optional `subscribe()` realtime seam),
|
|
15
|
+
- the in-house query/cache engine (`createDataEngine`: store, dedup,
|
|
16
|
+
retry/backoff, background revalidation, GC) — **no `@tanstack/*` dependency**,
|
|
17
|
+
- the durable offline mutation queue + replay-on-reconnect,
|
|
18
|
+
- the conflict-resolver registry (server-wins default, field-merge,
|
|
19
|
+
client-wins, surface, CRDT-via-injection, custom),
|
|
20
|
+
- the storage port + shipped platform defaults (IndexedDB `.web` /
|
|
21
|
+
AsyncStorage `.native`),
|
|
22
|
+
- the reachability **input** port (the engine never imports `integrations/*`),
|
|
23
|
+
- tag-invalidation runtime, observability, persisted-state redaction,
|
|
24
|
+
- `economic-evidence-attached` engine events and observability bindings that carry provider-neutral usage/cost/budget/replay/projection refs without importing billing, finance, provider, or analytics SDKs,
|
|
25
|
+
- the framework-neutral scoped approval runtime (`createOperationApprovalPort`),
|
|
26
|
+
canonical SHA-256 input digest helper, and atomic nonce-store port,
|
|
27
|
+
- the re-homed `Collaboration*` envelopes (consumed by `integration.crdt-loro`).
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Native apps can wire durable storage through the published Expo/React Native subpath:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import AsyncStorage from "@react-native-async-storage/async-storage";
|
|
35
|
+
import { createAsyncStorage } from "@lssm/lib.contracts-runtime-core/storage/async-storage";
|
|
36
|
+
|
|
37
|
+
const storage = createAsyncStorage(AsyncStorage);
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`createDataEngine({ transport })` returns a `DataEngine` exposing
|
|
41
|
+
`query`/`getState`/`subscribe`/`invalidateTags`/`refresh`/`emitConflict`/
|
|
42
|
+
`mutate`/`drain`/`resume`/`dispose` over one shared cache. The React binding
|
|
43
|
+
(`useContractQuery`/`useContractMutation` in
|
|
44
|
+
`@lssm/lib.contracts-runtime-client-react`) is thin; this core stays
|
|
45
|
+
framework-neutral and is also consumed by the realtime worker and the
|
|
46
|
+
provider resolver.
|
|
47
|
+
|
|
48
|
+
## Local/offline capability mode
|
|
49
|
+
|
|
50
|
+
`createDataEngine` now owns the semantic local/offline toggle through
|
|
51
|
+
`localOffline`. The safe default is `online-only`: online queries and direct
|
|
52
|
+
online mutations work, but the engine does not persist local data, serve prior
|
|
53
|
+
local data while offline, or enqueue offline mutations.
|
|
54
|
+
|
|
55
|
+
| Mode | Cache/read behavior | Offline mutations |
|
|
56
|
+
| --- | --- | --- |
|
|
57
|
+
| `online-only` | No local/offline cache; offline reads return the canonical offline/no-data state. | Disabled; returns `OFFLINE_QUEUE_DISABLED` without queueing. |
|
|
58
|
+
| `memory-cache` | Same-engine in-memory cache only; nothing persists across engine instances. | Disabled while offline. |
|
|
59
|
+
| `offline-capable` | Uses the supplied storage/cache seams for warm offline reads, queue/replay, conflicts, and maintenance. | Queued and replayed when reachability returns. |
|
|
60
|
+
|
|
61
|
+
Use the pure resolver when policy comes from env/config/feature flags:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import {
|
|
65
|
+
createDataEngine,
|
|
66
|
+
createMemoryStorage,
|
|
67
|
+
resolveLocalOfflinePolicy,
|
|
68
|
+
} from "@lssm/lib.contracts-runtime-core";
|
|
69
|
+
|
|
70
|
+
const localOffline = resolveLocalOfflinePolicy({
|
|
71
|
+
env: process.env.CONTRACTSPEC_LOCAL_OFFLINE,
|
|
72
|
+
flag: featureFlags.localOffline,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const engine = createDataEngine({
|
|
76
|
+
transport,
|
|
77
|
+
localOffline,
|
|
78
|
+
advanced:
|
|
79
|
+
localOffline.mode === "offline-capable"
|
|
80
|
+
? { storage: createMemoryStorage() /* use IndexedDB/AsyncStorage in apps */ }
|
|
81
|
+
: undefined,
|
|
82
|
+
});
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`localOffline` is the source of truth. Low-level composition seams now live
|
|
86
|
+
under `advanced`; `online-only` rejects `advanced.storage` and
|
|
87
|
+
`advanced.cacheStore`, while `advanced.reachability` remains valid in every
|
|
88
|
+
mode. `offline-capable` requires an explicit `advanced.storage` so durable
|
|
89
|
+
local persistence is never enabled accidentally.
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
## Economic Evidence Observability
|
|
93
|
+
|
|
94
|
+
`attachObservability()` consumes `economic-evidence-attached` events automatically. It increments `contracts_runtime_economic_evidence_refs_total` and logs `economic.evidence.attached` with only operation key, latency, ref count, and allowlisted ref fields (`id`, `kind`, optional `source`). Raw provider receipts, prompts, database payloads, replay payloads, and unsafe ref IDs containing PII/secret-shaped values are rejected or dropped before logs consume the event.
|
|
95
|
+
|
|
96
|
+
## Scoped operation approvals
|
|
97
|
+
|
|
98
|
+
`createOperationApprovalPort()` validates receipt validity, subject and tenant
|
|
99
|
+
scope, operation/version, canonical input digest, effect coverage, optional
|
|
100
|
+
cost ceiling, and issuer evidence before atomically consuming the nonce.
|
|
101
|
+
Production deployments can inject a durable `ApprovalNonceStore` and evidence
|
|
102
|
+
verifier; the in-memory nonce store is intended for tests and single-process
|
|
103
|
+
runtimes.
|
|
104
|
+
|
|
105
|
+
## Status
|
|
106
|
+
|
|
107
|
+
Engine landed (B1a synchronous + B1b offline/conflict). Full WebSocket realtime
|
|
108
|
+
delivery on `subscribe()` is a follow-on; the seam is defined and the realtime
|
|
109
|
+
worker runs on it. This package absorbed the former
|
|
110
|
+
`@lssm/lib.data-transmission-{spec,runtime}` concepts.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { OperationApprovalPort, OperationApprovalReceipt } from '@lssm/lib.contracts-spec/operations';
|
|
2
|
+
export interface ApprovalNonceStore {
|
|
3
|
+
/** Atomically consumes a nonce, returning false when it was already consumed. */
|
|
4
|
+
consume(nonce: string, expiresAt: string): Promise<boolean>;
|
|
5
|
+
}
|
|
6
|
+
export interface OperationApprovalPortOptions {
|
|
7
|
+
now?: () => Date;
|
|
8
|
+
nonceStore?: ApprovalNonceStore;
|
|
9
|
+
verifyEvidence?: (receipt: OperationApprovalReceipt) => Promise<boolean>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createMemoryApprovalNonceStore(now?: () => Date): ApprovalNonceStore;
|
|
12
|
+
export declare function createOperationApprovalPort(options?: OperationApprovalPortOptions): OperationApprovalPort;
|
|
13
|
+
export declare function operationApprovalInputDigest(input: unknown): Promise<string>;
|
package/dist/approval.js
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
import{OPERATION_EXECUTION_EFFECTS as f}from"@lssm/lib.contracts-spec/operations";function l(e=()=>new Date){let n=new Map;return{async consume(r,t){let o=e().getTime();for(let[p,a]of n)if(a<=o)n.delete(p);if(n.has(r))return!1;return n.set(r,Date.parse(t)),!0}}}function b(e={}){let n=e.now??(()=>new Date),r=e.nonceStore??l(n);return{async authorize(t){let o=await m(t,n(),e.verifyEvidence);if(o)return o;if(!await r.consume(t.receipt.nonce,t.receipt.expiresAt))return{effect:"deny",reason:"replayed_receipt"};return{effect:"allow",receiptId:t.receipt.id,nonce:t.receipt.nonce}}}}async function d(e){let n=new TextEncoder().encode(s(e)),r=await globalThis.crypto.subtle.digest("SHA-256",n);return`sha256:${Array.from(new Uint8Array(r),(t)=>t.toString(16).padStart(2,"0")).join("")}`}async function m(e,n,r){let{receipt:t}=e,o=Date.parse(t.issuedAt),p=Date.parse(t.expiresAt);if(!t.id||!t.nonce||!t.issuer||!t.evidenceRef||!y(t.subject)||t.effects.some((c)=>!f.includes(c))||!Number.isFinite(o)||!Number.isFinite(p)||p<=o)return i("malformed_receipt");if(p<=n.getTime())return i("expired_receipt");if(o>n.getTime())return i("not_yet_valid");if(!u(t.subject,e.subject,["actor","userId"]))return i("wrong_subject");if(!u(t.subject,e.subject,["tenantId","workspaceId","organizationId"]))return i("wrong_tenant");if(t.operation.key!==e.operation.key||t.operation.version!==e.operation.version)return i("wrong_operation");if(t.inputDigest!==await d(e.input))return i("wrong_input");let a=new Set(t.effects);if(e.requiredEffects.some((c)=>!a.has(c)))return i("insufficient_effects");if(!A(t,e))return i("insufficient_cost_ceiling");if(r&&!await r(t))return i("invalid_evidence");return}function u(e,n,r){return r.every((t)=>{let o=n[t];return o==null||e[t]===o})}function A(e,n){if(!n.requestedCost)return!0;if(!Number.isFinite(n.requestedCost.amount)||n.requestedCost.amount<0)return!1;let r=e.costCeiling;if(!r||!Number.isFinite(r.amount)||r.amount<0)return!1;return r.amount>=n.requestedCost.amount&&(!n.requestedCost.currency||r.currency===n.requestedCost.currency)&&(!n.requestedCost.unit||r.unit===n.requestedCost.unit)}function y(e){return Object.values(e).some((n)=>typeof n==="string"&&n.length>0)}function i(e){return{effect:"deny",reason:e}}function s(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(s).join(",")}]`;if(e instanceof Date)return JSON.stringify(e.toISOString());if(typeof e==="object")return`{${Object.entries(e).filter(([,r])=>r!==void 0).sort(([r],[t])=>r.localeCompare(t)).map(([r,t])=>`${JSON.stringify(r)}:${s(t)}`).join(",")}}`;throw TypeError("Approval input is not JSON.")}export{l as createMemoryApprovalNonceStore,b as createOperationApprovalPort,d as operationApprovalInputDigest};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{OPERATION_EXECUTION_EFFECTS as f}from"@lssm/lib.contracts-spec/operations";function l(e=()=>new Date){let n=new Map;return{async consume(r,t){let o=e().getTime();for(let[p,a]of n)if(a<=o)n.delete(p);if(n.has(r))return!1;return n.set(r,Date.parse(t)),!0}}}function b(e={}){let n=e.now??(()=>new Date),r=e.nonceStore??l(n);return{async authorize(t){let o=await m(t,n(),e.verifyEvidence);if(o)return o;if(!await r.consume(t.receipt.nonce,t.receipt.expiresAt))return{effect:"deny",reason:"replayed_receipt"};return{effect:"allow",receiptId:t.receipt.id,nonce:t.receipt.nonce}}}}async function d(e){let n=new TextEncoder().encode(s(e)),r=await globalThis.crypto.subtle.digest("SHA-256",n);return`sha256:${Array.from(new Uint8Array(r),(t)=>t.toString(16).padStart(2,"0")).join("")}`}async function m(e,n,r){let{receipt:t}=e,o=Date.parse(t.issuedAt),p=Date.parse(t.expiresAt);if(!t.id||!t.nonce||!t.issuer||!t.evidenceRef||!y(t.subject)||t.effects.some((c)=>!f.includes(c))||!Number.isFinite(o)||!Number.isFinite(p)||p<=o)return i("malformed_receipt");if(p<=n.getTime())return i("expired_receipt");if(o>n.getTime())return i("not_yet_valid");if(!u(t.subject,e.subject,["actor","userId"]))return i("wrong_subject");if(!u(t.subject,e.subject,["tenantId","workspaceId","organizationId"]))return i("wrong_tenant");if(t.operation.key!==e.operation.key||t.operation.version!==e.operation.version)return i("wrong_operation");if(t.inputDigest!==await d(e.input))return i("wrong_input");let a=new Set(t.effects);if(e.requiredEffects.some((c)=>!a.has(c)))return i("insufficient_effects");if(!A(t,e))return i("insufficient_cost_ceiling");if(r&&!await r(t))return i("invalid_evidence");return}function u(e,n,r){return r.every((t)=>{let o=n[t];return o==null||e[t]===o})}function A(e,n){if(!n.requestedCost)return!0;if(!Number.isFinite(n.requestedCost.amount)||n.requestedCost.amount<0)return!1;let r=e.costCeiling;if(!r||!Number.isFinite(r.amount)||r.amount<0)return!1;return r.amount>=n.requestedCost.amount&&(!n.requestedCost.currency||r.currency===n.requestedCost.currency)&&(!n.requestedCost.unit||r.unit===n.requestedCost.unit)}function y(e){return Object.values(e).some((n)=>typeof n==="string"&&n.length>0)}function i(e){return{effect:"deny",reason:e}}function s(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(s).join(",")}]`;if(e instanceof Date)return JSON.stringify(e.toISOString());if(typeof e==="object")return`{${Object.entries(e).filter(([,r])=>r!==void 0).sort(([r],[t])=>r.localeCompare(t)).map(([r,t])=>`${JSON.stringify(r)}:${s(t)}`).join(",")}}`;throw TypeError("Approval input is not JSON.")}export{l as createMemoryApprovalNonceStore,b as createOperationApprovalPort,d as operationApprovalInputDigest};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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 @@
|
|
|
1
|
+
function M(n,t={}){let a=t.baseMs??200,e=t.factor??2,r=t.maxMs??30000,c=t.jitter??Math.random,o=Math.max(0,n-1),s=Math.min(r,a*e**o);return Math.round(s*(0.5+c()*0.5))}export{M as backoffDelayMs};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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 @@
|
|
|
1
|
+
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
|
+
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
|
+
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/* — 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 _(){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
|
+
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 @@
|
|
|
1
|
+
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/* — 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 _(){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
|
+
function s(){let e=new Map;return{run(n,t){let r=e.get(n);if(r!==void 0)return r;let i=t().finally(()=>{e.delete(n)});return e.set(n,i),i},size(){return e.size}}}export{s as createInflightRegistry};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function i(){let t=new Set;return{emit(e){for(let n of t)n(e)},subscribe(e){return t.add(e),()=>{t.delete(e)}}}}export{i as createEngineEvents};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function W(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),d=Math.min(i,n*a**r);return Math.round(d*(0.5+u()*0.5))}import{invalidationTagMatches as le}from"@lssm/lib.contracts-spec/query/invalidation";var re="cache:";function L(e){let t=new Map,n=(a)=>`${re}${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(re),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,d]of t.entries()){if(!d.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 I(){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)}}}}}import{defaultConflictPolicy as de}from"@lssm/lib.contracts-spec/query/conflict-policy";async function _(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 z(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,d=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:d.outcome}),d.outcome){case"use-server":{await _(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:d.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 pe=new Set(["online-only","memory-cache","offline-capable"]),se={"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 Le(e){return typeof e==="string"&&pe.has(e)}function K(e={}){let t=e.defaultMode===void 0?{mode:"online-only",source:"default",reason:"No local/offline policy input was supplied."}:G(e.defaultMode,"explicit")??{mode:"online-only",source:"explicit"},n=G(me(e.env),"env",{allowUnknown:!0});return G(e.flag,"feature-flag")??n??t}function me(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 G(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=se[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"?se[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 q(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var _e={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 X(){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()}}}function P(){let e=new Set;return{emit(t){for(let n of e)n(t)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}}function H(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 ye,parseSafeEconomicEvidenceRefId as ge}from"@lssm/lib.contracts-spec/operations";import{defaultConflictPolicy as ve}from"@lssm/lib.contracts-spec/query/conflict-policy";var he=5;function J(...e){return ye(...e)}function Ee(e){let t=ge(e);if(!t)throw Error("EconomicEvidenceInvalidRef: unsafe or invalid economic evidence ref");return t}function Y(e){let{transport:t,cache:n,queue:a,reachability:i,resolvers:u,events:r}=e,d=e.now??(()=>Date.now()),M=e.maxAttempts??he,T={cache:n,queue:a,resolvers:u,events:r,now:d,queryEngine:e.queryEngine},h=!1,E=0,S=null,D=new Map,j=(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:d(),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:d()});await e.queryEngine?.refresh(f.key)}await n.invalidate(o.tags,d())}async function m(o){let l;try{l=J(o.economicEvidenceRefs)}catch{await _(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(j(o,l),e.contextProvider?.());if(g.ok){let b=J(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(Ee)});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 z(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=W(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?.(()=>{p()},b),!0}await _(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(!C(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 p(){if(S!==null)return S;return S=c().finally(()=>{S=null}),S}async function s(o,l={}){J(o.economicEvidenceRefs);let f=d();E+=1;let g=`${f}-${E}`,k=l.optimistic?await v(l.optimistic):[];if(await a.enqueue({...o,conflictPolicy:o.conflictPolicy??ve(),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 p();return D.get(g)??{id:g,status:"pending"}}async function y(){h=!1;for(let o of await a.list())if(o.status==="paused-auth")await a.update({...o,status:"pending"});await p()}let O=i.subscribe((o)=>{if(!h&&C(o.signal))p()});return{mutate:s,drain:p,resume:y,dispose:O}}function Z(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 ee}from"@lssm/lib.contracts-spec/query/key";function x(e,t){if(e?.staleAt===void 0)return!1;return t>=e.staleAt}function A(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function U(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 F(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 N(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 V(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 B(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 ce(e,t){let{transport:n}=e,a=e.advanced??{},i=a.now??(()=>Date.now()),u=a.reachability??q("online"),r=a.events??P(),d=a.resolvers??I(),M=t??K({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=ee(v),c=u.getSnapshot().signal;if(!C(c)){let l=B(void 0,i());return S(m,l),l}S(m,U(void 0,i()));let p=i(),s=await n.execute(v,w),y=i();if(!s.ok){r.emit({type:"fetch-error",key:m,code:s.code,latencyMs:y-p});let l=s.problem.category==="auth"?N(s.problem,void 0,y):F(s.problem,void 0,y);return S(m,l),l}r.emit({type:"fetch-success",key:m,latencyMs:y-p});let O={key:m,data:s.data.data,versionToken:s.data.versionToken??s.versionToken,status:c==="restricted"?"warm":"fresh",tags:[],storedAt:y,...s.data.pageInfo===void 0?{}:{pageInfo:s.data.pageInfo}},o=Q(O,y);return S(m,o),o},getState(v){return T.get(ee(v))??A()},subscribe(v,w){let m=ee(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)??A();return S(v,w),w},async emitConflict(v,w){let m=V(void 0,w,i());return S(v,m),m}},...{async mutate(v,w={}){E+=1;let m=`${i()}-${E}`;if(!C(u.getSnapshot().signal))return{id:m,status:"disabled-offline",problem:we()};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:d,localOffline:M}}function we(){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 ne}from"@lssm/lib.contracts-spec/query/key";function te(){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 Se=0;function ae(e){let{transport:t}=e,n=e.storage??X(),a=e.reachability??q("online"),i=e.now??(()=>Date.now()),u=e.defaultStaleTimeMs??Se,r=e.cacheStore??L(n),d=e.events??P(),M=te(),T=new Map,h=new Map;function E(c,p){T.set(c,p);let s=h.get(c);if(s===void 0)return;for(let y of s)y(p)}async function S(c,p){let s=ne(c),y=a.getSnapshot().signal,O=await r.read(s);if(!C(y)){let R=B(O,i());return E(s,R),R}if(O!==void 0&&!x(O,i())){d.emit({type:"cache-hit",key:s});let R=Q(O,i());return E(s,R),R}d.emit({type:"cache-miss",key:s}),E(s,U(O,i()));let o=i(),l=await M.run(s,()=>t.execute(c,p)),f=i();if(!l.ok){d.emit({type:"fetch-error",key:s,code:l.code,latencyMs:f-o});let R=l.problem.category==="auth"?N(l.problem,O,f):F(l.problem,O,f);return E(s,R),R}d.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:y==="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 ie=Q(b,f);return E(s,ie),ie}async function D(c){let p=await r.read(c),s=p===void 0?A():Q(p,i());return E(c,s),s}async function j(c,p){let s=await r.read(c),y=V(s,p,i());return E(c,y),y}function v(c){return T.get(ne(c))??A()}function w(c,p){let s=ne(c),y=h.get(s);if(y===void 0)y=new Set,h.set(s,y);return y.add(p),()=>{h.get(s)?.delete(p)}}async function m(c){let p=await r.invalidate(c,i());for(let s of p){let y=await r.read(s);if(y!==void 0)E(s,Q(y,i()))}return p}return{query:S,getState:v,subscribe:w,invalidateTags:m,refresh:D,emitConflict:j}}function oe(e){Te(e);let t=e.advanced??{},n=K({defaultMode:e.localOffline,env:e.env,flag:e.flag});if(be(n.mode,t),n.mode==="online-only")return ce(e,n);let a=t.now??(()=>Date.now()),i=t.storage??X(),u=t.events??P(),r=t.cacheStore??L(i),d=t.reachability??q("online"),M=t.resolvers??I(),T=Z(i),h=ae({...t,transport:e.transport,now:a,storage:i,events:u,cacheStore:r,reachability:d}),E=Y({transport:e.transport,cache:r,queue:T,reachability:d,resolvers:M,events:u,now:a,maxAttempts:t.maxAttempts,queryEngine:h,contextProvider:t.contextProvider,refreshAuth:t.refreshAuth,schedule:t.schedule,backoff:t.backoff}),S=H({cache:r,events:u,now:a,revalidate:(D)=>h.query(D)});return{...h,...E,maintenance:S,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 Te(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 ue(){return globalThis.process?.env}function ke(e){return oe({...e,env:e.env??ue()})}function De(){return{read:async(e)=>{return},write:async()=>{},remove:async()=>{},entries:async()=>[],invalidate:async(e,t)=>[]}}function Me(){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 Oe=(e)=>({operation:e.operation,input:e.input,tags:e.tags});function Ce(e){let t=e.now??(()=>Date.now()),n=0;async function a(i,u={}){n+=1;let r=`${t()}-${n}`;if(!C(e.reachability.getSnapshot().signal))return{id:r,status:"disabled-offline",problem:Me()};let d=await e.transport.execute(Oe(i),u.ctx);if(d.ok)return{id:r,status:"done",data:d.data.data};return{id:r,status:"failed",problem:d.problem}}return{mutate:a,drain:async()=>{},resume:async()=>{},dispose:()=>{}}}export{N as authExpiredState,W as backoffDelayMs,x as computeIsStale,V as conflictState,L as createCacheStore,I as createConflictResolverRegistry,oe as createDataEngine,ke as createDataEngineFromEnv,P as createEngineEvents,te as createInflightRegistry,H as createMaintenance,Y as createMutationEngine,Z as createMutationQueue,De as createNoCacheStore,Ce as createOnlineOnlyMutationEngine,ae as createQueryEngine,F as errorState,A as idleState,U as loadingState,B as offlineState,ue as readHostEnv,z as resolveQueuedConflict,_ as restoreSnapshots,Q as successState};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function o(e){return{async collectGarbage(){let r=e.now(),n=0;for(let t of await e.cache.entries())if(t.expiresAt!==void 0&&r>=t.expiresAt)await e.cache.remove(t.key),n+=1;return e.events.emit({type:"gc-collected",evicted:n}),n},async revalidateStale(){let r=e.revalidate;if(r===void 0)return 0;let n=e.now(),t=0;for(let a of await e.cache.entries()){if(!(a.staleAt!==void 0&&n>=a.staleAt)||a.envelope===void 0)continue;await r(a.envelope),e.events.emit({type:"revalidated",key:a.key}),t+=1}return t}}}export{o as createMaintenance};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{mergeSafeEconomicEvidenceRefIds as Q,parseSafeEconomicEvidenceRefId as F}from"@lssm/lib.contracts-spec/operations";import{defaultConflictPolicy as I}from"@lssm/lib.contracts-spec/query/conflict-policy";function g(t){return t!=="offline"}function U(t="online"){let n={signal:t,since:Date.now()};return{getSnapshot:()=>n,subscribe:()=>()=>{}}}var B={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 M(t,n={}){let i=n.baseMs??200,a=n.factor??2,u=n.maxMs??30000,m=n.jitter??Math.random,s=Math.max(0,t-1),l=Math.min(u,i*a**s);return Math.round(l*(0.5+m()*0.5))}import{defaultConflictPolicy as _}from"@lssm/lib.contracts-spec/query/conflict-policy";async function w(t,n){for(let i of t)if(i.previous===void 0)await n.remove(i.key);else await n.write(i.previous)}async function R(t,n,i){let a=t.conflictPolicy??_(),u=t.rollback[0]?.key,m=u===void 0?void 0:await i.cache.read(u),s=n.metadata?.serverData,l=await i.resolvers.resolve({strategy:a.strategy,policy:a,localData:m?.data,serverData:s,localVersion:t.versionToken,serverVersion:n.versionToken,mergeFields:a.mergeFields});switch(i.events.emit({type:"mutation-conflict",id:t.id,strategy:a.strategy,outcome:l.outcome}),l.outcome){case"use-server":{await w(t.rollback,i.cache),await i.cache.invalidate(t.tags,i.now());for(let E of t.rollback)await i.queryEngine?.refresh(E.key);return"resolved"}case"use-client":return await i.queue.update({...t,versionToken:n.versionToken,status:"pending"}),"re-enqueued";case"merged":{if(u!==void 0&&m!==void 0)await i.cache.write({...m,data:l.data,versionToken:n.versionToken,status:"warm",storedAt:i.now()}),await i.queryEngine?.refresh(u);return await i.queue.update({...t,versionToken:n.versionToken,status:"pending"}),"re-enqueued"}default:{if(u!==void 0)await i.queryEngine?.emitConflict(u,{versionToken:n.versionToken,serverData:s,clientData:m?.data,strategy:a.strategy});return"resolved"}}}var L=5;function b(...t){return Q(...t)}function N(t){let n=F(t);if(!n)throw Error("EconomicEvidenceInvalidRef: unsafe or invalid economic evidence ref");return n}function Y(t){let{transport:n,cache:i,queue:a,reachability:u,resolvers:m,events:s}=t,l=t.now??(()=>Date.now()),E=t.maxAttempts??L,S={cache:i,queue:a,resolvers:m,events:s,now:l,queryEngine:t.queryEngine},h=!1,k=0,y=null,p=new Map,T=(e,c=e.economicEvidenceRefs)=>({operation:e.operation,input:e.input,tags:e.tags,economicEvidenceRefs:c});async function A(e){let c=[];for(let r of e){let o=await i.read(r.key);c.push({key:r.key,previous:o}),await i.write({key:r.key,data:r.data,versionToken:r.versionToken??o?.versionToken,status:"warm",tags:o?.tags??[],storedAt:l(),staleAt:o?.staleAt,expiresAt:o?.expiresAt}),await t.queryEngine?.refresh(r.key)}return c}async function D(e,c){for(let r of e.rollback){let o=await i.read(r.key);if(o!==void 0)await i.write({...o,versionToken:c.versionToken??o.versionToken,status:"stale",staleAt:l()});await t.queryEngine?.refresh(r.key)}await i.invalidate(e.tags,l())}async function q(e){let c;try{c=b(e.economicEvidenceRefs)}catch{await w(e.rollback,i);for(let d of e.rollback)await t.queryEngine?.refresh(d.key);return await a.remove(e.id),s.emit({type:"mutation-failed",id:e.id,code:"ECONOMIC_EVIDENCE_INVALID_REF"}),p.set(e.id,{id:e.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 r=e.attempts+1;await a.update({...e,status:"in-flight",attempts:r});let o=await n.execute(T(e,c),t.contextProvider?.());if(o.ok){let d=b(c,o.data.economicEvidenceRefs);if(await D(e,o.data),await a.remove(e.id),s.emit({type:"mutation-replayed",id:e.id,queueDepth:await a.depth()}),d.length>0)s.emit({type:"economic-evidence-attached",operationKey:e.operation.key,evidenceRefs:d.map(N)});return p.set(e.id,{id:e.id,status:"done",data:o.data.data,economicEvidenceRefs:d}),!0}let{problem:f}=o;if(f.category==="auth"){if(h=!0,await a.update({...e,status:"paused-auth",attempts:r}),s.emit({type:"auth-expired",id:e.id}),p.set(e.id,{id:e.id,status:"paused-auth",problem:f}),t.refreshAuth!==void 0&&await t.refreshAuth())return h=!1,await a.update({...e,status:"pending",attempts:r}),!0;return!1}if(f.category==="conflict"){if(await R(e,f,S)==="resolved")await a.remove(e.id),p.set(e.id,{id:e.id,status:"done",problem:f});return!0}if(f.retryable&&r<E){let d=M(r,t.backoff);return await a.update({...e,status:"failed",attempts:r}),s.emit({type:"mutation-retry",id:e.id,attempt:r,delayMs:d}),p.set(e.id,{id:e.id,status:"failed",problem:f}),t.schedule?.(()=>{v()},d),!0}await w(e.rollback,i);for(let d of e.rollback)await t.queryEngine?.refresh(d.key);return await a.remove(e.id),s.emit({type:"mutation-failed",id:e.id,code:o.code}),p.set(e.id,{id:e.id,status:"failed",problem:f}),!0}async function x(){if(h)return;if(!g(u.getSnapshot().signal))return;for(let e of await a.list()){if(e.status==="paused-auth")continue;if(!await q(e))return}s.emit({type:"queue-drained",remaining:await a.depth()})}function v(){if(y!==null)return y;return y=x().finally(()=>{y=null}),y}async function C(e,c={}){b(e.economicEvidenceRefs);let r=l();k+=1;let o=`${r}-${k}`,f=c.optimistic?await A(c.optimistic):[];if(await a.enqueue({...e,conflictPolicy:e.conflictPolicy??I(),id:o,enqueuedAt:r,attempts:0,status:"pending",rollback:f}),s.emit({type:"mutation-enqueued",id:o,queueDepth:await a.depth()}),!h&&g(u.getSnapshot().signal))await v();return p.get(o)??{id:o,status:"pending"}}async function O(){h=!1;for(let e of await a.list())if(e.status==="paused-auth")await a.update({...e,status:"pending"});await v()}let P=u.subscribe((e)=>{if(!h&&g(e.signal))v()});return{mutate:C,drain:v,resume:O,dispose:P}}export{Y as createMutationEngine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function s(t){let o=(e)=>`queue:${e}`;async function r(){let e=await t.keys("queue:"),i=[];for(let n of e){let u=await t.get(n);if(u!==void 0)i.push(u)}return i.sort((n,u)=>n.enqueuedAt===u.enqueuedAt?n.id.localeCompare(u.id):n.enqueuedAt-u.enqueuedAt),i}return{enqueue:(e)=>t.set(o(e.id),e),update:(e)=>t.set(o(e.id),e),remove:(e)=>t.remove(o(e)),list:()=>r(),depth:async()=>(await t.keys("queue:")).length}}export{s as createMutationQueue};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function n(){return{read:async(e)=>{return},write:async()=>{},remove:async()=>{},entries:async()=>[],invalidate:async(e,a)=>[]}}export{n as createNoCacheStore};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createQueryKey as T}from"@lssm/lib.contracts-spec/query/key";var F=new Set(["online-only","memory-cache","offline-capable"]),O={"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 G(e){return typeof e==="string"&&F.has(e)}function D(e={}){let t=e.defaultMode===void 0?{mode:"online-only",source:"default",reason:"No local/offline policy input was supplied."}:b(e.defaultMode,"explicit")??{mode:"online-only",source:"explicit"},n=b(_(e.env),"env",{allowUnknown:!0});return b(e.flag,"feature-flag")??n??t}function _(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 b(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 o=O[e.trim().toLowerCase()];if(o!==void 0)return{mode:o,source:t};if(n.allowUnknown)return}if(typeof e==="object"&&e!==null&&"mode"in e){let o=e,l=typeof o.mode==="string"?O[o.mode.trim().toLowerCase()]:void 0;if(l!==void 0)return{mode:l,source:t,...typeof o.reason==="string"?{reason:o.reason}:{}};if(n.allowUnknown)return}throw Error(`Unsupported localOffline policy value: ${String(e)}`)}function S(e){return e!=="offline"}function C(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var H={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(e){let{serverData:t,localData:n}=e,o=e.mergeFields??[];if(typeof t!=="object"||t===null||typeof n!=="object"||n===null)return{outcome:"surface"};let l={...t},y=n;for(let u of o)if(u in y)l[u]=y[u];return{outcome:"merged",data:l}}function R(){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 j(t);case"surface":return{outcome:"surface"};default:{let n=t.policy.resolver,o=n===void 0?void 0:e.get(n);if(o===void 0)return{outcome:"surface"};return o(t)}}}}}function L(){let e=new Set;return{emit(t){for(let n of e)n(t)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}}function p(e,t){if(e?.staleAt===void 0)return!1;return t>=e.staleAt}function v(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function x(e,t){if(e===void 0)return{status:"loading",fetchStatus:"fetching",cacheStatus:"empty",isStale:!1,isOffline:!1};let n=p(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 A(e,t){let n=p(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 M(e,t,n){return{status:"error",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:p(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function Q(e,t,n){return{status:"auth-expired",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:p(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function P(e,t,n){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"conflict",isStale:p(e,n),isOffline:!1,conflict:t,versionToken:t.versionToken??e?.versionToken,dataUpdatedAt:e?.storedAt}}function I(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 ie(e,t){let{transport:n}=e,o=e.advanced??{},l=o.now??(()=>Date.now()),y=o.reachability??C("online"),u=o.events??L(),q=o.resolvers??R(),U=t??D({defaultMode:e.localOffline,env:e.env,flag:e.flag}),g=new Map,h=new Map,w=0;function f(i,r){g.set(i,r);for(let a of h.get(i)??[])a(r)}return{...{async query(i,r){let a=T(i),s=y.getSnapshot().signal;if(!S(s)){let m=I(void 0,l());return f(a,m),m}f(a,x(void 0,l()));let k=l(),c=await n.execute(i,r),d=l();if(!c.ok){u.emit({type:"fetch-error",key:a,code:c.code,latencyMs:d-k});let m=c.problem.category==="auth"?Q(c.problem,void 0,d):M(c.problem,void 0,d);return f(a,m),m}u.emit({type:"fetch-success",key:a,latencyMs:d-k});let N={key:a,data:c.data.data,versionToken:c.data.versionToken??c.versionToken,status:s==="restricted"?"warm":"fresh",tags:[],storedAt:d,...c.data.pageInfo===void 0?{}:{pageInfo:c.data.pageInfo}},E=A(N,d);return f(a,E),E},getState(i){return g.get(T(i))??v()},subscribe(i,r){let a=T(i),s=h.get(a);if(s===void 0)s=new Set,h.set(a,s);return s.add(r),()=>s?.delete(r)},async invalidateTags(){return[]},async refresh(i){let r=g.get(i)??v();return f(i,r),r},async emitConflict(i,r){let a=P(void 0,r,l());return f(i,a),a}},...{async mutate(i,r={}){w+=1;let a=`${l()}-${w}`;if(!S(y.getSnapshot().signal))return{id:a,status:"disabled-offline",problem:B()};let s=await n.execute({operation:i.operation,input:i.input,tags:i.tags},r.ctx??o.contextProvider?.());if(s.ok)return{id:a,status:"done",data:s.data.data};if(s.problem.category==="auth")return{id:a,status:"paused-auth",problem:s.problem};return{id:a,status:"failed",problem:s.problem}},async drain(){},async resume(){},dispose(){}},maintenance:{collectGarbage:async()=>0,revalidateStale:async()=>0},events:u,resolvers:q,localOffline:U}}function B(){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."}}export{ie as createOnlineOnlyDataEngine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function a(e){return e!=="offline"}function p(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var d={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 c(){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 u=(e)=>({operation:e.operation,input:e.input,tags:e.tags});function y(e){let t=e.now??(()=>Date.now()),o=0;async function r(s,l={}){o+=1;let n=`${t()}-${o}`;if(!a(e.reachability.getSnapshot().signal))return{id:n,status:"disabled-offline",problem:c()};let i=await e.transport.execute(u(s),l.ctx);if(i.ok)return{id:n,status:"done",data:i.data.data};return{id:n,status:"failed",problem:i.problem}}return{mutate:r,drain:async()=>{},resume:async()=>{},dispose:()=>{}}}export{y as createOnlineOnlyMutationEngine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{createQueryKey as w}from"@lssm/lib.contracts-spec/query/key";function Q(e){return e!=="offline"}function A(e="online"){let t={signal:e,since:Date.now()};return{getSnapshot:()=>t,subscribe:()=>()=>{}}}var G={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 P(){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((r)=>r.startsWith(t))},clear:async()=>{e.clear()}}}import{invalidationTagMatches as W}from"@lssm/lib.contracts-spec/query/invalidation";var I="cache:";function R(e){let t=new Map,n=(r)=>`${I}${r}`;return{read:(r)=>e.get(n(r)),write:async(r)=>{t.set(r.key,r.tags),await e.set(n(r.key),r)},remove:async(r)=>{t.delete(r),await e.remove(n(r))},entries:async()=>{let r=await e.keys(I),s=[];for(let g of r){let u=await e.get(g);if(u!==void 0)s.push(u)}return s},invalidate:async(r,s)=>{let g=[];for(let[u,h]of t.entries()){if(!h.some((T)=>r.some((l)=>W(T,l))))continue;let S=await e.get(n(u));if(S===void 0)continue;await e.set(n(u),{...S,status:"stale",staleAt:s}),g.push(u)}return g}}}function M(){let e=new Map;return{run(t,n){let r=e.get(t);if(r!==void 0)return r;let s=n().finally(()=>{e.delete(t)});return e.set(t,s),s},size(){return e.size}}}function O(){let e=new Set;return{emit(t){for(let n of e)n(t)},subscribe(t){return e.add(t),()=>{e.delete(t)}}}}function m(e,t){if(e?.staleAt===void 0)return!1;return t>=e.staleAt}function v(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function q(e,t){if(e===void 0)return{status:"loading",fetchStatus:"fetching",cacheStatus:"empty",isStale:!1,isOffline:!1};let n=m(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 b(e,t){let n=m(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 L(e,t,n){return{status:"error",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:m(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function V(e,t,n){return{status:"auth-expired",fetchStatus:"idle",data:t?.data,error:e,cacheStatus:t===void 0?"empty":t.status,isStale:m(t,n),isOffline:!1,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,errorUpdatedAt:n}}function U(e,t,n){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"conflict",isStale:m(e,n),isOffline:!1,conflict:t,versionToken:t.versionToken??e?.versionToken,dataUpdatedAt:e?.storedAt}}function N(e,t){return{status:e===void 0?"idle":"success",fetchStatus:"idle",data:e?.data,cacheStatus:"offline",isStale:m(e,t),isOffline:!0,versionToken:e?.versionToken,dataUpdatedAt:e?.storedAt,...e?.pageInfo===void 0?{}:{pageInfo:e.pageInfo}}}var X=0;function de(e){let{transport:t}=e,n=e.storage??P(),r=e.reachability??A("online"),s=e.now??(()=>Date.now()),g=e.defaultStaleTimeMs??X,u=e.cacheStore??R(n),h=e.events??O(),D=M(),S=new Map,T=new Map;function l(i,o){S.set(i,o);let a=T.get(i);if(a===void 0)return;for(let c of a)c(o)}async function K(i,o){let a=w(i),c=r.getSnapshot().signal,y=await u.read(a);if(!Q(c)){let p=N(y,s());return l(a,p),p}if(y!==void 0&&!m(y,s())){h.emit({type:"cache-hit",key:a});let p=b(y,s());return l(a,p),p}h.emit({type:"cache-miss",key:a}),l(a,q(y,s()));let k=s(),d=await D.run(a,()=>t.execute(i,o)),f=s();if(!d.ok){h.emit({type:"fetch-error",key:a,code:d.code,latencyMs:f-k});let p=d.problem.category==="auth"?V(d.problem,y,f):L(d.problem,y,f);return l(a,p),p}h.emit({type:"fetch-success",key:a,latencyMs:f-k});let H=i.cache?.staleTimeMs??g,E=i.cache?.cacheTimeMs,C={key:a,data:d.data.data,versionToken:d.data.versionToken??d.versionToken,status:c==="restricted"?"warm":"fresh",tags:i.tags??[],storedAt:f,staleAt:f+H,envelope:i,...E===void 0?{}:{expiresAt:f+E},...d.data.pageInfo===void 0?{}:{pageInfo:d.data.pageInfo}};await u.write(C);let x=b(C,f);return l(a,x),x}async function _(i){let o=await u.read(i),a=o===void 0?v():b(o,s());return l(i,a),a}async function z(i,o){let a=await u.read(i),c=U(a,o,s());return l(i,c),c}function B(i){return S.get(w(i))??v()}function F(i,o){let a=w(i),c=T.get(a);if(c===void 0)c=new Set,T.set(a,c);return c.add(o),()=>{T.get(a)?.delete(o)}}async function j(i){let o=await u.invalidate(i,s());for(let a of o){let c=await u.read(a);if(c!==void 0)l(a,b(c,s()))}return o}return{query:K,getState:B,subscribe:F,invalidateTags:j,refresh:_,emitConflict:z}}export{de as createQueryEngine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function s(t,e){if(t?.staleAt===void 0)return!1;return e>=t.staleAt}function n(){return{status:"idle",fetchStatus:"idle",cacheStatus:"empty",isStale:!1,isOffline:!1}}function u(t,e){if(t===void 0)return{status:"loading",fetchStatus:"fetching",cacheStatus:"empty",isStale:!1,isOffline:!1};let a=s(t,e);return{status:"success",fetchStatus:"fetching",data:t.data,cacheStatus:a?"stale":t.status,isStale:a,isOffline:!1,versionToken:t.versionToken,dataUpdatedAt:t.storedAt,...t.pageInfo===void 0?{}:{pageInfo:t.pageInfo}}}function o(t,e){let a=s(t,e);return{status:"success",fetchStatus:"idle",data:t.data,cacheStatus:a?"stale":t.status,isStale:a,isOffline:!1,versionToken:t.versionToken,dataUpdatedAt:t.storedAt,...t.pageInfo===void 0?{}:{pageInfo:t.pageInfo}}}function i(t,e,a){return{status:"error",fetchStatus:"idle",data:e?.data,error:t,cacheStatus:e===void 0?"empty":e.status,isStale:s(e,a),isOffline:!1,versionToken:e?.versionToken,dataUpdatedAt:e?.storedAt,errorUpdatedAt:a}}function d(t,e,a){return{status:"auth-expired",fetchStatus:"idle",data:e?.data,error:t,cacheStatus:e===void 0?"empty":e.status,isStale:s(e,a),isOffline:!1,versionToken:e?.versionToken,dataUpdatedAt:e?.storedAt,errorUpdatedAt:a}}function f(t,e,a){return{status:t===void 0?"idle":"success",fetchStatus:"idle",data:t?.data,cacheStatus:"conflict",isStale:s(t,a),isOffline:!1,conflict:e,versionToken:e.versionToken??t?.versionToken,dataUpdatedAt:t?.storedAt}}function r(t,e){return{status:t===void 0?"idle":"success",fetchStatus:"idle",data:t?.data,cacheStatus:"offline",isStale:s(t,e),isOffline:!0,versionToken:t?.versionToken,dataUpdatedAt:t?.storedAt,...t?.pageInfo===void 0?{}:{pageInfo:t.pageInfo}}}export{d as authExpiredState,s as computeIsStale,f as conflictState,i as errorState,n as idleState,u as loadingState,r as offlineState,o as successState};
|
|
File without changes
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{flattenPortableFormFields as p}from"@lssm/lib.contracts-spec/forms";function u(e){let r=new Set(e.questions.map(({name:o})=>o)),l=e.questions.map(c),s=new Set(e.formSpec?.policy?.pii??[]),m=p(e.formSpec?.fields??[]),a=Object.entries(e.formSpec?.modelFields??{}).flatMap(([o,i])=>{if(r.has(o))return[];let t=m.find((n)=>n.name===o);return[{id:o,labelKey:t?.labelI18n??o,sensitive:s.has(o)||[...s].some((n)=>n.startsWith(`${o}.`))||t?.kind==="email"||t?.kind==="phone"||i.type==="email"||i.type==="phone"}]});return[...l,...a]}function c(e){return{id:e.id,labelKey:e.labelKey,sensitive:e.sensitive===!0||e.kind==="email"||e.kind==="phone"}}export{u as formCsvColumns};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{flattenPortableFormFields as v}from"@lssm/lib.contracts-spec/forms";function u(e){let o=new Set(e.questions.map(({name:t})=>t)),n=e.questions.map(y),r=new Set(e.formSpec?.policy?.pii??[]),i=v(e.formSpec?.fields??[]),m=Object.entries(e.formSpec?.modelFields??{}).flatMap(([t,c])=>{if(o.has(t))return[];let s=i.find((p)=>p.name===t);return[{id:t,labelKey:s?.labelI18n??t,sensitive:r.has(t)||[...r].some((p)=>p.startsWith(`${t}.`))||s?.kind==="email"||s?.kind==="phone"||c.type==="email"||c.type==="phone"}]});return[...n,...m]}function y(e){return{id:e.id,labelKey:e.labelKey,sensitive:e.sensitive===!0||e.kind==="email"||e.kind==="phone"}}import{compileFormDocument as g,validateFormDocument as R}from"@lssm/lib.contracts-spec/forms";class a extends Error{code;constructor(e,o){super(o);this.code=e;this.name="FormsPlatformError"}}function O(e,o){let n=e?.owner.product===o.product&&e.owner.tenantRef===o.tenantRef&&e.owner.workspaceRef===o.workspaceRef&&e.owner.ownerRef===o.ownerRef;if(!e||!n)throw new a("FORM_NOT_FOUND","Form definition was not found in the caller scope");return e}function P(e){let o=R(e);if(o.length)throw new a("INVALID_FORM_DOCUMENT",o.map((n)=>n.message).join("; "));g(e)}function b(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 x(e,o){if(b(o,e.verification.requirement))return"completed";return e.verification.unverifiedDisposition==="pending-hidden"?"pending-verification":"completed-unverified"}function f(e,o){let n=new Set(e.content.enabledLocales.map((r)=>r.toLowerCase()));for(let r of o){if(!r)continue;let i=r.trim().toLowerCase(),m=e.content.enabledLocales.find((s)=>s.toLowerCase()===i);if(n.has(i)&&m)return m;let t=i.split("-")[0],c=e.content.enabledLocales.find((s)=>s.toLowerCase().split("-")[0]===t);if(c)return c}return e.content.defaultLocale}var D="response_extensions:AI follow-ups";function w(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 l(e){return`${e.map(w).join(",")}\r
|
|
2
|
+
`}function d(e,o,n){if(o.revisionId!==e.id)throw Error("CSV responses must belong to the exported revision");let r=u(e.document),i=new Set(r.map(({id:t})=>t)),m=Object.fromEntries(Object.entries(o.answers).filter(([t])=>!i.has(t)));return[o.id,o.status,o.locale,o.submittedAt??"",...r.map((t)=>{if(!(n.includeSensitive&&n.sensitiveGrant?.authorized)&&t.sensitive)return"[REDACTED]";return o.answers[t.id]}),n.includeSensitive&&n.sensitiveGrant?.authorized?m:"[REDACTED]"]}function F(e,o){let n=f(e.document,[o]),r=e.document.content.catalogs[n]??{};return["response_id","status","locale","submitted_at",...u(e.document).map((i)=>`${i.id}:${r[i.labelKey]??i.labelKey}`),D]}function _(e,o,n={}){return`\uFEFF${l(F(e,n.locale))}${o.map((r)=>l(d(e,r,n))).join("")}`}async function*N(e,o,n={}){yield`\uFEFF${l(F(e,n.locale))}`;for await(let r of o)yield l(d(e,r,n))}export{_ as serializeFormResponsesCsv,N as streamFormResponsesCsv};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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 @@
|
|
|
1
|
+
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 @@
|
|
|
1
|
+
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 @@
|
|
|
1
|
+
class i extends Error{code;constructor(o,e){super(e);this.code=o;this.name="FormsPlatformError"}}export{i as FormsPlatformError};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
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,2 @@
|
|
|
1
|
+
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
|
|
2
|
+
`}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};
|