@volter/world-core 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (180) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +29 -0
  3. package/app-route.cjs +154 -0
  4. package/app-route.d.cts +7 -0
  5. package/attach.cjs +80 -0
  6. package/dist/app-route.cjs +154 -0
  7. package/dist/app-route.d.cts +7 -0
  8. package/dist/attach.cjs +80 -0
  9. package/dist/generated/pack-facts.json +4306 -0
  10. package/dist/inject.cjs +1097 -0
  11. package/dist/network-policy.cjs +92 -0
  12. package/dist/network-policy.d.cts +10 -0
  13. package/dist/src/actions.d.ts +276 -0
  14. package/dist/src/actions.js +436 -0
  15. package/dist/src/ancestry.d.ts +22 -0
  16. package/dist/src/ancestry.js +238 -0
  17. package/dist/src/args.d.ts +3 -0
  18. package/dist/src/args.js +12 -0
  19. package/dist/src/blob-store.d.ts +55 -0
  20. package/dist/src/blob-store.js +186 -0
  21. package/dist/src/brand-tokens.d.ts +2 -0
  22. package/dist/src/brand-tokens.js +17 -0
  23. package/dist/src/changeset.d.ts +431 -0
  24. package/dist/src/changeset.js +0 -0
  25. package/dist/src/client-bundle.d.ts +1 -0
  26. package/dist/src/client-bundle.js +28 -0
  27. package/dist/src/credential.d.ts +38 -0
  28. package/dist/src/credential.js +114 -0
  29. package/dist/src/derived-core.d.ts +452 -0
  30. package/dist/src/derived-core.js +782 -0
  31. package/dist/src/derived.d.ts +84 -0
  32. package/dist/src/derived.js +122 -0
  33. package/dist/src/emit.d.ts +106 -0
  34. package/dist/src/emit.js +157 -0
  35. package/dist/src/executor.d.ts +120 -0
  36. package/dist/src/executor.js +387 -0
  37. package/dist/src/file-response.d.ts +3 -0
  38. package/dist/src/file-response.js +22 -0
  39. package/dist/src/fork.d.ts +26 -0
  40. package/dist/src/fork.js +68 -0
  41. package/dist/src/git/history.d.ts +36 -0
  42. package/dist/src/git/history.js +298 -0
  43. package/dist/src/git/index.d.ts +6 -0
  44. package/dist/src/git/index.js +6 -0
  45. package/dist/src/git/inflate.d.ts +11 -0
  46. package/dist/src/git/inflate.js +194 -0
  47. package/dist/src/git/objects.d.ts +64 -0
  48. package/dist/src/git/objects.js +161 -0
  49. package/dist/src/git/pack.d.ts +14 -0
  50. package/dist/src/git/pack.js +199 -0
  51. package/dist/src/git/refs.d.ts +19 -0
  52. package/dist/src/git/refs.js +35 -0
  53. package/dist/src/git/smart-http.d.ts +45 -0
  54. package/dist/src/git/smart-http.js +223 -0
  55. package/dist/src/hash.d.ts +38 -0
  56. package/dist/src/hash.js +48 -0
  57. package/dist/src/head.d.ts +140 -0
  58. package/dist/src/head.js +313 -0
  59. package/dist/src/history.d.ts +76 -0
  60. package/dist/src/history.js +322 -0
  61. package/dist/src/index.d.ts +73 -0
  62. package/dist/src/index.js +98 -0
  63. package/dist/src/lifecycle.d.ts +1 -0
  64. package/dist/src/lifecycle.js +8 -0
  65. package/dist/src/log.d.ts +254 -0
  66. package/dist/src/log.js +801 -0
  67. package/dist/src/mirror-shell.d.ts +2 -0
  68. package/dist/src/mirror-shell.js +13 -0
  69. package/dist/src/observe.d.ts +49 -0
  70. package/dist/src/observe.js +148 -0
  71. package/dist/src/pack-assets.d.ts +30 -0
  72. package/dist/src/pack-assets.js +88 -0
  73. package/dist/src/packRegistry.d.ts +374 -0
  74. package/dist/src/packRegistry.js +142 -0
  75. package/dist/src/placeholder-remote.d.ts +22 -0
  76. package/dist/src/placeholder-remote.js +86 -0
  77. package/dist/src/proxy.d.ts +25 -0
  78. package/dist/src/proxy.js +155 -0
  79. package/dist/src/rateBudget.d.ts +367 -0
  80. package/dist/src/rateBudget.js +925 -0
  81. package/dist/src/references.d.ts +18 -0
  82. package/dist/src/references.js +27 -0
  83. package/dist/src/remote-execute.d.ts +22 -0
  84. package/dist/src/remote-execute.js +1 -0
  85. package/dist/src/resource-blob.d.ts +10 -0
  86. package/dist/src/resource-blob.js +56 -0
  87. package/dist/src/scenario.d.ts +197 -0
  88. package/dist/src/scenario.js +425 -0
  89. package/dist/src/schemas.d.ts +78 -0
  90. package/dist/src/schemas.js +50 -0
  91. package/dist/src/serve-http.d.ts +48 -0
  92. package/dist/src/serve-http.js +340 -0
  93. package/dist/src/serve.d.ts +147 -0
  94. package/dist/src/serve.js +507 -0
  95. package/dist/src/shared-blob-index.d.ts +4 -0
  96. package/dist/src/shared-blob-index.js +126 -0
  97. package/dist/src/state-system.d.ts +70 -0
  98. package/dist/src/state-system.js +90 -0
  99. package/dist/src/storage.d.ts +101 -0
  100. package/dist/src/storage.js +337 -0
  101. package/dist/src/twin-fetch.d.ts +64 -0
  102. package/dist/src/twin-fetch.js +91 -0
  103. package/dist/src/types.d.ts +40 -0
  104. package/dist/src/types.js +1 -0
  105. package/dist/src/v1-removed.d.ts +159 -0
  106. package/dist/src/v1-removed.js +124 -0
  107. package/dist/src/volter-home.d.ts +5 -0
  108. package/dist/src/volter-home.js +10 -0
  109. package/dist/src/world-clock.d.ts +4 -0
  110. package/dist/src/world-clock.js +32 -0
  111. package/dist/src/world-env.d.ts +3 -0
  112. package/dist/src/world-env.js +22 -0
  113. package/dist/src/world-store-sql.d.ts +27 -0
  114. package/dist/src/world-store-sql.js +86 -0
  115. package/dist/src/world-store.d.ts +168 -0
  116. package/dist/src/world-store.js +475 -0
  117. package/dist/src/worldConfig.d.ts +9 -0
  118. package/dist/src/worldConfig.js +17 -0
  119. package/dist/stream-bridge.cjs +80 -0
  120. package/dist/vendor-hosts.cjs +200 -0
  121. package/generated/pack-facts.json +4306 -0
  122. package/inject.cjs +1097 -0
  123. package/network-policy.cjs +92 -0
  124. package/network-policy.d.cts +10 -0
  125. package/package.json +103 -0
  126. package/src/actions.ts +564 -0
  127. package/src/ancestry.ts +213 -0
  128. package/src/args.ts +14 -0
  129. package/src/blob-store.ts +185 -0
  130. package/src/brand-tokens.ts +17 -0
  131. package/src/changeset.ts +1032 -0
  132. package/src/client-bundle.ts +29 -0
  133. package/src/credential.ts +140 -0
  134. package/src/derived-core.ts +1004 -0
  135. package/src/derived.ts +176 -0
  136. package/src/emit.ts +242 -0
  137. package/src/executor.ts +431 -0
  138. package/src/file-response.ts +22 -0
  139. package/src/fork.ts +89 -0
  140. package/src/git/history.ts +177 -0
  141. package/src/git/index.ts +6 -0
  142. package/src/git/inflate.ts +125 -0
  143. package/src/git/objects.ts +110 -0
  144. package/src/git/pack.ts +105 -0
  145. package/src/git/refs.ts +25 -0
  146. package/src/git/smart-http.ts +149 -0
  147. package/src/hash.ts +66 -0
  148. package/src/head.ts +318 -0
  149. package/src/history.ts +246 -0
  150. package/src/index.ts +323 -0
  151. package/src/lifecycle.ts +8 -0
  152. package/src/log.ts +793 -0
  153. package/src/mirror-shell.ts +15 -0
  154. package/src/observe.ts +130 -0
  155. package/src/pack-assets.ts +81 -0
  156. package/src/packRegistry.ts +408 -0
  157. package/src/placeholder-remote.ts +81 -0
  158. package/src/proxy.ts +183 -0
  159. package/src/rateBudget.ts +1115 -0
  160. package/src/references.ts +46 -0
  161. package/src/remote-execute.ts +26 -0
  162. package/src/resource-blob.ts +57 -0
  163. package/src/scenario.ts +479 -0
  164. package/src/schemas.ts +56 -0
  165. package/src/serve-http.ts +299 -0
  166. package/src/serve.ts +618 -0
  167. package/src/shared-blob-index.ts +108 -0
  168. package/src/state-system.ts +115 -0
  169. package/src/storage.ts +407 -0
  170. package/src/twin-fetch.ts +147 -0
  171. package/src/types.ts +50 -0
  172. package/src/v1-removed.ts +172 -0
  173. package/src/volter-home.ts +11 -0
  174. package/src/world-clock.ts +33 -0
  175. package/src/world-env.ts +18 -0
  176. package/src/world-store-sql.ts +118 -0
  177. package/src/world-store.ts +572 -0
  178. package/src/worldConfig.ts +27 -0
  179. package/stream-bridge.cjs +80 -0
  180. package/vendor-hosts.cjs +200 -0
@@ -0,0 +1,92 @@
1
+ 'use strict';
2
+
3
+ const WORLD_NETWORK_POLICY_ENV = 'VOLTER_WORLD_NETWORK_POLICY';
4
+
5
+ function object(value, keys, label) {
6
+ if (!value || typeof value !== 'object' || Array.isArray(value)
7
+ || Object.keys(value).some(key => !keys.includes(key))) {
8
+ throw new Error(`${label} has an invalid shape`);
9
+ }
10
+ return value;
11
+ }
12
+
13
+ function origins(value) {
14
+ if (!Array.isArray(value) || value.length > 256) throw new Error('World network egress must be an array of at most 256 exact HTTPS origins');
15
+ const normalized = value.map(origin => {
16
+ if (typeof origin !== 'string' || origin.length > 2048) throw new Error('World network egress origins must be strings');
17
+ let url;
18
+ try { url = new URL(origin); } catch { throw new Error(`Invalid World network origin: ${origin}`); }
19
+ if (url.protocol !== 'https:' || url.origin !== origin || url.username || url.password
20
+ || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/.test(url.hostname)
21
+ || url.hostname.split('.').some(label => !label || label.length > 63 || label.startsWith('-') || label.endsWith('-'))
22
+ || /^[\d.]+$/.test(url.hostname) || url.hostname === 'localhost' || url.hostname.endsWith('.localhost')) {
23
+ throw new Error(`World network requires an exact canonical HTTPS origin: ${origin}`);
24
+ }
25
+ return origin;
26
+ });
27
+ return Object.freeze([...new Set(normalized)].sort());
28
+ }
29
+
30
+ function normalizeWorldNetwork(value) {
31
+ const network = object(value, ['egress'], 'World network');
32
+ return Object.freeze({ egress: origins(network.egress) });
33
+ }
34
+
35
+ function createWorldNetworkPolicy(world, network) {
36
+ if (typeof world !== 'string' || !world || world.length > 256) throw new Error('World network policy requires its World identity');
37
+ return Object.freeze({ version: 1, world, egress: normalizeWorldNetwork(network).egress });
38
+ }
39
+
40
+ function parseWorldNetworkPolicy(value) {
41
+ const policy = object(value, ['version', 'world', 'egress'], 'World network policy');
42
+ if (policy.version !== 1) throw new Error('Unsupported World network policy version');
43
+ return createWorldNetworkPolicy(policy.world, { egress: policy.egress });
44
+ }
45
+
46
+ function worldNetworkPolicyFromEnv(env) {
47
+ const serialized = env[WORLD_NETWORK_POLICY_ENV];
48
+ if (serialized === undefined) return undefined;
49
+ if (typeof serialized !== 'string' || serialized.length > 65536) throw new Error('Invalid published World network policy');
50
+ const policy = parseWorldNetworkPolicy(JSON.parse(serialized));
51
+ if (!env.VOLTER_WORLD || policy.world !== env.VOLTER_WORLD) throw new Error('World network policy does not belong to the attached World');
52
+ return policy;
53
+ }
54
+
55
+ function allowsWorldNetworkEgress(policy, value) {
56
+ let url;
57
+ try { url = new URL(String(value)); } catch { return false; }
58
+ return url.protocol === 'https:' && !url.username && !url.password && policy.egress.includes(url.origin);
59
+ }
60
+
61
+ /** Loopback and private addresses stay inside the machine a World runs on: never refused. */
62
+ function isWorldInternalHost(hostname) {
63
+ const h = String(hostname || '').toLowerCase().replace(/^\[|\]$/g, '');
64
+ if (!h) return true;
65
+ if (h === 'localhost' || h.endsWith('.localhost') || h.endsWith('.test') || h === '0.0.0.0' || h === '::1') return true;
66
+ const v4 = /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/.exec(h);
67
+ if (v4) {
68
+ const a = Number(v4[1]); const b = Number(v4[2]);
69
+ return a === 127 || a === 10 || (a === 172 && b >= 16 && b <= 31) || (a === 192 && b === 168) || (a === 169 && b === 254);
70
+ }
71
+ return h.includes(':') && (h.startsWith('fc') || h.startsWith('fd') || h.startsWith('fe80:'));
72
+ }
73
+
74
+ /**
75
+ * Why this World refuses an outbound request to `value`, or null when it allows it. The one rule
76
+ * every enforcement point applies: the injector for application processes, and a twin's own
77
+ * World-internal outbound calls (a queue delivering to its destination), which run in a host the
78
+ * injector does not attach to. Internal addresses pass; strict egress refuses everything else; a
79
+ * published network policy refuses what it does not list. An unreadable policy refuses.
80
+ */
81
+ function worldEgressRefusal(value, env = process.env) {
82
+ let url;
83
+ try { url = new URL(String(value)); } catch { return `unparseable destination ${String(value)}`; }
84
+ if (isWorldInternalHost(url.hostname)) return null;
85
+ if (env.VOLTER_TWIN_STRICT_EGRESS === '1' || env.VOLTER_TWIN_STRICT_EGRESS === 'true') return `sealed World refuses untwinned host ${url.hostname}`;
86
+ let policy;
87
+ try { policy = worldNetworkPolicyFromEnv(env); } catch (error) { return `World network policy unreadable (${error.message}); refusing ${url.hostname}`; }
88
+ if (policy !== undefined && !allowsWorldNetworkEgress(policy, url)) return `World network policy does not allow ${url.origin}`;
89
+ return null;
90
+ }
91
+
92
+ module.exports = { isWorldInternalHost, worldEgressRefusal, WORLD_NETWORK_POLICY_ENV, normalizeWorldNetwork, createWorldNetworkPolicy, parseWorldNetworkPolicy, worldNetworkPolicyFromEnv, allowsWorldNetworkEgress };
@@ -0,0 +1,10 @@
1
+ export interface WorldNetwork { readonly egress: readonly string[] }
2
+ export interface WorldNetworkPolicy extends WorldNetwork { readonly version: 1; readonly world: string }
3
+ export const WORLD_NETWORK_POLICY_ENV: 'VOLTER_WORLD_NETWORK_POLICY';
4
+ export function normalizeWorldNetwork(value: unknown): WorldNetwork;
5
+ export function createWorldNetworkPolicy(world: string, network: WorldNetwork): WorldNetworkPolicy;
6
+ export function parseWorldNetworkPolicy(value: unknown): WorldNetworkPolicy;
7
+ export function worldNetworkPolicyFromEnv(env: Readonly<Record<string, string | undefined>>): WorldNetworkPolicy | undefined;
8
+ export function allowsWorldNetworkEgress(policy: WorldNetworkPolicy, value: string | URL): boolean;
9
+ export function isWorldInternalHost(hostname: string): boolean;
10
+ export function worldEgressRefusal(value: string | URL, env?: Readonly<Record<string, string | undefined>>): string | null;
@@ -0,0 +1,276 @@
1
+ import type { SubjectFields } from './hash.js';
2
+ import { type Receipt } from './log.js';
3
+ import type { TwinResource } from './serve.js';
4
+ export type TwinActionOp = 'set' | 'revert';
5
+ export type TwinActionPreconditionOp = 'exists' | 'not_exists' | 'eq' | 'neq' | 'version_eq';
6
+ export type TwinActionPrecondition = {
7
+ subject: {
8
+ type: string;
9
+ id: string;
10
+ };
11
+ field: string;
12
+ op: TwinActionPreconditionOp;
13
+ value?: unknown;
14
+ };
15
+ export type TwinActionRevertSpec = {
16
+ strategy: 'inverse' | 'suppress' | 'compensating-action';
17
+ operation?: string;
18
+ fields?: SubjectFields;
19
+ };
20
+ export type ProjectedResource = {
21
+ type: string;
22
+ id: string;
23
+ fields: SubjectFields;
24
+ };
25
+ export type ProjectedResourcePatch = {
26
+ type: string;
27
+ id: string;
28
+ fields: SubjectFields;
29
+ };
30
+ export type ProjectedResourceRef = {
31
+ type: string;
32
+ id: string;
33
+ };
34
+ export type ProjectedDelivery = {
35
+ kind: 'webhook' | 'event' | 'notification';
36
+ target: string;
37
+ payload: Record<string, unknown>;
38
+ };
39
+ export type ActionProjection = {
40
+ creates?: ProjectedResource[];
41
+ updates?: ProjectedResourcePatch[];
42
+ deletes?: ProjectedResourceRef[];
43
+ emits?: ProjectedDelivery[];
44
+ };
45
+ export type TwinAction = {
46
+ id: string;
47
+ service: string;
48
+ op: TwinActionOp;
49
+ subject: {
50
+ type: string;
51
+ id: string;
52
+ };
53
+ occurredAt: string;
54
+ actor?: {
55
+ kind: 'agent' | 'human' | 'bot' | 'system';
56
+ id?: string;
57
+ };
58
+ /** Optional vendor operation name, e.g. `issue.update` or `message.send`. */
59
+ operation?: string;
60
+ /** The caller's own request key, when it asked for at-most-once. Provenance, AND the marker that
61
+ * makes replay identity ignore `occurredAt`: the whole point of a request key is that a retry
62
+ * arriving at a different millisecond is the SAME request, so comparing the two bodies must not
63
+ * fail on the timestamp. Without this, a caller that did not pin `occurredAt` got a thrown
64
+ * "Conflicting duplicate twin action" — a 500 where it had asked for idempotence. */
65
+ idempotencyKey?: string;
66
+ /** Raw operation input (provenance); not projected — `fields`/`projection` carry the state change. */
67
+ input?: Record<string, unknown>;
68
+ /** Preconditions are evaluated against the current projected twin state before append. */
69
+ preconditions?: TwinActionPrecondition[];
70
+ fields?: SubjectFields;
71
+ projection?: ActionProjection;
72
+ revertsActionId?: string;
73
+ /** A pending row a snapshot import planted (contract "A snapshot import never plants a push"):
74
+ * still local state the projection serves, never awaiting push until released on purpose. */
75
+ quarantined?: {
76
+ at: string;
77
+ by: string;
78
+ };
79
+ /** Optional machine-readable hint for how a UI/pack should construct a revert. */
80
+ revert?: TwinActionRevertSpec;
81
+ /**
82
+ * Request-scoped correlation id (D3 — the purpose-3 audit trail: "who reviewed the
83
+ * change that caused this real write"). Always present on an appended action —
84
+ * appendAction/appendActionIfAbsent generate one when the caller doesn't supply it —
85
+ * and threaded through to the push-ledger row(s) a push against this action produces
86
+ * (see pushLedger.ts), so an action row and its push-ledger row(s) join on this id
87
+ * alone, with no dependence on actionId/pushId naming conventions.
88
+ */
89
+ correlationId?: string;
90
+ };
91
+ export type TwinTransactionCommit = TwinAction;
92
+ export type TwinTransactionCommitOp = TwinActionOp;
93
+ export type TwinTransactionPrecondition = TwinActionPrecondition;
94
+ export type TwinTransactionRevertSpec = TwinActionRevertSpec;
95
+ export declare class TwinActionPreconditionError extends Error {
96
+ readonly actionId: string;
97
+ readonly failed: TwinActionPrecondition;
98
+ constructor(actionId: string, failed: TwinActionPrecondition);
99
+ }
100
+ /** Subjects a `set` action touches: its own subject, every projection resource, and every
101
+ * precondition subject — a precondition established the action's validity against that
102
+ * subject's state, so remote movement there is drift for this action too. */
103
+ export declare function touchedSubjects(action: TwinAction): Array<{
104
+ type: string;
105
+ id: string;
106
+ }>;
107
+ export declare function observeAppends(observer: ((action: TwinAction, root: string | undefined) => void) | undefined): void;
108
+ export declare function runWithCorrelationId<T>(id: string, fn: () => T): T;
109
+ export declare function currentCorrelationId(): string | undefined;
110
+ export declare function appendAction(action: TwinAction, root?: string): TwinAction;
111
+ /** Append `action` only if no exact action with the same id already exists — the whole
112
+ * replay/precondition/append decision runs under the service projection + actions locks, so it's
113
+ * atomic across processes (two concurrent identical writes converge to ONE action; distinct
114
+ * writes both land). A reused id with different content fails loudly. */
115
+ export declare function appendActionIfAbsent(action: TwinAction, root?: string): {
116
+ action: TwinAction;
117
+ appended: boolean;
118
+ placeholder?: true;
119
+ };
120
+ /**
121
+ * Append `build(n)` as an OCCURRENCE: the nth time this exact write has been made.
122
+ *
123
+ * A local vendor write is an occurrence, not a replay — two calls are two actions, even
124
+ * byte-identical in the same instant (docs/contributing/adding-a-twin.md#5-build-on-the-shared-kernel--dont-reinvent,
125
+ * "A local write is an occurrence"). `appendActionIfAbsent` cannot express that: its identity is content, and
126
+ * content provably cannot separate "the same request delivered twice" from "the same change made
127
+ * twice". So a caller wanting at-most-once passes an explicit key and uses that function; a caller
128
+ * recording what actually happened uses this one.
129
+ *
130
+ * The ordinal keeps every EXISTING action id byte-identical: occurrence 0 is `build(0)`'s own id,
131
+ * and only a genuine repeat becomes `<id>#1`, `#2`. It is derived under the same projection +
132
+ * actions locks as the append, so two processes racing take different ordinals rather than
133
+ * colliding, and it is deterministic — replaying one sequence of writes onto a fresh root yields
134
+ * the same ordinals, which is what serve-path determinism requires.
135
+ */
136
+ export declare function appendActionOccurrence(base: TwinAction, root?: string): {
137
+ action: TwinAction;
138
+ appended: boolean;
139
+ placeholder?: true;
140
+ };
141
+ /** Occurrence 0 IS the base id — so nothing that exists today changes shape. The appender owns
142
+ * this math; a caller passing an already-ordinalled id would stack them (`…#1#1`). */
143
+ export declare function occurrenceId(base: string, ordinal: number): string;
144
+ export type AtomicActionDecision<T> = {
145
+ kind: 'skip';
146
+ value: T;
147
+ } | {
148
+ kind: 'append';
149
+ action: TwinAction;
150
+ value: T;
151
+ /** `'occurrence'` (default) appends every call, with the ordinal and the first occurrence's
152
+ * merge base. `'caller'` is at-most-once on the caller's own identity — what an
153
+ * `idempotencyKey` or `actionId` means. It rides on the DECISION rather than on the
154
+ * function's arguments because only the callback knows which write it chose. */
155
+ identity?: 'occurrence' | 'caller';
156
+ };
157
+ /**
158
+ * Evaluate current projected state and optionally append one action under ONE service projection
159
+ * transaction (the shared projection lock plus the action-log lock). This is the narrow
160
+ * state-dependent seam for vendor operations whose acceptance and resulting fields depend on the
161
+ * latest observed + local projection (for example, immutable version publish).
162
+ *
163
+ * The callback must remain synchronous and side-effect free: it computes a decision from the
164
+ * supplied snapshot. Durable state changes only through the returned action, which this helper
165
+ * appends before releasing the lock.
166
+ */
167
+ export declare function decideAndAppendAction<T>(service: string, decide: (resources: TwinResource[]) => AtomicActionDecision<T>, root?: string): {
168
+ value: T;
169
+ action?: TwinAction;
170
+ appended: boolean;
171
+ placeholder?: true;
172
+ };
173
+ export declare function appendTransactionCommit(commit: TwinTransactionCommit, root?: string): TwinTransactionCommit;
174
+ export declare function listActions(service: string, root?: string): TwinAction[];
175
+ /** The kernel's ONE deterministic check: does `actual` satisfy the precondition expression?
176
+ * Shared by write-time preconditions (here), plan conflict detection (plan.ts) and changeset
177
+ * verifiers (changeset.ts) — one evaluator, so a check means the same thing everywhere. */
178
+ export declare function checkPrecondition(precondition: TwinActionPrecondition, actual: unknown): boolean;
179
+ /** Look a precondition subject's field up in projected resources (undefined = no resource or
180
+ * no field — exactly what `exists`/`not_exists` distinguish). */
181
+ export declare function projectedPreconditionValue(precondition: TwinActionPrecondition, resources: TwinResource[]): unknown;
182
+ /**
183
+ * Project the action log over the observed mirror → current twin resources.
184
+ * `set` actions overlay fields (creating subjects that don't exist in the mirror);
185
+ * reverted and confirmed actions are skipped (confirmed facts come from the
186
+ * observed log instead, so they are not projected twice).
187
+ */
188
+ /** THE TREE (log.ts): what a read sees — the nearest checkpoint plus the entries since; the parent's
189
+ * entries, then this branch's, skipping any the parent already holds. `until` folds only the
190
+ * branch entries BEFORE that id — the state a write saw at its own append (the rebase's
191
+ * evaluation point). */
192
+ export declare function projectResources(service: string, root?: string, opts?: {
193
+ until?: string;
194
+ }): TwinResource[];
195
+ /** The local → vendor id aliases the landed copies carry: a read by the id a caller was handed before
196
+ * its write was performed resolves to the row the vendor now owns. */
197
+ export declare function subjectAliases(service: string, root?: string): Map<string, string>;
198
+ /** Resolve a subject id through the aliases: the vendor's id when a confirm rebound it, else the id itself. */
199
+ export declare function resolveSubjectId(service: string, type: string, id: string, root?: string): string;
200
+ /**
201
+ * Confirm a local action after it was pushed to the real vendor (R18): record the
202
+ * confirmed fields as an OBSERVED event (origin 'external' — it's now real) and
203
+ * append a `confirm` action mapping the local action → that observed event id.
204
+ * Projection then drops the local action (the fact lives in the observed log), so
205
+ * the change is counted exactly once. Returns the observed event id.
206
+ */
207
+ export declare function confirmAction(opts: {
208
+ service: string;
209
+ actionId: string;
210
+ subject: {
211
+ type: string;
212
+ id: string;
213
+ };
214
+ fields: SubjectFields;
215
+ /** Additional observed resources landed by the same compound action, under the same lock. */
216
+ additionalObservations?: Array<{
217
+ subject: {
218
+ type: string;
219
+ id: string;
220
+ };
221
+ fields: SubjectFields;
222
+ }>;
223
+ /** The subject id the VENDOR minted for this write (the push outcome's externalId). When it
224
+ * differs from the local id, the landed copy carries the vendor's id and `aliasOf` names the
225
+ * local one, so a read by either id finds the row. */
226
+ vendorSubjectId?: string;
227
+ /** the receipt on the landed copy; `deployed` when omitted (the push performed it) */
228
+ receipt?: Partial<Receipt>;
229
+ occurredAt: string;
230
+ root?: string;
231
+ }): {
232
+ observedEventId: string;
233
+ observedEventIds: string[];
234
+ };
235
+ export type RevertOutcome = {
236
+ status: 'reverted';
237
+ revertId: string;
238
+ } | {
239
+ status: 'already-reverted';
240
+ } | {
241
+ status: 'confirmed';
242
+ } | {
243
+ status: 'not-found';
244
+ } | {
245
+ status: 'not-revertable';
246
+ op: TwinActionOp;
247
+ };
248
+ /**
249
+ * Revert one pending `set` action — decision AND append under the service projection +
250
+ * actions locks, so a concurrent writer (a connector confirming the same action from
251
+ * another process) cannot land between the check and the revert: the whole read-decide-
252
+ * append is ONE critical section. Idempotent: a repeat answers 'already-reverted'.
253
+ * Only `set` rows are revertable — reverting a revert or a confirm is a category error.
254
+ */
255
+ export declare function revertAction(opts: {
256
+ service: string;
257
+ actionId: string;
258
+ occurredAt: string;
259
+ root?: string;
260
+ }): RevertOutcome;
261
+ /** The LOCAL OVERLAY: pending actions (set, not reverted, not yet confirmed) — the divergence
262
+ * from the mirror, what a twin's projection folds over the observed state. A QUARANTINED row
263
+ * (contract "A snapshot import never plants a push") is still local state and stays here; it
264
+ * leaves only the PUSHABLE suffix below. */
265
+ export declare function pendingActions(service: string, root?: string): TwinAction[];
266
+ /** A twin's OWN bookkeeping in its action log — a subject type beginning with `_` (stripe's
267
+ * `_idempotency` store, for one): part of the overlay the twin serves from, never a change the
268
+ * app made. It is not in the log a user reads, not in a diff, not in a changeset, not pushed. */
269
+ export declare function isTwinBookkeeping(action: Pick<TwinAction, 'subject'>): boolean;
270
+ /** The PUSHABLE suffix — `log origin..HEAD` as the push arm, the pending door, plans and the
271
+ * unpushed count read it: the overlay minus quarantined rows (an import is a copy, not a
272
+ * decision; releasing a quarantined row is an explicit act, never a scheduler's) and minus the
273
+ * twin's own bookkeeping. */
274
+ export declare function pushablePendingActions(service: string, root?: string): TwinAction[];
275
+ export declare const listTransactionCommits: typeof listActions;
276
+ export declare const pendingTransactionCommits: typeof pendingActions;