@hardkas/core 0.6.1-alpha → 0.7.1-alpha
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/dist/index.d.ts +8 -1
- package/dist/index.js +91 -39
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -763,6 +763,7 @@ declare function detectSemanticDrift(dashboardView: SemanticIdentity, queryStore
|
|
|
763
763
|
declare function assertNoSemanticDrift(dashboardView: SemanticIdentity, queryStoreView: SemanticIdentity, replayView: SemanticIdentity, filesystemView: SemanticIdentity): void;
|
|
764
764
|
|
|
765
765
|
declare class AppendCoordinator {
|
|
766
|
+
private static _lastRecovery;
|
|
766
767
|
/**
|
|
767
768
|
* Safely appends a line to a JSONL log under process coordination locks.
|
|
768
769
|
* Performs an immediate fsync to ensure data durability.
|
|
@@ -771,15 +772,21 @@ declare class AppendCoordinator {
|
|
|
771
772
|
static appendAtomic(filePath: string, line: string, rootDir: string): void;
|
|
772
773
|
/**
|
|
773
774
|
* Scans a JSONL stream for corruption, truncating malformed trailing lines.
|
|
775
|
+
* Utilizes a backward newline scanning logic with a rolling buffer,
|
|
776
|
+
* supporting lines of arbitrary size and only truncating the last complete
|
|
777
|
+
* valid JSONL boundary if a parse failure is detected.
|
|
774
778
|
*/
|
|
775
779
|
static recoverCorruptedTail(filePath: string): {
|
|
776
780
|
repaired: boolean;
|
|
777
781
|
linesDiscarded: number;
|
|
778
782
|
originalTail: string;
|
|
783
|
+
originalSize: number;
|
|
784
|
+
recoveredSize: number;
|
|
785
|
+
reason: string;
|
|
779
786
|
};
|
|
780
787
|
}
|
|
781
788
|
|
|
782
|
-
declare const CURRENT_RUNTIME_VERSION = "0.
|
|
789
|
+
declare const CURRENT_RUNTIME_VERSION = "0.7.0-alpha";
|
|
783
790
|
declare const MIN_SUPPORTED_VERSION = "0.5.0-alpha";
|
|
784
791
|
interface MigrationStatus {
|
|
785
792
|
needsMigration: boolean;
|
package/dist/index.js
CHANGED
|
@@ -115,6 +115,7 @@ var EnvironmentTelemetry = new TelemetryProxy();
|
|
|
115
115
|
|
|
116
116
|
// src/append-coordinator.ts
|
|
117
117
|
var AppendCoordinator = class _AppendCoordinator {
|
|
118
|
+
static _lastRecovery = null;
|
|
118
119
|
/**
|
|
119
120
|
* Safely appends a line to a JSONL log under process coordination locks.
|
|
120
121
|
* Performs an immediate fsync to ensure data durability.
|
|
@@ -157,6 +158,7 @@ var AppendCoordinator = class _AppendCoordinator {
|
|
|
157
158
|
repaired = true;
|
|
158
159
|
linesDiscarded = recovery.linesDiscarded;
|
|
159
160
|
originalTail = recovery.originalTail;
|
|
161
|
+
_AppendCoordinator._lastRecovery = recovery;
|
|
160
162
|
}
|
|
161
163
|
const logDir = path2.dirname(filePath);
|
|
162
164
|
if (!fs.existsSync(logDir)) {
|
|
@@ -178,12 +180,13 @@ var AppendCoordinator = class _AppendCoordinator {
|
|
|
178
180
|
}
|
|
179
181
|
if (repaired) {
|
|
180
182
|
try {
|
|
183
|
+
const recovery = _AppendCoordinator._lastRecovery;
|
|
181
184
|
const telemetry = getTelemetry();
|
|
182
185
|
telemetry.logAnomaly(
|
|
183
186
|
"EXTERNAL_MUTATION",
|
|
184
187
|
"medium",
|
|
185
188
|
"fs",
|
|
186
|
-
`Recovered corrupted
|
|
189
|
+
`Recovered corrupted tail in ${logBase}. Original size: ${recovery.originalSize} bytes, Recovered size: ${recovery.recoveredSize} bytes, Truncated bytes: ${linesDiscarded}. Reason: ${recovery.reason}. Original tail snippet: "${originalTail.slice(0, 60)}..."`,
|
|
187
190
|
rootDir
|
|
188
191
|
);
|
|
189
192
|
} catch {
|
|
@@ -192,47 +195,96 @@ var AppendCoordinator = class _AppendCoordinator {
|
|
|
192
195
|
}
|
|
193
196
|
/**
|
|
194
197
|
* Scans a JSONL stream for corruption, truncating malformed trailing lines.
|
|
198
|
+
* Utilizes a backward newline scanning logic with a rolling buffer,
|
|
199
|
+
* supporting lines of arbitrary size and only truncating the last complete
|
|
200
|
+
* valid JSONL boundary if a parse failure is detected.
|
|
195
201
|
*/
|
|
196
202
|
static recoverCorruptedTail(filePath) {
|
|
197
|
-
|
|
203
|
+
const defaultRes = {
|
|
204
|
+
repaired: false,
|
|
205
|
+
linesDiscarded: 0,
|
|
206
|
+
originalTail: "",
|
|
207
|
+
originalSize: 0,
|
|
208
|
+
recoveredSize: 0,
|
|
209
|
+
reason: ""
|
|
210
|
+
};
|
|
211
|
+
if (!fs.existsSync(filePath)) return defaultRes;
|
|
198
212
|
const stat = fs.statSync(filePath);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
213
|
+
defaultRes.originalSize = stat.size;
|
|
214
|
+
defaultRes.recoveredSize = stat.size;
|
|
215
|
+
if (stat.size === 0) return defaultRes;
|
|
202
216
|
const fd = fs.openSync(filePath, "r");
|
|
203
|
-
const buf = Buffer.alloc(Math.min(TAIL_SIZE, stat.size));
|
|
204
|
-
fs.readSync(fd, buf, 0, buf.length, readStart);
|
|
205
|
-
fs.closeSync(fd);
|
|
206
|
-
const tail = buf.toString("utf-8");
|
|
207
|
-
const lines = tail.split("\n");
|
|
208
|
-
if (lines.length === 0) return { repaired: false, linesDiscarded: 0, originalTail: "" };
|
|
209
|
-
let lastLine = "";
|
|
210
|
-
let lastLineIdx = -1;
|
|
211
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
212
|
-
const l = lines[i].trim();
|
|
213
|
-
if (l) {
|
|
214
|
-
lastLine = l;
|
|
215
|
-
lastLineIdx = i;
|
|
216
|
-
break;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
if (!lastLine) return { repaired: false, linesDiscarded: 0, originalTail: "" };
|
|
220
|
-
if (readStart > 0 && lastLineIdx === 0) {
|
|
221
|
-
return { repaired: false, linesDiscarded: 0, originalTail: "" };
|
|
222
|
-
}
|
|
223
217
|
try {
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
218
|
+
let lastCharPos = -1;
|
|
219
|
+
let precedingNewlinePos = -1;
|
|
220
|
+
const CHUNK_SIZE = 64 * 1024;
|
|
221
|
+
let position = stat.size;
|
|
222
|
+
const buffer = Buffer.alloc(CHUNK_SIZE);
|
|
223
|
+
outer1: while (position > 0) {
|
|
224
|
+
const readLength = Math.min(CHUNK_SIZE, position);
|
|
225
|
+
position -= readLength;
|
|
226
|
+
fs.readSync(fd, buffer, 0, readLength, position);
|
|
227
|
+
for (let i = readLength - 1; i >= 0; i--) {
|
|
228
|
+
const charCode = buffer[i];
|
|
229
|
+
if (charCode !== 32 && charCode !== 9 && charCode !== 10 && charCode !== 13) {
|
|
230
|
+
lastCharPos = position + i;
|
|
231
|
+
break outer1;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (lastCharPos === -1) {
|
|
236
|
+
fs.closeSync(fd);
|
|
237
|
+
fs.truncateSync(filePath, 0);
|
|
238
|
+
return {
|
|
239
|
+
repaired: true,
|
|
240
|
+
linesDiscarded: stat.size,
|
|
241
|
+
originalTail: "",
|
|
242
|
+
originalSize: stat.size,
|
|
243
|
+
recoveredSize: 0,
|
|
244
|
+
reason: "File only contained whitespaces or newlines"
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
position = lastCharPos;
|
|
248
|
+
outer2: while (position > 0) {
|
|
249
|
+
const readLength = Math.min(CHUNK_SIZE, position);
|
|
250
|
+
position -= readLength;
|
|
251
|
+
fs.readSync(fd, buffer, 0, readLength, position);
|
|
252
|
+
for (let i = readLength - 1; i >= 0; i--) {
|
|
253
|
+
if (buffer[i] === 10) {
|
|
254
|
+
precedingNewlinePos = position + i;
|
|
255
|
+
break outer2;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const lastLineStart = precedingNewlinePos === -1 ? 0 : precedingNewlinePos + 1;
|
|
260
|
+
const lastLineEnd = lastCharPos + 1;
|
|
261
|
+
const lastLineLength = lastLineEnd - lastLineStart;
|
|
262
|
+
const lastLineBuf = Buffer.alloc(lastLineLength);
|
|
263
|
+
fs.readSync(fd, lastLineBuf, 0, lastLineLength, lastLineStart);
|
|
264
|
+
fs.closeSync(fd);
|
|
265
|
+
const lastLine = lastLineBuf.toString("utf-8");
|
|
266
|
+
try {
|
|
267
|
+
JSON.parse(lastLine);
|
|
268
|
+
return defaultRes;
|
|
269
|
+
} catch (err) {
|
|
270
|
+
const truncateTo = lastLineStart;
|
|
271
|
+
const discardedBytes = stat.size - truncateTo;
|
|
272
|
+
fs.truncateSync(filePath, truncateTo);
|
|
273
|
+
return {
|
|
274
|
+
repaired: true,
|
|
275
|
+
linesDiscarded: discardedBytes,
|
|
276
|
+
originalTail: lastLine,
|
|
277
|
+
originalSize: stat.size,
|
|
278
|
+
recoveredSize: truncateTo,
|
|
279
|
+
reason: err instanceof Error ? err.message : "Invalid JSON syntax"
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
} catch (e) {
|
|
283
|
+
try {
|
|
284
|
+
fs.closeSync(fd);
|
|
285
|
+
} catch {
|
|
286
|
+
}
|
|
287
|
+
throw e;
|
|
236
288
|
}
|
|
237
289
|
}
|
|
238
290
|
};
|
|
@@ -827,7 +879,7 @@ async function createSnapshot(options) {
|
|
|
827
879
|
const manifest = {
|
|
828
880
|
snapshotVersion: 1,
|
|
829
881
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
830
|
-
hardkasVersion: "0.
|
|
882
|
+
hardkasVersion: "0.7.0-alpha",
|
|
831
883
|
stateAuthority: "filesystem",
|
|
832
884
|
projectionAuthority: "sqlite",
|
|
833
885
|
deterministicScope,
|
|
@@ -1060,7 +1112,7 @@ Resolution Command: ${report.exactReplayCommand}`
|
|
|
1060
1112
|
// src/migrations.ts
|
|
1061
1113
|
import fs6 from "fs";
|
|
1062
1114
|
import path8 from "path";
|
|
1063
|
-
var CURRENT_RUNTIME_VERSION = "0.
|
|
1115
|
+
var CURRENT_RUNTIME_VERSION = "0.7.0-alpha";
|
|
1064
1116
|
var MIN_SUPPORTED_VERSION = "0.5.0-alpha";
|
|
1065
1117
|
var MigrationManager = class {
|
|
1066
1118
|
static checkVersion(rootDir) {
|