xytara 2.0.0 → 2.2.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.
- package/.env.example +6 -0
- package/ADAPTER_START_HERE.md +56 -0
- package/BSV_TERANODE_SETUP.md +30 -1
- package/CARRIED_HANDOFF_DEMO.md +60 -0
- package/EXAMPLE_PATHS.md +53 -0
- package/FINAL_CONTRACT.md +6 -6
- package/OPERATIONS_RUNBOOK.md +65 -0
- package/OPERATOR_START_HERE.md +63 -0
- package/PARTNER_READY_PATH.md +71 -0
- package/PROGRAM_COMPLETE_RELEASE.md +63 -0
- package/PROGRAM_STATUS.md +57 -0
- package/PUBLIC_DEMO_SCRIPT.md +78 -0
- package/PUBLISH_PLAN.md +14 -0
- package/README.md +142 -6
- package/RELEASE_CHECKLIST.md +16 -0
- package/RELEASE_NOTES.md +28 -0
- package/SERVICE_CONTRACT.md +4 -4
- package/START_HERE.md +32 -0
- package/SUPABASE_RUNTIME_STATE_SETUP.md +61 -0
- package/WHY_XYTARA_XOONYA.md +63 -0
- package/adapters/examples/minimal-third-party-execution-adapter.js +61 -0
- package/adapters/examples/minimal-third-party-execution-adapter.manifest.json +79 -0
- package/adapters/examples/minimal-third-party-execution-registration.record.json +26 -0
- package/adapters/examples/minimal-third-party-execution.certification-pack.json +22 -0
- package/adapters/examples/minimal-third-party-execution.submission-bundle.json +126 -0
- package/bin/xytara-release.js +1419 -0
- package/bin/xytara-run.js +186 -0
- package/examples/adapter_review_walkthrough.js +53 -0
- package/examples/export_carried_handoff.js +80 -0
- package/examples/funded_runtime_walkthrough.js +70 -0
- package/examples/partner_launch_walkthrough.js +43 -0
- package/examples/quickstart.js +3255 -0
- package/index.js +87 -1
- package/lib/adapter_pack.js +118 -0
- package/lib/adapter_partner_pack.js +89 -0
- package/lib/adapter_promotion_pack.js +89 -0
- package/lib/announcement_pack.js +61 -0
- package/lib/commerce_artifacts.js +3 -0
- package/lib/commerce_economics.js +14 -0
- package/lib/commerce_reports.js +26 -1
- package/lib/commerce_shell.js +18 -2
- package/lib/ecosystem_entry.js +64 -0
- package/lib/launch_narrative.js +53 -0
- package/lib/outreach_copy_pack.js +51 -0
- package/lib/outreach_message_pack.js +71 -0
- package/lib/outreach_proof.js +71 -0
- package/lib/outreach_target_pack.js +60 -0
- package/lib/phase_10_closeout_pack.js +45 -0
- package/lib/phase_10_completion_pack.js +76 -0
- package/lib/phase_10_decision_record_pack.js +54 -0
- package/lib/phase_10_decision_resolution_pack.js +53 -0
- package/lib/phase_10_demand_signal_adapters_runtime_pack.js +74 -0
- package/lib/phase_10_ecosystem_backlog_triage_runtime_pack.js +74 -0
- package/lib/phase_10_freeze_baseline_pack.js +67 -0
- package/lib/phase_10_freeze_review_pack.js +64 -0
- package/lib/phase_10_gate_pack.js +57 -0
- package/lib/phase_10_long_tail_continuity_pack.js +80 -0
- package/lib/phase_10_long_tail_ecosystem_pack.js +66 -0
- package/lib/phase_10_niche_extension_registry_runtime_pack.js +74 -0
- package/lib/phase_1_claude_mcp_pack.js +60 -0
- package/lib/phase_1_claude_mcp_runtime_pack.js +52 -0
- package/lib/phase_1_ecosystem_pack.js +67 -0
- package/lib/phase_1_openai_codex_pack.js +60 -0
- package/lib/phase_1_openai_codex_runtime_pack.js +52 -0
- package/lib/phase_2_base_runtime_pack.js +71 -0
- package/lib/phase_2_base_usdc_runtime_pack.js +72 -0
- package/lib/phase_2_closeout_pack.js +65 -0
- package/lib/phase_2_completion_pack.js +113 -0
- package/lib/phase_2_decision_record_pack.js +66 -0
- package/lib/phase_2_decision_resolution_pack.js +69 -0
- package/lib/phase_2_eth_runtime_pack.js +70 -0
- package/lib/phase_2_evm_base_runtime_pack.js +73 -0
- package/lib/phase_2_evm_runtime_pack.js +71 -0
- package/lib/phase_2_flip_preview_pack.js +60 -0
- package/lib/phase_2_freeze_review_pack.js +99 -0
- package/lib/phase_2_gate_pack.js +77 -0
- package/lib/phase_2_payment_rails_pack.js +71 -0
- package/lib/phase_2_usdc_runtime_pack.js +71 -0
- package/lib/phase_3_brc_runtime_pack.js +81 -0
- package/lib/phase_3_bsv_teranode_runtime_pack.js +83 -0
- package/lib/phase_3_closeout_pack.js +62 -0
- package/lib/phase_3_completion_pack.js +94 -0
- package/lib/phase_3_decision_record_pack.js +64 -0
- package/lib/phase_3_decision_resolution_pack.js +69 -0
- package/lib/phase_3_freeze_baseline_pack.js +67 -0
- package/lib/phase_3_freeze_review_pack.js +80 -0
- package/lib/phase_3_gate_pack.js +76 -0
- package/lib/phase_3_metanet_runtime_pack.js +81 -0
- package/lib/phase_3_native_bsv_pack.js +91 -0
- package/lib/phase_3_native_continuity_pack.js +82 -0
- package/lib/phase_4_attestation_runtime_pack.js +75 -0
- package/lib/phase_4_closeout_pack.js +45 -0
- package/lib/phase_4_completion_pack.js +94 -0
- package/lib/phase_4_decision_record_pack.js +54 -0
- package/lib/phase_4_decision_resolution_pack.js +45 -0
- package/lib/phase_4_erc8004_runtime_pack.js +76 -0
- package/lib/phase_4_freeze_baseline_pack.js +67 -0
- package/lib/phase_4_freeze_review_pack.js +68 -0
- package/lib/phase_4_gate_pack.js +58 -0
- package/lib/phase_4_identity_registry_runtime_pack.js +75 -0
- package/lib/phase_4_identity_trust_pack.js +73 -0
- package/lib/phase_4_trust_continuity_pack.js +86 -0
- package/lib/phase_5_antelope_runtime_pack.js +76 -0
- package/lib/phase_5_closeout_pack.js +44 -0
- package/lib/phase_5_completion_pack.js +82 -0
- package/lib/phase_5_decision_record_pack.js +54 -0
- package/lib/phase_5_decision_resolution_pack.js +45 -0
- package/lib/phase_5_freeze_baseline_pack.js +67 -0
- package/lib/phase_5_freeze_review_pack.js +68 -0
- package/lib/phase_5_gate_pack.js +58 -0
- package/lib/phase_5_major_rails_continuity_pack.js +86 -0
- package/lib/phase_5_major_rails_pack.js +74 -0
- package/lib/phase_5_proton_xpr_runtime_pack.js +76 -0
- package/lib/phase_5_solana_runtime_pack.js +76 -0
- package/lib/phase_6_autogen_runtime_pack.js +72 -0
- package/lib/phase_6_closeout_pack.js +46 -0
- package/lib/phase_6_completion_pack.js +77 -0
- package/lib/phase_6_decision_record_pack.js +54 -0
- package/lib/phase_6_decision_resolution_pack.js +53 -0
- package/lib/phase_6_framework_continuity_pack.js +81 -0
- package/lib/phase_6_framework_runtime_pack.js +68 -0
- package/lib/phase_6_freeze_baseline_pack.js +68 -0
- package/lib/phase_6_freeze_review_pack.js +65 -0
- package/lib/phase_6_gate_pack.js +59 -0
- package/lib/phase_6_langchain_runtime_pack.js +72 -0
- package/lib/phase_6_langgraph_runtime_pack.js +72 -0
- package/lib/phase_6_semantic_kernel_runtime_pack.js +72 -0
- package/lib/phase_7_closeout_pack.js +45 -0
- package/lib/phase_7_completion_pack.js +85 -0
- package/lib/phase_7_decision_record_pack.js +53 -0
- package/lib/phase_7_decision_resolution_pack.js +53 -0
- package/lib/phase_7_event_system_continuity_pack.js +89 -0
- package/lib/phase_7_event_system_pack.js +76 -0
- package/lib/phase_7_freeze_baseline_pack.js +74 -0
- package/lib/phase_7_freeze_review_pack.js +65 -0
- package/lib/phase_7_gate_pack.js +58 -0
- package/lib/phase_7_kafka_runtime_pack.js +74 -0
- package/lib/phase_7_mqtt_runtime_pack.js +74 -0
- package/lib/phase_7_nats_runtime_pack.js +74 -0
- package/lib/phase_7_webhook_event_bus_runtime_pack.js +74 -0
- package/lib/phase_8_closeout_pack.js +46 -0
- package/lib/phase_8_completion_pack.js +82 -0
- package/lib/phase_8_decision_record_pack.js +54 -0
- package/lib/phase_8_decision_resolution_pack.js +53 -0
- package/lib/phase_8_external_receipt_import_runtime_pack.js +74 -0
- package/lib/phase_8_external_result_import_runtime_pack.js +74 -0
- package/lib/phase_8_freeze_baseline_pack.js +71 -0
- package/lib/phase_8_freeze_review_pack.js +64 -0
- package/lib/phase_8_gate_pack.js +58 -0
- package/lib/phase_8_proof_bridge_continuity_pack.js +86 -0
- package/lib/phase_8_proof_bridges_pack.js +72 -0
- package/lib/phase_8_proof_bundle_normalization_runtime_pack.js +74 -0
- package/lib/phase_9_closeout_pack.js +46 -0
- package/lib/phase_9_completion_pack.js +82 -0
- package/lib/phase_9_custody_refs_runtime_pack.js +74 -0
- package/lib/phase_9_decision_record_pack.js +54 -0
- package/lib/phase_9_decision_resolution_pack.js +53 -0
- package/lib/phase_9_freeze_baseline_pack.js +71 -0
- package/lib/phase_9_freeze_review_pack.js +64 -0
- package/lib/phase_9_gate_pack.js +58 -0
- package/lib/phase_9_operator_accounting_bridges_runtime_pack.js +74 -0
- package/lib/phase_9_treasury_connectivity_pack.js +72 -0
- package/lib/phase_9_treasury_continuity_pack.js +86 -0
- package/lib/phase_9_treasury_destinations_runtime_pack.js +74 -0
- package/lib/phase_program_pack.js +120 -0
- package/lib/publish_plan.js +51 -0
- package/lib/release_candidate.js +50 -0
- package/lib/release_center.js +1085 -0
- package/lib/release_history.js +72 -0
- package/lib/release_manifest.js +114 -0
- package/lib/release_pack.js +454 -0
- package/lib/runtime_state_store.js +354 -0
- package/lib/settlement_bsv_live.js +262 -13
- package/lib/soft_launch_pack.js +78 -0
- package/package.json +24 -2
- package/server.js +2562 -220
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
function normalizeFilePath(filePath) {
|
|
7
|
+
const raw = typeof filePath === "string" ? filePath.trim() : "";
|
|
8
|
+
return raw ? path.resolve(raw) : "";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function normalizeUrl(url) {
|
|
12
|
+
const raw = typeof url === "string" ? url.trim() : "";
|
|
13
|
+
return raw ? raw.replace(/\/+$/, "") : "";
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function normalizeString(value, fallback = "") {
|
|
17
|
+
const raw = typeof value === "string" ? value.trim() : "";
|
|
18
|
+
return raw || fallback;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function serializeValue(value) {
|
|
22
|
+
if (value instanceof Map) {
|
|
23
|
+
return {
|
|
24
|
+
__runtime_state_kind: "Map",
|
|
25
|
+
value: Array.from(value.entries()).map(([key, entry]) => [key, serializeValue(entry)])
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
if (value instanceof Set) {
|
|
29
|
+
return {
|
|
30
|
+
__runtime_state_kind: "Set",
|
|
31
|
+
value: Array.from(value.values()).map((entry) => serializeValue(entry))
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.map((entry) => serializeValue(entry));
|
|
36
|
+
}
|
|
37
|
+
if (value && typeof value === "object") {
|
|
38
|
+
return Object.fromEntries(
|
|
39
|
+
Object.entries(value).map(([key, entry]) => [key, serializeValue(entry)])
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function deserializeValue(value) {
|
|
46
|
+
if (Array.isArray(value)) {
|
|
47
|
+
return value.map((entry) => deserializeValue(entry));
|
|
48
|
+
}
|
|
49
|
+
if (!value || typeof value !== "object") {
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
if (value.__runtime_state_kind === "Map" && Array.isArray(value.value)) {
|
|
53
|
+
return new Map(value.value.map((entry) => [entry[0], deserializeValue(entry[1])]));
|
|
54
|
+
}
|
|
55
|
+
if (value.__runtime_state_kind === "Set" && Array.isArray(value.value)) {
|
|
56
|
+
return new Set(value.value.map((entry) => deserializeValue(entry)));
|
|
57
|
+
}
|
|
58
|
+
return Object.fromEntries(
|
|
59
|
+
Object.entries(value).map(([key, entry]) => [key, deserializeValue(entry)])
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function createRuntimeStateStore(config = {}) {
|
|
64
|
+
const filePath = normalizeFilePath(config.filePath);
|
|
65
|
+
const directoryPath = filePath ? path.dirname(filePath) : "";
|
|
66
|
+
const intervalMs = Math.max(1000, Number.parseInt(String(config.intervalMs || 5000), 10) || 5000);
|
|
67
|
+
const supabaseUrl = normalizeUrl(config.supabaseUrl);
|
|
68
|
+
const supabaseServiceRoleKey = normalizeString(config.supabaseServiceRoleKey);
|
|
69
|
+
const supabaseTable = normalizeString(config.supabaseTable, "xytara_runtime_state");
|
|
70
|
+
const stateKey = normalizeString(config.stateKey, "default");
|
|
71
|
+
const storageMode = supabaseUrl && supabaseServiceRoleKey ? "supabase" : filePath ? "file" : "memory";
|
|
72
|
+
let dirty = false;
|
|
73
|
+
let timer = null;
|
|
74
|
+
let lastPersistedAtIso = null;
|
|
75
|
+
let lastFlushSucceeded = false;
|
|
76
|
+
let lastError = null;
|
|
77
|
+
let snapshotExists = false;
|
|
78
|
+
let snapshotUpdatedAtIso = null;
|
|
79
|
+
let snapshotSizeBytes = 0;
|
|
80
|
+
let remoteConfigured = storageMode === "supabase";
|
|
81
|
+
let remoteReachable = storageMode !== "supabase";
|
|
82
|
+
let remoteTableReady = storageMode !== "supabase";
|
|
83
|
+
|
|
84
|
+
function enabled() {
|
|
85
|
+
return storageMode !== "memory";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function markDirty() {
|
|
89
|
+
if (!enabled()) return;
|
|
90
|
+
dirty = true;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function applyRestoredState(state, restored) {
|
|
94
|
+
if (!restored || typeof restored !== "object" || !state) return false;
|
|
95
|
+
for (const key of Object.keys(state)) {
|
|
96
|
+
if (Object.prototype.hasOwnProperty.call(restored, key)) {
|
|
97
|
+
state[key] = restored[key];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return true;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function inspectFileDirectory() {
|
|
104
|
+
let exists = false;
|
|
105
|
+
let writable = false;
|
|
106
|
+
let error = null;
|
|
107
|
+
if (storageMode !== "file") {
|
|
108
|
+
return {
|
|
109
|
+
exists: false,
|
|
110
|
+
writable: false,
|
|
111
|
+
error: null
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
exists = fs.existsSync(directoryPath);
|
|
116
|
+
if (exists) {
|
|
117
|
+
fs.accessSync(directoryPath, fs.constants.W_OK);
|
|
118
|
+
writable = true;
|
|
119
|
+
}
|
|
120
|
+
} catch (issue) {
|
|
121
|
+
writable = false;
|
|
122
|
+
error = issue;
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
exists,
|
|
126
|
+
writable,
|
|
127
|
+
error
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
async function fetchSupabase(pathname, options = {}) {
|
|
132
|
+
const response = await fetch(`${supabaseUrl}${pathname}`, {
|
|
133
|
+
...options,
|
|
134
|
+
headers: {
|
|
135
|
+
apikey: supabaseServiceRoleKey,
|
|
136
|
+
authorization: `Bearer ${supabaseServiceRoleKey}`,
|
|
137
|
+
...(options.headers || {})
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
remoteReachable = true;
|
|
141
|
+
if (!response.ok) {
|
|
142
|
+
const text = await response.text();
|
|
143
|
+
const error = new Error(`supabase_runtime_state_${response.status}: ${text || response.statusText}`);
|
|
144
|
+
error.status = response.status;
|
|
145
|
+
throw error;
|
|
146
|
+
}
|
|
147
|
+
return response;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function updateSuccessSnapshotMetadata(persistedAtIso, serializedSnapshot) {
|
|
151
|
+
lastPersistedAtIso = persistedAtIso;
|
|
152
|
+
lastFlushSucceeded = true;
|
|
153
|
+
lastError = null;
|
|
154
|
+
snapshotExists = true;
|
|
155
|
+
snapshotUpdatedAtIso = persistedAtIso;
|
|
156
|
+
snapshotSizeBytes = Buffer.byteLength(JSON.stringify(serializedSnapshot), "utf8");
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function flushSupabase(state) {
|
|
160
|
+
if (!dirty || !state) return false;
|
|
161
|
+
const persistedAtIso = new Date().toISOString();
|
|
162
|
+
const serializedSnapshot = serializeValue(state);
|
|
163
|
+
const payload = [{
|
|
164
|
+
state_key: stateKey,
|
|
165
|
+
snapshot: serializedSnapshot,
|
|
166
|
+
persisted_at_iso: persistedAtIso
|
|
167
|
+
}];
|
|
168
|
+
try {
|
|
169
|
+
await fetchSupabase(`/rest/v1/${encodeURIComponent(supabaseTable)}?on_conflict=state_key`, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: {
|
|
172
|
+
"content-type": "application/json",
|
|
173
|
+
prefer: "resolution=merge-duplicates,return=minimal"
|
|
174
|
+
},
|
|
175
|
+
body: JSON.stringify(payload)
|
|
176
|
+
});
|
|
177
|
+
remoteTableReady = true;
|
|
178
|
+
dirty = false;
|
|
179
|
+
updateSuccessSnapshotMetadata(persistedAtIso, serializedSnapshot);
|
|
180
|
+
return true;
|
|
181
|
+
} catch (error) {
|
|
182
|
+
lastFlushSucceeded = false;
|
|
183
|
+
lastError = error;
|
|
184
|
+
remoteReachable = false;
|
|
185
|
+
remoteTableReady = false;
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function flushFile(state) {
|
|
191
|
+
if (!dirty || !state) return false;
|
|
192
|
+
try {
|
|
193
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
194
|
+
const tempPath = `${filePath}.tmp`;
|
|
195
|
+
const persistedAtIso = new Date().toISOString();
|
|
196
|
+
const serializedSnapshot = serializeValue(state);
|
|
197
|
+
const snapshot = {
|
|
198
|
+
persisted_at_iso: persistedAtIso,
|
|
199
|
+
state: serializedSnapshot
|
|
200
|
+
};
|
|
201
|
+
fs.writeFileSync(tempPath, JSON.stringify(snapshot, null, 2), "utf8");
|
|
202
|
+
fs.renameSync(tempPath, filePath);
|
|
203
|
+
dirty = false;
|
|
204
|
+
updateSuccessSnapshotMetadata(persistedAtIso, serializedSnapshot);
|
|
205
|
+
return true;
|
|
206
|
+
} catch (error) {
|
|
207
|
+
lastFlushSucceeded = false;
|
|
208
|
+
lastError = error;
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function flush(state) {
|
|
214
|
+
if (!enabled() || !dirty || !state) return false;
|
|
215
|
+
if (storageMode === "supabase") {
|
|
216
|
+
return flushSupabase(state);
|
|
217
|
+
}
|
|
218
|
+
if (storageMode === "file") {
|
|
219
|
+
return flushFile(state);
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async function loadSupabase(state) {
|
|
225
|
+
try {
|
|
226
|
+
const response = await fetchSupabase(
|
|
227
|
+
`/rest/v1/${encodeURIComponent(supabaseTable)}?state_key=eq.${encodeURIComponent(stateKey)}&select=snapshot,persisted_at_iso&limit=1`
|
|
228
|
+
);
|
|
229
|
+
const rows = await response.json();
|
|
230
|
+
const row = Array.isArray(rows) && rows[0] ? rows[0] : null;
|
|
231
|
+
remoteTableReady = true;
|
|
232
|
+
if (!row || !row.snapshot) {
|
|
233
|
+
dirty = false;
|
|
234
|
+
lastFlushSucceeded = true;
|
|
235
|
+
lastError = null;
|
|
236
|
+
snapshotExists = false;
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
const restored = deserializeValue(row.snapshot);
|
|
240
|
+
const applied = applyRestoredState(state, restored);
|
|
241
|
+
dirty = false;
|
|
242
|
+
lastPersistedAtIso = row.persisted_at_iso ? String(row.persisted_at_iso) : null;
|
|
243
|
+
lastFlushSucceeded = true;
|
|
244
|
+
lastError = null;
|
|
245
|
+
snapshotExists = applied;
|
|
246
|
+
snapshotUpdatedAtIso = lastPersistedAtIso;
|
|
247
|
+
snapshotSizeBytes = Buffer.byteLength(JSON.stringify(row.snapshot), "utf8");
|
|
248
|
+
return applied;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
lastFlushSucceeded = false;
|
|
251
|
+
lastError = error;
|
|
252
|
+
remoteReachable = false;
|
|
253
|
+
remoteTableReady = false;
|
|
254
|
+
throw error;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async function loadFile(state) {
|
|
259
|
+
if (!state || !fs.existsSync(filePath)) return false;
|
|
260
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
261
|
+
const parsed = JSON.parse(raw);
|
|
262
|
+
const restored = deserializeValue(parsed && parsed.state ? parsed.state : parsed);
|
|
263
|
+
const applied = applyRestoredState(state, restored);
|
|
264
|
+
dirty = false;
|
|
265
|
+
lastPersistedAtIso = parsed && parsed.persisted_at_iso ? String(parsed.persisted_at_iso) : null;
|
|
266
|
+
lastFlushSucceeded = true;
|
|
267
|
+
lastError = null;
|
|
268
|
+
snapshotExists = applied;
|
|
269
|
+
snapshotUpdatedAtIso = parsed && parsed.persisted_at_iso ? String(parsed.persisted_at_iso) : null;
|
|
270
|
+
snapshotSizeBytes = Buffer.byteLength(raw, "utf8");
|
|
271
|
+
return applied;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async function load(state) {
|
|
275
|
+
if (!enabled() || !state) return false;
|
|
276
|
+
if (storageMode === "supabase") {
|
|
277
|
+
return loadSupabase(state);
|
|
278
|
+
}
|
|
279
|
+
if (storageMode === "file") {
|
|
280
|
+
return loadFile(state);
|
|
281
|
+
}
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function inspect() {
|
|
286
|
+
const fileDirectory = inspectFileDirectory();
|
|
287
|
+
const fileStats = storageMode === "file" && snapshotExists && fs.existsSync(filePath)
|
|
288
|
+
? fs.statSync(filePath)
|
|
289
|
+
: null;
|
|
290
|
+
return {
|
|
291
|
+
enabled: enabled(),
|
|
292
|
+
storage_mode: storageMode,
|
|
293
|
+
file_path: filePath || null,
|
|
294
|
+
directory_path: directoryPath || null,
|
|
295
|
+
interval_ms: intervalMs,
|
|
296
|
+
dirty,
|
|
297
|
+
timer_active: Boolean(timer),
|
|
298
|
+
directory_exists: storageMode === "file" ? fileDirectory.exists : false,
|
|
299
|
+
directory_writable: storageMode === "file" ? fileDirectory.writable : false,
|
|
300
|
+
directory_error: storageMode === "file" && fileDirectory.error ? fileDirectory.error.message : null,
|
|
301
|
+
snapshot_exists: snapshotExists,
|
|
302
|
+
snapshot_size_bytes: fileStats ? fileStats.size : snapshotSizeBytes,
|
|
303
|
+
snapshot_updated_at_iso: fileStats ? fileStats.mtime.toISOString() : snapshotUpdatedAtIso,
|
|
304
|
+
last_persisted_at_iso: lastPersistedAtIso,
|
|
305
|
+
last_flush_succeeded: lastFlushSucceeded,
|
|
306
|
+
last_error: lastError ? lastError.message : null,
|
|
307
|
+
supabase_url: supabaseUrl || null,
|
|
308
|
+
supabase_table: storageMode === "supabase" ? supabaseTable : null,
|
|
309
|
+
supabase_state_key: storageMode === "supabase" ? stateKey : null,
|
|
310
|
+
remote_configured: remoteConfigured,
|
|
311
|
+
remote_reachable: remoteReachable,
|
|
312
|
+
remote_table_ready: remoteTableReady
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function start(state) {
|
|
317
|
+
if (!enabled() || timer) return;
|
|
318
|
+
timer = setInterval(() => {
|
|
319
|
+
Promise.resolve(flush(state)).catch((error) => {
|
|
320
|
+
console.error("runtime state snapshot flush failed", error);
|
|
321
|
+
});
|
|
322
|
+
}, intervalMs);
|
|
323
|
+
if (typeof timer.unref === "function") timer.unref();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function stop(state) {
|
|
327
|
+
if (timer) {
|
|
328
|
+
clearInterval(timer);
|
|
329
|
+
timer = null;
|
|
330
|
+
}
|
|
331
|
+
try {
|
|
332
|
+
await flush(state);
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.error("runtime state snapshot final flush failed", error);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
enabled,
|
|
340
|
+
storageMode,
|
|
341
|
+
filePath,
|
|
342
|
+
intervalMs,
|
|
343
|
+
inspect,
|
|
344
|
+
load,
|
|
345
|
+
markDirty,
|
|
346
|
+
flush,
|
|
347
|
+
start,
|
|
348
|
+
stop
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
module.exports = {
|
|
353
|
+
createRuntimeStateStore
|
|
354
|
+
};
|